Scrapboard.org client
at main 138 lines 4.3 kB view raw
1import { 2 Dialog, 3 DialogContent, 4 DialogDescription, 5 DialogFooter, 6 DialogHeader, 7 DialogTitle, 8 DialogTrigger, 9} from "@/components/ui/dialog"; 10import { useAuth } from "@/lib/hooks/useAuth"; 11import { useState } from "react"; 12import { Button } from "./ui/button"; 13import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 14import { EditIcon, LoaderCircle } from "lucide-react"; 15import { Board, useBoardsStore } from "@/lib/stores/boards"; 16import { BoardsPicker } from "./BoardPicker"; 17import { toast } from "sonner"; 18import { AtUri } from "@atproto/api"; 19import { LIST_COLLECTION, LIST_ITEM_COLLECTION } from "@/constants"; 20import { FeedItem } from "./Feed"; 21import { BoardItem, useBoardItemsStore } from "@/lib/stores/boardItems"; 22import clsx from "clsx"; 23import { Input } from "./ui/input"; 24import { Textarea } from "./ui/textarea"; 25import { DeleteButton } from "./DeleteButton"; 26 27export function EditButton({ 28 board, 29 rkey, 30 className, 31}: { 32 board: Board; 33 rkey: string; 34 className?: string; 35}) { 36 const { agent } = useAuth(); 37 const [isLoading, setLoading] = useState(false); 38 const [isOpen, setOpen] = useState(false); 39 const [name, setName] = useState(board.name); 40 const [description, setDescription] = useState(board.description); 41 const { setBoard } = useBoardsStore(); 42 43 if (agent == null) return <div>not logged in :(</div>; 44 return ( 45 <Dialog open={isOpen} onOpenChange={setOpen}> 46 <DialogTrigger asChild> 47 <span 48 onClick={(e) => { 49 e.stopPropagation(); 50 }} 51 className={clsx("cursor-pointer", className)} 52 > 53 <Button 54 size="sm" 55 className={clsx("cursor-pointer")} 56 variant={"ghost"} 57 > 58 <EditIcon /> 59 </Button> 60 </span> 61 </DialogTrigger> 62 63 <DialogContent> 64 <DialogHeader> 65 <DialogTitle>Update board</DialogTitle> 66 <DialogDescription className="pt-5"> 67 <Input 68 onChange={(e) => setName(e.target.value)} 69 value={name} 70 className="dark:text-white text-black" 71 /> 72 <Textarea 73 className="mt-2 dark:text-white text-black" 74 onChange={(e) => setDescription(e.target.value)} 75 value={description} 76 placeholder="Enter a description of your board..." 77 /> 78 </DialogDescription> 79 </DialogHeader> 80 <DialogFooter className="justify-between flex w-full"> 81 <DeleteButton board={board} rkey={rkey} /> 82 <Button 83 onClick={async (e) => { 84 e.stopPropagation(); // Optional, but safe 85 86 setLoading(true); 87 try { 88 const record: Board = { 89 name, 90 description, 91 }; 92 93 const result = await agent.com.atproto.repo.applyWrites({ 94 repo: agent.assertDid, 95 writes: [ 96 { 97 $type: "com.atproto.repo.applyWrites#update", 98 collection: LIST_COLLECTION, 99 value: record, 100 rkey: rkey, 101 }, 102 ], 103 }); 104 105 const newRecord = await agent.com.atproto.repo.getRecord({ 106 repo: agent.assertDid, 107 collection: LIST_COLLECTION, 108 rkey: rkey, 109 }); 110 111 const newRecordData = Board.safeParse(newRecord.data.value); 112 113 if ( 114 result?.success && 115 newRecord.success && 116 newRecordData.success 117 ) { 118 setBoard(agent.assertDid, rkey, newRecordData.data); 119 toast("Board updated"); 120 setOpen(false); 121 } else { 122 toast("Failed to update board"); 123 } 124 } finally { 125 setLoading(false); 126 } 127 }} 128 disabled={name.length <= 0} 129 className="cursor-pointer" 130 > 131 {isLoading && <LoaderCircle className="animate-spin ml-2" />} 132 Update 133 </Button> 134 </DialogFooter> 135 </DialogContent> 136 </Dialog> 137 ); 138}