Show channel thumbnails in side menu

This commit is contained in:
Marios Vladimerou 2021-06-09 20:20:19 +01:00 committed by jessopb
parent 72a4ed107b
commit d8b40931f3
4 changed files with 65 additions and 24 deletions

View file

@ -5,7 +5,14 @@ import { selectGetSyncErrorMessage, selectSyncFatalError } from 'redux/selectors
import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user'; import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user';
import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectUnclaimedRewards } from 'redux/selectors/rewards'; 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 { import {
makeSelectClientSetting, makeSelectClientSetting,
selectLanguage, selectLanguage,
@ -47,6 +54,7 @@ const select = (state) => ({
syncFatalError: selectSyncFatalError(state), syncFatalError: selectSyncFatalError(state),
activeChannelClaim: selectActiveChannelClaim(state), activeChannelClaim: selectActiveChannelClaim(state),
myChannelUrls: selectMyChannelUrls(state), myChannelUrls: selectMyChannelUrls(state),
subscriptions: selectSubscriptions(state),
}); });
const perform = (dispatch) => ({ const perform = (dispatch) => ({
@ -63,6 +71,7 @@ const perform = (dispatch) => ({
setActiveChannelIfNotSet: () => dispatch(doSetActiveChannel()), setActiveChannelIfNotSet: () => dispatch(doSetActiveChannel()),
setIncognito: () => dispatch(doSetIncognito()), setIncognito: () => dispatch(doSetIncognito()),
fetchModBlockedList: () => dispatch(doFetchModBlockedList()), fetchModBlockedList: () => dispatch(doFetchModBlockedList()),
resolveUris: (uris) => dispatch(doResolveUris(uris)),
}); });
export default hot(connect(select, perform)(App)); export default hot(connect(select, perform)(App));

View file

@ -1,6 +1,6 @@
// @flow // @flow
import * as PAGES from 'constants/pages'; 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 classnames from 'classnames';
import analytics from 'analytics'; import analytics from 'analytics';
import { buildURI, parseURI } from 'lbry-redux'; import { buildURI, parseURI } from 'lbry-redux';
@ -84,9 +84,11 @@ type Props = {
syncFatalError: boolean, syncFatalError: boolean,
activeChannelClaim: ?ChannelClaim, activeChannelClaim: ?ChannelClaim,
myChannelUrls: ?Array<string>, myChannelUrls: ?Array<string>,
subscriptions: Array<Subscription>,
setActiveChannelIfNotSet: () => void, setActiveChannelIfNotSet: () => void,
setIncognito: (boolean) => void, setIncognito: (boolean) => void,
fetchModBlockedList: () => void, fetchModBlockedList: () => void,
resolveUris: (Array<string>) => void,
}; };
function App(props: Props) { function App(props: Props) {
@ -119,6 +121,8 @@ function App(props: Props) {
setActiveChannelIfNotSet, setActiveChannelIfNotSet,
setIncognito, setIncognito,
fetchModBlockedList, fetchModBlockedList,
resolveUris,
subscriptions,
} = props; } = props;
const appRef = useRef(); const appRef = useRef();
@ -136,6 +140,8 @@ function App(props: Props) {
// @endif // @endif
const { pathname, hash, search } = props.location; const { pathname, hash, search } = props.location;
const [upgradeNagClosed, setUpgradeNagClosed] = useState(false); const [upgradeNagClosed, setUpgradeNagClosed] = useState(false);
const [resolvedSubscriptions, setResolvedSubscriptions] = useState(false);
const [sidebarOpen] = usePersistedState('sidebar', true);
const showUpgradeButton = const showUpgradeButton =
(autoUpdateDownloaded || (process.platform === 'linux' && isUpgradeAvailable)) && !upgradeNagClosed; (autoUpdateDownloaded || (process.platform === 'linux' && isUpgradeAvailable)) && !upgradeNagClosed;
// referral claiming // referral claiming
@ -150,6 +156,7 @@ function App(props: Props) {
const hasNoChannels = myChannelUrls && myChannelUrls.length === 0; const hasNoChannels = myChannelUrls && myChannelUrls.length === 0;
const shouldMigrateLanguage = LANGUAGE_MIGRATIONS[language]; const shouldMigrateLanguage = LANGUAGE_MIGRATIONS[language];
const hasActiveChannelClaim = activeChannelClaim !== undefined; const hasActiveChannelClaim = activeChannelClaim !== undefined;
const isPersonalized = !IS_WEB || hasVerifiedEmail;
let uri; let uri;
try { try {
@ -338,6 +345,16 @@ function App(props: Props) {
} }
}, [hasVerifiedEmail, signIn, hasSignedIn]); }, [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' // @if TARGET='web'
useDegradedPerformance(setLbryTvApiStatus, user); useDegradedPerformance(setLbryTvApiStatus, user);
// @endif // @endif

View file

@ -8,6 +8,7 @@ import classnames from 'classnames';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import NotificationBubble from 'component/notificationBubble'; import NotificationBubble from 'component/notificationBubble';
import I18nMessage from 'component/i18nMessage'; 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'; import { PINNED_LABEL_1, PINNED_URI_1, PINNED_URI_2, PINNED_LABEL_2, SIMPLE_SITE, DOMAIN } from 'config';
// @if TARGET='app' // @if TARGET='app'
import { IS_MAC } from 'component/app/view'; import { IS_MAC } from 'component/app/view';
@ -362,15 +363,8 @@ function SideNavigation(props: Props) {
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && ( {sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
<ul className="navigation__secondary navigation-links"> <ul className="navigation__secondary navigation-links">
{subscriptions.map(({ uri, channelName }, index) => ( {subscriptions.map((subscription) => (
<li key={uri} className="navigation-link__wrapper"> <SubscriptionListItem key={subscription.uri} subscription={subscription} />
<Button
navigate={uri}
label={channelName}
className="navigation-link"
activeClass="navigation-link--active"
/>
</li>
))} ))}
</ul> </ul>
)} )}
@ -445,15 +439,8 @@ function SideNavigation(props: Props) {
</ul> </ul>
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && ( {sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
<ul className="navigation__secondary navigation-links"> <ul className="navigation__secondary navigation-links">
{subscriptions.map(({ uri, channelName }, index) => ( {subscriptions.map((subscription) => (
<li key={uri} className="navigation-link__wrapper"> <SubscriptionListItem key={subscription.uri} subscription={subscription} />
<Button
navigate={uri}
label={channelName}
className="navigation-link"
activeClass="navigation-link--active"
/>
</li>
))} ))}
</ul> </ul>
)} )}
@ -484,4 +471,22 @@ function SideNavigation(props: Props) {
); );
} }
function SubscriptionListItem({ subscription }: { subscription: Subscription }) {
const { uri, channelName } = subscription;
return (
<li className="navigation-link__wrapper">
<Button
navigate={uri}
className="navigation-link navigation-link--with-thumbnail"
activeClass="navigation-link--active"
>
<ChannelThumbnail uri={uri} />
<span dir="auto" className="button__label">
{channelName}
</span>
</Button>
</li>
);
}
export default SideNavigation; export default SideNavigation;

View file

@ -104,6 +104,16 @@
flex-direction: column; 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), &:hover:not(.navigation-link--active),
&:focus { &:focus {
@extend .navigation-link--highlighted; @extend .navigation-link--highlighted;
@ -114,8 +124,8 @@
margin-bottom: 0; margin-bottom: 0;
.icon { .icon {
height: 1rem; height: 1.5rem;
width: 1rem; width: 1.5rem;
} }
.button__content { .button__content {
@ -201,8 +211,8 @@
margin-bottom: 0; margin-bottom: 0;
.icon { .icon {
height: 1rem; height: 1.5rem;
width: 1rem; width: 1.5rem;
} }
.button__content { .button__content {