Skip to content

Commit

Permalink
Colorful + animated progress bars (#215)
Browse files Browse the repository at this point in the history
* Implement colorful + animated progress bars

* Enable migrating from old progress bar config

* Move common progress bar variant logic to trutil.js

* Move active calculation into torrentProgressbarStyle

* Expand colorful changes to 'colorful' only option
  • Loading branch information
melyux committed Sep 12, 2024
1 parent 14caf62 commit 807041d
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 35 deletions.
12 changes: 10 additions & 2 deletions src/components/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import React, { memo, useCallback, useContext, useEffect, useMemo, useState } from "react";
import type { Torrent, TrackerStats } from "../rpc/torrent";
import { bytesToHumanReadableStr, ensurePathDelimiter, fileSystemSafeName, secondsToHumanReadableStr, timestampToDateString } from "../trutil";
import { bytesToHumanReadableStr, ensurePathDelimiter, fileSystemSafeName, secondsToHumanReadableStr, timestampToDateString, torrentProgressbarStyle } from "../trutil";
import { FileTreeTable, useUnwantedFiles } from "./tables/filetreetable";
import { PiecesCanvas } from "./piecescanvas";
import { ProgressBar } from "./progressbar";
Expand Down Expand Up @@ -56,11 +56,19 @@ function DownloadBar(props: { torrent: Torrent }) {
prefix = "Downloaded";
}

const config = useContext(ConfigContext);
const now = Math.floor(percent * 1000);
const nowStr = `${prefix}: ${now / 10}%`;
const progressbarStyle = torrentProgressbarStyle(props.torrent, config);

return (
<Box w="100%" my="0.5rem">
<ProgressBar now={now} max={1000} label={nowStr} />
<ProgressBar
now={now}
max={1000}
label={nowStr}
{...progressbarStyle}
/>
</Box>
);
}
Expand Down
18 changes: 11 additions & 7 deletions src/components/modals/interfacepanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import { Checkbox, Grid, MultiSelect, NativeSelect, NumberInput, Textarea, useMa
import type { UseFormReturnType } from "@mantine/form";
import ColorChooser from "components/colorchooser";
import { useGlobalStyleOverrides } from "themehooks";
import { DeleteTorrentDataOptions, ProgressbarStyleOptions } from "config";
import type { ProgressbarStyleOption, ColorSetting, DeleteTorrentDataOption, StyleOverrides } from "config";
import { DeleteTorrentDataOptions } from "config";
import type { ColorSetting, DeleteTorrentDataOption, StyleOverrides } from "config";
import { ColorSchemeToggle } from "components/miscbuttons";
import { Label } from "./common";
const { TAURI, invoke } = await import(/* webpackChunkName: "taurishim" */"taurishim");
Expand All @@ -34,7 +34,8 @@ export interface InterfaceFormValues {
styleOverrides: StyleOverrides,
skipAddDialog: boolean,
deleteTorrentData: DeleteTorrentDataOption,
progressbarStyle: ProgressbarStyleOption,
animatedProgressbars: boolean,
colorfulProgressbars: boolean,
numLastSaveDirs: number,
sortLastSaveDirs: boolean,
preconfiguredLabels: string[],
Expand Down Expand Up @@ -146,11 +147,14 @@ export function InterfaceSettigsPanel<V extends InterfaceFormValues>(props: { fo
<Checkbox label="Sort download directories list"
{...props.form.getInputProps("interface.sortLastSaveDirs", { type: "checkbox" })} />
</Grid.Col>
<Grid.Col span={3}>Progressbars</Grid.Col>
<Grid.Col span={3}>Progress bars</Grid.Col>
<Grid.Col span={3}>
<NativeSelect data={ProgressbarStyleOptions as unknown as string[]}
value={props.form.values.interface.progressbarStyle}
onChange={(e) => { setFieldValue("interface.progressbarStyle", e.target.value); }} />
<Checkbox label="Colorful"
{...props.form.getInputProps("interface.colorfulProgressbars", { type: "checkbox" })} />
</Grid.Col>
<Grid.Col span={3}>
<Checkbox label="Animated"
{...props.form.getInputProps("interface.animatedProgressbars", { type: "checkbox" })} />
</Grid.Col>
<Grid.Col>
<MultiSelect
Expand Down
2 changes: 1 addition & 1 deletion src/components/progressbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import "../css/progressbar.css";
import React from "react";

export type ProgressBarVariant = "default" | "green" | "dark-green" | "red" | "grey";
export type ProgressBarVariant = "default" | "yellow" | "green" | "dark-green" | "red" | "grey";

interface ProgressBarProps {
now: number,
Expand Down
4 changes: 2 additions & 2 deletions src/components/tables/filetreetable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ function PercentBarField(props: TableFieldProps) {
const config = useContext(ConfigContext);
const now = props.entry.percent ?? 0;
let variant: ProgressBarVariant = "default";
if (config.values.interface.progressbarStyle === "colorful") {
if (config.values.interface.colorfulProgressbars) {
if (props.entry.want === false) variant = "grey";
else if (now === 100) variant = "dark-green";
else if (now === 100) variant = "green";
}

return <ProgressBar now={now} className="white-outline" variant={variant} />;
Expand Down
5 changes: 3 additions & 2 deletions src/components/tables/peerstable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ function PercentField(props: TableFieldProps) {
return <ProgressBar
now={now}
className="white-outline"
animate={config.values.interface.progressbarStyle === "animated" && active}
variant={config.values.interface.progressbarStyle === "colorful" && active ? "green" : "default"} />;
animate={config.values.interface.animatedProgressbars && active}
variant={config.values.interface.colorfulProgressbars && now === 100 ? "green" : "default"}
/>;
}

const Columns = AllFields.map((field): ColumnDef<PeerStats> => {
Expand Down
20 changes: 4 additions & 16 deletions src/components/tables/torrenttable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ import { useServerTorrentData, useServerRpcVersion, useServerSelectedTorrents }
import type { TorrentAllFieldsType, TorrentFieldsType } from "rpc/transmission";
import { PriorityColors, PriorityStrings, Status, StatusStrings, TorrentMinimumFields } from "rpc/transmission";
import type { ColumnDef, VisibilityState } from "@tanstack/react-table";
import { bytesToHumanReadableStr, fileSystemSafeName, modKeyString, pathMapFromServer, secondsToHumanReadableStr, timestampToDateString } from "trutil";
import type { ProgressBarVariant } from "../progressbar";
import { bytesToHumanReadableStr, fileSystemSafeName, modKeyString, pathMapFromServer, secondsToHumanReadableStr, timestampToDateString, torrentProgressbarStyle } from "trutil";
import { ProgressBar } from "../progressbar";
import type { AccessorFn, CellContext } from "@tanstack/table-core";
import type { TableSelectReducer } from "./common";
Expand Down Expand Up @@ -347,24 +346,13 @@ function ByteRateField(props: TableFieldProps) {
function PercentBarField(props: TableFieldProps) {
const config = useContext(ConfigContext);
const now = props.torrent[props.fieldName] * 100;
const active = props.torrent.rateDownload > 0 || props.torrent.rateUpload > 0;
let variant: ProgressBarVariant = "default";
if (config.values.interface.progressbarStyle === "colorful") {
if ((props.torrent.error !== undefined && props.torrent.error > 0) ||
props.torrent.cachedError !== "") variant = "red";
else {
if (active) variant = "green";
else if (props.torrent.status === Status.stopped &&
props.torrent.sizeWhenDone > 0 &&
props.torrent.leftUntilDone === 0) variant = "dark-green";
}
}
const progressbarStyle = torrentProgressbarStyle(props.torrent, config);

return <ProgressBar
now={now}
className="white-outline"
animate={config.values.interface.progressbarStyle === "animated" && active}
variant={variant} />;
{...progressbarStyle}
/>;
}

const Columns = AllFields.map((f): ColumnDef<Torrent> => {
Expand Down
14 changes: 10 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,9 @@ export type SectionsVisibility<S extends string> = Array<{
export const WindowMinimizeOptions = ["minimize", "hide"] as const;
export const WindowCloseOptions = ["hide", "close", "quit"] as const;
export const DeleteTorrentDataOptions = ["default off", "default on", "remember selection"] as const;
export const ProgressbarStyleOptions = ["plain", "animated", "colorful"] as const;
export type WindowMinimizeOption = typeof WindowMinimizeOptions[number];
export type WindowCloseOption = typeof WindowCloseOptions[number];
export type DeleteTorrentDataOption = typeof DeleteTorrentDataOptions[number];
export type ProgressbarStyleOption = typeof ProgressbarStyleOptions[number];

export interface ColorSetting {
color: DefaultMantineColor,
Expand Down Expand Up @@ -162,7 +160,9 @@ interface Settings {
preconfiguredLabels: string[],
defaultTrackers: string[],
styleOverrides: StyleOverrides,
progressbarStyle: ProgressbarStyleOption,
progressbarStyle?: string, // deprecated
animatedProgressbars: boolean,
colorfulProgressbars: boolean,
},
configVersion: number,
}
Expand Down Expand Up @@ -285,7 +285,8 @@ const DefaultSettings: Settings = {
dark: {},
light: {},
},
progressbarStyle: "animated",
animatedProgressbars: true,
colorfulProgressbars: false,
},
// This field is used to verify config struct compatibility when importing settings
// Bump this only when incompatible changes are made that cannot be imported into older
Expand Down Expand Up @@ -315,6 +316,11 @@ export class Config {
this.values.app.openTabs = this.values.openTabs;
this.values.openTabs = undefined;
}
if (this.values.interface.progressbarStyle !== undefined) {
this.values.interface.animatedProgressbars = this.values.interface.progressbarStyle === "animated";
this.values.interface.colorfulProgressbars = this.values.interface.progressbarStyle === "colorful";
this.values.interface.progressbarStyle = undefined;
}
} catch (e) {
console.log(e);
}
Expand Down
4 changes: 4 additions & 0 deletions src/css/progressbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
color: white;
}

.progressbar.yellow>:first-child {
background: #fab007;
}

.progressbar.red>:first-child {
background: #fa5252;
}
Expand Down
33 changes: 32 additions & 1 deletion src/trutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import type { ServerConfig } from "config";
import type { Config, ServerConfig } from "config";
import { useReducer } from "react";
import type { ProgressBarVariant } from "./components/progressbar";
import { Status } from "rpc/transmission";
import type { Torrent } from "rpc/torrent";

const SISuffixes = ["B", "KB", "MB", "GB", "TB"];

Expand Down Expand Up @@ -231,3 +234,31 @@ export function fileSystemSafeName(name: string) {
export function * chainedIterables<T>(...iterables: Array<Iterable<T>>) {
for (const iterable of iterables) yield * iterable;
}

export function torrentProgressbarStyle(torrent: Torrent, config: Config) {
const active = torrent.rateDownload > 0 || torrent.rateUpload > 0;
const animate = config.values.interface.animatedProgressbars && active;

let variant: ProgressBarVariant = "default";
if (config.values.interface.colorfulProgressbars) {
if ((torrent.error !== undefined && torrent.error > 0) || torrent.cachedError !== "") {
variant = "red";
} else {
if (torrent.status === Status.stopped && torrent.sizeWhenDone > 0) {
if (torrent.leftUntilDone === 0) {
variant = "dark-green";
} else {
variant = "yellow";
}
} else if (torrent.status === Status.seeding) {
variant = "green";
} else if (torrent.status === Status.queuedToVerify ||
torrent.status === Status.queuedToDownload ||
torrent.status === Status.queuedToSeed) {
variant = "grey";
}
}
}

return { animate, variant };
}

0 comments on commit 807041d

Please sign in to comment.