640237c630
## Ticket 725 ## Issue Upload a video. When `notify` is sent at the end of the tus upload, refresh immediately. The GUI allowed the user to resume the upload, but the ID is no longer present in the server. ## Approach Until the polling API for `notify` is available, we can only assume the best and let the user know how to handle it. - Store the "notify was sent" state. - Show a dialog explaining the situation. Thought of trying to make `claim_list` calls behind the scenes to clear itself, but it doesn't handle the case of `notify` actually failing. The best is to just let the user handle it for now. Note that for the case of `onerror` actually received, we still retry since a network error could be the culprit (`notify` wasn't sent).
234 lines
7.4 KiB
JavaScript
234 lines
7.4 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) {
|
|
if (status === 'notify') {
|
|
return __('File uploaded to server.');
|
|
} else {
|
|
return __('Stopped.');
|
|
}
|
|
}
|
|
|
|
if (resumable) {
|
|
if (status) {
|
|
switch (status) {
|
|
case 'retry':
|
|
return __('Uploading...');
|
|
case 'error':
|
|
return __('Failed.');
|
|
case 'conflict':
|
|
return __('Stopped. Duplicate session detected.');
|
|
case 'notify':
|
|
return __('Processing file. Please wait...');
|
|
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 ...
|
|
|
|
if (status === 'notify') {
|
|
// ... but 'notify' sent, so we have to assume it is processed.
|
|
// Can't do much until the polling API is available.
|
|
return null;
|
|
}
|
|
|
|
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) {
|
|
if (status === 'notify') {
|
|
return (
|
|
<Button
|
|
button="link"
|
|
label={__('Remove')}
|
|
onClick={() => {
|
|
doOpenModal(MODALS.CONFIRM, {
|
|
title: __('Remove entry?'),
|
|
body: (
|
|
<>
|
|
<p>
|
|
{__('The file was successfully uploaded, but we could not retrieve the confirmation status.')}
|
|
</p>
|
|
<p>
|
|
{__(
|
|
'Wait 5-10 minutes, then refresh and check the Uploads list and Wallet transactions before attempting to re-upload.'
|
|
)}
|
|
</p>
|
|
<p className="section__subtitle">
|
|
{__('This entry can be safely removed if the transaction is visible in those pages.')}
|
|
</p>
|
|
<div className="help--warning">
|
|
<p>{__('Press OK to clear this entry from the "Currently Uploading" list.')}</p>
|
|
</div>
|
|
</>
|
|
),
|
|
onConfirm: (closeModal) => {
|
|
doUpdateUploadRemove(params.guid);
|
|
closeModal();
|
|
},
|
|
});
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
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>
|
|
);
|
|
}
|