diff --git a/package.json b/package.json index c563073eb..51ccb5214 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "imagesloaded": "^4.1.4", "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", - "lbry-redux": "lbryio/lbry-redux#a327385cdf71568dbd15a17f3dcf5f4b83e0966d", + "lbry-redux": "lbryio/lbry-redux#8f66a2fe7c84d4587ec95698bce9f3e4360f8e88", "lbryinc": "lbryio/lbryinc#8f9a58bfc8312a65614fd7327661cdcc502c4e59", "lint-staged": "^7.0.2", "localforage": "^1.7.1", diff --git a/ui/component/claimList/view.jsx b/ui/component/claimList/view.jsx index 1cf6743fe..7666f553b 100644 --- a/ui/component/claimList/view.jsx +++ b/ui/component/claimList/view.jsx @@ -21,7 +21,6 @@ type Props = { headerAltControls: Node, loading: boolean, type: string, - activeUri?: string, empty?: string, defaultSort?: boolean, onScrollBottom?: (any) => void, @@ -51,7 +50,6 @@ type Props = { export default function ClaimList(props: Props) { const { - activeUri, uris, headerAltControls, loading, @@ -192,7 +190,6 @@ export default function ClaimList(props: Props) { splitBySeparator(uri)[1])) - ); + const mutedAndBlockedChannelIds = Array.from(new Set(mutedUris.concat(blockedUris).map((uri) => uri.split('#')[1]))); const langParam = urlParams.get(CS.LANGUAGE_KEY) || null; const languageParams = searchInLanguage diff --git a/ui/component/claimMenuList/view.jsx b/ui/component/claimMenuList/view.jsx index 204181e4f..df7925e82 100644 --- a/ui/component/claimMenuList/view.jsx +++ b/ui/component/claimMenuList/view.jsx @@ -7,7 +7,7 @@ import React from 'react'; import classnames from 'classnames'; import { Menu, MenuButton, MenuList, MenuItem } from '@reach/menu-button'; import Icon from 'component/common/icon'; -import { generateShareUrl, generateRssUrl, generateLbryContentUrl } from 'util/url'; +import { generateShareUrl, generateRssUrl } from 'util/url'; import { useHistory } from 'react-router'; import { buildURI, parseURI, COLLECTIONS_CONSTS } from 'lbry-redux'; @@ -107,8 +107,7 @@ function ClaimMenuList(props: Props) { return null; } - const lbryUrl: string = generateLbryContentUrl(claim.canonical_url, claim.permanent_url); - const shareUrl: string = generateShareUrl(SHARE_DOMAIN, lbryUrl); + const shareUrl: string = generateShareUrl(SHARE_DOMAIN, uri); const rssUrl: string = isChannel ? generateRssUrl(URL, claim) : ''; const isCollectionClaim = claim && claim.value_type === 'collection'; // $FlowFixMe diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index 850fb2f49..4e04211b2 100644 --- a/ui/component/claimPreview/view.jsx +++ b/ui/component/claimPreview/view.jsx @@ -4,7 +4,7 @@ import React, { useEffect, forwardRef } from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import { lazyImport } from 'util/lazyImport'; import classnames from 'classnames'; -import { parseURI, COLLECTIONS_CONSTS, isURIEqual } from 'lbry-redux'; +import { parseURI, COLLECTIONS_CONSTS } from 'lbry-redux'; import { formatLbryUrlForWeb } from 'util/url'; import { isEmpty } from 'util/object'; import FileThumbnail from 'component/fileThumbnail'; @@ -29,14 +29,11 @@ import { ENABLE_NO_SOURCE_CLAIMS } from 'config'; import Button from 'component/button'; import * as ICONS from 'constants/icons'; -const AbandonedChannelPreview = lazyImport(() => - import('component/abandonedChannelPreview' /* webpackChunkName: "abandonedChannelPreview" */) -); +const AbandonedChannelPreview = lazyImport(() => import('component/abandonedChannelPreview' /* webpackChunkName: "abandonedChannelPreview" */)); type Props = { uri: string, claim: ?Claim, - active: boolean, obscureNsfw: boolean, showUserBlocked: boolean, claimIsMine: boolean, @@ -114,7 +111,6 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { pending, empty, // modifiers - active, customShouldHide, showNullPlaceholder, // value from show mature content user setting @@ -211,10 +207,10 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { } // block stream claims if (claim && !shouldHide && !showUserBlocked && mutedUris.length && signingChannel) { - shouldHide = mutedUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); + shouldHide = mutedUris.some((blockedUri) => blockedUri === signingChannel.permanent_url); } if (claim && !shouldHide && !showUserBlocked && blockedUris.length && signingChannel) { - shouldHide = blockedUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); + shouldHide = blockedUris.some((blockedUri) => blockedUri === signingChannel.permanent_url); } if (!shouldHide && customShouldHide && claim) { @@ -295,7 +291,6 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { 'claim-preview__wrapper--inline': type === 'inline', 'claim-preview__wrapper--small': type === 'small', 'claim-preview__live': live, - 'claim-preview__active': active, })} > <> @@ -352,7 +347,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { {pending ? ( ) : ( - + )} diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index 62e95d155..6533dbba8 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -10,7 +10,7 @@ import ChannelThumbnail from 'component/channelThumbnail'; import SubscribeButton from 'component/subscribeButton'; import useGetThumbnail from 'effects/use-get-thumbnail'; import { formatLbryUrlForWeb } from 'util/url'; -import { parseURI, COLLECTIONS_CONSTS, isURIEqual } from 'lbry-redux'; +import { parseURI, COLLECTIONS_CONSTS } from 'lbry-redux'; import PreviewOverlayProperties from 'component/previewOverlayProperties'; import FileDownloadLink from 'component/fileDownloadLink'; import FileWatchLaterLink from 'component/fileWatchLaterLink'; @@ -155,12 +155,12 @@ function ClaimPreviewTile(props: Props) { // block stream claims if (claim && !shouldHide && !showHiddenByUser && blockedChannelUris.length && signingChannel) { - shouldHide = blockedChannelUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); + shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === signingChannel.permanent_url); } // block channel claims if we can't control for them in claim search // e.g. fetchRecommendedSubscriptions - if (claim && isChannel && !shouldHide && !showHiddenByUser && blockedChannelUris.length && signingChannel) { - shouldHide = blockedChannelUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); + if (claim && isChannel && !shouldHide && !showHiddenByUser && blockedChannelUris.length) { + shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === claim.permanent_url); } if (shouldHide || (isLivestream && !showNoSourceClaims)) { diff --git a/ui/component/claimTilesDiscover/view.jsx b/ui/component/claimTilesDiscover/view.jsx index f580c2028..b38863a6f 100644 --- a/ui/component/claimTilesDiscover/view.jsx +++ b/ui/component/claimTilesDiscover/view.jsx @@ -3,7 +3,7 @@ import { ENABLE_NO_SOURCE_CLAIMS, SIMPLE_SITE } from 'config'; import * as CS from 'constants/claim_search'; import type { Node } from 'react'; import React from 'react'; -import { createNormalizedClaimSearchKey, MATURE_TAGS, splitBySeparator } from 'lbry-redux'; +import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux'; import ClaimPreviewTile from 'component/claimPreviewTile'; import { useHistory } from 'react-router'; import { getLivestreamOnlyOptions } from 'util/search'; @@ -155,9 +155,7 @@ function ClaimTilesDiscover(props: Props) { const urlParams = new URLSearchParams(location.search); const feeAmountInUrl = urlParams.get('fee_amount'); const feeAmountParam = feeAmountInUrl || feeAmount; - const mutedAndBlockedChannelIds = Array.from( - new Set(mutedUris.concat(blockedUris).map((uri) => splitBySeparator(uri)[1])) - ); + const mutedAndBlockedChannelIds = Array.from(new Set(mutedUris.concat(blockedUris).map((uri) => uri.split('#')[1]))); const liveUris = []; const [prevUris, setPrevUris] = React.useState([]); diff --git a/ui/component/collectionContentSidebar/index.js b/ui/component/collectionContentSidebar/index.js index ffe8126a7..9af808fc6 100644 --- a/ui/component/collectionContentSidebar/index.js +++ b/ui/component/collectionContentSidebar/index.js @@ -4,19 +4,19 @@ import { makeSelectUrlsForCollectionId, makeSelectNameForCollectionId, makeSelectCollectionForId, - makeSelectClaimForUri, + makeSelectClaimForClaimId, makeSelectClaimIsMine, } from 'lbry-redux'; const select = (state, props) => { - const claim = makeSelectClaimForUri(props.uri)(state); + const claim = makeSelectClaimForClaimId(props.id)(state); const url = claim && claim.permanent_url; return { - url, collection: makeSelectCollectionForId(props.id)(state), collectionUrls: makeSelectUrlsForCollectionId(props.id)(state), collectionName: makeSelectNameForCollectionId(props.id)(state), + claim, isMine: makeSelectClaimIsMine(url)(state), }; }; diff --git a/ui/component/collectionContentSidebar/view.jsx b/ui/component/collectionContentSidebar/view.jsx index 69d838ab1..89b8d573a 100644 --- a/ui/component/collectionContentSidebar/view.jsx +++ b/ui/component/collectionContentSidebar/view.jsx @@ -9,17 +9,18 @@ import * as ICONS from 'constants/icons'; import { COLLECTIONS_CONSTS } from 'lbry-redux'; type Props = { - id: string, - url: string, - isMine: boolean, collectionUrls: Array, collectionName: string, collection: any, createUnpublishedCollection: (string, Array, ?string) => void, + id: string, + claim: Claim, + isMine: boolean, }; export default function CollectionContent(props: Props) { - const { collectionUrls, collectionName, id, url } = props; + const { collectionUrls, collectionName, id } = props; + return ( } titleActions={ - <> +
{/* TODO: BUTTON TO SAVE COLLECTION - Probably save/copy modal */}
} + body={} /> ); } diff --git a/ui/component/commentsList/index.js b/ui/component/commentsList/index.js index 72a5b2916..7433f6990 100644 --- a/ui/component/commentsList/index.js +++ b/ui/component/commentsList/index.js @@ -4,7 +4,6 @@ import { makeSelectTopLevelCommentsForUri, makeSelectTopLevelTotalPagesForUri, selectIsFetchingComments, - selectIsFetchingReacts, makeSelectTotalCommentsCountForUri, selectOthersReactsById, makeSelectCommentsDisabledForUri, @@ -25,7 +24,6 @@ const select = (state, props) => { totalComments: makeSelectTotalCommentsCountForUri(props.uri)(state), claimIsMine: makeSelectClaimIsMine(props.uri)(state), isFetchingComments: selectIsFetchingComments(state), - isFetchingReacts: selectIsFetchingReacts(state), commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true, commentsDisabledBySettings: makeSelectCommentsDisabledForUri(props.uri)(state), fetchingChannels: selectFetchingMyChannels(state), diff --git a/ui/component/commentsList/view.jsx b/ui/component/commentsList/view.jsx index e3380cad7..cd3d1e6b4 100644 --- a/ui/component/commentsList/view.jsx +++ b/ui/component/commentsList/view.jsx @@ -37,7 +37,6 @@ type Props = { claimIsMine: boolean, myChannels: ?Array, isFetchingComments: boolean, - isFetchingReacts: boolean, linkedCommentId?: string, totalComments: number, fetchingChannels: boolean, @@ -60,7 +59,6 @@ function CommentList(props: Props) { claimIsMine, myChannels, isFetchingComments, - isFetchingReacts, linkedCommentId, totalComments, fetchingChannels, @@ -124,7 +122,7 @@ function CommentList(props: Props) { // Fetch reacts useEffect(() => { - if (totalFetchedComments > 0 && ENABLE_COMMENT_REACTIONS && !fetchingChannels && !isFetchingReacts) { + if (totalFetchedComments > 0 && ENABLE_COMMENT_REACTIONS && !fetchingChannels) { let idsForReactionFetch; if (!othersReactsById || !myReactsByCommentId) { @@ -153,7 +151,6 @@ function CommentList(props: Props) { uri, activeChannelId, fetchingChannels, - isFetchingReacts, ]); // Scroll to linked-comment diff --git a/ui/component/commentsReplies/view.jsx b/ui/component/commentsReplies/view.jsx index c30d86121..57d19055b 100644 --- a/ui/component/commentsReplies/view.jsx +++ b/ui/component/commentsReplies/view.jsx @@ -72,34 +72,33 @@ function CommentsReplies(props: Props) { /> )} - {isExpanded && ( + {fetchedReplies && displayedComments && isExpanded && (
); diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index 67a00c4a0..fb94d6834 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -626,6 +626,7 @@ export function doGetAndPopulatePreferences() { function successCb(savedPreferences) { const successState = getState(); const daemonSettings = selectDaemonSettings(successState); + if (savedPreferences !== null) { dispatch(doPopulateSharedUserState(savedPreferences)); // @if TARGET='app' @@ -652,7 +653,7 @@ export function doGetAndPopulatePreferences() { return true; } - function failCb(er) { + function failCb() { dispatch( doToast({ isError: true, @@ -662,7 +663,6 @@ export function doGetAndPopulatePreferences() { dispatch({ type: ACTIONS.SYNC_FATAL_ERROR, - error: er, }); return false; @@ -681,8 +681,6 @@ export function doHandleSyncComplete(error, hasNewData) { // we just got sync data, better update our channels dispatch(doFetchChannelListMine()); } - } else { - console.error('Error in doHandleSyncComplete', error); } }; } diff --git a/ui/redux/actions/comments.js b/ui/redux/actions/comments.js index 301fc2011..e1686cc18 100644 --- a/ui/redux/actions/comments.js +++ b/ui/redux/actions/comments.js @@ -3,15 +3,7 @@ import * as ACTIONS from 'constants/action_types'; import * as REACTION_TYPES from 'constants/reactions'; import * as PAGES from 'constants/pages'; import { SORT_BY, BLOCK_LEVEL } from 'constants/comment'; -import { - Lbry, - parseURI, - buildURI, - selectClaimsById, - selectClaimsByUri, - selectMyChannelClaims, - isURIEqual, -} from 'lbry-redux'; +import { Lbry, parseURI, buildURI, selectClaimsById, selectClaimsByUri, selectMyChannelClaims } from 'lbry-redux'; import { doToast, doSeeNotifications } from 'redux/actions/notifications'; import { makeSelectMyReactionsForComment, @@ -943,7 +935,7 @@ export function doFetchModBlockedList() { claimId: blockedChannel.blocked_channel_id, }); - if (!blockedList.find((blockedChannel) => isURIEqual(blockedChannel.channelUri, channelUri))) { + if (!blockedList.find((blockedChannel) => blockedChannel.channelUri === channelUri)) { blockedList.push({ channelUri, blockedAt: blockedChannel.blocked_at }); } diff --git a/ui/redux/reducers/comments.js b/ui/redux/reducers/comments.js index 5065fa9bd..88ff167ea 100644 --- a/ui/redux/reducers/comments.js +++ b/ui/redux/reducers/comments.js @@ -2,7 +2,8 @@ import * as ACTIONS from 'constants/action_types'; import { handleActions } from 'util/redux-utils'; import { BLOCK_LEVEL } from 'constants/comment'; -import { isURIEqual } from 'lbry-redux'; + +const IS_DEV = process.env.NODE_ENV !== 'production'; const defaultState: CommentsState = { commentById: {}, // commentId -> Comment @@ -275,15 +276,6 @@ export default handleActions( const totalRepliesByParentId = Object.assign({}, state.totalRepliesByParentId); const isLoadingByParentId = Object.assign({}, state.isLoadingByParentId); - if (!parentId) { - totalCommentsById[claimId] = totalItems; - topLevelTotalCommentsById[claimId] = totalFilteredItems; - topLevelTotalPagesById[claimId] = totalPages; - } else { - totalRepliesByParentId[parentId] = totalFilteredItems; - isLoadingByParentId[parentId] = false; - } - const commonUpdateAction = (comment, commentById, commentIds, index) => { // map the comment_ids to the new comments commentById[comment.comment_id] = comment; @@ -296,19 +288,46 @@ export default handleActions( // sort comments by their timestamp const commentIds = Array(comments.length); + // totalCommentsById[claimId] = totalItems; + // --> currently, this value is only correct when done via a top-level query. + // Until this is fixed, I'm moving it downwards to ** + // --- Top-level comments --- if (!parentId) { + totalCommentsById[claimId] = totalItems; // ** + + topLevelTotalCommentsById[claimId] = totalFilteredItems; + topLevelTotalPagesById[claimId] = totalPages; + + if (!topLevelCommentsById[claimId]) { + topLevelCommentsById[claimId] = []; + } + + const topLevelCommentIds = topLevelCommentsById[claimId]; + for (let i = 0; i < comments.length; ++i) { const comment = comments[i]; commonUpdateAction(comment, commentById, commentIds, i); - pushToArrayInObject(topLevelCommentsById, claimId, comment.comment_id); + + if (IS_DEV && comment['parent_id']) console.error('Invalid top-level comment:', comment); // eslint-disable-line + + if (!topLevelCommentIds.includes(comment.comment_id)) { + topLevelCommentIds.push(comment.comment_id); + } } } // --- Replies --- else { + totalRepliesByParentId[parentId] = totalFilteredItems; + isLoadingByParentId[parentId] = false; + for (let i = 0; i < comments.length; ++i) { const comment = comments[i]; commonUpdateAction(comment, commentById, commentIds, i); + + if (IS_DEV && !comment['parent_id']) console.error('Missing parent_id:', comment); // eslint-disable-line + if (IS_DEV && comment.parent_id !== parentId) console.error('Black sheep in the family?:', comment); // eslint-disable-line + pushToArrayInObject(repliesByParentId, parentId, comment.comment_id); } } @@ -787,7 +806,7 @@ export default handleActions( for (const commentId in commentById) { const comment = commentById[commentId]; - if (isURIEqual(blockedUri, comment.channel_url)) { + if (blockedUri === comment.channel_url) { delete commentById[comment.comment_id]; } } diff --git a/ui/redux/reducers/subscriptions.js b/ui/redux/reducers/subscriptions.js index 1c8d37a4c..5d35510c7 100644 --- a/ui/redux/reducers/subscriptions.js +++ b/ui/redux/reducers/subscriptions.js @@ -1,6 +1,6 @@ // @flow import * as ACTIONS from 'constants/action_types'; -import { parseURI, normalizeURI, isURIEqual, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux'; +import { parseURI, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux'; import { handleActions } from 'util/redux-utils'; const defaultState: SubscriptionState = { @@ -17,21 +17,21 @@ export default handleActions( const newSubscriptions: Array = state.subscriptions.slice(); let newFollowing: Array = state.following.slice(); // prevent duplicates in the sidebar - if (!newSubscriptions.some((sub) => isURIEqual(sub.uri, newSubscription.uri))) { + if (!newSubscriptions.some((sub) => sub.uri === newSubscription.uri)) { // $FlowFixMe newSubscriptions.unshift(newSubscription); } - if (!newFollowing.some((sub) => isURIEqual(sub.uri, newSubscription.uri))) { + if (!newFollowing.some((sub) => sub.uri === newSubscription.uri)) { newFollowing.unshift({ uri: newSubscription.uri, notificationsDisabled: newSubscription.notificationsDisabled, }); } else { newFollowing = newFollowing.map((following) => { - if (isURIEqual(following.uri, newSubscription.uri)) { + if (following.uri === newSubscription.uri) { return { - uri: normalizeURI(newSubscription.uri), + uri: newSubscription.uri, notificationsDisabled: newSubscription.notificationsDisabled, }; } else { diff --git a/ui/redux/selectors/subscriptions.js b/ui/redux/selectors/subscriptions.js index 1b43ae0f4..19f8e9063 100644 --- a/ui/redux/selectors/subscriptions.js +++ b/ui/redux/selectors/subscriptions.js @@ -5,7 +5,6 @@ import { makeSelectChannelForClaimUri, parseURI, makeSelectClaimForUri, - isURIEqual, } from 'lbry-redux'; import { swapKeyAndValue } from 'util/swap-json'; @@ -113,7 +112,7 @@ export const makeSelectIsSubscribed = (uri) => makeSelectClaimForUri(uri), (subscriptions, channelUri, claim) => { if (channelUri) { - return subscriptions.some((sub) => isURIEqual(sub.uri, channelUri)); + return subscriptions.some((sub) => sub.uri === channelUri); } // If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already @@ -124,11 +123,11 @@ export const makeSelectIsSubscribed = (uri) => if (isChannel && claim) { const uri = claim.permanent_url; - return subscriptions.some((sub) => isURIEqual(sub.uri, uri)); + return subscriptions.some((sub) => sub.uri === uri); } if (isChannel && !claim) { - return subscriptions.some((sub) => isURIEqual(sub.uri, uri)); + return subscriptions.some((sub) => sub.uri === uri); } return false; diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index 2f8fd6d93..efbefa673 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -226,6 +226,11 @@ } } +.card__title-actions--link { + margin-top: var(--spacing-xs); + margin-right: var(--spacing-xs); +} + .card__title-actions--small { padding: 0; } diff --git a/ui/scss/component/_claim-list.scss b/ui/scss/component/_claim-list.scss index 65db87481..cc70fd551 100644 --- a/ui/scss/component/_claim-list.scss +++ b/ui/scss/component/_claim-list.scss @@ -242,6 +242,10 @@ justify-content: space-between; flex-wrap: wrap; align-items: center; + + .claim-preview__actions { + margin-left: var(--spacing-m); + } } @media (max-width: $breakpoint-xsmall) { @@ -705,10 +709,6 @@ } } -.claim-preview__active { - background-color: var(--color-card-background-highlighted); -} - .claim-preview__live { .claim-preview__file-property-overlay { opacity: 1; // The original 0.7 is not visible over bright thumbnails diff --git a/ui/scss/component/_collection.scss b/ui/scss/component/_collection.scss index cd050192f..f67883022 100644 --- a/ui/scss/component/_collection.scss +++ b/ui/scss/component/_collection.scss @@ -4,6 +4,11 @@ position: relative; } +.collection__subtitle { + display: flex; + margin-bottom: var(--spacing-s); +} + .collection-preview__items { display: flex; align-items: center; diff --git a/yarn.lock b/yarn.lock index e4359b4b6..4f63b9351 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7283,9 +7283,9 @@ lazy-val@^1.0.4: yargs "^13.2.2" zstd-codec "^0.1.1" -lbry-redux@lbryio/lbry-redux#a327385cdf71568dbd15a17f3dcf5f4b83e0966d: +lbry-redux@lbryio/lbry-redux#8f66a2fe7c84d4587ec95698bce9f3e4360f8e88: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/a327385cdf71568dbd15a17f3dcf5f4b83e0966d" + resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/8f66a2fe7c84d4587ec95698bce9f3e4360f8e88" dependencies: proxy-polyfill "0.1.6" reselect "^3.0.0"