From f20f832cc3dcbcafe14fb4c32d9dd636468bec2c Mon Sep 17 00:00:00 2001 From: qu1ck Date: Mon, 20 Nov 2023 18:11:01 -0800 Subject: [PATCH] Make arrow left/right keys toggle expandable rows + more render optimizations --- src/components/details.tsx | 6 +++--- src/components/modals/add.tsx | 12 ++++++------ src/components/modals/editlabels.tsx | 6 +++--- src/components/modals/edittorrent.tsx | 6 +++--- src/components/modals/edittrackers.tsx | 6 +++--- src/components/modals/move.tsx | 6 +++--- src/components/modals/remove.tsx | 6 +++--- src/components/tables/common.tsx | 11 ++++++++--- src/components/tables/filetreetable.tsx | 22 +++++++++++++--------- src/components/tables/torrenttable.tsx | 6 +++--- src/components/toolbar.tsx | 10 +++++----- src/queries.ts | 2 +- 12 files changed, 54 insertions(+), 45 deletions(-) diff --git a/src/components/details.tsx b/src/components/details.tsx index e58ce5e..9750267 100644 --- a/src/components/details.tsx +++ b/src/components/details.tsx @@ -305,16 +305,16 @@ function FileTreePane(props: { torrent: Torrent }) { void refetch(); }, [props.torrent, fileTree, refetch]); - const mutation = useMutateTorrent(); + const { mutate } = useMutateTorrent(); const onCheckboxChange = useUnwantedFiles(fileTree, true); const updateUnwanted = useCallback((entryPath: string, state: boolean) => { onCheckboxChange(entryPath, state); - mutation.mutate({ + mutate({ torrentIds: [props.torrent.id], fields: { [state ? "files-wanted" : "files-unwanted"]: fileTree.getChildFilesIndexes(entryPath) }, }); - }, [fileTree, mutation, onCheckboxChange, props.torrent.id]); + }, [fileTree, mutate, onCheckboxChange, props.torrent.id]); return ( { if (magnet === "") return; @@ -213,7 +213,7 @@ export function AddMagnet(props: AddCommonModalProps) { ); common.location.addPath(common.location.path); } else { - trackersMutation.mutate( + mutateAddTrackers( { torrentId: existingTorrent.id, trackers: magnetData?.trackers ?? [] }, { onSuccess: () => { @@ -227,7 +227,7 @@ export function AddMagnet(props: AddCommonModalProps) { } setMagnet(""); close(); - }, [existingTorrent, close, addMutation, magnet, common, trackersMutation, magnetData]); + }, [existingTorrent, close, addMutation, magnet, common, mutateAddTrackers, magnetData]); const config = useContext(ConfigContext); const shouldOpen = !config.values.interface.skipAddDialog || typeof props.uri !== "string"; @@ -499,7 +499,7 @@ export function AddTorrent(props: AddCommonModalProps) { }); }, []), ); - const trackersMutation = useTorrentAddTrackers(); + const mutateAddTrackers = useTorrentAddTrackers(); const onAdd = useCallback(() => { if (torrentData === undefined) return; @@ -521,7 +521,7 @@ export function AddTorrent(props: AddCommonModalProps) { common.location.addPath(common.location.path); } else { - trackersMutation.mutate( + mutateAddTrackers( { torrentId: existingTorrent.id, trackers: torrentData[0].trackers }, { onSuccess: () => { @@ -540,7 +540,7 @@ export function AddTorrent(props: AddCommonModalProps) { } setTorrentData(undefined); close(); - }, [torrentData, existingTorrent, close, common, addMutation, fileTree, trackersMutation, config]); + }, [torrentData, existingTorrent, close, common, addMutation, fileTree, mutateAddTrackers, config]); const shouldOpen = !config.values.interface.skipAddDialog && torrentData !== undefined; useEffect(() => { diff --git a/src/components/modals/editlabels.tsx b/src/components/modals/editlabels.tsx index 75d8bb4..2c5a23e 100644 --- a/src/components/modals/editlabels.tsx +++ b/src/components/modals/editlabels.tsx @@ -45,7 +45,7 @@ export function EditLabelsModal(props: ModalState) { if (opened) setLabels(calculateInitialLabels()); }, [calculateInitialLabels, opened]); - const mutation = useMutateTorrent(); + const { mutate } = useMutateTorrent(); const onSave = useCallback(() => { if (rpcVersion < 16) { @@ -57,7 +57,7 @@ export function EditLabelsModal(props: ModalState) { close(); return; } - mutation.mutate( + mutate( { torrentIds: Array.from(serverSelected), fields: { labels }, @@ -79,7 +79,7 @@ export function EditLabelsModal(props: ModalState) { }, ); close(); - }, [rpcVersion, mutation, serverSelected, labels, close]); + }, [rpcVersion, mutate, serverSelected, labels, close]); return <> {props.opened && diff --git a/src/components/modals/edittorrent.tsx b/src/components/modals/edittorrent.tsx index 53c092a..a651c5a 100644 --- a/src/components/modals/edittorrent.tsx +++ b/src/components/modals/edittorrent.tsx @@ -73,12 +73,12 @@ export function EditTorrent(props: ModalState) { }); }, [setValues, torrent]); - const mutation = useMutateTorrent(); + const { mutate } = useMutateTorrent(); const onSave = useCallback(() => { if (torrentId === undefined || torrent === undefined) return; - mutation.mutate( + mutate( { torrentIds: [...selected], fields: { @@ -97,7 +97,7 @@ export function EditTorrent(props: ModalState) { }, ); props.close(); - }, [torrentId, torrent, mutation, selected, form.values, props]); + }, [torrentId, torrent, mutate, selected, form.values, props]); return <>{props.opened && { if (torrentId === undefined || torrent === undefined) return; @@ -77,7 +77,7 @@ export function EditTrackers(props: ModalState) { if (toAdd.length === 0) toAdd = undefined; if (toRemove.length === 0) toRemove = undefined; } - mutation.mutate( + mutate( { torrentIds: [...selected], fields: { @@ -97,7 +97,7 @@ export function EditTrackers(props: ModalState) { }, ); props.close(); - }, [torrentId, torrent, rpcVersion, mutation, selected, form.values, props]); + }, [torrentId, torrent, rpcVersion, mutate, selected, form.values, props]); const addDefaultTrackers = useCallback(() => { let list = form.values.trackerList; diff --git a/src/components/modals/move.tsx b/src/components/modals/move.tsx index 9b076a3..c37cdf8 100644 --- a/src/components/modals/move.tsx +++ b/src/components/modals/move.tsx @@ -32,10 +32,10 @@ export function MoveModal(props: ModalState) { const location = useTorrentLocation(); const { setPath } = location; - const mutation = useTorrentChangeDirectory(); + const changeDirectory = useTorrentChangeDirectory(); const onMove = useCallback(() => { - mutation.mutate( + changeDirectory( { torrentIds: Array.from(serverSelected), location: location.path, @@ -53,7 +53,7 @@ export function MoveModal(props: ModalState) { ); props.close(); - }, [mutation, serverSelected, location.path, moveData, props]); + }, [changeDirectory, serverSelected, location.path, moveData, props]); const calculateInitialLocation = useCallback(() => { const [id] = [...serverSelected]; diff --git a/src/components/modals/remove.tsx b/src/components/modals/remove.tsx index 5920b84..dc95cef 100644 --- a/src/components/modals/remove.tsx +++ b/src/components/modals/remove.tsx @@ -28,10 +28,10 @@ export function RemoveModal(props: ModalState) { const serverSelected = useServerSelectedTorrents(); const [deleteData, setDeleteData] = useState(false); - const mutation = useRemoveTorrents(); + const remove = useRemoveTorrents(); const onDelete = useCallback(() => { - mutation.mutate( + remove( { torrentIds: Array.from(serverSelected), deleteData, @@ -47,7 +47,7 @@ export function RemoveModal(props: ModalState) { }, ); props.close(); - }, [mutation, serverSelected, deleteData, props]); + }, [remove, serverSelected, deleteData, props]); return ( diff --git a/src/components/tables/common.tsx b/src/components/tables/common.tsx index c0f4b46..7e487de 100644 --- a/src/components/tables/common.tsx +++ b/src/components/tables/common.tsx @@ -564,6 +564,7 @@ export function TrguiTable(props: { interface EditableNameFieldProps extends React.PropsWithChildren { currentName: string, onUpdate?: (newName: string, onStart: () => void, onEnd: () => void) => void, + onArrowLeftRight?: (key: string) => void, } export function EditableNameField(props: EditableNameFieldProps) { @@ -613,14 +614,18 @@ export function EditableNameField(props: EditableNameFieldProps) { } }, []); - const onF2 = useCallback((event: React.KeyboardEvent) => { + const { onArrowLeftRight } = props; + + const onKeyDown = useCallback((event: React.KeyboardEvent) => { if (event.key === "F2" && !isRenaming) { renameHandler(); + } else if (onArrowLeftRight !== undefined && ["ArrowLeft", "ArrowRight"].includes(event.key)) { + onArrowLeftRight(event.key); } - }, [isRenaming, renameHandler]); + }, [isRenaming, onArrowLeftRight, renameHandler]); return ( - { setHover(true); }} onMouseLeave={() => { setHover(false); }} sx={{ display: "flex", alignItems: "center", width: "100%", height: "100%" }}> {props.children} diff --git a/src/components/tables/filetreetable.tsx b/src/components/tables/filetreetable.tsx index 8764d91..0211ae7 100644 --- a/src/components/tables/filetreetable.tsx +++ b/src/components/tables/filetreetable.tsx @@ -96,8 +96,14 @@ function NameField(props: TableFieldProps) { const rpcVersion = useServerRpcVersion(); + const onArrowLeftRight = useCallback((key: string) => { + if (props.row.subRows.length === 0) return; + if (key === "ArrowLeft" && props.row.getIsExpanded()) props.row.toggleExpanded(); + if (key === "ArrowRight" && !props.row.getIsExpanded()) props.row.toggleExpanded(); + }, [props.row]); + return ( - = 15) ? updatePath : undefined}> @@ -282,9 +288,7 @@ export function FileTreeTable(props: FileTreeTableProps) { }), [props.brief, props.fileTree, nameSortFunc, onCheckboxChange]); const getRowId = useCallback((row: FileDirEntryView) => row.fullpath, []); - const getSubRows = useCallback((row: FileDirEntryView) => { - return row.subrows; - }, []); + const getSubRows = useCallback((row: FileDirEntryView) => row.subrows, []); const { selected, selectedReducer } = useSelected(props); @@ -412,7 +416,7 @@ function FiletreeContextMenu(props: { onEntryOpen(rowPath, reveal); }, [onEntryOpen, props.fileTree, props.selected]); - const mutation = useMutateTorrent(); + const { mutate } = useMutateTorrent(); const setPriority = useCallback((priority: "priority-high" | "priority-normal" | "priority-low") => { const fileIds = Array.from(props.selected @@ -422,7 +426,7 @@ function FiletreeContextMenu(props: { return set; }, new Set())); - mutation.mutate( + mutate( { torrentIds: [props.fileTree.torrentId], fields: { @@ -438,7 +442,7 @@ function FiletreeContextMenu(props: { }, }, ); - }, [mutation, props.fileTree, props.selected]); + }, [mutate, props.fileTree, props.selected]); const setWanted = useCallback((wanted: boolean) => { const fileIds = Array.from(props.selected @@ -448,7 +452,7 @@ function FiletreeContextMenu(props: { return set; }, new Set())); - mutation.mutate( + mutate( { torrentIds: [props.fileTree.torrentId], fields: { @@ -464,7 +468,7 @@ function FiletreeContextMenu(props: { }, }, ); - }, [mutation, props.fileTree, props.selected]); + }, [mutate, props.fileTree, props.selected]); return ( diff --git a/src/components/tables/torrenttable.tsx b/src/components/tables/torrenttable.tsx index 8889595..a2306a9 100644 --- a/src/components/tables/torrenttable.tsx +++ b/src/components/tables/torrenttable.tsx @@ -468,10 +468,10 @@ function TorrentContextMenu(props: { onRowDoubleClick(torrent, reveal); }, [onRowDoubleClick, serverData.torrents, serverSelected]); - const mutation = useTorrentAction(); + const mutate = useTorrentAction(); const torrentAction = useCallback((method: TorrentActionMethodsType, successMessage: string) => { - mutation.mutate( + mutate( { method, torrentIds: Array.from(serverSelected), @@ -485,7 +485,7 @@ function TorrentContextMenu(props: { }, }, ); - }, [mutation, serverSelected]); + }, [mutate, serverSelected]); const [queueSubmenuOpened, setQueueSubmenuOpened] = useState(false); const queueRef = useRef(null); diff --git a/src/components/toolbar.tsx b/src/components/toolbar.tsx index cc6a262..ff632b4 100644 --- a/src/components/toolbar.tsx +++ b/src/components/toolbar.tsx @@ -74,8 +74,8 @@ function useButtonHandlers( setAltSpeedMode: React.Dispatch, ) { const serverSelected = useServerSelectedTorrents(); - const actionMutation = useTorrentAction(); - const priorityMutation = useMutateTorrent(); + const actionMutate = useTorrentAction(); + const { mutate: mutateTorrent } = useMutateTorrent(); const handlers = useMemo(() => { const checkSelected = (action?: () => void) => { @@ -84,7 +84,7 @@ function useButtonHandlers( }; }; const action = (method: TorrentActionMethodsType) => () => { - actionMutation.mutate( + actionMutate( { method, torrentIds: Array.from(serverSelected), @@ -101,7 +101,7 @@ function useButtonHandlers( ); }; const priority = (bandwidthPriority: PriorityNumberType) => () => { - priorityMutation.mutate( + mutateTorrent( { torrentIds: Array.from(serverSelected), fields: { bandwidthPriority }, @@ -137,7 +137,7 @@ function useButtonHandlers( setPriorityLow: checkSelected(priority(BandwidthPriority.low)), daemonSettings: () => { props.modals.current?.daemonSettings(); }, }; - }, [actionMutation, priorityMutation, props.modals, serverSelected]); + }, [actionMutate, mutateTorrent, props.modals, serverSelected]); const sessionMutation = useMutateSession(); diff --git a/src/queries.ts b/src/queries.ts index fc5e404..c555194 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -200,7 +200,7 @@ function useInvalidatingTorrentAction(mutationFn: (params: ActionP onSuccess: () => { void queryClient.invalidateQueries(TorrentKeys.all(serverConfig.name)); }, - }); + }).mutate; } export interface TorrentAddQueryParams extends TorrentAddParams {