Merge branch 'multi-resolve'

This commit is contained in:
Jeremy Kauffman 2017-10-10 09:05:00 -04:00
commit d6b86c0f07
13 changed files with 81 additions and 139 deletions

View file

@ -16,6 +16,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
* There is no longer a minimum channel length (#645) * There is no longer a minimum channel length (#645)
* Changed the File page to make it clearer how to to open the folder for a file * Changed the File page to make it clearer how to to open the folder for a file
* The upgrade message is now friendlier and includes a link to the release notes. * The upgrade message is now friendlier and includes a link to the release notes.
* Improved Discover page load time by batching all URIs into one API call
### Fixed ### Fixed
* Improve layout (and implementation) of the icon panel in file tiles and cards * Improve layout (and implementation) of the icon panel in file tiles and cards

View file

@ -24,68 +24,46 @@ const { ipcRenderer } = require("electron");
const DOWNLOAD_POLL_INTERVAL = 250; const DOWNLOAD_POLL_INTERVAL = 250;
export function doResolveUri(uri) { export function doResolveUris(uris) {
return function(dispatch, getState) { return function(dispatch, getState) {
uri = lbryuri.normalize(uri); uris = uris.map(lbryuri.normalize);
const state = getState(); const state = getState();
const alreadyResolving = selectResolvingUris(state).indexOf(uri) !== -1;
if (!alreadyResolving) { // Filter out URIs that are already resolving
dispatch({
type: types.RESOLVE_URI_STARTED,
data: { uri },
});
lbry.resolve({ uri }).then(resolutionInfo => {
const { claim, certificate } = resolutionInfo
? resolutionInfo
: { claim: null, certificate: null };
dispatch({
type: types.RESOLVE_URI_COMPLETED,
data: {
uri,
claim,
certificate,
},
});
});
}
};
}
export function doCancelResolveUri(uri) {
return function(dispatch, getState) {
uri = lbryuri.normalize(uri);
const state = getState();
const alreadyResolving = selectResolvingUris(state).indexOf(uri) !== -1;
if (alreadyResolving) {
lbry.cancelResolve({ uri });
dispatch({
type: types.RESOLVE_URI_CANCELED,
data: {
uri,
},
});
}
};
}
export function doCancelAllResolvingUris() {
return function(dispatch, getState) {
const state = getState();
const resolvingUris = selectResolvingUris(state); const resolvingUris = selectResolvingUris(state);
const actions = []; const urisToResolve = uris.filter(uri => !resolvingUris.includes(uri));
resolvingUris.forEach(uri => actions.push(doCancelResolveUri(uri))); if (urisToResolve.length === 0) {
return;
}
dispatch(batchActions(...actions)); dispatch({
type: types.RESOLVE_URIS_STARTED,
data: { uris },
});
let resolveInfo = {};
lbry.resolve({ uris: urisToResolve }).then(result => {
for (let [uri, uriResolveInfo] of Object.entries(result)) {
const { claim, certificate } = uriResolveInfo || {
claim: null,
certificate: null,
};
resolveInfo[uri] = { claim, certificate };
}
dispatch({
type: types.RESOLVE_URIS_COMPLETED,
data: { resolveInfo },
});
});
}; };
} }
export function doResolveUri(uri) {
return doResolveUris([uri]);
}
export function doFetchFeaturedUris() { export function doFetchFeaturedUris() {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(); const state = getState();
@ -96,28 +74,27 @@ export function doFetchFeaturedUris() {
const success = ({ Categories, Uris }) => { const success = ({ Categories, Uris }) => {
let featuredUris = {}; let featuredUris = {};
const actions = []; let urisToResolve = [];
Categories.forEach(category => { Categories.forEach(category => {
if (Uris[category] && Uris[category].length) { if (Uris[category] && Uris[category].length) {
const uris = Uris[category]; const uris = Uris[category];
featuredUris[category] = uris; featuredUris[category] = uris;
uris.forEach(uri => { urisToResolve = [...urisToResolve, ...uris];
actions.push(doResolveUri(uri));
});
} }
}); });
actions.push({ const actions = [
type: types.FETCH_FEATURED_CONTENT_COMPLETED, doResolveUris(urisToResolve),
data: { {
categories: Categories, type: types.FETCH_FEATURED_CONTENT_COMPLETED,
uris: featuredUris, data: {
success: true, categories: Categories,
uris: featuredUris,
success: true,
},
}, },
}); ];
dispatch(batchActions(...actions)); dispatch(batchActions(...actions));
}; };

View file

@ -47,9 +47,8 @@ export const SUPPORT_TRANSACTION_FAILED = "SUPPORT_TRANSACTION_FAILED";
export const FETCH_FEATURED_CONTENT_STARTED = "FETCH_FEATURED_CONTENT_STARTED"; export const FETCH_FEATURED_CONTENT_STARTED = "FETCH_FEATURED_CONTENT_STARTED";
export const FETCH_FEATURED_CONTENT_COMPLETED = export const FETCH_FEATURED_CONTENT_COMPLETED =
"FETCH_FEATURED_CONTENT_COMPLETED"; "FETCH_FEATURED_CONTENT_COMPLETED";
export const RESOLVE_URI_STARTED = "RESOLVE_URI_STARTED"; export const RESOLVE_URIS_STARTED = "RESOLVE_URIS_STARTED";
export const RESOLVE_URI_COMPLETED = "RESOLVE_URI_COMPLETED"; export const RESOLVE_URIS_COMPLETED = "RESOLVE_URIS_COMPLETED";
export const RESOLVE_URI_CANCELED = "RESOLVE_URI_CANCELED";
export const FETCH_CHANNEL_CLAIMS_STARTED = "FETCH_CHANNEL_CLAIMS_STARTED"; export const FETCH_CHANNEL_CLAIMS_STARTED = "FETCH_CHANNEL_CLAIMS_STARTED";
export const FETCH_CHANNEL_CLAIMS_COMPLETED = "FETCH_CHANNEL_CLAIMS_COMPLETED"; export const FETCH_CHANNEL_CLAIMS_COMPLETED = "FETCH_CHANNEL_CLAIMS_COMPLETED";
export const FETCH_CHANNEL_CLAIM_COUNT_STARTED = export const FETCH_CHANNEL_CLAIM_COUNT_STARTED =

View file

@ -311,30 +311,24 @@ lbry.claim_list_mine = function(params = {}) {
}); });
}; };
lbry._resolveXhrs = {};
lbry.resolve = function(params = {}) { lbry.resolve = function(params = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!params.uri) { apiCall(
throw __("Resolve has hacked cache on top of it that requires a URI");
}
lbry._resolveXhrs[params.uri] = apiCall(
"resolve", "resolve",
params, params,
function(data) { function(data) {
resolve(data && data[params.uri] ? data[params.uri] : {}); if ("uri" in params) {
// If only a single URI was requested, don't nest the results in an object
resolve(data && data[params.uri] ? data[params.uri] : {});
} else {
resolve(data || {});
}
}, },
reject reject
); );
}); });
}; };
lbry.cancelResolve = function(params = {}) {
const xhr = lbry._resolveXhrs[params.uri];
if (xhr && xhr.readyState > 0 && xhr.readyState < 4) {
xhr.abort();
}
};
lbry = new Proxy(lbry, { lbry = new Proxy(lbry, {
get: function(target, name) { get: function(target, name) {
if (name in target) { if (name in target) {

View file

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { doFetchFeaturedUris, doCancelAllResolvingUris } from "actions/content"; import { doFetchFeaturedUris } from "actions/content";
import { import {
selectFeaturedUris, selectFeaturedUris,
selectFetchingFeaturedUris, selectFetchingFeaturedUris,
@ -14,7 +14,6 @@ const select = state => ({
const perform = dispatch => ({ const perform = dispatch => ({
fetchFeaturedUris: () => dispatch(doFetchFeaturedUris()), fetchFeaturedUris: () => dispatch(doFetchFeaturedUris()),
cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()),
}); });
export default connect(select, perform)(DiscoverPage); export default connect(select, perform)(DiscoverPage);

View file

@ -204,10 +204,6 @@ class DiscoverPage extends React.PureComponent {
this.props.fetchFeaturedUris(); this.props.fetchFeaturedUris();
} }
componentWillUnmount() {
this.props.cancelResolvingUris();
}
render() { render() {
const { featuredUris, fetchingFeaturedUris } = this.props; const { featuredUris, fetchingFeaturedUris } = this.props;
const hasContent = const hasContent =

View file

@ -11,7 +11,6 @@ import {
} from "selectors/claims"; } from "selectors/claims";
import { doFetchClaimListMine } from "actions/content"; import { doFetchClaimListMine } from "actions/content";
import { doNavigate } from "actions/navigation"; import { doNavigate } from "actions/navigation";
import { doCancelAllResolvingUris } from "actions/content";
import FileListDownloaded from "./view"; import FileListDownloaded from "./view";
const select = state => ({ const select = state => ({
@ -25,7 +24,6 @@ const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)), navigate: path => dispatch(doNavigate(path)),
fetchFileInfosDownloaded: () => fetchFileInfosDownloaded: () =>
dispatch(doFetchFileInfosAndPublishedClaims()), dispatch(doFetchFileInfosAndPublishedClaims()),
cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()),
fetchClaims: () => dispatch(doFetchClaimListMine()), fetchClaims: () => dispatch(doFetchClaimListMine()),
}); });

View file

@ -11,10 +11,6 @@ class FileListDownloaded extends React.PureComponent {
if (!this.props.isFetching) this.props.fetchFileInfosDownloaded(); if (!this.props.isFetching) this.props.fetchFileInfosDownloaded();
} }
componentWillUnmount() {
this.props.cancelResolvingUris();
}
render() { render() {
const { fileInfos, isFetching, navigate } = this.props; const { fileInfos, isFetching, navigate } = this.props;

View file

@ -8,7 +8,6 @@ import {
} from "selectors/claims"; } from "selectors/claims";
import { doClaimRewardType } from "actions/rewards"; import { doClaimRewardType } from "actions/rewards";
import { doNavigate } from "actions/navigation"; import { doNavigate } from "actions/navigation";
import { doCancelAllResolvingUris } from "actions/content";
import FileListPublished from "./view"; import FileListPublished from "./view";
const select = state => ({ const select = state => ({
@ -21,7 +20,6 @@ const perform = dispatch => ({
fetchClaims: () => dispatch(doFetchClaimListMine()), fetchClaims: () => dispatch(doFetchClaimListMine()),
claimFirstPublishReward: () => claimFirstPublishReward: () =>
dispatch(doClaimRewardType(rewards.TYPE_FIRST_PUBLISH)), dispatch(doClaimRewardType(rewards.TYPE_FIRST_PUBLISH)),
cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()),
}); });
export default connect(select, perform)(FileListPublished); export default connect(select, perform)(FileListPublished);

View file

@ -14,10 +14,6 @@ class FileListPublished extends React.PureComponent {
// if (this.props.claims.length > 0) this.props.fetchClaims(); // if (this.props.claims.length > 0) this.props.fetchClaims();
} }
componentWillUnmount() {
this.props.cancelResolvingUris();
}
render() { render() {
const { claims, isFetching, navigate } = this.props; const { claims, isFetching, navigate } = this.props;

View file

@ -6,17 +6,15 @@ import FilePage from "page/file";
class ShowPage extends React.PureComponent { class ShowPage extends React.PureComponent {
componentWillMount() { componentWillMount() {
const { isResolvingUri, resolveUri, uri } = this.props; const { resolveUri, uri } = this.props;
if (!isResolvingUri) resolveUri(uri); resolveUri(uri);
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { isResolvingUri, resolveUri, claim, uri } = nextProps; const { resolveUri, uri } = nextProps;
if (!isResolvingUri && claim === undefined && uri) { resolveUri(uri);
resolveUri(uri);
}
} }
render() { render() {

View file

@ -4,26 +4,27 @@ const reducers = {};
const defaultState = {}; const defaultState = {};
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) { reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) {
const { uri, certificate, claim } = action.data; const { resolveInfo } = action.data;
const byUri = Object.assign({}, state.claimsByUri); const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
if (claim) { for (let [uri, { certificate, claim }] of Object.entries(resolveInfo)) {
byId[claim.claim_id] = claim; if (claim) {
byUri[uri] = claim.claim_id; byId[claim.claim_id] = claim;
} else if (claim === undefined && certificate !== undefined) { byUri[uri] = claim.claim_id;
byId[certificate.claim_id] = certificate; } else if (claim === undefined && certificate !== undefined) {
// Don't point URI at the channel certificate unless it actually is byId[certificate.claim_id] = certificate;
// a channel URI. This is brittle. // Don't point URI at the channel certificate unless it actually is
if (!uri.split(certificate.name)[1].match(/\//)) { // a channel URI. This is brittle.
byUri[uri] = certificate.claim_id; if (!uri.split(certificate.name)[1].match(/\//)) {
byUri[uri] = certificate.claim_id;
} else {
byUri[uri] = null;
}
} else { } else {
byUri[uri] = null; byUri[uri] = null;
} }
} else {
byUri[uri] = null;
} }
return Object.assign({}, state, { return Object.assign({}, state, {

View file

@ -31,34 +31,23 @@ reducers[types.FETCH_REWARD_CONTENT_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.RESOLVE_URI_STARTED] = function(state, action) { reducers[types.RESOLVE_URIS_STARTED] = function(state, action) {
const { uri } = action.data; let { uris } = action.data;
const oldResolving = state.resolvingUris || []; const oldResolving = state.resolvingUris || [];
const newResolving = Object.assign([], oldResolving); const newResolving = Object.assign([], oldResolving);
if (newResolving.indexOf(uri) === -1) newResolving.push(uri);
for (let uri of uris) {
if (!newResolving.includes(uri)) {
newResolving.push(uri);
}
}
return Object.assign({}, state, { return Object.assign({}, state, {
resolvingUris: newResolving, resolvingUris: newResolving,
}); });
}; };
reducers[types.RESOLVE_URI_CANCELED] = reducers[
types.RESOLVE_URI_COMPLETED
] = function(state, action) {
const { uri } = action.data;
const resolvingUris = state.resolvingUris;
const index = state.resolvingUris.indexOf(uri);
const newResolvingUris = [
...resolvingUris.slice(0, index),
...resolvingUris.slice(index + 1),
];
return Object.assign({}, state, {
resolvingUris: newResolvingUris,
});
};
reducers[types.SET_PLAYING_URI] = (state, action) => { reducers[types.SET_PLAYING_URI] = (state, action) => {
return Object.assign({}, state, { return Object.assign({}, state, {
playingUri: action.data.uri, playingUri: action.data.uri,