Show channel thumbnails in side menu
This commit is contained in:
parent
72a4ed107b
commit
d8b40931f3
4 changed files with 65 additions and 24 deletions
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue