Temp revert to allow putting back the commits
This reverts commit 157b50c58e
.
This commit is contained in:
parent
fcf19a07e8
commit
994d9c6027
13 changed files with 58 additions and 286 deletions
3
flow-typed/publish.js
vendored
3
flow-typed/publish.js
vendored
|
@ -62,8 +62,7 @@ declare type FileUploadSdkParams = {
|
||||||
remote_url?: string,
|
remote_url?: string,
|
||||||
thumbnail_url?: string,
|
thumbnail_url?: string,
|
||||||
title?: string,
|
title?: string,
|
||||||
// Temporary values; remove when passing to SDK
|
// Temporary values
|
||||||
guid: string,
|
|
||||||
uploadUrl?: string,
|
uploadUrl?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1307,8 +1307,6 @@
|
||||||
"Uploading...": "Uploading...",
|
"Uploading...": "Uploading...",
|
||||||
"Creating...": "Creating...",
|
"Creating...": "Creating...",
|
||||||
"Stopped. Duplicate session detected.": "Stopped. Duplicate session detected.",
|
"Stopped. Duplicate session detected.": "Stopped. Duplicate session detected.",
|
||||||
"File being uploaded in another tab or window.": "File being uploaded in another tab or window.",
|
|
||||||
"There are pending uploads.": "There are pending uploads.",
|
|
||||||
"Use a URL": "Use a URL",
|
"Use a URL": "Use a URL",
|
||||||
"Edit Cover Image": "Edit Cover Image",
|
"Edit Cover Image": "Edit Cover Image",
|
||||||
"Cover Image": "Cover Image",
|
"Cover Image": "Cover Image",
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { useEffect, useRef, useState, useLayoutEffect } from 'react';
|
import React, { useEffect, useRef, useState, useLayoutEffect } from 'react';
|
||||||
import { lazyImport } from 'util/lazyImport';
|
import { lazyImport } from 'util/lazyImport';
|
||||||
import { tusUnlockAndNotify, tusHandleTabUpdates } from 'util/tus';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import { setSearchUserId } from 'redux/actions/search';
|
import { setSearchUserId } from 'redux/actions/search';
|
||||||
|
@ -232,29 +231,12 @@ function App(props: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!uploadCount) return;
|
if (!uploadCount) return;
|
||||||
|
|
||||||
const handleUnload = (event) => tusUnlockAndNotify();
|
|
||||||
const handleBeforeUnload = (event) => {
|
const handleBeforeUnload = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.returnValue = __('There are pending uploads.'); // without setting this to something it doesn't work
|
event.returnValue = 'magic'; // without setting this to something it doesn't work
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('unload', handleUnload);
|
|
||||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||||
|
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||||
return () => {
|
|
||||||
window.removeEventListener('unload', handleUnload);
|
|
||||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
};
|
|
||||||
}, [uploadCount]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!uploadCount) return;
|
|
||||||
|
|
||||||
const onStorageUpdate = (e) => tusHandleTabUpdates(e.key);
|
|
||||||
window.addEventListener('storage', onStorageUpdate);
|
|
||||||
|
|
||||||
return () => window.removeEventListener('storage', onStorageUpdate);
|
|
||||||
}, [uploadCount]);
|
}, [uploadCount]);
|
||||||
|
|
||||||
// allows user to pause miniplayer using the spacebar without the page scrolling down
|
// allows user to pause miniplayer using the spacebar without the page scrolling down
|
||||||
|
|
|
@ -5,12 +5,11 @@ import Button from 'component/button';
|
||||||
import FileThumbnail from 'component/fileThumbnail';
|
import FileThumbnail from 'component/fileThumbnail';
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
import { serializeFileObj } from 'util/file';
|
import { serializeFileObj } from 'util/file';
|
||||||
import { tusIsSessionLocked } from 'util/tus';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uploadItem: FileUploadItem,
|
uploadItem: FileUploadItem,
|
||||||
doPublishResume: (any) => void,
|
doPublishResume: (any) => void,
|
||||||
doUpdateUploadRemove: (string, any) => void,
|
doUpdateUploadRemove: (any) => void,
|
||||||
doOpenModal: (string, {}) => void,
|
doOpenModal: (string, {}) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,16 +18,11 @@ export default function WebUploadItem(props: Props) {
|
||||||
const { params, file, fileFingerprint, progress, status, resumable, uploader } = uploadItem;
|
const { params, file, fileFingerprint, progress, status, resumable, uploader } = uploadItem;
|
||||||
|
|
||||||
const [showFileSelector, setShowFileSelector] = useState(false);
|
const [showFileSelector, setShowFileSelector] = useState(false);
|
||||||
const locked = tusIsSessionLocked(params.guid);
|
|
||||||
|
|
||||||
function handleFileChange(newFile: WebFile, clearName = true) {
|
function handleFileChange(newFile: WebFile, clearName = true) {
|
||||||
if (serializeFileObj(newFile) === fileFingerprint) {
|
if (serializeFileObj(newFile) === fileFingerprint) {
|
||||||
setShowFileSelector(false);
|
setShowFileSelector(false);
|
||||||
doPublishResume({ ...params, file_path: newFile });
|
doPublishResume({ ...params, file_path: newFile });
|
||||||
if (!params.guid) {
|
|
||||||
// Can remove this if-block after January 2022.
|
|
||||||
doUpdateUploadRemove('', params);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
doOpenModal(MODALS.CONFIRM, {
|
doOpenModal(MODALS.CONFIRM, {
|
||||||
title: __('Invalid file'),
|
title: __('Invalid file'),
|
||||||
|
@ -46,34 +40,21 @@ export default function WebUploadItem(props: Props) {
|
||||||
subtitle: __('Cancel and remove the selected upload?'),
|
subtitle: __('Cancel and remove the selected upload?'),
|
||||||
body: params.name ? <p className="empty">{`lbry://${params.name}`}</p> : undefined,
|
body: params.name ? <p className="empty">{`lbry://${params.name}`}</p> : undefined,
|
||||||
onConfirm: (closeModal) => {
|
onConfirm: (closeModal) => {
|
||||||
if (tusIsSessionLocked(params.guid)) {
|
if (uploader) {
|
||||||
// Corner-case: it's possible for the upload to resume in another tab
|
if (resumable) {
|
||||||
// after the modal has appeared. Make a final lock-check here.
|
// $FlowFixMe - couldn't resolve to TusUploader manually.
|
||||||
// We can invoke a toast here, but just do nothing for now.
|
uploader.abort(true); // TUS
|
||||||
// The upload status should make things obvious.
|
} else {
|
||||||
} else {
|
uploader.abort(); // XHR
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
doUpdateUploadRemove(params);
|
||||||
closeModal();
|
closeModal();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveProgressStr() {
|
function resolveProgressStr() {
|
||||||
if (locked) {
|
|
||||||
return __('File being uploaded in another tab or window.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!uploader) {
|
if (!uploader) {
|
||||||
return __('Stopped.');
|
return __('Stopped.');
|
||||||
}
|
}
|
||||||
|
@ -100,7 +81,7 @@ export default function WebUploadItem(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRetryButton() {
|
function getRetryButton() {
|
||||||
if (!resumable || locked) {
|
if (!resumable) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,9 +109,7 @@ export default function WebUploadItem(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCancelButton() {
|
function getCancelButton() {
|
||||||
if (!locked) {
|
return <Button label={__('Cancel')} button="link" onClick={handleCancel} />;
|
||||||
return <Button label={__('Cancel')} button="link" onClick={handleCancel} />;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileSelector() {
|
function getFileSelector() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ type Props = {
|
||||||
currentUploads: { [key: string]: FileUploadItem },
|
currentUploads: { [key: string]: FileUploadItem },
|
||||||
uploadCount: number,
|
uploadCount: number,
|
||||||
doPublishResume: (any) => void,
|
doPublishResume: (any) => void,
|
||||||
doUpdateUploadRemove: (string, any) => void,
|
doUpdateUploadRemove: (any) => void,
|
||||||
doOpenModal: (string, {}) => void,
|
doOpenModal: (string, {}) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
// Local Storage keys
|
|
||||||
export const TUS_LOCKED_UPLOADS = 'tusLockedUploads';
|
|
||||||
export const TUS_REMOVED_UPLOADS = 'tusRemovedUploads';
|
|
|
@ -702,28 +702,21 @@ export function doUpdateUploadAdd(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const doUpdateUploadProgress = (props: { guid: string, progress?: string, status?: string }) => (
|
export const doUpdateUploadProgress = (props: {
|
||||||
dispatch: Dispatch
|
params: { [key: string]: any },
|
||||||
) =>
|
progress?: string,
|
||||||
|
status?: string,
|
||||||
|
}) => (dispatch: Dispatch) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.UPDATE_UPLOAD_PROGRESS,
|
type: ACTIONS.UPDATE_UPLOAD_PROGRESS,
|
||||||
data: props,
|
data: props,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
export function doUpdateUploadRemove(params: { [key: string]: any }) {
|
||||||
* doUpdateUploadRemove
|
|
||||||
*
|
|
||||||
* @param guid
|
|
||||||
* @param params Optional. Retain to allow removal of old keys, which are
|
|
||||||
* derived from `name#channel` instead of using a guid.
|
|
||||||
* Can be removed after January 2022.
|
|
||||||
* @returns {(function(Dispatch, GetState): void)|*}
|
|
||||||
*/
|
|
||||||
export function doUpdateUploadRemove(guid: string, params?: { [key: string]: any }) {
|
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.UPDATE_UPLOAD_REMOVE,
|
type: ACTIONS.UPDATE_UPLOAD_REMOVE,
|
||||||
data: { guid, params },
|
data: { params },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,11 @@
|
||||||
import { handleActions } from 'util/redux-utils';
|
import { handleActions } from 'util/redux-utils';
|
||||||
import { buildURI } from 'util/lbryURI';
|
import { buildURI } from 'util/lbryURI';
|
||||||
import { serializeFileObj } from 'util/file';
|
import { serializeFileObj } from 'util/file';
|
||||||
import { tusLockAndNotify, tusUnlockAndNotify, tusRemoveAndNotify, tusClearRemovedUploads } from 'util/tus';
|
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses';
|
import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses';
|
||||||
import { CHANNEL_ANONYMOUS } from 'constants/claim';
|
import { CHANNEL_ANONYMOUS } from 'constants/claim';
|
||||||
|
|
||||||
// This is the old key formula. Retain it for now to allow users to delete
|
const getKeyFromParam = (params) => `${params.name}#${params.channel || 'anonymous'}`;
|
||||||
// any pending uploads. Can be removed from January 2022 onwards.
|
|
||||||
const getOldKeyFromParam = (params) => `${params.name}#${params.channel || 'anonymous'}`;
|
|
||||||
|
|
||||||
type PublishState = {
|
type PublishState = {
|
||||||
editingURI: ?string,
|
editingURI: ?string,
|
||||||
|
@ -141,9 +138,10 @@ export const publishReducer = handleActions(
|
||||||
},
|
},
|
||||||
[ACTIONS.UPDATE_UPLOAD_ADD]: (state: PublishState, action) => {
|
[ACTIONS.UPDATE_UPLOAD_ADD]: (state: PublishState, action) => {
|
||||||
const { file, params, uploader } = action.data;
|
const { file, params, uploader } = action.data;
|
||||||
|
const key = getKeyFromParam(params);
|
||||||
const currentUploads = Object.assign({}, state.currentUploads);
|
const currentUploads = Object.assign({}, state.currentUploads);
|
||||||
|
|
||||||
currentUploads[params.guid] = {
|
currentUploads[key] = {
|
||||||
file,
|
file,
|
||||||
fileFingerprint: file ? serializeFileObj(file) : undefined, // TODO: get hash instead?
|
fileFingerprint: file ? serializeFileObj(file) : undefined, // TODO: get hash instead?
|
||||||
progress: '0',
|
progress: '0',
|
||||||
|
@ -155,14 +153,10 @@ export const publishReducer = handleActions(
|
||||||
return { ...state, currentUploads };
|
return { ...state, currentUploads };
|
||||||
},
|
},
|
||||||
[ACTIONS.UPDATE_UPLOAD_PROGRESS]: (state: PublishState, action) => {
|
[ACTIONS.UPDATE_UPLOAD_PROGRESS]: (state: PublishState, action) => {
|
||||||
const { guid, progress, status } = action.data;
|
const { params, progress, status } = action.data;
|
||||||
const key = guid;
|
const key = getKeyFromParam(params);
|
||||||
const currentUploads = Object.assign({}, state.currentUploads);
|
const currentUploads = Object.assign({}, state.currentUploads);
|
||||||
|
|
||||||
if (guid === 'force--update') {
|
|
||||||
return { ...state, currentUploads };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentUploads[key]) {
|
if (!currentUploads[key]) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -171,16 +165,10 @@ export const publishReducer = handleActions(
|
||||||
currentUploads[key].progress = progress;
|
currentUploads[key].progress = progress;
|
||||||
delete currentUploads[key].status;
|
delete currentUploads[key].status;
|
||||||
|
|
||||||
if (currentUploads[key].uploader.url) {
|
if (currentUploads[key].uploader.url && !currentUploads[key].params.uploadUrl) {
|
||||||
// TUS has finally obtained an upload url from the server...
|
// TUS has finally obtained an upload url from the server. Stash that to check later when resuming.
|
||||||
if (!currentUploads[key].params.uploadUrl) {
|
// Ignoring immutable-update requirement (probably doesn't matter to the GUI).
|
||||||
// ... Stash that to check later when resuming.
|
currentUploads[key].params.uploadUrl = currentUploads[key].uploader.url;
|
||||||
// Ignoring immutable-update requirement (probably doesn't matter to the GUI).
|
|
||||||
currentUploads[key].params.uploadUrl = currentUploads[key].uploader.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... lock this tab as the active uploader.
|
|
||||||
tusLockAndNotify(key);
|
|
||||||
}
|
}
|
||||||
} else if (status) {
|
} else if (status) {
|
||||||
currentUploads[key].status = status;
|
currentUploads[key].status = status;
|
||||||
|
@ -192,19 +180,13 @@ export const publishReducer = handleActions(
|
||||||
return { ...state, currentUploads };
|
return { ...state, currentUploads };
|
||||||
},
|
},
|
||||||
[ACTIONS.UPDATE_UPLOAD_REMOVE]: (state: PublishState, action) => {
|
[ACTIONS.UPDATE_UPLOAD_REMOVE]: (state: PublishState, action) => {
|
||||||
const { guid, params } = action.data;
|
const { params } = action.data;
|
||||||
const key = guid || getOldKeyFromParam(params);
|
const key = getKeyFromParam(params);
|
||||||
|
const currentUploads = Object.assign({}, state.currentUploads);
|
||||||
|
|
||||||
if (state.currentUploads[key]) {
|
delete currentUploads[key];
|
||||||
const currentUploads = Object.assign({}, state.currentUploads);
|
|
||||||
delete currentUploads[key];
|
|
||||||
tusUnlockAndNotify(key);
|
|
||||||
tusRemoveAndNotify(key);
|
|
||||||
|
|
||||||
return { ...state, currentUploads };
|
return { ...state, currentUploads };
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
},
|
},
|
||||||
[ACTIONS.REHYDRATE]: (state: PublishState, action) => {
|
[ACTIONS.REHYDRATE]: (state: PublishState, action) => {
|
||||||
if (action && action.payload && action.payload.publish) {
|
if (action && action.payload && action.payload.publish) {
|
||||||
|
@ -212,20 +194,14 @@ export const publishReducer = handleActions(
|
||||||
|
|
||||||
// Cleanup for 'publish::currentUploads'
|
// Cleanup for 'publish::currentUploads'
|
||||||
if (newPublish.currentUploads) {
|
if (newPublish.currentUploads) {
|
||||||
const uploadKeys = Object.keys(newPublish.currentUploads);
|
Object.keys(newPublish.currentUploads).forEach((key) => {
|
||||||
if (uploadKeys.length > 0) {
|
const params = newPublish.currentUploads[key].params;
|
||||||
// Clear uploader and corrupted params
|
if (!params || Object.keys(params).length === 0) {
|
||||||
uploadKeys.forEach((key) => {
|
delete newPublish.currentUploads[key];
|
||||||
const params = newPublish.currentUploads[key].params;
|
} else {
|
||||||
if (!params || Object.keys(params).length === 0) {
|
delete newPublish.currentUploads[key].uploader;
|
||||||
delete newPublish.currentUploads[key];
|
}
|
||||||
} else {
|
});
|
||||||
delete newPublish.currentUploads[key].uploader;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
tusClearRemovedUploads();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPublish;
|
return newPublish;
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
export function isLocalStorageAvailable() {
|
|
||||||
try {
|
|
||||||
return Boolean(window.localStorage);
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSessionStorageAvailable() {
|
|
||||||
try {
|
|
||||||
return Boolean(window.sessionStorage);
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
129
ui/util/tus.js
129
ui/util/tus.js
|
@ -1,129 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This serves a bridge between tabs using localStorage to indicate whether an
|
|
||||||
* upload is currently in progress (locked) or removed.
|
|
||||||
*
|
|
||||||
* An alternative is to sync the redux's 'publish::currentUploads' through the
|
|
||||||
* wallet's sync process, but let's not pollute the wallet for now.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
import { TUS_LOCKED_UPLOADS, TUS_REMOVED_UPLOADS } from 'constants/storage';
|
|
||||||
import { isLocalStorageAvailable } from 'util/storage';
|
|
||||||
import { doUpdateUploadRemove, doUpdateUploadProgress } from 'redux/actions/publish';
|
|
||||||
|
|
||||||
const localStorageAvailable = isLocalStorageAvailable();
|
|
||||||
|
|
||||||
let gTabId: string = '';
|
|
||||||
|
|
||||||
function getTabId() {
|
|
||||||
if (!gTabId) {
|
|
||||||
// We want to maximize bootup speed, so only initialize
|
|
||||||
// the tab ID on first use instead when declared.
|
|
||||||
gTabId = uuid();
|
|
||||||
}
|
|
||||||
return gTabId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************
|
|
||||||
// Locked
|
|
||||||
// ****************************************************************************
|
|
||||||
|
|
||||||
function getLockedUploads() {
|
|
||||||
if (localStorageAvailable) {
|
|
||||||
const storedValue = window.localStorage.getItem(TUS_LOCKED_UPLOADS);
|
|
||||||
return storedValue ? JSON.parse(storedValue) : {};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tusIsSessionLocked(guid: string) {
|
|
||||||
const lockedUploads = getLockedUploads();
|
|
||||||
return lockedUploads[guid] && lockedUploads[guid] !== getTabId();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tusLockAndNotify(guid: string) {
|
|
||||||
const lockedUploads = getLockedUploads();
|
|
||||||
if (!lockedUploads[guid] && localStorageAvailable) {
|
|
||||||
lockedUploads[guid] = getTabId();
|
|
||||||
window.localStorage.setItem(TUS_LOCKED_UPLOADS, JSON.stringify(lockedUploads));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tusUnlockAndNotify
|
|
||||||
*
|
|
||||||
* @param guid The upload session to unlock and notify other tabs of.
|
|
||||||
* Passing 'undefined' will clear all sessions locked by this tab.
|
|
||||||
*/
|
|
||||||
export function tusUnlockAndNotify(guid?: string) {
|
|
||||||
if (!localStorageAvailable) return;
|
|
||||||
|
|
||||||
const lockedUploads = getLockedUploads();
|
|
||||||
|
|
||||||
if (guid) {
|
|
||||||
delete lockedUploads[guid];
|
|
||||||
} else {
|
|
||||||
const ourTabId = getTabId();
|
|
||||||
const lockedUploadsEntries = Object.entries(lockedUploads);
|
|
||||||
lockedUploadsEntries.forEach(([lockedGuid, tabId]) => {
|
|
||||||
if (tabId === ourTabId) {
|
|
||||||
delete lockedUploads[lockedGuid];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(lockedUploads).length > 0) {
|
|
||||||
window.localStorage.setItem(TUS_LOCKED_UPLOADS, JSON.stringify(lockedUploads));
|
|
||||||
} else {
|
|
||||||
window.localStorage.removeItem(TUS_LOCKED_UPLOADS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************
|
|
||||||
// Removed
|
|
||||||
// ****************************************************************************
|
|
||||||
|
|
||||||
function getRemovedUploads() {
|
|
||||||
if (localStorageAvailable) {
|
|
||||||
const storedValue = window.localStorage.getItem(TUS_REMOVED_UPLOADS);
|
|
||||||
return storedValue ? storedValue.split(',') : [];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tusRemoveAndNotify(guid: string) {
|
|
||||||
if (!localStorageAvailable) return;
|
|
||||||
const removedUploads = getRemovedUploads();
|
|
||||||
removedUploads.push(guid);
|
|
||||||
window.localStorage.setItem(TUS_REMOVED_UPLOADS, removedUploads.join(','));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tusClearRemovedUploads() {
|
|
||||||
if (!localStorageAvailable) return;
|
|
||||||
window.localStorage.removeItem(TUS_REMOVED_UPLOADS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************
|
|
||||||
// Respond to changes from other tabs.
|
|
||||||
// ****************************************************************************
|
|
||||||
|
|
||||||
export function tusHandleTabUpdates(storageKey: string) {
|
|
||||||
switch (storageKey) {
|
|
||||||
case TUS_LOCKED_UPLOADS:
|
|
||||||
// The locked IDs are in localStorage, but related GUI is unaware.
|
|
||||||
// Send a redux update to force an update.
|
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid: 'force--update' }));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TUS_REMOVED_UPLOADS:
|
|
||||||
// The other tab's store has removed this upload, so it's safe to do the
|
|
||||||
// same without affecting rehydration.
|
|
||||||
if (localStorageAvailable) {
|
|
||||||
const removedUploads = getRemovedUploads();
|
|
||||||
removedUploads.forEach((guid) => window.store.dispatch(doUpdateUploadRemove(guid)));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -30,12 +30,10 @@ export function makeUploadRequest(
|
||||||
delete params['remote_url'];
|
delete params['remote_url'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { uploadUrl, guid, ...sdkParams } = params;
|
|
||||||
|
|
||||||
const jsonPayload = JSON.stringify({
|
const jsonPayload = JSON.stringify({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
method: ENDPOINT_METHOD,
|
method: ENDPOINT_METHOD,
|
||||||
params: sdkParams,
|
params,
|
||||||
id: new Date().getTime(),
|
id: new Date().getTime(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,18 +47,18 @@ export function makeUploadRequest(
|
||||||
xhr.responseType = 'json';
|
xhr.responseType = 'json';
|
||||||
xhr.upload.onprogress = (e) => {
|
xhr.upload.onprogress = (e) => {
|
||||||
const percentage = ((e.loaded / e.total) * 100).toFixed(2);
|
const percentage = ((e.loaded / e.total) * 100).toFixed(2);
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid, progress: percentage }));
|
window.store.dispatch(doUpdateUploadProgress({ params, progress: percentage }));
|
||||||
};
|
};
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
window.store.dispatch(doUpdateUploadRemove(guid));
|
window.store.dispatch(doUpdateUploadRemove(params));
|
||||||
resolve(xhr);
|
resolve(xhr);
|
||||||
};
|
};
|
||||||
xhr.onerror = () => {
|
xhr.onerror = () => {
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid, status: 'error' }));
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'error' }));
|
||||||
reject(new Error(__('There was a problem with your upload. Please try again.')));
|
reject(new Error(__('There was a problem with your upload. Please try again.')));
|
||||||
};
|
};
|
||||||
xhr.onabort = () => {
|
xhr.onabort = () => {
|
||||||
window.store.dispatch(doUpdateUploadRemove(guid));
|
window.store.dispatch(doUpdateUploadRemove(params));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isPreview) {
|
if (!isPreview) {
|
||||||
|
|
|
@ -38,12 +38,13 @@ export function makeResumableUploadRequest(
|
||||||
reject(new Error('Publish: v2 does not support remote_url'));
|
reject(new Error('Publish: v2 does not support remote_url'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { uploadUrl, guid, ...sdkParams } = params;
|
const payloadParams = Object.assign({}, params);
|
||||||
|
delete payloadParams.uploadUrl; // cleanup
|
||||||
|
|
||||||
const jsonPayload = JSON.stringify({
|
const jsonPayload = JSON.stringify({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
method: RESUMABLE_ENDPOINT_METHOD,
|
method: RESUMABLE_ENDPOINT_METHOD,
|
||||||
params: sdkParams,
|
params: payloadParams,
|
||||||
id: new Date().getTime(),
|
id: new Date().getTime(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ export function makeResumableUploadRequest(
|
||||||
filetype: file instanceof File ? file.type : undefined,
|
filetype: file instanceof File ? file.type : undefined,
|
||||||
},
|
},
|
||||||
onShouldRetry: (err, retryAttempt, options) => {
|
onShouldRetry: (err, retryAttempt, options) => {
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid, status: 'retry' }));
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'retry' }));
|
||||||
const status = err.originalResponse ? err.originalResponse.getStatus() : 0;
|
const status = err.originalResponse ? err.originalResponse.getStatus() : 0;
|
||||||
return !inStatusCategory(status, 400);
|
return !inStatusCategory(status, 400);
|
||||||
},
|
},
|
||||||
|
@ -78,17 +79,17 @@ export function makeResumableUploadRequest(
|
||||||
const errMsg = typeof err === 'string' ? err : err.message;
|
const errMsg = typeof err === 'string' ? err : err.message;
|
||||||
|
|
||||||
if (status === STATUS_CONFLICT || status === STATUS_LOCKED || errMsg === 'file currently locked') {
|
if (status === STATUS_CONFLICT || status === STATUS_LOCKED || errMsg === 'file currently locked') {
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid, status: 'conflict' }));
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'conflict' }));
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
reject(new Error(`${status}: concurrent upload detected. Uploading the same file from multiple tabs or windows is not allowed.`));
|
reject(new Error(`${status}: concurrent upload detected. Uploading the same file from multiple tabs or windows is not allowed.`));
|
||||||
} else {
|
} else {
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid, status: 'error' }));
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'error' }));
|
||||||
reject(new Error(err));
|
reject(new Error(err));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onProgress: (bytesUploaded, bytesTotal) => {
|
onProgress: (bytesUploaded, bytesTotal) => {
|
||||||
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
|
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid, progress: percentage }));
|
window.store.dispatch(doUpdateUploadProgress({ params, progress: percentage }));
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
let retries = 1;
|
let retries = 1;
|
||||||
|
@ -101,7 +102,7 @@ export function makeResumableUploadRequest(
|
||||||
xhr.setRequestHeader(X_LBRY_AUTH_TOKEN, token);
|
xhr.setRequestHeader(X_LBRY_AUTH_TOKEN, token);
|
||||||
xhr.responseType = 'json';
|
xhr.responseType = 'json';
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
window.store.dispatch(doUpdateUploadRemove(guid));
|
window.store.dispatch(doUpdateUploadRemove(params));
|
||||||
resolve(xhr);
|
resolve(xhr);
|
||||||
};
|
};
|
||||||
xhr.onerror = () => {
|
xhr.onerror = () => {
|
||||||
|
@ -110,12 +111,12 @@ export function makeResumableUploadRequest(
|
||||||
analytics.error('notify: first attempt failed (status=0). Retrying after 10s...');
|
analytics.error('notify: first attempt failed (status=0). Retrying after 10s...');
|
||||||
setTimeout(() => makeNotifyRequest(), 10000); // Auto-retry after 10s delay.
|
setTimeout(() => makeNotifyRequest(), 10000); // Auto-retry after 10s delay.
|
||||||
} else {
|
} else {
|
||||||
window.store.dispatch(doUpdateUploadProgress({ guid, status: 'error' }));
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'error' }));
|
||||||
reject(new Error(`There was a problem in the processing. Please retry. (${xhr.status})`));
|
reject(new Error(`There was a problem in the processing. Please retry. (${xhr.status})`));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.onabort = () => {
|
xhr.onabort = () => {
|
||||||
window.store.dispatch(doUpdateUploadRemove(guid));
|
window.store.dispatch(doUpdateUploadRemove(params));
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.send(jsonPayload);
|
xhr.send(jsonPayload);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as tus from 'tus-js-client';
|
import * as tus from 'tus-js-client';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
import { makeUploadRequest } from './publish-v1';
|
import { makeUploadRequest } from './publish-v1';
|
||||||
import { makeResumableUploadRequest } from './publish-v2';
|
import { makeResumableUploadRequest } from './publish-v2';
|
||||||
|
|
||||||
|
@ -35,12 +34,6 @@ export default function apiPublishCallViaWeb(
|
||||||
params.file_path = '__POST_FILE__';
|
params.file_path = '__POST_FILE__';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a random ID to serve as the redux key.
|
|
||||||
// If it already exists, then it is a resumed session.
|
|
||||||
if (!params.guid) {
|
|
||||||
params.guid = uuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
const useV1 = remoteUrl || preview || !tus.isSupported;
|
const useV1 = remoteUrl || preview || !tus.isSupported;
|
||||||
|
|
||||||
// Note: both function signature (params) should match.
|
// Note: both function signature (params) should match.
|
||||||
|
|
Loading…
Reference in a new issue