Merge pull request #2122 from lbryio/rc-fixes

RC fixes
This commit is contained in:
Sean Yesmunt 2018-11-28 18:22:23 -05:00 committed by GitHub
commit 5ce7152bba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 215 additions and 151 deletions

View file

@ -50,7 +50,7 @@
"hast-util-sanitize": "^1.1.2",
"keytar": "^4.2.1",
"lbry-redux": "lbryio/lbry-redux#1ed2ea8b2de99bbcba652aa7b00478d02df4a290",
"lbryinc": "lbryio/lbryinc#82308ece97188747adbf6d71d55b6e7a6fa0bd95",
"lbryinc": "lbryio/lbryinc#e9508e259fa9bce3b69915b91534516cf5937075",
"localforage": "^1.7.1",
"mammoth": "^1.4.6",
"mime": "^2.3.1",

View file

@ -1,7 +1,7 @@
// @flow
import type { Claim } from 'types/claim';
import * as ICONS from 'constants/icons';
import * as React from 'react';
import React, { PureComponent } from 'react';
import { normalizeURI } from 'lbry-redux';
import ToolTip from 'component/common/tooltip';
import FileCard from 'component/fileCard';

View file

@ -2,7 +2,7 @@
import * as React from 'react';
type Props = {
text: string,
text: ?string,
lines: number,
};

View file

@ -3,8 +3,8 @@ import {
makeSelectCostInfoForUri,
makeSelectFileInfoForUri,
makeSelectClaimIsMine,
doOpenModal,
} from 'lbry-redux';
import { doOpenModal } from 'redux/actions/app';
import FileActions from './view';
const select = (state, props) => ({

View file

@ -14,7 +14,7 @@ import {
makeSelectContentPositionForUri,
} from 'redux/selectors/content';
import { selectShowNsfw } from 'redux/selectors/settings';
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
import { makeSelectIsSubscribed, makeSelectIsNew } from 'redux/selectors/subscriptions';
import { doClearContentHistoryUri } from 'redux/actions/content';
import FileCard from './view';
@ -29,6 +29,7 @@ const select = (state, props) => ({
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
position: makeSelectContentPositionForUri(props.uri)(state),
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
isNew: makeSelectIsNew(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -23,7 +23,7 @@ const select = (state, props) => ({
const perform = dispatch => ({
openFolder: path => dispatch(doOpenFileInFolder(path)),
showSnackBar: message => dispatch(doToast(message)),
showSnackBar: message => dispatch(doToast({ message })),
clickCommentButton: () => dispatch(doClickCommentButton()),
});

View file

@ -11,7 +11,7 @@ import { selectShowNsfw } from 'redux/selectors/settings';
import { doNavigate } from 'redux/actions/navigation';
import { doClearPublish, doUpdatePublishForm } from 'redux/actions/publish';
import { selectRewardContentClaimIds } from 'redux/selectors/content';
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
import { makeSelectIsSubscribed, makeSelectIsNew } from 'redux/selectors/subscriptions';
import FileTile from './view';
const select = (state, props) => ({
@ -23,6 +23,7 @@ const select = (state, props) => ({
obscureNsfw: !selectShowNsfw(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
isNew: makeSelectIsNew(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -30,6 +30,7 @@ type Props = {
displayDescription?: boolean,
size: string,
isSubscribed: boolean,
isNew: boolean,
};
class FileTile extends React.PureComponent<Props> {
@ -49,12 +50,21 @@ class FileTile extends React.PureComponent<Props> {
}
renderFileProperties() {
const { isSubscribed, isDownloaded, claim, uri, rewardedContentClaimIds, size } = this.props;
const {
isSubscribed,
isDownloaded,
claim,
uri,
rewardedContentClaimIds,
size,
isNew,
} = this.props;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
return (
<div className={classnames('card__file-properties', { card__subtitle: size === 'large' })}>
<FilePrice hideFree uri={uri} />
{isNew && <span className="badge badge--alert icon">{__('NEW')}</span>}
{isSubscribed && <Icon icon={ICONS.HEART} />}
{isRewardContent && <Icon iconColor="red" icon={ICONS.FEATURED} />}
{isDownloaded && <Icon icon={ICONS.LOCAL} />}

View file

@ -0,0 +1,10 @@
import { connect } from 'react-redux';
import { doRemoveUnreadSubscriptions } from 'redux/actions/subscriptions';
import MarkAsRead from './view';
export default connect(
null,
{
doRemoveUnreadSubscriptions,
}
)(MarkAsRead);

View file

@ -0,0 +1,42 @@
// @flow
import * as ICONS from 'constants/icons';
import React, { PureComponent } from 'react';
import Button from 'component/button';
type Props = {
channel: ?string,
doRemoveUnreadSubscriptions: (?string) => void,
};
export default class MarkAsRead extends PureComponent<Props> {
constructor() {
super();
(this: any).handleClick = this.handleClick.bind(this);
}
handleClick() {
const { channel, doRemoveUnreadSubscriptions } = this.props;
// If there is no channel, mark all as read
// If there is a channel, only mark that channel as read
if (channel) {
doRemoveUnreadSubscriptions(channel);
} else {
doRemoveUnreadSubscriptions();
}
}
render() {
const { channel } = this.props;
const label = channel ? __('Mark as read') : __('Mark all as read');
return (
<Button
noPadding
button="inverse"
icon={ICONS.CHECK_SIMPLE}
label={label}
onClick={this.handleClick}
/>
);
}
}

View file

@ -4,8 +4,8 @@ export default function doLogWarningConsoleMessage(activeOnDev = false) {
if (isDev && !activeOnDev) return;
const style = {
redTitle:
'color: red; font-size: 50px; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;, font-weight: bold;',
normalText: 'font-size: 24px;',
'color: red; font-size: 36px; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;, font-weight: bold;',
normalText: 'font-size: 18px;',
redText:
'color: red; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; font-size: 24px;',
};

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { doFetchClaimsByChannel, doFetchClaimCountByChannel } from 'redux/actions/content';
import { doFetchClaimsByChannel } from 'redux/actions/content';
import { PAGE_SIZE } from 'constants/claim';
import {
makeSelectClaimForUri,
@ -26,7 +26,6 @@ const select = (state, props) => ({
const perform = dispatch => ({
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
fetchClaimCount: uri => dispatch(doFetchClaimCountByChannel(uri)),
navigate: (path, params) => dispatch(doNavigate(path, params)),
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
});

View file

@ -22,28 +22,22 @@ type Props = {
claimsInChannel: Array<Claim>,
channelIsMine: boolean,
fetchClaims: (string, number) => void,
fetchClaimCount: string => void,
navigate: (string, {}) => void,
openModal: ({ id: string }, { uri: string }) => void,
};
class ChannelPage extends React.PureComponent<Props> {
componentDidMount() {
const { uri, page, fetchClaims, fetchClaimCount } = this.props;
const { uri, page, fetchClaims } = this.props;
fetchClaims(uri, page || 1);
fetchClaimCount(uri);
}
componentWillReceiveProps(nextProps: Props) {
const { page, uri, fetchClaims, fetchClaimCount } = this.props;
const { page, fetchClaims } = this.props;
if (nextProps.page && page !== nextProps.page) {
fetchClaims(nextProps.uri, nextProps.page);
}
if (nextProps.uri !== uri) {
fetchClaimCount(uri);
}
}
changePage(pageNumber: number) {

View file

@ -11,6 +11,7 @@ import FileCard from 'component/fileCard';
import { parseURI } from 'lbry-redux';
import Native from 'native';
import SuggestedSubscriptions from 'component/subscribeSuggested';
import MarkAsRead from 'component/subscribeMarkAsRead';
type Props = {
viewMode: ViewMode,
@ -90,7 +91,10 @@ export default (props: Props) => {
<div className="card__content">
{viewMode === VIEW_ALL && (
<Fragment>
<div className="card__title">{__('Your subscriptions')}</div>
<div className="card__title card__actions card__actions--no-margin">
{__('Your subscriptions')}
{unreadSubscriptions.length > 0 && <MarkAsRead />}
</div>
<FileList hideFilter sortByHeight fileInfos={subscriptions} />
</Fragment>
)}
@ -102,16 +106,17 @@ export default (props: Props) => {
const { claimName } = parseURI(channel);
return (
<section key={channel}>
<div className="card__title">
<div className="card__title card__actions card__actions--no-margin">
<Button
button="link"
navigate="/show"
navigateParams={{ uri: channel }}
label={claimName}
/>
<MarkAsRead channel={channel} />
</div>
<div className="card__list card__content">
{uris.map(uri => <FileCard isNew key={uri} uri={uri} />)}
{uris.map(uri => <FileCard key={uri} uri={uri} />)}
</div>
</section>
);
@ -120,7 +125,7 @@ export default (props: Props) => {
<Fragment>
<div className="page__empty">
<h3 className="card__title">{__('All caught up!')}</h3>
<p className="card__subtitle">{__('You might like these channels.')}</p>
<p className="card__subtitle">{__('You might like these channels below.')}</p>
</div>
<SuggestedSubscriptions />
</Fragment>

View file

@ -4,12 +4,7 @@ import path from 'path';
import { ipcRenderer, remote } from 'electron';
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import {
Lbry,
doBalanceSubscribe,
doFetchFileInfosAndPublishedClaims,
selectNotification,
} from 'lbry-redux';
import { Lbry, doBalanceSubscribe, doFetchFileInfosAndPublishedClaims, doError } from 'lbry-redux';
import Native from 'native';
import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doAuthNavigate } from 'redux/actions/navigation';
@ -23,8 +18,9 @@ import {
selectAutoUpdateDeclined,
selectRemoteVersion,
selectUpgradeTimer,
selectModal,
} from 'redux/selectors/app';
import { doAuthenticate, doFetchRewardedContent } from 'lbryinc';
import { doAuthenticate } from 'lbryinc';
import { lbrySettings as config, version as appVersion } from 'package.json';
const { autoUpdater } = remote.require('electron-updater');
@ -33,6 +29,22 @@ const Fs = remote.require('fs');
const CHECK_UPGRADE_INTERVAL = 10 * 60 * 1000;
export function doOpenModal(id, modalProps = {}) {
return {
type: ACTIONS.SHOW_MODAL,
data: {
id,
modalProps,
},
};
}
export function doHideModal() {
return {
type: ACTIONS.HIDE_MODAL,
};
}
export function doUpdateDownloadProgress(percent) {
return {
type: ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED,
@ -88,9 +100,7 @@ export function doDownloadUpgrade() {
type: ACTIONS.UPGRADE_DOWNLOAD_STARTED,
});
dispatch(doHideModal());
dispatch(
doOpenModal(MODALS.DOWNLOADING)
);
dispatch(doOpenModal(MODALS.DOWNLOADING));
};
}
@ -109,15 +119,11 @@ export function doDownloadUpgradeRequested() {
// electron-updater behavior
if (autoUpdateDeclined) {
// The user declined an update before, so show the "confirm" dialog
dispatch(
doOpenModal(MODALS.AUTO_UPDATE_CONFIRM)
);
dispatch(doOpenModal(MODALS.AUTO_UPDATE_CONFIRM));
} else {
// The user was never shown the original update dialog (e.g. because they were
// watching a video). So show the inital "update downloaded" dialog.
dispatch(
doOpenModal(MODALS.AUTO_UPDATE_DOWNLOADED)
);
dispatch(doOpenModal(MODALS.AUTO_UPDATE_DOWNLOADED));
}
} else {
// Old behavior for Linux
@ -145,9 +151,7 @@ export function doAutoUpdate() {
type: ACTIONS.AUTO_UPDATE_DOWNLOADED,
});
dispatch(
doOpenModal(MODALS.AUTO_UPDATE_DOWNLOADED)
);
dispatch(doOpenModal(MODALS.AUTO_UPDATE_DOWNLOADED));
dispatch(doClearUpgradeTimer());
};
@ -216,12 +220,10 @@ export function doCheckUpgradeAvailable() {
if (
upgradeAvailable &&
!selectNotification(state) &&
!selectModal(state) &&
(!selectIsUpgradeSkipped(state) || remoteVersion !== selectRemoteVersion(state))
) {
dispatch(
doOpenModal(MODALS.UPGRADE)
);
dispatch(doOpenModal(MODALS.UPGRADE));
}
};
@ -266,42 +268,32 @@ export function doCheckDaemonVersion() {
type: ACTIONS.DAEMON_VERSION_MISMATCH,
});
return dispatch(
doOpenModal(MODALS.INCOMPATIBLE_DAEMON)
);
return dispatch(doOpenModal(MODALS.INCOMPATIBLE_DAEMON));
});
};
}
export function doNotifyEncryptWallet() {
return dispatch => {
dispatch(
doOpenModal(MODALS.WALLET_ENCRYPT)
);
dispatch(doOpenModal(MODALS.WALLET_ENCRYPT));
};
}
export function doNotifyDecryptWallet() {
return dispatch => {
dispatch(
doOpenModal(MODALS.WALLET_DECRYPT)
);
dispatch(doOpenModal(MODALS.WALLET_DECRYPT));
};
}
export function doNotifyUnlockWallet() {
return dispatch => {
dispatch(
doOpenModal(MODALS.WALLET_UNLOCK)
);
dispatch(doOpenModal(MODALS.WALLET_UNLOCK));
};
}
export function doAlertError(errorList) {
return dispatch => {
dispatch(
doError(errorList)
);
dispatch(doError(errorList));
};
}
@ -314,7 +306,6 @@ export function doDaemonReady() {
dispatch(doFetchDaemonSettings());
dispatch(doBalanceSubscribe());
dispatch(doFetchFileInfosAndPublishedClaims());
dispatch(doFetchRewardedContent());
if (!selectIsUpgradeSkipped(state)) {
dispatch(doCheckUpgradeAvailable());
}
@ -373,26 +364,10 @@ export function doClickCommentButton() {
export function doConditionalAuthNavigate(newSession) {
return (dispatch, getState) => {
const state = getState();
const notification = selectNotification(state);
const notification = selectModal(state);
if (newSession || (notification && notification.id !== 'email_collection')) {
dispatch(doAuthNavigate());
}
};
}
export function doOpenModal(id, modalProps = {}) {
return {
type: ACTIONS.SHOW_MODAL,
data: {
id,
modalProps,
},
};
}
export function doHideModal() {
return {
type: ACTIONS.HIDE_MODAL
}
}

View file

@ -14,7 +14,6 @@ import {
Lbry,
Lbryapi,
buildURI,
doFetchClaimListMine,
makeSelectCostInfoForUri,
makeSelectFileInfoForUri,
selectFileInfosByOutpoint,
@ -331,28 +330,6 @@ export function doFetchClaimsByChannel(uri, page, pageSize) {
};
}
export function doFetchClaimCountByChannel(uri) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_STARTED,
data: { uri },
});
Lbry.claim_list_by_channel({ uri }).then(result => {
const claimResult = result[uri];
const totalClaims = claimResult ? claimResult.claims_in_channel : 0;
dispatch({
type: ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED,
data: {
uri,
totalClaims,
},
});
});
};
}
export function doPlayUri(uri) {
return dispatch => {
dispatch(doSetPlayingUri(uri));
@ -405,24 +382,6 @@ export function doCreateChannel(name: string, amount: number) {
};
}
export function doPublish(params) {
return dispatch =>
new Promise((resolve, reject) => {
const success = claim => {
resolve(claim);
if (claim === true) dispatch(doFetchClaimListMine());
else
setTimeout(() => dispatch(doFetchClaimListMine()), 20000, {
once: true,
});
};
const failure = err => reject(err);
Lbry.publishDeprecated(params, null, success, failure);
});
}
export function savePosition(claimId: string, outpoint: string, position: number) {
return dispatch => {
dispatch({

View file

@ -15,7 +15,7 @@ import * as NOTIFICATION_TYPES from 'constants/subscriptions';
import { Lbryio, rewards, doClaimRewardType } from 'lbryinc';
import { selectSubscriptions, selectUnreadByChannel } from 'redux/selectors/subscriptions';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { Lbry, buildURI, parseURI } from 'lbry-redux';
import { Lbry, buildURI, parseURI, doResolveUris } from 'lbry-redux';
import { doPurchaseUri, doFetchClaimsByChannel } from 'redux/actions/content';
import Promise from 'bluebird';
@ -101,6 +101,7 @@ export const doFetchMySubscriptions = () => (dispatch: ReduxDispatch, getState:
data: subscriptions,
});
dispatch(doResolveUris(subscriptions.map(({ uri }) => uri)));
subscriptions.forEach(({ uri }) => dispatch(doFetchClaimsByChannel(uri, 1)));
})
.catch(() => {
@ -167,28 +168,44 @@ export const doUpdateUnreadSubscriptions = (
};
// Remove multiple files (or all) from a channels unread subscriptions
export const doRemoveUnreadSubscriptions = (channelUri: string, readUris: Array<string>) => (
export const doRemoveUnreadSubscriptions = (channelUri: ?string, readUris: ?Array<string>) => (
dispatch: ReduxDispatch,
getState: GetState
) => {
const state = getState();
const unreadByChannel = selectUnreadByChannel(state);
// If no channel is passed in, remove all unread subscriptions from all channels
if (!channelUri) {
return dispatch({
type: ACTIONS.REMOVE_SUBSCRIPTION_UNREADS,
data: { channel: null },
});
}
const currentChannelUnread = unreadByChannel[channelUri];
if (!currentChannelUnread || !currentChannelUnread.uris) {
// Channel passed in doesn't have any unreads
return;
}
// For each uri passed in, remove it from the list of unread uris
const urisToRemoveMap = readUris.reduce(
(acc, val) => ({
...acc,
[val]: true,
}),
{}
);
// If no uris are passed in, remove them all
let newUris;
if (readUris) {
const urisToRemoveMap = readUris.reduce(
(acc, val) => ({
...acc,
[val]: true,
}),
{}
);
const filteredUris = currentChannelUnread.uris.filter(uri => !urisToRemoveMap[uri]);
const newUris = filteredUris.length ? filteredUris : null;
const filteredUris = currentChannelUnread.uris.filter(uri => !urisToRemoveMap[uri]);
newUris = filteredUris.length ? filteredUris : null;
} else {
newUris = null;
}
dispatch({
type: ACTIONS.REMOVE_SUBSCRIPTION_UNREADS,

View file

@ -66,10 +66,11 @@ export default handleActions(
action: SetSubscriptionLatest
): SubscriptionState => ({
...state,
subscriptions: state.subscriptions.map(subscription =>
subscription.channelName === action.data.subscription.channelName
? { ...subscription, latest: action.data.uri }
: subscription
subscriptions: state.subscriptions.map(
subscription =>
subscription.channelName === action.data.subscription.channelName
? { ...subscription, latest: action.data.uri }
: subscription
),
}),
[ACTIONS.UPDATE_SUBSCRIPTION_UNREADS]: (
@ -94,12 +95,19 @@ export default handleActions(
action: DoRemoveSubscriptionUnreads
): SubscriptionState => {
const { channel, uris } = action.data;
const newUnread = { ...state.unread };
if (!uris) {
delete newUnread[channel];
// If no channel is passed in, remove all unreads
let newUnread;
if (channel) {
newUnread = { ...state.unread };
if (!uris) {
delete newUnread[channel];
} else {
newUnread[channel].uris = uris;
}
} else {
newUnread[channel].uris = uris;
newUnread = {};
}
return {

View file

@ -253,3 +253,23 @@ export const makeSelectIsSubscribed = uri =>
return false;
}
);
export const makeSelectIsNew = uri =>
createSelector(
makeSelectIsSubscribed(uri),
makeSelectChannelForClaimUri(uri),
selectUnreadByChannel,
(isSubscribed, channel, unreadByChannel) => {
if (!isSubscribed) {
return false;
}
const unreadForChannel = unreadByChannel[`lbry://${channel}`];
if (unreadForChannel) {
return unreadForChannel.uris.includes(uri);
}
return false;
// If they are subscribed, check to see if this uri is in the list of unreads
}
);

View file

@ -2,7 +2,7 @@
// component specific styling should go in the component scss file
html {
background-color: $lbry-white;
background-color: $lbry-teal-5;
font-family: 'Inter UI', sans-serif;
height: 100%;
}

View file

@ -32,6 +32,14 @@
}
}
&.btn--no-padding {
.btn__content {
padding: 0;
display: flex;
align-items: center;
}
}
&.btn--alt {
&:not(:disabled) {
background-color: $lbry-white;

View file

@ -324,6 +324,7 @@
.card-row__scrollhouse {
padding-top: $spacing-vertical * 2/3;
padding-bottom: $spacing-vertical * 1/6;
overflow: hidden;
.card {

View file

@ -1,4 +1,11 @@
html[data-theme='dark'] {
//
// HTML Elements
//
blockquote {
background-color: rgba($lbry-black, 0.9);
}
.header {
background-color: rgba($lbry-black, 0.9);
}
@ -203,4 +210,11 @@ html[data-theme='dark'] {
.expandable--closed::after {
background-image: linear-gradient(to bottom, transparent 0%, $lbry-black 90%);
}
//
// File Viewer
//
.document-viewer {
background-color: transparent;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 181 KiB

View file

@ -5663,6 +5663,14 @@ lazy-val@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc"
lbry-redux@lbryio/lbry-redux#1ed2ea8b2de99bbcba652aa7b00478d02df4a290:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/1ed2ea8b2de99bbcba652aa7b00478d02df4a290"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
uuid "^3.3.2"
lbry-redux@lbryio/lbry-redux#2375860d6269d0369418879c2531b1d48c4e47f2:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/2375860d6269d0369418879c2531b1d48c4e47f2"
@ -5670,17 +5678,9 @@ lbry-redux@lbryio/lbry-redux#2375860d6269d0369418879c2531b1d48c4e47f2:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
lbry-redux@lbryio/lbry-redux#df8e8c6b44718f8de36aabb9222e74ca9433e13c:
lbryinc@lbryio/lbryinc#e9508e259fa9bce3b69915b91534516cf5937075:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/df8e8c6b44718f8de36aabb9222e74ca9433e13c"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
uuid "^3.3.2"
lbryinc@lbryio/lbryinc#82308ece97188747adbf6d71d55b6e7a6fa0bd95:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/82308ece97188747adbf6d71d55b6e7a6fa0bd95"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/e9508e259fa9bce3b69915b91534516cf5937075"
dependencies:
lbry-redux lbryio/lbry-redux#2375860d6269d0369418879c2531b1d48c4e47f2
reselect "^3.0.0"