mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-13 14:14:04 +00:00
Cloud Sync Done
This commit is contained in:
parent
449fc43d30
commit
442bbe69fc
|
@ -15,6 +15,7 @@ import { ModalInput } from '~/components/primitive/Input';
|
||||||
import useForwardedRef from '~/hooks/useForwardedRef';
|
import useForwardedRef from '~/hooks/useForwardedRef';
|
||||||
import { tw } from '~/lib/tailwind';
|
import { tw } from '~/lib/tailwind';
|
||||||
import { currentLibraryStore } from '~/utils/nav';
|
import { currentLibraryStore } from '~/utils/nav';
|
||||||
|
import Card from '../layout/Card';
|
||||||
|
|
||||||
const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
|
const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
@ -22,34 +23,10 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { libraries } = useClientContext();
|
const { libraries } = useClientContext();
|
||||||
const [libName, setLibName] = useState('');
|
|
||||||
|
|
||||||
const submitPlausibleEvent = usePlausibleEvent();
|
|
||||||
|
|
||||||
const cloudLibraries = useBridgeQuery(['cloud.library.list']);
|
const cloudLibraries = useBridgeQuery(['cloud.library.list']);
|
||||||
const joinLibrary = useBridgeMutation(['cloud.library.join']);
|
const joinLibrary = useBridgeMutation(['cloud.library.join']);
|
||||||
|
|
||||||
// const { mutate: createLibrary, isLoading: createLibLoading } = useBridgeMutation(
|
|
||||||
// 'library.create',
|
|
||||||
// {
|
|
||||||
// onSuccess: (lib) => {
|
|
||||||
// // Reset form
|
|
||||||
// setLibName('');
|
|
||||||
|
|
||||||
// // We do this instead of invalidating the query because it triggers a full app re-render??
|
|
||||||
// insertLibrary(queryClient, lib);
|
|
||||||
|
|
||||||
// // Switch to the new library
|
|
||||||
// currentLibraryStore.id = lib.uuid;
|
|
||||||
|
|
||||||
// submitPlausibleEvent({ event: { type: 'libraryCreate' } });
|
|
||||||
// },
|
|
||||||
// onSettled: () => {
|
|
||||||
// modalRef.current?.dismiss();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (cloudLibraries.isLoading)
|
if (cloudLibraries.isLoading)
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -57,10 +34,6 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||||
snapPoints={['30']}
|
snapPoints={['30']}
|
||||||
title="Join a Cloud Library"
|
title="Join a Cloud Library"
|
||||||
description="Connect to one of your cloud libraries."
|
description="Connect to one of your cloud libraries."
|
||||||
onDismiss={() => {
|
|
||||||
// Resets form onDismiss
|
|
||||||
setLibName('');
|
|
||||||
}}
|
|
||||||
showCloseButton
|
showCloseButton
|
||||||
// Disable panning gestures
|
// Disable panning gestures
|
||||||
enableHandlePanningGesture={false}
|
enableHandlePanningGesture={false}
|
||||||
|
@ -86,10 +59,6 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||||
snapPoints={['30']}
|
snapPoints={['30']}
|
||||||
title="Join a Cloud Library"
|
title="Join a Cloud Library"
|
||||||
description="Connect to one of your cloud libraries."
|
description="Connect to one of your cloud libraries."
|
||||||
onDismiss={() => {
|
|
||||||
// Resets form onDismiss
|
|
||||||
setLibName('');
|
|
||||||
}}
|
|
||||||
showCloseButton
|
showCloseButton
|
||||||
// Disable panning gestures
|
// Disable panning gestures
|
||||||
enableHandlePanningGesture={false}
|
enableHandlePanningGesture={false}
|
||||||
|
@ -101,11 +70,11 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||||
(cloudLibrary) => !libraries.data?.find((l) => l.uuid === cloudLibrary.uuid)
|
(cloudLibrary) => !libraries.data?.find((l) => l.uuid === cloudLibrary.uuid)
|
||||||
)
|
)
|
||||||
.map((cloudLibrary) => (
|
.map((cloudLibrary) => (
|
||||||
<View
|
<Card
|
||||||
key={cloudLibrary.uuid}
|
key={cloudLibrary.uuid}
|
||||||
style={tw`flex flex-row items-center gap-2 rounded-lg bg-gray-600 p-2`}
|
style={tw`flex flex-row items-center gap-2 rounded-lg bg-gray-600 p-2`}
|
||||||
>
|
>
|
||||||
<Text style={tw`text-xl font-bold text-ink`}>{cloudLibrary.name}</Text>
|
<Text style={tw`text-lg font-bold text-ink`}>{cloudLibrary.name}</Text>
|
||||||
<View style={tw`flex flex-row gap-2`}>
|
<View style={tw`flex flex-row gap-2`}>
|
||||||
<Button
|
<Button
|
||||||
variant="accent"
|
variant="accent"
|
||||||
|
@ -153,7 +122,7 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Gear, Lock, Plus } from '@phosphor-icons/react';
|
import { CloudArrowDown, Gear, Lock, Plus } from '@phosphor-icons/react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useClientContext } from '@sd/client';
|
import { useClientContext } from '@sd/client';
|
||||||
import { dialogManager, Dropdown, DropdownMenu } from '@sd/ui';
|
import { dialogManager, Dropdown, DropdownMenu } from '@sd/ui';
|
||||||
|
@ -6,6 +6,7 @@ import { useLocale } from '~/hooks';
|
||||||
|
|
||||||
import CreateDialog from '../../../settings/node/libraries/CreateDialog';
|
import CreateDialog from '../../../settings/node/libraries/CreateDialog';
|
||||||
import { useSidebarContext } from './Context';
|
import { useSidebarContext } from './Context';
|
||||||
|
import JoinDialog from '~/app/$libraryId/settings/node/libraries/JoinDialog';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const { library, libraries, currentLibraryId } = useClientContext();
|
const { library, libraries, currentLibraryId } = useClientContext();
|
||||||
|
@ -62,6 +63,13 @@ export default () => {
|
||||||
onClick={() => dialogManager.create((dp) => <CreateDialog {...dp} />)}
|
onClick={() => dialogManager.create((dp) => <CreateDialog {...dp} />)}
|
||||||
className="font-medium"
|
className="font-medium"
|
||||||
/>
|
/>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
label={t('join_library')}
|
||||||
|
icon={CloudArrowDown}
|
||||||
|
iconProps={{ weight: 'bold', size: 16 }}
|
||||||
|
onClick={() => dialogManager.create((dp) => <JoinDialog librariesCtx={libraries.data} {...dp} />)}
|
||||||
|
className="font-medium"
|
||||||
|
/>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
label={t('manage_library')}
|
label={t('manage_library')}
|
||||||
icon={Gear}
|
icon={Gear}
|
||||||
|
|
105
interface/app/$libraryId/settings/node/libraries/JoinDialog.tsx
Normal file
105
interface/app/$libraryId/settings/node/libraries/JoinDialog.tsx
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import {
|
||||||
|
LibraryConfigWrapped,
|
||||||
|
useBridgeMutation,
|
||||||
|
useBridgeQuery,
|
||||||
|
useClientContext,
|
||||||
|
useLibraryContext,
|
||||||
|
usePlausibleEvent,
|
||||||
|
useZodForm
|
||||||
|
} from '@sd/client';
|
||||||
|
import { Button, Dialog, Select, SelectOption, toast, useDialog, UseDialogProps, z } from '@sd/ui';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { useNavigate } from 'react-router';
|
||||||
|
import { useLocale } from '~/hooks';
|
||||||
|
import { usePlatform } from '~/util/Platform';
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
libraryId: z.string().refine((value) => value !== 'select_library', {
|
||||||
|
message: 'Please select a library'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default (props: UseDialogProps & { librariesCtx: LibraryConfigWrapped[] | undefined }) => {
|
||||||
|
const cloudLibraries = useBridgeQuery(['cloud.library.list']);
|
||||||
|
const joinLibrary = useBridgeMutation(['cloud.library.join']);
|
||||||
|
|
||||||
|
const { t } = useLocale();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const platform = usePlatform();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const form = useZodForm({ schema, defaultValues: { libraryId: 'select_library' } });
|
||||||
|
|
||||||
|
// const queryClient = useQueryClient();
|
||||||
|
// const submitPlausibleEvent = usePlausibleEvent();
|
||||||
|
// const platform = usePlatform();
|
||||||
|
|
||||||
|
const onSubmit = form.handleSubmit(async (data) => {
|
||||||
|
try {
|
||||||
|
const library = await joinLibrary.mutateAsync(data.libraryId);
|
||||||
|
|
||||||
|
queryClient.setQueryData(['library.list'], (libraries: any) => {
|
||||||
|
// The invalidation system beat us to it
|
||||||
|
if ((libraries || []).find((l: any) => l.uuid === library.uuid)) return libraries;
|
||||||
|
|
||||||
|
return [...(libraries || []), library];
|
||||||
|
});
|
||||||
|
|
||||||
|
platform.refreshMenuBar && platform.refreshMenuBar();
|
||||||
|
|
||||||
|
navigate(`/${library.uuid}`, { replace: true });
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(e);
|
||||||
|
toast.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
form={form}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
dialog={useDialog(props)}
|
||||||
|
submitDisabled={!form.formState.isValid}
|
||||||
|
title={t('join_library')}
|
||||||
|
closeLabel={t('close')}
|
||||||
|
cancelLabel={t('cancel')}
|
||||||
|
description={t('join_library_description')}
|
||||||
|
ctaLabel={form.formState.isSubmitting ? t('joining') : t('join')}
|
||||||
|
>
|
||||||
|
<div className="mt-5 space-y-4">
|
||||||
|
{cloudLibraries.isLoading && <span>{t('loading')}...</span>}
|
||||||
|
{cloudLibraries.data && (
|
||||||
|
<Select
|
||||||
|
value={form.watch('libraryId')}
|
||||||
|
size="sm"
|
||||||
|
className="w-full"
|
||||||
|
onChange={(key) => {
|
||||||
|
console.log('Key:', key);
|
||||||
|
// Update the form value
|
||||||
|
form.setValue('libraryId', key, {
|
||||||
|
shouldValidate: true
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectOption value="select_library">
|
||||||
|
{t('select_library')}
|
||||||
|
</SelectOption>
|
||||||
|
{cloudLibraries.data
|
||||||
|
.filter(
|
||||||
|
(cloudLibrary) =>
|
||||||
|
!props.librariesCtx?.find(
|
||||||
|
(l: any) => l.uuid === cloudLibrary.uuid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map((cloudLibrary) => (
|
||||||
|
<SelectOption key={cloudLibrary.uuid} value={cloudLibrary.uuid}>
|
||||||
|
{cloudLibrary.name}
|
||||||
|
</SelectOption>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,16 +1,25 @@
|
||||||
import { useBridgeQuery, useLibraryContext } from '@sd/client';
|
import { useRef } from 'react';
|
||||||
import { Button, dialogManager } from '@sd/ui';
|
import { useNavigate } from 'react-router';
|
||||||
|
import { useBridgeQuery, useClientContext, useFeatureFlag, useLibraryContext, useZodForm } from '@sd/client';
|
||||||
|
import { Button, Dialog, dialogManager, useDialog, z } from '@sd/ui';
|
||||||
import { useLocale } from '~/hooks';
|
import { useLocale } from '~/hooks';
|
||||||
|
|
||||||
import { Heading } from '../../Layout';
|
import { Heading } from '../../Layout';
|
||||||
import CreateDialog from './CreateDialog';
|
import CreateDialog from './CreateDialog';
|
||||||
|
import JoinDialog from './JoinDialog';
|
||||||
import ListItem from './ListItem';
|
import ListItem from './ListItem';
|
||||||
|
|
||||||
export const Component = () => {
|
export const Component = () => {
|
||||||
const librariesQuery = useBridgeQuery(['library.list']);
|
const librariesQuery = useBridgeQuery(['library.list']);
|
||||||
|
const cloudLibrariesQuery = useBridgeQuery(['cloud.library.list']);
|
||||||
const libraries = librariesQuery.data;
|
const libraries = librariesQuery.data;
|
||||||
|
const cloudLibraries = cloudLibrariesQuery.data;
|
||||||
|
|
||||||
|
const cloudEnabled = useFeatureFlag('cloudSync');
|
||||||
|
|
||||||
const { library } = useLibraryContext();
|
const { library } = useLibraryContext();
|
||||||
|
const { libraries: librariesCtx } = useClientContext();
|
||||||
|
const librariesCtxData = librariesCtx.data;
|
||||||
|
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
|
@ -30,10 +39,20 @@ export const Component = () => {
|
||||||
>
|
>
|
||||||
{t('add_library')}
|
{t('add_library')}
|
||||||
</Button>
|
</Button>
|
||||||
|
{cloudEnabled && (
|
||||||
|
<Button
|
||||||
|
variant="accent"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
dialogManager.create((dp) => <JoinDialog librariesCtx={librariesCtxData} {...dp} />);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('join_library')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{libraries
|
{libraries
|
||||||
?.sort((a, b) => {
|
?.sort((a, b) => {
|
||||||
|
@ -49,6 +68,22 @@ export const Component = () => {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Heading title={'Cloud Libraries'} description={'Cloud Libraries'} />
|
||||||
|
{cloudLibraries
|
||||||
|
?.sort((a, b) => {
|
||||||
|
if (a.uuid === library.uuid) return -1;
|
||||||
|
if (b.uuid === library.uuid) return 1;
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
.map((lib) => (
|
||||||
|
<ListItem
|
||||||
|
current={lib.uuid === library.uuid}
|
||||||
|
key={lib.uuid}
|
||||||
|
library={library}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -356,6 +356,7 @@
|
||||||
"join_discord": "Join Discord",
|
"join_discord": "Join Discord",
|
||||||
"join_library": "Join a Library",
|
"join_library": "Join a Library",
|
||||||
"join_library_description": "Libraries are a secure, on-device database. Your files remain where they are, the Library catalogs them and stores all Spacedrive related data.",
|
"join_library_description": "Libraries are a secure, on-device database. Your files remain where they are, the Library catalogs them and stores all Spacedrive related data.",
|
||||||
|
"joining": "Joining",
|
||||||
"key": "Key",
|
"key": "Key",
|
||||||
"key_manager": "Key Manager",
|
"key_manager": "Key Manager",
|
||||||
"key_manager_description": "Create encryption keys, mount and unmount your keys to see files decrypted on the fly.",
|
"key_manager_description": "Create encryption keys, mount and unmount your keys to see files decrypted on the fly.",
|
||||||
|
@ -629,6 +630,7 @@
|
||||||
"size_tb": "TB",
|
"size_tb": "TB",
|
||||||
"size_tbs": "TBs",
|
"size_tbs": "TBs",
|
||||||
"skip_login": "Skip login",
|
"skip_login": "Skip login",
|
||||||
|
"select_library": "Select a Cloud Library",
|
||||||
"software": "Software",
|
"software": "Software",
|
||||||
"sort_by": "Sort by",
|
"sort_by": "Sort by",
|
||||||
"spacedrive_account": "Spacedrive Account",
|
"spacedrive_account": "Spacedrive Account",
|
||||||
|
|
Loading…
Reference in a new issue