From 05b92bc19b4c25e2543c141a05c04d6f2b4822fa Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:22:38 +0100 Subject: [PATCH 01/14] remove annoying log --- interface/app/$libraryId/Layout/Sidebar/EphemeralSection.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/app/$libraryId/Layout/Sidebar/EphemeralSection.tsx b/interface/app/$libraryId/Layout/Sidebar/EphemeralSection.tsx index 213c1097481b..d503a2aa88a0 100644 --- a/interface/app/$libraryId/Layout/Sidebar/EphemeralSection.tsx +++ b/interface/app/$libraryId/Layout/Sidebar/EphemeralSection.tsx @@ -53,8 +53,6 @@ export const EphemeralSection = () => { return locationIdsMap; }, [locations.data, volumes.data]); - console.log('locationIdsForVolumes', locationIdsForVolumes); - const items = [ { type: 'network' }, home ? { type: 'home', path: home } : null, From ab031dd7e9b4392893fb061aa425031635dbceca Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:24:57 +0100 Subject: [PATCH 02/14] return location id on creation --- core/src/api/locations.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/api/locations.rs b/core/src/api/locations.rs index 781a1638d14b..96d9f55f8e68 100644 --- a/core/src/api/locations.rs +++ b/core/src/api/locations.rs @@ -181,11 +181,13 @@ pub(crate) fn mount() -> AlphaRouter { R.with2(library()) .mutation(|(node, library), args: LocationCreateArgs| async move { if let Some(location) = args.create(&node, &library).await? { + let id = Some(location.id); scan_location(&node, &library, location).await?; invalidate_query!(library, "locations.list"); + Ok(id) + } else { + Ok(None) } - - Ok(()) }) }) .procedure("update", { @@ -217,10 +219,13 @@ pub(crate) fn mount() -> AlphaRouter { R.with2(library()) .mutation(|(node, library), args: LocationCreateArgs| async move { if let Some(location) = args.add_library(&node, &library).await? { + let id = location.id; scan_location(&node, &library, location).await?; invalidate_query!(library, "locations.list"); + Ok(Some(id)) + } else { + Ok(None) } - Ok(()) }) }) .procedure("fullRescan", { From 4d178de916be8cfac1d9d81b714a0e78890ce075 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:25:21 +0100 Subject: [PATCH 03/14] add checkbox to open new location once it's been added --- .../library/locations/AddLocationDialog.tsx | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index 060241a7a58c..7a499a078482 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo } from 'react'; +import { MutableRefObject, useCallback, useEffect, useMemo } from 'react'; import { Controller, get } from 'react-hook-form'; import { useDebouncedCallback } from 'use-debounce'; import { @@ -9,7 +9,7 @@ import { usePlausibleEvent, useZodForm } from '@sd/client'; -import { Dialog, ErrorMessage, toast, useDialog, UseDialogProps, z } from '@sd/ui'; +import { CheckBox, Dialog, ErrorMessage, Label, toast, useDialog, UseDialogProps, z } from '@sd/ui'; import Accordion from '~/components/Accordion'; import { useCallbackToWatchForm } from '~/hooks'; import { usePlatform } from '~/util/Platform'; @@ -34,13 +34,15 @@ const isRemoteErrorFormMessage = (message: unknown): message is RemoteErrorFormM const schema = z.object({ path: z.string().min(1), method: z.enum(Object.keys(REMOTE_ERROR_FORM_MESSAGE) as UnionToTuple), - indexerRulesIds: z.array(z.number()) + indexerRulesIds: z.array(z.number()), + shouldRedirect: z.boolean() }); type SchemaType = z.infer; export interface AddLocationDialog extends UseDialogProps { path: string; + redirect: MutableRefObject; method?: RemoteErrorFormMessage; } @@ -57,6 +59,8 @@ export const AddLocationDialog = ({ const listIndexerRules = useLibraryQuery(['locations.indexer_rules.list']); const addLocationToLibrary = useLibraryMutation('locations.addLibrary'); + const redirect = dialogProps.redirect; + // This is required because indexRules is undefined on first render const indexerRulesIds = useMemo( () => listIndexerRules.data?.filter((rule) => rule.default).map((rule) => rule.id) ?? [], @@ -65,7 +69,7 @@ export const AddLocationDialog = ({ const form = useZodForm({ schema, - defaultValues: { path, method, indexerRulesIds } + defaultValues: { path, method, indexerRulesIds, shouldRedirect: true } }); useEffect(() => { @@ -78,10 +82,12 @@ export const AddLocationDialog = ({ }, [form, path, indexerRulesIds]); const addLocation = useCallback( - async ({ path, method, indexerRulesIds }: SchemaType, dryRun = false) => { + async ({ path, method, indexerRulesIds, shouldRedirect }: SchemaType, dryRun = false) => { + let id = null; + switch (method) { case 'CREATE': - await createLocation.mutateAsync({ + id = await createLocation.mutateAsync({ path, dry_run: dryRun, indexer_rules_ids: indexerRulesIds @@ -91,7 +97,7 @@ export const AddLocationDialog = ({ break; case 'NEED_RELINK': - if (!dryRun) await relinkLocation.mutateAsync(path); + if (!dryRun) id = await relinkLocation.mutateAsync(path); // TODO: Update relinked location with new indexer rules, don't have a way to get location id yet though // await updateLocation.mutateAsync({ // id: locationId, @@ -104,7 +110,7 @@ export const AddLocationDialog = ({ break; case 'ADD_LIBRARY': - await addLocationToLibrary.mutateAsync({ + id = await addLocationToLibrary.mutateAsync({ path, dry_run: dryRun, indexer_rules_ids: indexerRulesIds @@ -116,8 +122,10 @@ export const AddLocationDialog = ({ default: throw new Error('Unimplemented custom remote error handling'); } + + redirect.current = shouldRedirect ? id : null; }, - [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent] + [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent, redirect] ); const handleAddError = useCallback( @@ -206,27 +214,40 @@ export const AddLocationDialog = ({ : '' } > - - - - - - - - ( - - )} - control={form.control} +
+ - + + + +
+ + +
+ + + + + ( + + )} + control={form.control} + /> + +
); }; From 995f0e5eb6096c3244a18cc14f6cc59f5804f048 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:25:56 +0100 Subject: [PATCH 04/14] redirect if checkbox was true and a location id was provided, and update bindings --- .../library/locations/AddLocationButton.tsx | 22 ++++++++++++++++--- packages/client/src/core.ts | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx index 958cd96a5e1e..3e6353a8bf94 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx @@ -2,6 +2,8 @@ import { FolderSimplePlus } from '@phosphor-icons/react'; import clsx from 'clsx'; import { motion } from 'framer-motion'; import { useRef, useState } from 'react'; +import { useNavigate } from 'react-router'; +import { useLibraryContext } from '@sd/client'; import { Button, dialogManager, type ButtonProps } from '@sd/ui'; import { useCallbackToWatchResize } from '~/hooks'; import { usePlatform } from '~/util/Platform'; @@ -15,6 +17,9 @@ interface AddLocationButton extends ButtonProps { export const AddLocationButton = ({ path, className, ...props }: AddLocationButton) => { const platform = usePlatform(); + const libraryId = useLibraryContext().library.uuid; + const navigate = useNavigate(); + const transition = { type: 'keyframes', ease: 'easeInOut', @@ -26,6 +31,9 @@ export const AddLocationButton = ({ path, className, ...props }: AddLocationButt const overflowRef = useRef(null); const [isOverflowing, setIsOverflowing] = useState(false); + // if this is set, it'll be the new location id and we should redirect + const locationIdRedirect = useRef(null); + useCallbackToWatchResize(() => { const text = textRef.current; const overflow = overflowRef.current; @@ -46,10 +54,18 @@ export const AddLocationButton = ({ path, className, ...props }: AddLocationButt } // Remember `path` will be `undefined` on web cause the user has to provide it in the modal - if (path !== '') - dialogManager.create((dp) => ( - + if (path !== '') { + await dialogManager.create((dp) => ( + )); + + locationIdRedirect.current && + navigate(`/${libraryId}/location/${locationIdRedirect.current}`); + } }} {...props} > diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index 831e16f87216..1ecb4ea4a0f5 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -69,8 +69,8 @@ export type Procedures = { { key: "library.create", input: CreateLibraryArgs, result: LibraryConfigWrapped } | { key: "library.delete", input: string, result: null } | { key: "library.edit", input: EditLibraryArgs, result: null } | - { key: "locations.addLibrary", input: LibraryArgs, result: null } | - { key: "locations.create", input: LibraryArgs, result: null } | + { key: "locations.addLibrary", input: LibraryArgs, result: number | null } | + { key: "locations.create", input: LibraryArgs, result: number | null } | { key: "locations.delete", input: LibraryArgs, result: null } | { key: "locations.fullRescan", input: LibraryArgs, result: null } | { key: "locations.indexer_rules.create", input: LibraryArgs, result: null } | From 1197d6b906a0b20e1813afe944432f83fbf8c037 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:49:25 +0100 Subject: [PATCH 05/14] add `new()` for `MissingFieldError` --- core/src/util/db.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/util/db.rs b/core/src/util/db.rs index 03b23391d45a..6e8d2554df51 100644 --- a/core/src/util/db.rs +++ b/core/src/util/db.rs @@ -69,6 +69,13 @@ pub fn inode_to_db(inode: u64) -> Vec { #[error("Missing field {0}")] pub struct MissingFieldError(&'static str); +impl MissingFieldError { + #[must_use] + pub const fn new(value: &'static str) -> Self { + Self(value) + } +} + impl From for rspc::Error { fn from(value: MissingFieldError) -> Self { rspc::Error::with_cause( From 42726ae3d35e6811fc185d5bd704d66b3324e872 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:50:45 +0100 Subject: [PATCH 06/14] return location id on location relink --- core/src/location/mod.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/core/src/location/mod.rs b/core/src/location/mod.rs index 5ef71f68e028..3517fef9c273 100644 --- a/core/src/location/mod.rs +++ b/core/src/location/mod.rs @@ -16,7 +16,10 @@ use crate::{ }, }, prisma::{file_path, indexer_rules_in_location, location, PrismaClient}, - util::{db::maybe_missing, error::FileIOError}, + util::{ + db::{maybe_missing, MissingFieldError}, + error::FileIOError, + }, Node, }; @@ -522,7 +525,7 @@ pub async fn light_scan_location( pub async fn relink_location( library: &Arc, location_path: impl AsRef, -) -> Result<(), LocationError> { +) -> Result { let Library { db, id, sync, .. } = &**library; let mut metadata = SpacedriveLocationMetadataFile::try_load(&location_path) @@ -548,13 +551,23 @@ pub async fn relink_location( json!(path), ), db.location().update( - location::pub_id::equals(pub_id), + location::pub_id::equals(pub_id.clone()), vec![location::path::set(Some(path))], ), ) .await?; - Ok(()) + let location_id = db + .location() + .find_unique(location::pub_id::equals(pub_id)) + .select(location::select!({ id })) + .exec() + .await? + .ok_or_else(|| { + LocationError::MissingField(MissingFieldError::new("missing id of location")) + })?; + + Ok(location_id.id) } #[derive(Debug)] From e231c018fac2d1450bdbaad06ee5b43becf7d1e5 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:55:28 +0100 Subject: [PATCH 07/14] working, clean redirecting --- .../Layout/Sidebar/LocationsContextMenu.tsx | 9 ++++++++- .../library/locations/AddLocationButton.tsx | 17 +++-------------- .../library/locations/AddLocationDialog.tsx | 19 +++++++++++++------ packages/client/src/core.ts | 2 +- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/interface/app/$libraryId/Layout/Sidebar/LocationsContextMenu.tsx b/interface/app/$libraryId/Layout/Sidebar/LocationsContextMenu.tsx index 0c07dba3877f..1bbe6d5413da 100644 --- a/interface/app/$libraryId/Layout/Sidebar/LocationsContextMenu.tsx +++ b/interface/app/$libraryId/Layout/Sidebar/LocationsContextMenu.tsx @@ -1,5 +1,6 @@ import { Pencil, Plus, Trash } from '@phosphor-icons/react'; import { useNavigate } from 'react-router'; +import { useLibraryContext } from '@sd/client'; import { ContextMenu as CM, dialogManager, toast } from '@sd/ui'; import { AddLocationDialog } from '~/app/$libraryId/settings/library/locations/AddLocationDialog'; import DeleteDialog from '~/app/$libraryId/settings/library/locations/DeleteDialog'; @@ -14,6 +15,8 @@ interface Props { export default ({ children, locationId }: Props) => { const navigate = useNavigate(); const platform = usePlatform(); + const libraryId = useLibraryContext().library.uuid; + return ( { const path = await openDirectoryPickerDialog(platform); if (path !== '') { dialogManager.create((dp) => ( - + )); } } catch (error) { diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx index 3e6353a8bf94..0c3ecde7a1e3 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx @@ -31,9 +31,6 @@ export const AddLocationButton = ({ path, className, ...props }: AddLocationButt const overflowRef = useRef(null); const [isOverflowing, setIsOverflowing] = useState(false); - // if this is set, it'll be the new location id and we should redirect - const locationIdRedirect = useRef(null); - useCallbackToWatchResize(() => { const text = textRef.current; const overflow = overflowRef.current; @@ -54,18 +51,10 @@ export const AddLocationButton = ({ path, className, ...props }: AddLocationButt } // Remember `path` will be `undefined` on web cause the user has to provide it in the modal - if (path !== '') { - await dialogManager.create((dp) => ( - + if (path !== '') + dialogManager.create((dp) => ( + )); - - locationIdRedirect.current && - navigate(`/${libraryId}/location/${locationIdRedirect.current}`); - } }} {...props} > diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index 7a499a078482..1a90babd36f2 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -1,5 +1,6 @@ -import { MutableRefObject, useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { Controller, get } from 'react-hook-form'; +import { useNavigate } from 'react-router'; import { useDebouncedCallback } from 'use-debounce'; import { extractInfoRSPCError, @@ -42,7 +43,7 @@ type SchemaType = z.infer; export interface AddLocationDialog extends UseDialogProps { path: string; - redirect: MutableRefObject; + libraryId: string; method?: RemoteErrorFormMessage; } @@ -53,14 +54,13 @@ export const AddLocationDialog = ({ }: AddLocationDialog) => { const platform = usePlatform(); const submitPlausibleEvent = usePlausibleEvent(); + const navigate = useNavigate(); const listLocations = useLibraryQuery(['locations.list']); const createLocation = useLibraryMutation('locations.create'); const relinkLocation = useLibraryMutation('locations.relink'); const listIndexerRules = useLibraryQuery(['locations.indexer_rules.list']); const addLocationToLibrary = useLibraryMutation('locations.addLibrary'); - const redirect = dialogProps.redirect; - // This is required because indexRules is undefined on first render const indexerRulesIds = useMemo( () => listIndexerRules.data?.filter((rule) => rule.default).map((rule) => rule.id) ?? [], @@ -123,9 +123,16 @@ export const AddLocationDialog = ({ throw new Error('Unimplemented custom remote error handling'); } - redirect.current = shouldRedirect ? id : null; + shouldRedirect && id && navigate(`${dialogProps.libraryId}/location/${id}`); }, - [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent, redirect] + [ + createLocation, + relinkLocation, + addLocationToLibrary, + submitPlausibleEvent, + navigate, + dialogProps.libraryId + ] ); const handleAddError = useCallback( diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index 1ecb4ea4a0f5..1ec28d0cf828 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -75,7 +75,7 @@ export type Procedures = { { key: "locations.fullRescan", input: LibraryArgs, result: null } | { key: "locations.indexer_rules.create", input: LibraryArgs, result: null } | { key: "locations.indexer_rules.delete", input: LibraryArgs, result: null } | - { key: "locations.relink", input: LibraryArgs, result: null } | + { key: "locations.relink", input: LibraryArgs, result: number } | { key: "locations.subPathRescan", input: LibraryArgs, result: null } | { key: "locations.update", input: LibraryArgs, result: null } | { key: "nodes.edit", input: ChangeNodeNameArgs, result: null } | From 52189f24f0f005367fce1d17f5d97801ab03f9a6 Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue, 17 Oct 2023 14:34:38 +0300 Subject: [PATCH 08/14] accordion closing tag --- .../library/locations/AddLocationDialog.tsx | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index e1cc80be5c21..879f0e41d489 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -220,37 +220,39 @@ export const AddLocationDialog = ({ 'need to specify an absolute URL of a directory local to the remote node.' : '' } - >
- - + > + {' '} +
+ - + - + -
- - +
+ +
- - ( - - )} - control={form.control} - /> - - + + ( + + )} + control={form.control} + /> +
); From a574db28452d578741671ce916207288a1719e63 Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:28:29 +0300 Subject: [PATCH 09/14] navigate to location after adding --- interface/app/$libraryId/Explorer/store.ts | 4 +- .../library/locations/AddLocationDialog.tsx | 16 ++---- interface/app/index.tsx | 9 ++- interface/hooks/index.ts | 1 + interface/hooks/useShouldRedirect.ts | 56 +++++++++++++++++++ 5 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 interface/hooks/useShouldRedirect.ts diff --git a/interface/app/$libraryId/Explorer/store.ts b/interface/app/$libraryId/Explorer/store.ts index 451dccac6574..c20e37ec74d6 100644 --- a/interface/app/$libraryId/Explorer/store.ts +++ b/interface/app/$libraryId/Explorer/store.ts @@ -8,7 +8,8 @@ import { type ExplorerItem, type ExplorerLayout, type ExplorerSettings, - type SortOrder + type SortOrder, + JobGroup } from '@sd/client'; export enum ExplorerKind { @@ -121,6 +122,7 @@ const state = { tagAssignMode: false, showInspector: false, showMoreInfo: false, + shouldRedirectJob: {redirect: false, locationId: null} as {redirect: boolean, locationId: number | null}, mediaPlayerVolume: 0.7, newThumbnails: proxySet() as Set, cutCopyState: { type: 'Idle' } as CutCopyState, diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index 879f0e41d489..f977a771bfa3 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -11,6 +11,7 @@ import { useZodForm } from '@sd/client'; import { CheckBox, Dialog, ErrorMessage, Label, toast, useDialog, UseDialogProps, z } from '@sd/ui'; +import { getExplorerStore } from '~/app/$libraryId/Explorer/store'; import Accordion from '~/components/Accordion'; import { useCallbackToWatchForm } from '~/hooks'; import { usePlatform } from '~/util/Platform'; @@ -122,17 +123,12 @@ export const AddLocationDialog = ({ default: throw new Error('Unimplemented custom remote error handling'); } - - shouldRedirect && id && navigate(`${dialogProps.libraryId}/location/${id}`); + getExplorerStore().shouldRedirectJob = { + redirect: shouldRedirect, + locationId: id + }; }, - [ - createLocation, - relinkLocation, - addLocationToLibrary, - submitPlausibleEvent, - navigate, - dialogProps.libraryId - ] + [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent] ); const handleAddError = useCallback( diff --git a/interface/app/index.tsx b/interface/app/index.tsx index 1081611bd13e..d6d3bed8e937 100644 --- a/interface/app/index.tsx +++ b/interface/app/index.tsx @@ -1,9 +1,9 @@ -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import { Navigate, Outlet, useMatches, type RouteObject } from 'react-router-dom'; import { currentLibraryCache, useCachedLibraries, useInvalidateQuery } from '@sd/client'; -import { Dialogs, toast, Toaster } from '@sd/ui'; +import { Dialogs, Toaster } from '@sd/ui'; import { RouterErrorBoundary } from '~/ErrorFallback'; -import { useKeybindHandler, useTheme } from '~/hooks'; +import { useKeybindHandler, useShouldRedirect, useTheme } from '~/hooks'; import libraryRoutes from './$libraryId'; import onboardingRoutes from './onboarding'; @@ -11,8 +11,6 @@ import { RootContext } from './RootContext'; import './style.scss'; -import { usePlatform } from '..'; - const Index = () => { const libraries = useCachedLibraries(); @@ -31,6 +29,7 @@ const Wrapper = () => { useKeybindHandler(); useInvalidateQuery(); useTheme(); + useShouldRedirect(); const rawPath = useRawRoutePath(); diff --git a/interface/hooks/index.ts b/interface/hooks/index.ts index fa25ba9bbf07..01c57c212b7a 100644 --- a/interface/hooks/index.ts +++ b/interface/hooks/index.ts @@ -23,3 +23,4 @@ export * from './useIsTextTruncated'; export * from './useKeyMatcher'; export * from './useKeyCopyCutPaste'; export * from './useMouseNavigate'; +export * from './useShouldRedirect'; diff --git a/interface/hooks/useShouldRedirect.ts b/interface/hooks/useShouldRedirect.ts new file mode 100644 index 000000000000..28c477d81697 --- /dev/null +++ b/interface/hooks/useShouldRedirect.ts @@ -0,0 +1,56 @@ +import { getExplorerStore, useExplorerStore } from "~/app/$libraryId/Explorer/store" +import { JobGroup, useLibraryQuery } from '@sd/client'; +import { useCallback, useEffect } from "react"; +import { useNavigate } from "react-router"; +import { useZodRouteParams } from "../hooks/useZodRouteParams"; +import { LibraryIdParamsSchema } from "../app/route-schemas"; +import { useRef } from "react"; + +/** + * When a user adds a location and checks the should redirect box, + * this hook will redirect them to the location + * once the indexer has been invoked + */ + +export const useShouldRedirect = () => { + const { shouldRedirectJob } = useExplorerStore(); + const navigate = useNavigate(); + const { libraryId } = useZodRouteParams(LibraryIdParamsSchema); + const jobGroups = useLibraryQuery(['jobs.reports'], { + enabled: !!shouldRedirectJob.redirect && !!shouldRedirectJob.locationId, + refetchOnWindowFocus: false, + }); + const cacheJobGroup = useRef(undefined); + + const lookForJob = useCallback(() => { + let started = false; + if (!jobGroups.data) return false; + const newestJobGRoup = jobGroups.data[0] + if (newestJobGRoup) { + for (const job of newestJobGRoup.jobs) { + if (job.name === 'indexer' && job.completed_task_count !== 0) { + started = true; + break; + } + } + } + return started; + } + , [jobGroups.data]); + + useEffect(() => { + cacheJobGroup.current = jobGroups.data?.[0]; + }, [jobGroups.data]) + + useEffect(() => { + if (!shouldRedirectJob.redirect || !shouldRedirectJob.locationId) return; + const indexerJobFound = lookForJob(); + if (cacheJobGroup.current !== undefined) { + if (indexerJobFound && shouldRedirectJob.locationId) { + cacheJobGroup.current = undefined; + navigate(`/${libraryId}/location/${shouldRedirectJob.locationId}`); + getExplorerStore().shouldRedirectJob = { redirect: false, locationId: null }; + } + } + }, [lookForJob, shouldRedirectJob, navigate, libraryId]) +} From 038cfe6c05dda36d65b88bd66a92b684663d5e37 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Wed, 18 Oct 2023 03:59:51 +0100 Subject: [PATCH 10/14] chore: remove erroneous `{' '}` --- .../$libraryId/settings/library/locations/AddLocationDialog.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index f977a771bfa3..c5db6c21bb90 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -217,7 +217,6 @@ export const AddLocationDialog = ({ : '' } > - {' '}
Date: Fri, 20 Oct 2023 17:33:07 +0300 Subject: [PATCH 11/14] multiple location redirect support --- interface/app/$libraryId/Explorer/store.ts | 2 +- .../library/locations/AddLocationDialog.tsx | 10 +-- interface/hooks/useShouldRedirect.ts | 64 ++++++++++--------- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/interface/app/$libraryId/Explorer/store.ts b/interface/app/$libraryId/Explorer/store.ts index c20e37ec74d6..632c9b700329 100644 --- a/interface/app/$libraryId/Explorer/store.ts +++ b/interface/app/$libraryId/Explorer/store.ts @@ -122,7 +122,7 @@ const state = { tagAssignMode: false, showInspector: false, showMoreInfo: false, - shouldRedirectJob: {redirect: false, locationId: null} as {redirect: boolean, locationId: number | null}, + jobsToRedirect: [] as {locationId: number | null}[], mediaPlayerVolume: 0.7, newThumbnails: proxySet() as Set, cutCopyState: { type: 'Idle' } as CutCopyState, diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index c5db6c21bb90..1e00d4531a7e 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -11,7 +11,7 @@ import { useZodForm } from '@sd/client'; import { CheckBox, Dialog, ErrorMessage, Label, toast, useDialog, UseDialogProps, z } from '@sd/ui'; -import { getExplorerStore } from '~/app/$libraryId/Explorer/store'; +import { getExplorerStore, useExplorerStore } from '~/app/$libraryId/Explorer/store'; import Accordion from '~/components/Accordion'; import { useCallbackToWatchForm } from '~/hooks'; import { usePlatform } from '~/util/Platform'; @@ -123,10 +123,10 @@ export const AddLocationDialog = ({ default: throw new Error('Unimplemented custom remote error handling'); } - getExplorerStore().shouldRedirectJob = { - redirect: shouldRedirect, - locationId: id - }; + getExplorerStore().jobsToRedirect = [ + { locationId: id }, + ...getExplorerStore().jobsToRedirect + ]; }, [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent] ); diff --git a/interface/hooks/useShouldRedirect.ts b/interface/hooks/useShouldRedirect.ts index 28c477d81697..6ef1834b222d 100644 --- a/interface/hooks/useShouldRedirect.ts +++ b/interface/hooks/useShouldRedirect.ts @@ -1,11 +1,9 @@ import { getExplorerStore, useExplorerStore } from "~/app/$libraryId/Explorer/store" -import { JobGroup, useLibraryQuery } from '@sd/client'; +import { useLibraryQuery} from '@sd/client'; import { useCallback, useEffect } from "react"; import { useNavigate } from "react-router"; import { useZodRouteParams } from "../hooks/useZodRouteParams"; import { LibraryIdParamsSchema } from "../app/route-schemas"; -import { useRef } from "react"; - /** * When a user adds a location and checks the should redirect box, * this hook will redirect them to the location @@ -13,44 +11,48 @@ import { useRef } from "react"; */ export const useShouldRedirect = () => { - const { shouldRedirectJob } = useExplorerStore(); + const { jobsToRedirect } = useExplorerStore(); const navigate = useNavigate(); const { libraryId } = useZodRouteParams(LibraryIdParamsSchema); const jobGroups = useLibraryQuery(['jobs.reports'], { - enabled: !!shouldRedirectJob.redirect && !!shouldRedirectJob.locationId, + enabled: !!(jobsToRedirect.length > 0), refetchOnWindowFocus: false, }); - const cacheJobGroup = useRef(undefined); - - const lookForJob = useCallback(() => { - let started = false; - if (!jobGroups.data) return false; - const newestJobGRoup = jobGroups.data[0] - if (newestJobGRoup) { - for (const job of newestJobGRoup.jobs) { - if (job.name === 'indexer' && job.completed_task_count !== 0) { - started = true; - break; + + //We loop all job groups and pull the first job that matches the location id from the job group + + const pullMatchingJob = useCallback(() => { + if (jobsToRedirect.length === 0) return; + let jobFound + if (jobGroups.data) { + for (const jobGroup of jobGroups.data) { + for (const job of jobGroup.jobs) { + if (job.name === 'indexer') { + const locationId = jobsToRedirect.find((l) => l.locationId === job.metadata.location.id)?.locationId + if (job.metadata.location.id === locationId && job.completed_task_count > 0) { + jobFound = job; + break; + } + } } } } - return started; - } - , [jobGroups.data]); + return jobFound + }, [jobGroups.data, jobsToRedirect]) - useEffect(() => { - cacheJobGroup.current = jobGroups.data?.[0]; - }, [jobGroups.data]) + //Once we have a matching job, we redirect the user to the location useEffect(() => { - if (!shouldRedirectJob.redirect || !shouldRedirectJob.locationId) return; - const indexerJobFound = lookForJob(); - if (cacheJobGroup.current !== undefined) { - if (indexerJobFound && shouldRedirectJob.locationId) { - cacheJobGroup.current = undefined; - navigate(`/${libraryId}/location/${shouldRedirectJob.locationId}`); - getExplorerStore().shouldRedirectJob = { redirect: false, locationId: null }; - } + if (jobGroups.data) { + const matchingJob = pullMatchingJob(); + if (matchingJob) { + const locationId = jobsToRedirect.find((l) => l.locationId === matchingJob.metadata.location.id)?.locationId + navigate(`/${libraryId}/location/${locationId}`); + getExplorerStore().jobsToRedirect = jobsToRedirect.filter((l) => l.locationId !== matchingJob.metadata.location.id); } - }, [lookForJob, shouldRedirectJob, navigate, libraryId]) + } + }, [jobGroups.data, pullMatchingJob, navigate, libraryId, jobsToRedirect]) + } + + From 26a7b4a94e5d693b9f243b19c4dc322cf4b959bd Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:40:28 +0300 Subject: [PATCH 12/14] oops missed check --- .../settings/library/locations/AddLocationDialog.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index 1e00d4531a7e..5ead9bdae2db 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -123,10 +123,12 @@ export const AddLocationDialog = ({ default: throw new Error('Unimplemented custom remote error handling'); } - getExplorerStore().jobsToRedirect = [ - { locationId: id }, - ...getExplorerStore().jobsToRedirect - ]; + if (shouldRedirect) { + getExplorerStore().jobsToRedirect = [ + { locationId: id }, + ...getExplorerStore().jobsToRedirect + ]; + } }, [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent] ); From ba817edf5d78baf052dcd9dca8b035fa25922b04 Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:48:15 +0300 Subject: [PATCH 13/14] cleanup --- .../settings/library/locations/AddLocationDialog.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx index 5ead9bdae2db..3dbaaf30fe0f 100644 --- a/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx +++ b/interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx @@ -55,12 +55,12 @@ export const AddLocationDialog = ({ }: AddLocationDialog) => { const platform = usePlatform(); const submitPlausibleEvent = usePlausibleEvent(); - const navigate = useNavigate(); const listLocations = useLibraryQuery(['locations.list']); const createLocation = useLibraryMutation('locations.create'); const relinkLocation = useLibraryMutation('locations.relink'); const listIndexerRules = useLibraryQuery(['locations.indexer_rules.list']); const addLocationToLibrary = useLibraryMutation('locations.addLibrary'); + const explorerStore = useExplorerStore(); // This is required because indexRules is undefined on first render const indexerRulesIds = useMemo( @@ -126,11 +126,11 @@ export const AddLocationDialog = ({ if (shouldRedirect) { getExplorerStore().jobsToRedirect = [ { locationId: id }, - ...getExplorerStore().jobsToRedirect + ...explorerStore.jobsToRedirect ]; } }, - [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent] + [createLocation, relinkLocation, addLocationToLibrary, submitPlausibleEvent, explorerStore] ); const handleAddError = useCallback( From eba208372a27d37fcf02671c158d443c227bb452 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Fri, 20 Oct 2023 21:46:04 +0100 Subject: [PATCH 14/14] fix bad merge --- core/src/location/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/location/mod.rs b/core/src/location/mod.rs index a0ce6db71942..cd7fa9dc8304 100644 --- a/core/src/location/mod.rs +++ b/core/src/location/mod.rs @@ -533,8 +533,6 @@ pub async fn relink_location( Library { db, id, sync, .. }: &Library, location_path: impl AsRef, ) -> Result { - let Library { db, id, sync, .. } = &**library; - let location_path = location_path.as_ref(); let mut metadata = SpacedriveLocationMetadataFile::try_load(&location_path) .await? @@ -549,7 +547,7 @@ pub async fn relink_location( .ok_or_else(|| NonUtf8PathError(location_path.into()))?; sync.write_op( - db, + &db, sync.shared_update( prisma_sync::location::SyncId { pub_id: pub_id.clone(),