diff --git a/ui/v2.5/graphql/subscriptions.graphql b/ui/v2.5/graphql/subscriptions.graphql index 4c7ffaebf5f..a5bf4122de0 100644 --- a/ui/v2.5/graphql/subscriptions.graphql +++ b/ui/v2.5/graphql/subscriptions.graphql @@ -8,6 +8,7 @@ subscription JobsSubscribe { description progress error + startTime } } } diff --git a/ui/v2.5/package.json b/ui/v2.5/package.json index 210b750fe0c..cb59056406e 100644 --- a/ui/v2.5/package.json +++ b/ui/v2.5/package.json @@ -50,6 +50,7 @@ "intersection-observer": "^0.12.2", "localforage": "^1.10.0", "lodash-es": "^4.17.21", + "moment": "^2.30.1", "mousetrap": "^1.6.5", "mousetrap-pause": "^1.0.0", "normalize-url": "^4.5.1", diff --git a/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx b/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx index 82ed46c854b..6496dadcbc4 100644 --- a/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx +++ b/ui/v2.5/src/components/Settings/Tasks/JobTable.tsx @@ -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 { faBan, faCheck, @@ -17,10 +7,27 @@ import { faHourglassStart, faTimes, } from "@fortawesome/free-solid-svg-icons"; +import moment from "moment"; +import React, { useEffect, useState } from "react"; +import { Button, Card, ProgressBar } from "react-bootstrap"; +import { 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< GQL.Job, - "id" | "status" | "subTasks" | "description" | "progress" | "error" + | "id" + | "status" + | "subTasks" + | "description" + | "progress" + | "error" + | "startTime" >; interface IJob { @@ -124,6 +131,25 @@ const Task: React.FC = ({ 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 now_ms = now.valueOf(); + const start_ms = start.valueOf(); + const estimated_length = (now_ms - start_ms) / job.progress; + const est_len_str = moment.duration(estimated_length).humanize(); + return ETA: {est_len_str}; + } + } + function maybeRenderSubTasks() { if ( job.status === GQL.JobStatus.Running || @@ -159,9 +185,17 @@ const Task: React.FC = ({ job }) => {
-
- {getStatusIcon()} - {job.description} +
+
+ {getStatusIcon()} + {job.description} +
+ {maybeRenderETA()}
{maybeRenderProgress()}
{maybeRenderSubTasks()} diff --git a/ui/v2.5/yarn.lock b/ui/v2.5/yarn.lock index aa27a347444..b11ee8116c7 100644 --- a/ui/v2.5/yarn.lock +++ b/ui/v2.5/yarn.lock @@ -5818,6 +5818,11 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" 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: version "2.29.4" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"