lbry-desktop/ui/component/webUploadList/internal/web-upload-item.jsx
infinite-persistence e358f0715d tus: retry only after 2-minute wait
There is anecdote that we need to wait up to 2 minutes to preven the locking scenario.
`https://github.com/tus/tusd/pull/667#issuecomment-1079647640`

## Change
Instead of multiple retries at short intervals, do a one-time retry after a 2-minute wait. We'll do this until the fix is available in tusd v2.
2022-03-29 09:01:10 -04:00

185 lines
5.7 KiB
JavaScript

// @flow
import React, { useState } from 'react';
import FileSelector from 'component/common/file-selector';
import Button from 'component/button';
import FileThumbnail from 'component/fileThumbnail';
import * as MODALS from 'constants/modal_types';
import { serializeFileObj } from 'util/file';
import { tusIsSessionLocked } from 'util/tus';
type Props = {
uploadItem: FileUploadItem,
doPublishResume: (any) => void,
doUpdateUploadRemove: (string, any) => void,
doOpenModal: (string, {}) => void,
};
export default function WebUploadItem(props: Props) {
const { uploadItem, doPublishResume, doUpdateUploadRemove, doOpenModal } = props;
const { params, file, fileFingerprint, progress, status, resumable, uploader } = uploadItem;
const [showFileSelector, setShowFileSelector] = useState(false);
const locked = tusIsSessionLocked(params.guid);
function handleFileChange(newFile: WebFile, clearName = true) {
if (serializeFileObj(newFile) === fileFingerprint) {
setShowFileSelector(false);
doPublishResume({ ...params, file_path: newFile });
if (!params.guid) {
// Can remove this if-block after January 2022.
doUpdateUploadRemove('', params);
}
} else {
doOpenModal(MODALS.CONFIRM, {
title: __('Invalid file'),
subtitle: __('It appears to be a different or modified file.'),
body: <p className="help--warning">{__('Please select the same file from the initial upload.')}</p>,
onConfirm: (closeModal) => closeModal(),
hideCancel: true,
});
}
}
function handleCancel() {
doOpenModal(MODALS.CONFIRM, {
title: __('Cancel upload'),
subtitle: __('Cancel and remove the selected upload?'),
body: params.name ? <p className="empty">{`lbry://${params.name}`}</p> : undefined,
onConfirm: (closeModal) => {
if (tusIsSessionLocked(params.guid)) {
// Corner-case: it's possible for the upload to resume in another tab
// after the modal has appeared. Make a final lock-check here.
// We can invoke a toast here, but just do nothing for now.
// The upload status should make things obvious.
} else {
if (uploader) {
if (resumable) {
// $FlowFixMe - couldn't resolve to TusUploader manually.
uploader.abort(true); // TUS
} else {
uploader.abort(); // XHR
}
}
// The second parameter (params) can be removed after January 2022.
doUpdateUploadRemove(params.guid, params);
}
closeModal();
},
});
}
function resolveProgressStr() {
if (locked) {
return __('File being uploaded in another tab or window.');
}
if (!uploader) {
return __('Stopped.');
}
if (resumable) {
if (status) {
switch (status) {
case 'retry':
return __('Uploading...');
case 'error':
return __('Failed.');
case 'conflict':
return __('Stopped. Duplicate session detected.');
default:
return status;
}
} else {
const progressInt = parseInt(progress);
return progressInt === 100 ? __('Processing...') : __('Uploading...');
}
} else {
return __('Uploading...');
}
}
function getRetryButton() {
if (!resumable || locked) {
return null;
}
if (uploader) {
// Should still be uploading. Don't show.
return null;
} else {
// Refreshed or connection broken.
const isFileActive = file instanceof File;
return (
<Button
label={isFileActive ? __('Resume') : __('Retry')}
button="link"
onClick={() => {
if (isFileActive) {
doPublishResume({ ...params, file_path: file });
} else {
setShowFileSelector(true);
}
}}
disabled={showFileSelector}
/>
);
}
}
function getCancelButton() {
if (!locked) {
return <Button label={__('Cancel')} button="link" onClick={handleCancel} />;
}
}
function getFileSelector() {
return (
<div className="claim-preview--padded">
<FileSelector
label={__('File')}
onFileChosen={handleFileChange}
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
accept={'video/mp4,video/x-m4v,video/*,audio/*'}
placeholder={__('Select the file to resume upload...')}
/>
</div>
);
}
function getProgressBar() {
return (
<>
<div className="claim-upload__progress--label">lbry://{params.name}</div>
<div className={'claim-upload__progress--outer card--inline'}>
<div className={'claim-upload__progress--inner'} style={{ width: `${progress}%` }}>
<span className="claim-upload__progress--inner-text">{resolveProgressStr()}</span>
</div>
</div>
</>
);
}
React.useEffect(() => {
if (locked && showFileSelector) {
setShowFileSelector(false);
}
}, [locked, showFileSelector]);
return (
<li className={'web-upload-item claim-preview claim-preview--inactive card--inline'}>
<FileThumbnail thumbnail={params.thumbnail_url} />
<div className={'claim-preview-metadata'}>
<div className="claim-preview-info">
<div className="claim-preview__title">{params.title}</div>
<div className="card__actions--inline">
{getRetryButton()}
{getCancelButton()}
</div>
</div>
{showFileSelector && getFileSelector()}
{!showFileSelector && getProgressBar()}
</div>
</li>
);
}