Fix sidebar subs inefficiencies

- `filteredSubscriptions` was running the filter code whether or not it actually required filtering, because the `!subscriptionFilter` logic is placed in the predicate. While it is a clean one-liner, it is slow when the subs list is huge.
    - While at it, moved the code into `getSubscriptionSection` to isolate things.

- The sidebar was resolving the entire subs list, which is super slow for a list of 500+. Since we now have a "Manage Subscriptions" page, just batch-resolve the N visible subs.
    - TODO: the code should probably be moved from 'app' to 'sideNavigate'.
This commit is contained in:
infinite-persistence 2022-03-01 16:14:38 +08:00 committed by Thomas Zarebczan
parent 3edb00b99d
commit 6c76cff2a0
3 changed files with 20 additions and 17 deletions

View file

@ -23,6 +23,7 @@ import usePersistedState from 'effects/use-persisted-state';
import useConnectionStatus from 'effects/use-connection-status'; import useConnectionStatus from 'effects/use-connection-status';
import Spinner from 'component/spinner'; import Spinner from 'component/spinner';
import LANGUAGES from 'constants/languages'; import LANGUAGES from 'constants/languages';
import { SIDEBAR_SUBS_DISPLAYED } from 'constants/subscriptions';
import YoutubeWelcome from 'web/component/youtubeReferralWelcome'; import YoutubeWelcome from 'web/component/youtubeReferralWelcome';
import { import {
useDegradedPerformance, useDegradedPerformance,
@ -477,7 +478,7 @@ function App(props: Props) {
useLayoutEffect(() => { useLayoutEffect(() => {
if (sidebarOpen && isPersonalized && subscriptions && !resolvedSubscriptions) { if (sidebarOpen && isPersonalized && subscriptions && !resolvedSubscriptions) {
setResolvedSubscriptions(true); setResolvedSubscriptions(true);
resolveUris(subscriptions.map((sub) => sub.uri)); resolveUris(subscriptions.slice(0, SIDEBAR_SUBS_DISPLAYED).map((sub) => sub.uri));
} }
}, [sidebarOpen, isPersonalized, resolvedSubscriptions, subscriptions, resolveUris, setResolvedSubscriptions]); }, [sidebarOpen, isPersonalized, resolvedSubscriptions, subscriptions, resolveUris, setResolvedSubscriptions]);

View file

@ -3,6 +3,7 @@ import type { Node } from 'react';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import * as KEYCODES from 'constants/keycodes'; import * as KEYCODES from 'constants/keycodes';
import { SIDEBAR_SUBS_DISPLAYED } from 'constants/subscriptions';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import Button from 'component/button'; import Button from 'component/button';
import ClaimPreviewTitle from 'component/claimPreviewTitle'; import ClaimPreviewTitle from 'component/claimPreviewTitle';
@ -16,7 +17,6 @@ import { useIsMobile, useIsLargeScreen, isTouch } from 'effects/use-screensize';
import { GetLinksData } from 'util/buildHomepage'; import { GetLinksData } from 'util/buildHomepage';
import { DOMAIN, ENABLE_UI_NOTIFICATIONS, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config'; import { DOMAIN, ENABLE_UI_NOTIFICATIONS, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config';
const FOLLOWED_ITEM_INITIAL_LIMIT = 10;
const touch = isTouch(); const touch = isTouch();
type SideNavLink = { type SideNavLink = {
@ -265,23 +265,13 @@ function SideNavigation(props: Props) {
const showPushMenu = sidebarOpen && !menuCanCloseCompletely; const showPushMenu = sidebarOpen && !menuCanCloseCompletely;
const showOverlay = isAbsolute && sidebarOpen; const showOverlay = isAbsolute && sidebarOpen;
const showSubscriptionSection = shouldRenderLargeMenu && isPersonalized && subscriptions && subscriptions.length > 0;
const showTagSection = sidebarOpen && isPersonalized && followedTags && followedTags.length; const showTagSection = sidebarOpen && isPersonalized && followedTags && followedTags.length;
const [subscriptionFilter, setSubscriptionFilter] = React.useState(''); const [subscriptionFilter, setSubscriptionFilter] = React.useState('');
const filteredSubscriptions = subscriptions.filter(
(sub) => !subscriptionFilter || sub.channelName.toLowerCase().includes(subscriptionFilter.toLowerCase())
);
let displayedSubscriptions = filteredSubscriptions;
if (showSubscriptionSection && !subscriptionFilter && subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT) {
displayedSubscriptions = subscriptions.slice(0, FOLLOWED_ITEM_INITIAL_LIMIT);
}
let displayedFollowedTags = followedTags; let displayedFollowedTags = followedTags;
if (showTagSection && followedTags.length > FOLLOWED_ITEM_INITIAL_LIMIT && !expandTags) { if (showTagSection && followedTags.length > SIDEBAR_SUBS_DISPLAYED && !expandTags) {
displayedFollowedTags = followedTags.slice(0, FOLLOWED_ITEM_INITIAL_LIMIT); displayedFollowedTags = followedTags.slice(0, SIDEBAR_SUBS_DISPLAYED);
} }
function getLink(props: SideNavLink) { function getLink(props: SideNavLink) {
@ -312,11 +302,20 @@ function SideNavigation(props: Props) {
} }
function getSubscriptionSection() { function getSubscriptionSection() {
if (showSubscriptionSection) { const showSubsSection = shouldRenderLargeMenu && isPersonalized && subscriptions && subscriptions.length > 0;
if (showSubsSection) {
let displayedSubscriptions;
if (subscriptionFilter) {
const filter = subscriptionFilter.toLowerCase();
displayedSubscriptions = subscriptions.filter((sub) => sub.channelName.toLowerCase().includes(filter));
} else {
displayedSubscriptions = subscriptions.slice(0, SIDEBAR_SUBS_DISPLAYED);
}
return ( return (
<> <>
<ul className="navigation__secondary navigation-links"> <ul className="navigation__secondary navigation-links">
{subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT && ( {subscriptions.length > SIDEBAR_SUBS_DISPLAYED && (
<li className="navigation-item"> <li className="navigation-item">
<DebouncedInput icon={ICONS.SEARCH} placeholder={__('Filter')} onChange={setSubscriptionFilter} /> <DebouncedInput icon={ICONS.SEARCH} placeholder={__('Filter')} onChange={setSubscriptionFilter} />
</li> </li>
@ -356,7 +355,7 @@ function SideNavigation(props: Props) {
<Button navigate={`/$/discover?t=${name}`} label={`#${name}`} className="navigation-link" /> <Button navigate={`/$/discover?t=${name}`} label={`#${name}`} className="navigation-link" />
</li> </li>
))} ))}
{followedTags.length > FOLLOWED_ITEM_INITIAL_LIMIT && ( {followedTags.length > SIDEBAR_SUBS_DISPLAYED && (
<Button <Button
key="showMore" key="showMore"
label={expandTags ? __('Show less') : __('Show more')} label={expandTags ? __('Show less') : __('Show more')}

View file

@ -10,3 +10,6 @@ export const NOTIFY_ONLY = 'NOTIFY_ONLY;';
export const SUGGESTED_TOP_BID = 'top_bid'; export const SUGGESTED_TOP_BID = 'top_bid';
export const SUGGESTED_TOP_SUBSCRIBED = 'top_subscribed'; export const SUGGESTED_TOP_SUBSCRIBED = 'top_subscribed';
export const SUGGESTED_FEATURED = 'featured'; export const SUGGESTED_FEATURED = 'featured';
// Sidebar UI
export const SIDEBAR_SUBS_DISPLAYED = 10;