diff --git a/ui/component/app/index.js b/ui/component/app/index.js index 16b0cd288..940ebb7ef 100644 --- a/ui/component/app/index.js +++ b/ui/component/app/index.js @@ -5,7 +5,14 @@ import { selectGetSyncErrorMessage, selectSyncFatalError } from 'redux/selectors import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user'; import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectUnclaimedRewards } from 'redux/selectors/rewards'; -import { doFetchChannelListMine, doFetchCollectionListMine, SETTINGS, selectMyChannelUrls } from 'lbry-redux'; +import { + doFetchChannelListMine, + doFetchCollectionListMine, + SETTINGS, + selectMyChannelUrls, + doResolveUris, +} from 'lbry-redux'; +import { selectSubscriptions } from 'redux/selectors/subscriptions'; import { makeSelectClientSetting, selectLanguage, @@ -47,6 +54,7 @@ const select = (state) => ({ syncFatalError: selectSyncFatalError(state), activeChannelClaim: selectActiveChannelClaim(state), myChannelUrls: selectMyChannelUrls(state), + subscriptions: selectSubscriptions(state), }); const perform = (dispatch) => ({ @@ -63,6 +71,7 @@ const perform = (dispatch) => ({ setActiveChannelIfNotSet: () => dispatch(doSetActiveChannel()), setIncognito: () => dispatch(doSetIncognito()), fetchModBlockedList: () => dispatch(doFetchModBlockedList()), + resolveUris: (uris) => dispatch(doResolveUris(uris)), }); export default hot(connect(select, perform)(App)); diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index e4ff246cf..9b2c64304 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -1,6 +1,6 @@ // @flow import * as PAGES from 'constants/pages'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState, useLayoutEffect } from 'react'; import classnames from 'classnames'; import analytics from 'analytics'; import { buildURI, parseURI } from 'lbry-redux'; @@ -84,9 +84,11 @@ type Props = { syncFatalError: boolean, activeChannelClaim: ?ChannelClaim, myChannelUrls: ?Array, + subscriptions: Array, setActiveChannelIfNotSet: () => void, setIncognito: (boolean) => void, fetchModBlockedList: () => void, + resolveUris: (Array) => void, }; function App(props: Props) { @@ -119,6 +121,8 @@ function App(props: Props) { setActiveChannelIfNotSet, setIncognito, fetchModBlockedList, + resolveUris, + subscriptions, } = props; const appRef = useRef(); @@ -136,6 +140,8 @@ function App(props: Props) { // @endif const { pathname, hash, search } = props.location; const [upgradeNagClosed, setUpgradeNagClosed] = useState(false); + const [resolvedSubscriptions, setResolvedSubscriptions] = useState(false); + const [sidebarOpen] = usePersistedState('sidebar', true); const showUpgradeButton = (autoUpdateDownloaded || (process.platform === 'linux' && isUpgradeAvailable)) && !upgradeNagClosed; // referral claiming @@ -150,6 +156,7 @@ function App(props: Props) { const hasNoChannels = myChannelUrls && myChannelUrls.length === 0; const shouldMigrateLanguage = LANGUAGE_MIGRATIONS[language]; const hasActiveChannelClaim = activeChannelClaim !== undefined; + const isPersonalized = !IS_WEB || hasVerifiedEmail; let uri; try { @@ -338,6 +345,16 @@ function App(props: Props) { } }, [hasVerifiedEmail, signIn, hasSignedIn]); + // batch resolve subscriptions to be used by the sideNavigation component. + // add it here so that it only resolves the first time, despite route changes. + // useLayoutEffect because it has to be executed before the sideNavigation component requests them + useLayoutEffect(() => { + if (sidebarOpen && isPersonalized && subscriptions && !resolvedSubscriptions) { + setResolvedSubscriptions(true); + resolveUris(subscriptions.map((sub) => sub.uri)); + } + }, [sidebarOpen, isPersonalized, resolvedSubscriptions, subscriptions, resolveUris, setResolvedSubscriptions]); + // @if TARGET='web' useDegradedPerformance(setLbryTvApiStatus, user); // @endif diff --git a/ui/component/sideNavigation/view.jsx b/ui/component/sideNavigation/view.jsx index 2397a1e25..c32d68fb0 100644 --- a/ui/component/sideNavigation/view.jsx +++ b/ui/component/sideNavigation/view.jsx @@ -8,6 +8,7 @@ import classnames from 'classnames'; import Icon from 'component/common/icon'; import NotificationBubble from 'component/notificationBubble'; import I18nMessage from 'component/i18nMessage'; +import ChannelThumbnail from 'component/channelThumbnail'; import { PINNED_LABEL_1, PINNED_URI_1, PINNED_URI_2, PINNED_LABEL_2, SIMPLE_SITE, DOMAIN } from 'config'; // @if TARGET='app' import { IS_MAC } from 'component/app/view'; @@ -362,15 +363,8 @@ function SideNavigation(props: Props) { {sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
    - {subscriptions.map(({ uri, channelName }, index) => ( -
  • -
  • + {subscriptions.map((subscription) => ( + ))}
)} @@ -445,15 +439,8 @@ function SideNavigation(props: Props) { {sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
    - {subscriptions.map(({ uri, channelName }, index) => ( -
  • -
  • + {subscriptions.map((subscription) => ( + ))}
)} @@ -484,4 +471,22 @@ function SideNavigation(props: Props) { ); } +function SubscriptionListItem({ subscription }: { subscription: Subscription }) { + const { uri, channelName } = subscription; + return ( +
  • + +
  • + ); +} + export default SideNavigation; diff --git a/ui/scss/component/_navigation.scss b/ui/scss/component/_navigation.scss index 93d4cc59b..c26eb8940 100644 --- a/ui/scss/component/_navigation.scss +++ b/ui/scss/component/_navigation.scss @@ -104,6 +104,16 @@ flex-direction: column; } + &.navigation-link--with-thumbnail .button__content { + flex-direction: row; + + .channel-thumbnail { + @include handleChannelGif(1.5rem); + flex-shrink: 0; + margin-right: var(--spacing-s); + } + } + &:hover:not(.navigation-link--active), &:focus { @extend .navigation-link--highlighted; @@ -114,8 +124,8 @@ margin-bottom: 0; .icon { - height: 1rem; - width: 1rem; + height: 1.5rem; + width: 1.5rem; } .button__content { @@ -201,8 +211,8 @@ margin-bottom: 0; .icon { - height: 1rem; - width: 1rem; + height: 1.5rem; + width: 1.5rem; } .button__content {