mirror of
https://github.com/get-drexa/drive.git
synced 2025-11-30 21:41:39 +00:00
Compare commits
2 Commits
ad99bca7fd
...
5cc13a34b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
5cc13a34b2
|
|||
|
879287f8bf
|
@@ -1,15 +1,25 @@
|
||||
import { api } from "@fileone/convex/api"
|
||||
import { Link, useLocation } from "@tanstack/react-router"
|
||||
import { useQuery as useConvexQuery } from "convex/react"
|
||||
import { useAtomValue } from "jotai"
|
||||
import { newDirectoryHandle } from "@fileone/convex/filesystem"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { Link, useLocation, useParams } from "@tanstack/react-router"
|
||||
import {
|
||||
useMutation as useConvexMutation,
|
||||
useQuery as useConvexQuery,
|
||||
} from "convex/react"
|
||||
import { useAtomValue, useSetAtom, useStore } from "jotai"
|
||||
import {
|
||||
CircleXIcon,
|
||||
ClockIcon,
|
||||
FilesIcon,
|
||||
FolderInputIcon,
|
||||
LogOutIcon,
|
||||
ScissorsIcon,
|
||||
SettingsIcon,
|
||||
TrashIcon,
|
||||
User2Icon,
|
||||
} from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -26,7 +36,10 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "@/components/ui/sidebar"
|
||||
import { formatError } from "@/lib/error"
|
||||
import { Button } from "../components/ui/button"
|
||||
import { LoadingSpinner } from "../components/ui/loading-spinner"
|
||||
import { clearCutItemsAtom, cutHandlesAtom } from "../files/store"
|
||||
import { backgroundTaskProgressAtom } from "./state"
|
||||
|
||||
export function DashboardSidebar() {
|
||||
@@ -46,6 +59,7 @@ export function DashboardSidebar() {
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<SidebarMenu>
|
||||
<CutItemsCard />
|
||||
<BackgroundTaskProgressItem />
|
||||
</SidebarMenu>
|
||||
</SidebarFooter>
|
||||
@@ -134,6 +148,93 @@ function BackgroundTaskProgressItem() {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the number of cut items and allows the user to perform actions on them, such as moving them to a target directory.
|
||||
* Visible when there are cut items.
|
||||
*/
|
||||
function CutItemsCard() {
|
||||
const { directoryId } = useParams({ strict: false })
|
||||
const cutHandles = useAtomValue(cutHandlesAtom)
|
||||
const clearCutItems = useSetAtom(clearCutItemsAtom)
|
||||
const setCutHandles = useSetAtom(cutHandlesAtom)
|
||||
const setBackgroundTaskProgress = useSetAtom(backgroundTaskProgressAtom)
|
||||
const store = useStore()
|
||||
|
||||
const _moveItems = useConvexMutation(api.filesystem.moveItems)
|
||||
const { mutate: moveItems } = useMutation({
|
||||
mutationFn: _moveItems,
|
||||
onMutate: () => {
|
||||
setBackgroundTaskProgress({
|
||||
label: "Moving items…",
|
||||
})
|
||||
const cutHandles = store.get(cutHandlesAtom)
|
||||
clearCutItems()
|
||||
return { cutHandles }
|
||||
},
|
||||
onError: (error, _variables, context) => {
|
||||
if (context?.cutHandles) {
|
||||
setCutHandles(context.cutHandles)
|
||||
}
|
||||
toast.error("Failed to move items", {
|
||||
description: formatError(error),
|
||||
})
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success("Items moved")
|
||||
},
|
||||
onSettled: () => {
|
||||
setBackgroundTaskProgress(null)
|
||||
},
|
||||
})
|
||||
|
||||
if (cutHandles.length === 0) return null
|
||||
|
||||
const moveCutItems = () => {
|
||||
if (directoryId) {
|
||||
moveItems({
|
||||
targetDirectory: newDirectoryHandle(directoryId),
|
||||
items: cutHandles,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<Card className="p-0 gap-0 rounded-md overflow-clip">
|
||||
<CardHeader className="px-3.5 py-1.5! gap-0 border-b border-b-primary-foreground/10 bg-primary text-primary-foreground">
|
||||
<CardTitle className="p-0 m-0 text-xs uppercase">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<ScissorsIcon size={16} /> {cutHandles.length} Cut
|
||||
Items
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardFooter className="p-1 flex flex-col">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="w-full justify-start transition-none"
|
||||
disabled={!directoryId}
|
||||
onClick={moveCutItems}
|
||||
>
|
||||
<FolderInputIcon size={16} />
|
||||
Move items here
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="w-full justify-start transition-none"
|
||||
onClick={() => clearCutItems()}
|
||||
>
|
||||
<CircleXIcon size={16} />
|
||||
Clear
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</SidebarMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
function UserMenu() {
|
||||
function handleSignOut() {}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { FileSystemHandle } from "@fileone/convex/filesystem"
|
||||
import { atom } from "jotai"
|
||||
import { atomFamily } from "jotai/utils"
|
||||
|
||||
@@ -92,3 +93,8 @@ export const hasFileUploadsErrorAtom = atom((get) => {
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
export const cutHandlesAtom = atom<FileSystemHandle[]>([])
|
||||
export const clearCutItemsAtom = atom(null, (_, set) => {
|
||||
set(cutHandlesAtom, [])
|
||||
})
|
||||
|
||||
@@ -17,6 +17,7 @@ import { atom, useAtom, useAtomValue, useSetAtom, useStore } from "jotai"
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
PlusIcon,
|
||||
ScissorsIcon,
|
||||
TextCursorInputIcon,
|
||||
TrashIcon,
|
||||
} from "lucide-react"
|
||||
@@ -46,7 +47,7 @@ import { NewDirectoryDialog } from "@/directories/directory-page/new-directory-d
|
||||
import { RenameFileDialog } from "@/directories/directory-page/rename-file-dialog"
|
||||
import { DirectoryPathBreadcrumb } from "@/directories/directory-path-breadcrumb"
|
||||
import { FilePreviewDialog } from "@/files/file-preview-dialog"
|
||||
import { inProgressFileUploadCountAtom } from "@/files/store"
|
||||
import { cutHandlesAtom, inProgressFileUploadCountAtom } from "@/files/store"
|
||||
import { UploadFileDialog } from "@/files/upload-file-dialog"
|
||||
import type { FileDragInfo } from "@/files/use-file-drop"
|
||||
|
||||
@@ -250,6 +251,7 @@ function DirectoryContentContextMenu({
|
||||
const [target, setTarget] = useAtom(contextMenuTargetItemsAtom)
|
||||
const setOptimisticDeletedItems = useSetAtom(optimisticDeletedItemsAtom)
|
||||
const setBackgroundTaskProgress = useSetAtom(backgroundTaskProgressAtom)
|
||||
const setCutHandles = useSetAtom(cutHandlesAtom)
|
||||
const moveToTrashMutation = useContextMutation(api.filesystem.moveToTrash)
|
||||
|
||||
const { mutate: moveToTrash } = useMutation({
|
||||
@@ -293,6 +295,13 @@ function DirectoryContentContextMenu({
|
||||
},
|
||||
})
|
||||
|
||||
const handleCut = () => {
|
||||
const selectedItems = store.get(contextMenuTargetItemsAtom)
|
||||
if (selectedItems.length > 0) {
|
||||
setCutHandles(selectedItems.map(newFileSystemHandle))
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
const selectedItems = store.get(contextMenuTargetItemsAtom)
|
||||
if (selectedItems.length > 0) {
|
||||
@@ -314,6 +323,10 @@ function DirectoryContentContextMenu({
|
||||
{target.length > 0 && (
|
||||
<ContextMenuContent>
|
||||
<RenameMenuItem />
|
||||
<ContextMenuItem onClick={handleCut}>
|
||||
<ScissorsIcon />
|
||||
Cut
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
variant="destructive"
|
||||
onClick={handleDelete}
|
||||
|
||||
@@ -71,7 +71,6 @@ export type DeleteResult = {
|
||||
}
|
||||
|
||||
export function newFileSystemHandle(item: FileSystemItem): FileSystemHandle {
|
||||
console.log("item", item)
|
||||
switch (item.kind) {
|
||||
case FileType.File:
|
||||
return { kind: item.kind, id: item.doc._id }
|
||||
|
||||
Reference in New Issue
Block a user