diff --git a/web/components/accounts-table.tsx b/web/components/accounts-table.tsx index 549de6a..11c07de 100644 --- a/web/components/accounts-table.tsx +++ b/web/components/accounts-table.tsx @@ -68,7 +68,6 @@ export default function AccountsTable({ const [sortDir, setSortDir] = useState("desc"); const [editingKey, setEditingKey] = useState(null); const [, startTransition] = useTransition(); - const [refreshing, setRefreshing] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [deleting, setDeleting] = useState(false); @@ -112,16 +111,15 @@ export default function AccountsTable({ }); } + // Force-evict the cache and re-fetch page 1. Used by the create-success + // path so a freshly added row appears in its sorted position. Keeping + // the function (instead of inlining at the call site) means a future + // 'pull to refresh' gesture has a single hook. async function refresh() { - setRefreshing(true); - try { - const page = await refreshAccounts({ prefix: prefixPattern, dir: sortDir }); - setRows(page.rows); - setHasMore(page.hasMore); - setTotal(page.total); - } finally { - setRefreshing(false); - } + const page = await refreshAccounts({ prefix: prefixPattern, dir: sortDir }); + setRows(page.rows); + setHasMore(page.hasMore); + setTotal(page.total); } async function changeSort(next: SortDir) { @@ -146,7 +144,7 @@ export default function AccountsTable({ // 300px rootMargin so the next page starts loading before the user // hits the bottom — feels seamless when scrolling fast. useEffect(() => { - if (!hasMore || loadingMore || refreshing) return; + if (!hasMore || loadingMore) return; const sentinel = sentinelRef.current; if (!sentinel) return; @@ -171,7 +169,7 @@ export default function AccountsTable({ ); observer.observe(sentinel); return () => observer.disconnect(); - }, [hasMore, loadingMore, refreshing, rows.length, prefixPattern, sortDir]); + }, [hasMore, loadingMore, rows.length, prefixPattern, sortDir]); async function confirmDelete() { if (!deleteTarget) return; @@ -196,8 +194,6 @@ export default function AccountsTable({ setCreateOpen(true)} />
@@ -225,8 +221,6 @@ export default function AccountsTable({ setCreateOpen(true)} /> @@ -446,14 +440,10 @@ function CardRow({ label, children }: { label: string; children: React.ReactNode function PageHead({ total, loaded, - onRefresh, - refreshing, onAdd, }: { total: number; loaded: number; - onRefresh: () => void; - refreshing: boolean; onAdd: () => void; }) { // total = COUNT(*) from the API; loaded = how many rows the user has @@ -461,44 +451,32 @@ function PageHead({ // from the total, so a fully-scrolled list reads cleanly. const showLoaded = loaded > 0 && loaded < total; return ( -
-
-

- Table -

-

+
+

+ Table +

+
+

Accounts {total}

- {showLoaded && ( -

- Showing {loaded} of {total} -

- )} -
-
-
+ {showLoaded && ( +

+ Showing {loaded} of {total} +

+ )}
); } diff --git a/web/components/users-table.tsx b/web/components/users-table.tsx index 4f32f2b..6b64b73 100644 --- a/web/components/users-table.tsx +++ b/web/components/users-table.tsx @@ -70,7 +70,6 @@ export default function UsersTable({ const [sortDir, setSortDir] = useState("desc"); const [editingKey, setEditingKey] = useState(null); const [, startTransition] = useTransition(); - const [refreshing, setRefreshing] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [deleting, setDeleting] = useState(false); @@ -123,20 +122,17 @@ export default function UsersTable({ }); } + // Force-evict the cache and re-fetch page 1. Used by the create-success + // path so a freshly added row appears in its sorted position. async function refresh() { - setRefreshing(true); - try { - const page = await refreshUsers({ - prefix: prefixPattern, - sort: sortKey, - dir: sortDir, - }); - setRows(page.rows); - setHasMore(page.hasMore); - setTotal(page.total); - } finally { - setRefreshing(false); - } + const page = await refreshUsers({ + prefix: prefixPattern, + sort: sortKey, + dir: sortDir, + }); + setRows(page.rows); + setHasMore(page.hasMore); + setTotal(page.total); } async function changeSort(nextKey: SortKey) { @@ -165,7 +161,7 @@ export default function UsersTable({ } useEffect(() => { - if (!hasMore || loadingMore || refreshing) return; + if (!hasMore || loadingMore) return; const sentinel = sentinelRef.current; if (!sentinel) return; const observer = new IntersectionObserver( @@ -190,7 +186,7 @@ export default function UsersTable({ ); observer.observe(sentinel); return () => observer.disconnect(); - }, [hasMore, loadingMore, refreshing, rows.length, prefixPattern, sortKey, sortDir]); + }, [hasMore, loadingMore, rows.length, prefixPattern, sortKey, sortDir]); async function confirmDelete() { if (!deleteTarget) return; @@ -231,8 +227,6 @@ export default function UsersTable({ setCreateOpen(true)} />
@@ -258,8 +252,6 @@ export default function UsersTable({ setCreateOpen(true)} /> @@ -468,54 +460,38 @@ function MobileRow({ label, children }: { label: string; children: React.ReactNo function PageHead({ total, loaded, - onRefresh, - refreshing, onAdd, }: { total: number; loaded: number; - onRefresh: () => void; - refreshing: boolean; onAdd: () => void; }) { const showLoaded = loaded > 0 && loaded < total; return ( -
-
-

Table

-

+
+

Table

+
+

Users {total}

- {showLoaded && ( -

- Showing {loaded} of {total} -

- )} -
-
-
+ {showLoaded && ( +

+ Showing {loaded} of {total} +

+ )}
); }