refactor(widgets): migrate all widgets from useSuspenseQuery to useQuery

Non-blocking data fetching for all 38 widget components. Widgets now
render immediately and show data when available, with loading state
delegated to the global refresh indicator in the board header.
This commit is contained in:
Thomas Camlong committed 2026-05-28 18:07:03 +02:00
1 parent cb5ae65f8a
commit 394debbea1
38 files changed
+96 -67

No files matched your search

+19 -15
View File
@@ -144,7 +144,7 @@ interface AnchorNoteWidgetContentProps {
const AnchorNoteWidgetContent = ({ options, integrationId, noteId }: AnchorNoteWidgetContentProps) => {
const t = useScopedI18n("widget.anchorNote");
const [note, { refetch }] = clientApi.widget.anchorNotes.getNote.useSuspenseQuery(
const { data: note, refetch } = clientApi.widget.anchorNotes.getNote.useQuery(
{
integrationId,
noteId,
@@ -159,31 +159,32 @@ const AnchorNoteWidgetContent = ({ options, integrationId, noteId }: AnchorNoteW
const { mutateAsync: updateNoteAsync, isPending: isUpdating } = clientApi.widget.anchorNotes.updateNote.useMutation();
const [isEditing, setIsEditing] = useState(false);
const [draftTitle, setDraftTitle] = useState(note.title);
const [draftContent, setDraftContent] = useState(note.content ?? "");
const [draftTitle, setDraftTitle] = useState(note?.title ?? "");
const [draftContent, setDraftContent] = useState(note?.content ?? "");
const [saveError, setSaveError] = useState<string | null>(null);
useEffect(() => {
if (isEditing) return;
if (isEditing || !note) return;
startTransition(() => {
setDraftTitle(note.title);
setDraftContent(note.content ?? "");
});
}, [isEditing, note.content, note.title]);
}, [isEditing, note]);
const canEdit = canEditPermission(note.permission);
const isViewer = note.permission === "viewer";
const updatedAt = useMemo(() => new Date(note.updatedAt), [note.updatedAt]);
const canEdit = canEditPermission(note?.permission ?? "viewer");
const isViewer = !note || note.permission === "viewer";
const updatedAt = useMemo(() => (note ? new Date(note.updatedAt) : new Date()), [note]);
const updatedAtRelative = useTimeAgo(updatedAt, 30000);
const hasChanges = useMemo(() => {
if (!note) return false;
const normalizedTitle = draftTitle.trim() || note.title;
return normalizedTitle !== note.title || draftContent !== (note.content ?? "");
}, [draftContent, draftTitle, note.content, note.title]);
}, [draftContent, draftTitle, note]);
const editorValue = useMemo(() => parseStoredContent(draftContent), [draftContent]);
const readOnlyValue = useMemo(() => parseStoredContent(note.content), [note.content]);
const readOnlyValue = useMemo(() => parseStoredContent(note?.content), [note?.content]);
const handleEditorChange = useCallback<ReactQuillOnChange>(
(_html, _delta, source, editor) => {
if (!isEditing || source !== "user") return;
@@ -194,26 +195,27 @@ const AnchorNoteWidgetContent = ({ options, integrationId, noteId }: AnchorNoteW
);
const handleEdit = useCallback(() => {
if (!canEdit) return;
if (!canEdit || !note) return;
startTransition(() => {
setDraftTitle(note.title);
setDraftContent(note.content ?? "");
setSaveError(null);
setIsEditing(true);
});
}, [canEdit, note.content, note.title]);
}, [canEdit, note]);
const handleCancel = useCallback(() => {
if (!note) return;
startTransition(() => {
setDraftTitle(note.title);
setDraftContent(note.content ?? "");
setSaveError(null);
setIsEditing(false);
});
}, [note.content, note.title]);
}, [note]);
const handleSave = useCallback(async () => {
if (!canEdit) return;
if (!canEdit || !note) return;
const normalizedTitle = draftTitle.trim() || note.title || t("untitled");
@@ -260,7 +262,9 @@ const AnchorNoteWidgetContent = ({ options, integrationId, noteId }: AnchorNoteW
},
},
);
}, [canEdit, draftContent, draftTitle, hasChanges, integrationId, note.title, noteId, refetch, t, updateNoteAsync]);
}, [canEdit, draftContent, draftTitle, hasChanges, integrationId, note, noteId, refetch, t, updateNoteAsync]);
if (!note) return null;
return (
<Stack h="100%" gap="xs" p="sm">