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 zeppi
parent 618ab5e195
commit a26305d75a
12 changed files with 260 additions and 49 deletions

View file

@ -44,7 +44,10 @@ type Props = {
collectionId?: string, collectionId?: string,
showNoSourceClaims?: boolean, showNoSourceClaims?: boolean,
onClick?: (e: any, claim?: ?Claim, index?: number) => void, onClick?: (e: any, claim?: ?Claim, index?: number) => void,
noEmpty: boolean, maxClaimRender?: number,
excludeUris?: Array<string>,
loadedCallback?: (number) => void,
swipeLayout: boolean,
}; };
export default function ClaimList(props: Props) { export default function ClaimList(props: Props) {
@ -75,7 +78,10 @@ export default function ClaimList(props: Props) {
collectionId, collectionId,
showNoSourceClaims, showNoSourceClaims,
onClick, onClick,
noEmpty, maxClaimRender,
excludeUris = [],
loadedCallback,
swipeLayout = false,
} = props; } = props;
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW); const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
@ -85,8 +91,18 @@ export default function ClaimList(props: Props) {
const timedOut = uris === null; const timedOut = uris === null;
const urisLength = (uris && uris.length) || 0; const urisLength = (uris && uris.length) || 0;
const tileUris = (prefixUris || []).concat(uris); let tileUris = (prefixUris || []).concat(uris || []);
const sortedUris = (urisLength > 0 && (currentSort === SORT_NEW ? tileUris : tileUris.slice().reverse())) || []; tileUris = tileUris.filter((uri) => !excludeUris.includes(uri));
const totalLength = tileUris.length;
if (maxClaimRender) tileUris = tileUris.slice(0, maxClaimRender);
let sortedUris = (urisLength > 0 && (currentSort === SORT_NEW ? tileUris : tileUris.slice().reverse())) || [];
React.useEffect(() => {
if (typeof loadedCallback === 'function') loadedCallback(totalLength);
}, [totalLength]); // eslint-disable-line react-hooks/exhaustive-deps
const noResultMsg = searchInLanguage const noResultMsg = searchInLanguage
? __('No results. Contents may be hidden by the Language filter.') ? __('No results. Contents may be hidden by the Language filter.')
@ -96,11 +112,21 @@ export default function ClaimList(props: Props) {
setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW); setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW);
} }
function handleClaimClicked(e, claim, index) { const handleClaimClicked = React.useCallback(
(e, claim, index) => {
if (onClick) { if (onClick) {
onClick(e, claim, index); onClick(e, claim, index);
} }
} },
[onClick]
);
const customShouldHide = React.useCallback((claim: StreamClaim) => {
// Hack to hide spee.ch thumbnail publishes
// If it meets these requirements, it was probably uploaded here:
// https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79
return claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch';
}, []);
useEffect(() => { useEffect(() => {
const handleScroll = debounce((e) => { const handleScroll = debounce((e) => {
@ -124,7 +150,7 @@ export default function ClaimList(props: Props) {
}, [loading, onScrollBottom, urisLength, pageSize, page]); }, [loading, onScrollBottom, urisLength, pageSize, page]);
return tileLayout && !header ? ( return tileLayout && !header ? (
<section className="claim-grid"> <section className={classnames('claim-grid', { 'swipe-list': swipeLayout })}>
{urisLength > 0 && {urisLength > 0 &&
tileUris.map((uri) => ( tileUris.map((uri) => (
<ClaimPreviewTile <ClaimPreviewTile
@ -134,11 +160,10 @@ export default function ClaimList(props: Props) {
properties={renderProperties} properties={renderProperties}
collectionId={collectionId} collectionId={collectionId}
showNoSourceClaims={showNoSourceClaims} showNoSourceClaims={showNoSourceClaims}
swipeLayout={swipeLayout}
/> />
))} ))}
{!timedOut && urisLength === 0 && !loading && !noEmpty && ( {!timedOut && urisLength === 0 && !loading && <div className="empty main--empty">{empty || noResultMsg}</div>}
<div className="empty main--empty">{empty || noResultMsg}</div>
)}
{timedOut && timedOutMessage && <div className="empty main--empty">{timedOutMessage}</div>} {timedOut && timedOutMessage && <div className="empty main--empty">{timedOutMessage}</div>}
</section> </section>
) : ( ) : (
@ -178,8 +203,9 @@ export default function ClaimList(props: Props) {
{urisLength > 0 && ( {urisLength > 0 && (
<ul <ul
className={classnames('ul--no-style', { className={classnames('ul--no-style', {
card: !(tileLayout || type === 'small'), card: !(tileLayout || swipeLayout || type === 'small'),
'claim-list--card-body': tileLayout, 'claim-list--card-body': tileLayout,
'swipe-list': swipeLayout,
})} })}
> >
{sortedUris.map((uri, index) => ( {sortedUris.map((uri, index) => (
@ -199,22 +225,16 @@ export default function ClaimList(props: Props) {
showHiddenByUser={showHiddenByUser} showHiddenByUser={showHiddenByUser}
collectionId={collectionId} collectionId={collectionId}
showNoSourceClaims={showNoSourceClaims} showNoSourceClaims={showNoSourceClaims}
customShouldHide={(claim: StreamClaim) => { customShouldHide={customShouldHide}
// Hack to hide spee.ch thumbnail publishes onClick={handleClaimClicked}
// If it meets these requirements, it was probably uploaded here: swipeLayout={swipeLayout}
// https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79
return claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch';
}}
onClick={(e, claim, index) => handleClaimClicked(e, claim, index)}
/> />
</React.Fragment> </React.Fragment>
))} ))}
</ul> </ul>
)} )}
{!timedOut && urisLength === 0 && !loading && !noEmpty && ( {!timedOut && urisLength === 0 && !loading && <div className="empty empty--centered">{empty || noResultMsg}</div>}
<div className="empty empty--centered">{empty || noResultMsg}</div>
)}
{!loading && timedOut && timedOutMessage && <div className="empty empty--centered">{timedOutMessage}</div>} {!loading && timedOut && timedOutMessage && <div className="empty empty--centered">{timedOutMessage}</div>}
</section> </section>
); );

View file

@ -94,6 +94,13 @@ type Props = {
doClaimSearch: ({}) => void, doClaimSearch: ({}) => void,
doToggleTagFollowDesktop: (string) => void, doToggleTagFollowDesktop: (string) => void,
doFetchViewCount: (claimIdCsv: string) => void, doFetchViewCount: (claimIdCsv: string) => void,
loadedCallback?: (number) => void,
maxClaimRender?: number,
useSkeletonScreen?: boolean,
excludeUris?: Array<string>,
swipeLayout: boolean,
}; };
function ClaimListDiscover(props: Props) { function ClaimListDiscover(props: Props) {
@ -157,6 +164,11 @@ function ClaimListDiscover(props: Props) {
empty, empty,
claimsByUri, claimsByUri,
doFetchViewCount, doFetchViewCount,
loadedCallback,
maxClaimRender,
useSkeletonScreen = true,
excludeUris = [],
swipeLayout = false,
} = props; } = props;
const didNavigateForward = history.action === 'PUSH'; const didNavigateForward = history.action === 'PUSH';
const { search } = location; const { search } = location;
@ -493,6 +505,21 @@ function ClaimListDiscover(props: Props) {
} }
function resolveOrderByOption(orderBy: string | Array<string>, sortBy: string | Array<string>) { function resolveOrderByOption(orderBy: string | Array<string>, sortBy: string | Array<string>) {
// let order_by; // peterson 038692cafc793616cceaf10b88909fecde07ad0b
//
// switch (orderBy) {
// case CS.ORDER_BY_TRENDING:
// order_by = CS.ORDER_BY_TRENDING_VALUE;
// break;
// case CS.ORDER_BY_NEW:
// order_by = CS.ORDER_BY_NEW_VALUE;
// break;
// case CS.ORDER_BY_NEW_ASC:
// order_by = CS.ORDER_BY_NEW_ASC_VALUE;
// break;
// default:
// order_by = CS.ORDER_BY_TOP_VALUE;
// }
const order_by = const order_by =
orderBy === CS.ORDER_BY_TRENDING orderBy === CS.ORDER_BY_TRENDING
? CS.ORDER_BY_TRENDING_VALUE ? CS.ORDER_BY_TRENDING_VALUE
@ -569,8 +596,12 @@ function ClaimListDiscover(props: Props) {
searchOptions={options} searchOptions={options}
showNoSourceClaims={showNoSourceClaims} showNoSourceClaims={showNoSourceClaims}
empty={empty} empty={empty}
maxClaimRender={maxClaimRender}
excludeUris={excludeUris}
loadedCallback={loadedCallback}
swipeLayout={swipeLayout}
/> />
{loading && ( {loading && useSkeletonScreen && (
<div className="claim-grid"> <div className="claim-grid">
{new Array(dynamicPageSize).fill(1).map((x, i) => ( {new Array(dynamicPageSize).fill(1).map((x, i) => (
<ClaimPreviewTile key={i} placeholder="loading" /> <ClaimPreviewTile key={i} placeholder="loading" />
@ -602,8 +633,13 @@ function ClaimListDiscover(props: Props) {
searchOptions={options} searchOptions={options}
showNoSourceClaims={hasNoSource || showNoSourceClaims} showNoSourceClaims={hasNoSource || showNoSourceClaims}
empty={empty} empty={empty}
maxClaimRender={maxClaimRender}
excludeUris={excludeUris}
loadedCallback={loadedCallback}
swipeLayout={swipeLayout}
/> />
{loading && {loading &&
useSkeletonScreen &&
new Array(dynamicPageSize) new Array(dynamicPageSize)
.fill(1) .fill(1)
.map((x, i) => ( .map((x, i) => (

View file

@ -6,6 +6,7 @@ import { isEmpty } from 'util/object';
import classnames from 'classnames'; import classnames from 'classnames';
import { isURIValid } from 'util/lbryURI'; import { isURIValid } from 'util/lbryURI';
import * as COLLECTIONS_CONSTS from 'constants/collections'; import * as COLLECTIONS_CONSTS from 'constants/collections';
import { isChannelClaim } from 'util/claim';
import { formatLbryUrlForWeb } from 'util/url'; import { formatLbryUrlForWeb } from 'util/url';
import { formatClaimPreviewTitle } from 'util/formatAriaLabel'; import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
import FileThumbnail from 'component/fileThumbnail'; import FileThumbnail from 'component/fileThumbnail';
@ -49,7 +50,7 @@ type Props = {
type: string, type: string,
banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean }, banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean },
hasVisitedUri: boolean, hasVisitedUri: boolean,
channelIsBlocked: boolean, blockedUris: Array<string>,
actions: boolean | Node | string | number, actions: boolean | Node | string | number,
properties: boolean | Node | string | number | ((Claim) => Node), properties: boolean | Node | string | number | ((Claim) => Node),
empty?: Node, empty?: Node,
@ -77,6 +78,7 @@ type Props = {
date?: any, date?: any,
indexInContainer?: number, // The index order of this component within 'containerId'. indexInContainer?: number, // The index order of this component within 'containerId'.
channelSubCount?: number, channelSubCount?: number,
swipeLayout: boolean,
}; };
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => { const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
@ -97,7 +99,6 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
streamingUrl, streamingUrl,
mediaDuration, mediaDuration,
// user properties // user properties
channelIsBlocked,
hasVisitedUri, hasVisitedUri,
// component // component
history, history,
@ -136,10 +137,11 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
disableNavigation, disableNavigation,
indexInContainer, indexInContainer,
channelSubCount, channelSubCount,
swipeLayout = false,
} = props; } = props;
const isCollection = claim && claim.value_type === 'collection'; const isCollection = claim && claim.value_type === 'collection';
const collectionClaimId = isCollection && claim && claim.claim_id; const collectionClaimId = isCollection && claim && claim.claim_id;
const listId = collectionId || collectionClaimId || null; const listId = collectionId || collectionClaimId;
const WrapperElement = wrapperElement || 'li'; const WrapperElement = wrapperElement || 'li';
const shouldFetch = const shouldFetch =
claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending); claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending);
@ -170,7 +172,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
claim.value.stream_type && claim.value.stream_type &&
// $FlowFixMe // $FlowFixMe
(claim.value.stream_type === 'audio' || claim.value.stream_type === 'video'); (claim.value.stream_type === 'audio' || claim.value.stream_type === 'video');
const isChannelUri = claim ? claim.value_type === 'channel' : false; const isChannelUri = isChannelClaim(claim, uri);
const signingChannel = claim && claim.signing_channel; const signingChannel = claim && claim.signing_channel;
const repostedChannelUri = const repostedChannelUri =
claim && claim.repost_channel_url && claim.value_type === 'channel' claim && claim.repost_channel_url && claim.value_type === 'channel'
@ -321,6 +323,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri, 'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri,
'claim-preview--pending': pending, 'claim-preview--pending': pending,
'claim-preview--collection-mine': isMyCollection && listId && type === 'listview', 'claim-preview--collection-mine': isMyCollection && listId && type === 'listview',
'swipe-list__item': swipeLayout,
})} })}
> >
{isMyCollection && listId && type === 'listview' && ( {isMyCollection && listId && type === 'listview' && (
@ -391,8 +394,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
<ChannelThumbnail uri={signingChannel.permanent_url} xsmall /> <ChannelThumbnail uri={signingChannel.permanent_url} xsmall />
</div> </div>
)} )}
{isChannelUri && !banState.muted && !claimIsMine && (
{isChannelUri && !channelIsBlocked && !claimIsMine && (
<SubscribeButton <SubscribeButton
uri={repostedChannelUri || (uri.startsWith('lbry://') ? uri : `lbry://${uri}`)} uri={repostedChannelUri || (uri.startsWith('lbry://') ? uri : `lbry://${uri}`)}
/> />

View file

@ -42,6 +42,7 @@ type Props = {
properties?: (Claim) => void, properties?: (Claim) => void,
collectionId?: string, collectionId?: string,
viewCount: string, viewCount: string,
swipeLayout: boolean,
}; };
// preview image cards used in related video functionality, channel overview page and homepage // preview image cards used in related video functionality, channel overview page and homepage
@ -66,6 +67,7 @@ function ClaimPreviewTile(props: Props) {
collectionId, collectionId,
mediaDuration, mediaDuration,
viewCount, viewCount,
swipeLayout = false,
} = props; } = props;
const isRepost = claim && claim.repost_channel_url; const isRepost = claim && claim.repost_channel_url;
const isCollection = claim && claim.value_type === 'collection'; const isCollection = claim && claim.value_type === 'collection';
@ -168,6 +170,7 @@ function ClaimPreviewTile(props: Props) {
onClick={handleClick} onClick={handleClick}
className={classnames('card claim-preview--tile', { className={classnames('card claim-preview--tile', {
'claim-preview__wrapper--channel': isChannel, 'claim-preview__wrapper--channel': isChannel,
'swipe-list__item claim-preview--horizontal-tile': swipeLayout,
})} })}
> >
<NavLink {...navLinkProps} role="none" tabIndex={-1} aria-hidden> <NavLink {...navLinkProps} role="none" tabIndex={-1} aria-hidden>

View file

@ -8,6 +8,7 @@ import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment';
import { parseURI, isURIValid } from 'util/lbryURI'; import { parseURI, isURIValid } from 'util/lbryURI';
import { WELCOME_VERSION } from 'config'; import { WELCOME_VERSION } from 'config';
import { GetLinksData } from 'util/buildHomepage'; import { GetLinksData } from 'util/buildHomepage';
import { useIsLargeScreen } from 'effects/use-screensize';
import HomePage from 'page/home'; import HomePage from 'page/home';
import BackupPage from 'page/backup'; import BackupPage from 'page/backup';
@ -125,8 +126,8 @@ function AppRouter(props: Props) {
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const resetScroll = urlParams.get('reset_scroll'); const resetScroll = urlParams.get('reset_scroll');
const hasLinkedCommentInUrl = urlParams.get(LINKED_COMMENT_QUERY_PARAM); const hasLinkedCommentInUrl = urlParams.get(LINKED_COMMENT_QUERY_PARAM);
const isLargeScreen = useIsLargeScreen();
const dynamicRoutes = GetLinksData(homepageData).filter( const dynamicRoutes = GetLinksData(homepageData, isLargeScreen).filter(
(potentialRoute: any) => potentialRoute && potentialRoute.route (potentialRoute: any) => potentialRoute && potentialRoute.route
); );

View file

@ -1,7 +1,7 @@
// @flow // @flow
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 { SITE_NAME, ENABLE_NO_SOURCE_CLAIMS } from 'config'; import { SITE_NAME } from 'config';
import React from 'react'; import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import Button from 'component/button'; import Button from 'component/button';
@ -9,6 +9,7 @@ import ClaimTilesDiscover from 'component/claimTilesDiscover';
import ClaimPreviewTile from 'component/claimPreviewTile'; import ClaimPreviewTile from 'component/claimPreviewTile';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import WaitUntilOnPage from 'component/common/wait-until-on-page'; import WaitUntilOnPage from 'component/common/wait-until-on-page';
import { useIsLargeScreen } from 'effects/use-screensize'; // have this?
import { GetLinksData } from 'util/buildHomepage'; import { GetLinksData } from 'util/buildHomepage';
type Props = { type Props = {
@ -24,9 +25,11 @@ function HomePage(props: Props) {
const showPersonalizedChannels = subscribedChannels && subscribedChannels.length > 0; const showPersonalizedChannels = subscribedChannels && subscribedChannels.length > 0;
const showPersonalizedTags = followedTags && followedTags.length > 0; const showPersonalizedTags = followedTags && followedTags.length > 0;
const showIndividualTags = showPersonalizedTags && followedTags.length < 5; const showIndividualTags = showPersonalizedTags && followedTags.length < 5;
const isLargeScreen = useIsLargeScreen();
const rowData: Array<RowDataItem> = GetLinksData( const rowData: Array<RowDataItem> = GetLinksData(
homepageData, homepageData,
isLargeScreen,
true, true,
authenticated, authenticated,
showPersonalizedChannels, showPersonalizedChannels,
@ -37,29 +40,40 @@ function HomePage(props: Props) {
showNsfw showNsfw
); );
function getRowElements(title, route, link, icon, help, options, index, pinUrls) { type SectionHeaderProps = {
const tilePlaceholder = ( title: string,
<ul className="claim-grid"> navigate?: string,
{new Array(options.pageSize || 8).fill(1).map((x, i) => ( icon?: string,
<ClaimPreviewTile showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS} key={i} placeholder /> help?: string,
))} };
</ul> const SectionHeader = ({ title, navigate = '/', icon = '', help }: SectionHeaderProps) => {
);
const claimTiles = (
<ClaimTilesDiscover {...options} showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS} hasSource pinUrls={pinUrls} />
);
return ( return (
<div key={title} className="claim-grid__wrapper">
{index !== 0 && title && typeof title === 'string' && (
<h1 className="claim-grid__header"> <h1 className="claim-grid__header">
<Button navigate={route || link} button="link"> <Button navigate={navigate} button="link">
{icon && <Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />} <Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />
<span className="claim-grid__title">{title}</span> <span className="claim-grid__title">{title}</span>
{help} {help}
</Button> </Button>
</h1> </h1>
);
};
function getRowElements(title, route, link, icon, help, options, index, pinUrls) {
const tilePlaceholder = (
<ul className="claim-grid">
{new Array(options.pageSize || 8).fill(1).map((x, i) => (
<ClaimPreviewTile key={i} placeholder />
))}
</ul>
);
const claimTiles = <ClaimTilesDiscover {...options} hasSource pinUrls={pinUrls} />;
return (
<div key={title} className="claim-grid__wrapper">
{/* category header */}
{index !== 0 && title && typeof title === 'string' && (
<SectionHeader title={__(title)} navigate={route || link} icon={icon} help={help} />
)} )}
{index === 0 && <>{claimTiles}</>} {index === 0 && <>{claimTiles}</>}
@ -69,6 +83,7 @@ function HomePage(props: Props) {
</WaitUntilOnPage> </WaitUntilOnPage>
)} )}
{/* view more button */}
{(route || link) && ( {(route || link) && (
<Button <Button
className="claim-grid__title--secondary" className="claim-grid__title--secondary"

View file

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

View file

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

@ -0,0 +1,92 @@
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.justify-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}
.items-center {
align-items: center;
}
.w-full {
width: 100%;
}
.opacity-40 {
opacity: 0.4;
}
.h-12 {
height: 3rem;
}
.mt-0 {
margin-top: 0px;
}
.mt-s {
margin-top: var(--spacing-s);
}
.mt-m {
margin-top: var(--spacing-m);
}
.mb-m {
margin-bottom: var(--spacing-m);
}
.mb-xl {
margin-bottom: var(--spacing-xl);
}
.ml-s {
margin-left: var(--spacing-s);
}
.ml-m {
margin-left: var(--spacing-m);
}
.mr-m {
margin-right: var(--spacing-m);
}
.mb-0 {
margin-bottom: 0;
}
.text-s {
font-size: var(--font-small);
}
@media (min-width: $breakpoint-small) {
.md\:items-center {
align-items: center;
}
.md\:flex-col {
flex-direction: column;
}
.md\:ml-m {
margin-left: var(--spacing-m);
}
.md\:flex-row {
flex-direction: row;
}
.md\:w-auto {
width: auto;
}
.md\:mt-0 {
margin-top: 0;
}
.md\:mb-xl {
margin-bottom: var(--spacing-xl);
}
.md\:h-12 {
height: 3rem;
}
}

View file

@ -5,7 +5,6 @@ import * as CS from 'constants/claim_search';
import { parseURI } from 'util/lbryURI'; import { parseURI } from 'util/lbryURI';
import moment from 'moment'; import moment from 'moment';
import { toCapitalCase } from 'util/string'; import { toCapitalCase } from 'util/string';
import { useIsLargeScreen } from 'effects/use-screensize';
import { CUSTOM_HOMEPAGE } from 'config'; import { CUSTOM_HOMEPAGE } from 'config';
export type RowDataItem = { export type RowDataItem = {
@ -125,6 +124,7 @@ export const getHomepageRowForCat = (cat: HomepageCat) => {
export function GetLinksData( export function GetLinksData(
all: any, // HomepageData type? all: any, // HomepageData type?
isLargeScreen: boolean,
isHomepage?: boolean = false, isHomepage?: boolean = false,
authenticated?: boolean, authenticated?: boolean,
showPersonalizedChannels?: boolean, showPersonalizedChannels?: boolean,
@ -134,8 +134,6 @@ export function GetLinksData(
showIndividualTags?: boolean, showIndividualTags?: boolean,
showNsfw?: boolean showNsfw?: boolean
) { ) {
const isLargeScreen = useIsLargeScreen();
function getPageSize(originalSize) { function getPageSize(originalSize) {
return isLargeScreen ? originalSize * (3 / 2) : originalSize; return isLargeScreen ? originalSize * (3 / 2) : originalSize;
} }

View file

@ -1,5 +1,6 @@
// @flow // @flow
import { MATURE_TAGS } from 'constants/tags'; import { MATURE_TAGS } from 'constants/tags';
import { parseURI } from 'util/lbryURI';
const matureTagMap = MATURE_TAGS.reduce((acc, tag) => ({ ...acc, [tag]: true }), {}); const matureTagMap = MATURE_TAGS.reduce((acc, tag) => ({ ...acc, [tag]: true }), {});
@ -66,6 +67,28 @@ export function filterClaims(claims: Array<Claim>, query: ?string): Array<Claim>
return claims; return claims;
} }
/**
* Determines if the claim is a channel.
*
* @param claim
* @param uri An abandoned claim will be null, so provide the `uri` as a fallback to parse.
*/
export function isChannelClaim(claim: ?Claim, uri?: string) {
// 1. parseURI can't resolve a repost's channel, so a `claim` will be needed.
// 2. parseURI is still needed to cover the case of abandoned claims.
if (claim) {
return claim.value_type === 'channel';
} else if (uri) {
try {
return Boolean(parseURI(uri).isChannel);
} catch (err) {
return false;
}
} else {
return false;
}
}
export function getChannelIdFromClaim(claim: ?Claim) { export function getChannelIdFromClaim(claim: ?Claim) {
if (claim) { if (claim) {
if (claim.value_type === 'channel') { if (claim.value_type === 'channel') {