List Page: use swipe layout for mobile (#1069)
* Add swipe layout support for Collection Tiles * Lists: use swipe layout for mobile Ticket: 950 "playlists page - right now we show watch later on top, and if you have stuff here, you have to scroll down to other playlists. Show a selector on top? Whatever other improvements we can make here to improve UX."
This commit is contained in:
parent
a3ef81cded
commit
5b2c901496
5 changed files with 58 additions and 31 deletions
|
@ -2122,6 +2122,7 @@
|
|||
"Enter the full channel name or URL to search.\n\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8": "Enter the full channel name or URL to search.\n\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8",
|
||||
"Choose %asset%": "Choose %asset%",
|
||||
"Showing %filtered% results of %total%": "Showing %filtered% results of %total%",
|
||||
"filtered": "filtered",
|
||||
"Failed to synchronize settings. Wait a while before retrying.": "Failed to synchronize settings. Wait a while before retrying.",
|
||||
"You are offline. Check your internet connection.": "You are offline. Check your internet connection.",
|
||||
"A new version of Odysee is available.": "A new version of Odysee is available.",
|
||||
|
|
|
@ -152,7 +152,11 @@ function ClaimPreviewTile(props: Props) {
|
|||
|
||||
if (placeholder || (!claim && isResolvingUri)) {
|
||||
return (
|
||||
<li className={classnames('placeholder claim-preview--tile', {})}>
|
||||
<li
|
||||
className={classnames('placeholder claim-preview--tile', {
|
||||
'swipe-list__item claim-preview--horizontal-tile': swipeLayout,
|
||||
})}
|
||||
>
|
||||
<div className="media__thumb">
|
||||
<img src={PlaceholderTx} alt="Placeholder" />
|
||||
</div>
|
||||
|
|
|
@ -33,8 +33,8 @@ function CollectionPreviewOverlay(props: Props) {
|
|||
collectionItemUrls.map((item, index) => {
|
||||
if (index < 2) {
|
||||
return (
|
||||
<div className="collection-preview__overlay-grid-items">
|
||||
<FileThumbnail uri={item} key={item} />
|
||||
<div className="collection-preview__overlay-grid-items" key={item}>
|
||||
<FileThumbnail uri={item} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,18 +25,12 @@ type Props = {
|
|||
thumbnail?: string,
|
||||
title?: string,
|
||||
placeholder: boolean,
|
||||
blackListedOutpoints: Array<{
|
||||
txid: string,
|
||||
nout: number,
|
||||
}>,
|
||||
filteredOutpoints: Array<{
|
||||
txid: string,
|
||||
nout: number,
|
||||
}>,
|
||||
swipeLayout?: boolean,
|
||||
blackListedOutpoints: Array<{ txid: string, nout: number }>,
|
||||
filteredOutpoints: Array<{ txid: string, nout: number }>,
|
||||
blockedChannelUris: Array<string>,
|
||||
isMature?: boolean,
|
||||
showMature: boolean,
|
||||
collectionId: string,
|
||||
deleteCollection: (string) => void,
|
||||
resolveCollectionItems: (any) => void,
|
||||
isResolvingCollectionClaims: boolean,
|
||||
|
@ -53,6 +47,7 @@ function CollectionPreviewTile(props: Props) {
|
|||
collectionItemUrls,
|
||||
claim,
|
||||
resolveCollectionItems,
|
||||
swipeLayout = false,
|
||||
} = props;
|
||||
|
||||
const { push } = useHistory();
|
||||
|
@ -120,7 +115,11 @@ function CollectionPreviewTile(props: Props) {
|
|||
|
||||
if (isResolvingUri || isResolvingCollectionClaims) {
|
||||
return (
|
||||
<li className={classnames('claim-preview--tile', {})}>
|
||||
<li
|
||||
className={classnames('claim-preview--tile', {
|
||||
'swipe-list__item claim-preview--horizontal-tile': swipeLayout,
|
||||
})}
|
||||
>
|
||||
<div className="placeholder media__thumb" />
|
||||
<div className="placeholder__wrapper">
|
||||
<div className="placeholder claim-tile__title" />
|
||||
|
@ -129,12 +128,19 @@ function CollectionPreviewTile(props: Props) {
|
|||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
if (uri) {
|
||||
return <ClaimPreviewTile uri={uri} />;
|
||||
return <ClaimPreviewTile swipeLayout={swipeLayout} uri={uri} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<li role="link" onClick={handleClick} className={'card claim-preview--tile'}>
|
||||
<li
|
||||
role="link"
|
||||
onClick={handleClick}
|
||||
className={classnames('card claim-preview--tile', {
|
||||
'swipe-list__item claim-preview--horizontal-tile': swipeLayout,
|
||||
})}
|
||||
>
|
||||
<NavLink {...navLinkProps}>
|
||||
<FileThumbnail uri={collectionItemUrls && collectionItemUrls.length && collectionItemUrls[0]}>
|
||||
<React.Fragment>
|
||||
|
|
|
@ -8,10 +8,10 @@ import Icon from 'component/common/icon';
|
|||
import * as ICONS from 'constants/icons';
|
||||
import * as PAGES from 'constants/pages';
|
||||
import * as KEYCODES from 'constants/keycodes';
|
||||
|
||||
import Yrbl from 'component/yrbl';
|
||||
import classnames from 'classnames';
|
||||
import { FormField, Form } from 'component/common/form';
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
|
||||
type Props = {
|
||||
builtinCollections: CollectionGroup,
|
||||
|
@ -21,10 +21,8 @@ type Props = {
|
|||
fetchingCollections: boolean,
|
||||
};
|
||||
|
||||
const ALL = 'All';
|
||||
const PRIVATE = 'Private';
|
||||
const PUBLIC = 'Public';
|
||||
const COLLECTION_FILTERS = [ALL, PRIVATE, PUBLIC];
|
||||
const LIST_TYPE = Object.freeze({ ALL: 'All', PRIVATE: 'Private', PUBLIC: 'Public' });
|
||||
const COLLECTION_FILTERS = [LIST_TYPE.ALL, LIST_TYPE.PRIVATE, LIST_TYPE.PUBLIC];
|
||||
const COLLECTION_SHOW_COUNT = 12;
|
||||
|
||||
export default function CollectionsListMine(props: Props) {
|
||||
|
@ -40,16 +38,17 @@ export default function CollectionsListMine(props: Props) {
|
|||
const unpublishedCollectionsList = (Object.keys(unpublishedCollections || {}): any);
|
||||
const publishedList = (Object.keys(publishedCollections || {}): any);
|
||||
const hasCollections = unpublishedCollectionsList.length || publishedList.length;
|
||||
const [filterType, setFilterType] = React.useState(ALL);
|
||||
const [filterType, setFilterType] = React.useState(LIST_TYPE.ALL);
|
||||
const [searchText, setSearchText] = React.useState('');
|
||||
const isMobileScreen = useIsMobile();
|
||||
|
||||
const playlistPageUrl = `/$/${PAGES.PLAYLISTS}?type=${filterType}`;
|
||||
let collectionsToShow = [];
|
||||
if (filterType === ALL) {
|
||||
if (filterType === LIST_TYPE.ALL) {
|
||||
collectionsToShow = unpublishedCollectionsList.concat(publishedList);
|
||||
} else if (filterType === PRIVATE) {
|
||||
} else if (filterType === LIST_TYPE.PRIVATE) {
|
||||
collectionsToShow = unpublishedCollectionsList;
|
||||
} else if (filterType === PUBLIC) {
|
||||
} else if (filterType === LIST_TYPE.PUBLIC) {
|
||||
collectionsToShow = publishedList;
|
||||
}
|
||||
|
||||
|
@ -92,6 +91,7 @@ export default function CollectionsListMine(props: Props) {
|
|||
}
|
||||
return (
|
||||
<>
|
||||
{/* Built-in lists */}
|
||||
{builtin.map((list: Collection) => {
|
||||
const { items: itemUrls } = list;
|
||||
return (
|
||||
|
@ -122,7 +122,13 @@ export default function CollectionsListMine(props: Props) {
|
|||
}
|
||||
/>
|
||||
</h1>
|
||||
<ClaimList tileLayout key={list.name} uris={itemUrls.slice(0, 6)} collectionId={list.id} />
|
||||
<ClaimList
|
||||
swipeLayout={isMobileScreen}
|
||||
tileLayout
|
||||
key={list.name}
|
||||
uris={itemUrls.slice(0, 6)}
|
||||
collectionId={list.id}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!(itemUrls && itemUrls.length) && (
|
||||
|
@ -135,6 +141,8 @@ export default function CollectionsListMine(props: Props) {
|
|||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Playlists: header */}
|
||||
<div className="claim-grid__wrapper">
|
||||
<div className="claim-grid__header section">
|
||||
<h1 className="claim-grid__title">
|
||||
|
@ -159,6 +167,8 @@ export default function CollectionsListMine(props: Props) {
|
|||
)}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Playlists: search */}
|
||||
<div className="section__header-action-stack">
|
||||
<div className="section__header--actions">
|
||||
<div className="claim-search__wrapper">
|
||||
|
@ -179,6 +189,7 @@ export default function CollectionsListMine(props: Props) {
|
|||
<Form onSubmit={() => {}} className="wunderbar--inline">
|
||||
<Icon icon={ICONS.SEARCH} />
|
||||
<FormField
|
||||
name="collection_search"
|
||||
onFocus={onTextareaFocus}
|
||||
onBlur={onTextareaBlur}
|
||||
className="wunderbar__input--inline"
|
||||
|
@ -192,10 +203,7 @@ export default function CollectionsListMine(props: Props) {
|
|||
<p className="collection-grid__results-summary">
|
||||
{isTruncated && (
|
||||
<>
|
||||
{__(`Showing %filtered% results of %total%`, {
|
||||
filtered: filteredLength,
|
||||
total: totalLength,
|
||||
})}
|
||||
{__('Showing %filtered% results of %total%', { filtered: filteredLength, total: totalLength })}
|
||||
{`${searchText ? ' (' + __('filtered') + ') ' : ' '}`}
|
||||
</>
|
||||
)}
|
||||
|
@ -206,12 +214,20 @@ export default function CollectionsListMine(props: Props) {
|
|||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Playlists: tiles */}
|
||||
{Boolean(hasCollections) && (
|
||||
<div>
|
||||
<div className="claim-grid">
|
||||
<div
|
||||
className={classnames('claim-grid', {
|
||||
'swipe-list': isMobileScreen,
|
||||
})}
|
||||
>
|
||||
{filteredCollections &&
|
||||
filteredCollections.length > 0 &&
|
||||
filteredCollections.map((key) => <CollectionPreviewTile tileLayout collectionId={key} key={key} />)}
|
||||
filteredCollections.map((key) => (
|
||||
<CollectionPreviewTile swipeLayout={isMobileScreen} tileLayout collectionId={key} key={key} />
|
||||
))}
|
||||
{!filteredCollections.length && <div className="empty main--empty">{__('No matching playlists')}</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue