feat(upload): add transfer rate tracking and 6-hour expiry option
All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m47s
All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m47s
- Implement real-time transfer rate tracking and display upload speed (e.g., Mb/s) in the progress status. - Add a 6-hour (360 minutes) option to the upload expiry selection ladder. - Fix an issue where the "new upload" button remained visible by explicitly toggling its display style and adding a CSS fallback for the `hidden` attribute.
This commit is contained in:
@@ -95,7 +95,7 @@ func (a *App) homeExpiryOptions(settings services.UploadPolicySettings, user ser
|
||||
}
|
||||
|
||||
func buildExpiryOptions(maxDays int, unlimited bool) ([]expiryOption, int) {
|
||||
ladder := []int{60, 720, 1440, 2880, 4320, 7200, 10080, 14400, 20160, 43200, 86400, 129600, 259200, 525600}
|
||||
ladder := []int{60, 360, 720, 1440, 2880, 4320, 7200, 10080, 14400, 20160, 43200, 86400, 129600, 259200, 525600}
|
||||
|
||||
capMinutes := maxDays * 24 * 60
|
||||
if unlimited || capMinutes <= 0 {
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
|
||||
.upload-options .form-footer .upload-new-button[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -117,9 +117,7 @@
|
||||
uploadQueue.hidden = true;
|
||||
uploadQueue.replaceChildren();
|
||||
}
|
||||
if (newUpload) {
|
||||
newUpload.hidden = true;
|
||||
}
|
||||
updateNewUploadVisibility();
|
||||
if (fileSummary) {
|
||||
fileSummary.textContent = "Upload complete.";
|
||||
}
|
||||
@@ -189,9 +187,16 @@
|
||||
uploadQueue.hidden = true;
|
||||
uploadQueue.replaceChildren();
|
||||
}
|
||||
if (newUpload) {
|
||||
newUpload.hidden = !(resumeMode && recoveredDraft);
|
||||
updateNewUploadVisibility();
|
||||
}
|
||||
|
||||
function updateNewUploadVisibility() {
|
||||
if (!newUpload) {
|
||||
return;
|
||||
}
|
||||
const visible = Boolean(resumeMode && recoveredDraft);
|
||||
newUpload.hidden = !visible;
|
||||
newUpload.style.display = visible ? "" : "none";
|
||||
}
|
||||
|
||||
function setLoading(isLoading, submit) {
|
||||
@@ -216,6 +221,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateUploadProgress(percent, bytesPerSecond) {
|
||||
const clamped = Math.max(0, Math.min(100, Math.round(percent || 0)));
|
||||
const rate = formatTransferRate(bytesPerSecond);
|
||||
updateStatus(rate ? `${clamped}% · ${rate}` : `${clamped}%`);
|
||||
}
|
||||
|
||||
function createTransferRateTracker(initialBytes) {
|
||||
const startedAt = performance.now();
|
||||
const baseline = Math.max(0, initialBytes || 0);
|
||||
let lastRate = 0;
|
||||
return function track(currentBytes) {
|
||||
const elapsedSeconds = (performance.now() - startedAt) / 1000;
|
||||
const transferred = Math.max(0, (currentBytes || 0) - baseline);
|
||||
if (elapsedSeconds < 0.25 || transferred <= 0) {
|
||||
return lastRate;
|
||||
}
|
||||
lastRate = transferred / elapsedSeconds;
|
||||
return lastRate;
|
||||
};
|
||||
}
|
||||
|
||||
function formatTransferRate(bytesPerSecond) {
|
||||
if (!Number.isFinite(bytesPerSecond) || bytesPerSecond <= 0) {
|
||||
return "";
|
||||
}
|
||||
const units = ["b/s", "Kb/s", "Mb/s", "Gb/s"];
|
||||
let value = bytesPerSecond * 8;
|
||||
let unit = 0;
|
||||
while (value >= 1000 && unit < units.length - 1) {
|
||||
value /= 1000;
|
||||
unit += 1;
|
||||
}
|
||||
return `${value >= 10 || unit === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unit]}`;
|
||||
}
|
||||
|
||||
function renderResult(payload) {
|
||||
if (!result || !resultList || !resultMeta || !openBox) {
|
||||
return;
|
||||
@@ -248,16 +288,18 @@
|
||||
function uploadWithProgress(url, formData, files) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = new XMLHttpRequest();
|
||||
const rateTracker = createTransferRateTracker(0);
|
||||
request.open("POST", url);
|
||||
request.setRequestHeader("Accept", "application/json");
|
||||
|
||||
request.upload.addEventListener("progress", (event) => {
|
||||
const rate = rateTracker(event.loaded || 0);
|
||||
if (!event.lengthComputable) {
|
||||
updateStatus("Uploading...");
|
||||
updateStatus(rate > 0 ? `Uploading · ${formatTransferRate(rate)}` : "Uploading...");
|
||||
return;
|
||||
}
|
||||
const percent = Math.round((event.loaded / event.total) * 100);
|
||||
updateStatus(`${percent}%`);
|
||||
updateUploadProgress(percent, rate);
|
||||
setTotalProgress(percent);
|
||||
setFileProgress(files, percent);
|
||||
});
|
||||
@@ -348,7 +390,9 @@
|
||||
completedByFile[index] = uploadedBytesForSessionFile(sessionFile, session.chunkSize);
|
||||
setSingleFileProgress(index, files[index], percentForBytes(completedByFile[index], files[index].size));
|
||||
});
|
||||
setTotalProgress(percentForBytes(completedByFile.reduce((sum, bytes) => sum + bytes, 0), totalBytes));
|
||||
const initiallyUploadedBytes = completedByFile.reduce((sum, bytes) => sum + bytes, 0);
|
||||
const rateTracker = createTransferRateTracker(initiallyUploadedBytes);
|
||||
setTotalProgress(percentForBytes(initiallyUploadedBytes, totalBytes));
|
||||
|
||||
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
||||
const file = files[fileIndex];
|
||||
@@ -362,9 +406,11 @@
|
||||
const end = Math.min(file.size, start + session.chunkSize);
|
||||
await uploadChunkWithRetry(session, sessionFile, chunkIndex, file.slice(start, end), (loaded) => {
|
||||
const currentTotal = completedByFile.reduce((sum, bytes) => sum + bytes, 0) + loaded;
|
||||
setTotalProgress(percentForBytes(currentTotal, totalBytes));
|
||||
const percent = percentForBytes(currentTotal, totalBytes);
|
||||
const rate = rateTracker(currentTotal);
|
||||
setTotalProgress(percent);
|
||||
setSingleFileProgress(fileIndex, file, percentForBytes(completedByFile[fileIndex] + loaded, file.size));
|
||||
updateStatus(`${percentForBytes(currentTotal, totalBytes)}%`);
|
||||
updateUploadProgress(percent, rate);
|
||||
});
|
||||
completedByFile[fileIndex] += end - start;
|
||||
uploaded.add(chunkIndex);
|
||||
@@ -749,6 +795,7 @@
|
||||
selectedFiles = [];
|
||||
renderResumeQueue(recoveredDraft.session, selectedFiles);
|
||||
updateSelectedState();
|
||||
updateNewUploadVisibility();
|
||||
updateStatus("Drop or reselect missing files to continue. Extra files will be added to this upload.");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user