From b22449dba9e8043d6ec3ce969d8fd8ce9907d491 Mon Sep 17 00:00:00 2001 From: FTCHD <144691102+FTCHD@users.noreply.github.com> Date: Thu, 20 Aug 2026 18:49:50 +0300 Subject: [PATCH] virtualize remotes list --- package-lock.json | 1 + package.json | 1 + src/pages/Settings/RemotesSection.tsx | 106 ++++++++++++++++++-------- 3 files changed, 77 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index bdf4a27..81df767 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@tanstack/query-persist-client-core": "^5.101.4", "@tanstack/query-sync-storage-persister": "^5.101.4", "@tanstack/react-query": "^5.101.4", + "@tanstack/react-virtual": "^3.11.3", "@tauri-apps/api": "~2.11.0", "@tauri-apps/plugin-autostart": "^2.5.1", "@tauri-apps/plugin-clipboard-manager": "^2.3.2", diff --git a/package.json b/package.json index 922fead..645ef23 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@tanstack/query-persist-client-core": "^5.101.4", "@tanstack/query-sync-storage-persister": "^5.101.4", "@tanstack/react-query": "^5.101.4", + "@tanstack/react-virtual": "^3.11.3", "@tauri-apps/api": "~2.11.0", "@tauri-apps/plugin-autostart": "^2.5.1", "@tauri-apps/plugin-clipboard-manager": "^2.3.2", diff --git a/src/pages/Settings/RemotesSection.tsx b/src/pages/Settings/RemotesSection.tsx index 5f84319..9766883 100644 --- a/src/pages/Settings/RemotesSection.tsx +++ b/src/pages/Settings/RemotesSection.tsx @@ -10,6 +10,7 @@ import { Spinner, } from '@heroui/react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { useVirtualizer } from '@tanstack/react-virtual' import { ask, message } from '@tauri-apps/plugin-dialog' import { platform } from '@tauri-apps/plugin-os' import { @@ -21,7 +22,7 @@ import { SettingsIcon, Trash2Icon, } from 'lucide-react' -import { type ReactNode, startTransition, useEffect, useMemo, useState } from 'react' +import { type ReactNode, startTransition, useEffect, useMemo, useRef, useState } from 'react' import { useSearchParams } from 'react-router-dom' import { onErrorDialog } from '../../../lib/errors' import { formatBytes } from '../../../lib/format' @@ -64,6 +65,18 @@ export default function RemotesSection() { [sortedRemotes, searchQuery] ) + // Virtualize the remotes list: each RemoteCard is a fixed-height (h-20 = 80px) card with a + // gap-2.5 (10px) between rows, so a row slot is 90px. Each card also fires its own queries, + // so windowing keeps a long list from mounting every card (and its request fan-out) at once. + const scrollRef = useRef(null) + + const rowVirtualizer = useVirtualizer({ + count: filteredRemotes.length, + getScrollElement: () => scrollRef.current, + estimateSize: () => 90, + overscan: 6, + }) + const [pickedRemote, setPickedRemote] = useState(null) const [editingDrawerOpen, setEditingDrawerOpen] = useState(false) @@ -201,7 +214,7 @@ export default function RemotesSection() { {Placeholder} {!Placeholder && ( -
+
{sortedRemotes.length > 5 && ( )} - {filteredRemotes.map((remote) => ( - { - startTransition(() => { - setPickedRemote(remote) - setAutoMountDrawerOpen(true) - }) +
+
{ - startTransition(() => { - setPickedRemote(remote) - setEditingDrawerOpen(true) - }) - }} - onDeletePress={async () => { - const confirmation = await ask( - `Are you sure you want to remove ${remote}? This action cannot be reverted.`, - { title: `Removing ${remote}`, kind: 'warning' } + > + {rowVirtualizer.getVirtualItems().map((virtualRow) => { + const remote = filteredRemotes[virtualRow.index] + return ( +
+ { + startTransition(() => { + setPickedRemote(remote) + setAutoMountDrawerOpen(true) + }) + }} + onConfigPress={() => { + startTransition(() => { + setPickedRemote(remote) + setEditingDrawerOpen(true) + }) + }} + onDeletePress={async () => { + const confirmation = await ask( + `Are you sure you want to remove ${remote}? This action cannot be reverted.`, + { + title: `Removing ${remote}`, + kind: 'warning', + } + ) + + if (!confirmation) { + return + } + + deleteRemoteMutation.mutate(remote) + }} + /> +
) - - if (!confirmation) { - return - } - - deleteRemoteMutation.mutate(remote) - }} - /> - ))} + })} +
+
)} @@ -337,7 +381,7 @@ function RemoteCard({ key={remote} shadow="sm" isBlurred={true} - className="h-20 border-[0.5px] dark:border-none border-divider bg-content3/50 dark:bg-content2/90" + className="w-full h-20 border-[0.5px] dark:border-none border-divider bg-content3/50 dark:bg-content2/90" isPressable={true} onPress={onConfigPress} >