From 6d217dbc505daba1d8e6cb0096e4bf184e027be7 Mon Sep 17 00:00:00 2001 From: infinite-persistence <64950861+infinite-persistence@users.noreply.github.com> Date: Fri, 12 Nov 2021 23:59:11 +0800 Subject: [PATCH] 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. --- ui/component/channelEdit/index.js | 4 +- ui/component/channelThumbnail/index.js | 6 +- ui/component/claimPreviewTile/index.js | 4 +- ui/component/collectionEdit/index.js | 4 +- ui/component/collectionPreviewTile/index.js | 4 +- ui/component/comment/index.js | 4 +- ui/component/embedPlayButton/index.js | 4 +- ui/component/fileRender/index.js | 4 +- ui/component/fileRenderInitiator/index.js | 4 +- ui/component/livestreamLayout/index.js | 4 +- ui/component/previewLink/index.js | 4 +- ui/component/sideNavigation/view.jsx | 263 +++++++++----------- ui/component/viewers/videoViewer/index.js | 4 +- ui/page/channel/index.js | 4 +- ui/page/collection/index.js | 4 +- ui/redux/selectors/claims.js | 13 +- ui/util/buildHomepage.js | 53 ++-- 17 files changed, 190 insertions(+), 197 deletions(-) diff --git a/ui/component/channelEdit/index.js b/ui/component/channelEdit/index.js index 43e054f7e..17ef05479 100644 --- a/ui/component/channelEdit/index.js +++ b/ui/component/channelEdit/index.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { makeSelectTitleForUri, - makeSelectThumbnailForUri, + selectThumbnailForUri, makeSelectCoverForUri, makeSelectMetadataItemForUri, makeSelectAmountForUri, @@ -22,7 +22,7 @@ import ChannelForm from './view'; const select = (state, props) => ({ claim: makeSelectClaimForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state), - thumbnailUrl: makeSelectThumbnailForUri(props.uri)(state), + thumbnailUrl: selectThumbnailForUri(state, props.uri), coverUrl: makeSelectCoverForUri(props.uri)(state), description: makeSelectMetadataItemForUri(props.uri, 'description')(state), website: makeSelectMetadataItemForUri(props.uri, 'website_url')(state), diff --git a/ui/component/channelThumbnail/index.js b/ui/component/channelThumbnail/index.js index ebedaff1a..c5ac39490 100644 --- a/ui/component/channelThumbnail/index.js +++ b/ui/component/channelThumbnail/index.js @@ -1,11 +1,11 @@ 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 ChannelThumbnail from './view'; const select = (state, props) => ({ - thumbnail: makeSelectThumbnailForUri(props.uri)(state), - claim: makeSelectClaimForUri(props.uri)(state), + thumbnail: selectThumbnailForUri(state, props.uri), + claim: selectClaimForUri(state, props.uri), isResolving: makeSelectIsUriResolving(props.uri)(state), }); diff --git a/ui/component/claimPreviewTile/index.js b/ui/component/claimPreviewTile/index.js index c19121726..5789b1565 100644 --- a/ui/component/claimPreviewTile/index.js +++ b/ui/component/claimPreviewTile/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import { makeSelectClaimForUri, makeSelectIsUriResolving, - makeSelectThumbnailForUri, + getThumbnailFromClaim, makeSelectTitleForUri, makeSelectChannelForClaimUri, makeSelectClaimIsNsfw, @@ -28,7 +28,7 @@ const select = (state, props) => { date: props.uri && selectDateForUri(state, props.uri), channel: props.uri && makeSelectChannelForClaimUri(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), banState: selectBanStateForUri(state, props.uri), showMature: selectShowMatureContent(state), diff --git a/ui/component/collectionEdit/index.js b/ui/component/collectionEdit/index.js index 54cf51ee3..5ecf17e87 100644 --- a/ui/component/collectionEdit/index.js +++ b/ui/component/collectionEdit/index.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { makeSelectTitleForUri, - makeSelectThumbnailForUri, + selectThumbnailForUri, makeSelectMetadataItemForUri, makeSelectAmountForUri, makeSelectClaimForUri, @@ -26,7 +26,7 @@ import { doSetActiveChannel, doSetIncognito } from 'redux/actions/app'; const select = (state, props) => ({ claim: makeSelectClaimForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state), - thumbnailUrl: makeSelectThumbnailForUri(props.uri)(state), + thumbnailUrl: selectThumbnailForUri(state, props.uri), description: makeSelectMetadataItemForUri(props.uri, 'description')(state), tags: makeSelectMetadataItemForUri(props.uri, 'tags')(state), locations: makeSelectMetadataItemForUri(props.uri, 'locations')(state), diff --git a/ui/component/collectionPreviewTile/index.js b/ui/component/collectionPreviewTile/index.js index 706cd974d..aec991b35 100644 --- a/ui/component/collectionPreviewTile/index.js +++ b/ui/component/collectionPreviewTile/index.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { makeSelectIsUriResolving, - makeSelectThumbnailForUri, + getThumbnailFromClaim, makeSelectTitleForUri, makeSelectChannelForClaimUri, makeSelectClaimIsNsfw, @@ -40,7 +40,7 @@ const select = (state, props) => { isResolvingCollectionClaims: makeSelectIsResolvingCollectionForId(collectionId)(state), channelClaim: collectionUri && makeSelectChannelForClaimUri(collectionUri)(state), isResolvingUri: collectionUri && makeSelectIsUriResolving(collectionUri)(state), - thumbnail: collectionUri && makeSelectThumbnailForUri(collectionUri)(state), + thumbnail: getThumbnailFromClaim(claim), title: collectionUri && makeSelectTitleForUri(collectionUri)(state), blackListedOutpoints: selectBlackListedOutpoints(state), filteredOutpoints: selectFilteredOutpoints(state), diff --git a/ui/component/comment/index.js b/ui/component/comment/index.js index 27a847ee1..9ad2c532c 100644 --- a/ui/component/comment/index.js +++ b/ui/component/comment/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import { selectStakedLevelForChannelUri, makeSelectClaimForUri, - makeSelectThumbnailForUri, + selectThumbnailForUri, selectHasChannels, } from 'redux/selectors/claims'; import { doCommentUpdate, doCommentList } from 'redux/actions/comments'; @@ -26,7 +26,7 @@ const select = (state, props) => { return { 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), commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true, othersReacts: selectOthersReactsForComment(state, reactionKey), diff --git a/ui/component/embedPlayButton/index.js b/ui/component/embedPlayButton/index.js index 70847b149..339f02b3b 100644 --- a/ui/component/embedPlayButton/index.js +++ b/ui/component/embedPlayButton/index.js @@ -1,5 +1,5 @@ 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 * as SETTINGS from 'constants/settings'; import { doFetchCostInfoForUri, makeSelectCostInfoForUri } from 'lbryinc'; @@ -11,7 +11,7 @@ import { makeSelectFileRenderModeForUri } from 'redux/selectors/content'; import ChannelThumbnail from './view'; const select = (state, props) => ({ - thumbnail: makeSelectThumbnailForUri(props.uri)(state), + thumbnail: selectThumbnailForUri(state, props.uri), claim: makeSelectClaimForUri(props.uri)(state), floatingPlayerEnabled: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state), costInfo: makeSelectCostInfoForUri(props.uri)(state), diff --git a/ui/component/fileRender/index.js b/ui/component/fileRender/index.js index 83a29abc0..6ddfd880f 100644 --- a/ui/component/fileRender/index.js +++ b/ui/component/fileRender/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; 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 { makeSelectClientSetting } from 'redux/selectors/settings'; import { makeSelectFileRenderModeForUri, makeSelectFileExtensionForUri } from 'redux/selectors/content'; @@ -11,7 +11,7 @@ const select = (state, props) => { return { currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state), claim: makeSelectClaimForUri(props.uri)(state), - thumbnail: makeSelectThumbnailForUri(props.uri)(state), + thumbnail: selectThumbnailForUri(state, props.uri), contentType: makeSelectContentTypeForUri(props.uri)(state), downloadPath: makeSelectDownloadPathForUri(props.uri)(state), fileExtension: makeSelectFileExtensionForUri(props.uri)(state), diff --git a/ui/component/fileRenderInitiator/index.js b/ui/component/fileRenderInitiator/index.js index 178ccb403..3c581a47f 100644 --- a/ui/component/fileRenderInitiator/index.js +++ b/ui/component/fileRenderInitiator/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; 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 * as SETTINGS from 'constants/settings'; import * as COLLECTIONS_CONSTS from 'constants/collections'; @@ -23,7 +23,7 @@ const select = (state, props) => { const collectionId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID); return { - claimThumbnail: makeSelectThumbnailForUri(props.uri)(state), + claimThumbnail: selectThumbnailForUri(state, props.uri), fileInfo: makeSelectFileInfoForUri(props.uri)(state), obscurePreview: makeSelectShouldObscurePreview(props.uri)(state), isPlaying: makeSelectIsPlaying(props.uri)(state), diff --git a/ui/component/livestreamLayout/index.js b/ui/component/livestreamLayout/index.js index 03547a34d..0a4f1be4d 100644 --- a/ui/component/livestreamLayout/index.js +++ b/ui/component/livestreamLayout/index.js @@ -2,14 +2,14 @@ import { connect } from 'react-redux'; import { makeSelectClaimForUri, makeSelectTagInClaimOrChannelForUri, - makeSelectThumbnailForUri, + selectThumbnailForUri, } from 'redux/selectors/claims'; import LivestreamLayout from './view'; import { DISABLE_COMMENTS_TAG } from 'constants/tags'; const select = (state, props) => ({ claim: makeSelectClaimForUri(props.uri)(state), - thumbnail: makeSelectThumbnailForUri(props.uri)(state), + thumbnail: selectThumbnailForUri(state, props.uri), chatDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state), }); diff --git a/ui/component/previewLink/index.js b/ui/component/previewLink/index.js index 9d93d2c63..25a3aad39 100644 --- a/ui/component/previewLink/index.js +++ b/ui/component/previewLink/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import { selectClaimIsMine, makeSelectTitleForUri, - makeSelectThumbnailForUri, + getThumbnailFromClaim, selectClaimForUri, makeSelectIsUriResolving, makeSelectMetadataItemForUri, @@ -18,7 +18,7 @@ const select = (state, props) => { uri: props.uri, claim, title: makeSelectTitleForUri(props.uri)(state), - thumbnail: makeSelectThumbnailForUri(props.uri)(state), + thumbnail: getThumbnailFromClaim(claim), description: makeSelectMetadataItemForUri(props.uri, 'description')(state), channelIsMine: selectClaimIsMine(state, claim), isResolvingUri: makeSelectIsUriResolving(props.uri)(state), diff --git a/ui/component/sideNavigation/view.jsx b/ui/component/sideNavigation/view.jsx index 2e8d07096..c17c3ca15 100644 --- a/ui/component/sideNavigation/view.jsx +++ b/ui/component/sideNavigation/view.jsx @@ -11,16 +11,19 @@ import NotificationBubble from 'component/notificationBubble'; import I18nMessage from 'component/i18nMessage'; import ChannelThumbnail from 'component/channelThumbnail'; import { GetLinksData } from 'util/buildHomepage'; -import { - SIMPLE_SITE, - DOMAIN, - ENABLE_UI_NOTIFICATIONS, - ENABLE_NO_SOURCE_CLAIMS, - CHANNEL_STAKED_LEVEL_LIVESTREAM, -} from 'config'; -// @if TARGET='app' -import { IS_MAC } from 'component/app/view'; -// @endif +import { DOMAIN, ENABLE_UI_NOTIFICATIONS, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config'; + +const FOLLOWED_ITEM_INITIAL_LIMIT = 10; + +type SideNavLink = { + title: string, + link?: string, + route?: string, + onClick?: () => any, + icon: string, + extra?: Node, + hideForUnauth?: boolean, +}; const HOME = { title: 'Home', @@ -37,6 +40,39 @@ const RECENT_FROM_FOLLOWING = { icon: ICONS.SUBSCRIBE, }; +const PLAYLISTS = { + title: 'Lists', + link: `/$/${PAGES.LISTS}`, + icon: ICONS.STACK, + hideForUnauth: true, +}; + +const UNAUTH_LINKS: Array = [ + { + 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 = { subscriptions: Array, followedTags: Array, @@ -55,16 +91,6 @@ type Props = { activeChannelStakedLevel: number, }; -type SideNavLink = { - title: string, - link?: string, - route?: string, - onClick?: () => any, - icon: string, - extra?: Node, - hideForUnauth?: boolean, -}; - function SideNavigation(props: Props) { const { subscriptions, @@ -85,26 +111,6 @@ function SideNavigation(props: Props) { const EXTRA_SIDEBAR_LINKS = GetLinksData(homepageData).map(({ pinnedUrls, ...theRest }) => theRest); - const FULL_LINKS: Array = [ - { - 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 = [ { title: 'Notifications', @@ -181,56 +187,17 @@ function SideNavigation(props: Props) { }, ]; - const UNAUTH_LINKS: Array = [ - { - 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 isAuthenticated = Boolean(email); + // SIDE LINKS: FOLLOWING, HOME, [FULL,] [EXTRA] let SIDE_LINKS: Array = []; SIDE_LINKS.push(HOME); SIDE_LINKS.push(RECENT_FROM_FOLLOWING); - if (!SIMPLE_SITE) { - 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, - }); - } + SIDE_LINKS.push(PLAYLISTS); - if (SIMPLE_SITE && EXTRA_SIDEBAR_LINKS) { + if (EXTRA_SIDEBAR_LINKS) { // $FlowFixMe SIDE_LINKS.push(...EXTRA_SIDEBAR_LINKS); @@ -251,6 +218,9 @@ function SideNavigation(props: Props) { ); const [pulseLibrary, setPulseLibrary] = React.useState(false); + const [expandSubscriptions, setExpandSubscriptions] = React.useState(false); + const [expandTags, setExpandTags] = React.useState(false); + const isPersonalized = !IS_WEB || isAuthenticated; const isAbsolute = isOnFilePage || isMediumScreen; const microNavigation = !sidebarOpen || isMediumScreen; @@ -264,6 +234,65 @@ function SideNavigation(props: Props) { }) : 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 ( + <> +
    + {displayedSubscriptions.map((subscription) => ( + + ))} +
+ {subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT && ( +