List page fixes #6905
2 changed files with 92 additions and 52 deletions
|
@ -9,7 +9,7 @@ import CollectionsListMine from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
builtinCollections: selectBuiltinCollections(state),
|
builtinCollections: selectBuiltinCollections(state),
|
||||||
publishedPlaylists: selectMyPublishedPlaylistCollections(state),
|
publishedCollections: selectMyPublishedPlaylistCollections(state),
|
||||||
unpublishedCollections: selectMyUnpublishedCollections(state),
|
unpublishedCollections: selectMyUnpublishedCollections(state),
|
||||||
// savedCollections: selectSavedCollections(state),
|
// savedCollections: selectSavedCollections(state),
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,48 +8,78 @@ import Icon from 'component/common/icon';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import Yrbl from 'component/yrbl';
|
import Yrbl from 'component/yrbl';
|
||||||
import usePersistedState from 'effects/use-persisted-state';
|
|
||||||
import Card from 'component/common/card';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { FormField, Form } from 'component/common/form';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
builtinCollections: CollectionGroup,
|
builtinCollections: CollectionGroup,
|
||||||
publishedCollections: CollectionGroup,
|
publishedCollections: CollectionGroup,
|
||||||
publishedPlaylists: CollectionGroup,
|
publishedCollections: CollectionGroup,
|
||||||
unpublishedCollections: CollectionGroup,
|
unpublishedCollections: CollectionGroup,
|
||||||
// savedCollections: CollectionGroup,
|
// savedCollections: CollectionGroup,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ALL = 'All';
|
||||||
|
const PRIVATE = 'Private';
|
||||||
|
const PUBLIC = 'Public';
|
||||||
|
const COLLECTION_FILTERS = [ALL, PRIVATE, PUBLIC];
|
||||||
|
|
||||||
export default function CollectionsListMine(props: Props) {
|
export default function CollectionsListMine(props: Props) {
|
||||||
const {
|
const {
|
||||||
builtinCollections,
|
builtinCollections,
|
||||||
publishedPlaylists,
|
publishedCollections,
|
||||||
unpublishedCollections,
|
unpublishedCollections,
|
||||||
// savedCollections, these are resolved on startup from sync'd claimIds or urls
|
// savedCollections, these are resolved on startup from sync'd claimIds or urls
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const builtinCollectionsList = (Object.values(builtinCollections || {}): any);
|
const builtinCollectionsList = (Object.values(builtinCollections || {}): any);
|
||||||
const unpublishedCollectionsList = (Object.keys(unpublishedCollections || {}): any);
|
const unpublishedCollectionsList = (Object.keys(unpublishedCollections || {}): any);
|
||||||
const publishedList = (Object.keys(publishedPlaylists || {}): any);
|
const publishedList = (Object.keys(publishedCollections || {}): any);
|
||||||
const hasCollections = unpublishedCollectionsList.length || publishedList.length;
|
const hasCollections = unpublishedCollectionsList.length || publishedList.length;
|
||||||
|
const [filterType, setFilterType] = React.useState(ALL);
|
||||||
|
const [searchText, setSearchText] = React.useState('');
|
||||||
|
|
||||||
|
let collectionsToShow = [];
|
||||||
|
if (filterType === ALL) {
|
||||||
|
collectionsToShow = unpublishedCollectionsList.concat(publishedList);
|
||||||
|
} else if (filterType === PRIVATE) {
|
||||||
|
collectionsToShow = unpublishedCollectionsList;
|
||||||
|
} else if (filterType === PUBLIC) {
|
||||||
|
collectionsToShow = publishedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filteredCollections;
|
||||||
|
if (searchText && collectionsToShow) {
|
||||||
|
filteredCollections = collectionsToShow.filter((id) => {
|
||||||
|
return (
|
||||||
|
(unpublishedCollections[id] &&
|
||||||
|
unpublishedCollections[id].name.toLocaleLowerCase().includes(searchText.toLocaleLowerCase())) ||
|
||||||
|
(publishedCollections[id] &&
|
||||||
|
publishedCollections[id].name.toLocaleLowerCase().includes(searchText.toLocaleLowerCase()))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
filteredCollections = collectionsToShow || [];
|
||||||
|
}
|
||||||
|
|
||||||
const watchLater = builtinCollectionsList.find((list) => list.id === COLLECTIONS_CONSTS.WATCH_LATER_ID);
|
const watchLater = builtinCollectionsList.find((list) => list.id === COLLECTIONS_CONSTS.WATCH_LATER_ID);
|
||||||
const favorites = builtinCollectionsList.find((list) => list.id === COLLECTIONS_CONSTS.FAVORITES_ID);
|
const favorites = builtinCollectionsList.find((list) => list.id === COLLECTIONS_CONSTS.FAVORITES_ID);
|
||||||
const builtin = [watchLater, favorites];
|
const builtin = [watchLater, favorites];
|
||||||
const [showHelp, setShowHelp] = usePersistedState('livestream-help-seen', true);
|
function escapeListener(e: SyntheticKeyboardEvent<*>) {
|
||||||
|
const KEYCODE_ESCAPE = 27;
|
||||||
|
if (e.keyCode === KEYCODE_ESCAPE) {
|
||||||
|
e.preventDefault();
|
||||||
|
setSearchText('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const helpText = (
|
function onTextareaFocus() {
|
||||||
<div className="section__subtitle">
|
window.addEventListener('keydown', escapeListener);
|
||||||
<p>{__(`Everyone starts with 2 private lists - Watch Later and Favorites.`)}</p>
|
}
|
||||||
<p>{__(`Add content to existing lists or new lists from content pages or content previews.`)}</p>
|
|
||||||
<p>
|
|
||||||
{__(
|
|
||||||
`By default, lists are private. You can edit them and later publish them from the Lists page or the Publish context menu on this page. Similar to uploads, small blockchain fees apply.`
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
function onTextareaBlur() {
|
||||||
|
window.removeEventListener('keydown', escapeListener);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{builtin.map((list: Collection) => {
|
{builtin.map((list: Collection) => {
|
||||||
|
@ -68,9 +98,14 @@ export default function CollectionsListMine(props: Props) {
|
||||||
<span className="claim-grid__title-span">
|
<span className="claim-grid__title-span">
|
||||||
{__(`${list.name}`)}
|
{__(`${list.name}`)}
|
||||||
<div className="claim-grid__title--empty">
|
<div className="claim-grid__title--empty">
|
||||||
<Icon className="icon--margin-right"
|
<Icon
|
||||||
icon={(list.id === COLLECTIONS_CONSTS.WATCH_LATER_ID && ICONS.TIME) ||
|
className="icon--margin-right"
|
||||||
(list.id === COLLECTIONS_CONSTS.FAVORITES_ID && ICONS.STAR) || ICONS.STACK} />
|
icon={
|
||||||
|
(list.id === COLLECTIONS_CONSTS.WATCH_LATER_ID && ICONS.TIME) ||
|
||||||
|
(list.id === COLLECTIONS_CONSTS.FAVORITES_ID && ICONS.STAR) ||
|
||||||
|
ICONS.STACK
|
||||||
|
}
|
||||||
|
/>
|
||||||
{itemUrls.length}
|
{itemUrls.length}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@ -91,52 +126,57 @@ export default function CollectionsListMine(props: Props) {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<div className="claim-grid__wrapper">
|
<div className="claim-grid__wrapper">
|
||||||
<div className="claim-grid__header claim-grid__header--between section">
|
<div className="claim-grid__header section">
|
||||||
<h1 className="claim-grid__title">
|
<h1 className="claim-grid__title">
|
||||||
{__('Playlists')}
|
{__('Playlists')}
|
||||||
{!hasCollections && (
|
{!hasCollections && (
|
||||||
<div className="claim-grid__title--empty">{__('(Empty) --[indicates empty playlist]--')}</div>
|
<div className="claim-grid__title--empty">{__('(Empty) --[indicates empty playlist]--')}</div>
|
||||||
)}
|
)}
|
||||||
</h1>
|
</h1>
|
||||||
<Button button="link" onClick={() => setShowHelp(!showHelp)} label={__('How does this work?')} />
|
|
||||||
</div>
|
</div>
|
||||||
{showHelp && (
|
<div className="section__header--actions">
|
||||||
<Card
|
<div className="claim-search__wrapper">
|
||||||
titleActions={<Button button="close" icon={ICONS.REMOVE} onClick={() => setShowHelp(false)} />}
|
<div className="claim-search__menu-group">
|
||||||
title={__('Introducing Lists')}
|
{COLLECTION_FILTERS.map((value) => (
|
||||||
actions={helpText}
|
<Button
|
||||||
/>
|
label={__(value)}
|
||||||
)}
|
key={value}
|
||||||
|
button="alt"
|
||||||
|
onClick={() => setFilterType(value)}
|
||||||
|
className={classnames('button-toggle', {
|
||||||
|
'button-toggle--active': filterType === value,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Form onSubmit={() => {}} className="wunderbar--inline">
|
||||||
|
<Icon icon={ICONS.SEARCH} />
|
||||||
|
<FormField
|
||||||
|
onFocus={onTextareaFocus}
|
||||||
|
onBlur={onTextareaBlur}
|
||||||
|
className="wunderbar__input--inline"
|
||||||
|
value={searchText}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
type="text"
|
||||||
|
placeholder={__('Search')}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
{Boolean(hasCollections) && (
|
{Boolean(hasCollections) && (
|
||||||
<div
|
<div>
|
||||||
className={classnames({
|
|
||||||
section: showHelp,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{/* TODO: fix above spacing hack */}
|
{/* TODO: fix above spacing hack */}
|
||||||
<div className="claim-grid">
|
<div className="claim-grid">
|
||||||
{unpublishedCollectionsList &&
|
{filteredCollections &&
|
||||||
unpublishedCollectionsList.length > 0 &&
|
filteredCollections.length > 0 &&
|
||||||
unpublishedCollectionsList.map((key) => (
|
filteredCollections.map((key) => <CollectionPreviewTile tileLayout collectionId={key} key={key} />)}
|
||||||
<CollectionPreviewTile tileLayout collectionId={key} key={key} />
|
{!filteredCollections.length && <div className="empty main--empty">{__('No matching collections')}</div>}
|
||||||
))}
|
|
||||||
{publishedList &&
|
|
||||||
publishedList.length > 0 &&
|
|
||||||
publishedList.map((key) => <CollectionPreviewTile tileLayout collectionId={key} key={key} />)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!hasCollections && (
|
{!hasCollections && (
|
||||||
<div className="main--empty">
|
<div className="main--empty">
|
||||||
<Yrbl
|
<Yrbl type={'sad'} title={__('You have no lists yet. Better start hoarding!')} />
|
||||||
type={'sad'}
|
|
||||||
title={__('You have no lists yet. Better start hoarding!')}
|
|
||||||
// actions={
|
|
||||||
// <div className="section__actions">
|
|
||||||
// <Button button="primary" label={__('View Content')} onClick={() => setViewBlockedChannel(true)} />
|
|
||||||
// </div>
|
|
||||||
// }
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue