Skip to content

Commit b4bdc3b

Browse files
committed
Render optimizations + fix cursor keys in filetreetable
1 parent aa7cc40 commit b4bdc3b

7 files changed

Lines changed: 99 additions & 66 deletions

File tree

src/cachedfiletree.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,14 @@ export interface DirEntry extends Entry {
4343
priority?: PriorityNumberType,
4444
subdirs: Map<string, DirEntry>,
4545
files: Map<string, FileEntry>,
46-
subrows: FileDirEntry[],
4746
}
4847

4948
export type FileDirEntry = FileEntry | DirEntry;
5049

50+
export interface FileDirEntryView extends Omit<FileDirEntry, "subdirs" | "files" | "isSelected" | "parent" | "index"> {
51+
subrows: FileDirEntryView[],
52+
}
53+
5154
export function isDirEntry(entry: FileDirEntry): entry is DirEntry {
5255
return "files" in entry;
5356
}
@@ -78,7 +81,6 @@ export class CachedFileTree {
7881
percent: 0,
7982
subdirs: new Map(),
8083
files: new Map(),
81-
subrows: [],
8284
isSelected: false,
8385
wantedUpdating: false,
8486
};
@@ -187,7 +189,6 @@ export class CachedFileTree {
187189
percent: 0,
188190
subdirs: new Map(),
189191
files: new Map(),
190-
subrows: [],
191192
parent: node,
192193
isSelected: false,
193194
wantedUpdating: false,
@@ -368,18 +369,30 @@ export class CachedFileTree {
368369
return result;
369370
}
370371

371-
getView(): FileDirEntry[] {
372-
const treeCopy = { ...this.tree };
373-
374-
const recurse = (dir: DirEntry) => {
375-
dir.subdirs.forEach(recurse);
376-
dir.subrows = [
377-
...Array.from(dir.subdirs.values()).map((d) => ({ ...d, parent: undefined })),
378-
...Array.from(dir.files.values()).map((f) => ({ ...f, parent: undefined }))];
372+
getView(): FileDirEntryView[] {
373+
const toView: (e: FileDirEntry) => FileDirEntryView = (e) => {
374+
const subrows: FileDirEntryView[] = [];
375+
if (isDirEntry(e)) {
376+
subrows.push(...Array.from(e.subdirs.values()).map(toView));
377+
subrows.push(...Array.from(e.files.values()).map(toView));
378+
}
379+
return {
380+
name: e.name,
381+
level: e.level,
382+
fullpath: e.fullpath,
383+
size: e.size,
384+
done: e.done,
385+
percent: e.percent,
386+
wantedUpdating: e.wantedUpdating,
387+
want: e.want,
388+
priority: e.priority,
389+
subrows,
390+
};
379391
};
380392

381-
recurse(treeCopy);
393+
const result = Array.from(this.tree.subdirs.values()).map(toView);
394+
result.push(...Array.from(this.tree.files.values()).map(toView));
382395

383-
return treeCopy.subrows;
396+
return result;
384397
}
385398
}

src/components/details.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import { Status, type SessionStatEntry } from "rpc/transmission";
2929
import type { MantineTheme } from "@mantine/core";
3030
import { Anchor, Box, Flex, Container, Group, Table, Tabs, TextInput, LoadingOverlay, Grid, useMantineTheme } from "@mantine/core";
3131
import * as Icon from "react-bootstrap-icons";
32-
import type { FileDirEntry } from "cachedfiletree";
3332
import { CachedFileTree } from "cachedfiletree";
3433
import { useFileTree, useMutateTorrent, useSessionStats, useTorrentDetails } from "queries";
3534
import { ConfigContext } from "config";
@@ -309,11 +308,11 @@ function FileTreePane(props: { torrent: Torrent }) {
309308
const mutation = useMutateTorrent();
310309

311310
const onCheckboxChange = useUnwantedFiles(fileTree, true);
312-
const updateUnwanted = useCallback((entry: FileDirEntry, state: boolean) => {
313-
onCheckboxChange(entry, state);
311+
const updateUnwanted = useCallback((entryPath: string, state: boolean) => {
312+
onCheckboxChange(entryPath, state);
314313
mutation.mutate({
315314
torrentIds: [props.torrent.id],
316-
fields: { [state ? "files-wanted" : "files-unwanted"]: fileTree.getChildFilesIndexes(entry.fullpath) },
315+
fields: { [state ? "files-wanted" : "files-unwanted"]: fileTree.getChildFilesIndexes(entryPath) },
317316
});
318317
}, [fileTree, mutation, onCheckboxChange, props.torrent.id]);
319318

src/components/fileicon.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import * as Icon from "react-bootstrap-icons";
2020
import React from "react";
2121
import { useMantineTheme, type DefaultMantineColor } from "@mantine/core";
22+
import { useRowSelected } from "./tables/common";
2223

2324
interface FileType {
2425
icon: React.FunctionComponent<Icon.IconProps>,
@@ -80,9 +81,9 @@ const extensions = fileTypes.reduce<Record<string, FileType>>((v, c) => {
8081
return v;
8182
}, {});
8283

83-
export function FileIcon({ name, selected }: { name: string, selected: boolean }) {
84+
export function FileIcon({ name }: { name: string }) {
8485
const theme = useMantineTheme();
85-
86+
const selected = useRowSelected();
8687
const ext = name.substring(name.lastIndexOf(".") + 1).toLowerCase();
8788
const FileIcon = Object.prototype.hasOwnProperty.call(extensions, ext) ? extensions[ext].icon : Icon.FileEarmark;
8889
const color = Object.prototype.hasOwnProperty.call(extensions, ext) ? extensions[ext].color : "gray";

src/components/modals/add.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import type { PriorityNumberType } from "rpc/transmission";
2424
import { PriorityColors, PriorityStrings } from "rpc/transmission";
2525
import type { Torrent } from "rpc/torrent";
2626
import { useServerTorrentData, useServerRpcVersion } from "rpc/torrent";
27-
import type { FileDirEntry } from "cachedfiletree";
2827
import { CachedFileTree } from "cachedfiletree";
2928
import { FileTreeTable, useUnwantedFiles } from "components/tables/filetreetable";
3029
import { notifications } from "@mantine/notifications";
@@ -459,13 +458,13 @@ export function AddTorrent(props: AddCommonModalProps) {
459458

460459
const unwantedHandler = useUnwantedFiles(fileTree, false);
461460

462-
const onCheckboxChange = useCallback((entry: FileDirEntry, state: boolean) => {
463-
unwantedHandler(entry, state);
461+
const onCheckboxChange = useCallback((entryPath: string, state: boolean) => {
462+
unwantedHandler(entryPath, state);
464463
setWantedSize(fileTree.getWantedSize());
465464
}, [fileTree, unwantedHandler]);
466465

467466
const setAllWanted = useCallback((wanted: boolean) => {
468-
onCheckboxChange(fileTree.tree, wanted);
467+
onCheckboxChange(fileTree.tree.fullpath, wanted);
469468
void refetch();
470469
}, [fileTree, onCheckboxChange, refetch]);
471470

src/components/tables/common.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ function InnerRow<TData>(props: {
232232
columnSizing: ColumnSizingState,
233233
columnVisibility: VisibilityState,
234234
columnOrder: ColumnOrderState,
235-
selected: boolean,
236235
}) {
237236
return <>
238237
{props.row.getVisibleCells().map(cell => {
@@ -251,11 +250,16 @@ const MemoizedInnerRow = memo(InnerRow, (prev, next) => {
251250
prev.expanded === next.expanded &&
252251
prev.columnSizing === next.columnSizing &&
253252
prev.columnVisibility === next.columnVisibility &&
254-
prev.columnOrder === next.columnOrder &&
255-
prev.selected === next.selected
253+
prev.columnOrder === next.columnOrder
256254
);
257255
}) as typeof InnerRow;
258256

257+
const RowSelectedContext = React.createContext<boolean>(false);
258+
259+
export function useRowSelected() {
260+
return useContext(RowSelectedContext);
261+
}
262+
259263
function TableRow<TData>(props: {
260264
row: Row<TData>,
261265
selected: boolean,
@@ -328,7 +332,9 @@ function TableRow<TData>(props: {
328332
onKeyDown={onKeyDown}
329333
tabIndex={-1}
330334
>
331-
<MemoizedInnerRow {...props} />
335+
<RowSelectedContext.Provider value={props.selected}>
336+
<MemoizedInnerRow {...props} />
337+
</RowSelectedContext.Provider>
332338
</div>
333339
);
334340
}
@@ -600,7 +606,9 @@ export function EditableNameField(props: EditableNameFieldProps) {
600606
useEffect(() => {
601607
if (ref.current != null) {
602608
const row = ref.current.parentNode?.parentNode as HTMLDivElement;
603-
row.onfocus = () => { ref.current?.focus(); };
609+
row.onfocus = () => {
610+
ref.current?.focus();
611+
};
604612
return () => { row.onfocus = null; };
605613
}
606614
}, []);

0 commit comments

Comments
 (0)