Merge branch 'multi-resolve'
This commit is contained in:
commit
d6b86c0f07
13 changed files with 81 additions and 139 deletions
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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, {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Reference in a new issue