diff options
| author | Fuwn <[email protected]> | 2026-02-09 23:48:27 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-09 23:48:27 -0800 |
| commit | d0d49d9b759f841c0a05b2410efbd26957a813dc (patch) | |
| tree | 6c72bd6a27e13b6cadb2d1797afedc172ffc32e7 /apps/web/app/reader | |
| parent | fix: P0 correctness and security fixes (diff) | |
| download | asa.news-d0d49d9b759f841c0a05b2410efbd26957a813dc.tar.xz asa.news-d0d49d9b759f841c0a05b2410efbd26957a813dc.zip | |
fix: P0 correctness/security fixes and P1 lint error resolution
P0: add missing 'developer' case to check_custom_feed_limit trigger,
scope user_entry_states join to authenticated user in API v1 entries,
replace in-memory rate limiting with Supabase-backed check_rate_limit RPC.
P1: fix all 9 ESLint errors — useSyncExternalStore for useIsMobile,
restructure WebhookSection to avoid set-state-in-effect, move ref
mutations into useEffect, replace <a> with <Link> on shared page,
ignore generated public/sw.js in eslint config.
Diffstat (limited to 'apps/web/app/reader')
5 files changed, 44 insertions, 26 deletions
diff --git a/apps/web/app/reader/_components/entry-detail-panel.tsx b/apps/web/app/reader/_components/entry-detail-panel.tsx index 6982083..9848d3f 100644 --- a/apps/web/app/reader/_components/entry-detail-panel.tsx +++ b/apps/web/app/reader/_components/entry-detail-panel.tsx @@ -186,7 +186,7 @@ export function EntryDetailPanel({ } } - setUnpositionedHighlights(failedHighlights) + queueMicrotask(() => setUnpositionedHighlights(failedHighlights)) }, [sanitisedContent, highlightsData]) const handleTextSelection = useCallback(() => { diff --git a/apps/web/app/reader/_components/reader-layout-shell.tsx b/apps/web/app/reader/_components/reader-layout-shell.tsx index fe158b5..391e6a4 100644 --- a/apps/web/app/reader/_components/reader-layout-shell.tsx +++ b/apps/web/app/reader/_components/reader-layout-shell.tsx @@ -162,9 +162,15 @@ export function ReaderLayoutShell({ const sidebarGroupRef = useGroupRef() const sidebarMaxWidthRef = useRef(sidebarMaxWidth) - sidebarMaxWidthRef.current = sidebarMaxWidth const sidebarOnLayoutChangedRef = useRef(sidebarLayout.onLayoutChanged) - sidebarOnLayoutChangedRef.current = sidebarLayout.onLayoutChanged + + useEffect(() => { + sidebarMaxWidthRef.current = sidebarMaxWidth + }, [sidebarMaxWidth]) + + useEffect(() => { + sidebarOnLayoutChangedRef.current = sidebarLayout.onLayoutChanged + }, [sidebarLayout.onLayoutChanged]) useEffect(() => { useUserInterfaceStore.getState().setResetSidebarLayout(() => { diff --git a/apps/web/app/reader/_components/reader-shell.tsx b/apps/web/app/reader/_components/reader-shell.tsx index 8a5a044..65a4900 100644 --- a/apps/web/app/reader/_components/reader-shell.tsx +++ b/apps/web/app/reader/_components/reader-shell.tsx @@ -67,7 +67,10 @@ export function ReaderShell({ const detailGroupRef = useGroupRef() const detailOnLayoutChangedRef = useRef(detailLayout.onLayoutChanged) - detailOnLayoutChangedRef.current = detailLayout.onLayoutChanged + + useEffect(() => { + detailOnLayoutChangedRef.current = detailLayout.onLayoutChanged + }, [detailLayout.onLayoutChanged]) useEffect(() => { useUserInterfaceStore.getState().setResetDetailLayout(() => { diff --git a/apps/web/app/reader/_components/search-overlay.tsx b/apps/web/app/reader/_components/search-overlay.tsx index 5cfdb57..11e709c 100644 --- a/apps/web/app/reader/_components/search-overlay.tsx +++ b/apps/web/app/reader/_components/search-overlay.tsx @@ -50,10 +50,6 @@ export function SearchOverlay({ onClose }: SearchOverlayProperties) { }, []) useEffect(() => { - setSelectedResultIndex(-1) - }, [searchQuery]) - - useEffect(() => { function handleKeyDown(event: KeyboardEvent) { if (event.key === "Escape") { onClose() @@ -122,7 +118,10 @@ export function SearchOverlay({ onClose }: SearchOverlayProperties) { ref={inputReference} type="text" value={searchQuery} - onChange={(event) => setSearchQuery(event.target.value)} + onChange={(event) => { + setSearchQuery(event.target.value) + setSelectedResultIndex(-1) + }} onKeyDown={handleInputKeyDown} placeholder="search entries..." className="w-full bg-transparent text-text-primary outline-none placeholder:text-text-dim" diff --git a/apps/web/app/reader/settings/_components/api-settings.tsx b/apps/web/app/reader/settings/_components/api-settings.tsx index cca673f..102e3fe 100644 --- a/apps/web/app/reader/settings/_components/api-settings.tsx +++ b/apps/web/app/reader/settings/_components/api-settings.tsx @@ -1,9 +1,8 @@ "use client" -import { useState, useEffect } from "react" +import { useState } from "react" import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { useUserProfile } from "@/lib/queries/use-user-profile" -import { queryKeys } from "@/lib/queries/query-keys" import { notify } from "@/lib/notify" interface ApiKey { @@ -293,21 +292,36 @@ function ApiKeysSection() { ) } -function WebhookSection() { +function WebhookSectionLoading() { const { data: webhookConfig, isLoading } = useWebhookConfig() + + if (isLoading) { + return <p className="text-text-dim">loading webhook configuration ...</p> + } + + if (!webhookConfig) { + return <p className="text-text-dim">failed to load webhook configuration</p> + } + + return <WebhookSection initialConfiguration={webhookConfig} /> +} + +function WebhookSection({ + initialConfiguration, +}: { + initialConfiguration: WebhookConfiguration +}) { + const { data: webhookConfig } = useWebhookConfig() const updateWebhookConfig = useUpdateWebhookConfig() const testWebhook = useTestWebhook() - const [webhookUrl, setWebhookUrl] = useState("") - const [webhookSecret, setWebhookSecret] = useState("") + const [webhookUrl, setWebhookUrl] = useState( + initialConfiguration.webhookUrl ?? "" + ) + const [webhookSecret, setWebhookSecret] = useState( + initialConfiguration.webhookSecret ?? "" + ) const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false) - useEffect(() => { - if (webhookConfig) { - setWebhookUrl(webhookConfig.webhookUrl ?? "") - setWebhookSecret(webhookConfig.webhookSecret ?? "") - } - }, [webhookConfig]) - function handleSaveWebhookConfig() { updateWebhookConfig.mutate( { @@ -371,10 +385,6 @@ function WebhookSection() { setHasUnsavedChanges(true) } - if (isLoading) { - return <p className="text-text-dim">loading webhook configuration ...</p> - } - return ( <div className="mb-6"> <h3 className="mb-2 text-text-primary">webhooks</h3> @@ -503,7 +513,7 @@ export function ApiSettings() { return ( <div className="px-4 py-3"> <ApiKeysSection /> - <WebhookSection /> + <WebhookSectionLoading /> <div> <h3 className="mb-2 text-text-primary">api documentation</h3> |