Attempt to speed up sidebar menu for mobile (#283)

* Exclude default homepage data at compile time

The youtuber IDs alone is pretty huge, and is unused in the `CUSTOM_HOMEPAGE=true` configuration.

* Remove Desktop items and other cleanup

- Moved constants out of the component.
- Remove SIMPLE_SITE check.
- Remove Desktop-only items

* Sidebar: limit subscription and tag section

## Issue
Too slow for huge lists

## Change
Limit to 10 initially, and load everything on "Show more"

* Fix makeSelectThumbnailForUri

- Fix memo
- Expose function to extract directly from claim if client already have it.
This commit is contained in:
infinite-persistence 2021-11-12 23:59:11 +08:00 committed by GitHub
parent 529a9cbc40
commit 6d217dbc50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 190 additions and 197 deletions

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, selectThumbnailForUri,
makeSelectCoverForUri, makeSelectCoverForUri,
makeSelectMetadataItemForUri, makeSelectMetadataItemForUri,
makeSelectAmountForUri, makeSelectAmountForUri,
@ -22,7 +22,7 @@ import ChannelForm from './view';
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnailUrl: makeSelectThumbnailForUri(props.uri)(state), thumbnailUrl: selectThumbnailForUri(state, props.uri),
coverUrl: makeSelectCoverForUri(props.uri)(state), coverUrl: makeSelectCoverForUri(props.uri)(state),
description: makeSelectMetadataItemForUri(props.uri, 'description')(state), description: makeSelectMetadataItemForUri(props.uri, 'description')(state),
website: makeSelectMetadataItemForUri(props.uri, 'website_url')(state), website: makeSelectMetadataItemForUri(props.uri, 'website_url')(state),

View file

@ -1,11 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectThumbnailForUri, makeSelectClaimForUri, makeSelectIsUriResolving } from 'redux/selectors/claims'; import { selectThumbnailForUri, selectClaimForUri, makeSelectIsUriResolving } from 'redux/selectors/claims';
import { doResolveUri } from 'redux/actions/claims'; import { doResolveUri } from 'redux/actions/claims';
import ChannelThumbnail from './view'; import ChannelThumbnail from './view';
const select = (state, props) => ({ const select = (state, props) => ({
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: selectThumbnailForUri(state, props.uri),
claim: makeSelectClaimForUri(props.uri)(state), claim: selectClaimForUri(state, props.uri),
isResolving: makeSelectIsUriResolving(props.uri)(state), isResolving: makeSelectIsUriResolving(props.uri)(state),
}); });

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { import {
makeSelectClaimForUri, makeSelectClaimForUri,
makeSelectIsUriResolving, makeSelectIsUriResolving,
makeSelectThumbnailForUri, getThumbnailFromClaim,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectChannelForClaimUri, makeSelectChannelForClaimUri,
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
@ -28,7 +28,7 @@ const select = (state, props) => {
date: props.uri && selectDateForUri(state, props.uri), date: props.uri && selectDateForUri(state, props.uri),
channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state), channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state),
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state), thumbnail: getThumbnailFromClaim(claim),
title: props.uri && makeSelectTitleForUri(props.uri)(state), title: props.uri && makeSelectTitleForUri(props.uri)(state),
banState: selectBanStateForUri(state, props.uri), banState: selectBanStateForUri(state, props.uri),
showMature: selectShowMatureContent(state), showMature: selectShowMatureContent(state),

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, selectThumbnailForUri,
makeSelectMetadataItemForUri, makeSelectMetadataItemForUri,
makeSelectAmountForUri, makeSelectAmountForUri,
makeSelectClaimForUri, makeSelectClaimForUri,
@ -26,7 +26,7 @@ import { doSetActiveChannel, doSetIncognito } from 'redux/actions/app';
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnailUrl: makeSelectThumbnailForUri(props.uri)(state), thumbnailUrl: selectThumbnailForUri(state, props.uri),
description: makeSelectMetadataItemForUri(props.uri, 'description')(state), description: makeSelectMetadataItemForUri(props.uri, 'description')(state),
tags: makeSelectMetadataItemForUri(props.uri, 'tags')(state), tags: makeSelectMetadataItemForUri(props.uri, 'tags')(state),
locations: makeSelectMetadataItemForUri(props.uri, 'locations')(state), locations: makeSelectMetadataItemForUri(props.uri, 'locations')(state),

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
makeSelectIsUriResolving, makeSelectIsUriResolving,
makeSelectThumbnailForUri, getThumbnailFromClaim,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectChannelForClaimUri, makeSelectChannelForClaimUri,
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
@ -40,7 +40,7 @@ const select = (state, props) => {
isResolvingCollectionClaims: makeSelectIsResolvingCollectionForId(collectionId)(state), isResolvingCollectionClaims: makeSelectIsResolvingCollectionForId(collectionId)(state),
channelClaim: collectionUri && makeSelectChannelForClaimUri(collectionUri)(state), channelClaim: collectionUri && makeSelectChannelForClaimUri(collectionUri)(state),
isResolvingUri: collectionUri && makeSelectIsUriResolving(collectionUri)(state), isResolvingUri: collectionUri && makeSelectIsUriResolving(collectionUri)(state),
thumbnail: collectionUri && makeSelectThumbnailForUri(collectionUri)(state), thumbnail: getThumbnailFromClaim(claim),
title: collectionUri && makeSelectTitleForUri(collectionUri)(state), title: collectionUri && makeSelectTitleForUri(collectionUri)(state),
blackListedOutpoints: selectBlackListedOutpoints(state), blackListedOutpoints: selectBlackListedOutpoints(state),
filteredOutpoints: selectFilteredOutpoints(state), filteredOutpoints: selectFilteredOutpoints(state),

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { import {
selectStakedLevelForChannelUri, selectStakedLevelForChannelUri,
makeSelectClaimForUri, makeSelectClaimForUri,
makeSelectThumbnailForUri, selectThumbnailForUri,
selectHasChannels, selectHasChannels,
} from 'redux/selectors/claims'; } from 'redux/selectors/claims';
import { doCommentUpdate, doCommentList } from 'redux/actions/comments'; import { doCommentUpdate, doCommentList } from 'redux/actions/comments';
@ -26,7 +26,7 @@ const select = (state, props) => {
return { return {
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state), thumbnail: props.authorUri && selectThumbnailForUri(state, props.authorUri),
channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state), channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state),
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true, commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
othersReacts: selectOthersReactsForComment(state, reactionKey), othersReacts: selectOthersReactsForComment(state, reactionKey),

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectThumbnailForUri, makeSelectClaimForUri } from 'redux/selectors/claims'; import { selectThumbnailForUri, makeSelectClaimForUri } from 'redux/selectors/claims';
import { doResolveUri } from 'redux/actions/claims'; import { doResolveUri } from 'redux/actions/claims';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import { doFetchCostInfoForUri, makeSelectCostInfoForUri } from 'lbryinc'; import { doFetchCostInfoForUri, makeSelectCostInfoForUri } from 'lbryinc';
@ -11,7 +11,7 @@ import { makeSelectFileRenderModeForUri } from 'redux/selectors/content';
import ChannelThumbnail from './view'; import ChannelThumbnail from './view';
const select = (state, props) => ({ const select = (state, props) => ({
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: selectThumbnailForUri(state, props.uri),
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
floatingPlayerEnabled: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state), floatingPlayerEnabled: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state),
costInfo: makeSelectCostInfoForUri(props.uri)(state), costInfo: makeSelectCostInfoForUri(props.uri)(state),

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectDownloadPathForUri, makeSelectStreamingUrlForUri } from 'redux/selectors/file_info'; import { makeSelectDownloadPathForUri, makeSelectStreamingUrlForUri } from 'redux/selectors/file_info';
import { makeSelectClaimForUri, makeSelectThumbnailForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims'; import { makeSelectClaimForUri, selectThumbnailForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings';
import { makeSelectFileRenderModeForUri, makeSelectFileExtensionForUri } from 'redux/selectors/content'; import { makeSelectFileRenderModeForUri, makeSelectFileExtensionForUri } from 'redux/selectors/content';
@ -11,7 +11,7 @@ const select = (state, props) => {
return { return {
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state), currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: selectThumbnailForUri(state, props.uri),
contentType: makeSelectContentTypeForUri(props.uri)(state), contentType: makeSelectContentTypeForUri(props.uri)(state),
downloadPath: makeSelectDownloadPathForUri(props.uri)(state), downloadPath: makeSelectDownloadPathForUri(props.uri)(state),
fileExtension: makeSelectFileExtensionForUri(props.uri)(state), fileExtension: makeSelectFileExtensionForUri(props.uri)(state),

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doPlayUri, doSetPlayingUri, doSetPrimaryUri } from 'redux/actions/content'; import { doPlayUri, doSetPlayingUri, doSetPrimaryUri } from 'redux/actions/content';
import { makeSelectThumbnailForUri, makeSelectClaimForUri, makeSelectClaimWasPurchased } from 'redux/selectors/claims'; import { selectThumbnailForUri, makeSelectClaimForUri, makeSelectClaimWasPurchased } from 'redux/selectors/claims';
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info'; import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import * as COLLECTIONS_CONSTS from 'constants/collections'; import * as COLLECTIONS_CONSTS from 'constants/collections';
@ -23,7 +23,7 @@ const select = (state, props) => {
const collectionId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID); const collectionId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID);
return { return {
claimThumbnail: makeSelectThumbnailForUri(props.uri)(state), claimThumbnail: selectThumbnailForUri(state, props.uri),
fileInfo: makeSelectFileInfoForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state),
obscurePreview: makeSelectShouldObscurePreview(props.uri)(state), obscurePreview: makeSelectShouldObscurePreview(props.uri)(state),
isPlaying: makeSelectIsPlaying(props.uri)(state), isPlaying: makeSelectIsPlaying(props.uri)(state),

View file

@ -2,14 +2,14 @@ import { connect } from 'react-redux';
import { import {
makeSelectClaimForUri, makeSelectClaimForUri,
makeSelectTagInClaimOrChannelForUri, makeSelectTagInClaimOrChannelForUri,
makeSelectThumbnailForUri, selectThumbnailForUri,
} from 'redux/selectors/claims'; } from 'redux/selectors/claims';
import LivestreamLayout from './view'; import LivestreamLayout from './view';
import { DISABLE_COMMENTS_TAG } from 'constants/tags'; import { DISABLE_COMMENTS_TAG } from 'constants/tags';
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: selectThumbnailForUri(state, props.uri),
chatDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state), chatDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state),
}); });

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { import {
selectClaimIsMine, selectClaimIsMine,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, getThumbnailFromClaim,
selectClaimForUri, selectClaimForUri,
makeSelectIsUriResolving, makeSelectIsUriResolving,
makeSelectMetadataItemForUri, makeSelectMetadataItemForUri,
@ -18,7 +18,7 @@ const select = (state, props) => {
uri: props.uri, uri: props.uri,
claim, claim,
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: getThumbnailFromClaim(claim),
description: makeSelectMetadataItemForUri(props.uri, 'description')(state), description: makeSelectMetadataItemForUri(props.uri, 'description')(state),
channelIsMine: selectClaimIsMine(state, claim), channelIsMine: selectClaimIsMine(state, claim),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state), isResolvingUri: makeSelectIsUriResolving(props.uri)(state),

View file

@ -11,16 +11,19 @@ import NotificationBubble from 'component/notificationBubble';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import ChannelThumbnail from 'component/channelThumbnail'; import ChannelThumbnail from 'component/channelThumbnail';
import { GetLinksData } from 'util/buildHomepage'; import { GetLinksData } from 'util/buildHomepage';
import { import { DOMAIN, ENABLE_UI_NOTIFICATIONS, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config';
SIMPLE_SITE,
DOMAIN, const FOLLOWED_ITEM_INITIAL_LIMIT = 10;
ENABLE_UI_NOTIFICATIONS,
ENABLE_NO_SOURCE_CLAIMS, type SideNavLink = {
CHANNEL_STAKED_LEVEL_LIVESTREAM, title: string,
} from 'config'; link?: string,
// @if TARGET='app' route?: string,
import { IS_MAC } from 'component/app/view'; onClick?: () => any,
// @endif icon: string,
extra?: Node,
hideForUnauth?: boolean,
};
const HOME = { const HOME = {
title: 'Home', title: 'Home',
@ -37,6 +40,39 @@ const RECENT_FROM_FOLLOWING = {
icon: ICONS.SUBSCRIBE, icon: ICONS.SUBSCRIBE,
}; };
const PLAYLISTS = {
title: 'Lists',
link: `/$/${PAGES.LISTS}`,
icon: ICONS.STACK,
hideForUnauth: true,
};
const UNAUTH_LINKS: Array<SideNavLink> = [
{
title: 'Log In',
link: `/$/${PAGES.AUTH_SIGNIN}`,
icon: ICONS.SIGN_IN,
},
{
title: 'Sign Up',
link: `/$/${PAGES.AUTH}`,
icon: ICONS.SIGN_UP,
},
{
title: 'Settings',
link: `/$/${PAGES.SETTINGS}`,
icon: ICONS.SETTINGS,
},
{
title: 'Help',
link: `/$/${PAGES.HELP}`,
icon: ICONS.HELP,
},
];
// ****************************************************************************
// ****************************************************************************
type Props = { type Props = {
subscriptions: Array<Subscription>, subscriptions: Array<Subscription>,
followedTags: Array<Tag>, followedTags: Array<Tag>,
@ -55,16 +91,6 @@ type Props = {
activeChannelStakedLevel: number, activeChannelStakedLevel: number,
}; };
type SideNavLink = {
title: string,
link?: string,
route?: string,
onClick?: () => any,
icon: string,
extra?: Node,
hideForUnauth?: boolean,
};
function SideNavigation(props: Props) { function SideNavigation(props: Props) {
const { const {
subscriptions, subscriptions,
@ -85,26 +111,6 @@ function SideNavigation(props: Props) {
const EXTRA_SIDEBAR_LINKS = GetLinksData(homepageData).map(({ pinnedUrls, ...theRest }) => theRest); const EXTRA_SIDEBAR_LINKS = GetLinksData(homepageData).map(({ pinnedUrls, ...theRest }) => theRest);
const FULL_LINKS: Array<SideNavLink> = [
{
title: 'Your Tags',
link: `/$/${PAGES.TAGS_FOLLOWING}`,
icon: ICONS.TAG,
hideForUnauth: true,
},
{
title: 'Discover',
link: `/$/${PAGES.DISCOVER}`,
icon: ICONS.DISCOVER,
},
{
title: IS_WEB ? 'Purchased' : 'Library',
link: `/$/${PAGES.LIBRARY}`,
icon: ICONS.PURCHASED,
hideForUnauth: true,
},
];
const MOBILE_LINKS: Array<SideNavLink> = [ const MOBILE_LINKS: Array<SideNavLink> = [
{ {
title: 'Notifications', title: 'Notifications',
@ -181,56 +187,17 @@ function SideNavigation(props: Props) {
}, },
]; ];
const UNAUTH_LINKS: Array<SideNavLink> = [
{
title: 'Log In',
link: `/$/${PAGES.AUTH_SIGNIN}`,
icon: ICONS.SIGN_IN,
},
{
title: 'Sign Up',
link: `/$/${PAGES.AUTH}`,
icon: ICONS.SIGN_UP,
},
{
title: 'Settings',
link: `/$/${PAGES.SETTINGS}`,
icon: ICONS.SETTINGS,
},
{
title: 'Help',
link: `/$/${PAGES.HELP}`,
icon: ICONS.HELP,
},
];
const notificationsEnabled = ENABLE_UI_NOTIFICATIONS || (user && user.experimental_ui); const notificationsEnabled = ENABLE_UI_NOTIFICATIONS || (user && user.experimental_ui);
const isAuthenticated = Boolean(email); const isAuthenticated = Boolean(email);
// SIDE LINKS: FOLLOWING, HOME, [FULL,] [EXTRA] // SIDE LINKS: FOLLOWING, HOME, [FULL,] [EXTRA]
let SIDE_LINKS: Array<SideNavLink> = []; let SIDE_LINKS: Array<SideNavLink> = [];
SIDE_LINKS.push(HOME); SIDE_LINKS.push(HOME);
SIDE_LINKS.push(RECENT_FROM_FOLLOWING); SIDE_LINKS.push(RECENT_FROM_FOLLOWING);
if (!SIMPLE_SITE) { SIDE_LINKS.push(PLAYLISTS);
FULL_LINKS.push({
title: 'Lists',
link: `/$/${PAGES.LISTS}`,
icon: ICONS.STACK,
hideForUnauth: true,
});
}
if (!SIMPLE_SITE) {
SIDE_LINKS.push(...FULL_LINKS);
} else if (SIMPLE_SITE) {
SIDE_LINKS.push({
title: 'Lists',
link: `/$/${PAGES.LISTS}`,
icon: ICONS.STACK,
hideForUnauth: true,
});
}
if (SIMPLE_SITE && EXTRA_SIDEBAR_LINKS) { if (EXTRA_SIDEBAR_LINKS) {
// $FlowFixMe // $FlowFixMe
SIDE_LINKS.push(...EXTRA_SIDEBAR_LINKS); SIDE_LINKS.push(...EXTRA_SIDEBAR_LINKS);
@ -251,6 +218,9 @@ function SideNavigation(props: Props) {
); );
const [pulseLibrary, setPulseLibrary] = React.useState(false); const [pulseLibrary, setPulseLibrary] = React.useState(false);
const [expandSubscriptions, setExpandSubscriptions] = React.useState(false);
const [expandTags, setExpandTags] = React.useState(false);
const isPersonalized = !IS_WEB || isAuthenticated; const isPersonalized = !IS_WEB || isAuthenticated;
const isAbsolute = isOnFilePage || isMediumScreen; const isAbsolute = isOnFilePage || isMediumScreen;
const microNavigation = !sidebarOpen || isMediumScreen; const microNavigation = !sidebarOpen || isMediumScreen;
@ -264,6 +234,65 @@ function SideNavigation(props: Props) {
}) })
: UNAUTH_LINKS; : UNAUTH_LINKS;
const showSubscriptionSection = sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0;
const showTagSection = sidebarOpen && isPersonalized && followedTags && followedTags.length;
let displayedSubscriptions = subscriptions;
if (showSubscriptionSection && subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT && !expandSubscriptions) {
displayedSubscriptions = subscriptions.slice(0, FOLLOWED_ITEM_INITIAL_LIMIT);
}
let displayedFollowedTags = followedTags;
if (showTagSection && followedTags.length > FOLLOWED_ITEM_INITIAL_LIMIT && !expandTags) {
displayedFollowedTags = followedTags.slice(0, FOLLOWED_ITEM_INITIAL_LIMIT);
}
function getSubscriptionSection() {
if (showSubscriptionSection) {
return (
<>
<ul className="navigation__secondary navigation-links">
{displayedSubscriptions.map((subscription) => (
<SubscriptionListItem key={subscription.uri} subscription={subscription} />
))}
</ul>
{subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT && (
<Button
label={expandSubscriptions ? __('Show less') : __('Show more')}
className="navigation-link"
onClick={() => setExpandSubscriptions(!expandSubscriptions)}
/>
)}
</>
);
}
return null;
}
function getFollowedTagsSection() {
if (showTagSection) {
return (
<>
<ul className="navigation__secondary navigation-links navigation-links--small">
{displayedFollowedTags.map(({ name }, key) => (
<li key={name} className="navigation-link__wrapper">
<Button navigate={`/$/discover?t=${name}`} label={`#${name}`} className="navigation-link" />
</li>
))}
</ul>
{followedTags.length > FOLLOWED_ITEM_INITIAL_LIMIT && (
<Button
label={expandTags ? __('Show less') : __('Show more')}
className="navigation-link"
onClick={() => setExpandTags(!expandTags)}
/>
)}
</>
);
}
return null;
}
React.useEffect(() => { React.useEffect(() => {
if (purchaseSuccess) { if (purchaseSuccess) {
setPulseLibrary(true); setPulseLibrary(true);
@ -316,11 +345,9 @@ function SideNavigation(props: Props) {
<li className="navigation-link"> <li className="navigation-link">
<Button label={__('FAQ and Support')} href="https://odysee.com/@OdyseeHelp:b" /> <Button label={__('FAQ and Support')} href="https://odysee.com/@OdyseeHelp:b" />
</li> </li>
{SIMPLE_SITE && ( // GUIDELINES_URL?
<li className="navigation-link"> <li className="navigation-link">
<Button label={__('Community Guidelines')} href="https://odysee.com/@OdyseeHelp:b/Community-Guidelines:c" /> <Button label={__('Community Guidelines')} href="https://odysee.com/@OdyseeHelp:b/Community-Guidelines:c" />
</li> </li>
)}
<li className="navigation-link"> <li className="navigation-link">
<Button label={__('Terms')} href="https://odysee.com/$/tos" /> <Button label={__('Terms')} href="https://odysee.com/$/tos" />
</li> </li>
@ -342,9 +369,6 @@ function SideNavigation(props: Props) {
aria-label={'Sidebar'} aria-label={'Sidebar'}
className={classnames('navigation', { className={classnames('navigation', {
'navigation--micro': microNavigation, 'navigation--micro': microNavigation,
// @if TARGET='app'
'navigation--mac': IS_MAC,
// @endif
})} })}
> >
<div> <div>
@ -372,40 +396,18 @@ function SideNavigation(props: Props) {
); );
})} })}
</ul> </ul>
{getSubscriptionSection()}
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && ( {getFollowedTagsSection()}
<ul className="navigation__secondary navigation-links">
{subscriptions.map((subscription) => (
<SubscriptionListItem key={subscription.uri} subscription={subscription} />
))}
</ul>
)}
{sidebarOpen && isPersonalized && followedTags && followedTags.length > 0 && (
<ul className="navigation__secondary navigation-links navigation-links--small">
{followedTags.map(({ name }, key) => (
<li key={name} className="navigation-link__wrapper">
<Button navigate={`/$/discover?t=${name}`} label={`#${name}`} className="navigation-link" />
</li>
))}
</ul>
)}
{!isAuthenticated && sidebarOpen && unAuthNudge} {!isAuthenticated && sidebarOpen && unAuthNudge}
</div> </div>
{SIMPLE_SITE && sidebarOpen && helpLinks} {sidebarOpen && helpLinks}
</nav> </nav>
)} )}
{(isOnFilePage || isMediumScreen) && sidebarOpen && ( {(isOnFilePage || isMediumScreen) && sidebarOpen && (
<> <>
<nav <nav className="navigation--absolute">
className={classnames('navigation--absolute', {
// @if TARGET='app'
'navigation--mac': IS_MAC,
// @endif
})}
>
<div> <div>
<ul className="navigation-links--absolute mobile-only"> <ul className="navigation-links--absolute mobile-only">
{email && livestreamEnabled && ( {email && livestreamEnabled && (
@ -463,34 +465,13 @@ function SideNavigation(props: Props) {
); );
})} })}
</ul> </ul>
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && ( {getSubscriptionSection()}
<ul className="navigation__secondary navigation-links"> {getFollowedTagsSection()}
{subscriptions.map((subscription) => (
<SubscriptionListItem key={subscription.uri} subscription={subscription} />
))}
</ul>
)}
{sidebarOpen && isPersonalized && followedTags && followedTags.length > 0 && (
<ul className="navigation__secondary navigation-links navigation-links--small">
{followedTags.map(({ name }, key) => (
<li key={name} className="navigation-link__wrapper">
<Button navigate={`/$/discover?t=${name}`} label={`#${name}`} className="navigation-link" />
</li>
))}
</ul>
)}
{!isAuthenticated && unAuthNudge} {!isAuthenticated && unAuthNudge}
{SIMPLE_SITE && helpLinks} {helpLinks}
</div> </div>
</nav> </nav>
<div <div className="navigation__overlay" onClick={() => setSidebarOpen(false)} />
className={classnames('navigation__overlay', {
// @if TARGET='app'
'navigation__overlay--mac': IS_MAC,
// @endif
})}
onClick={() => setSidebarOpen(false)}
/>
</> </>
)} )}
</div> </div>

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectClaimForUri, makeSelectThumbnailForUri } from 'redux/selectors/claims'; import { makeSelectClaimForUri, selectThumbnailForUri } from 'redux/selectors/claims';
import { import {
makeSelectNextUrlForCollectionAndUrl, makeSelectNextUrlForCollectionAndUrl,
makeSelectPreviousUrlForCollectionAndUrl, makeSelectPreviousUrlForCollectionAndUrl,
@ -61,7 +61,7 @@ const select = (state, props) => {
volume: selectVolume(state), volume: selectVolume(state),
muted: selectMute(state), muted: selectMute(state),
videoPlaybackRate: makeSelectClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE)(state), videoPlaybackRate: makeSelectClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE)(state),
thumbnail: makeSelectThumbnailForUri(uri)(state), thumbnail: selectThumbnailForUri(state, uri),
claim: makeSelectClaimForUri(uri)(state), claim: makeSelectClaimForUri(uri)(state),
homepageData: selectHomepageData(state), homepageData: selectHomepageData(state),
authenticated: selectUserVerifiedEmail(state), authenticated: selectUserVerifiedEmail(state),

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { import {
selectClaimIsMine, selectClaimIsMine,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, getThumbnailFromClaim,
makeSelectCoverForUri, makeSelectCoverForUri,
selectCurrentChannelPage, selectCurrentChannelPage,
selectClaimForUri, selectClaimForUri,
@ -22,7 +22,7 @@ const select = (state, props) => {
return { return {
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: getThumbnailFromClaim(claim),
cover: makeSelectCoverForUri(props.uri)(state), cover: makeSelectCoverForUri(props.uri)(state),
channelIsMine: selectClaimIsMine(state, claim), channelIsMine: selectClaimIsMine(state, claim),
page: selectCurrentChannelPage(state), page: selectCurrentChannelPage(state),

View file

@ -4,7 +4,7 @@ import { withRouter } from 'react-router-dom';
import CollectionPage from './view'; import CollectionPage from './view';
import { import {
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, getThumbnailFromClaim,
selectClaimIsMine, selectClaimIsMine,
makeSelectClaimIsPending, makeSelectClaimIsPending,
makeSelectClaimForClaimId, makeSelectClaimForClaimId,
@ -39,7 +39,7 @@ const select = (state, props) => {
collectionCount: makeSelectCountForCollectionId(collectionId)(state), collectionCount: makeSelectCountForCollectionId(collectionId)(state),
isResolvingCollection: makeSelectIsResolvingCollectionForId(collectionId)(state), isResolvingCollection: makeSelectIsResolvingCollectionForId(collectionId)(state),
title: makeSelectTitleForUri(uri)(state), title: makeSelectTitleForUri(uri)(state),
thumbnail: makeSelectThumbnailForUri(uri)(state), thumbnail: getThumbnailFromClaim(claim),
isMyClaim: selectClaimIsMine(state, claim), // or collection is mine? isMyClaim: selectClaimIsMine(state, claim), // or collection is mine?
isMyCollection: makeSelectCollectionIsMine(collectionId)(state), isMyCollection: makeSelectCollectionIsMine(collectionId)(state),
claimIsPending: makeSelectClaimIsPending(uri)(state), claimIsPending: makeSelectClaimIsPending(uri)(state),

View file

@ -388,11 +388,14 @@ export const makeSelectContentTypeForUri = (uri: string) =>
return source ? source.media_type : undefined; return source ? source.media_type : undefined;
}); });
export const makeSelectThumbnailForUri = (uri: string) => export const getThumbnailFromClaim = (claim: Claim) => {
createSelector(makeSelectClaimForUri(uri), (claim) => {
const thumbnail = claim && claim.value && claim.value.thumbnail; const thumbnail = claim && claim.value && claim.value.thumbnail;
return thumbnail && thumbnail.url ? thumbnail.url.trim().replace(/^http:\/\//i, 'https://') : undefined; return thumbnail && thumbnail.url ? thumbnail.url.trim().replace(/^http:\/\//i, 'https://') : undefined;
}); };
export const selectThumbnailForUri = createCachedSelector(selectClaimForUri, (claim) => {
return getThumbnailFromClaim(claim);
})((state, uri) => String(uri));
export const makeSelectCoverForUri = (uri: string) => export const makeSelectCoverForUri = (uri: string) =>
createSelector(makeSelectClaimForUri(uri), (claim) => { createSelector(makeSelectClaimForUri(uri), (claim) => {

View file

@ -147,6 +147,32 @@ export function GetLinksData(
let rowData: Array<RowDataItem> = []; let rowData: Array<RowDataItem> = [];
const individualTagDataItems: Array<RowDataItem> = []; const individualTagDataItems: Array<RowDataItem> = [];
if (isHomepage && showPersonalizedChannels && subscribedChannels) {
const RECENT_FROM_FOLLOWING = {
title: __('Recent From Following'),
link: `/$/${PAGES.CHANNELS_FOLLOWING}`,
icon: ICONS.SUBSCRIBE,
options: {
orderBy: CS.ORDER_BY_NEW_VALUE,
releaseTime:
subscribedChannels.length > 20
? `>${Math.floor(moment().subtract(9, 'months').startOf('week').unix())}`
: `>${Math.floor(moment().subtract(1, 'year').startOf('week').unix())}`,
pageSize: getPageSize(subscribedChannels.length > 3 ? (subscribedChannels.length > 6 ? 16 : 8) : 4),
streamTypes: null,
channelIds: subscribedChannels.map((subscription: Subscription) => {
const { channelClaimId } = parseURI(subscription.uri);
if (channelClaimId) return channelClaimId;
}),
},
};
// $FlowFixMe flow thinks this might not be Array<string>
rowData.push(RECENT_FROM_FOLLOWING);
}
// **************************************************************************
// @if CUSTOM_HOMEPAGE='false'
const YOUTUBER_CHANNEL_IDS = [ const YOUTUBER_CHANNEL_IDS = [
'fb364ef587872515f545a5b4b3182b58073f230f', 'fb364ef587872515f545a5b4b3182b58073f230f',
'589276465a23c589801d874f484cc39f307d7ec7', '589276465a23c589801d874f484cc39f307d7ec7',
@ -277,28 +303,6 @@ export function GetLinksData(
}, },
}; };
if (isHomepage && showPersonalizedChannels && subscribedChannels) {
const RECENT_FROM_FOLLOWING = {
title: __('Recent From Following'),
link: `/$/${PAGES.CHANNELS_FOLLOWING}`,
icon: ICONS.SUBSCRIBE,
options: {
orderBy: CS.ORDER_BY_NEW_VALUE,
releaseTime:
subscribedChannels.length > 20
? `>${Math.floor(moment().subtract(9, 'months').startOf('week').unix())}`
: `>${Math.floor(moment().subtract(1, 'year').startOf('week').unix())}`,
pageSize: getPageSize(subscribedChannels.length > 3 ? (subscribedChannels.length > 6 ? 16 : 8) : 4),
streamTypes: null,
channelIds: subscribedChannels.map((subscription: Subscription) => {
const { channelClaimId } = parseURI(subscription.uri);
if (channelClaimId) return channelClaimId;
}),
},
};
// $FlowFixMe flow thinks this might not be Array<string>
rowData.push(RECENT_FROM_FOLLOWING);
}
if (isHomepage && !CUSTOM_HOMEPAGE) { if (isHomepage && !CUSTOM_HOMEPAGE) {
if (followedTags) { if (followedTags) {
const TRENDING_FOR_TAGS = { const TRENDING_FOR_TAGS = {
@ -333,6 +337,7 @@ export function GetLinksData(
} }
} }
} }
if (!CUSTOM_HOMEPAGE) { if (!CUSTOM_HOMEPAGE) {
if (!authenticated) { if (!authenticated) {
rowData.push(YOUTUBE_CREATOR_ROW); rowData.push(YOUTUBE_CREATOR_ROW);
@ -341,6 +346,10 @@ export function GetLinksData(
rowData.push(LATEST_FROM_LBRY); rowData.push(LATEST_FROM_LBRY);
if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS); if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS);
} }
// @endif
// **************************************************************************
// TODO: provide better method for exempting from homepage // TODO: provide better method for exempting from homepage
(Object.values(all): any) (Object.values(all): any)
.filter((row) => !(isHomepage && row.name === 'news')) .filter((row) => !(isHomepage && row.name === 'news'))