mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 11:47:47 +01:00
Use tanstackdb for folder creation
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
import { WorkspaceMutationHandlerBase } from '@colanode/client/handlers/mutations/workspace-mutation-handler-base';
|
||||
import { MutationHandler } from '@colanode/client/lib/types';
|
||||
import {
|
||||
FolderCreateMutationInput,
|
||||
FolderCreateMutationOutput,
|
||||
} from '@colanode/client/mutations';
|
||||
import { FolderAttributes, generateId, IdType } from '@colanode/core';
|
||||
|
||||
export class FolderCreateMutationHandler
|
||||
extends WorkspaceMutationHandlerBase
|
||||
implements MutationHandler<FolderCreateMutationInput>
|
||||
{
|
||||
async handleMutation(
|
||||
input: FolderCreateMutationInput
|
||||
): Promise<FolderCreateMutationOutput> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
|
||||
const id = generateId(IdType.Folder);
|
||||
const attributes: FolderAttributes = {
|
||||
type: 'folder',
|
||||
parentId: input.parentId,
|
||||
name: input.name,
|
||||
avatar: input.avatar,
|
||||
};
|
||||
|
||||
await workspace.nodes.createNode({
|
||||
id,
|
||||
attributes,
|
||||
parentId: input.parentId,
|
||||
});
|
||||
|
||||
return {
|
||||
id: id,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import { DocumentUpdateMutationHandler } from './documents/document-update';
|
||||
import { FileCreateMutationHandler } from './files/file-create';
|
||||
import { FileDownloadMutationHandler } from './files/file-download';
|
||||
import { TempFileCreateMutationHandler } from './files/temp-file-create';
|
||||
import { FolderCreateMutationHandler } from './folders/folder-create';
|
||||
import { FolderUpdateMutationHandler } from './folders/folder-update';
|
||||
import { MessageCreateMutationHandler } from './messages/message-create';
|
||||
import { NodeCollaboratorCreateMutationHandler } from './nodes/node-collaborator-create';
|
||||
@@ -117,7 +116,6 @@ export const buildMutationHandlerMap = (
|
||||
'workspace.update': new WorkspaceUpdateMutationHandler(app),
|
||||
'avatar.upload': new AvatarUploadMutationHandler(app),
|
||||
'account.logout': new AccountLogoutMutationHandler(app),
|
||||
'folder.create': new FolderCreateMutationHandler(app),
|
||||
'file.create': new FileCreateMutationHandler(app),
|
||||
'file.download': new FileDownloadMutationHandler(app),
|
||||
'space.update': new SpaceUpdateMutationHandler(app),
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export type FolderCreateMutationInput = {
|
||||
type: 'folder.create';
|
||||
userId: string;
|
||||
parentId: string;
|
||||
name: string;
|
||||
avatar?: string | null;
|
||||
generateIndex: boolean;
|
||||
};
|
||||
|
||||
export type FolderCreateMutationOutput = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/mutations' {
|
||||
interface MutationMap {
|
||||
'folder.create': {
|
||||
input: FolderCreateMutationInput;
|
||||
output: FolderCreateMutationOutput;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ export * from './databases/database-name-field-update';
|
||||
export * from './documents/document-update';
|
||||
export * from './files/file-create';
|
||||
export * from './files/file-download';
|
||||
export * from './folders/folder-create';
|
||||
export * from './folders/folder-update';
|
||||
export * from './messages/message-create';
|
||||
export * from './nodes/node-collaborator-create';
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { LocalFolderNode } from '@colanode/client/types';
|
||||
import { generateId, IdType } from '@colanode/core';
|
||||
import { FolderForm } from '@colanode/ui/components/folders/folder-form';
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import {
|
||||
FolderForm,
|
||||
FolderFormValues,
|
||||
} from '@colanode/ui/components/folders/folder-form';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -11,7 +17,6 @@ import {
|
||||
DialogTitle,
|
||||
} from '@colanode/ui/components/ui/dialog';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
||||
|
||||
interface FolderCreateDialogProps {
|
||||
spaceId: string;
|
||||
@@ -26,7 +31,45 @@ export const FolderCreateDialog = ({
|
||||
}: FolderCreateDialogProps) => {
|
||||
const workspace = useWorkspace();
|
||||
const navigate = useNavigate({ from: '/workspace/$userId' });
|
||||
const { mutate, isPending } = useMutation();
|
||||
const { mutate } = useMutation({
|
||||
mutationFn: async (values: FolderFormValues) => {
|
||||
const folderId = generateId(IdType.Folder);
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
|
||||
const folder: LocalFolderNode = {
|
||||
id: folderId,
|
||||
type: 'folder',
|
||||
attributes: {
|
||||
type: 'folder',
|
||||
name: values.name,
|
||||
parentId: spaceId,
|
||||
},
|
||||
parentId: spaceId,
|
||||
rootId: spaceId,
|
||||
createdAt: new Date().toISOString(),
|
||||
createdBy: workspace.userId,
|
||||
updatedAt: null,
|
||||
updatedBy: null,
|
||||
localRevision: '0',
|
||||
serverRevision: '0',
|
||||
};
|
||||
|
||||
nodes.insert(folder);
|
||||
return folder;
|
||||
},
|
||||
onSuccess: (folder) => {
|
||||
navigate({
|
||||
to: '$nodeId',
|
||||
params: {
|
||||
nodeId: folder.id,
|
||||
},
|
||||
});
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
@@ -42,36 +85,12 @@ export const FolderCreateDialog = ({
|
||||
values={{
|
||||
name: '',
|
||||
}}
|
||||
isPending={isPending}
|
||||
submitText="Create"
|
||||
handleCancel={() => {
|
||||
onCancel={() => {
|
||||
onOpenChange(false);
|
||||
}}
|
||||
handleSubmit={(values) => {
|
||||
if (isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'folder.create',
|
||||
parentId: spaceId,
|
||||
name: values.name,
|
||||
avatar: values.avatar,
|
||||
userId: workspace.userId,
|
||||
generateIndex: true,
|
||||
},
|
||||
onSuccess(output) {
|
||||
onOpenChange(false);
|
||||
navigate({
|
||||
to: '$nodeId',
|
||||
params: { nodeId: output.id },
|
||||
});
|
||||
},
|
||||
onError(error) {
|
||||
toast.error(error.message);
|
||||
},
|
||||
});
|
||||
onSubmit={(values) => {
|
||||
mutate(values);
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
|
||||
@@ -14,30 +14,29 @@ import {
|
||||
FormMessage,
|
||||
} from '@colanode/ui/components/ui/form';
|
||||
import { Input } from '@colanode/ui/components/ui/input';
|
||||
import { Spinner } from '@colanode/ui/components/ui/spinner';
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(3, 'Name must be at least 3 characters long.'),
|
||||
avatar: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export type FolderFormValues = z.infer<typeof formSchema>;
|
||||
|
||||
interface FolderFormProps {
|
||||
id: string;
|
||||
values: z.infer<typeof formSchema>;
|
||||
isPending: boolean;
|
||||
submitText: string;
|
||||
handleCancel: () => void;
|
||||
handleSubmit: (values: z.infer<typeof formSchema>) => void;
|
||||
onCancel: () => void;
|
||||
onSubmit: (values: z.infer<typeof formSchema>) => void;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export const FolderForm = ({
|
||||
id,
|
||||
values,
|
||||
isPending,
|
||||
submitText,
|
||||
handleCancel,
|
||||
handleSubmit,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
readOnly = false,
|
||||
}: FolderFormProps) => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@@ -60,10 +59,7 @@ export const FolderForm = ({
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="flex flex-col"
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
>
|
||||
<form className="flex flex-col" onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<div className="grow flex flex-row items-end gap-2 py-2 pb-4">
|
||||
{readOnly ? (
|
||||
<Button type="button" variant="outline" size="icon">
|
||||
@@ -72,7 +68,6 @@ export const FolderForm = ({
|
||||
) : (
|
||||
<AvatarPopover
|
||||
onPick={(avatar) => {
|
||||
if (isPending) return;
|
||||
if (avatar === values.avatar) return;
|
||||
|
||||
form.setValue('avatar', avatar);
|
||||
@@ -102,16 +97,10 @@ export const FolderForm = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
disabled={isPending}
|
||||
onClick={handleCancel}
|
||||
>
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" disabled={isPending || readOnly}>
|
||||
{isPending && <Spinner className="mr-1" />}
|
||||
<Button type="submit" disabled={readOnly}>
|
||||
{submitText}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -43,13 +43,12 @@ export const FolderUpdateDialog = ({
|
||||
name: folder.attributes.name,
|
||||
avatar: folder.attributes.avatar,
|
||||
}}
|
||||
isPending={isPending}
|
||||
submitText="Update"
|
||||
readOnly={!canEdit}
|
||||
handleCancel={() => {
|
||||
onCancel={() => {
|
||||
onOpenChange(false);
|
||||
}}
|
||||
handleSubmit={(values) => {
|
||||
onSubmit={(values) => {
|
||||
if (isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Folder } from 'lucide-react';
|
||||
|
||||
import { EditorCommand } from '@colanode/client/types';
|
||||
import { EditorCommand, LocalFolderNode } from '@colanode/client/types';
|
||||
import { generateId, IdType } from '@colanode/core/lib/id.js';
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
|
||||
export const FolderCommand: EditorCommand = {
|
||||
key: 'folder',
|
||||
@@ -14,19 +16,30 @@ export const FolderCommand: EditorCommand = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { userId, documentId } = context;
|
||||
const output = await window.colanode.executeMutation({
|
||||
type: 'folder.create',
|
||||
name: 'Untitled',
|
||||
avatar: null,
|
||||
userId,
|
||||
parentId: documentId,
|
||||
generateIndex: false,
|
||||
});
|
||||
const { userId, documentId, rootId } = context;
|
||||
const folderId = generateId(IdType.Folder);
|
||||
const nodes = collections.workspace(userId).nodes;
|
||||
|
||||
if (!output.success) {
|
||||
return;
|
||||
}
|
||||
const folder: LocalFolderNode = {
|
||||
id: folderId,
|
||||
type: 'folder',
|
||||
attributes: {
|
||||
type: 'folder',
|
||||
name: 'Untitled',
|
||||
avatar: null,
|
||||
parentId: documentId,
|
||||
},
|
||||
parentId: documentId,
|
||||
rootId: rootId,
|
||||
createdAt: new Date().toISOString(),
|
||||
createdBy: userId,
|
||||
updatedAt: null,
|
||||
updatedBy: null,
|
||||
localRevision: '0',
|
||||
serverRevision: '0',
|
||||
};
|
||||
|
||||
nodes.insert(folder);
|
||||
|
||||
editor
|
||||
.chain()
|
||||
@@ -35,7 +48,7 @@ export const FolderCommand: EditorCommand = {
|
||||
.insertContent({
|
||||
type: 'folder',
|
||||
attrs: {
|
||||
id: output.output.id,
|
||||
id: folder.id,
|
||||
},
|
||||
})
|
||||
.run();
|
||||
|
||||
Reference in New Issue
Block a user