this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: extract local toast hook

+43 -56
+3 -14
components/form-builder.tsx
··· 41 41 type FormMetadataDraft, 42 42 } from "@/components/form-builder-panels"; 43 43 import { useI18n } from "@/components/i18n-provider"; 44 + import { useLocalToasts } from "@/components/use-local-toasts"; 44 45 import { Button } from "@/components/ui/button"; 45 46 import { ConfirmDialog } from "@/components/ui/confirm-dialog"; 46 - import { ToastViewport, type ToastData } from "@/components/ui/toast"; 47 + import { ToastViewport } from "@/components/ui/toast"; 47 48 import { 48 49 getBranchRules, 49 50 isQuestionBlock, ··· 248 249 ? { kind: "block", blockId: initialForm.blocks[0].id } 249 250 : { kind: "form" }, 250 251 ); 251 - const [toasts, setToasts] = useState<ToastData[]>([]); 252 + const { toasts, showToast, dismissToast } = useLocalToasts(); 252 253 const [busy, setBusy] = useState<string | null>(null); 253 254 const [isDndReady, setIsDndReady] = useState(false); 254 255 ··· 359 360 window.addEventListener("pointerdown", handlePointerDown); 360 361 return () => window.removeEventListener("pointerdown", handlePointerDown); 361 362 }, [isBlockMenuOpen]); 362 - 363 - function showToast( 364 - message: string, 365 - variant: ToastData["variant"] = "success", 366 - ) { 367 - const id = crypto.randomUUID(); 368 - setToasts((current) => [...current, { id, message, variant }]); 369 - } 370 - 371 - function dismissToast(id: string) { 372 - setToasts((current) => current.filter((toast) => toast.id !== id)); 373 - } 374 363 375 364 async function withTask(task: string, runner: () => Promise<void>) { 376 365 setBusy(task);
+3 -14
components/organization-settings-panel.tsx
··· 12 12 revokeOrganizationInviteLinkAction, 13 13 } from "@/app/(creator)/actions"; 14 14 import { DeleteOrganizationButton } from "@/components/delete-organization-button"; 15 + import { useLocalToasts } from "@/components/use-local-toasts"; 15 16 import { Badge } from "@/components/ui/badge"; 16 17 import { Button } from "@/components/ui/button"; 17 18 import { Input } from "@/components/ui/input"; ··· 22 23 SelectTrigger, 23 24 SelectValue, 24 25 } from "@/components/ui/select"; 25 - import { ToastViewport, type ToastData } from "@/components/ui/toast"; 26 + import { ToastViewport } from "@/components/ui/toast"; 26 27 import { useI18n } from "@/components/i18n-provider"; 27 28 import { UserAvatar } from "@/components/user-avatar"; 28 29 import type { OrganizationSettingsSummary } from "@/lib/form-types"; ··· 43 44 const [selectedOrganizationId, setSelectedOrganizationId] = useState< 44 45 string | null 45 46 >(initialOrganizationId ?? organizations[0]?.id ?? null); 46 - const [toasts, setToasts] = useState<ToastData[]>([]); 47 + const { toasts, showToast, dismissToast } = useLocalToasts(); 47 48 const selectedOrganization = useMemo( 48 49 () => 49 50 organizations.find( ··· 53 54 null, 54 55 [organizations, selectedOrganizationId], 55 56 ); 56 - 57 - function showToast( 58 - message: string, 59 - variant: ToastData["variant"] = "success", 60 - ) { 61 - const id = crypto.randomUUID(); 62 - setToasts((current) => [...current, { id, message, variant }]); 63 - } 64 - 65 - function dismissToast(id: string) { 66 - setToasts((current) => current.filter((toast) => toast.id !== id)); 67 - } 68 57 69 58 async function copyInviteLink(token: string) { 70 59 try {
+3 -14
components/public-form-runner.tsx
··· 20 20 } from "react"; 21 21 22 22 import { useI18n } from "@/components/i18n-provider"; 23 + import { useLocalToasts } from "@/components/use-local-toasts"; 23 24 import { Button, buttonVariants } from "@/components/ui/button"; 24 25 import { Card } from "@/components/ui/card"; 25 26 import { DatePickerInput } from "@/components/ui/date-picker-input"; 26 27 import { AuthoredMarkdown } from "@/components/ui/authored-markdown"; 27 28 import { Input } from "@/components/ui/input"; 28 - import { ToastViewport, type ToastData } from "@/components/ui/toast"; 29 + import { ToastViewport } from "@/components/ui/toast"; 29 30 import { Textarea } from "@/components/ui/textarea"; 30 31 import { 31 32 AGREEMENT_ANSWER_VALUES, ··· 89 90 const { t } = useI18n(); 90 91 const [answers, setAnswers] = useState<Record<string, AnswerValue>>({}); 91 92 const [route, setRoute] = useState({ history: initialHistory, cursor: 0 }); 92 - const [toasts, setToasts] = useState<ToastData[]>([]); 93 + const { toasts, showToast, dismissToast } = useLocalToasts(); 93 94 const [isSubmitting, setIsSubmitting] = useState(false); 94 95 const [isComplete, setIsComplete] = useState(false); 95 96 const [hasResolvedDraft, setHasResolvedDraft] = useState(false); ··· 149 150 [blockId]: value, 150 151 })); 151 152 } 152 - 153 - const showToast = useCallback( 154 - (message: string, variant: ToastData["variant"] = "success") => { 155 - const id = crypto.randomUUID(); 156 - setToasts((current) => [...current, { id, message, variant }]); 157 - }, 158 - [], 159 - ); 160 - 161 - const dismissToast = useCallback((id: string) => { 162 - setToasts((current) => current.filter((toast) => toast.id !== id)); 163 - }, []); 164 153 165 154 const hasDraftProgress = useMemo(() => { 166 155 const hasAnyAnswer = Object.values(answers).some((value) =>
+3 -14
components/response-export-actions.tsx
··· 9 9 } from "lucide-react"; 10 10 11 11 import { useI18n } from "@/components/i18n-provider"; 12 + import { useLocalToasts } from "@/components/use-local-toasts"; 12 13 import { Button } from "@/components/ui/button"; 13 - import { ToastViewport, type ToastData } from "@/components/ui/toast"; 14 + import { ToastViewport } from "@/components/ui/toast"; 14 15 15 16 type ResponseExportFormat = "csv" | "xlsx"; 16 17 ··· 38 39 null, 39 40 ); 40 41 const [isMenuOpen, setIsMenuOpen] = useState(false); 41 - const [toasts, setToasts] = useState<ToastData[]>([]); 42 + const { toasts, showToast, dismissToast } = useLocalToasts(); 42 43 const menuRef = useRef<HTMLDivElement | null>(null); 43 - 44 - function showToast( 45 - message: string, 46 - variant: ToastData["variant"] = "success", 47 - ) { 48 - const id = crypto.randomUUID(); 49 - setToasts((current) => [...current, { id, message, variant }]); 50 - } 51 - 52 - function dismissToast(id: string) { 53 - setToasts((current) => current.filter((toast) => toast.id !== id)); 54 - } 55 44 56 45 useEffect(() => { 57 46 function handlePointerDown(event: PointerEvent) {
+31
components/use-local-toasts.ts
··· 1 + "use client"; 2 + 3 + import { useCallback, useState } from "react"; 4 + 5 + import type { ToastData } from "@/components/ui/toast"; 6 + 7 + export function useLocalToasts() { 8 + const [toasts, setToasts] = useState<ToastData[]>([]); 9 + 10 + const showToast = useCallback( 11 + ( 12 + message: string, 13 + variant: ToastData["variant"] = "success", 14 + duration?: number, 15 + ) => { 16 + const id = crypto.randomUUID(); 17 + setToasts((current) => [...current, { id, message, variant, duration }]); 18 + }, 19 + [], 20 + ); 21 + 22 + const dismissToast = useCallback((id: string) => { 23 + setToasts((current) => current.filter((toast) => toast.id !== id)); 24 + }, []); 25 + 26 + return { 27 + toasts, 28 + showToast, 29 + dismissToast, 30 + }; 31 + }