feat(uploader): show estimated remaining time (#2393)

The processing progress line now appends an ETA computed from
remaining bytes over the displayed speed (instant or average per the
existing toggle). Empty while speed is unknown.

Authored By: TDvorak <info@tdvorak.dev>

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
pull/3582/head
Tomas Dvorak 2 weeks ago
parent 2c8e686b4d
commit 1fbad3cd37

@ -666,7 +666,7 @@
"preparing": "Preparing...",
"processing": "Processing...",
"progressDescription": "{{uploaded}} uploaded, {{total}} total - {{percentage}}%",
"progressDescriptionFull": "{{uploaded}} uploaded, {{total}} total - {{percentage}}% ({{speed}})",
"progressDescriptionFull": "{{uploaded}} uploaded, {{total}} total - {{percentage}}% ({{speed}}){{eta}}",
"progressDescriptionPlaceHolder": " - uploaded",
"uploaded": "Uploaded",
"rootFolder": "Root folder",
@ -725,7 +725,8 @@
"upyunUploadFailed": "Upload failed: {{msg}}",
"parseResponseError": "Unable to parse response: {{msg}} ({{content}})",
"concurrentTaskNumber": "Concurrent task limit",
"dropFileHere": "Drop file to upload"
"dropFileHere": "Drop file to upload",
"etaSuffix": ", ETA {{eta}}"
},
"share": {
"statistics": "Statistics",

@ -666,7 +666,7 @@
"preparing": "准备中...",
"processing": "处理中...",
"progressDescription": "已上传 {{uploaded}},共 {{total}} - {{percentage}}%",
"progressDescriptionFull": "{{speed}} 已上传 {{uploaded}},共 {{total}} - {{percentage}}%",
"progressDescriptionFull": "{{speed}} 已上传 {{uploaded}},共 {{total}} - {{percentage}}%{{eta}}",
"progressDescriptionPlaceHolder": "已上传 - ",
"uploaded": "已上传",
"rootFolder": "根目录",
@ -725,7 +725,8 @@
"upyunUploadFailed": "上传失败:{{msg}}",
"parseResponseError": "无法解析响应:{{msg}} ({{content}})",
"concurrentTaskNumber": "同时上传的任务数量",
"dropFileHere": "松开鼠标开始上传"
"dropFileHere": "松开鼠标开始上传",
"etaSuffix": ",预计剩余 {{eta}}"
},
"share": {
"statistics": "统计",

@ -5,11 +5,13 @@ import MuiAccordionSummary from "@mui/material/AccordionSummary";
import Chip from "@mui/material/Chip";
import { lighten, useTheme } from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import dayjs from "dayjs";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { FileType } from "../../../api/explorer.ts";
import { navigateToPath } from "../../../redux/thunks/filemanager.ts";
import { sizeToString } from "../../../util";
import { formatDuration } from "../../../util/datetime.ts";
import { NoWrapBox } from "../../Common/StyledComponents.tsx";
import FileTypeIcon from "../../FileManager/Explorer/FileTypeIcon.tsx";
import ArrowClockwiseFilled from "../../Icons/ArrowClockwiseFilled.tsx";
@ -70,6 +72,16 @@ const getSpeedText = (speed: number, speedAvg: number, useSpeedAvg: boolean) =>
return `${sizeToString(displayedSpeed ? displayedSpeed : 0)}/s`;
};
// ETA from remaining bytes over the displayed speed; empty when speed is
// unknown or zero.
const getEtaText = (speed: number, speedAvg: number, useSpeedAvg: boolean, loaded: number, total: number) => {
const displayedSpeed = useSpeedAvg ? speedAvg : speed;
if (!displayedSpeed || displayedSpeed <= 0 || total <= loaded) {
return "";
}
return formatDuration(dayjs.duration(Math.ceil((total - loaded) / displayedSpeed), "seconds"));
};
const getErrMsg = (error?: Error) => {
if (error) {
let errMsg = error.message;
@ -165,6 +177,7 @@ export default function UploadTask({
);
case Status.processing:
if (progress) {
const eta = getEtaText(speed, speedAvg, useAvgSpeed, progress.total.loaded, progress.total.size);
return (
<div>
{t("progressDescriptionFull", {
@ -172,6 +185,7 @@ export default function UploadTask({
uploaded: sizeToString(progress.total.loaded),
total: sizeToString(progress.total.size),
percentage: progress.total.percent.toFixed(2),
eta: eta ? t("etaSuffix", { eta }) : "",
})}
</div>
);

Loading…
Cancel
Save