Add channel list filter
This commit is contained in:
parent
e1b6c5149a
commit
a869e9dc2c
2 changed files with 54 additions and 3 deletions
|
@ -10,11 +10,13 @@ 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 ChannelThumbnail from 'component/channelThumbnail';
|
||||||
|
import useDebounce from 'effects/use-debounce';
|
||||||
import { useIsMobile, useIsLargeScreen } from 'effects/use-screensize';
|
import { useIsMobile, useIsLargeScreen } 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 FOLLOWED_ITEM_INITIAL_LIMIT = 10;
|
||||||
|
const FILTER_DEBOUNCE_MS = 300;
|
||||||
|
|
||||||
type SideNavLink = {
|
type SideNavLink = {
|
||||||
title: string,
|
title: string,
|
||||||
|
@ -250,8 +252,20 @@ function SideNavigation(props: Props) {
|
||||||
const showSubscriptionSection = shouldRenderLargeMenu && isPersonalized && subscriptions && subscriptions.length > 0;
|
const showSubscriptionSection = shouldRenderLargeMenu && isPersonalized && subscriptions && subscriptions.length > 0;
|
||||||
const showTagSection = sidebarOpen && isPersonalized && followedTags && followedTags.length;
|
const showTagSection = sidebarOpen && isPersonalized && followedTags && followedTags.length;
|
||||||
|
|
||||||
let displayedSubscriptions = subscriptions;
|
const [rawSubscriptionFilter, setRawSubscriptionFilter] = React.useState('');
|
||||||
if (showSubscriptionSection && subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT && !expandSubscriptions) {
|
const subscriptionFilter: string = useDebounce(rawSubscriptionFilter, FILTER_DEBOUNCE_MS);
|
||||||
|
|
||||||
|
const filteredSubscriptions = subscriptions.filter(
|
||||||
|
(sub) => !subscriptionFilter || sub.channelName.toLowerCase().includes(subscriptionFilter)
|
||||||
|
);
|
||||||
|
|
||||||
|
let displayedSubscriptions = filteredSubscriptions;
|
||||||
|
if (
|
||||||
|
showSubscriptionSection &&
|
||||||
|
!subscriptionFilter &&
|
||||||
|
subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT &&
|
||||||
|
!expandSubscriptions
|
||||||
|
) {
|
||||||
displayedSubscriptions = subscriptions.slice(0, FOLLOWED_ITEM_INITIAL_LIMIT);
|
displayedSubscriptions = subscriptions.slice(0, FOLLOWED_ITEM_INITIAL_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,10 +306,29 @@ function SideNavigation(props: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ul className="navigation__secondary navigation-links">
|
<ul className="navigation__secondary navigation-links">
|
||||||
|
<li className="navigation-link__wrapper">
|
||||||
|
<div className="wunderbar">
|
||||||
|
<Icon icon={ICONS.SEARCH} />
|
||||||
|
<input
|
||||||
|
className="wunderbar__input"
|
||||||
|
spellCheck={false}
|
||||||
|
placeholder={__('Filter')}
|
||||||
|
value={rawSubscriptionFilter}
|
||||||
|
onChange={(e) => setRawSubscriptionFilter(e.target.value.trim())}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
{displayedSubscriptions.map((subscription) => (
|
{displayedSubscriptions.map((subscription) => (
|
||||||
<SubscriptionListItem key={subscription.uri} subscription={subscription} />
|
<SubscriptionListItem key={subscription.uri} subscription={subscription} />
|
||||||
))}
|
))}
|
||||||
{subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT && (
|
{!!subscriptionFilter && !displayedSubscriptions.length && (
|
||||||
|
<li className="navigation-link__wrapper ">
|
||||||
|
<div className="navigation-link">
|
||||||
|
<div className="button__content">{__('No results')}</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{!subscriptionFilter && subscriptions.length > FOLLOWED_ITEM_INITIAL_LIMIT && (
|
||||||
<Button
|
<Button
|
||||||
key="showMore"
|
key="showMore"
|
||||||
label={expandSubscriptions ? __('Show less') : __('Show more')}
|
label={expandSubscriptions ? __('Show less') : __('Show more')}
|
||||||
|
|
18
ui/effects/use-debounce.js
Normal file
18
ui/effects/use-debounce.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// @flow
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function useDebounce(value: string, delay: number) {
|
||||||
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedValue(value);
|
||||||
|
}, delay);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler);
|
||||||
|
};
|
||||||
|
}, [value, delay]);
|
||||||
|
|
||||||
|
return debouncedValue;
|
||||||
|
}
|
Loading…
Reference in a new issue