Basic sync backfill dialog (#2405)

* basic sync backfill dialog

* only call enableSync once

* rename sync enable to backfill

* disallow closing dialog
This commit is contained in:
Brendan Allan
2024-04-30 12:42:44 +08:00
committed by GitHub
parent 1b6b8973d4
commit ce669f203c
4 changed files with 67 additions and 35 deletions

View File

@@ -34,7 +34,7 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
.await?)
})
})
.procedure("enable", {
.procedure("backfill", {
R.with2(library())
.mutation(|(node, library), _: ()| async move {
if library

View File

@@ -1,12 +1,13 @@
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import {
CRDTOperation,
CRDTOperationData,
useLibraryMutation,
useLibraryQuery,
useLibrarySubscription
useLibrarySubscription,
useZodForm
} from '@sd/client';
import { Button } from '@sd/ui';
import { Button, Dialog, dialogManager, useDialog, UseDialogProps, z } from '@sd/ui';
import { useRouteTitle } from '~/hooks/useRouteTitle';
type MessageGroup = {
@@ -21,7 +22,7 @@ export const Component = () => {
const syncEnabled = useLibraryQuery(['sync.enabled']);
const messages = useLibraryQuery(['sync.messages']);
const enableSync = useLibraryMutation(['sync.enable'], {
const backfillSync = useLibraryMutation(['sync.backfill'], {
onSuccess: async () => {
await syncEnabled.refetch();
await messages.refetch();
@@ -32,18 +33,17 @@ export const Component = () => {
onData: () => messages.refetch()
});
const groups = useMemo(
() => (messages.data && calculateGroups(messages.data)) || [],
[messages]
);
const groups = useMemo(() => (messages.data && calculateGroups(messages.data)) || [], [messages]);
return (
<ul className="space-y-4 p-4">
{!syncEnabled.data && (
<Button
variant="accent"
onClick={() => enableSync.mutate(null)}
disabled={enableSync.isLoading}
onClick={() => {
dialogManager.create((dialogProps) => <SyncBackfillDialog {...dialogProps} />);
}}
disabled={backfillSync.isLoading}
>
Enable sync messages
</Button>
@@ -127,3 +127,32 @@ function calculateGroups(messages: CRDTOperation[]) {
return acc;
}, []);
}
function SyncBackfillDialog(props: UseDialogProps) {
const form = useZodForm({ schema: z.object({}) });
const dialog = useDialog(props);
const enableSync = useLibraryMutation(['sync.backfill'], {});
// dialog is in charge of enabling sync
useEffect(() => {
form.handleSubmit(
async () => {
await enableSync.mutateAsync(null).then(() => (dialog.state.open = false));
},
() => {}
)();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<Dialog
title="Backfilling Sync Operations"
description="Library is paused until backfill completes"
form={form}
dialog={dialog}
hideButtons
ignoreClickOutside
/>
);
}

View File

@@ -125,7 +125,7 @@ export type Procedures = {
{ key: "search.saved.create", input: LibraryArgs<{ name: string; target?: SearchTarget; search?: string | null; filters?: string | null; description?: string | null; icon?: string | null }>, result: null } |
{ key: "search.saved.delete", input: LibraryArgs<number>, result: null } |
{ key: "search.saved.update", input: LibraryArgs<[number, Args]>, result: null } |
{ key: "sync.enable", input: LibraryArgs<null>, result: null } |
{ key: "sync.backfill", input: LibraryArgs<null>, result: null } |
{ key: "tags.assign", input: LibraryArgs<{ targets: Target[]; tag_id: number; unassign: boolean }>, result: null } |
{ key: "tags.create", input: LibraryArgs<TagCreateArgs>, result: Tag } |
{ key: "tags.delete", input: LibraryArgs<number>, result: null } |

View File

@@ -137,6 +137,8 @@ export interface DialogProps<S extends FieldValues>
errorMessageException?: string; //this is to bypass a specific form error message if it starts with a specific string
formClassName?: string;
icon?: ReactNode;
hideButtons?: boolean;
ignoreClickOutside?: boolean;
}
export function Dialog<S extends FieldValues>({
@@ -269,6 +271,7 @@ export function Dialog<S extends FieldValues>({
<AnimatedDialogContent
className="!pointer-events-none fixed inset-0 z-50 grid place-items-center overflow-y-auto"
style={styles}
onInteractOutside={(e) => props.ignoreClickOutside && e.preventDefault()}
>
<Form
form={form}
@@ -302,30 +305,30 @@ export function Dialog<S extends FieldValues>({
)}
>
{form.formState.isSubmitting && <Loader />}
{props.buttonsSideContent && (
<div>{props.buttonsSideContent}</div>
)}
{props.buttonsSideContent && <div>{props.buttonsSideContent}</div>}
<div className="grow" />
<div
className={clsx(
invertButtonFocus ? 'flex-row-reverse' : ' flex-row',
'flex gap-2'
)}
>
{invertButtonFocus ? (
<>
{submitButton}
{props.cancelBtn && cancelButton}
{onCancelled && closeButton}
</>
) : (
<>
{onCancelled && closeButton}
{props.cancelBtn && cancelButton}
{submitButton}
</>
)}
</div>
{!props.hideButtons && (
<div
className={clsx(
invertButtonFocus ? 'flex-row-reverse' : ' flex-row',
'flex gap-2'
)}
>
{invertButtonFocus ? (
<>
{submitButton}
{props.cancelBtn && cancelButton}
{onCancelled && closeButton}
</>
) : (
<>
{onCancelled && closeButton}
{props.cancelBtn && cancelButton}
{submitButton}
</>
)}
</div>
)}
</div>
</Form>
<Remover id={dialog.id} />