From 3be6fa52ac1cb6224cf9de45623183e62c2b24ab Mon Sep 17 00:00:00 2001 From: jessop Date: Thu, 7 May 2020 07:41:56 -0400 Subject: [PATCH] track reflecting files --- dist/bundle.es.js | 133 +++++++++++++++++++++++++++++++++- dist/flow-typed/File.js | 3 + dist/flow-typed/Reflector.js | 5 ++ flow-typed/File.js | 3 + flow-typed/Reflector.js | 5 ++ src/constants/action_types.js | 4 + src/index.js | 3 + src/redux/actions/publish.js | 76 ++++++++++++++++++- src/redux/reducers/claims.js | 48 +++++++++++- src/redux/selectors/claims.js | 18 ++++- 10 files changed, 289 insertions(+), 9 deletions(-) create mode 100644 dist/flow-typed/Reflector.js create mode 100644 flow-typed/Reflector.js diff --git a/dist/bundle.es.js b/dist/bundle.es.js index 383ac2e..35b2ef8 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -145,6 +145,10 @@ const CHECK_PUBLISH_NAME_STARTED = 'CHECK_PUBLISH_NAME_STARTED'; const CHECK_PUBLISH_NAME_COMPLETED = 'CHECK_PUBLISH_NAME_COMPLETED'; const UPDATE_PENDING_CLAIMS = 'UPDATE_PENDING_CLAIMS'; const UPDATE_CONFIRMED_CLAIMS = 'UPDATE_CONFIRMED_CLAIMS'; +const ADD_FILES_REFLECTING = 'ADD_FILES_REFLECTING'; +const UPDATE_FILES_REFLECTING = 'UPDATE_FILES_REFLECTING'; +const TOGGLE_CHECKING_REFLECTING = 'TOGGLE_CHECKING_REFLECTING'; +const TOGGLE_CHECKING_PENDING = 'TOGGLE_CHECKING_PENDING'; // Comments const COMMENT_LIST_STARTED = 'COMMENT_LIST_STARTED'; @@ -421,6 +425,10 @@ var action_types = /*#__PURE__*/Object.freeze({ CHECK_PUBLISH_NAME_COMPLETED: CHECK_PUBLISH_NAME_COMPLETED, UPDATE_PENDING_CLAIMS: UPDATE_PENDING_CLAIMS, UPDATE_CONFIRMED_CLAIMS: UPDATE_CONFIRMED_CLAIMS, + ADD_FILES_REFLECTING: ADD_FILES_REFLECTING, + UPDATE_FILES_REFLECTING: UPDATE_FILES_REFLECTING, + TOGGLE_CHECKING_REFLECTING: TOGGLE_CHECKING_REFLECTING, + TOGGLE_CHECKING_PENDING: TOGGLE_CHECKING_PENDING, COMMENT_LIST_STARTED: COMMENT_LIST_STARTED, COMMENT_LIST_COMPLETED: COMMENT_LIST_COMPLETED, COMMENT_LIST_FAILED: COMMENT_LIST_FAILED, @@ -2184,6 +2192,7 @@ const makeSelectPendingByUri = uri => reselect.createSelector(selectPendingById, const claimId = isChannel ? channelClaimId : streamClaimId; return pendingById[claimId]; }); +const selectReflectingById = reselect.createSelector(selectState$2, state => state.reflectingById); const makeSelectClaimForUri = (uri, returnRepost = true) => reselect.createSelector(selectClaimsByUri, selectPendingById, (byUri, pendingById) => { // Check if a claim is pending first @@ -2546,6 +2555,11 @@ const selectUpdatingChannel = reselect.createSelector(selectState$2, state => st const selectUpdateChannelError = reselect.createSelector(selectState$2, state => state.updateChannelError); +const makeSelectReflectingClaimForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), selectReflectingById, (claim, reflectingById) => { + const claimId = claim && claim.claimId; + return reflectingById[claimId]; +}); + const makeSelectMyStreamUrlsForPage = (page = 1) => reselect.createSelector(selectMyClaimUrisWithoutChannels, urls => { const start = (Number(page) - 1) * Number(PAGE_SIZE); const end = Number(page) * Number(PAGE_SIZE); @@ -4114,6 +4128,8 @@ const selectTakeOverAmount = reselect.createSelector(selectState$5, selectMyClai var _extends$7 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + const doResetThumbnailStatus = () => dispatch => { dispatch({ type: UPDATE_PUBLISH_FORM, @@ -4400,7 +4416,82 @@ const doPublish = (success, fail) => (dispatch, getState) => { return lbryProxy.publish(publishPayload).then(success, fail); }; -// Calls claim_list_mine until any pending publishes are confirmed +// Calls file_list until any reflecting files are done +const doCheckReflectingFiles = () => (dispatch, getState) => { + const state = getState(); + const { checkingReflector } = state.claims; + let reflectorCheckInterval; + + const checkFileList = (() => { + var _ref = _asyncToGenerator(function* () { + const state = getState(); + const reflectingById = selectReflectingById(state); + const ids = Object.keys(reflectingById); + + const newReflectingById = {}; + const promises = []; + // TODO: just use file_list({claim_id: Array}) + if (Object.keys(reflectingById).length) { + ids.forEach(function (claimId) { + promises.push(lbryProxy.file_list({ claim_id: claimId })); + }); + + Promise.all(promises).then(function (results) { + results.forEach(function (res) { + const fileListItem = res.items[0]; + const fileClaimId = fileListItem.claim_id; + const { + is_fully_reflected: done, + uploading_to_reflector: uploading, + reflector_progress: progress + } = fileListItem; + if (uploading) { + newReflectingById[fileClaimId] = { + fileListItem: fileListItem, + progress, + stalled: !done && !uploading + }; + } + }); + }).then(function () { + dispatch({ + type: UPDATE_FILES_REFLECTING, + data: newReflectingById + }); + if (!Object.keys(newReflectingById).length) { + dispatch({ + type: TOGGLE_CHECKING_REFLECTING, + data: false + }); + clearInterval(reflectorCheckInterval); + } + }); + } else { + dispatch({ + type: TOGGLE_CHECKING_REFLECTING, + data: false + }); + clearInterval(reflectorCheckInterval); + } + }); + + return function checkFileList() { + return _ref.apply(this, arguments); + }; + })(); + // do it once... + checkFileList(); + // then start the interval if it's not already started + if (!checkingReflector) { + dispatch({ + type: TOGGLE_CHECKING_REFLECTING, + data: true + }); + reflectorCheckInterval = setInterval(() => { + checkFileList(); + }, 5000); + } +}; const doCheckPendingPublishes = onConfirmed => (dispatch, getState) => { let publishCheckInterval; @@ -4940,6 +5031,7 @@ const defaultState = { fetchingMyChannels: false, abandoningById: {}, pendingById: {}, + reflectingById: {}, claimSearchError: false, claimSearchByQuery: {}, claimSearchByQueryLastPageReached: {}, @@ -4956,7 +5048,9 @@ const defaultState = { myClaimsPageNumber: undefined, myClaimsPageTotalResults: undefined, isFetchingClaimListMine: false, - isCheckingNameForPublish: false + isCheckingNameForPublish: false, + checkingPending: false, + checkingReflecting: false }; function handleClaimAction(state, action) { @@ -5438,6 +5532,38 @@ reducers[CLEAR_REPOST_ERROR] = state => { repostError: null }); }; +reducers[ADD_FILES_REFLECTING] = (state, action) => { + const pendingClaim = action.data; + const { reflectingById } = state; + const claimId = pendingClaim && pendingClaim.claim_id; + + reflectingById[claimId] = { fileListItem: pendingClaim, progress: 0, stalled: false }; + + return Object.assign({}, state, _extends$9({}, state, { + reflectingById: reflectingById + })); +}; +reducers[UPDATE_FILES_REFLECTING] = (state, action) => { + const newReflectingById = action.data; + + return Object.assign({}, state, _extends$9({}, state, { + reflectingById: newReflectingById + })); +}; +reducers[TOGGLE_CHECKING_REFLECTING] = (state, action) => { + const checkingReflecting = action.data; + + return Object.assign({}, state, _extends$9({}, state, { + checkingReflecting + })); +}; +reducers[TOGGLE_CHECKING_PENDING] = (state, action) => { + const checking = action.data; + + return Object.assign({}, state, _extends$9({}, state, { + checkingPending: checking + })); +}; function claimsReducer(state = defaultState, action) { const handler = reducers[action.type]; @@ -6715,6 +6841,7 @@ exports.doBlurSearchInput = doBlurSearchInput; exports.doCheckAddressIsMine = doCheckAddressIsMine; exports.doCheckPendingPublishes = doCheckPendingPublishes; exports.doCheckPublishNameAvailability = doCheckPublishNameAvailability; +exports.doCheckReflectingFiles = doCheckReflectingFiles; exports.doClaimSearch = doClaimSearch; exports.doClearPublish = doClearPublish; exports.doClearRepostError = doClearRepostError; @@ -6824,6 +6951,7 @@ exports.makeSelectPermanentUrlForUri = makeSelectPermanentUrlForUri; exports.makeSelectPublishFormValue = makeSelectPublishFormValue; exports.makeSelectQueryWithOptions = makeSelectQueryWithOptions; exports.makeSelectRecommendedContentForUri = makeSelectRecommendedContentForUri; +exports.makeSelectReflectingClaimForUri = makeSelectReflectingClaimForUri; exports.makeSelectResolvedRecommendedContentForUri = makeSelectResolvedRecommendedContentForUri; exports.makeSelectResolvedSearchResults = makeSelectResolvedSearchResults; exports.makeSelectResolvedSearchResultsLastPageReached = makeSelectResolvedSearchResultsLastPageReached; @@ -6928,6 +7056,7 @@ exports.selectPurchaseUriErrorMessage = selectPurchaseUriErrorMessage; exports.selectPurchasedUris = selectPurchasedUris; exports.selectReceiveAddress = selectReceiveAddress; exports.selectRecentTransactions = selectRecentTransactions; +exports.selectReflectingById = selectReflectingById; exports.selectRepostError = selectRepostError; exports.selectRepostLoading = selectRepostLoading; exports.selectReservedBalance = selectReservedBalance; diff --git a/dist/flow-typed/File.js b/dist/flow-typed/File.js index 71a2523..75a3965 100644 --- a/dist/flow-typed/File.js +++ b/dist/flow-typed/File.js @@ -20,6 +20,7 @@ declare type FileListItem = { outpoint: string, points_paid: number, protobuf: string, + reflector_progress: number, sd_hash: string, status: string, stopped: false, @@ -29,10 +30,12 @@ declare type FileListItem = { suggested_file_name: string, total_bytes: number, total_bytes_lower_bound: number, + is_fully_reflected: boolean, // TODO: sdk plans to change `tx` // It isn't currently used by the apps tx: {}, txid: string, + uploading_to_reflector: boolean, written_bytes: number, }; diff --git a/dist/flow-typed/Reflector.js b/dist/flow-typed/Reflector.js new file mode 100644 index 0000000..fc6be97 --- /dev/null +++ b/dist/flow-typed/Reflector.js @@ -0,0 +1,5 @@ +declare type ReflectingUpdate = { + fileListItem: FileListItem, + progress: number | boolean, + stalled: boolean, +}; diff --git a/flow-typed/File.js b/flow-typed/File.js index 71a2523..75a3965 100644 --- a/flow-typed/File.js +++ b/flow-typed/File.js @@ -20,6 +20,7 @@ declare type FileListItem = { outpoint: string, points_paid: number, protobuf: string, + reflector_progress: number, sd_hash: string, status: string, stopped: false, @@ -29,10 +30,12 @@ declare type FileListItem = { suggested_file_name: string, total_bytes: number, total_bytes_lower_bound: number, + is_fully_reflected: boolean, // TODO: sdk plans to change `tx` // It isn't currently used by the apps tx: {}, txid: string, + uploading_to_reflector: boolean, written_bytes: number, }; diff --git a/flow-typed/Reflector.js b/flow-typed/Reflector.js new file mode 100644 index 0000000..fc6be97 --- /dev/null +++ b/flow-typed/Reflector.js @@ -0,0 +1,5 @@ +declare type ReflectingUpdate = { + fileListItem: FileListItem, + progress: number | boolean, + stalled: boolean, +}; diff --git a/src/constants/action_types.js b/src/constants/action_types.js index deed387..cbaad87 100644 --- a/src/constants/action_types.js +++ b/src/constants/action_types.js @@ -122,6 +122,10 @@ export const CHECK_PUBLISH_NAME_STARTED = 'CHECK_PUBLISH_NAME_STARTED'; export const CHECK_PUBLISH_NAME_COMPLETED = 'CHECK_PUBLISH_NAME_COMPLETED'; export const UPDATE_PENDING_CLAIMS = 'UPDATE_PENDING_CLAIMS'; export const UPDATE_CONFIRMED_CLAIMS = 'UPDATE_CONFIRMED_CLAIMS'; +export const ADD_FILES_REFLECTING = 'ADD_FILES_REFLECTING'; +export const UPDATE_FILES_REFLECTING = 'UPDATE_FILES_REFLECTING'; +export const TOGGLE_CHECKING_REFLECTING = 'TOGGLE_CHECKING_REFLECTING'; +export const TOGGLE_CHECKING_PENDING = 'TOGGLE_CHECKING_PENDING'; // Comments export const COMMENT_LIST_STARTED = 'COMMENT_LIST_STARTED'; diff --git a/src/index.js b/src/index.js index 1e1b615..a661d00 100644 --- a/src/index.js +++ b/src/index.js @@ -94,6 +94,7 @@ export { doPrepareEdit, doPublish, doCheckPendingPublishes, + doCheckReflectingFiles, } from 'redux/actions/publish'; export { @@ -206,12 +207,14 @@ export { makeSelectChannelForClaimUri, makeSelectClaimIsPending, makeSelectPendingByUri, + makeSelectReflectingClaimForUri, makeSelectClaimsInChannelForCurrentPageState, makeSelectShortUrlForUri, makeSelectCanonicalUrlForUri, makeSelectPermanentUrlForUri, makeSelectSupportsForUri, selectPendingById, + selectReflectingById, selectClaimsById, selectClaimsByUri, selectAllClaimsByChannel, diff --git a/src/redux/actions/publish.js b/src/redux/actions/publish.js index c5a0ec0..23d6d74 100644 --- a/src/redux/actions/publish.js +++ b/src/redux/actions/publish.js @@ -12,6 +12,7 @@ import { selectMyChannelClaims, selectPendingById, selectMyClaimsWithoutChannels, + selectReflectingById, } from 'redux/selectors/claims'; import { selectPublishFormValues, selectMyClaimForUri } from 'redux/selectors/publish'; @@ -354,7 +355,78 @@ export const doPublish = (success: Function, fail: Function) => ( return Lbry.publish(publishPayload).then(success, fail); }; -// Calls claim_list_mine until any pending publishes are confirmed +// Calls file_list until any reflecting files are done +export const doCheckReflectingFiles = () => (dispatch: Dispatch, getState: GetState) => { + const state = getState(); + const { checkingReflector } = state.claims; + let reflectorCheckInterval; + + const checkFileList = async() => { + const state = getState(); + const reflectingById = selectReflectingById(state); + const ids = Object.keys(reflectingById); + + const newReflectingById = {}; + const promises = []; + // TODO: just use file_list({claim_id: Array}) + if (Object.keys(reflectingById).length) { + ids.forEach(claimId => { + promises.push(Lbry.file_list({ claim_id: claimId })); + }); + + Promise.all(promises) + .then(results => { + results.forEach(res => { + const fileListItem = res.items[0]; + const fileClaimId = fileListItem.claim_id; + const { + is_fully_reflected: done, + uploading_to_reflector: uploading, + reflector_progress: progress, + } = fileListItem; + if (uploading) { + newReflectingById[fileClaimId] = { + fileListItem: fileListItem, + progress, + stalled: !done && !uploading, + }; + } + }); + }) + .then(() => { + dispatch({ + type: ACTIONS.UPDATE_FILES_REFLECTING, + data: newReflectingById, + }); + if (!Object.keys(newReflectingById).length) { + dispatch({ + type: ACTIONS.TOGGLE_CHECKING_REFLECTING, + data: false, + }); + clearInterval(reflectorCheckInterval); + } + }); + } else { + dispatch({ + type: ACTIONS.TOGGLE_CHECKING_REFLECTING, + data: false, + }); + clearInterval(reflectorCheckInterval); + } + }; + // do it once... + checkFileList(); + // then start the interval if it's not already started + if (!checkingReflector) { + dispatch({ + type: ACTIONS.TOGGLE_CHECKING_REFLECTING, + data: true, + }); + reflectorCheckInterval = setInterval(() => { + checkFileList(); + }, 5000); + } +}; export const doCheckPendingPublishes = (onConfirmed: Function) => ( dispatch: Dispatch, getState: GetState @@ -387,7 +459,7 @@ export const doCheckPendingPublishes = (onConfirmed: Function) => ( } return Object.keys(pendingById).length; }) - .then((len) => { + .then(len => { if (!len) { clearInterval(publishCheckInterval); } diff --git a/src/redux/reducers/claims.js b/src/redux/reducers/claims.js index 8dbdd28..ee9e85c 100644 --- a/src/redux/reducers/claims.js +++ b/src/redux/reducers/claims.js @@ -18,6 +18,7 @@ type State = { byId: { [string]: Claim }, resolvingUris: Array, pendingById: { [string]: Claim }, + reflectingById: { [string]: ReflectingUpdate }, myClaims: ?Array, myChannelClaims: ?Array, abandoningById: { [string]: boolean }, @@ -46,6 +47,8 @@ type State = { myClaimsPageTotalResults: ?number, isFetchingClaimListMine: boolean, isCheckingNameForPublish: boolean, + checkingPending: boolean, + checkingReflecting: boolean, }; const reducers = {}; @@ -63,6 +66,7 @@ const defaultState = { fetchingMyChannels: false, abandoningById: {}, pendingById: {}, + reflectingById: {}, claimSearchError: false, claimSearchByQuery: {}, claimSearchByQueryLastPageReached: {}, @@ -80,6 +84,8 @@ const defaultState = { myClaimsPageTotalResults: undefined, isFetchingClaimListMine: false, isCheckingNameForPublish: false, + checkingPending: false, + checkingReflecting: false, }; function handleClaimAction(state: State, action: any): State { @@ -362,7 +368,7 @@ reducers[ACTIONS.ABANDON_CLAIM_STARTED] = (state: State, action: any): State => }; reducers[ACTIONS.UPDATE_PENDING_CLAIMS] = (state: State, action: any): State => { - const { claims }: { claims: Array } = action.data; + const { claims }: { claims: Array } = action.data; const byId = Object.assign({}, state.byId); const byUri = Object.assign({}, state.claimsByUri); const pendingById: { [string]: Claim } = Object.assign({}, state.pendingById); @@ -387,13 +393,13 @@ reducers[ACTIONS.UPDATE_PENDING_CLAIMS] = (state: State, action: any): State => }; reducers[ACTIONS.UPDATE_CONFIRMED_CLAIMS] = (state: State, action: any): State => { - const { claims }: { claims: Array } = action.data; + const { claims }: { claims: Array } = action.data; const byId = Object.assign({}, state.byId); const byUri = Object.assign({}, state.claimsByUri); const pendingById: { [string]: Claim } = Object.assign({}, state.pendingById); let myClaimIds = new Set(state.myClaims); - claims.forEach((claim: Claim) => { + claims.forEach((claim: GenericClaim) => { const uri = buildURI({ streamName: claim.name, streamClaimId: claim.claim_id }); const { claim_id: claimId } = claim; if (claim.type && claim.type.match(/claim|update/)) { @@ -594,6 +600,42 @@ reducers[ACTIONS.CLEAR_REPOST_ERROR] = (state: State): State => { repostError: null, }; }; +reducers[ACTIONS.ADD_FILES_REFLECTING] = (state: State, action): State => { + const pendingClaim = action.data; + const { reflectingById } = state; + const claimId = pendingClaim && pendingClaim.claim_id; + + reflectingById[claimId] = { fileListItem: pendingClaim, progress: 0, stalled: false }; + + return Object.assign({}, state, { + ...state, + reflectingById: reflectingById, + }); +}; +reducers[ACTIONS.UPDATE_FILES_REFLECTING] = (state: State, action): State => { + const newReflectingById = action.data; + + return Object.assign({}, state, { + ...state, + reflectingById: newReflectingById, + }); +}; +reducers[ACTIONS.TOGGLE_CHECKING_REFLECTING] = (state: State, action): State => { + const checkingReflecting = action.data; + + return Object.assign({}, state, { + ...state, + checkingReflecting, + }); +}; +reducers[ACTIONS.TOGGLE_CHECKING_PENDING] = (state: State, action): State => { + const checking = action.data; + + return Object.assign({}, state, { + ...state, + checkingPending: checking, + }); +}; export function claimsReducer(state: State = defaultState, action: any) { const handler = reducers[action.type]; diff --git a/src/redux/selectors/claims.js b/src/redux/selectors/claims.js index a04987c..78b5a71 100644 --- a/src/redux/selectors/claims.js +++ b/src/redux/selectors/claims.js @@ -111,6 +111,10 @@ export const makeSelectPendingByUri = (uri: string) => return pendingById[claimId]; } ); +export const selectReflectingById = createSelector( + selectState, + state => state.reflectingById +); export const makeSelectClaimForUri = (uri: string, returnRepost: boolean = true) => createSelector( @@ -314,8 +318,8 @@ export const makeSelectDateForUri = (uri: string) => (claim.value.release_time ? claim.value.release_time * 1000 : claim.meta && claim.meta.creation_timestamp - ? claim.meta.creation_timestamp * 1000 - : null); + ? claim.meta.creation_timestamp * 1000 + : null); if (!timestamp) { return undefined; } @@ -717,6 +721,16 @@ export const selectUpdateChannelError = createSelector( state => state.updateChannelError ); +export const makeSelectReflectingClaimForUri = (uri: string) => + createSelector( + makeSelectClaimForUri(uri), + selectReflectingById, + (claim, reflectingById) => { + const claimId = claim && claim.claimId; + return reflectingById[claimId]; + } + ); + export const makeSelectMyStreamUrlsForPage = (page: number = 1) => createSelector( selectMyClaimUrisWithoutChannels,