feat: impl multi file/dir moving

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2025-09-21 17:03:50 +00:00
parent a331276c43
commit 29eab87c71
10 changed files with 147 additions and 53 deletions

View File

@@ -14,6 +14,7 @@ import type {
FunctionReference,
} from "convex/server";
import type * as files from "../files.js";
import type * as filesystem from "../filesystem.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";
@@ -32,6 +33,7 @@ import type * as users from "../users.js";
*/
declare const fullApi: ApiFromModules<{
files: typeof files;
filesystem: typeof filesystem;
functions: typeof functions;
"model/directories": typeof model_directories;
"model/error": typeof model_error;

View File

@@ -141,13 +141,3 @@ export const moveToTrash = authenticatedMutation({
return itemId
},
})
export const moveFiles = authenticatedMutation({
args: {
targetDirectoryId: v.id("directories"),
items: v.array(v.id("files")),
},
handler: async (ctx, { targetDirectoryId, items }) => {
return await Files.moveFiles(ctx, { targetDirectoryId, items })
},
})

View File

@@ -0,0 +1,62 @@
import { v } from "convex/values"
import { authenticatedMutation } from "./functions"
import * as Directories from "./model/directories"
import * as Err from "./model/error"
import * as Files from "./model/files"
import type { DirectoryHandle, FileHandle } from "./model/filesystem"
const VDirectoryHandle = v.object({
kind: v.literal("directory"),
id: v.id("directories"),
})
const VFileHandle = v.object({
kind: v.literal("file"),
id: v.id("files"),
})
const VFileSystemHandle = v.union(VFileHandle, VDirectoryHandle)
export const moveItems = authenticatedMutation({
args: {
targetDirectory: VDirectoryHandle,
items: v.array(VFileSystemHandle),
},
handler: async (ctx, { targetDirectory: targetDirectoryHandle, items }) => {
const targetDirectory = await Directories.fetchHandle(
ctx,
targetDirectoryHandle,
)
if (!targetDirectory) {
throw Err.create(
Err.Code.DirectoryNotFound,
`Directory ${targetDirectoryHandle.id} not found`,
)
}
const directoryHandles: DirectoryHandle[] = []
const fileHandles: FileHandle[] = []
for (const item of items) {
switch (item.kind) {
case "directory":
directoryHandles.push(item)
break
case "file":
fileHandles.push(item)
break
}
}
await Promise.all([
Files.move(ctx, {
targetDirectory: targetDirectoryHandle,
items: fileHandles,
}),
Directories.move(ctx, {
targetDirectory: targetDirectoryHandle,
sourceDirectories: directoryHandles,
}),
])
return { items, targetDirectory }
},
})

View File

@@ -4,7 +4,7 @@ import type {
AuthenticatedQueryCtx,
} from "../functions"
import * as Err from "./error"
import type { FilePath, ReverseFilePath } from "./filesystem"
import type { DirectoryHandle, FilePath, ReverseFilePath } from "./filesystem"
import { newDirectoryHandle } from "./filesystem"
type Directory = {
@@ -31,6 +31,20 @@ export async function fetchRoot(ctx: AuthenticatedQueryCtx) {
.first()
}
export async function fetchHandle(
ctx: AuthenticatedQueryCtx,
handle: DirectoryHandle,
): Promise<Doc<"directories">> {
const directory = await ctx.db.get(handle.id)
if (!directory || directory.userId !== ctx.user._id) {
throw Err.create(
Err.Code.DirectoryNotFound,
`Directory ${handle.id} not found`,
)
}
return directory
}
export async function fetch(
ctx: AuthenticatedQueryCtx,
{ directoryId }: { directoryId: Id<"directories"> },
@@ -147,6 +161,23 @@ export async function create(
})
}
export async function move(
ctx: AuthenticatedMutationCtx,
{
targetDirectory,
sourceDirectories,
}: {
targetDirectory: DirectoryHandle
sourceDirectories: DirectoryHandle[]
},
): Promise<void> {
await Promise.all(
sourceDirectories.map((directory) =>
ctx.db.patch(directory.id, { parentId: targetDirectory.id }),
),
)
}
export async function moveToTrashRecursive(
ctx: AuthenticatedMutationCtx,
directoryId: Id<"directories">,

View File

@@ -1,6 +1,7 @@
import type { Id } from "../_generated/dataModel"
import type { AuthenticatedMutationCtx } from "../functions"
import * as Err from "./error"
import type { DirectoryHandle, FileHandle } from "./filesystem"
export async function renameFile(
ctx: AuthenticatedMutationCtx,
@@ -35,27 +36,19 @@ export async function renameFile(
await ctx.db.patch(itemId, { name: newName })
}
export async function moveFiles(
export async function move(
ctx: AuthenticatedMutationCtx,
{
targetDirectoryId,
targetDirectory: targetDirectoryHandle,
items,
}: {
targetDirectoryId: Id<"directories">
items: Id<"files">[]
targetDirectory: DirectoryHandle
items: FileHandle[]
},
) {
const targetDirectory = await ctx.db.get(targetDirectoryId)
if (!targetDirectory) {
throw Err.create(
Err.Code.DirectoryNotFound,
"Target directory not found",
)
}
await Promise.all(
items.map((itemId) =>
ctx.db.patch(itemId, { directoryId: targetDirectoryId }),
items.map((item) =>
ctx.db.patch(item.id, { directoryId: targetDirectoryHandle.id }),
),
)
return { items, targetDirectory }
}