From 9fdcd4d293e7195ce78b28d56e9b0640d059815c Mon Sep 17 00:00:00 2001 From: kenneth Date: Thu, 18 Sep 2025 00:14:16 +0000 Subject: [PATCH] feat: impl file rename --- packages/convex/_generated/api.d.ts | 2 + packages/convex/files.ts | 12 ++ packages/convex/model/files.ts | 36 +++++ packages/web/convex/_generated/api.d.ts | 33 ---- packages/web/convex/_generated/api.js | 22 --- packages/web/convex/_generated/dataModel.d.ts | 58 ------- packages/web/convex/_generated/server.d.ts | 142 ------------------ packages/web/convex/_generated/server.js | 89 ----------- packages/web/src/components/ui/button.tsx | 103 +++++++------ .../web/src/components/ui/context-menu.tsx | 2 +- packages/web/src/components/ui/dialog.tsx | 141 +++++++++++++++++ packages/web/src/files/file-table.tsx | 11 +- packages/web/src/files/files-page.tsx | 2 + packages/web/src/files/rename-file-dialog.tsx | 111 ++++++++++++++ packages/web/src/files/state.ts | 6 + 15 files changed, 377 insertions(+), 393 deletions(-) create mode 100644 packages/convex/model/files.ts delete mode 100644 packages/web/convex/_generated/api.d.ts delete mode 100644 packages/web/convex/_generated/api.js delete mode 100644 packages/web/convex/_generated/dataModel.d.ts delete mode 100644 packages/web/convex/_generated/server.d.ts delete mode 100644 packages/web/convex/_generated/server.js create mode 100644 packages/web/src/components/ui/dialog.tsx create mode 100644 packages/web/src/files/rename-file-dialog.tsx diff --git a/packages/convex/_generated/api.d.ts b/packages/convex/_generated/api.d.ts index 46e2aeb..e392b05 100644 --- a/packages/convex/_generated/api.d.ts +++ b/packages/convex/_generated/api.d.ts @@ -17,6 +17,7 @@ import type * as files from "../files.js"; import type * as functions from "../functions.js"; import type * as model_directories from "../model/directories.js"; import type * as model_error from "../model/error.js"; +import type * as model_files from "../model/files.js"; import type * as model_user from "../model/user.js"; import type * as users from "../users.js"; @@ -33,6 +34,7 @@ declare const fullApi: ApiFromModules<{ functions: typeof functions; "model/directories": typeof model_directories; "model/error": typeof model_error; + "model/files": typeof model_files; "model/user": typeof model_user; users: typeof users; }>; diff --git a/packages/convex/files.ts b/packages/convex/files.ts index f493dc4..354dfa3 100644 --- a/packages/convex/files.ts +++ b/packages/convex/files.ts @@ -3,6 +3,7 @@ import { v } from "convex/values" import { authenticatedMutation, authenticatedQuery } from "./functions" import type { DirectoryItem } from "./model/directories" import * as Directories from "./model/directories" +import * as Files from "./model/files" export const generateUploadUrl = authenticatedMutation({ handler: async (ctx) => { @@ -72,6 +73,17 @@ export const saveFile = authenticatedMutation({ }, }) +export const renameFile = authenticatedMutation({ + args: { + directoryId: v.optional(v.id("directories")), + itemId: v.id("files"), + newName: v.string(), + }, + handler: async (ctx, { directoryId, itemId, newName }) => { + await Files.renameFile(ctx, { directoryId, itemId, newName }) + }, +}) + export const moveToTrash = authenticatedMutation({ args: { kind: v.union(v.literal("file"), v.literal("directory")), diff --git a/packages/convex/model/files.ts b/packages/convex/model/files.ts new file mode 100644 index 0000000..aa295f5 --- /dev/null +++ b/packages/convex/model/files.ts @@ -0,0 +1,36 @@ +import type { Id } from "../_generated/dataModel" +import type { AuthenticatedMutationCtx } from "../functions" +import * as Err from "./error" + +export async function renameFile( + ctx: AuthenticatedMutationCtx, + { + directoryId, + itemId, + newName, + }: { + directoryId?: Id<"directories"> + itemId: Id<"files"> + newName: string + }, +) { + const existing = await ctx.db + .query("files") + .withIndex("uniqueFileInDirectory", (q) => + q + .eq("userId", ctx.user._id) + .eq("directoryId", directoryId) + .eq("name", newName) + .eq("deletedAt", undefined), + ) + .first() + + if (existing) { + throw Err.create( + Err.Code.FileExists, + `File with name ${newName} already exists in ${directoryId ? `directory ${directoryId}` : "root"}`, + ) + } + + await ctx.db.patch(itemId, { name: newName }) +} diff --git a/packages/web/convex/_generated/api.d.ts b/packages/web/convex/_generated/api.d.ts deleted file mode 100644 index 7d71a01..0000000 --- a/packages/web/convex/_generated/api.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable */ -/** - * Generated `api` utility. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import type { - ApiFromModules, - FilterApi, - FunctionReference, -} from "convex/server"; - -/** - * A utility for referencing Convex functions in your app's API. - * - * Usage: - * ```js - * const myFunctionReference = api.myModule.myFunction; - * ``` - */ -declare const fullApi: ApiFromModules<{}>; -export declare const api: FilterApi< - typeof fullApi, - FunctionReference ->; -export declare const internal: FilterApi< - typeof fullApi, - FunctionReference ->; diff --git a/packages/web/convex/_generated/api.js b/packages/web/convex/_generated/api.js deleted file mode 100644 index 3f9c482..0000000 --- a/packages/web/convex/_generated/api.js +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable */ -/** - * Generated `api` utility. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import { anyApi } from "convex/server"; - -/** - * A utility for referencing Convex functions in your app's API. - * - * Usage: - * ```js - * const myFunctionReference = api.myModule.myFunction; - * ``` - */ -export const api = anyApi; -export const internal = anyApi; diff --git a/packages/web/convex/_generated/dataModel.d.ts b/packages/web/convex/_generated/dataModel.d.ts deleted file mode 100644 index fb12533..0000000 --- a/packages/web/convex/_generated/dataModel.d.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable */ -/** - * Generated data model types. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import { AnyDataModel } from "convex/server"; -import type { GenericId } from "convex/values"; - -/** - * No `schema.ts` file found! - * - * This generated code has permissive types like `Doc = any` because - * Convex doesn't know your schema. If you'd like more type safety, see - * https://docs.convex.dev/using/schemas for instructions on how to add a - * schema file. - * - * After you change a schema, rerun codegen with `npx convex dev`. - */ - -/** - * The names of all of your Convex tables. - */ -export type TableNames = string; - -/** - * The type of a document stored in Convex. - */ -export type Doc = any; - -/** - * An identifier for a document in Convex. - * - * Convex documents are uniquely identified by their `Id`, which is accessible - * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). - * - * Documents can be loaded using `db.get(id)` in query and mutation functions. - * - * IDs are just strings at runtime, but this type can be used to distinguish them from other - * strings when type checking. - */ -export type Id = - GenericId; - -/** - * A type describing your Convex data model. - * - * This type includes information about what tables you have, the type of - * documents stored in those tables, and the indexes defined on them. - * - * This type is used to parameterize methods like `queryGeneric` and - * `mutationGeneric` to make them type-safe. - */ -export type DataModel = AnyDataModel; diff --git a/packages/web/convex/_generated/server.d.ts b/packages/web/convex/_generated/server.d.ts deleted file mode 100644 index 7f337a4..0000000 --- a/packages/web/convex/_generated/server.d.ts +++ /dev/null @@ -1,142 +0,0 @@ -/* eslint-disable */ -/** - * Generated utilities for implementing server-side Convex query and mutation functions. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import { - ActionBuilder, - HttpActionBuilder, - MutationBuilder, - QueryBuilder, - GenericActionCtx, - GenericMutationCtx, - GenericQueryCtx, - GenericDatabaseReader, - GenericDatabaseWriter, -} from "convex/server"; -import type { DataModel } from "./dataModel.js"; - -/** - * Define a query in this Convex app's public API. - * - * This function will be allowed to read your Convex database and will be accessible from the client. - * - * @param func - The query function. It receives a {@link QueryCtx} as its first argument. - * @returns The wrapped query. Include this as an `export` to name it and make it accessible. - */ -export declare const query: QueryBuilder; - -/** - * Define a query that is only accessible from other Convex functions (but not from the client). - * - * This function will be allowed to read from your Convex database. It will not be accessible from the client. - * - * @param func - The query function. It receives a {@link QueryCtx} as its first argument. - * @returns The wrapped query. Include this as an `export` to name it and make it accessible. - */ -export declare const internalQuery: QueryBuilder; - -/** - * Define a mutation in this Convex app's public API. - * - * This function will be allowed to modify your Convex database and will be accessible from the client. - * - * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. - * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. - */ -export declare const mutation: MutationBuilder; - -/** - * Define a mutation that is only accessible from other Convex functions (but not from the client). - * - * This function will be allowed to modify your Convex database. It will not be accessible from the client. - * - * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. - * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. - */ -export declare const internalMutation: MutationBuilder; - -/** - * Define an action in this Convex app's public API. - * - * An action is a function which can execute any JavaScript code, including non-deterministic - * code and code with side-effects, like calling third-party services. - * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. - * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. - * - * @param func - The action. It receives an {@link ActionCtx} as its first argument. - * @returns The wrapped action. Include this as an `export` to name it and make it accessible. - */ -export declare const action: ActionBuilder; - -/** - * Define an action that is only accessible from other Convex functions (but not from the client). - * - * @param func - The function. It receives an {@link ActionCtx} as its first argument. - * @returns The wrapped function. Include this as an `export` to name it and make it accessible. - */ -export declare const internalAction: ActionBuilder; - -/** - * Define an HTTP action. - * - * This function will be used to respond to HTTP requests received by a Convex - * deployment if the requests matches the path and method where this action - * is routed. Be sure to route your action in `convex/http.js`. - * - * @param func - The function. It receives an {@link ActionCtx} as its first argument. - * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. - */ -export declare const httpAction: HttpActionBuilder; - -/** - * A set of services for use within Convex query functions. - * - * The query context is passed as the first argument to any Convex query - * function run on the server. - * - * This differs from the {@link MutationCtx} because all of the services are - * read-only. - */ -export type QueryCtx = GenericQueryCtx; - -/** - * A set of services for use within Convex mutation functions. - * - * The mutation context is passed as the first argument to any Convex mutation - * function run on the server. - */ -export type MutationCtx = GenericMutationCtx; - -/** - * A set of services for use within Convex action functions. - * - * The action context is passed as the first argument to any Convex action - * function run on the server. - */ -export type ActionCtx = GenericActionCtx; - -/** - * An interface to read from the database within Convex query functions. - * - * The two entry points are {@link DatabaseReader.get}, which fetches a single - * document by its {@link Id}, or {@link DatabaseReader.query}, which starts - * building a query. - */ -export type DatabaseReader = GenericDatabaseReader; - -/** - * An interface to read from and write to the database within Convex mutation - * functions. - * - * Convex guarantees that all writes within a single mutation are - * executed atomically, so you never have to worry about partial writes leaving - * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control) - * for the guarantees Convex provides your functions. - */ -export type DatabaseWriter = GenericDatabaseWriter; diff --git a/packages/web/convex/_generated/server.js b/packages/web/convex/_generated/server.js deleted file mode 100644 index 566d485..0000000 --- a/packages/web/convex/_generated/server.js +++ /dev/null @@ -1,89 +0,0 @@ -/* eslint-disable */ -/** - * Generated utilities for implementing server-side Convex query and mutation functions. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import { - actionGeneric, - httpActionGeneric, - queryGeneric, - mutationGeneric, - internalActionGeneric, - internalMutationGeneric, - internalQueryGeneric, -} from "convex/server"; - -/** - * Define a query in this Convex app's public API. - * - * This function will be allowed to read your Convex database and will be accessible from the client. - * - * @param func - The query function. It receives a {@link QueryCtx} as its first argument. - * @returns The wrapped query. Include this as an `export` to name it and make it accessible. - */ -export const query = queryGeneric; - -/** - * Define a query that is only accessible from other Convex functions (but not from the client). - * - * This function will be allowed to read from your Convex database. It will not be accessible from the client. - * - * @param func - The query function. It receives a {@link QueryCtx} as its first argument. - * @returns The wrapped query. Include this as an `export` to name it and make it accessible. - */ -export const internalQuery = internalQueryGeneric; - -/** - * Define a mutation in this Convex app's public API. - * - * This function will be allowed to modify your Convex database and will be accessible from the client. - * - * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. - * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. - */ -export const mutation = mutationGeneric; - -/** - * Define a mutation that is only accessible from other Convex functions (but not from the client). - * - * This function will be allowed to modify your Convex database. It will not be accessible from the client. - * - * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. - * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. - */ -export const internalMutation = internalMutationGeneric; - -/** - * Define an action in this Convex app's public API. - * - * An action is a function which can execute any JavaScript code, including non-deterministic - * code and code with side-effects, like calling third-party services. - * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. - * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. - * - * @param func - The action. It receives an {@link ActionCtx} as its first argument. - * @returns The wrapped action. Include this as an `export` to name it and make it accessible. - */ -export const action = actionGeneric; - -/** - * Define an action that is only accessible from other Convex functions (but not from the client). - * - * @param func - The function. It receives an {@link ActionCtx} as its first argument. - * @returns The wrapped function. Include this as an `export` to name it and make it accessible. - */ -export const internalAction = internalActionGeneric; - -/** - * Define a Convex HTTP action. - * - * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object - * as its second. - * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`. - */ -export const httpAction = httpActionGeneric; diff --git a/packages/web/src/components/ui/button.tsx b/packages/web/src/components/ui/button.tsx index a2df8dc..e31e05f 100644 --- a/packages/web/src/components/ui/button.tsx +++ b/packages/web/src/components/ui/button.tsx @@ -1,59 +1,72 @@ -import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" +import type * as React from "react" import { cn } from "@/lib/utils" +import { LoadingSpinner } from "./loading-spinner" const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", - { - variants: { - variant: { - default: - "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", - destructive: - "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", - outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", - secondary: - "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", - ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-9 px-4 py-2 has-[>svg]:px-3", - sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 rounded-md px-6 has-[>svg]:px-4", - icon: "size-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, ) function Button({ - className, - variant, - size, - asChild = false, - ...props + className, + variant, + size, + asChild = false, + loading = false, + children, + ...props }: React.ComponentProps<"button"> & - VariantProps & { - asChild?: boolean - }) { - const Comp = asChild ? Slot : "button" + VariantProps & { + loading?: boolean + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" - return ( - - ) + return ( + + {asChild ? ( + children + ) : ( + <> + {loading ? : null} + {children} + + )} + + ) } export { Button, buttonVariants } diff --git a/packages/web/src/components/ui/context-menu.tsx b/packages/web/src/components/ui/context-menu.tsx index 65c175d..6af75df 100644 --- a/packages/web/src/components/ui/context-menu.tsx +++ b/packages/web/src/components/ui/context-menu.tsx @@ -106,7 +106,7 @@ function ContextMenuContent({ ) { + return +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/packages/web/src/files/file-table.tsx b/packages/web/src/files/file-table.tsx index da0fada..17ab4ce 100644 --- a/packages/web/src/files/file-table.tsx +++ b/packages/web/src/files/file-table.tsx @@ -38,6 +38,7 @@ import { withDefaultOnError } from "../lib/error" import { cn } from "../lib/utils" import { contextMenuTargeItemAtom, + itemBeingRenamedAtom, newItemKindAtom, optimisticDeletedItemsAtom, } from "./state" @@ -132,6 +133,7 @@ export function FileTableContextMenu({ const target = useAtomValue(contextMenuTargeItemAtom) const setOptimisticDeletedItems = useSetAtom(optimisticDeletedItemsAtom) const moveToTrashMutation = useContextMutation(api.files.moveToTrash) + const setItemBeingRenamed = useSetAtom(itemBeingRenamedAtom) const { mutate: moveToTrash } = useMutation({ mutationFn: moveToTrashMutation, onMutate: ({ itemId }) => { @@ -150,8 +152,11 @@ export function FileTableContextMenu({ const handleRename = () => { const selectedItem = store.get(contextMenuTargeItemAtom) if (selectedItem) { - console.log("Renaming:", selectedItem.doc.name) - // TODO: Implement rename functionality + setItemBeingRenamed({ + kind: selectedItem.kind, + originalItem: selectedItem, + name: selectedItem.doc.name, + }) } } @@ -169,7 +174,7 @@ export function FileTableContextMenu({ {children} {target && ( - + Rename diff --git a/packages/web/src/files/files-page.tsx b/packages/web/src/files/files-page.tsx index a8c097c..413d3f1 100644 --- a/packages/web/src/files/files-page.tsx +++ b/packages/web/src/files/files-page.tsx @@ -30,6 +30,7 @@ import { } from "../components/ui/breadcrumb" import { Button } from "../components/ui/button" import { FileTable } from "./file-table" +import { RenameFileDialog } from "./rename-file-dialog" import { newItemKindAtom } from "./state" export function FilesPage({ path }: { path: string }) { @@ -45,6 +46,7 @@ export function FilesPage({ path }: { path: string }) {
+ ) } diff --git a/packages/web/src/files/rename-file-dialog.tsx b/packages/web/src/files/rename-file-dialog.tsx new file mode 100644 index 0000000..83c3a88 --- /dev/null +++ b/packages/web/src/files/rename-file-dialog.tsx @@ -0,0 +1,111 @@ +import { api } from "@fileone/convex/_generated/api" +import { useMutation } from "@tanstack/react-query" +import { useMutation as useContextMutation } from "convex/react" +import { atom, useAtom, useStore } from "jotai" +import { useId } from "react" +import { toast } from "sonner" +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { itemBeingRenamedAtom } from "./state" + +const fielNameAtom = atom( + (get) => get(itemBeingRenamedAtom)?.name, + (get, set, newName: string) => { + const current = get(itemBeingRenamedAtom) + if (current) { + set(itemBeingRenamedAtom, { + ...current, + name: newName, + }) + } + }, +) + +export function RenameFileDialog() { + const [itemBeingRenamed, setItemBeingRenamed] = + useAtom(itemBeingRenamedAtom) + const store = useStore() + const formId = useId() + + const { mutate: renameFile, isPending: isRenaming } = useMutation({ + mutationFn: useContextMutation(api.files.renameFile), + onSuccess: () => { + setItemBeingRenamed(null) + toast.success("File renamed successfully") + }, + }) + + const onSubmit = (event: React.FormEvent) => { + event.preventDefault() + + const itemBeingRenamed = store.get(itemBeingRenamedAtom) + if (itemBeingRenamed) { + const formData = new FormData(event.currentTarget) + const newName = formData.get("itemName") as string + + if (newName) { + switch (itemBeingRenamed.originalItem.kind) { + case "file": + renameFile({ + directoryId: + itemBeingRenamed.originalItem.doc.directoryId, + itemId: itemBeingRenamed.originalItem.doc._id, + newName, + }) + break + default: + break + } + } + } + } + + return ( + + setItemBeingRenamed(open ? itemBeingRenamed : null) + } + > + + + Rename File + + +
+ + + + + + + + + +
+
+ ) +} + +function RenameFileInput() { + const [fileName, setFileName] = useAtom(fielNameAtom) + return ( + setFileName(e.target.value)} + /> + ) +} diff --git a/packages/web/src/files/state.ts b/packages/web/src/files/state.ts index 632441c..68720c4 100644 --- a/packages/web/src/files/state.ts +++ b/packages/web/src/files/state.ts @@ -14,3 +14,9 @@ export const optimisticDeletedItemsAtom = atom( export const selectedFileRowsAtom = atom({}) export const newItemKindAtom = atom(null) + +export const itemBeingRenamedAtom = atom<{ + kind: DirectoryItemKind + originalItem: DirectoryItem + name: string +} | null>(null)