feat: Add ETA for tasks (#5535)

Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
0x60B2 2025-01-30 03:28:40 +01:00 committed by GitHub
parent 44d764d832
commit 4d43763a39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 63 additions and 14 deletions

View file

@ -8,6 +8,7 @@ subscription JobsSubscribe {
description description
progress progress
error error
startTime
} }
} }
} }

View file

@ -50,6 +50,7 @@
"intersection-observer": "^0.12.2", "intersection-observer": "^0.12.2",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"moment": "^2.30.1",
"mousetrap": "^1.6.5", "mousetrap": "^1.6.5",
"mousetrap-pause": "^1.0.0", "mousetrap-pause": "^1.0.0",
"normalize-url": "^4.5.1", "normalize-url": "^4.5.1",

View file

@ -48,6 +48,8 @@ import "./pluginApi";
import { ConnectionMonitor } from "./ConnectionMonitor"; import { ConnectionMonitor } from "./ConnectionMonitor";
import { PatchFunction } from "./patch"; import { PatchFunction } from "./patch";
import moment from "moment/min/moment-with-locales";
const Performers = lazyComponent( const Performers = lazyComponent(
() => import("./components/Performers/Performers") () => import("./components/Performers/Performers")
); );
@ -158,6 +160,7 @@ export const App: React.FC = () => {
}); });
setMessages(newMessages); setMessages(newMessages);
moment.locale([language, defaultLocale]);
}; };
setLocale(); setLocale();

View file

@ -1,13 +1,3 @@
import React, { useState, useEffect } from "react";
import { Button, Card, ProgressBar } from "react-bootstrap";
import {
mutateStopJob,
useJobQueue,
useJobsSubscribe,
} from "src/core/StashService";
import * as GQL from "src/core/generated-graphql";
import { Icon } from "src/components/Shared/Icon";
import { useIntl } from "react-intl";
import { import {
faBan, faBan,
faCheck, faCheck,
@ -17,10 +7,27 @@ import {
faHourglassStart, faHourglassStart,
faTimes, faTimes,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import moment from "moment/min/moment-with-locales";
import React, { useEffect, useState } from "react";
import { Button, Card, ProgressBar } from "react-bootstrap";
import { FormattedMessage, useIntl } from "react-intl";
import { Icon } from "src/components/Shared/Icon";
import {
mutateStopJob,
useJobQueue,
useJobsSubscribe,
} from "src/core/StashService";
import * as GQL from "src/core/generated-graphql";
type JobFragment = Pick< type JobFragment = Pick<
GQL.Job, GQL.Job,
"id" | "status" | "subTasks" | "description" | "progress" | "error" | "id"
| "status"
| "subTasks"
| "description"
| "progress"
| "error"
| "startTime"
>; >;
interface IJob { interface IJob {
@ -124,6 +131,29 @@ const Task: React.FC<IJob> = ({ job }) => {
} }
} }
function maybeRenderETA() {
if (
job.status === GQL.JobStatus.Running &&
job.startTime !== null &&
job.startTime !== undefined &&
job.progress !== null &&
job.progress !== undefined &&
job.progress > 0
) {
const now = new Date();
const start = new Date(job.startTime);
const nowMS = now.valueOf();
const startMS = start.valueOf();
const estimatedLength = (nowMS - startMS) / job.progress;
const estLenStr = moment.duration(estimatedLength).humanize();
return (
<span className="job-eta">
<FormattedMessage id="eta" />: {estLenStr}
</span>
);
}
}
function maybeRenderSubTasks() { function maybeRenderSubTasks() {
if ( if (
job.status === GQL.JobStatus.Running || job.status === GQL.JobStatus.Running ||
@ -159,9 +189,12 @@ const Task: React.FC<IJob> = ({ job }) => {
<Icon icon={faTimes} /> <Icon icon={faTimes} />
</Button> </Button>
<div className={`job-status ${getStatusClass()}`}> <div className={`job-status ${getStatusClass()}`}>
<div> <div className="job-description">
{getStatusIcon()} <div>
<span>{job.description}</span> {getStatusIcon()}
<span>{job.description}</span>
</div>
{maybeRenderETA()}
</div> </div>
<div>{maybeRenderProgress()}</div> <div>{maybeRenderProgress()}</div>
{maybeRenderSubTasks()} {maybeRenderSubTasks()}

View file

@ -293,6 +293,11 @@
width: 100%; width: 100%;
} }
.job-description {
display: flex;
justify-content: space-between;
}
.stop:not(:disabled), .stop:not(:disabled),
.stopping .fa-icon, .stopping .fa-icon,
.cancelled .fa-icon { .cancelled .fa-icon {

View file

@ -1054,6 +1054,7 @@
"loading_type": "Error loading {type}", "loading_type": "Error loading {type}",
"something_went_wrong": "Something went wrong." "something_went_wrong": "Something went wrong."
}, },
"eta": "ETA",
"ethnicity": "Ethnicity", "ethnicity": "Ethnicity",
"existing_value": "existing value", "existing_value": "existing value",
"eye_color": "Eye Colour", "eye_color": "Eye Colour",

View file

@ -5818,6 +5818,11 @@ mkdirp@^1.0.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
moment@^2.30.1:
version "2.30.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
moment@~2.29.1: moment@~2.29.1:
version "2.29.4" version "2.29.4"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"