Cloud Sync Done

This commit is contained in:
Arnab Chakraborty 2024-06-10 16:28:04 +05:30
parent 449fc43d30
commit 442bbe69fc
5 changed files with 158 additions and 39 deletions

View file

@ -15,6 +15,7 @@ import { ModalInput } from '~/components/primitive/Input';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw } from '~/lib/tailwind';
import { currentLibraryStore } from '~/utils/nav';
import Card from '../layout/Card';
const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
const navigation = useNavigation();
@ -22,34 +23,10 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
const queryClient = useQueryClient();
const { libraries } = useClientContext();
const [libName, setLibName] = useState('');
const submitPlausibleEvent = usePlausibleEvent();
const cloudLibraries = useBridgeQuery(['cloud.library.list']);
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)
return (
<Modal
@ -57,10 +34,6 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
snapPoints={['30']}
title="Join a Cloud Library"
description="Connect to one of your cloud libraries."
onDismiss={() => {
// Resets form onDismiss
setLibName('');
}}
showCloseButton
// Disable panning gestures
enableHandlePanningGesture={false}
@ -86,10 +59,6 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
snapPoints={['30']}
title="Join a Cloud Library"
description="Connect to one of your cloud libraries."
onDismiss={() => {
// Resets form onDismiss
setLibName('');
}}
showCloseButton
// Disable panning gestures
enableHandlePanningGesture={false}
@ -101,11 +70,11 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
(cloudLibrary) => !libraries.data?.find((l) => l.uuid === cloudLibrary.uuid)
)
.map((cloudLibrary) => (
<View
<Card
key={cloudLibrary.uuid}
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`}>
<Button
variant="accent"
@ -153,7 +122,7 @@ const ImportModalLibrary = forwardRef<ModalRef, unknown>((_, ref) => {
</Text>
</Button>
</View>
</View>
</Card>
))}
</View>
</Modal>

View file

@ -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 { useClientContext } from '@sd/client';
import { dialogManager, Dropdown, DropdownMenu } from '@sd/ui';
@ -6,6 +6,7 @@ import { useLocale } from '~/hooks';
import CreateDialog from '../../../settings/node/libraries/CreateDialog';
import { useSidebarContext } from './Context';
import JoinDialog from '~/app/$libraryId/settings/node/libraries/JoinDialog';
export default () => {
const { library, libraries, currentLibraryId } = useClientContext();
@ -62,6 +63,13 @@ export default () => {
onClick={() => dialogManager.create((dp) => <CreateDialog {...dp} />)}
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
label={t('manage_library')}
icon={Gear}

View 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>
);
};

View file

@ -1,16 +1,25 @@
import { useBridgeQuery, useLibraryContext } from '@sd/client';
import { Button, dialogManager } from '@sd/ui';
import { useRef } from 'react';
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 { Heading } from '../../Layout';
import CreateDialog from './CreateDialog';
import JoinDialog from './JoinDialog';
import ListItem from './ListItem';
export const Component = () => {
const librariesQuery = useBridgeQuery(['library.list']);
const cloudLibrariesQuery = useBridgeQuery(['cloud.library.list']);
const libraries = librariesQuery.data;
const cloudLibraries = cloudLibrariesQuery.data;
const cloudEnabled = useFeatureFlag('cloudSync');
const { library } = useLibraryContext();
const { libraries: librariesCtx } = useClientContext();
const librariesCtxData = librariesCtx.data;
const { t } = useLocale();
@ -30,10 +39,20 @@ export const Component = () => {
>
{t('add_library')}
</Button>
{cloudEnabled && (
<Button
variant="accent"
size="sm"
onClick={() => {
dialogManager.create((dp) => <JoinDialog librariesCtx={librariesCtxData} {...dp} />);
}}
>
{t('join_library')}
</Button>
)}
</div>
}
/>
<div className="space-y-2">
{libraries
?.sort((a, b) => {
@ -49,6 +68,22 @@ export const Component = () => {
/>
))}
</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>
</>
);
};

View file

@ -356,6 +356,7 @@
"join_discord": "Join Discord",
"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.",
"joining": "Joining",
"key": "Key",
"key_manager": "Key Manager",
"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_tbs": "TBs",
"skip_login": "Skip login",
"select_library": "Select a Cloud Library",
"software": "Software",
"sort_by": "Sort by",
"spacedrive_account": "Spacedrive Account",