From 33172ab4441fec1e19d647c70bea1c076db688fb Mon Sep 17 00:00:00 2001 From: Melik <10296053+melyux@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:42:56 -0500 Subject: [PATCH 1/2] Implement colorful progress bars --- src/components/createtorrentform.tsx | 3 ++- src/components/details.tsx | 7 +++++-- src/components/modals/interfacepanel.tsx | 9 ++++++++- src/components/progressbar.tsx | 23 ++++++++++++++++++++--- src/components/tables/filetreetable.tsx | 2 +- src/components/tables/peerstable.tsx | 3 ++- src/components/tables/torrenttable.tsx | 16 ++++++++++++++-- src/config.ts | 2 ++ src/css/progressbar.css | 17 +++++++++++++++++ 9 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/components/createtorrentform.tsx b/src/components/createtorrentform.tsx index 768ae12..a24e9fc 100644 --- a/src/components/createtorrentform.tsx +++ b/src/components/createtorrentform.tsx @@ -261,7 +261,8 @@ export default function CreateTorrentForm() { now={pieces.done} max={Math.max(pieces.total, 1)} label={`Hashing, done ${pieces.done} of ${pieces.total}`} - animate />} + animate + status='Verifying' />} {state.state === "done" && {`Torrent infohash: ${state.hash}`}} diff --git a/src/components/details.tsx b/src/components/details.tsx index 5fe133d..6c82843 100644 --- a/src/components/details.tsx +++ b/src/components/details.tsx @@ -25,7 +25,7 @@ import { ProgressBar } from "./progressbar"; import { DateField, LabelsField, StatusField, TrackerField } from "./tables/torrenttable"; import { TrackersTable } from "./tables/trackertable"; import { PeersTable } from "./tables/peerstable"; -import { Status, type SessionStatEntry } from "rpc/transmission"; +import { Status, StatusStrings, type SessionStatEntry } from "rpc/transmission"; import type { MantineTheme } from "@mantine/core"; import { Anchor, Box, Flex, Container, Group, Table, Tabs, TextInput, LoadingOverlay, Grid, useMantineTheme } from "@mantine/core"; import * as Icon from "react-bootstrap-icons"; @@ -43,12 +43,14 @@ interface DetailsProps { function DownloadBar(props: { torrent: Torrent }) { let prefix = ""; let percent = props.torrent.percentDone as number; + let status: string = StatusStrings[props.torrent.status]; if (props.torrent.status === Status.verifying) { prefix = "Verified"; percent = props.torrent.recheckProgress; } else if (props.torrent.status === Status.downloading && props.torrent.pieceCount === 0) { prefix = "Downloading metadata"; percent = props.torrent.metadataPercentComplete; + status = "Magnetizing"; } else if (props.torrent.status === Status.stopped) { prefix = "Stopped"; } else { @@ -57,9 +59,10 @@ function DownloadBar(props: { torrent: Torrent }) { const now = Math.floor(percent * 1000); const nowStr = `${prefix}: ${now / 10}%`; + const active = props.torrent.rateDownload > 0 || props.torrent.rateUpload > 0; return ( - + ); } diff --git a/src/components/modals/interfacepanel.tsx b/src/components/modals/interfacepanel.tsx index 5341c94..48c0a92 100644 --- a/src/components/modals/interfacepanel.tsx +++ b/src/components/modals/interfacepanel.tsx @@ -17,7 +17,7 @@ */ import React from "react"; -import { Checkbox, Grid, NumberInput, Textarea } from "@mantine/core"; +import { Checkbox, Grid, NumberInput, Switch, Textarea } from "@mantine/core"; import type { UseFormReturnType } from "@mantine/form"; export interface InterfaceFormValues { @@ -28,6 +28,8 @@ export interface InterfaceFormValues { }, } +const bigSwitchStyles = { track: { flexGrow: 1 } }; + export function InterfaceSettigsPanel(props: { form: UseFormReturnType }) { return ( @@ -35,6 +37,11 @@ export function InterfaceSettigsPanel(props: { fo + Colorful progress bars + + + Max number of saved download directories
{label}
diff --git a/src/components/tables/filetreetable.tsx b/src/components/tables/filetreetable.tsx index e7b55db..ebe7e54 100644 --- a/src/components/tables/filetreetable.tsx +++ b/src/components/tables/filetreetable.tsx @@ -136,7 +136,7 @@ function ByteSizeField(props: TableFieldProps) { function PercentBarField(props: TableFieldProps) { const now = props.entry.percent ?? 0; - return ; + return ; } function PriorityField(props: TableFieldProps) { diff --git a/src/components/tables/peerstable.tsx b/src/components/tables/peerstable.tsx index 6ee5c5a..1e58f3c 100644 --- a/src/components/tables/peerstable.tsx +++ b/src/components/tables/peerstable.tsx @@ -80,7 +80,8 @@ function PercentField(props: TableFieldProps) { return ; + animate={active} + status="Downloading" />; } const Columns = AllFields.map((field): ColumnDef => { diff --git a/src/components/tables/torrenttable.tsx b/src/components/tables/torrenttable.tsx index f7d91d4..20d0488 100644 --- a/src/components/tables/torrenttable.tsx +++ b/src/components/tables/torrenttable.tsx @@ -299,13 +299,25 @@ function ByteRateField(props: TableFieldProps) { } function PercentBarField(props: TableFieldProps) { - const now = props.torrent[props.fieldName] * 100; + let now: number = props.torrent[props.fieldName] * 100; + let label: string = ''; const active = props.torrent.rateDownload > 0 || props.torrent.rateUpload > 0; + let status: string = StatusStrings[props.torrent.status]; + if ((props.torrent.error !== undefined && props.torrent.error > 0) || + props.torrent.cachedError !== "") { + status = "Error"; + } else if (props.torrent.status === Status.downloading && props.torrent.pieceCount === 0) { + status = "Magnetizing"; + now = 100; + label = "🧲"; + } return ; + label={label} + animate={active} + status={status} />; } const Columns = AllFields.map((f): ColumnDef => { diff --git a/src/config.ts b/src/config.ts index 3091889..b692454 100644 --- a/src/config.ts +++ b/src/config.ts @@ -114,6 +114,7 @@ interface Settings { showFilesSearchBox: boolean, mainSplit: SplitType, skipAddDialog: boolean, + colorfulProgressBars: boolean, numLastSaveDirs: number, defaultTrackers: string[], }, @@ -210,6 +211,7 @@ const DefaultSettings: Settings = { showFilesSearchBox: false, mainSplit: "vertical", skipAddDialog: false, + colorfulProgressBars: true, numLastSaveDirs: 20, defaultTrackers: [...DefaultTrackerList], }, diff --git a/src/css/progressbar.css b/src/css/progressbar.css index c50f0a5..628ad55 100644 --- a/src/css/progressbar.css +++ b/src/css/progressbar.css @@ -33,6 +33,23 @@ transition: clip-path 0.5s ease; } +.progressbar.green>:first-child { + background: #36B24D; +} + +.progressbar.green.dark>:first-child { + background: #1d8931; +} + +.progressbar.red>:first-child { + background: #FA5352; +} + +.progressbar.red.dark>:first-child { + background: #C92B2A; +} + + .progressbar.animate>:first-child { background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-size: 1rem 1rem; From d992cd595832183524417689c633ef6e4430c0db Mon Sep 17 00:00:00 2001 From: Melik <10296053+melyux@users.noreply.github.com> Date: Wed, 20 Sep 2023 02:03:47 -0500 Subject: [PATCH 2/2] Simplify to using ProgressBar status enum --- src/components/createtorrentform.tsx | 3 ++- src/components/details.tsx | 6 +++--- src/components/progressbar.tsx | 24 ++++++++++++++++-------- src/components/tables/filetreetable.tsx | 4 ++-- src/components/tables/peerstable.tsx | 3 ++- src/components/tables/torrenttable.tsx | 11 +++++------ src/css/progressbar.css | 8 ++++++-- src/rpc/transmission.ts | 4 ++++ 8 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/components/createtorrentform.tsx b/src/components/createtorrentform.tsx index a24e9fc..b6c00c8 100644 --- a/src/components/createtorrentform.tsx +++ b/src/components/createtorrentform.tsx @@ -21,6 +21,7 @@ import { Box, Button, Checkbox, Flex, Group, Slider, Text, TextInput, Textarea, import { useForm } from "@mantine/form"; import { dialog } from "@tauri-apps/api"; import React, { useCallback, useEffect, useRef, useState } from "react"; +import { Status } from "rpc/transmission"; import { appVersion } from "./modals/version"; import { ProgressBar } from "./progressbar"; const { appWindow, invoke } = await import(/* webpackChunkName: "taurishim" */"taurishim"); @@ -262,7 +263,7 @@ export default function CreateTorrentForm() { max={Math.max(pieces.total, 1)} label={`Hashing, done ${pieces.done} of ${pieces.total}`} animate - status='Verifying' />} + status={Status.verifying} />} {state.state === "done" && {`Torrent infohash: ${state.hash}`}} diff --git a/src/components/details.tsx b/src/components/details.tsx index 6c82843..5ac3550 100644 --- a/src/components/details.tsx +++ b/src/components/details.tsx @@ -25,7 +25,7 @@ import { ProgressBar } from "./progressbar"; import { DateField, LabelsField, StatusField, TrackerField } from "./tables/torrenttable"; import { TrackersTable } from "./tables/trackertable"; import { PeersTable } from "./tables/peerstable"; -import { Status, StatusStrings, type SessionStatEntry } from "rpc/transmission"; +import { Status, type SessionStatEntry } from "rpc/transmission"; import type { MantineTheme } from "@mantine/core"; import { Anchor, Box, Flex, Container, Group, Table, Tabs, TextInput, LoadingOverlay, Grid, useMantineTheme } from "@mantine/core"; import * as Icon from "react-bootstrap-icons"; @@ -43,14 +43,14 @@ interface DetailsProps { function DownloadBar(props: { torrent: Torrent }) { let prefix = ""; let percent = props.torrent.percentDone as number; - let status: string = StatusStrings[props.torrent.status]; + let status = props.torrent.status; if (props.torrent.status === Status.verifying) { prefix = "Verified"; percent = props.torrent.recheckProgress; } else if (props.torrent.status === Status.downloading && props.torrent.pieceCount === 0) { prefix = "Downloading metadata"; percent = props.torrent.metadataPercentComplete; - status = "Magnetizing"; + status = Status.magnetizing; } else if (props.torrent.status === Status.stopped) { prefix = "Stopped"; } else { diff --git a/src/components/progressbar.tsx b/src/components/progressbar.tsx index 473a187..ae31e4f 100644 --- a/src/components/progressbar.tsx +++ b/src/components/progressbar.tsx @@ -19,13 +19,14 @@ import "../css/progressbar.css"; import React, { useContext } from "react"; import { ConfigContext } from "../config"; +import { Status } from "../rpc/transmission"; interface ProgressBarProps { now: number, max?: number, label?: string, animate?: boolean, - status?: string, + status?: number, className?: string, } @@ -33,22 +34,29 @@ export function ProgressBar(props: ProgressBarProps) { const max = props.max ?? 100; const percent = Math.floor(1000 * props.now / max) / 10; const label = props.label || `${percent}%`; - const animate = (props.animate && props.status !== "Waiting") || props.status == "Magnetizing" || props.status == "Verifying"; + const animate = (props.animate && props.status !== Status.queuedToVerify && + props.status !== Status.queuedToDownload && props.status !== Status.queuedToSeed) || + props.status == Status.magnetizing || props.status == Status.verifying; let color = "blue"; - let dark = false; const config = useContext(ConfigContext); const colorize = config.values.interface.colorfulProgressBars; if (colorize) { - dark = props.status == "Stopped" || props.status == "Waiting" || props.status == "Error"; - if (props.status == "Magnetizing" || props.status == "Error") { + if (props.status == Status.error) { + color = "dark-red"; + } else if (props.status == Status.magnetizing) { color = "red"; - } else if (props.status == "Seeding" || (percent == 100 && props.status !== "Waiting" && props.status !== "Verifying")) { + } else if (props.status == Status.stopped) { + color = "dark-green"; + } else if (props.status == Status.seeding || props.status == Status.downloading && percent == 100) { color = "green"; - } + } else if (props.status == Status.queuedToVerify || props.status == Status.queuedToDownload || + props.status == Status.queuedToSeed) { + color = "dark-blue"; + } // Waiting and Downloading @ <=99% will default to plain blue } - const className = `progressbar ${animate === true ? "animate" : ""} ${dark === true ? "dark" : ""} ${color} ${props.className ?? ""}`; + const className = `progressbar ${animate === true ? "animate" : ""} ${color} ${props.className ?? ""}`; return (
{label}
diff --git a/src/components/tables/filetreetable.tsx b/src/components/tables/filetreetable.tsx index ebe7e54..851adf8 100644 --- a/src/components/tables/filetreetable.tsx +++ b/src/components/tables/filetreetable.tsx @@ -21,7 +21,7 @@ import type { Row, ColumnDef, CellContext } from "@tanstack/react-table"; import type { CachedFileTree, FileDirEntry } from "../../cachedfiletree"; import { isDirEntry } from "../../cachedfiletree"; import { ConfigContext, ServerConfigContext } from "../../config"; -import { PriorityColors, PriorityStrings } from "../../rpc/transmission"; +import { Status, PriorityColors, PriorityStrings } from "../../rpc/transmission"; import { bytesToHumanReadableStr, pathMapFromServer } from "../../trutil"; import { ProgressBar } from "../progressbar"; import * as Icon from "react-bootstrap-icons"; @@ -136,7 +136,7 @@ function ByteSizeField(props: TableFieldProps) { function PercentBarField(props: TableFieldProps) { const now = props.entry.percent ?? 0; - return ; + return ; } function PriorityField(props: TableFieldProps) { diff --git a/src/components/tables/peerstable.tsx b/src/components/tables/peerstable.tsx index 1e58f3c..08f7458 100644 --- a/src/components/tables/peerstable.tsx +++ b/src/components/tables/peerstable.tsx @@ -19,6 +19,7 @@ import type { AccessorFn, CellContext, ColumnDef } from "@tanstack/react-table"; import React, { useMemo, useCallback } from "react"; import type { Torrent, PeerStats } from "rpc/torrent"; +import { Status } from "../../rpc/transmission"; import { bytesToHumanReadableStr } from "trutil"; import { TrguiTable, useStandardSelect } from "./common"; import { ProgressBar } from "components/progressbar"; @@ -81,7 +82,7 @@ function PercentField(props: TableFieldProps) { now={now} className="white-outline" animate={active} - status="Downloading" />; + status={Status.downloading} />; } const Columns = AllFields.map((field): ColumnDef => { diff --git a/src/components/tables/torrenttable.tsx b/src/components/tables/torrenttable.tsx index 20d0488..f8f32f5 100644 --- a/src/components/tables/torrenttable.tsx +++ b/src/components/tables/torrenttable.tsx @@ -301,16 +301,15 @@ function ByteRateField(props: TableFieldProps) { function PercentBarField(props: TableFieldProps) { let now: number = props.torrent[props.fieldName] * 100; let label: string = ''; - const active = props.torrent.rateDownload > 0 || props.torrent.rateUpload > 0; - let status: string = StatusStrings[props.torrent.status]; - if ((props.torrent.error !== undefined && props.torrent.error > 0) || - props.torrent.cachedError !== "") { - status = "Error"; + let status: number = props.torrent.status; + if ((props.torrent.error !== undefined && props.torrent.error > 0) || props.torrent.cachedError !== "") { + status = Status.error; } else if (props.torrent.status === Status.downloading && props.torrent.pieceCount === 0) { - status = "Magnetizing"; now = 100; label = "🧲"; + status = Status.magnetizing; } + const active = props.torrent.rateDownload > 0 || props.torrent.rateUpload > 0; return :first-child { + background: #0054ae; +} + .progressbar.green>:first-child { background: #36B24D; } -.progressbar.green.dark>:first-child { +.progressbar.dark-green>:first-child { background: #1d8931; } @@ -45,7 +49,7 @@ background: #FA5352; } -.progressbar.red.dark>:first-child { +.progressbar.dark-red>:first-child { background: #C92B2A; } diff --git a/src/rpc/transmission.ts b/src/rpc/transmission.ts index 4169d38..23fa5ea 100644 --- a/src/rpc/transmission.ts +++ b/src/rpc/transmission.ts @@ -29,6 +29,8 @@ export const Status = { downloading: 4, queuedToSeed: 5, seeding: 6, + magnetizing: -1, + error: -2, } as const; export const StatusStrings = [ @@ -39,6 +41,8 @@ export const StatusStrings = [ "Downloading", "Waiting", "Seeding", + "Magnetizing", + "Error", ] as const; const PriorityNumbers = [-1, 0, 1] as const;