Scrapboard.org client
at main 157 lines 5.1 kB view raw
1import { 2 Dialog, 3 DialogClose, 4 DialogContent, 5 DialogDescription, 6 DialogFooter, 7 DialogHeader, 8 DialogTitle, 9 DialogTrigger, 10} from "@/components/ui/dialog"; 11import { useAuth } from "@/lib/hooks/useAuth"; 12import { useState } from "react"; 13import { Button } from "./ui/button"; 14import { LoaderCircle, TrashIcon } from "lucide-react"; 15import { Board, useBoardsStore } from "@/lib/stores/boards"; 16import { toast } from "sonner"; 17import { AtUri } from "@atproto/api"; 18import { LIST_COLLECTION, LIST_ITEM_COLLECTION } from "@/constants"; 19import { useBoardItemsStore } from "@/lib/stores/boardItems"; 20import clsx from "clsx"; 21import { Progress } from "./ui/progress"; 22import { redirect } from "next/navigation"; 23 24export function DeleteButton({ board, rkey }: { board: Board; rkey: string }) { 25 const { agent } = useAuth(); 26 const [isLoading, setLoading] = useState(false); 27 const [isOpen, setOpen] = useState(false); 28 const [name, setName] = useState(board.name); 29 const [description, setDescription] = useState(board.description); 30 const { setBoard, removeBoard } = useBoardsStore(); 31 const { boardItems } = useBoardItemsStore(); 32 const [progress, setProgress] = useState(0); 33 const [totalItems, setTotalItems] = useState(0); 34 35 if (agent == null) return <div>not logged in :(</div>; 36 return ( 37 <Dialog open={isOpen} onOpenChange={setOpen}> 38 <DialogTrigger asChild> 39 <span 40 onClick={(e) => { 41 e.stopPropagation(); 42 }} 43 className={clsx("cursor-pointer")} 44 > 45 <Button 46 className={clsx( 47 "cursor-pointer", 48 "text-red-400 hover:text-red-400" 49 )} 50 variant={"ghost"} 51 > 52 <TrashIcon /> Delete Board 53 </Button> 54 </span> 55 </DialogTrigger> 56 57 <DialogContent> 58 <DialogHeader> 59 <DialogTitle>Delete board?</DialogTitle> 60 <DialogDescription className="pt-5"> 61 Are you sure you want to delete the board and all items in it? 62 Looked like it was a pretty good board you had going there. 63 </DialogDescription> 64 </DialogHeader> 65 66 {isLoading && ( 67 <div className="space-y-2 my-4"> 68 <div className="text-sm text-muted-foreground"> 69 Deleting items... ({progress}/{totalItems}) 70 </div> 71 <Progress value={(progress / Math.max(totalItems, 1)) * 100} /> 72 </div> 73 )} 74 75 <DialogFooter> 76 {!isLoading && ( 77 <DialogClose> 78 <Button className="cursor-pointer" variant={"secondary"}> 79 Cancel 80 </Button> 81 </DialogClose> 82 )} 83 84 {!isLoading ? ( 85 <Button 86 onClick={async (e) => { 87 e.stopPropagation(); 88 89 setLoading(true); 90 try { 91 const listUri = AtUri.make( 92 agent.assertDid, 93 LIST_COLLECTION, 94 rkey 95 ); 96 const items = Array.from( 97 boardItems 98 .entries() 99 .filter((e) => AtUri.make(e[1].list).rkey == listUri.rkey) 100 ); 101 102 setTotalItems(items.length + 1); // +1 for the board itself 103 setProgress(0); 104 105 for (let i = 0; i < items.length; i++) { 106 const item = items[i]; 107 const itemDeleteRes = 108 await agent.com.atproto.repo.deleteRecord({ 109 repo: agent.assertDid, 110 collection: LIST_ITEM_COLLECTION, 111 rkey: item[0], 112 }); 113 114 if (!itemDeleteRes.success) { 115 toast(`Failed to delete ${item[0]}`); 116 } 117 118 setProgress(i + 1); 119 } 120 121 const listDeleteRes = 122 await agent.com.atproto.repo.deleteRecord({ 123 repo: agent.assertDid, 124 collection: LIST_COLLECTION, 125 rkey: rkey, 126 }); 127 128 setProgress(totalItems); 129 130 if (listDeleteRes.success) { 131 removeBoard(agent.assertDid, rkey); 132 toast("Board deleted"); 133 setOpen(false); 134 redirect("/boards"); 135 } else { 136 toast("Failed to delete board"); 137 } 138 } finally { 139 setLoading(false); 140 } 141 }} 142 disabled={name.length <= 0} 143 className="cursor-pointer" 144 > 145 Confirm 146 </Button> 147 ) : ( 148 <Button disabled className="cursor-not-allowed"> 149 <LoaderCircle className="animate-spin mr-2" /> 150 Deleting... 151 </Button> 152 )} 153 </DialogFooter> 154 </DialogContent> 155 </Dialog> 156 ); 157}