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:
parent
ef2171e457
commit
9dc9d50e19
14 changed files with 415 additions and 114 deletions
|
@ -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.",
|
||||
"Show reposts": "Show reposts",
|
||||
"Show reposts from the creators you follow.": "Show reposts from the creators you follow."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import HiddenNsfwClaims from 'component/hiddenNsfwClaims';
|
|||
import { withRouter } from 'react-router-dom';
|
||||
import Button from 'component/button';
|
||||
import ClaimListDiscover from 'component/claimListDiscover';
|
||||
import { TYPE_NEW } from 'component/claimListDiscover/view';
|
||||
import * as CS from 'constants/claim_search';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -51,7 +51,7 @@ function ChannelContent(props: Props) {
|
|||
{!channelIsMine && claimsInChannel > 0 && <HiddenNsfwClaims uri={uri} />}
|
||||
|
||||
{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>
|
||||
)}
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { Node } from 'react';
|
|||
import classnames from 'classnames';
|
||||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import { withRouter } from 'react-router';
|
||||
import * as CS from 'constants/claim_search';
|
||||
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
|
||||
import { FormField } from 'component/common/form';
|
||||
import Button from 'component/button';
|
||||
|
@ -11,26 +12,12 @@ import ClaimList from 'component/claimList';
|
|||
import ClaimPreview from 'component/claimPreview';
|
||||
import { toCapitalCase } from 'util/string';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
|
||||
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];
|
||||
import * as ICONS from 'constants/icons';
|
||||
|
||||
type Props = {
|
||||
uris: Array<string>,
|
||||
subscribedChannels: Array<Subscription>,
|
||||
doClaimSearch: ({}) => void,
|
||||
tags: Array<string>,
|
||||
loading: boolean,
|
||||
personalView: boolean,
|
||||
doToggleTagFollow: string => void,
|
||||
|
@ -45,17 +32,22 @@ type Props = {
|
|||
hiddenUris: Array<string>,
|
||||
hiddenNsfwMessage?: Node,
|
||||
channelIds?: Array<string>,
|
||||
defaultTypeSort?: string,
|
||||
defaultTimeSort?: string,
|
||||
defaultOrderBy?: Array<string>,
|
||||
tags: Array<string>,
|
||||
orderBy?: Array<string>,
|
||||
defaultOrderBy?: string,
|
||||
freshness?: string,
|
||||
defaultFreshness?: string,
|
||||
header?: Node,
|
||||
headerLabel?: string | Node,
|
||||
name?: string,
|
||||
pageSize?: number,
|
||||
claimType?: Array<string>,
|
||||
hideBlock?: boolean,
|
||||
claimType?: string | Array<string>,
|
||||
defaultClaimType?: string | Array<string>,
|
||||
streamType?: string | Array<string>,
|
||||
defaultStreamType?: string | Array<string>,
|
||||
renderProperties?: Claim => Node,
|
||||
includeSupportAction?: boolean,
|
||||
hideBlock: boolean,
|
||||
noCustom?: boolean,
|
||||
};
|
||||
|
||||
function ClaimListDiscover(props: Props) {
|
||||
|
@ -64,7 +56,6 @@ function ClaimListDiscover(props: Props) {
|
|||
claimSearchByQuery,
|
||||
tags,
|
||||
loading,
|
||||
personalView,
|
||||
meta,
|
||||
channelIds,
|
||||
showNsfw,
|
||||
|
@ -73,70 +64,93 @@ function ClaimListDiscover(props: Props) {
|
|||
location,
|
||||
hiddenUris,
|
||||
hiddenNsfwMessage,
|
||||
defaultTypeSort,
|
||||
defaultTimeSort,
|
||||
defaultOrderBy,
|
||||
orderBy,
|
||||
headerLabel,
|
||||
header,
|
||||
name,
|
||||
claimType,
|
||||
pageSize,
|
||||
hideBlock,
|
||||
defaultClaimType,
|
||||
streamType,
|
||||
defaultStreamType,
|
||||
freshness,
|
||||
defaultFreshness,
|
||||
renderProperties,
|
||||
includeSupportAction,
|
||||
hideBlock,
|
||||
noCustom,
|
||||
} = props;
|
||||
const didNavigateForward = history.action === 'PUSH';
|
||||
const [page, setPage] = useState(1);
|
||||
const { search } = location;
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [forceRefresh, setForceRefresh] = useState();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const urlParams = new URLSearchParams(search);
|
||||
const typeSort = urlParams.get('type') || defaultTypeSort || TYPE_TRENDING;
|
||||
const timeSort = urlParams.get('time') || defaultTimeSort || TIME_WEEK;
|
||||
const tagsInUrl = urlParams.get('t') || '';
|
||||
const tagsParam = tags || urlParams.get(CS.TAGS_KEY) || null;
|
||||
const orderParam = orderBy || urlParams.get(CS.ORDER_BY_KEY) || defaultOrderBy || CS.ORDER_BY_TRENDING;
|
||||
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: {
|
||||
page_size: number,
|
||||
page: number,
|
||||
no_totals: boolean,
|
||||
any_tags: Array<string>,
|
||||
not_tags: Array<string>,
|
||||
channel_ids: Array<string>,
|
||||
not_channel_ids: Array<string>,
|
||||
not_tags: Array<string>,
|
||||
order_by: Array<string>,
|
||||
release_time?: string,
|
||||
claim_type?: Array<string>,
|
||||
name?: 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,
|
||||
name,
|
||||
claim_type: claimType || undefined,
|
||||
// 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
|
||||
no_totals: true,
|
||||
any_tags: tags || [],
|
||||
any_tags: tagsParam || [],
|
||||
channel_ids: channelIds || [],
|
||||
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]) : [],
|
||||
not_tags: !showNsfw ? MATURE_TAGS : [],
|
||||
order_by:
|
||||
defaultOrderBy ||
|
||||
(typeSort === TYPE_TRENDING
|
||||
? ['trending_group', 'trending_mixed']
|
||||
: typeSort === TYPE_NEW
|
||||
? ['release_time']
|
||||
: ['effective_amount']), // Sort by top
|
||||
orderParam === CS.ORDER_BY_TRENDING
|
||||
? CS.ORDER_BY_TRENDING_VALUE
|
||||
: orderParam === CS.ORDER_BY_NEW
|
||||
? CS.ORDER_BY_NEW_VALUE
|
||||
: CS.ORDER_BY_TOP_VALUE, // Sort by top
|
||||
};
|
||||
|
||||
if (typeSort === TYPE_TOP && timeSort !== TIME_ALL) {
|
||||
if (orderParam === CS.ORDER_BY_TOP && freshnessParam !== CS.FRESH_ALL) {
|
||||
options.release_time = `>${Math.floor(
|
||||
moment()
|
||||
.subtract(1, timeSort)
|
||||
.subtract(1, freshnessParam)
|
||||
.startOf('hour')
|
||||
.unix()
|
||||
)}`;
|
||||
} else if (typeSort === TYPE_NEW || typeSort === TYPE_TRENDING) {
|
||||
} else if (orderParam === CS.ORDER_BY_NEW || orderParam === CS.ORDER_BY_TRENDING) {
|
||||
// Warning - hack below
|
||||
// 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
|
||||
|
@ -145,14 +159,14 @@ function ClaimListDiscover(props: Props) {
|
|||
if (options.channel_ids.length > 20 || options.any_tags.length > 20) {
|
||||
options.release_time = `>${Math.floor(
|
||||
moment()
|
||||
.subtract(6, TIME_MONTH)
|
||||
.subtract(3, CS.FRESH_MONTH)
|
||||
.startOf('week')
|
||||
.unix()
|
||||
)}`;
|
||||
} else if (options.channel_ids.length > 10 || options.any_tags.length > 10) {
|
||||
options.release_time = `>${Math.floor(
|
||||
moment()
|
||||
.subtract(1, TIME_YEAR)
|
||||
.subtract(1, CS.FRESH_YEAR)
|
||||
.startOf('week')
|
||||
.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) {
|
||||
options.claim_type =
|
||||
options.claim_type === undefined
|
||||
|
@ -179,7 +213,7 @@ function ClaimListDiscover(props: Props) {
|
|||
const shouldPerformSearch =
|
||||
uris.length === 0 ||
|
||||
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
|
||||
const optionsStringForEffect = JSON.stringify(options);
|
||||
|
||||
|
@ -191,13 +225,13 @@ function ClaimListDiscover(props: Props) {
|
|||
again: (
|
||||
<Button
|
||||
button="link"
|
||||
label={__('Please try again in a few seconds.')}
|
||||
label={__('try again in a few seconds.')}
|
||||
onClick={() => setForceRefresh(Date.now())}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
Sorry, your request timed out. %again%
|
||||
Sorry, your request timed out. Modify your options or %again%
|
||||
</I18nMessage>
|
||||
</p>
|
||||
<p>
|
||||
|
@ -212,28 +246,45 @@ function ClaimListDiscover(props: Props) {
|
|||
</div>
|
||||
);
|
||||
|
||||
function getSearch() {
|
||||
let search = `?`;
|
||||
if (!personalView) {
|
||||
search += `t=${tagsInUrl}&`;
|
||||
}
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
function handleTypeSort(newTypeSort) {
|
||||
let url = `${getSearch()}type=${newTypeSort}`;
|
||||
if (newTypeSort === TYPE_TOP) {
|
||||
url += `&time=${timeSort}`;
|
||||
}
|
||||
|
||||
function handleChange(change) {
|
||||
const url = buildUrl(change);
|
||||
setPage(1);
|
||||
history.push(url);
|
||||
}
|
||||
|
||||
function handleTimeSort(newTimeSort) {
|
||||
setPage(1);
|
||||
history.push(`${getSearch()}type=${typeSort}&time=${newTimeSort}`);
|
||||
function buildUrl(delta) {
|
||||
const newUrlParams = new URLSearchParams();
|
||||
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() {
|
||||
|
@ -251,39 +302,151 @@ function ClaimListDiscover(props: Props) {
|
|||
|
||||
const defaultHeader = (
|
||||
<Fragment>
|
||||
{SEARCH_TYPES.map(type => (
|
||||
<Button
|
||||
key={type}
|
||||
button="alt"
|
||||
onClick={() => handleTypeSort(type)}
|
||||
className={classnames(`button-toggle button-toggle--${type}`, {
|
||||
'button-toggle--active': typeSort === type,
|
||||
})}
|
||||
icon={toCapitalCase(type)}
|
||||
label={__(toCapitalCase(type))}
|
||||
/>
|
||||
))}
|
||||
<div className={'claim-search__wrapper'}>
|
||||
<div className={'claim-search__top'}>
|
||||
<div className={'claim-search__top-row'}>
|
||||
{CS.ORDER_BY_TYPES.map(type => (
|
||||
<Button
|
||||
key={type}
|
||||
button="alt"
|
||||
onClick={e =>
|
||||
handleChange({
|
||||
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}
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -299,7 +462,7 @@ function ClaimListDiscover(props: Props) {
|
|||
headerAltControls={meta}
|
||||
onScrollBottom={handleScrollBottom}
|
||||
page={page}
|
||||
pageSize={PAGE_SIZE}
|
||||
pageSize={CS.PAGE_SIZE}
|
||||
empty={noResults}
|
||||
renderProperties={renderProperties}
|
||||
includeSupportAction={includeSupportAction}
|
||||
|
@ -307,7 +470,7 @@ function ClaimListDiscover(props: Props) {
|
|||
/>
|
||||
|
||||
<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>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -412,4 +412,24 @@ export const icons = {
|
|||
<polyline points="20 6 9 17 4 12" />
|
||||
</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>
|
||||
),
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
|
@ -22,7 +22,12 @@ function UserChannelFollowIntro(props: Props) {
|
|||
)}
|
||||
</p>
|
||||
<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 && (
|
||||
<Nag
|
||||
type="helpful"
|
||||
|
|
41
ui/constants/claim_search.js
Normal file
41
ui/constants/claim_search.js
Normal 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];
|
|
@ -89,3 +89,5 @@ export const TEXT = 'FileText';
|
|||
export const DOWNLOADABLE = 'Downloadable';
|
||||
export const REPOST = 'Repeat';
|
||||
export const VALIDATED = 'Check';
|
||||
export const SLIDERS = 'Sliders';
|
||||
export const SCIENCE = 'Science';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
import * as PAGES from 'constants/pages';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import { ORDER_BY_NEW } from 'constants/claim_search';
|
||||
import React from 'react';
|
||||
import ChannelsFollowingDiscoverPage from 'page/channelsFollowingDiscover';
|
||||
import ClaimListDiscover from 'component/claimListDiscover';
|
||||
|
@ -8,8 +9,6 @@ import Page from 'component/page';
|
|||
import Button from 'component/button';
|
||||
import Icon from 'component/common/icon';
|
||||
|
||||
import { TYPE_NEW } from 'component/claimListDiscover/view';
|
||||
|
||||
type Props = {
|
||||
subscribedChannels: Array<Subscription>,
|
||||
};
|
||||
|
@ -29,7 +28,7 @@ function ChannelsFollowingPage(props: Props) {
|
|||
{__('Following')}
|
||||
</span>
|
||||
}
|
||||
defaultTypeSort={TYPE_NEW}
|
||||
defaultOrderBy={ORDER_BY_NEW}
|
||||
channelIds={subscribedChannels.map(sub => sub.uri.split('#')[1])}
|
||||
meta={
|
||||
<Button
|
||||
|
|
|
@ -6,7 +6,7 @@ import Page from 'component/page';
|
|||
import Button from 'component/button';
|
||||
import ClaimTilesDiscover from 'component/claimTilesDiscover';
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
|
@ -115,7 +115,7 @@ function ChannelsFollowingDiscover(props: Props) {
|
|||
</div>
|
||||
))}
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import Page from 'component/page';
|
||||
import ClaimListDiscover from 'component/claimListDiscover';
|
||||
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 = {
|
||||
name: string,
|
||||
|
@ -16,9 +16,8 @@ function TopPage(props: Props) {
|
|||
<Page>
|
||||
<ClaimListDiscover
|
||||
name={name}
|
||||
defaultTypeSort={TYPE_TOP}
|
||||
defaultTimeSort={TIME_ALL}
|
||||
defaultOrderBy={['effective_amount']}
|
||||
defaultFreshness={FRESH_ALL}
|
||||
defaultOrderBy={ORDER_BY_TOP}
|
||||
includeSupportAction
|
||||
renderProperties={claim => (
|
||||
<span className="media__subtitle">
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
@import 'component/pagination';
|
||||
@import 'component/placeholder';
|
||||
@import 'component/search';
|
||||
@import 'component/claim-search';
|
||||
@import 'component/section';
|
||||
@import 'component/snack-bar';
|
||||
@import 'component/spinner';
|
||||
|
|
|
@ -78,7 +78,9 @@ svg + .button__label,
|
|||
border-radius: 0;
|
||||
margin: 0;
|
||||
background-color: var(--color-card-background);
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
padding: var(--spacing-medium) var(--spacing-small);
|
||||
}
|
||||
svg {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
@ -109,3 +111,10 @@ svg + .button__label,
|
|||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button-toggle--custom {
|
||||
color: var(--color-primary);
|
||||
svg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
61
ui/scss/component/_claim-search.scss
Normal file
61
ui/scss/component/_claim-search.scss
Normal 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;
|
||||
}
|
|
@ -26,6 +26,7 @@ $breakpoint-medium: 1150px;
|
|||
--floating-viewer-height: 18rem; // 32 * 9/16
|
||||
--floating-viewer-info-height: 5rem;
|
||||
--floating-viewer-container-height: calc(var(--floating-viewer-height) + var(--floating-viewer-info-height));
|
||||
--option-select-width: 8rem;
|
||||
|
||||
// Text
|
||||
--text-max-width: 660px;
|
||||
|
|
Loading…
Add table
Reference in a new issue