Add horizontal layout (#636)

* Test out a horizontal scroll for upcoming (tile only for now)

* - add support for list layout
- add following label on home page
- clan up css and naming conventions

* Update header type + show only if scheduled streams are showing
This commit is contained in:
Dan Peterson 2022-01-06 15:13:26 -06:00 committed by GitHub
parent 58bdcbd1ed
commit a89cb17ce4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 92 additions and 17 deletions

View file

@ -47,6 +47,7 @@ type Props = {
maxClaimRender?: number,
excludeUris?: Array<string>,
loadedCallback?: (number) => void,
swipeLayout: boolean,
};
export default function ClaimList(props: Props) {
@ -80,6 +81,7 @@ export default function ClaimList(props: Props) {
maxClaimRender,
excludeUris = [],
loadedCallback,
swipeLayout = false,
} = props;
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
@ -148,7 +150,7 @@ export default function ClaimList(props: Props) {
}, [loading, onScrollBottom, urisLength, pageSize, page]);
return tileLayout && !header ? (
<section className="claim-grid">
<section className={classnames('claim-grid', { 'swipe-list': swipeLayout })}>
{urisLength > 0 &&
tileUris.map((uri) => (
<ClaimPreviewTile
@ -158,6 +160,7 @@ export default function ClaimList(props: Props) {
properties={renderProperties}
collectionId={collectionId}
showNoSourceClaims={showNoSourceClaims}
swipeLayout={swipeLayout}
/>
))}
{!timedOut && urisLength === 0 && !loading && <div className="empty main--empty">{empty || noResultMsg}</div>}
@ -200,8 +203,9 @@ export default function ClaimList(props: Props) {
{urisLength > 0 && (
<ul
className={classnames('ul--no-style', {
card: !(tileLayout || type === 'small'),
card: !(tileLayout || swipeLayout || type === 'small'),
'claim-list--card-body': tileLayout,
'swipe-list': swipeLayout,
})}
>
{sortedUris.map((uri, index) => (
@ -223,6 +227,7 @@ export default function ClaimList(props: Props) {
showNoSourceClaims={showNoSourceClaims}
customShouldHide={customShouldHide}
onClick={handleClaimClicked}
swipeLayout={swipeLayout}
/>
</React.Fragment>
))}

View file

@ -100,6 +100,8 @@ type Props = {
maxClaimRender?: number,
useSkeletonScreen?: boolean,
excludeUris?: Array<string>,
swipeLayout: boolean,
};
function ClaimListDiscover(props: Props) {
@ -169,6 +171,7 @@ function ClaimListDiscover(props: Props) {
maxClaimRender,
useSkeletonScreen = true,
excludeUris = [],
swipeLayout = false,
} = props;
const didNavigateForward = history.action === 'PUSH';
const { search } = location;
@ -634,6 +637,7 @@ function ClaimListDiscover(props: Props) {
maxClaimRender={maxClaimRender}
excludeUris={excludeUris}
loadedCallback={loadedCallback}
swipeLayout={swipeLayout}
/>
{loading && useSkeletonScreen && (
<div className="claim-grid">
@ -670,6 +674,7 @@ function ClaimListDiscover(props: Props) {
maxClaimRender={maxClaimRender}
excludeUris={excludeUris}
loadedCallback={loadedCallback}
swipeLayout={swipeLayout}
/>
{loading &&
useSkeletonScreen &&

View file

@ -86,6 +86,7 @@ type Props = {
date?: any,
indexInContainer?: number, // The index order of this component within 'containerId'.
channelSubCount?: number,
swipeLayout: boolean,
};
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
@ -146,6 +147,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
disableNavigation,
indexInContainer,
channelSubCount,
swipeLayout = false,
} = props;
const isCollection = claim && claim.value_type === 'collection';
@ -341,6 +343,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
'claim-preview--channel': isChannelUri,
'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri,
'claim-preview--pending': pending,
'swipe-list__item': swipeLayout,
})}
>
{isChannelUri && claim ? (

View file

@ -46,6 +46,7 @@ type Props = {
isLivestream: boolean,
viewCount: string,
isLivestreamActive: boolean,
swipeLayout: boolean,
};
// preview image cards used in related video functionality, channel overview page and homepage
@ -73,6 +74,7 @@ function ClaimPreviewTile(props: Props) {
collectionId,
mediaDuration,
viewCount,
swipeLayout = false,
} = props;
const isRepost = claim && claim.repost_channel_url;
const isCollection = claim && claim.value_type === 'collection';
@ -178,6 +180,7 @@ function ClaimPreviewTile(props: Props) {
className={classnames('card claim-preview--tile', {
'claim-preview__wrapper--channel': isChannel,
'claim-preview__live': isLivestreamActive,
'swipe-list__item claim-preview--horizontal-tile': swipeLayout,
})}
>
<NavLink {...navLinkProps} role="none" tabIndex={-1} aria-hidden>

View file

@ -16,13 +16,22 @@ type Props = {
tileLayout: boolean,
liveUris: Array<string>,
limitClaimsPerChannel?: number,
onLoad: (number) => void,
// --- perform ---
setClientSetting: (string, boolean | string | number, boolean) => void,
doShowSnackBar: (string) => void,
};
const ScheduledStreams = (props: Props) => {
const { channelIds, tileLayout, liveUris = [], limitClaimsPerChannel, setClientSetting, doShowSnackBar } = props;
const {
channelIds,
tileLayout,
liveUris = [],
limitClaimsPerChannel,
setClientSetting,
doShowSnackBar,
onLoad,
} = props;
const isMediumScreen = useIsMediumScreen();
const isLargeScreen = useIsLargeScreen();
@ -30,16 +39,18 @@ const ScheduledStreams = (props: Props) => {
const [showAllUpcoming, setShowAllUpcoming] = React.useState(false);
const showUpcomingLivestreams = totalUpcomingLivestreams > 0;
const useSwipeLayout = totalUpcomingLivestreams > 1 && isMediumScreen;
const upcomingMax = React.useMemo(() => {
if (showAllUpcoming) return 50;
if (showAllUpcoming || useSwipeLayout) return 50;
if (isLargeScreen) return 6;
if (isMediumScreen) return 3;
return 4;
}, [showAllUpcoming, isMediumScreen, isLargeScreen]);
}, [showAllUpcoming, isMediumScreen, isLargeScreen, useSwipeLayout]);
const loadedCallback = (total) => {
setTotalUpcomingLivestreams(total);
if (typeof onLoad === 'function') onLoad(total);
};
const hideScheduledStreams = () => {
@ -57,8 +68,9 @@ const ScheduledStreams = (props: Props) => {
};
return (
<div className={'mb-xl'} style={{ display: showUpcomingLivestreams ? 'block' : 'none' }}>
<div className={'mb-m mt-m md:mb-xl'} style={{ display: showUpcomingLivestreams ? 'block' : 'none' }}>
<ClaimListDiscover
swipeLayout={useSwipeLayout}
useSkeletonScreen={false}
channelIds={channelIds}
limitClaimsPerChannel={limitClaimsPerChannel}
@ -78,7 +90,7 @@ const ScheduledStreams = (props: Props) => {
excludeUris={liveUris}
loadedCallback={loadedCallback}
/>
{totalUpcomingLivestreams > upcomingMax && !showAllUpcoming && (
{totalUpcomingLivestreams > upcomingMax && !showAllUpcoming && !useSwipeLayout && (
<div className="livestream-list--view-more">
<Button
label={__('Show more upcoming livestreams')}

View file

@ -3,7 +3,7 @@ import * as ICONS from 'constants/icons';
import * as PAGES from 'constants/pages';
import { SITE_NAME, SIMPLE_SITE, ENABLE_NO_SOURCE_CLAIMS, SHOW_ADS } from 'config';
import Ads, { injectAd } from 'web/component/ads';
import React from 'react';
import React, { useState } from 'react';
import Page from 'component/page';
import Button from 'component/button';
import ClaimTilesDiscover from 'component/claimTilesDiscover';
@ -65,6 +65,24 @@ function HomePage(props: Props) {
showNsfw
);
type SectionHeaderProps = {
title: string,
navigate?: string,
icon?: string,
help?: string,
};
const SectionHeader = ({ title, navigate = '/', icon = '', help }: SectionHeaderProps) => {
return (
<h1 className="claim-grid__header">
<Button navigate={navigate} button="link">
<Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />
<span className="claim-grid__title">{title}</span>
{help}
</Button>
</h1>
);
};
function getRowElements(title, route, link, icon, help, options, index, pinUrls) {
const tilePlaceholder = (
<ul className="claim-grid">
@ -88,13 +106,7 @@ function HomePage(props: Props) {
<div key={title} className="claim-grid__wrapper">
{/* category header */}
{index !== 0 && title && typeof title === 'string' && (
<h1 className="claim-grid__header">
<Button navigate={route || link} button="link">
{icon && <Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />}
<span className="claim-grid__title">{__(title)}</span>
{help}
</Button>
</h1>
<SectionHeader title={__(title)} navigate={route || link} icon={icon} help={help} />
)}
{index === 0 && <>{claimTiles}</>}
@ -128,6 +140,9 @@ function HomePage(props: Props) {
injectAd(shouldShowAds);
}, []);
const [hasScheduledStreams, setHasScheduledStreams] = useState(false);
const scheduledStreamsLoaded = (total) => setHasScheduledStreams(total > 0);
return (
<Page fullWidthPage>
{!SIMPLE_SITE && (authenticated || !IS_WEB) && !subscribedChannels.length && (
@ -158,8 +173,14 @@ function HomePage(props: Props) {
tileLayout
liveUris={getLivestreamUris(activeLivestreams, channelIds)}
limitClaimsPerChannel={2}
onLoad={scheduledStreamsLoaded}
/>
)}
{authenticated && hasScheduledStreams && !hideScheduledLivestreams && (
<SectionHeader title={__('Following')} navigate={`/$/${PAGES.CHANNELS_FOLLOWING}`} icon={ICONS.SUBSCRIBE} />
)}
{rowData.map(({ title, route, link, icon, help, pinnedUrls: pinUrls, options = {} }, index) => {
// add pins here
return getRowElements(title, route, link, icon, help, options, index, pinUrls);

View file

@ -68,4 +68,5 @@
@import 'component/empty';
@import 'component/stripe-card';
@import 'component/wallet-tip-send';
@import 'component/swipe-list';
@import 'component/utils';

View file

@ -552,6 +552,12 @@
}
}
.claim-preview--horizontal-tile {
&:not(:first-child) {
margin-top: 0;
}
}
.claim-tile__title {
position: relative;
padding: var(--spacing-s);
@ -562,7 +568,7 @@
font-weight: 600;
color: var(--color-text);
font-size: var(--font-small);
min-height: 2rem;
min-height: 3.2rem;
@media (min-width: $breakpoint-small) {
min-height: 2.5rem;

View file

@ -0,0 +1,13 @@
.swipe-list {
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.swipe-list__item {
width: 80vw;
margin-right: var(--spacing-s);
flex-shrink: 0;
scroll-snap-align: start;
}

View file

@ -1,7 +1,6 @@
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
@ -25,6 +24,10 @@
height: 3rem;
}
.mt-0 {
margin-top: 0px;
}
.mt-s {
margin-top: var(--spacing-s);
}
@ -80,6 +83,9 @@
.md\:mt-0 {
margin-top: 0;
}
.md\:mb-xl {
margin-bottom: var(--spacing-xl);
}
.md\:h-12 {
height: 3rem;
}