Move 'currentUploads' to 'publish' reducer

`publish` is currently rehydrated, so we can ride on that and don't need to store the `currentUploads` in `localStorage` for persistence. This would allow us to store Markdown Post data too, as `localStorage` has a 5MB limit per app.

We could have also made `webReducer` rehydrate, but in this repo, there is no need to split it to another reducer. It also makes more sense to be part of publish anyway (at least to me).

This change is mostly moving items between files, with the exception of
1. An additional REHYDRATE in the publish reducer to clean up the tusUploader.
2. Not clearing `currentUploads` in CLEAR_PUBLISH.
This commit is contained in:
infinite-persistence 2021-11-05 08:38:51 +08:00
parent 9d25d82bed
commit 38f511c2fb
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
16 changed files with 117 additions and 148 deletions

View file

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

View file

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

View file

@ -1,30 +0,0 @@
// @flow
import * as ACTIONS from 'constants/action_types';
export function doUpdateUploadAdd(file: File, params: { [key: string]: any }, tusUploader: any) {
return (dispatch: Dispatch, getState: GetState) => {
dispatch({
type: ACTIONS.UPDATE_UPLOAD_ADD,
data: { file, params, tusUploader },
});
};
}
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,81 +0,0 @@
// @flow
import * as ACTIONS from 'constants/action_types';
import { serializeFileObj } from 'util/file';
const CURRENT_UPLOADS = 'current_uploads';
const getKeyFromParam = (params) => `${params.name}#${params.channel || 'anonymous'}`;
export type TvState = {
currentUploads: { [key: string]: FileUploadItem },
};
const reducers = {};
const defaultState: TvState = {
currentUploads: {},
};
try {
const uploads = JSON.parse(window.localStorage.getItem(CURRENT_UPLOADS));
if (uploads) {
defaultState.currentUploads = uploads;
Object.keys(defaultState.currentUploads).forEach((key) => {
delete defaultState.currentUploads[key].tusUploader;
});
}
} catch (e) {
console.log(e);
}
reducers[ACTIONS.UPDATE_UPLOAD_ADD] = (state: TvState, action) => {
const { file, params, tusUploader } = action.data;
const key = getKeyFromParam(params);
const currentUploads = Object.assign({}, state.currentUploads);
currentUploads[key] = {
file,
fileFingerprint: serializeFileObj(file), // TODO: get hash instead?
progress: '0',
params,
tusUploader,
};
window.localStorage.setItem(CURRENT_UPLOADS, JSON.stringify(currentUploads));
return { ...state, currentUploads };
};
reducers[ACTIONS.UPDATE_UPLOAD_PROGRESS] = (state: TvState, action) => {
const { params, progress, status } = action.data;
const key = getKeyFromParam(params);
const currentUploads = Object.assign({}, state.currentUploads);
if (progress) {
currentUploads[key].progress = progress;
delete currentUploads[key].status;
} else if (status) {
currentUploads[key].status = status;
if (status === 'error') {
delete currentUploads[key].tusUploader;
}
}
window.localStorage.setItem(CURRENT_UPLOADS, JSON.stringify(currentUploads));
return { ...state, currentUploads };
};
reducers[ACTIONS.UPDATE_UPLOAD_REMOVE] = (state: TvState, action) => {
const { params } = action.data;
const key = getKeyFromParam(params);
const currentUploads = Object.assign({}, state.currentUploads);
delete currentUploads[key];
window.localStorage.setItem(CURRENT_UPLOADS, JSON.stringify(currentUploads));
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

@ -1,10 +0,0 @@
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
);

9
flow-typed/File.js vendored
View file

@ -76,12 +76,3 @@ declare type DeletePurchasedUri = {
uri: string, uri: string,
}, },
}; };
declare type FileUploadItem = {
params: UpdatePublishFormData,
file: File,
fileFingerprint: string,
progress: string,
status?: string,
tusUploader?: any,
};

View file

@ -52,3 +52,12 @@ declare type PublishParams = {
nsfw: boolean, nsfw: boolean,
tags: Array<Tag>, tags: Array<Tag>,
}; };
declare type FileUploadItem = {
params: UpdatePublishFormData,
file: File,
fileFingerprint: string,
progress: string,
status?: string,
tusUploader?: any,
};

View file

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

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectCurrentUploads, selectUploadCount, doUpdateUploadRemove } from 'lbryinc';
import { doOpenModal } from 'redux/actions/app'; import { doOpenModal } from 'redux/actions/app';
import { doPublishResume } from 'redux/actions/publish'; import { doPublishResume, doUpdateUploadRemove } from 'redux/actions/publish';
import { selectCurrentUploads, selectUploadCount } from 'redux/selectors/publish';
import WebUploadList from './view'; import WebUploadList from './view';
const select = (state) => ({ const select = (state) => ({

View file

@ -331,6 +331,9 @@ export const PUBLISH_FAIL = 'PUBLISH_FAIL';
export const CLEAR_PUBLISH_ERROR = 'CLEAR_PUBLISH_ERROR'; export const CLEAR_PUBLISH_ERROR = 'CLEAR_PUBLISH_ERROR';
export const REMOVE_PENDING_PUBLISH = 'REMOVE_PENDING_PUBLISH'; export const REMOVE_PENDING_PUBLISH = 'REMOVE_PENDING_PUBLISH';
export const DO_PREPARE_EDIT = 'DO_PREPARE_EDIT'; 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 // Media
export const MEDIA_PLAY = 'MEDIA_PLAY'; export const MEDIA_PLAY = 'MEDIA_PLAY';
@ -491,11 +494,6 @@ 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_FAILED = 'FETCH_SUB_COUNT_FAILED';
export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED'; export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED';
// Lbry.tv
export const UPDATE_UPLOAD_ADD = 'UPDATE_UPLOAD_ADD';
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
export const UPDATE_UPLOAD_REMOVE = 'UPDATE_UPLOAD_REMOVE';
// User // User
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE'; export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED'; export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';

View file

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

View file

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

View file

@ -688,3 +688,31 @@ export const doCheckReflectingFiles = () => (dispatch: Dispatch, getState: GetSt
}, 5000); }, 5000);
} }
}; };
export function doUpdateUploadAdd(file: File | string, params: { [key: string]: any }, tusUploader: any) {
return (dispatch: Dispatch, getState: GetState) => {
dispatch({
type: ACTIONS.UPDATE_UPLOAD_ADD,
data: { file, params, tusUploader },
});
};
}
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,10 +1,13 @@
// @flow // @flow
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 * 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';
const getKeyFromParam = (params) => `${params.name}#${params.channel || 'anonymous'}`;
type PublishState = { type PublishState = {
editingURI: ?string, editingURI: ?string,
fileText: ?string, fileText: ?string,
@ -38,6 +41,7 @@ type PublishState = {
tags: Array<string>, tags: Array<string>,
optimize: boolean, optimize: boolean,
useLBRYUploader: boolean, useLBRYUploader: boolean,
currentUploads: { [key: string]: FileUploadItem },
}; };
const defaultState: PublishState = { const defaultState: PublishState = {
@ -78,6 +82,7 @@ const defaultState: PublishState = {
publishError: undefined, publishError: undefined,
optimize: false, optimize: false,
useLBRYUploader: false, useLBRYUploader: false,
currentUploads: {},
}; };
export const publishReducer = handleActions( export const publishReducer = handleActions(
@ -96,6 +101,7 @@ export const publishReducer = handleActions(
bid: state.bid, bid: state.bid,
optimize: state.optimize, optimize: state.optimize,
language: state.language, language: state.language,
currentUploads: state.currentUploads,
}), }),
[ACTIONS.PUBLISH_START]: (state: PublishState): PublishState => ({ [ACTIONS.PUBLISH_START]: (state: PublishState): PublishState => ({
...state, ...state,
@ -127,8 +133,66 @@ export const publishReducer = handleActions(
...publishData, ...publishData,
editingURI: uri, editingURI: uri,
uri: shortUri, uri: shortUri,
currentUploads: state.currentUploads,
}; };
}, },
[ACTIONS.UPDATE_UPLOAD_ADD]: (state: PublishState, action) => {
const { file, params, tusUploader } = action.data;
const key = getKeyFromParam(params);
const currentUploads = Object.assign({}, state.currentUploads);
currentUploads[key] = {
file,
fileFingerprint: serializeFileObj(file), // TODO: get hash instead?
progress: '0',
params,
tusUploader,
};
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 (progress) {
currentUploads[key].progress = progress;
delete currentUploads[key].status;
} else if (status) {
currentUploads[key].status = status;
if (status === 'error') {
delete currentUploads[key].tusUploader;
}
}
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) => {
delete newPublish.currentUploads[key].tusUploader;
});
}
return newPublish;
}
return state;
},
}, },
defaultState defaultState
); );

View file

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

View file

@ -9,7 +9,7 @@
*/ */
import * as tus from 'tus-js-client'; import * as tus from 'tus-js-client';
import { X_LBRY_AUTH_TOKEN } from '../../ui/constants/token'; import { X_LBRY_AUTH_TOKEN } from '../../ui/constants/token';
import { doUpdateUploadAdd, doUpdateUploadProgress, doUpdateUploadRemove } from 'lbryinc'; import { doUpdateUploadAdd, doUpdateUploadProgress, doUpdateUploadRemove } from '../../ui/redux/actions/publish';
const UPLOAD_CHUNK_SIZE_BYTE = 100000000; const UPLOAD_CHUNK_SIZE_BYTE = 100000000;