-- Revert to allow restoring commits ---

This reverts commit cb6a044584.
This commit is contained in:
infinite-persistence 2021-11-11 07:59:37 +08:00
parent d649e3563f
commit 263c09500f
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
27 changed files with 325 additions and 814 deletions

View file

@ -8,7 +8,6 @@ const config = {
WEB_SERVER_PORT: process.env.WEB_SERVER_PORT,
LBRY_WEB_API: process.env.LBRY_WEB_API, //api.na-backend.odysee.com',
LBRY_WEB_PUBLISH_API: process.env.LBRY_WEB_PUBLISH_API,
LBRY_WEB_PUBLISH_API_V2: process.env.LBRY_WEB_PUBLISH_API_V2,
LBRY_API_URL: process.env.LBRY_API_URL, //api.lbry.com',
LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //cdn.lbryplayer.xyz',
LBRY_WEB_BUFFER_API: process.env.LBRY_WEB_BUFFER_API,

View file

@ -83,6 +83,9 @@ export const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED';
export const SYNC_APPLY_BAD_PASSWORD = 'SYNC_APPLY_BAD_PASSWORD';
export const SYNC_RESET = 'SYNC_RESET';
// Lbry.tv
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
// User
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';

View file

@ -27,6 +27,7 @@ export {
doResetSync,
doSyncEncryptAndDecrypt,
} from 'redux/actions/sync';
export { doUpdateUploadProgress } from './redux/actions/web';
// reducers
export { authReducer } from './redux/reducers/auth';
@ -36,6 +37,7 @@ export { filteredReducer } from './redux/reducers/filtered';
// export { homepageReducer } from './redux/reducers/homepage';
export { statsReducer } from './redux/reducers/stats';
export { syncReducer } from './redux/reducers/sync';
export { webReducer } from './redux/reducers/web';
// selectors
export { selectAuthToken, selectIsAuthenticating } from './redux/selectors/auth';
@ -68,3 +70,4 @@ export {
selectSyncApplyErrorMessage,
selectSyncApplyPasswordError,
} from './redux/selectors/sync';
export { selectCurrentUploads, selectUploadCount } from './redux/selectors/web';

View file

@ -0,0 +1,12 @@
// @flow
import * as ACTIONS from 'constants/action_types';
export const doUpdateUploadProgress = (
progress: string,
params: { [key: string]: any },
xhr: any
) => (dispatch: Dispatch) =>
dispatch({
type: ACTIONS.UPDATE_UPLOAD_PROGRESS,
data: { progress, params, xhr },
});

View file

@ -0,0 +1,62 @@
// @flow
import * as ACTIONS from 'constants/action_types';
/*
test mock:
currentUploads: {
'test#upload': {
progress: 50,
params: {
name: 'steve',
thumbnail_url: 'https://dev2.spee.ch/4/KMNtoSZ009fawGz59VG8PrID.jpeg',
},
},
},
*/
export type Params = {
channel?: string,
name: string,
thumbnail_url: ?string,
title: ?string,
};
export type UploadItem = {
progess: string,
params: Params,
xhr?: any,
};
export type TvState = {
currentUploads: { [key: string]: UploadItem },
};
const reducers = {};
const defaultState: TvState = {
currentUploads: {},
};
reducers[ACTIONS.UPDATE_UPLOAD_PROGRESS] = (state: TvState, action) => {
const { progress, params, xhr } = action.data;
const key = params.channel ? `${params.name}#${params.channel}` : `${params.name}#anonymous`;
let currentUploads;
if (!progress) {
currentUploads = Object.assign({}, state.currentUploads);
Object.keys(currentUploads).forEach(k => {
if (k === key) {
delete currentUploads[key];
}
});
} else {
currentUploads = Object.assign({}, state.currentUploads);
currentUploads[key] = { progress, params, xhr };
}
return { ...state, currentUploads };
};
export function webReducer(state: TvState = defaultState, action: any) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -0,0 +1,10 @@
import { createSelector } from 'reselect';
const selectState = (state) => state.web || {};
export const selectCurrentUploads = (state) => selectState(state).currentUploads;
export const selectUploadCount = createSelector(
selectCurrentUploads,
(currentUploads) => currentUploads && Object.keys(currentUploads).length
);

21
flow-typed/publish.js vendored
View file

@ -52,24 +52,3 @@ declare type PublishParams = {
nsfw: boolean,
tags: Array<Tag>,
};
declare type TusUploader = any;
declare type FileUploadSdkParams = {
file_path: string,
name: ?string,
preview?: boolean,
remote_url?: string,
thumbnail_url?: string,
title?: string,
};
declare type FileUploadItem = {
params: FileUploadSdkParams,
file: File,
fileFingerprint: string,
progress: string,
status?: string,
uploader?: TusUploader | XMLHttpRequest,
resumable: boolean,
};

View file

@ -68,7 +68,6 @@
"rss": "^1.2.2",
"source-map-explorer": "^2.5.2",
"tempy": "^0.6.0",
"tus-js-client": "^2.3.0",
"videojs-contrib-ads": "^6.9.0",
"videojs-ima": "^1.11.0",
"videojs-ima-player": "^0.5.6",

View file

@ -1291,16 +1291,6 @@
"Select a file to upload": "Select a file to upload",
"Select file to upload": "Select file to upload",
"Url copied.": "Url copied.",
"Failed to initiate upload (%err%)": "Failed to initiate upload (%err%)",
"Invalid file": "Invalid file",
"It appears to be a different or modified file.": "It appears to be a different or modified file.",
"Please select the same file from the initial upload.": "Please select the same file from the initial upload.",
"Cancel upload": "Cancel upload",
"Cancel and remove the selected upload?": "Cancel and remove the selected upload?",
"Select the file to resume upload...": "Select the file to resume upload...",
"Stopped.": "Stopped.",
"Resume": "Resume",
"Retrying...": "Retrying...",
"Uploading...": "Uploading...",
"Creating...": "Creating...",
"Use a URL": "Use a URL",
@ -1440,6 +1430,7 @@
"Log in to %SITE_NAME%": "Log in to %SITE_NAME%",
"Log in": "Log in",
"Not Yet": "Not Yet",
"Preparing...": "Preparing...",
"Confirm Upload": "Confirm Upload",
"Confirm Edit": "Confirm Edit",
"Create Livestream": "Create Livestream",

View file

@ -1,5 +1,6 @@
import { hot } from 'react-hot-loader/root';
import { connect } from 'react-redux';
import { selectUploadCount } from 'lbryinc';
import { selectGetSyncErrorMessage, selectSyncFatalError } from 'redux/selectors/sync';
import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user';
import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user';
@ -21,7 +22,6 @@ import {
selectActiveChannelClaim,
selectIsReloadRequired,
} from 'redux/selectors/app';
import { selectUploadCount } from 'redux/selectors/publish';
import { doGetWalletSyncPreference, doSetLanguage } from 'redux/actions/settings';
import { doSyncLoop } from 'redux/actions/sync';
import {

View file

@ -325,7 +325,7 @@ function PublishForm(props: Props) {
submitLabel = __('Uploading...');
}
} else if (previewing) {
submitLabel = <Spinner type="small" />;
submitLabel = __('Preparing...');
} else {
if (isStillEditing) {
submitLabel = __('Save');

View file

@ -1,18 +1,13 @@
import { connect } from 'react-redux';
import { doOpenModal } from 'redux/actions/app';
import { doPublishResume, doUpdateUploadRemove } from 'redux/actions/publish';
import { selectCurrentUploads, selectUploadCount } from 'redux/selectors/publish';
import { selectCurrentUploads, selectUploadCount } from 'lbryinc';
import WebUploadList from './view';
const select = (state) => ({
const select = state => ({
currentUploads: selectCurrentUploads(state),
uploadCount: selectUploadCount(state),
});
const perform = {
doPublishResume,
doUpdateUploadRemove,
doOpenModal,
};
export default connect(select, perform)(WebUploadList);
export default connect(
select,
null
)(WebUploadList);

View file

@ -1,155 +1,40 @@
// @flow
import React, { useState } from 'react';
import FileSelector from 'component/common/file-selector';
import React from 'react';
import Button from 'component/button';
import FileThumbnail from 'component/fileThumbnail';
import * as MODALS from 'constants/modal_types';
import { serializeFileObj } from 'util/file';
type Props = {
uploadItem: FileUploadItem,
doPublishResume: (any) => void,
doUpdateUploadRemove: (any) => void,
doOpenModal: (string, {}) => void,
params: UpdatePublishFormData,
progress: string,
xhr?: () => 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);
function handleFileChange(newFile: WebFile, clearName = true) {
if (serializeFileObj(newFile) === fileFingerprint) {
setShowFileSelector(false);
doPublishResume({ ...params, file_path: newFile });
} 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 (uploader) {
if (resumable) {
// $FlowFixMe - couldn't resolve to TusUploader manually.
uploader.abort(true); // TUS
} else {
uploader.abort(); // XHR
}
}
doUpdateUploadRemove(params);
closeModal();
},
});
}
function resolveProgressStr() {
if (!uploader) {
return __('Stopped.');
}
if (resumable) {
if (status) {
switch (status) {
case 'retry':
return __('Retrying...');
case 'error':
return __('Failed.');
default:
return status;
}
} else {
const progressInt = parseInt(progress);
return progressInt === 100 ? __('Processing...') : __('Uploading...');
}
} else {
return __('Uploading...');
}
}
function getRetryButton() {
if (!resumable) {
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() {
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}%` }}>
{resolveProgressStr()}
</div>
</div>
</>
);
}
const { params, progress, xhr } = props;
return (
<li className={'web-upload-item claim-preview claim-preview--padded claim-preview--inactive card--inline'}>
<li className={'claim-preview claim-preview--padded 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()}
{xhr && (
<div className="card__actions--inline">
<Button
button="link"
onClick={() => {
xhr.abort();
}}
label={__('Cancel')}
/>
</div>
)}
</div>
<h2>lbry://{params.name}</h2>
<div className={'claim-upload__progress--outer card--inline'}>
<div className={'claim-upload__progress--inner'} style={{ width: `${progress}%` }}>
Uploading...
</div>
</div>
{showFileSelector && getFileSelector()}
{!showFileSelector && getProgressBar()}
</div>
</li>
);

View file

@ -3,16 +3,19 @@ import * as React from 'react';
import Card from 'component/common/card';
import WebUploadItem from './internal/web-upload-item';
export type UploadItem = {
progess: string,
params: UpdatePublishFormData,
xhr?: { abort: () => void },
};
type Props = {
currentUploads: { [key: string]: FileUploadItem },
currentUploads: { [key: string]: UploadItem },
uploadCount: number,
doPublishResume: (any) => void,
doUpdateUploadRemove: (any) => void,
doOpenModal: (string, {}) => void,
};
export default function WebUploadList(props: Props) {
const { currentUploads, uploadCount, doPublishResume, doUpdateUploadRemove, doOpenModal } = props;
const { currentUploads, uploadCount } = props;
return (
!!uploadCount && (
@ -22,16 +25,8 @@ export default function WebUploadList(props: Props) {
body={
<section>
{/* $FlowFixMe */}
{Object.values(currentUploads).map((uploadItem) => (
<WebUploadItem
// $FlowFixMe
key={`upload${uploadItem.params.name}`}
// $FlowFixMe
uploadItem={uploadItem}
doPublishResume={doPublishResume}
doUpdateUploadRemove={doUpdateUploadRemove}
doOpenModal={doOpenModal}
/>
{Object.values(currentUploads).map(({ progress, params, xhr }) => (
<WebUploadItem key={`upload${params.name}`} progress={progress} params={params} xhr={xhr} />
))}
</section>
}

View file

@ -331,9 +331,6 @@ export const PUBLISH_FAIL = 'PUBLISH_FAIL';
export const CLEAR_PUBLISH_ERROR = 'CLEAR_PUBLISH_ERROR';
export const REMOVE_PENDING_PUBLISH = 'REMOVE_PENDING_PUBLISH';
export const DO_PREPARE_EDIT = 'DO_PREPARE_EDIT';
export const UPDATE_UPLOAD_ADD = 'UPDATE_UPLOAD_ADD';
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
export const UPDATE_UPLOAD_REMOVE = 'UPDATE_UPLOAD_REMOVE';
// Media
export const MEDIA_PLAY = 'MEDIA_PLAY';
@ -494,6 +491,9 @@ export const FETCH_SUB_COUNT_STARTED = 'FETCH_SUB_COUNT_STARTED';
export const FETCH_SUB_COUNT_FAILED = 'FETCH_SUB_COUNT_FAILED';
export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED';
// Lbry.tv
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
// User
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';

View file

@ -35,7 +35,7 @@ import {
doAuthTokenRefresh,
} from 'util/saved-passwords';
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
import { LBRY_WEB_API, DEFAULT_LANGUAGE, LBRY_API_URL } from 'config';
import { LBRY_WEB_API, DEFAULT_LANGUAGE, LBRY_API_URL, LBRY_WEB_PUBLISH_API } from 'config';
// Import 3rd-party styles before ours for the current way we are code-splitting.
import 'scss/third-party.scss';
@ -69,6 +69,7 @@ sdkAPIHost = LBRY_WEB_API;
export const SDK_API_PATH = `${sdkAPIHost}/api/v1`;
const proxyURL = `${SDK_API_PATH}/proxy`;
const publishURL = LBRY_WEB_PUBLISH_API; // || `${SDK_API_PATH}/proxy`;
Lbry.setDaemonConnectionString(proxyURL);
@ -78,6 +79,7 @@ Lbry.setOverride(
new Promise((resolve, reject) => {
apiPublishCallViaWeb(
apiCall,
publishURL,
Lbry.getApiRequestHeaders() && Object.keys(Lbry.getApiRequestHeaders()).includes(X_LBRY_AUTH_TOKEN)
? Lbry.getApiRequestHeaders()[X_LBRY_AUTH_TOKEN]
: '',

View file

@ -5,9 +5,9 @@ import {
selectMyClaimsPageItemCount,
selectFetchingMyClaimsPageError,
} from 'redux/selectors/claims';
import { selectUploadCount } from 'redux/selectors/publish';
import { doFetchClaimListMine, doCheckPendingClaims } from 'redux/actions/claims';
import { doClearPublish } from 'redux/actions/publish';
import { selectUploadCount } from 'lbryinc';
import FileListPublished from './view';
import { withRouter } from 'react-router';
import { MY_CLAIMS_PAGE_SIZE, PAGE_PARAM, PAGE_SIZE_PARAM } from 'constants/claim';

View file

@ -1,6 +1,6 @@
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import { costInfoReducer, blacklistReducer, filteredReducer, statsReducer } from 'lbryinc';
import { costInfoReducer, blacklistReducer, filteredReducer, statsReducer, webReducer } from 'lbryinc';
import { claimsReducer } from 'redux/reducers/claims';
import { fileInfoReducer } from 'redux/reducers/file_info';
import { walletReducer } from 'redux/reducers/wallet';
@ -50,5 +50,6 @@ export default (history) =>
user: userReducer,
wallet: walletReducer,
sync: syncReducer,
web: webReducer,
collections: collectionsReducer,
});

View file

@ -6,7 +6,6 @@ import { batchActions } from 'util/batch-actions';
import { doCheckPendingClaims } from 'redux/actions/claims';
import {
makeSelectClaimForUri,
selectMyActiveClaims,
selectMyClaims,
selectMyChannelClaims,
// selectMyClaimsWithoutChannels,
@ -48,147 +47,6 @@ function resolveClaimTypeForAnalytics(claim) {
}
export const NO_FILE = '---';
function resolvePublishPayload(publishData, myClaimForUri, myChannels, preview) {
const {
name,
bid,
filePath,
description,
language,
releaseTimeEdited,
// license,
licenseUrl,
useLBRYUploader,
licenseType,
otherLicenseDescription,
thumbnail,
channel,
title,
contentIsFree,
fee,
// uri,
tags,
// locations,
optimize,
isLivestreamPublish,
remoteFileUrl,
} = publishData;
// Handle scenario where we have a claim that has the same name as a channel we are publishing with.
const myClaimForUriEditing = myClaimForUri && myClaimForUri.name === name ? myClaimForUri : null;
let publishingLicense;
switch (licenseType) {
case COPYRIGHT:
case OTHER:
publishingLicense = otherLicenseDescription;
break;
default:
publishingLicense = licenseType;
}
// get the claim id from the channel name, we will use that instead
const namedChannelClaim = myChannels ? myChannels.find((myChannel) => myChannel.name === channel) : null;
const channelId = namedChannelClaim ? namedChannelClaim.claim_id : '';
const publishPayload: {
name: ?string,
bid: string,
description?: string,
channel_id?: string,
file_path?: string,
license_url?: string,
license?: string,
thumbnail_url?: string,
release_time?: number,
fee_currency?: string,
fee_amount?: string,
languages?: Array<string>,
tags: Array<string>,
locations?: Array<any>,
blocking: boolean,
optimize_file?: boolean,
preview?: boolean,
remote_url?: string,
} = {
name,
title,
description,
locations: [],
bid: creditsToString(bid),
languages: [language],
tags: tags && tags.map((tag) => tag.name),
thumbnail_url: thumbnail,
blocking: true,
preview: false,
};
// Temporary solution to keep the same publish flow with the new tags api
// Eventually we will allow users to enter their own tags on publish
// `nsfw` will probably be removed
if (remoteFileUrl) {
publishPayload.remote_url = remoteFileUrl;
}
if (publishingLicense) {
publishPayload.license = publishingLicense;
}
if (licenseUrl) {
publishPayload.license_url = licenseUrl;
}
if (thumbnail) {
publishPayload.thumbnail_url = thumbnail;
}
if (useLBRYUploader) {
publishPayload.tags.push('lbry-first');
}
// Set release time to curret date. On edits, keep original release/transaction time as release_time
if (releaseTimeEdited) {
publishPayload.release_time = releaseTimeEdited;
} else if (myClaimForUriEditing && myClaimForUriEditing.value.release_time) {
publishPayload.release_time = Number(myClaimForUri.value.release_time);
} else if (myClaimForUriEditing && myClaimForUriEditing.timestamp) {
publishPayload.release_time = Number(myClaimForUriEditing.timestamp);
} else {
publishPayload.release_time = Number(Math.round(Date.now() / 1000));
}
if (channelId) {
publishPayload.channel_id = channelId;
}
if (myClaimForUriEditing && myClaimForUriEditing.value && myClaimForUriEditing.value.locations) {
publishPayload.locations = myClaimForUriEditing.value.locations;
}
if (!contentIsFree && fee && fee.currency && Number(fee.amount) > 0) {
publishPayload.fee_currency = fee.currency;
publishPayload.fee_amount = creditsToString(fee.amount);
}
if (optimize) {
publishPayload.optimize_file = true;
}
// Only pass file on new uploads, not metadata only edits.
// The sdk will figure it out
if (filePath && !isLivestreamPublish) {
publishPayload.file_path = filePath;
}
if (preview) {
publishPayload.preview = true;
publishPayload.optimize_file = false;
}
return publishPayload;
}
export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispatch: Dispatch, getState: () => {}) => {
const publishPreview = (previewResponse) => {
dispatch(
@ -290,62 +148,6 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
dispatch(doPublish(publishSuccess, publishFail));
};
export const doPublishResume = (publishPayload: any) => (dispatch: Dispatch, getState: () => {}) => {
const publishSuccess = (successResponse, lbryFirstError) => {
const state = getState();
const myClaimIds: Set<string> = selectMyActiveClaims(state);
const pendingClaim = successResponse.outputs[0];
const { permanent_url: url } = pendingClaim;
analytics.apiLogPublish(pendingClaim);
// We have to fake a temp claim until the new pending one is returned by claim_list_mine
// We can't rely on claim_list_mine because there might be some delay before the new claims are returned
// Doing this allows us to show the pending claim immediately, it will get overwritten by the real one
const isEdit = myClaimIds.has(pendingClaim.claim_id);
const actions = [];
actions.push({
type: ACTIONS.PUBLISH_SUCCESS,
data: {
type: resolveClaimTypeForAnalytics(pendingClaim),
},
});
actions.push({
type: ACTIONS.UPDATE_PENDING_CLAIMS,
data: {
claims: [pendingClaim],
},
});
dispatch(batchActions(...actions));
dispatch(
doOpenModal(MODALS.PUBLISH, {
uri: url,
isEdit,
lbryFirstError,
})
);
dispatch(doCheckPendingClaims());
};
const publishFail = (error) => {
const actions = [];
actions.push({
type: ACTIONS.PUBLISH_FAIL,
});
actions.push(doError(error.message));
dispatch(batchActions(...actions));
};
dispatch(doPublish(publishSuccess, publishFail, false, publishPayload));
};
export const doResetThumbnailStatus = () => (dispatch: Dispatch) => {
dispatch({
type: ACTIONS.UPDATE_PUBLISH_FORM,
@ -571,7 +373,7 @@ export const doPrepareEdit = (claim: StreamClaim, uri: string, fileInfo: FileLis
dispatch({ type: ACTIONS.DO_PREPARE_EDIT, data: publishData });
};
export const doPublish = (success: Function, fail: Function, preview: Function, payload: any) => (
export const doPublish = (success: Function, fail: Function, preview: Function) => (
dispatch: Dispatch,
getState: () => {}
) => {
@ -586,9 +388,139 @@ export const doPublish = (success: Function, fail: Function, preview: Function,
// get redux publish form
const publishData = selectPublishFormValues(state);
const publishPayload = payload || resolvePublishPayload(publishData, myClaimForUri, myChannels, preview);
// destructure the data values
const {
name,
bid,
filePath,
description,
language,
releaseTimeEdited,
// license,
licenseUrl,
useLBRYUploader,
licenseType,
otherLicenseDescription,
thumbnail,
channel,
title,
contentIsFree,
fee,
// uri,
tags,
// locations,
optimize,
isLivestreamPublish,
remoteFileUrl,
} = publishData;
// Handle scenario where we have a claim that has the same name as a channel we are publishing with.
const myClaimForUriEditing = myClaimForUri && myClaimForUri.name === name ? myClaimForUri : null;
let publishingLicense;
switch (licenseType) {
case COPYRIGHT:
case OTHER:
publishingLicense = otherLicenseDescription;
break;
default:
publishingLicense = licenseType;
}
// get the claim id from the channel name, we will use that instead
const namedChannelClaim = myChannels ? myChannels.find((myChannel) => myChannel.name === channel) : null;
const channelId = namedChannelClaim ? namedChannelClaim.claim_id : '';
const publishPayload: {
name: ?string,
bid: string,
description?: string,
channel_id?: string,
file_path?: string,
license_url?: string,
license?: string,
thumbnail_url?: string,
release_time?: number,
fee_currency?: string,
fee_amount?: string,
languages?: Array<string>,
tags: Array<string>,
locations?: Array<any>,
blocking: boolean,
optimize_file?: boolean,
preview?: boolean,
remote_url?: string,
} = {
name,
title,
description,
locations: [],
bid: creditsToString(bid),
languages: [language],
tags: tags && tags.map((tag) => tag.name),
thumbnail_url: thumbnail,
blocking: true,
preview: false,
};
// Temporary solution to keep the same publish flow with the new tags api
// Eventually we will allow users to enter their own tags on publish
// `nsfw` will probably be removed
if (remoteFileUrl) {
publishPayload.remote_url = remoteFileUrl;
}
if (publishingLicense) {
publishPayload.license = publishingLicense;
}
if (licenseUrl) {
publishPayload.license_url = licenseUrl;
}
if (thumbnail) {
publishPayload.thumbnail_url = thumbnail;
}
if (useLBRYUploader) {
publishPayload.tags.push('lbry-first');
}
// Set release time to curret date. On edits, keep original release/transaction time as release_time
if (releaseTimeEdited) {
publishPayload.release_time = releaseTimeEdited;
} else if (myClaimForUriEditing && myClaimForUriEditing.value.release_time) {
publishPayload.release_time = Number(myClaimForUri.value.release_time);
} else if (myClaimForUriEditing && myClaimForUriEditing.timestamp) {
publishPayload.release_time = Number(myClaimForUriEditing.timestamp);
} else {
publishPayload.release_time = Number(Math.round(Date.now() / 1000));
}
if (channelId) {
publishPayload.channel_id = channelId;
}
if (myClaimForUriEditing && myClaimForUriEditing.value && myClaimForUriEditing.value.locations) {
publishPayload.locations = myClaimForUriEditing.value.locations;
}
if (!contentIsFree && fee && fee.currency && Number(fee.amount) > 0) {
publishPayload.fee_currency = fee.currency;
publishPayload.fee_amount = creditsToString(fee.amount);
}
if (optimize) {
publishPayload.optimize_file = true;
}
// Only pass file on new uploads, not metadata only edits.
// The sdk will figure it out
if (filePath && !isLivestreamPublish) publishPayload.file_path = filePath;
if (preview) {
publishPayload.preview = true;
publishPayload.optimize_file = false;
return Lbry.publish(publishPayload).then((previewResponse: PublishResponse) => {
return preview(previewResponse);
}, fail);
@ -688,35 +620,3 @@ export const doCheckReflectingFiles = () => (dispatch: Dispatch, getState: GetSt
}, 5000);
}
};
export function doUpdateUploadAdd(
file: File | string,
params: { [key: string]: any },
uploader: TusUploader | XMLHttpRequest
) {
return (dispatch: Dispatch, getState: GetState) => {
dispatch({
type: ACTIONS.UPDATE_UPLOAD_ADD,
data: { file, params, uploader },
});
};
}
export const doUpdateUploadProgress = (props: {
params: { [key: string]: any },
progress?: string,
status?: string,
}) => (dispatch: Dispatch) =>
dispatch({
type: ACTIONS.UPDATE_UPLOAD_PROGRESS,
data: props,
});
export function doUpdateUploadRemove(params: { [key: string]: any }) {
return (dispatch: Dispatch, getState: GetState) => {
dispatch({
type: ACTIONS.UPDATE_UPLOAD_REMOVE,
data: { params },
});
};
}

View file

@ -1,13 +1,10 @@
// @flow
import { handleActions } from 'util/redux-utils';
import { buildURI } from 'util/lbryURI';
import { serializeFileObj } from 'util/file';
import * as ACTIONS from 'constants/action_types';
import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses';
import { CHANNEL_ANONYMOUS } from 'constants/claim';
const getKeyFromParam = (params) => `${params.name}#${params.channel || 'anonymous'}`;
type PublishState = {
editingURI: ?string,
fileText: ?string,
@ -41,7 +38,6 @@ type PublishState = {
tags: Array<string>,
optimize: boolean,
useLBRYUploader: boolean,
currentUploads: { [key: string]: FileUploadItem },
};
const defaultState: PublishState = {
@ -82,7 +78,6 @@ const defaultState: PublishState = {
publishError: undefined,
optimize: false,
useLBRYUploader: false,
currentUploads: {},
};
export const publishReducer = handleActions(
@ -101,7 +96,6 @@ export const publishReducer = handleActions(
bid: state.bid,
optimize: state.optimize,
language: state.language,
currentUploads: state.currentUploads,
}),
[ACTIONS.PUBLISH_START]: (state: PublishState): PublishState => ({
...state,
@ -133,76 +127,8 @@ export const publishReducer = handleActions(
...publishData,
editingURI: uri,
uri: shortUri,
currentUploads: state.currentUploads,
};
},
[ACTIONS.UPDATE_UPLOAD_ADD]: (state: PublishState, action) => {
const { file, params, uploader } = action.data;
const key = getKeyFromParam(params);
const currentUploads = Object.assign({}, state.currentUploads);
currentUploads[key] = {
file,
fileFingerprint: file ? serializeFileObj(file) : undefined, // TODO: get hash instead?
progress: '0',
params,
uploader,
resumable: !(uploader instanceof XMLHttpRequest),
};
return { ...state, currentUploads };
},
[ACTIONS.UPDATE_UPLOAD_PROGRESS]: (state: PublishState, action) => {
const { params, progress, status } = action.data;
const key = getKeyFromParam(params);
const currentUploads = Object.assign({}, state.currentUploads);
if (!currentUploads[key]) {
return state;
}
if (progress) {
currentUploads[key].progress = progress;
delete currentUploads[key].status;
} else if (status) {
currentUploads[key].status = status;
if (status === 'error') {
delete currentUploads[key].uploader;
}
}
return { ...state, currentUploads };
},
[ACTIONS.UPDATE_UPLOAD_REMOVE]: (state: PublishState, action) => {
const { params } = action.data;
const key = getKeyFromParam(params);
const currentUploads = Object.assign({}, state.currentUploads);
delete currentUploads[key];
return { ...state, currentUploads };
},
[ACTIONS.REHYDRATE]: (state: PublishState, action) => {
if (action && action.payload && action.payload.publish) {
const newPublish = { ...action.payload.publish };
// Cleanup for 'publish::currentUploads'
if (newPublish.currentUploads) {
Object.keys(newPublish.currentUploads).forEach((key) => {
const params = newPublish.currentUploads[key].params;
if (!params || Object.keys(params).length === 0) {
delete newPublish.currentUploads[key];
} else {
delete newPublish.currentUploads[key].uploader;
}
});
}
return newPublish;
}
return state;
},
},
defaultState
);

View file

@ -119,10 +119,3 @@ export const selectTakeOverAmount = createSelector(
return null;
}
);
export const selectCurrentUploads = (state) => selectState(state).currentUploads;
export const selectUploadCount = createSelector(
selectCurrentUploads,
(currentUploads) => currentUploads && Object.keys(currentUploads).length
);

View file

@ -363,11 +363,6 @@
}
}
.claim-upload__progress--label {
font-size: var(--font-small);
color: var(--color-text-subtitle);
}
.claim-upload__progress--outer {
width: 100%;
}
@ -862,17 +857,3 @@
margin-top: var(--spacing-s);
}
}
.web-upload-item.claim-preview {
@media (max-width: $breakpoint-small) {
display: block;
.media__thumb {
margin-bottom: var(--spacing-s);
}
.claim-preview-metadata {
display: block;
}
}
}

View file

@ -1,5 +0,0 @@
// @flow
export function serializeFileObj(file: File) {
return `${file.name}#${file.type}#${file.size}`;
}

View file

@ -1,70 +0,0 @@
// @flow
// https://api.na-backend.odysee.com/api/v1/proxy currently expects publish to
// consist of a multipart/form-data POST request with:
// - 'file' binary
// - 'json_payload' publish params to be passed to the server's sdk.
import { X_LBRY_AUTH_TOKEN } from '../../ui/constants/token';
import { doUpdateUploadAdd, doUpdateUploadProgress, doUpdateUploadRemove } from '../../ui/redux/actions/publish';
import { LBRY_WEB_PUBLISH_API } from 'config';
const ENDPOINT = LBRY_WEB_PUBLISH_API;
const ENDPOINT_METHOD = 'publish';
export function makeUploadRequest(
token: string,
params: FileUploadSdkParams,
file: File | string,
isPreview?: boolean
) {
const { remote_url: remoteUrl } = params;
const body = new FormData();
if (file) {
body.append('file', file);
delete params['remote_url'];
} else if (remoteUrl) {
body.append('remote_url', remoteUrl);
delete params['remote_url'];
}
const jsonPayload = JSON.stringify({
jsonrpc: '2.0',
method: ENDPOINT_METHOD,
params,
id: new Date().getTime(),
});
// no fileData? do the livestream remote publish
body.append('json_payload', jsonPayload);
return new Promise<any>((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('POST', ENDPOINT);
xhr.setRequestHeader(X_LBRY_AUTH_TOKEN, token);
xhr.responseType = 'json';
xhr.upload.onprogress = (e) => {
const percentage = ((e.loaded / e.total) * 100).toFixed(2);
window.store.dispatch(doUpdateUploadProgress({ params, progress: percentage }));
};
xhr.onload = () => {
window.store.dispatch(doUpdateUploadRemove(params));
resolve(xhr);
};
xhr.onerror = () => {
window.store.dispatch(doUpdateUploadProgress({ params, status: 'error' }));
reject(new Error(__('There was a problem with your upload. Please try again.')));
};
xhr.onabort = () => {
window.store.dispatch(doUpdateUploadRemove(params));
};
if (!isPreview) {
window.store.dispatch(doUpdateUploadAdd(file, params, xhr));
}
xhr.send(body);
});
}

View file

@ -1,100 +0,0 @@
// @flow
import * as tus from 'tus-js-client';
import { X_LBRY_AUTH_TOKEN } from '../../ui/constants/token';
import { doUpdateUploadAdd, doUpdateUploadProgress, doUpdateUploadRemove } from '../../ui/redux/actions/publish';
import { LBRY_WEB_PUBLISH_API_V2 } from 'config';
const RESUMABLE_ENDPOINT = LBRY_WEB_PUBLISH_API_V2;
const RESUMABLE_ENDPOINT_METHOD = 'publish';
const UPLOAD_CHUNK_SIZE_BYTE = 100000000;
export function makeResumableUploadRequest(
token: string,
params: FileUploadSdkParams,
file: File | string,
isPreview?: boolean
) {
return new Promise<any>((resolve, reject) => {
if (!RESUMABLE_ENDPOINT) {
reject(new Error('Publish: endpoint undefined'));
}
// @if NODE_ENV!='production'
if (params.remote_url) {
reject(new Error('Publish: v2 does not support remote_url'));
}
// @endif
const jsonPayload = JSON.stringify({
jsonrpc: '2.0',
method: RESUMABLE_ENDPOINT_METHOD,
params,
id: new Date().getTime(),
});
const uploader = new tus.Upload(file, {
endpoint: RESUMABLE_ENDPOINT,
chunkSize: UPLOAD_CHUNK_SIZE_BYTE,
retryDelays: [0, 3000, 3000],
parallelUploads: 1,
removeFingerprintOnSuccess: true,
headers: { [X_LBRY_AUTH_TOKEN]: token },
metadata: {
filename: file instanceof File ? file.name : file,
filetype: file instanceof File ? file.type : undefined,
},
onShouldRetry: (err, retryAttempt, options) => {
window.store.dispatch(doUpdateUploadProgress({ params, status: 'retry' }));
const FORBIDDEN_ERROR = 403;
const status = err.originalResponse ? err.originalResponse.getStatus() : 0;
return status !== FORBIDDEN_ERROR;
},
onError: (error) => {
window.store.dispatch(doUpdateUploadProgress({ params, status: 'error' }));
reject(new Error(error));
},
onProgress: (bytesUploaded, bytesTotal) => {
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
window.store.dispatch(doUpdateUploadProgress({ params, progress: percentage }));
},
onSuccess: () => {
// Notify lbrynet server
const xhr = new XMLHttpRequest();
xhr.open('POST', `${uploader.url}/notify`);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Tus-Resumable', '1.0.0');
xhr.setRequestHeader(X_LBRY_AUTH_TOKEN, token);
xhr.responseType = 'json';
xhr.onload = () => {
window.store.dispatch(doUpdateUploadRemove(params));
resolve(xhr);
};
xhr.onerror = () => {
reject(new Error(__('There was a problem with your upload. Please try again.')));
};
xhr.onabort = () => {
window.store.dispatch(doUpdateUploadRemove(params));
};
xhr.send(jsonPayload);
},
});
uploader
.findPreviousUploads()
.then((previousUploads) => {
if (previousUploads.length > 0) {
uploader.resumeFromPreviousUpload(previousUploads[0]);
}
if (!isPreview) {
window.store.dispatch(doUpdateUploadAdd(file, params, uploader));
}
uploader.start();
})
.catch((err) => {
reject(new Error(__('Failed to initiate upload (%err%)', { err })));
});
});
}

View file

@ -1,16 +1,22 @@
// @flow
import * as tus from 'tus-js-client';
import { makeUploadRequest } from './publish-v1';
import { makeResumableUploadRequest } from './publish-v2';
/*
https://api.na-backend.odysee.com/api/v1/proxy currently expects publish to consist
of a multipart/form-data POST request with:
- 'file' binary
- 'json_payload' collection of publish params to be passed to the server's sdk.
*/
import { X_LBRY_AUTH_TOKEN } from '../../ui/constants/token';
import { doUpdateUploadProgress } from 'lbryinc';
// A modified version of Lbry.apiCall that allows
// to perform calling methods at arbitrary urls
// and pass form file fields
export default function apiPublishCallViaWeb(
apiCall: (any, any, any, any) => any,
connectionString: string,
token: string,
method: string,
params: FileUploadSdkParams,
params: { file_path: string, preview: boolean, remote_url?: string }, // new param for remoteUrl
resolve: Function,
reject: Function
) {
@ -20,6 +26,7 @@ export default function apiPublishCallViaWeb(
return apiCall(method, params, resolve, reject);
}
const counter = new Date().getTime();
let fileField = filePath;
if (preview) {
@ -30,16 +37,53 @@ export default function apiPublishCallViaWeb(
// Putting a dummy value here, the server is going to process the POSTed file
// and set the file_path itself
const body = new FormData();
if (fileField) {
body.append('file', fileField);
params.file_path = '__POST_FILE__';
delete params['remote_url'];
} else if (remoteUrl) {
body.append('remote_url', remoteUrl);
delete params['remote_url'];
}
const useV1 = remoteUrl || preview || !tus.isSupported;
const jsonPayload = JSON.stringify({
jsonrpc: '2.0',
method,
params,
id: counter,
});
// no fileData? do the livestream remote publish
body.append('json_payload', jsonPayload);
// Note: both function signature (params) should match.
const makeRequest = useV1 ? makeUploadRequest : makeResumableUploadRequest;
function makeRequest(connectionString, method, token, body, params) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open(method, connectionString);
xhr.setRequestHeader(X_LBRY_AUTH_TOKEN, token);
xhr.responseType = 'json';
xhr.upload.onprogress = (e) => {
let percentComplete = Math.ceil((e.loaded / e.total) * 100);
window.store.dispatch(doUpdateUploadProgress(percentComplete, params, xhr));
};
xhr.onload = () => {
window.store.dispatch(doUpdateUploadProgress(undefined, params));
resolve(xhr);
};
xhr.onerror = () => {
window.store.dispatch(doUpdateUploadProgress(undefined, params));
reject(new Error(__('There was a problem with your upload. Please try again.')));
};
return makeRequest(token, params, fileField, preview)
xhr.onabort = () => {
window.store.dispatch(doUpdateUploadProgress(undefined, params));
};
xhr.send(body);
});
}
return makeRequest(connectionString, 'POST', token, body, params)
.then((xhr) => {
let error;
if (xhr && xhr.response) {

View file

@ -3869,11 +3869,6 @@ buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
buffer-from@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0"
integrity sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@ -4639,14 +4634,6 @@ colors@~1.1.2:
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
combine-errors@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/combine-errors/-/combine-errors-3.0.3.tgz#f4df6740083e5703a3181110c2b10551f003da86"
integrity sha1-9N9nQAg+VwOjGBEQwrEFUfAD2oY=
dependencies:
custom-error-instance "2.1.1"
lodash.uniqby "4.5.0"
combined-stream@^1.0.6, combined-stream@~1.0.5, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@ -5350,11 +5337,6 @@ currently-unhandled@^0.4.1:
dependencies:
array-find-index "^1.0.1"
custom-error-instance@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/custom-error-instance/-/custom-error-instance-2.1.1.tgz#3cf6391487a6629a6247eb0ca0ce00081b7e361a"
integrity sha1-PPY5FIemYppiR+sMoM4ACBt+Nho=
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@ -9842,7 +9824,7 @@ jest@20.0.4:
dependencies:
jest-cli "^20.0.4"
js-base64@^2.1.9, js-base64@^2.6.1:
js-base64@^2.1.9:
version "2.6.4"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
@ -10393,48 +10375,11 @@ lodash-es@^4.17.14, lodash-es@^4.2.1:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
lodash._baseiteratee@~4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz#34a9b5543572727c3db2e78edae3c0e9e66bd102"
integrity sha1-NKm1VDVycnw9sueO2uPA6eZr0QI=
dependencies:
lodash._stringtopath "~4.8.0"
lodash._basetostring@~4.12.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz#9327c9dc5158866b7fa4b9d42f4638e5766dd9df"
integrity sha1-kyfJ3FFYhmt/pLnUL0Y45XZt2d8=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
integrity sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=
dependencies:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=
lodash._stringtopath@~4.8.0:
version "4.8.0"
resolved "https://registry.yarnpkg.com/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz#941bcf0e64266e5fc1d66fed0a6959544c576824"
integrity sha1-lBvPDmQmbl/B1m/tCmlZVExXaCQ=
dependencies:
lodash._basetostring "~4.12.0"
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
@ -10504,11 +10449,6 @@ lodash.templatesettings@^4.0.0:
dependencies:
lodash._reinterpolate "^3.0.0"
lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
lodash.toarray@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
@ -10517,14 +10457,6 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
lodash.uniqby@4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz#a3a17bbf62eeb6240f491846e97c1c4e2a5e1e21"
integrity sha1-o6F7v2LutiQPSRhG6XwcTipeHiE=
dependencies:
lodash._baseiteratee "~4.7.0"
lodash._baseuniq "~4.6.0"
lodash.unset@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.unset/-/lodash.unset-4.5.2.tgz#370d1d3e85b72a7e1b0cdf2d272121306f23e4ed"
@ -13048,14 +12980,6 @@ prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0,
object-assign "^4.1.1"
react-is "^16.8.1"
proper-lockfile@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-2.0.1.tgz#159fb06193d32003f4b3691dd2ec1a634aa80d1d"
integrity sha1-FZ+wYZPTIAP0s2kd0uwaY0qoDR0=
dependencies:
graceful-fs "^4.1.2"
retry "^0.10.0"
property-information@^5.3.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
@ -14321,11 +14245,6 @@ ret@~0.1.10:
version "0.1.15"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
retry@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
@ -15977,19 +15896,6 @@ tunnel@^0.0.6:
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
tus-js-client@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tus-js-client/-/tus-js-client-2.3.0.tgz#5d76145476cea46a4e7c045a0054637cddf8dc39"
integrity sha512-I4cSwm6N5qxqCmBqenvutwSHe9ntf81lLrtf6BmLpG2v4wTl89atCQKqGgqvkodE6Lx+iKIjMbaXmfvStTg01g==
dependencies:
buffer-from "^0.1.1"
combine-errors "^3.0.3"
is-stream "^2.0.0"
js-base64 "^2.6.1"
lodash.throttle "^4.1.1"
proper-lockfile "^2.0.1"
url-parse "^1.4.3"
tween-functions@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff"