add options card to claim search

changes

almost done

wip

wip

more

changes after comment

detect custom qs and show options

ux and mobile styling

bugfix

console logs

appstrings
This commit is contained in:
jessop 2020-02-10 14:43:24 -05:00 committed by Sean Yesmunt
parent ef2171e457
commit 9dc9d50e19
14 changed files with 415 additions and 114 deletions

View file

@ -991,4 +991,4 @@
"Email %help_link% or join our %chat_link% if you encounter any trouble verifying.": "Email %help_link% or join our %chat_link% if you encounter any trouble verifying.", "Email %help_link% or join our %chat_link% if you encounter any trouble verifying.": "Email %help_link% or join our %chat_link% if you encounter any trouble verifying.",
"Show reposts": "Show reposts", "Show reposts": "Show reposts",
"Show reposts from the creators you follow.": "Show reposts from the creators you follow." "Show reposts from the creators you follow.": "Show reposts from the creators you follow."
} }

View file

@ -4,7 +4,7 @@ import HiddenNsfwClaims from 'component/hiddenNsfwClaims';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import Button from 'component/button'; import Button from 'component/button';
import ClaimListDiscover from 'component/claimListDiscover'; import ClaimListDiscover from 'component/claimListDiscover';
import { TYPE_NEW } from 'component/claimListDiscover/view'; import * as CS from 'constants/claim_search';
type Props = { type Props = {
uri: string, uri: string,
@ -51,7 +51,7 @@ function ChannelContent(props: Props) {
{!channelIsMine && claimsInChannel > 0 && <HiddenNsfwClaims uri={uri} />} {!channelIsMine && claimsInChannel > 0 && <HiddenNsfwClaims uri={uri} />}
{claim && claimsInChannel > 0 ? ( {claim && claimsInChannel > 0 ? (
<ClaimListDiscover channelIds={[claim.claim_id]} defaultTypeSort={TYPE_NEW} /> <ClaimListDiscover channelIds={[claim.claim_id]} defaultTypeSort={CS.ORDER_BY_NEW} />
) : ( ) : (
<section className="main--empty">This channel hasn't published anything yet</section> <section className="main--empty">This channel hasn't published anything yet</section>
)} )}

View file

@ -3,6 +3,7 @@ import type { Node } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import React, { Fragment, useEffect, useState } from 'react'; import React, { Fragment, useEffect, useState } from 'react';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import * as CS from 'constants/claim_search';
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux'; import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
import Button from 'component/button'; import Button from 'component/button';
@ -11,26 +12,12 @@ import ClaimList from 'component/claimList';
import ClaimPreview from 'component/claimPreview'; import ClaimPreview from 'component/claimPreview';
import { toCapitalCase } from 'util/string'; import { toCapitalCase } from 'util/string';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import * as ICONS from 'constants/icons';
const PAGE_SIZE = 20;
export const TIME_DAY = 'day';
export const TIME_WEEK = 'week';
export const TIME_MONTH = 'month';
export const TIME_YEAR = 'year';
export const TIME_ALL = 'all';
export const TYPE_TRENDING = 'trending';
export const TYPE_TOP = 'top';
export const TYPE_NEW = 'new';
const SEARCH_TYPES = [TYPE_TRENDING, TYPE_NEW, TYPE_TOP];
const SEARCH_TIMES = [TIME_DAY, TIME_WEEK, TIME_MONTH, TIME_YEAR, TIME_ALL];
type Props = { type Props = {
uris: Array<string>, uris: Array<string>,
subscribedChannels: Array<Subscription>, subscribedChannels: Array<Subscription>,
doClaimSearch: ({}) => void, doClaimSearch: ({}) => void,
tags: Array<string>,
loading: boolean, loading: boolean,
personalView: boolean, personalView: boolean,
doToggleTagFollow: string => void, doToggleTagFollow: string => void,
@ -45,17 +32,22 @@ type Props = {
hiddenUris: Array<string>, hiddenUris: Array<string>,
hiddenNsfwMessage?: Node, hiddenNsfwMessage?: Node,
channelIds?: Array<string>, channelIds?: Array<string>,
defaultTypeSort?: string, tags: Array<string>,
defaultTimeSort?: string, orderBy?: Array<string>,
defaultOrderBy?: Array<string>, defaultOrderBy?: string,
freshness?: string,
defaultFreshness?: string,
header?: Node, header?: Node,
headerLabel?: string | Node, headerLabel?: string | Node,
name?: string, name?: string,
pageSize?: number, hideBlock?: boolean,
claimType?: Array<string>, claimType?: string | Array<string>,
defaultClaimType?: string | Array<string>,
streamType?: string | Array<string>,
defaultStreamType?: string | Array<string>,
renderProperties?: Claim => Node, renderProperties?: Claim => Node,
includeSupportAction?: boolean, includeSupportAction?: boolean,
hideBlock: boolean, noCustom?: boolean,
}; };
function ClaimListDiscover(props: Props) { function ClaimListDiscover(props: Props) {
@ -64,7 +56,6 @@ function ClaimListDiscover(props: Props) {
claimSearchByQuery, claimSearchByQuery,
tags, tags,
loading, loading,
personalView,
meta, meta,
channelIds, channelIds,
showNsfw, showNsfw,
@ -73,70 +64,93 @@ function ClaimListDiscover(props: Props) {
location, location,
hiddenUris, hiddenUris,
hiddenNsfwMessage, hiddenNsfwMessage,
defaultTypeSort,
defaultTimeSort,
defaultOrderBy, defaultOrderBy,
orderBy,
headerLabel, headerLabel,
header, header,
name, name,
claimType, claimType,
pageSize, pageSize,
hideBlock,
defaultClaimType,
streamType,
defaultStreamType,
freshness,
defaultFreshness,
renderProperties, renderProperties,
includeSupportAction, includeSupportAction,
hideBlock, noCustom,
} = props; } = props;
const didNavigateForward = history.action === 'PUSH'; const didNavigateForward = history.action === 'PUSH';
const [page, setPage] = useState(1);
const { search } = location; const { search } = location;
const [page, setPage] = useState(1);
const [forceRefresh, setForceRefresh] = useState(); const [forceRefresh, setForceRefresh] = useState();
const [expanded, setExpanded] = useState(false);
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const typeSort = urlParams.get('type') || defaultTypeSort || TYPE_TRENDING; const tagsParam = tags || urlParams.get(CS.TAGS_KEY) || null;
const timeSort = urlParams.get('time') || defaultTimeSort || TIME_WEEK; const orderParam = orderBy || urlParams.get(CS.ORDER_BY_KEY) || defaultOrderBy || CS.ORDER_BY_TRENDING;
const tagsInUrl = urlParams.get('t') || ''; const freshnessParam = freshness || urlParams.get(CS.FRESH_KEY) || defaultFreshness || CS.FRESH_WEEK;
const contentTypeParam = urlParams.get(CS.CONTENT_KEY);
const claimTypeParam =
claimType || (CS.CLAIM_TYPES.includes(contentTypeParam) && contentTypeParam) || defaultClaimType || null;
const streamTypeParam =
streamType || (CS.FILE_TYPES.includes(contentTypeParam) && contentTypeParam) || defaultStreamType || null;
const durationParam = urlParams.get(CS.DURATION_KEY) || null;
const isFiltered = () =>
Boolean(urlParams.get(CS.FRESH_KEY) || urlParams.get(CS.CONTENT_KEY) || urlParams.get(CS.DURATION_KEY));
useEffect(() => {
if (isFiltered()) setExpanded(true);
}, []);
const options: { const options: {
page_size: number, page_size: number,
page: number, page: number,
no_totals: boolean, no_totals: boolean,
any_tags: Array<string>, any_tags: Array<string>,
not_tags: Array<string>,
channel_ids: Array<string>, channel_ids: Array<string>,
not_channel_ids: Array<string>, not_channel_ids: Array<string>,
not_tags: Array<string>,
order_by: Array<string>, order_by: Array<string>,
release_time?: string, release_time?: string,
claim_type?: Array<string>, claim_type?: Array<string>,
name?: string, name?: string,
claim_type?: Array<string>, claim_type?: Array<string>,
duration?: string,
claim_type?: string | Array<string>,
stream_types?: any,
} = { } = {
page_size: pageSize || PAGE_SIZE, page_size: pageSize || CS.PAGE_SIZE,
page, page,
name, name,
claim_type: claimType || undefined, claim_type: claimType || undefined,
// no_totals makes it so the sdk doesn't have to calculate total number pages for pagination // no_totals makes it so the sdk doesn't have to calculate total number pages for pagination
// it's faster, but we will need to remove it if we start using total_pages // it's faster, but we will need to remove it if we start using total_pages
no_totals: true, no_totals: true,
any_tags: tags || [], any_tags: tagsParam || [],
channel_ids: channelIds || [], channel_ids: channelIds || [],
not_channel_ids: not_channel_ids:
// If channelIds were passed in, we don't need not_channel_ids // If channelIds were passed in, we don't need not_channel_ids
!channelIds && hiddenUris && hiddenUris.length ? hiddenUris.map(hiddenUri => hiddenUri.split('#')[1]) : [], !channelIds && hiddenUris && hiddenUris.length ? hiddenUris.map(hiddenUri => hiddenUri.split('#')[1]) : [],
not_tags: !showNsfw ? MATURE_TAGS : [], not_tags: !showNsfw ? MATURE_TAGS : [],
order_by: order_by:
defaultOrderBy || orderParam === CS.ORDER_BY_TRENDING
(typeSort === TYPE_TRENDING ? CS.ORDER_BY_TRENDING_VALUE
? ['trending_group', 'trending_mixed'] : orderParam === CS.ORDER_BY_NEW
: typeSort === TYPE_NEW ? CS.ORDER_BY_NEW_VALUE
? ['release_time'] : CS.ORDER_BY_TOP_VALUE, // Sort by top
: ['effective_amount']), // Sort by top
}; };
if (orderParam === CS.ORDER_BY_TOP && freshnessParam !== CS.FRESH_ALL) {
if (typeSort === TYPE_TOP && timeSort !== TIME_ALL) {
options.release_time = `>${Math.floor( options.release_time = `>${Math.floor(
moment() moment()
.subtract(1, timeSort) .subtract(1, freshnessParam)
.startOf('hour') .startOf('hour')
.unix() .unix()
)}`; )}`;
} else if (typeSort === TYPE_NEW || typeSort === TYPE_TRENDING) { } else if (orderParam === CS.ORDER_BY_NEW || orderParam === CS.ORDER_BY_TRENDING) {
// Warning - hack below // Warning - hack below
// If users are following more than 10 channels or tags, limit results to stuff less than a year old // If users are following more than 10 channels or tags, limit results to stuff less than a year old
// For more than 20, drop it down to 6 months // For more than 20, drop it down to 6 months
@ -145,14 +159,14 @@ function ClaimListDiscover(props: Props) {
if (options.channel_ids.length > 20 || options.any_tags.length > 20) { if (options.channel_ids.length > 20 || options.any_tags.length > 20) {
options.release_time = `>${Math.floor( options.release_time = `>${Math.floor(
moment() moment()
.subtract(6, TIME_MONTH) .subtract(3, CS.FRESH_MONTH)
.startOf('week') .startOf('week')
.unix() .unix()
)}`; )}`;
} else if (options.channel_ids.length > 10 || options.any_tags.length > 10) { } else if (options.channel_ids.length > 10 || options.any_tags.length > 10) {
options.release_time = `>${Math.floor( options.release_time = `>${Math.floor(
moment() moment()
.subtract(1, TIME_YEAR) .subtract(1, CS.FRESH_YEAR)
.startOf('week') .startOf('week')
.unix() .unix()
)}`; )}`;
@ -166,6 +180,26 @@ function ClaimListDiscover(props: Props) {
} }
} }
if (durationParam) {
if (durationParam === CS.DURATION_SHORT) {
options.duration = '<=1800';
} else if (durationParam === CS.DURATION_LONG) {
options.duration = '>=1800';
}
}
if (streamTypeParam) {
if (streamTypeParam !== CS.CONTENT_ALL) {
options.stream_types = [streamTypeParam];
}
}
if (claimTypeParam) {
if (claimTypeParam !== CS.CONTENT_ALL) {
options.claim_type = [claimTypeParam];
}
}
if (!showReposts) { if (!showReposts) {
options.claim_type = options.claim_type =
options.claim_type === undefined options.claim_type === undefined
@ -179,7 +213,7 @@ function ClaimListDiscover(props: Props) {
const shouldPerformSearch = const shouldPerformSearch =
uris.length === 0 || uris.length === 0 ||
didNavigateForward || didNavigateForward ||
(!loading && uris.length < PAGE_SIZE * page && uris.length % PAGE_SIZE === 0); (!loading && uris.length < CS.PAGE_SIZE * page && uris.length % CS.PAGE_SIZE === 0);
// Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time // Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time
const optionsStringForEffect = JSON.stringify(options); const optionsStringForEffect = JSON.stringify(options);
@ -191,13 +225,13 @@ function ClaimListDiscover(props: Props) {
again: ( again: (
<Button <Button
button="link" button="link"
label={__('Please try again in a few seconds.')} label={__('try again in a few seconds.')}
onClick={() => setForceRefresh(Date.now())} onClick={() => setForceRefresh(Date.now())}
/> />
), ),
}} }}
> >
Sorry, your request timed out. %again% Sorry, your request timed out. Modify your options or %again%
</I18nMessage> </I18nMessage>
</p> </p>
<p> <p>
@ -212,28 +246,45 @@ function ClaimListDiscover(props: Props) {
</div> </div>
); );
function getSearch() { function handleChange(change) {
let search = `?`; const url = buildUrl(change);
if (!personalView) {
search += `t=${tagsInUrl}&`;
}
return search;
}
function handleTypeSort(newTypeSort) {
let url = `${getSearch()}type=${newTypeSort}`;
if (newTypeSort === TYPE_TOP) {
url += `&time=${timeSort}`;
}
setPage(1); setPage(1);
history.push(url); history.push(url);
} }
function handleTimeSort(newTimeSort) { function buildUrl(delta) {
setPage(1); const newUrlParams = new URLSearchParams();
history.push(`${getSearch()}type=${typeSort}&time=${newTimeSort}`); CS.KEYS.forEach(k => {
// $FlowFixMe append() can't take null as second arg, but get() can return null
if (urlParams.get(k) !== null) newUrlParams.append(k, urlParams.get(k));
});
switch (delta.key) {
case CS.ORDER_BY_KEY:
newUrlParams.set(CS.ORDER_BY_KEY, delta.value);
break;
case CS.FRESH_KEY:
newUrlParams.set(CS.FRESH_KEY, delta.value);
break;
case CS.CONTENT_KEY:
if (delta.value === CS.CLAIM_CHANNEL || delta.value === CS.CLAIM_REPOST) {
newUrlParams.delete(CS.DURATION_KEY);
newUrlParams.set(CS.CONTENT_KEY, delta.value);
} else if (delta.value === CS.CONTENT_ALL) {
newUrlParams.delete(CS.CONTENT_KEY);
} else {
newUrlParams.set(CS.CONTENT_KEY, delta.value);
}
break;
case CS.DURATION_KEY:
if (delta.value === CS.DURATION_ALL) {
newUrlParams.delete(CS.DURATION_KEY);
} else {
newUrlParams.set(CS.DURATION_KEY, delta.value);
}
break;
}
return `?${newUrlParams.toString()}`;
} }
function handleScrollBottom() { function handleScrollBottom() {
@ -251,39 +302,151 @@ function ClaimListDiscover(props: Props) {
const defaultHeader = ( const defaultHeader = (
<Fragment> <Fragment>
{SEARCH_TYPES.map(type => ( <div className={'claim-search__wrapper'}>
<Button <div className={'claim-search__top'}>
key={type} <div className={'claim-search__top-row'}>
button="alt" {CS.ORDER_BY_TYPES.map(type => (
onClick={() => handleTypeSort(type)} <Button
className={classnames(`button-toggle button-toggle--${type}`, { key={type}
'button-toggle--active': typeSort === type, button="alt"
})} onClick={e =>
icon={toCapitalCase(type)} handleChange({
label={__(toCapitalCase(type))} key: CS.ORDER_BY_KEY,
/> value: type,
))} })
}
className={classnames(`button-toggle button-toggle--${type}`, {
'button-toggle--active': orderParam === type,
})}
disabled={orderBy}
icon={toCapitalCase(type)}
label={__(toCapitalCase(type))}
/>
))}
</div>
<div>
{!noCustom && (
<Button
button={'alt'}
aria-label={__('More')}
className={classnames(`button-toggle button-toggle--top button-toggle--more`, {
'button-toggle--custom': isFiltered(),
})}
icon={toCapitalCase(ICONS.SLIDERS)}
onClick={() => setExpanded(!expanded)}
/>
)}
</div>
</div>
{expanded && (
<>
<div className={classnames('card--inline', `claim-search__menus`)}>
{/* FRESHNESS FIELD */}
{orderParam === CS.ORDER_BY_TOP && (
<div className={'claim-search__input-container'}>
<FormField
className="claim-search__dropdown"
type="select"
name="trending_time"
label={__('How Fresh')}
value={freshnessParam}
onChange={e =>
handleChange({
key: CS.FRESH_KEY,
value: e.target.value,
})
}
>
{CS.FRESH_TYPES.map(time => (
<option key={time} value={time}>
{/* i18fixme */}
{time === CS.FRESH_DAY && __('Today')}
{time !== CS.FRESH_ALL &&
time !== CS.FRESH_DAY &&
__('This ' + toCapitalCase(time)) /* yes, concat before i18n, since it is read from const */}
{time === CS.FRESH_ALL && __('All time')}
</option>
))}
</FormField>
</div>
)}
{/* CONTENT_TYPES FIELD */}
<div
className={classnames('claim-search__input-container', {
'claim-search__input-container--selected': contentTypeParam,
})}
>
<FormField
className={classnames('claim-search__dropdown', {
'claim-search__dropdown--selected': contentTypeParam,
})}
type="select"
name="claimType"
label={__('Content Type')}
value={contentTypeParam || CS.CONTENT_ALL}
onChange={e =>
handleChange({
key: CS.CONTENT_KEY,
value: e.target.value,
})
}
>
{CS.CONTENT_TYPES.map(type => {
if (type !== CS.CLAIM_CHANNEL || (type === CS.CLAIM_CHANNEL && !channelIds)) {
return (
<option key={type} value={type}>
{/* i18fixme */}
{type === CS.CLAIM_CHANNEL && __('Channel')}
{type === CS.CLAIM_REPOST && __('Repost')}
{type === CS.FILE_VIDEO && __('Video')}
{type === CS.FILE_AUDIO && __('Audio')}
{type === CS.FILE_DOCUMENT && __('Document')}
{type === CS.CONTENT_ALL && __('Any')}
</option>
);
}
})}
</FormField>
</div>
{options.claim_type !== CS.CLAIM_CHANNEL &&
options.claim_type !== CS.CLAIM_REPOST &&
JSON.stringify(options.stream_types) !== JSON.stringify([CS.FILE_DOCUMENT]) && (
<>
{/* DURATIONS FIELD */}
<div className={'claim-search__input-container'}>
<FormField
className={classnames('claim-search__dropdown', {
'claim-search__dropdown--selected': durationParam,
})}
label={__('Duration')}
type="select"
name="duration"
value={durationParam || CS.DURATION_ALL}
onChange={e =>
handleChange({
key: CS.DURATION_KEY,
value: e.target.value,
})
}
>
{CS.DURATION_TYPES.map(dur => (
<option key={dur} value={dur}>
{/* i18fixme */}
{dur === CS.DURATION_SHORT && __('Short')}
{dur === CS.DURATION_LONG && __('Long')}
{dur === CS.DURATION_ALL && __('Any')}
</option>
))}
</FormField>
</div>
</>
)}
</div>
</>
)}
</div>
{typeSort === 'top' && (
<FormField
className="claim-list__dropdown"
type="select"
name="trending_time"
value={timeSort}
onChange={e => handleTimeSort(e.target.value)}
>
{SEARCH_TIMES.map(time => (
<option key={time} value={time}>
{/* i18fixme */}
{time === TIME_DAY && __('Today')}
{time !== TIME_ALL &&
time !== TIME_DAY &&
__('This ' + toCapitalCase(time)) /* yes, concat before i18n, since it is read from const */}
{time === TIME_ALL && __('All time')}
</option>
))}
</FormField>
)}
{hasMatureTags && hiddenNsfwMessage} {hasMatureTags && hiddenNsfwMessage}
</Fragment> </Fragment>
); );
@ -299,7 +462,7 @@ function ClaimListDiscover(props: Props) {
headerAltControls={meta} headerAltControls={meta}
onScrollBottom={handleScrollBottom} onScrollBottom={handleScrollBottom}
page={page} page={page}
pageSize={PAGE_SIZE} pageSize={CS.PAGE_SIZE}
empty={noResults} empty={noResults}
renderProperties={renderProperties} renderProperties={renderProperties}
includeSupportAction={includeSupportAction} includeSupportAction={includeSupportAction}
@ -307,7 +470,7 @@ function ClaimListDiscover(props: Props) {
/> />
<div className="card"> <div className="card">
{loading && new Array(PAGE_SIZE).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)} {loading && new Array(CS.PAGE_SIZE).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
</div> </div>
</React.Fragment> </React.Fragment>
); );

View file

@ -412,4 +412,24 @@ export const icons = {
<polyline points="20 6 9 17 4 12" /> <polyline points="20 6 9 17 4 12" />
</g> </g>
), ),
[ICONS.SLIDERS]: buildIcon(
<g>
<line x1="4" y1="21" x2="4" y2="14" />
<line x1="4" y1="10" x2="4" y2="3" />
<line x1="12" y1="21" x2="12" y2="12" />
<line x1="12" y1="8" x2="12" y2="3" />
<line x1="20" y1="21" x2="20" y2="16" />
<line x1="20" y1="12" x2="20" y2="3" />
<line x1="1" y1="14" x2="7" y2="14" />
<line x1="9" y1="8" x2="15" y2="8" />
<line x1="17" y1="16" x2="23" y2="16" />
</g>
),
[ICONS.SCIENCE]: buildIcon(
<g>
<path d="M 8.4312337,1.6285136 V 9.4232264 L 2.2367584,22.725564 H 22.030217 L 15.773797,9.2902071 V 1.6285136 Z" />
<path d="M 4.2426407,18.166369 H 12.197591" />
<path d="m 6.363961,14.188893 h 5.701048" />
</g>
),
}; };

View file

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import ClaimListDiscover from 'component/claimListDiscover'; import ClaimListDiscover from 'component/claimListDiscover';
import { TYPE_TOP, TIME_ALL } from 'component/claimListDiscover/view'; import * as CS from 'constants/claim_search';
import Nag from 'component/common/nag'; import Nag from 'component/common/nag';
type Props = { type Props = {
@ -22,7 +22,12 @@ function UserChannelFollowIntro(props: Props) {
)} )}
</p> </p>
<div className="section__body"> <div className="section__body">
<ClaimListDiscover defaultTypeSort={TYPE_TOP} defaultTimeSort={TIME_ALL} claimType="channel" hideBlock /> <ClaimListDiscover
defaultTypeSort={CS.ORDER_BY_TOP}
defaultTimeSort={CS.FRESH_ALL}
claimType="channel"
hideBlock
/>
{followingCount > 0 && ( {followingCount > 0 && (
<Nag <Nag
type="helpful" type="helpful"

View file

@ -0,0 +1,41 @@
export const PAGE_SIZE = 20;
export const FRESH_KEY = 'fresh';
export const ORDER_BY_KEY = 'order';
export const DURATION_KEY = 'duration';
export const TAGS_KEY = 't';
export const CONTENT_KEY = 'content';
export const FRESH_DAY = 'day';
export const FRESH_WEEK = 'week';
export const FRESH_MONTH = 'month';
export const FRESH_YEAR = 'year';
export const FRESH_ALL = 'all';
export const FRESH_TYPES = [FRESH_DAY, FRESH_WEEK, FRESH_MONTH, FRESH_YEAR, FRESH_ALL];
export const ORDER_BY_TRENDING = 'trending';
export const ORDER_BY_TRENDING_VALUE = ['trending_group', 'trending_mixed'];
export const ORDER_BY_TOP = 'top';
export const ORDER_BY_TOP_VALUE = ['effective_amount'];
export const ORDER_BY_NEW = 'new';
export const ORDER_BY_NEW_VALUE = ['release_time'];
export const ORDER_BY_TYPES = [ORDER_BY_TRENDING, ORDER_BY_NEW, ORDER_BY_TOP];
export const DURATION_SHORT = 'short';
export const DURATION_LONG = 'long';
export const DURATION_ALL = 'all';
export const DURATION_TYPES = [DURATION_ALL, DURATION_LONG, DURATION_SHORT];
export const FILE_VIDEO = 'video';
export const FILE_AUDIO = 'audio';
export const FILE_DOCUMENT = 'document';
export const FILE_TYPES = [FILE_VIDEO, FILE_AUDIO, FILE_DOCUMENT];
export const CLAIM_CHANNEL = 'channel';
export const CLAIM_STREAM = 'stream';
export const CLAIM_REPOST = 'repost';
export const CLAIM_TYPES = [CLAIM_CHANNEL, CLAIM_REPOST, CLAIM_STREAM];
export const CONTENT_ALL = 'all';
export const CONTENT_TYPES = [CONTENT_ALL, CLAIM_CHANNEL, CLAIM_REPOST, ...FILE_TYPES];
export const KEYS = [ORDER_BY_KEY, TAGS_KEY, FRESH_KEY, CONTENT_KEY, DURATION_KEY];

View file

@ -89,3 +89,5 @@ export const TEXT = 'FileText';
export const DOWNLOADABLE = 'Downloadable'; export const DOWNLOADABLE = 'Downloadable';
export const REPOST = 'Repeat'; export const REPOST = 'Repeat';
export const VALIDATED = 'Check'; export const VALIDATED = 'Check';
export const SLIDERS = 'Sliders';
export const SCIENCE = 'Science';

View file

@ -1,6 +1,7 @@
// @flow // @flow
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import { ORDER_BY_NEW } from 'constants/claim_search';
import React from 'react'; import React from 'react';
import ChannelsFollowingDiscoverPage from 'page/channelsFollowingDiscover'; import ChannelsFollowingDiscoverPage from 'page/channelsFollowingDiscover';
import ClaimListDiscover from 'component/claimListDiscover'; import ClaimListDiscover from 'component/claimListDiscover';
@ -8,8 +9,6 @@ import Page from 'component/page';
import Button from 'component/button'; import Button from 'component/button';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import { TYPE_NEW } from 'component/claimListDiscover/view';
type Props = { type Props = {
subscribedChannels: Array<Subscription>, subscribedChannels: Array<Subscription>,
}; };
@ -29,7 +28,7 @@ function ChannelsFollowingPage(props: Props) {
{__('Following')} {__('Following')}
</span> </span>
} }
defaultTypeSort={TYPE_NEW} defaultOrderBy={ORDER_BY_NEW}
channelIds={subscribedChannels.map(sub => sub.uri.split('#')[1])} channelIds={subscribedChannels.map(sub => sub.uri.split('#')[1])}
meta={ meta={
<Button <Button

View file

@ -6,7 +6,7 @@ import Page from 'component/page';
import Button from 'component/button'; import Button from 'component/button';
import ClaimTilesDiscover from 'component/claimTilesDiscover'; import ClaimTilesDiscover from 'component/claimTilesDiscover';
import ClaimListDiscover from 'component/claimListDiscover'; import ClaimListDiscover from 'component/claimListDiscover';
import { TYPE_TOP, TIME_ALL } from 'component/claimListDiscover/view'; import * as CS from 'constants/claim_search';
import { toCapitalCase } from 'util/string'; import { toCapitalCase } from 'util/string';
type Props = { type Props = {
@ -115,7 +115,7 @@ function ChannelsFollowingDiscover(props: Props) {
</div> </div>
))} ))}
<h1 className="claim-grid__title">{__('More Channels')}</h1> <h1 className="claim-grid__title">{__('More Channels')}</h1>
<ClaimListDiscover defaultTypeSort={TYPE_TOP} defaultTimeSort={TIME_ALL} claimType="channel" /> <ClaimListDiscover defaultTypeSort={CS.ORDER_BY_TOP} defaultTimeSort={CS.FRESH_ALL} claimType="channel" />
</Page> </Page>
); );
} }

View file

@ -3,7 +3,7 @@ import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import ClaimListDiscover from 'component/claimListDiscover'; import ClaimListDiscover from 'component/claimListDiscover';
import ClaimEffectiveAmount from 'component/claimEffectiveAmount'; import ClaimEffectiveAmount from 'component/claimEffectiveAmount';
import { TYPE_TOP, TIME_ALL } from 'component/claimListDiscover/view'; import { ORDER_BY_TOP, FRESH_ALL } from 'constants/claim_search';
type Props = { type Props = {
name: string, name: string,
@ -16,9 +16,8 @@ function TopPage(props: Props) {
<Page> <Page>
<ClaimListDiscover <ClaimListDiscover
name={name} name={name}
defaultTypeSort={TYPE_TOP} defaultFreshness={FRESH_ALL}
defaultTimeSort={TIME_ALL} defaultOrderBy={ORDER_BY_TOP}
defaultOrderBy={['effective_amount']}
includeSupportAction includeSupportAction
renderProperties={claim => ( renderProperties={claim => (
<span className="media__subtitle"> <span className="media__subtitle">

View file

@ -33,6 +33,7 @@
@import 'component/pagination'; @import 'component/pagination';
@import 'component/placeholder'; @import 'component/placeholder';
@import 'component/search'; @import 'component/search';
@import 'component/claim-search';
@import 'component/section'; @import 'component/section';
@import 'component/snack-bar'; @import 'component/snack-bar';
@import 'component/spinner'; @import 'component/spinner';

View file

@ -78,7 +78,9 @@ svg + .button__label,
border-radius: 0; border-radius: 0;
margin: 0; margin: 0;
background-color: var(--color-card-background); background-color: var(--color-card-background);
@media (max-width: $breakpoint-small) {
padding: var(--spacing-medium) var(--spacing-small);
}
svg { svg {
opacity: 0.5; opacity: 0.5;
} }
@ -109,3 +111,10 @@ svg + .button__label,
text-decoration: none; text-decoration: none;
} }
} }
.button-toggle--custom {
color: var(--color-primary);
svg {
opacity: 1;
}
}

View file

@ -0,0 +1,61 @@
.claim-search__wrapper {
width: 100%;
}
.claim-search__menus {
background-color: var(--color-card-background);
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
padding: var(--spacing-medium);
padding-bottom: var(--spacing-small);
margin-top: var(--spacing-medium);
@media (max-width: $breakpoint-small) {
flex-direction: column;
align-items: flex-start;
}
color: var(--color-text-subtitle);
}
.claim-search__dropdown {
padding: 0 var(--spacing-medium);
max-width: 400px;
@media (max-width: $breakpoint-small) {
margin-left: 0;
}
background-color: var(--color-card-background);
width: var(--option-select-width);
}
.claim-search__dropdown--selected {
background-color: var(--color-primary-alt);
}
.claim-search__input-container {
&:not(:first-of-type) {
padding-left: var(--spacing-medium);
}
@media (max-width: $breakpoint-small) {
&:not(:first-of-type) {
margin-top: var(--spacing-small);
}
padding-left: 0px;
&:not(:first-of-type) {
padding-left: 0;
}
}
}
.claim-search__extra {
display: flex;
flex-direction: row;
align-items: center;
}
.claim-search__top {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}

View file

@ -26,6 +26,7 @@ $breakpoint-medium: 1150px;
--floating-viewer-height: 18rem; // 32 * 9/16 --floating-viewer-height: 18rem; // 32 * 9/16
--floating-viewer-info-height: 5rem; --floating-viewer-info-height: 5rem;
--floating-viewer-container-height: calc(var(--floating-viewer-height) + var(--floating-viewer-info-height)); --floating-viewer-container-height: calc(var(--floating-viewer-height) + var(--floating-viewer-info-height));
--option-select-width: 8rem;
// Text // Text
--text-max-width: 660px; --text-max-width: 660px;