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)
* 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.
* Improved Discover page load time by batching all URIs into one API call
### Fixed
* 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;
export function doResolveUri(uri) {
export function doResolveUris(uris) {
return function(dispatch, getState) {
uri = lbryuri.normalize(uri);
uris = uris.map(lbryuri.normalize);
const state = getState();
const alreadyResolving = selectResolvingUris(state).indexOf(uri) !== -1;
if (!alreadyResolving) {
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();
// Filter out URIs that are already resolving
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() {
return function(dispatch, getState) {
const state = getState();
@ -96,28 +74,27 @@ export function doFetchFeaturedUris() {
const success = ({ Categories, Uris }) => {
let featuredUris = {};
const actions = [];
let urisToResolve = [];
Categories.forEach(category => {
if (Uris[category] && Uris[category].length) {
const uris = Uris[category];
featuredUris[category] = uris;
uris.forEach(uri => {
actions.push(doResolveUri(uri));
});
urisToResolve = [...urisToResolve, ...uris];
}
});
actions.push({
type: types.FETCH_FEATURED_CONTENT_COMPLETED,
data: {
categories: Categories,
uris: featuredUris,
success: true,
const actions = [
doResolveUris(urisToResolve),
{
type: types.FETCH_FEATURED_CONTENT_COMPLETED,
data: {
categories: Categories,
uris: featuredUris,
success: true,
},
},
});
];
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_COMPLETED =
"FETCH_FEATURED_CONTENT_COMPLETED";
export const RESOLVE_URI_STARTED = "RESOLVE_URI_STARTED";
export const RESOLVE_URI_COMPLETED = "RESOLVE_URI_COMPLETED";
export const RESOLVE_URI_CANCELED = "RESOLVE_URI_CANCELED";
export const RESOLVE_URIS_STARTED = "RESOLVE_URIS_STARTED";
export const RESOLVE_URIS_COMPLETED = "RESOLVE_URIS_COMPLETED";
export const FETCH_CHANNEL_CLAIMS_STARTED = "FETCH_CHANNEL_CLAIMS_STARTED";
export const FETCH_CHANNEL_CLAIMS_COMPLETED = "FETCH_CHANNEL_CLAIMS_COMPLETED";
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 = {}) {
return new Promise((resolve, reject) => {
if (!params.uri) {
throw __("Resolve has hacked cache on top of it that requires a URI");
}
lbry._resolveXhrs[params.uri] = apiCall(
apiCall(
"resolve",
params,
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
);
});
};
lbry.cancelResolve = function(params = {}) {
const xhr = lbry._resolveXhrs[params.uri];
if (xhr && xhr.readyState > 0 && xhr.readyState < 4) {
xhr.abort();
}
};
lbry = new Proxy(lbry, {
get: function(target, name) {
if (name in target) {

View file

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

View file

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

View file

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

View file

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

View file

@ -8,7 +8,6 @@ import {
} from "selectors/claims";
import { doClaimRewardType } from "actions/rewards";
import { doNavigate } from "actions/navigation";
import { doCancelAllResolvingUris } from "actions/content";
import FileListPublished from "./view";
const select = state => ({
@ -21,7 +20,6 @@ const perform = dispatch => ({
fetchClaims: () => dispatch(doFetchClaimListMine()),
claimFirstPublishReward: () =>
dispatch(doClaimRewardType(rewards.TYPE_FIRST_PUBLISH)),
cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()),
});
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();
}
componentWillUnmount() {
this.props.cancelResolvingUris();
}
render() {
const { claims, isFetching, navigate } = this.props;

View file

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

View file

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