Minor refactors in doResolveUri()
Add doResolveUris() Always call resolveUri() in FileTile and FileCard Before, these components would only try and resolve if claim info wasn't provided. Don't require uri param in lbry.resolve() It can now be "uris" instead, plus the error message about caching doesn't really apply anymore. Don't cache/cancel open resolve requests No longer needed because we're not doing resolve requests in bulk Add support for multiple URI resolution in lbry.resolve() Handle multi URL resolves with one action Update CHANGELOG.md
This commit is contained in:
parent
80f1cac4e5
commit
c5c67a0de5
15 changed files with 89 additions and 153 deletions
CHANGELOG.md
ui/js
|
@ -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));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,19 +20,13 @@ class FileCard extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.resolve(this.props);
|
const { uri, resolveUri } = this.props;
|
||||||
|
resolveUri(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
this.resolve(nextProps);
|
const { uri, resolveUri } = nextProps;
|
||||||
}
|
resolveUri(uri);
|
||||||
|
|
||||||
resolve(props) {
|
|
||||||
const { isResolvingUri, resolveUri, claim, uri } = props;
|
|
||||||
|
|
||||||
if (!isResolvingUri && claim === undefined && uri) {
|
|
||||||
resolveUri(uri);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseOver() {
|
handleMouseOver() {
|
||||||
|
|
|
@ -26,15 +26,15 @@ class FileTile extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { isResolvingUri, claim, uri, resolveUri } = this.props;
|
const { uri, resolveUri } = this.props;
|
||||||
|
|
||||||
if (!isResolvingUri && !claim && uri) resolveUri(uri);
|
resolveUri(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
const { isResolvingUri, claim, uri, resolveUri } = this.props;
|
const { uri, resolveUri } = this.props;
|
||||||
|
|
||||||
if (!isResolvingUri && claim === undefined && uri) resolveUri(uri);
|
resolveUri(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseOver() {
|
handleMouseOver() {
|
||||||
|
|
|
@ -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