feat: impl multi file deletion support

This commit is contained in:
2025-09-28 15:45:49 +00:00
parent 3dfcdd84cf
commit c6d346394c
7 changed files with 182 additions and 159 deletions

View File

@@ -1,10 +1,9 @@
import type { Id } from "@fileone/convex/_generated/dataModel"
import { v } from "convex/values" import { v } from "convex/values"
import type { Id } from "./_generated/dataModel"
import { authenticatedMutation, authenticatedQuery } from "./functions" import { authenticatedMutation, authenticatedQuery } from "./functions"
import type { DirectoryItem } from "./model/directories"
import * as Directories from "./model/directories" import * as Directories from "./model/directories"
import * as Err from "./model/error"
import * as Files from "./model/files" import * as Files from "./model/files"
import type { FileSystemItem } from "./model/filesystem"
export const generateUploadUrl = authenticatedMutation({ export const generateUploadUrl = authenticatedMutation({
handler: async (ctx) => { handler: async (ctx) => {
@@ -55,7 +54,7 @@ export const fetchDirectoryContent = authenticatedQuery({
args: { args: {
directoryId: v.optional(v.id("directories")), directoryId: v.optional(v.id("directories")),
}, },
handler: async (ctx, { directoryId }): Promise<DirectoryItem[]> => { handler: async (ctx, { directoryId }): Promise<FileSystemItem[]> => {
return await Directories.fetchContent(ctx, { directoryId }) return await Directories.fetchContent(ctx, { directoryId })
}, },
}) })
@@ -107,37 +106,3 @@ export const renameFile = authenticatedMutation({
await Files.renameFile(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")),
itemId: v.union(v.id("files"), v.id("directories")),
},
handler: async (ctx, { itemId, kind }) => {
switch (kind) {
case "file": {
const file = await ctx.db.get(itemId as Id<"files">)
if (!file || file.userId !== ctx.user._id) {
throw new Error("File not found or access denied")
}
await ctx.db.patch(itemId, {
deletedAt: new Date().toISOString(),
})
break
}
case "directory": {
const directory = await ctx.db.get(itemId as Id<"directories">)
if (!directory || directory.userId !== ctx.user._id) {
throw new Error("Directory not found or access denied")
}
await Directories.moveToTrashRecursive(
ctx,
itemId as Id<"directories">,
)
break
}
}
return itemId
},
})

View File

@@ -4,18 +4,12 @@ import * as Directories from "./model/directories"
import * as Err from "./model/error" import * as Err from "./model/error"
import * as Files from "./model/files" import * as Files from "./model/files"
import type { DirectoryHandle, FileHandle } from "./model/filesystem" import type { DirectoryHandle, FileHandle } from "./model/filesystem"
import {
const VDirectoryHandle = v.object({ type FileSystemHandle,
kind: v.literal("directory"), FileType,
id: v.id("directories"), VDirectoryHandle,
}) VFileSystemHandle,
} from "./model/filesystem"
const VFileHandle = v.object({
kind: v.literal("file"),
id: v.id("files"),
})
const VFileSystemHandle = v.union(VFileHandle, VDirectoryHandle)
export const moveItems = authenticatedMutation({ export const moveItems = authenticatedMutation({
args: { args: {
@@ -38,10 +32,10 @@ export const moveItems = authenticatedMutation({
const fileHandles: FileHandle[] = [] const fileHandles: FileHandle[] = []
for (const item of items) { for (const item of items) {
switch (item.kind) { switch (item.kind) {
case "directory": case FileType.Directory:
directoryHandles.push(item) directoryHandles.push(item)
break break
case "file": case FileType.File:
fileHandles.push(item) fileHandles.push(item)
break break
} }
@@ -64,3 +58,45 @@ export const moveItems = authenticatedMutation({
} }
}, },
}) })
export const moveToTrash = authenticatedMutation({
args: {
handles: v.array(VFileSystemHandle),
},
handler: async (ctx, { handles }) => {
// biome-ignore lint/suspicious/useIterableCallbackReturn: switch statement is exhaustive
const promises = handles.map((handle) => {
switch (handle.kind) {
case FileType.File:
return ctx.db
.patch(handle.id, {
deletedAt: new Date().toISOString(),
})
.then(() => handle)
case FileType.Directory:
return Directories.moveToTrashRecursive(ctx, handle).then(
() => handle,
)
}
})
const results = await Promise.allSettled(promises)
const errors: Err.ApplicationErrorData[] = []
const okHandles: FileSystemHandle[] = []
for (const result of results) {
switch (result.status) {
case "fulfilled":
okHandles.push(result.value)
break
case "rejected":
errors.push(Err.createJson(Err.Code.Internal))
break
}
}
return {
deleted: okHandles,
errors,
}
},
})

View File

@@ -4,21 +4,14 @@ import type {
AuthenticatedQueryCtx, AuthenticatedQueryCtx,
} from "../functions" } from "../functions"
import * as Err from "./error" import * as Err from "./error"
import type { DirectoryHandle, FilePath, ReverseFilePath } from "./filesystem" import {
import { newDirectoryHandle } from "./filesystem" type DirectoryHandle,
type FilePath,
type Directory = { type FileSystemItem,
kind: "directory" FileType,
doc: Doc<"directories"> newDirectoryHandle,
} type ReverseFilePath,
} from "./filesystem"
type File = {
kind: "file"
doc: Doc<"files">
}
export type DirectoryItem = Directory | File
export type DirectoryItemKind = DirectoryItem["kind"]
export type DirectoryInfo = Doc<"directories"> & { path: FilePath } export type DirectoryInfo = Doc<"directories"> & { path: FilePath }
@@ -83,7 +76,7 @@ export async function fetch(
export async function fetchContent( export async function fetchContent(
ctx: AuthenticatedQueryCtx, ctx: AuthenticatedQueryCtx,
{ directoryId }: { directoryId?: Id<"directories"> } = {}, { directoryId }: { directoryId?: Id<"directories"> } = {},
): Promise<DirectoryItem[]> { ): Promise<FileSystemItem[]> {
let dirId: Id<"directories"> | undefined let dirId: Id<"directories"> | undefined
if (directoryId) { if (directoryId) {
dirId = directoryId dirId = directoryId
@@ -110,12 +103,12 @@ export async function fetchContent(
.collect(), .collect(),
]) ])
const items: DirectoryItem[] = [] const items: FileSystemItem[] = []
for (const directory of directories) { for (const directory of directories) {
items.push({ kind: "directory", doc: directory }) items.push({ kind: FileType.Directory, doc: directory })
} }
for (const file of files) { for (const file of files) {
items.push({ kind: "file", doc: file }) items.push({ kind: FileType.File, doc: file })
} }
return items return items
@@ -206,7 +199,7 @@ export async function move(
), ),
) )
} else { } else {
okDirectories.push(sourceDirectories[i]) okDirectories.push(sourceDirectories[i]!)
} }
} else if (result.status === "rejected") { } else if (result.status === "rejected") {
errors.push(Err.createJson(Err.Code.Internal)) errors.push(Err.createJson(Err.Code.Internal))
@@ -244,14 +237,14 @@ export async function move(
export async function moveToTrashRecursive( export async function moveToTrashRecursive(
ctx: AuthenticatedMutationCtx, ctx: AuthenticatedMutationCtx,
directoryId: Id<"directories">, handle: DirectoryHandle,
): Promise<void> { ): Promise<void> {
const now = new Date().toISOString() const now = new Date().toISOString()
const filesToDelete: Id<"files">[] = [] const filesToDelete: Id<"files">[] = []
const directoriesToDelete: Id<"directories">[] = [] const directoriesToDelete: Id<"directories">[] = []
const directoryQueue: Id<"directories">[] = [directoryId] const directoryQueue: Id<"directories">[] = [handle.id]
while (directoryQueue.length > 0) { while (directoryQueue.length > 0) {
const currentDirectoryId = directoryQueue.shift()! const currentDirectoryId = directoryQueue.shift()!

View File

@@ -1,10 +1,21 @@
import type { Id } from "../_generated/dataModel" import { v } from "convex/values"
import type { Doc, Id } from "../_generated/dataModel"
export enum FileType { export enum FileType {
File = "File", File = "File",
Directory = "Directory", Directory = "Directory",
} }
export type Directory = {
kind: FileType.Directory
doc: Doc<"directories">
}
export type File = {
kind: FileType.File
doc: Doc<"files">
}
export type FileSystemItem = Directory | File
export type DirectoryPathComponent = { export type DirectoryPathComponent = {
handle: DirectoryHandle handle: DirectoryHandle
name: string name: string
@@ -14,31 +25,28 @@ export type FilePathComponent = {
handle: FileHandle handle: FileHandle
name: string name: string
} }
export type PathComponent = FilePathComponent | DirectoryPathComponent export type PathComponent = FilePathComponent | DirectoryPathComponent
export type FilePath = [...DirectoryPathComponent[], PathComponent] export type FilePath = [...DirectoryPathComponent[], PathComponent]
export type ReverseFilePath = [PathComponent, ...DirectoryPathComponent[]] export type ReverseFilePath = [PathComponent, ...DirectoryPathComponent[]]
export type FileHandle = {
kind: "file"
id: Id<"files">
}
export type DirectoryHandle = { export type DirectoryHandle = {
kind: "directory" kind: FileType.Directory
id: Id<"directories"> id: Id<"directories">
} }
export type FileHandle = {
kind: FileType.File
id: Id<"files">
}
export type FileSystemHandle = DirectoryHandle | FileHandle export type FileSystemHandle = DirectoryHandle | FileHandle
export function newDirectoryHandle(id: Id<"directories">): DirectoryHandle { export function newFileSystemHandle(item: FileSystemItem): FileSystemHandle {
return { kind: "directory", id } console.log("item", item)
switch (item.kind) {
case FileType.File:
return { kind: item.kind, id: item.doc._id }
case FileType.Directory:
return { kind: item.kind, id: item.doc._id }
} }
export function newFileHandle(id: Id<"files">): FileHandle {
return { kind: "file", id }
} }
export function isSameHandle( export function isSameHandle(
@@ -47,3 +55,21 @@ export function isSameHandle(
): boolean { ): boolean {
return handle1.kind === handle2.kind && handle1.id === handle2.id return handle1.kind === handle2.kind && handle1.id === handle2.id
} }
export function newDirectoryHandle(id: Id<"directories">): DirectoryHandle {
return { kind: FileType.Directory, id }
}
export function newFileHandle(id: Id<"files">): FileHandle {
return { kind: FileType.File, id }
}
export const VDirectoryHandle = v.object({
kind: v.literal(FileType.Directory),
id: v.id("directories"),
})
export const VFileHandle = v.object({
kind: v.literal(FileType.File),
id: v.id("files"),
})
export const VFileSystemHandle = v.union(VFileHandle, VDirectoryHandle)

View File

@@ -1,14 +1,12 @@
import type { Doc } from "@fileone/convex/_generated/dataModel" import type { Doc } from "@fileone/convex/_generated/dataModel"
import type { import type { DirectoryInfo } from "@fileone/convex/model/directories"
DirectoryInfo, import type { FileSystemItem } from "@fileone/convex/model/filesystem"
DirectoryItem,
} from "@fileone/convex/model/directories"
import { createContext } from "react" import { createContext } from "react"
type DirectoryPageContextType = { type DirectoryPageContextType = {
rootDirectory: Doc<"directories"> rootDirectory: Doc<"directories">
directory: DirectoryInfo directory: DirectoryInfo
directoryContent: DirectoryItem[] directoryContent: FileSystemItem[]
} }
export const DirectoryPageContext = createContext<DirectoryPageContextType>( export const DirectoryPageContext = createContext<DirectoryPageContextType>(

View File

@@ -1,11 +1,15 @@
import { api } from "@fileone/convex/_generated/api" import { api } from "@fileone/convex/_generated/api"
import type { Doc } from "@fileone/convex/_generated/dataModel" import type { Doc } from "@fileone/convex/_generated/dataModel"
import type { DirectoryItem } from "@fileone/convex/model/directories"
import { import {
type DirectoryHandle,
type FileHandle,
type FileSystemHandle, type FileSystemHandle,
type FileSystemItem,
FileType,
isSameHandle, isSameHandle,
newDirectoryHandle, newDirectoryHandle,
newFileHandle, newFileHandle,
newFileSystemHandle,
} from "@fileone/convex/model/filesystem" } from "@fileone/convex/model/filesystem"
import { useMutation } from "@tanstack/react-query" import { useMutation } from "@tanstack/react-query"
import { Link, useNavigate } from "@tanstack/react-router" import { Link, useNavigate } from "@tanstack/react-router"
@@ -19,8 +23,8 @@ import {
} from "@tanstack/react-table" } from "@tanstack/react-table"
import { useMutation as useContextMutation } from "convex/react" import { useMutation as useContextMutation } from "convex/react"
import { useAtom, useAtomValue, useSetAtom, useStore } from "jotai" import { useAtom, useAtomValue, useSetAtom, useStore } from "jotai"
import { CheckIcon, TextCursorInputIcon, TrashIcon, XIcon } from "lucide-react" import { TextCursorInputIcon, TrashIcon } from "lucide-react"
import { useContext, useEffect, useId, useRef } from "react" import { useContext, useEffect, useRef } from "react"
import { toast } from "sonner" import { toast } from "sonner"
import { DirectoryIcon } from "@/components/icons/directory-icon" import { DirectoryIcon } from "@/components/icons/directory-icon"
import { Checkbox } from "@/components/ui/checkbox" import { Checkbox } from "@/components/ui/checkbox"
@@ -43,17 +47,13 @@ import {
keyboardModifierAtom, keyboardModifierAtom,
} from "@/lib/keyboard" } from "@/lib/keyboard"
import { TextFileIcon } from "../../components/icons/text-file-icon" import { TextFileIcon } from "../../components/icons/text-file-icon"
import { Button } from "../../components/ui/button"
import { LoadingSpinner } from "../../components/ui/loading-spinner"
import { useFileDrop } from "../../files/use-file-drop" import { useFileDrop } from "../../files/use-file-drop"
import { withDefaultOnError } from "../../lib/error"
import { cn } from "../../lib/utils" import { cn } from "../../lib/utils"
import { DirectoryPageContext } from "./context" import { DirectoryPageContext } from "./context"
import { import {
contextMenuTargeItemAtom, contextMenuTargeItemsAtom,
dragInfoAtom, dragInfoAtom,
itemBeingRenamedAtom, itemBeingRenamedAtom,
newFileTypeAtom,
openedFileAtom, openedFileAtom,
optimisticDeletedItemsAtom, optimisticDeletedItemsAtom,
} from "./state" } from "./state"
@@ -68,7 +68,7 @@ function formatFileSize(bytes: number): string {
return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}` return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`
} }
const columns: ColumnDef<DirectoryItem>[] = [ const columns: ColumnDef<FileSystemItem>[] = [
{ {
id: "select", id: "select",
header: ({ table }) => ( header: ({ table }) => (
@@ -99,9 +99,9 @@ const columns: ColumnDef<DirectoryItem>[] = [
accessorKey: "doc.name", accessorKey: "doc.name",
cell: ({ row }) => { cell: ({ row }) => {
switch (row.original.kind) { switch (row.original.kind) {
case "file": case FileType.File:
return <FileNameCell file={row.original.doc} /> return <FileNameCell file={row.original.doc} />
case "directory": case FileType.Directory:
return <DirectoryNameCell directory={row.original.doc} /> return <DirectoryNameCell directory={row.original.doc} />
} }
}, },
@@ -112,9 +112,9 @@ const columns: ColumnDef<DirectoryItem>[] = [
accessorKey: "size", accessorKey: "size",
cell: ({ row }) => { cell: ({ row }) => {
switch (row.original.kind) { switch (row.original.kind) {
case "file": case FileType.File:
return <div>{formatFileSize(row.original.doc.size)}</div> return <div>{formatFileSize(row.original.doc.size)}</div>
case "directory": case FileType.Directory:
return <div className="font-mono">-</div> return <div className="font-mono">-</div>
} }
}, },
@@ -148,31 +148,44 @@ export function DirectoryContentTableContextMenu({
children: React.ReactNode children: React.ReactNode
}) { }) {
const store = useStore() const store = useStore()
const target = useAtomValue(contextMenuTargeItemAtom) const [target, setTarget] = useAtom(contextMenuTargeItemsAtom)
const setOptimisticDeletedItems = useSetAtom(optimisticDeletedItemsAtom) const setOptimisticDeletedItems = useSetAtom(optimisticDeletedItemsAtom)
const moveToTrashMutation = useContextMutation(api.files.moveToTrash) const moveToTrashMutation = useContextMutation(api.filesystem.moveToTrash)
const setItemBeingRenamed = useSetAtom(itemBeingRenamedAtom) const setItemBeingRenamed = useSetAtom(itemBeingRenamedAtom)
const setContextMenuTargetItem = useSetAtom(contextMenuTargeItemAtom)
const { mutate: moveToTrash } = useMutation({ const { mutate: moveToTrash } = useMutation({
mutationFn: moveToTrashMutation, mutationFn: moveToTrashMutation,
onMutate: ({ itemId }) => { onMutate: ({ handles }) => {
setOptimisticDeletedItems((prev) => new Set([...prev, itemId])) setOptimisticDeletedItems(
(prev) =>
new Set([...prev, ...handles.map((handle) => handle.id)]),
)
}, },
onSuccess: (itemId) => { onSuccess: ({ deleted, errors }, { handles }) => {
setOptimisticDeletedItems((prev) => { setOptimisticDeletedItems((prev) => {
const newSet = new Set(prev) const newSet = new Set(prev)
newSet.delete(itemId) for (const handle of handles) {
newSet.delete(handle.id)
}
return newSet return newSet
}) })
toast.success("Moved to trash") if (errors.length === 0 && deleted.length === handles.length) {
toast.success(`Moved ${handles.length} items to trash`)
} else if (errors.length === handles.length) {
toast.error("Failed to move to trash")
} else {
toast.info(
`Moved ${deleted.length} items to trash; failed to move ${errors.length} items`,
)
}
}, },
}) })
const handleRename = () => { const handleRename = () => {
const selectedItem = store.get(contextMenuTargeItemAtom) const selectedItems = store.get(contextMenuTargeItemsAtom)
if (selectedItem) { if (selectedItems.length === 1) {
// biome-ignore lint/style/noNonNullAssertion: length is checked
const selectedItem = selectedItems[0]!
setItemBeingRenamed({ setItemBeingRenamed({
kind: selectedItem.kind,
originalItem: selectedItem, originalItem: selectedItem,
name: selectedItem.doc.name, name: selectedItem.doc.name,
}) })
@@ -180,11 +193,10 @@ export function DirectoryContentTableContextMenu({
} }
const handleDelete = () => { const handleDelete = () => {
const selectedItem = store.get(contextMenuTargeItemAtom) const selectedItems = store.get(contextMenuTargeItemsAtom)
if (selectedItem) { if (selectedItems.length > 0) {
moveToTrash({ moveToTrash({
kind: selectedItem.kind, handles: selectedItems.map(newFileSystemHandle),
itemId: selectedItem.doc._id,
}) })
} }
} }
@@ -193,7 +205,7 @@ export function DirectoryContentTableContextMenu({
<ContextMenu <ContextMenu
onOpenChange={(open) => { onOpenChange={(open) => {
if (!open) { if (!open) {
setContextMenuTargetItem(null) setTarget([])
} }
}} }}
> >
@@ -217,7 +229,7 @@ export function DirectoryContentTableContextMenu({
export function DirectoryContentTableContent() { export function DirectoryContentTableContent() {
const { directoryContent } = useContext(DirectoryPageContext) const { directoryContent } = useContext(DirectoryPageContext)
const optimisticDeletedItems = useAtomValue(optimisticDeletedItemsAtom) const optimisticDeletedItems = useAtomValue(optimisticDeletedItemsAtom)
const setContextMenuTargetItem = useSetAtom(contextMenuTargeItemAtom) const setContextMenuTargetItem = useSetAtom(contextMenuTargeItemsAtom)
const store = useStore() const store = useStore()
const navigate = useNavigate() const navigate = useNavigate()
@@ -247,23 +259,26 @@ export function DirectoryContentTableContent() {
) )
const handleRowContextMenu = ( const handleRowContextMenu = (
row: Row<DirectoryItem>, row: Row<FileSystemItem>,
_event: React.MouseEvent, _event: React.MouseEvent,
) => { ) => {
const target = store.get(contextMenuTargeItemAtom) const target = store.get(contextMenuTargeItemsAtom)
if (target === row.original) { if (target.length > 0) {
setContextMenuTargetItem(null) setContextMenuTargetItem([])
} else if (row.getIsSelected()) {
setContextMenuTargetItem(
table.getSelectedRowModel().rows.map((row) => row.original),
)
} else { } else {
selectRow(row) selectRow(row)
setContextMenuTargetItem(row.original) setContextMenuTargetItem([row.original])
} }
} }
const selectRow = (row: Row<DirectoryItem>) => { const selectRow = (row: Row<FileSystemItem>) => {
const keyboardModifiers = store.get(keyboardModifierAtom) const keyboardModifiers = store.get(keyboardModifierAtom)
const isMultiSelectMode = isControlOrCommandKeyActive(keyboardModifiers) const isMultiSelectMode = isControlOrCommandKeyActive(keyboardModifiers)
const isRowSelected = row.getIsSelected() const isRowSelected = row.getIsSelected()
console.log({ isMultiSelectMode, isRowSelected })
if (isRowSelected && isMultiSelectMode) { if (isRowSelected && isMultiSelectMode) {
row.toggleSelected(false) row.toggleSelected(false)
} else if (isRowSelected && !isMultiSelectMode) { } else if (isRowSelected && !isMultiSelectMode) {
@@ -282,8 +297,8 @@ export function DirectoryContentTableContent() {
} }
} }
const handleRowDoubleClick = (row: Row<DirectoryItem>) => { const handleRowDoubleClick = (row: Row<FileSystemItem>) => {
if (row.original.kind === "directory") { if (row.original.kind === FileType.Directory) {
navigate({ navigate({
to: `/directories/${row.original.doc._id}`, to: `/directories/${row.original.doc._id}`,
}) })
@@ -355,8 +370,8 @@ function FileItemRow({
onContextMenu, onContextMenu,
onDoubleClick, onDoubleClick,
}: { }: {
table: TableType<DirectoryItem> table: TableType<FileSystemItem>
row: Row<DirectoryItem> row: Row<FileSystemItem>
onClick: () => void onClick: () => void
onContextMenu: (e: React.MouseEvent) => void onContextMenu: (e: React.MouseEvent) => void
onDoubleClick: () => void onDoubleClick: () => void
@@ -366,19 +381,19 @@ function FileItemRow({
const { isDraggedOver, dropHandlers } = useFileDrop({ const { isDraggedOver, dropHandlers } = useFileDrop({
destItem: destItem:
row.original.kind === "directory" row.original.kind === FileType.Directory
? newDirectoryHandle(row.original.doc._id) ? newDirectoryHandle(row.original.doc._id)
: null, : null,
dragInfoAtom, dragInfoAtom,
}) })
const handleDragStart = (e: React.DragEvent) => { const handleDragStart = (_e: React.DragEvent) => {
let source: FileSystemHandle let source: DirectoryHandle | FileHandle
switch (row.original.kind) { switch (row.original.kind) {
case "file": case FileType.File:
source = newFileHandle(row.original.doc._id) source = newFileHandle(row.original.doc._id)
break break
case "directory": case FileType.Directory:
source = newDirectoryHandle(row.original.doc._id) source = newDirectoryHandle(row.original.doc._id)
break break
} }
@@ -386,15 +401,9 @@ function FileItemRow({
let draggedItems: FileSystemHandle[] let draggedItems: FileSystemHandle[]
// drag all selections, but only if the currently dragged row is also selected // drag all selections, but only if the currently dragged row is also selected
if (row.getIsSelected()) { if (row.getIsSelected()) {
// biome-ignore lint/suspicious/useIterableCallbackReturn: the switch statement is exhaustive draggedItems = table
draggedItems = table.getSelectedRowModel().rows.map((row) => { .getSelectedRowModel()
switch (row.original.kind) { .rows.map((row) => newFileSystemHandle(row.original))
case "file":
return newFileHandle(row.original.doc._id)
case "directory":
return newDirectoryHandle(row.original.doc._id)
}
})
if (!draggedItems.some((item) => isSameHandle(item, source))) { if (!draggedItems.some((item) => isSameHandle(item, source))) {
draggedItems.push(source) draggedItems.push(source)
} }

View File

@@ -1,14 +1,11 @@
import type { Doc, Id } from "@fileone/convex/_generated/dataModel" import type { Doc, Id } from "@fileone/convex/_generated/dataModel"
import type { import type { DirectoryItemKind } from "@fileone/convex/model/directories"
DirectoryItem, import type { FileSystemItem, FileType } from "@fileone/convex/model/filesystem"
DirectoryItemKind,
} from "@fileone/convex/model/directories"
import type { FileType } from "@fileone/convex/model/filesystem"
import type { RowSelectionState } from "@tanstack/react-table" import type { RowSelectionState } from "@tanstack/react-table"
import { atom } from "jotai" import { atom } from "jotai"
import type { FileDragInfo } from "../../files/use-file-drop" import type { FileDragInfo } from "../../files/use-file-drop"
export const contextMenuTargeItemAtom = atom<DirectoryItem | null>(null) export const contextMenuTargeItemsAtom = atom<FileSystemItem[]>([])
export const optimisticDeletedItemsAtom = atom( export const optimisticDeletedItemsAtom = atom(
new Set<Id<"files"> | Id<"directories">>(), new Set<Id<"files"> | Id<"directories">>(),
) )
@@ -18,8 +15,7 @@ export const selectedFileRowsAtom = atom<RowSelectionState>({})
export const newFileTypeAtom = atom<FileType | null>(null) export const newFileTypeAtom = atom<FileType | null>(null)
export const itemBeingRenamedAtom = atom<{ export const itemBeingRenamedAtom = atom<{
kind: DirectoryItemKind originalItem: FileSystemItem
originalItem: DirectoryItem
name: string name: string
} | null>(null) } | null>(null)