select tags before channels and filter channels by tag

moartags

CS tags followed category
continue button,
Remove card header on tags select

limitShow tags count

tags limit fix

debug cs tags highlighting

bugfix

yarnlock
This commit is contained in:
jessop 2020-03-11 21:43:52 -04:00
parent f8357c4ec6
commit d9e65e8328
22 changed files with 325 additions and 93 deletions

View file

@ -131,7 +131,7 @@
"imagesloaded": "^4.1.4",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#8245b055746216f7e1a12744fe6fbda3e3e90705",
"lbry-redux": "lbryio/lbry-redux#6ed0dde5cbd7c25aa02631d5fa31fb6a4de76876",
"lbryinc": "lbryio/lbryinc#275f35b31ec614e2b89689f860fe19e645deee68",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",

View file

@ -1034,6 +1034,8 @@
"Deleting or editing comments is not currently possible. Please be mindful of this when posting.": "Deleting or editing comments is not currently possible. Please be mindful of this when posting.",
"When the alpha ends, we will attempt to transition comments, but do not promise to do so.": "When the alpha ends, we will attempt to transition comments, but do not promise to do so.",
"More Channels": "More Channels",
"Known Tags": "Known Tags",
"More Channels": "More Channels",
"You arent blocking any channels": "You arent blocking any channels",
"When you block a channel, all content from that channel will be hidden.": "When you block a channel, all content from that channel will be hidden.",
"View top claims for %normalized_uri%": "View top claims for %normalized_uri%",

View file

@ -9,6 +9,7 @@ import {
selectUploadCount,
selectUnclaimedRewards,
doUserSetReferrer,
selectUserVerifiedEmail,
} from 'lbryinc';
import { doFetchTransactions, doFetchChannelListMine } from 'lbry-redux';
import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings';
@ -35,6 +36,7 @@ const select = state => ({
syncError: selectGetSyncErrorMessage(state),
uploadCount: selectUploadCount(state),
rewards: selectUnclaimedRewards(state),
isAuthenticated: selectUserVerifiedEmail(state),
});
const perform = dispatch => ({
@ -50,9 +52,4 @@ const perform = dispatch => ({
setReferrer: (referrer, doClaim) => dispatch(doUserSetReferrer(referrer, doClaim)),
});
export default hot(
connect(
select,
perform
)(App)
);
export default hot(connect(select, perform)(App));

View file

@ -68,6 +68,7 @@ type Props = {
rewards: Array<Reward>,
setReferrer: (string, boolean) => void,
analyticsTagSync: () => void,
isAuthenticated: boolean,
};
function App(props: Props) {
@ -93,6 +94,7 @@ function App(props: Props) {
rewards,
setReferrer,
analyticsTagSync,
isAuthenticated,
} = props;
const appRef = useRef();
@ -242,11 +244,11 @@ function App(props: Props) {
}, [hasVerifiedEmail, syncEnabled, checkSync]);
useEffect(() => {
if (syncError) {
if (syncError && isAuthenticated) {
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [syncError, pathname]);
}, [syncError, pathname, isAuthenticated]);
// @if TARGET='web'
useEffect(() => {

View file

@ -5,12 +5,14 @@ import {
selectFetchingClaimSearch,
selectBlockedChannels,
SETTINGS,
selectFollowedTags,
} from 'lbry-redux';
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import ClaimListDiscover from './view';
const select = state => ({
followedTags: selectFollowedTags(state),
claimSearchByQuery: selectClaimSearchByQuery(state),
loading: selectFetchingClaimSearch(state),
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),

View file

@ -32,7 +32,8 @@ type Props = {
hiddenUris: Array<string>,
hiddenNsfwMessage?: Node,
channelIds?: Array<string>,
tags: Array<string>,
tags: string, // these are just going to be string. pass a CSV if you want multi
defaultTags: string,
orderBy?: Array<string>,
defaultOrderBy?: string,
freshness?: string,
@ -49,6 +50,7 @@ type Props = {
renderProperties?: Claim => Node,
includeSupportAction?: boolean,
pageSize?: number,
followedTags?: Array<Tag>,
};
function ClaimListDiscover(props: Props) {
@ -56,11 +58,12 @@ function ClaimListDiscover(props: Props) {
doClaimSearch,
claimSearchByQuery,
tags,
defaultTags,
loading,
meta,
channelIds,
showNsfw,
showReposts,
// showReposts,
history,
location,
hiddenUris,
@ -81,6 +84,7 @@ function ClaimListDiscover(props: Props) {
renderProperties,
includeSupportAction,
hideFilter,
followedTags,
} = props;
const didNavigateForward = history.action === 'PUSH';
const { search } = location;
@ -88,9 +92,12 @@ function ClaimListDiscover(props: Props) {
const [page, setPage] = useState(1);
const [forceRefresh, setForceRefresh] = useState();
const [expanded, setExpanded] = useState(false);
const followed = (followedTags && followedTags.map(t => t.name)) || [];
const urlParams = new URLSearchParams(search);
const tagsParam = tags || urlParams.get(CS.TAGS_KEY) || null;
const tagsParam = // can be 'x,y,z' or 'x' or ['x','y'] or CS.CONSTANT
(tags && getParamFromTags(tags)) ||
(urlParams.get(CS.TAGS_KEY) !== null && urlParams.get(CS.TAGS_KEY)) ||
(defaultTags && getParamFromTags(defaultTags));
const orderParam = orderBy || urlParams.get(CS.ORDER_BY_KEY) || defaultOrderBy || CS.ORDER_BY_TRENDING;
const freshnessParam = freshness || urlParams.get(CS.FRESH_KEY) || defaultFreshness;
const contentTypeParam = urlParams.get(CS.CONTENT_KEY);
@ -102,7 +109,12 @@ function ClaimListDiscover(props: Props) {
const showDuration = !(claimType && claimType === CS.CLAIM_CHANNEL);
const isFiltered = () =>
Boolean(urlParams.get(CS.FRESH_KEY) || urlParams.get(CS.CONTENT_KEY) || urlParams.get(CS.DURATION_KEY));
Boolean(
urlParams.get(CS.FRESH_KEY) ||
urlParams.get(CS.CONTENT_KEY) ||
urlParams.get(CS.DURATION_KEY) ||
urlParams.get(CS.TAGS_KEY)
);
useEffect(() => {
if (isFiltered()) setExpanded(true);
@ -113,7 +125,7 @@ function ClaimListDiscover(props: Props) {
page_size: number,
page: number,
no_totals: boolean,
any_tags: Array<string>,
any_tags?: Array<string>,
not_tags: Array<string>,
channel_ids: Array<string>,
not_channel_ids: Array<string>,
@ -131,7 +143,6 @@ function ClaimListDiscover(props: Props) {
// 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: tagsParam || [],
channel_ids: channelIds || [],
not_channel_ids:
// If channelIds were passed in, we don't need not_channel_ids
@ -211,6 +222,17 @@ function ClaimListDiscover(props: Props) {
}
}
if (tagsParam) {
if (tagsParam !== CS.TAGS_ALL && tagsParam !== '') {
if (tagsParam === CS.TAGS_FOLLOWED) {
options.any_tags = followed;
} else if (Array.isArray(tagsParam)) {
options.any_tags = tagsParam;
} else {
options.any_tags = tagsParam.split(',');
}
}
}
// https://github.com/lbryio/lbry-desktop/issues/3774
// if (!showReposts) {
// if (Array.isArray(options.claim_type)) {
@ -220,7 +242,7 @@ function ClaimListDiscover(props: Props) {
// }
// }
const hasMatureTags = tags && tags.some(t => MATURE_TAGS.includes(t));
const hasMatureTags = tagsParam && tagsParam.split(',').some(t => MATURE_TAGS.includes(t));
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
const shouldPerformSearch =
@ -265,6 +287,14 @@ function ClaimListDiscover(props: Props) {
history.push(url);
}
function getParamFromTags(t) {
if (t === CS.TAGS_ALL || t === CS.TAGS_FOLLOWED) {
return t;
} else if (Array.isArray(t)) {
return t.join(',');
}
}
function buildUrl(delta) {
const newUrlParams = new URLSearchParams();
CS.KEYS.forEach(k => {
@ -300,6 +330,23 @@ function ClaimListDiscover(props: Props) {
newUrlParams.set(CS.DURATION_KEY, delta.value);
}
break;
case CS.TAGS_KEY:
if (delta.value === CS.TAGS_ALL) {
if (defaultTags === CS.TAGS_ALL) {
newUrlParams.delete(CS.TAGS_KEY);
} else {
newUrlParams.set(CS.TAGS_KEY, delta.value);
}
} else if (delta.value === CS.TAGS_FOLLOWED) {
if (defaultTags === CS.TAGS_FOLLOWED) {
newUrlParams.delete(CS.TAGS_KEY);
} else {
newUrlParams.set(CS.TAGS_KEY, delta.value); // redundant but special
}
} else {
newUrlParams.set(CS.TAGS_KEY, delta.value);
}
break;
}
return `?${newUrlParams.toString()}`;
}
@ -393,46 +440,48 @@ function ClaimListDiscover(props: Props) {
)}
{/* 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,
{!claimType && (
<div
className={classnames('claim-search__input-container', {
'claim-search__input-container--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_IMAGE && __('Image')}
{type === CS.FILE_MODEL && __('Model')}
{type === CS.FILE_BINARY && __('Other')}
{type === CS.FILE_DOCUMENT && __('Document')}
{type === CS.CONTENT_ALL && __('Any')}
</option>
);
<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,
})
}
})}
</FormField>
</div>
>
{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_IMAGE && __('Image')}
{type === CS.FILE_MODEL && __('Model')}
{type === CS.FILE_BINARY && __('Other')}
{type === CS.FILE_DOCUMENT && __('Document')}
{type === CS.CONTENT_ALL && __('Any')}
</option>
);
}
})}
</FormField>
</div>
)}
{/* DURATIONS FIELD */}
{showDuration && (
<div className={'claim-search__input-container'}>
@ -469,6 +518,50 @@ function ClaimListDiscover(props: Props) {
</FormField>
</div>
)}
{/* TAGS FIELD */}
{!tags && (
<div className={'claim-search__input-container'}>
<FormField
className={classnames('claim-search__dropdown', {
'claim-search__dropdown--selected':
((!defaultTags || defaultTags === CS.TAGS_ALL) && tagsParam && tagsParam !== CS.TAGS_ALL) ||
(defaultTags === CS.TAGS_FOLLOWED && tagsParam !== CS.TAGS_FOLLOWED),
})}
label={__('Tags')}
type="select"
name="tags"
value={tagsParam || CS.TAGS_ALL}
onChange={e =>
handleChange({
key: CS.TAGS_KEY,
value: e.target.value,
})
}
>
{[
CS.TAGS_ALL,
CS.TAGS_FOLLOWED,
...followed,
...(followed.includes(tagsParam) || tagsParam === CS.TAGS_ALL || tagsParam === CS.TAGS_FOLLOWED
? []
: [tagsParam]), // if they unfollow while filtered, add Other
].map(tag => (
<option
key={tag}
value={tag}
className={classnames({
'claim-search__input-special': !followed.includes(tag),
})}
>
{followed.includes(tag) && typeof tag === 'string' && toCapitalCase(__(tag))}
{tag === CS.TAGS_ALL && __('Any')}
{tag === CS.TAGS_FOLLOWED && __('Following')}
{!followed.includes(tag) && tag !== CS.TAGS_ALL && tag !== CS.TAGS_FOLLOWED && __('Other')}
</option>
))}
</FormField>
</div>
)}
</div>
</>
)}

View file

@ -6,7 +6,6 @@ import ClaimPreviewTile from 'component/claimPreviewTile';
type Props = {
uris: Array<string>,
doClaimSearch: ({}) => void,
loading: boolean,
showNsfw: boolean,
showReposts: boolean,
history: { action: string, push: string => void, replace: string => void },
@ -29,9 +28,8 @@ function ClaimTilesDiscover(props: Props) {
const {
doClaimSearch,
claimSearchByQuery,
loading,
showNsfw,
showReposts,
// showReposts,
hiddenUris,
// Below are options to pass that are forwarded to claim_search
tags,
@ -95,7 +93,7 @@ function ClaimTilesDiscover(props: Props) {
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
const shouldPerformSearch = !hasSearched || uris.length === 0 || (!loading && uris.length < pageSize);
const shouldPerformSearch = !hasSearched || uris.length === 0;
// Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time
const optionsStringForEffect = JSON.stringify(options);

View file

@ -153,7 +153,7 @@ function PublishForm(props: Props) {
hideHeader
label={__('Selected Tags')}
empty={__('No tags added')}
limit={TAGS_LIMIT}
limitSelect={TAGS_LIMIT}
help={__(
'Add tags that are relevant to your content. If mature content, ensure it is tagged mature. Tag abuse and missing mature tags will not be tolerated.'
)}

View file

@ -18,7 +18,8 @@ type Props = {
placeholder?: string,
label?: string,
disabled?: boolean,
limit?: number,
limitSelect?: number,
limitShow?: number,
};
/*
@ -42,7 +43,8 @@ export default function TagsSearch(props: Props) {
placeholder,
label,
disabled,
limit,
limitSelect,
limitShow = 5,
} = props;
const [newTag, setNewTag] = useState('');
const doesTagMatch = name => {
@ -60,10 +62,10 @@ export default function TagsSearch(props: Props) {
const suggestedTagsSet = setUnion(remainingFollowedTagsSet, unfollowedTagsSet);
const countWithoutMature = selectedTagsSet.has('mature') ? selectedTagsSet.size - 1 : selectedTagsSet.size;
const maxed = Boolean(limit && countWithoutMature >= limit);
const maxed = Boolean(limitSelect && countWithoutMature >= limitSelect);
const suggestedTags = Array.from(suggestedTagsSet)
.filter(doesTagMatch)
.slice(0, 5);
.slice(0, limitShow);
// tack 'mature' onto the end if it's not already in the list
if (!newTag && suggestMature && !suggestedTags.some(tag => tag === 'mature')) {
@ -116,7 +118,7 @@ export default function TagsSearch(props: Props) {
<React.Fragment>
<Form className="tags__input-wrapper" onSubmit={handleSubmit}>
<label>
{limit ? (
{limitSelect ? (
<I18nMessage
tokens={{
number: 5 - countWithoutMature,

View file

@ -24,7 +24,8 @@ type Props = {
placeholder?: string,
disableAutoFocus?: boolean,
hideHeader?: boolean,
limit?: number,
limitShow?: number,
limitSelect?: number,
};
/*
@ -45,7 +46,8 @@ export default function TagsSelect(props: Props) {
placeholder,
hideHeader,
label,
limit,
limitShow,
limitSelect,
} = props;
const [hasClosed, setHasClosed] = usePersistedState('tag-select:has-closed', false);
const tagsToDisplay = tagsChosen || followedTags;
@ -107,7 +109,8 @@ export default function TagsSelect(props: Props) {
disableAutoFocus={disableAutoFocus}
tagsPassedIn={tagsToDisplay}
placeholder={placeholder}
limit={limit}
limitShow={limitShow}
limitSelect={limitSelect}
/>
</React.Fragment>
}

View file

@ -1,6 +1,7 @@
import { connect } from 'react-redux';
import { selectFollowedTags } from 'lbry-redux';
import { selectSubscriptions } from 'redux/selectors/subscriptions';
import { doChannelSubscribe } from 'redux/actions/subscriptions';
import UserChannelFollowIntro from './view';
const select = state => ({
@ -8,4 +9,11 @@ const select = state => ({
subscribedChannels: selectSubscriptions(state),
});
export default connect(select)(UserChannelFollowIntro);
const perform = dispatch => ({
channelSubscribe: uri => dispatch(doChannelSubscribe(uri)),
});
export default connect(
select,
perform
)(UserChannelFollowIntro);

View file

@ -1,18 +1,32 @@
// @flow
import React from 'react';
import React, { useEffect } from 'react';
import ClaimListDiscover from 'component/claimListDiscover';
import * as CS from 'constants/claim_search';
import Nag from 'component/common/nag';
import { parseURI } from 'lbry-redux';
import Button from 'component/button';
import { Form } from 'component/common/form-components/form';
type Props = {
subscribedChannels: Array<Subscription>,
onContinue: () => void,
onBack: () => void,
channelSubscribe: (sub: Subscription) => void,
};
const LBRYURI = 'lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a';
function UserChannelFollowIntro(props: Props) {
const { subscribedChannels, onContinue } = props;
const { subscribedChannels, channelSubscribe, onContinue, onBack } = props;
const followingCount = (subscribedChannels && subscribedChannels.length) || 0;
// subscribe to lbry
useEffect(() => {
channelSubscribe({
channelName: parseURI(LBRYURI).claimName,
uri: LBRYURI,
});
}, []);
return (
<React.Fragment>
<h1 className="section__title--large">{__('Find Channels to Follow')}</h1>
@ -21,13 +35,25 @@ function UserChannelFollowIntro(props: Props) {
'LBRY works better if you find and follow at least 5 creators you like. You can also block channels you never want to see.'
)}
</p>
<Form onSubmit={onContinue} className="section__body">
<div className="card__actions">
<Button button="secondary" onClick={onBack} label={__('Back')} />
<Button
button="primary"
type="Submit"
onClick={onContinue}
label={__('Continue')}
disabled={subscribedChannels.length < 2}
/>
</div>
</Form>
<div className="section__body">
<ClaimListDiscover
defaultOrderBy={CS.ORDER_BY_TOP}
defaultFreshness={CS.FRESH_ALL}
claimType="channel"
hideBlock
hideFilter
defaultTags={CS.TAGS_FOLLOWED}
/>
{followingCount > 0 && (
<Nag

View file

@ -6,6 +6,7 @@ import UserEmailNew from 'component/userEmailNew';
import UserEmailVerify from 'component/userEmailVerify';
import UserFirstChannel from 'component/userFirstChannel';
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
import UserTagFollowIntro from 'component/userTagFollowIntro';
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc';
import UserVerify from 'component/userVerify';
@ -59,10 +60,12 @@ function UserSignIn(props: Props) {
const { search } = location;
const urlParams = new URLSearchParams(search);
const redirect = urlParams.get('redirect');
const step = urlParams.get('step');
const shouldRedirectImmediately = urlParams.get('immediate');
const [initialSignInStep, setInitialSignInStep] = React.useState();
const [hasSeenFollowList, setHasSeenFollowList] = usePersistedState('channel-follow-intro', false);
const [hasSkippedRewards, setHasSkippedRewards] = usePersistedState('skip-rewards-intro', false);
const [hasSeenTagsList, setHasSeenTagsList] = usePersistedState('channel-follow-intro', false);
const hasVerifiedEmail = user && user.has_verified_email;
const rewardsApproved = user && user.is_reward_approved;
const isIdentityVerified = user && user.is_identity_verified;
@ -92,7 +95,8 @@ function UserSignIn(props: Props) {
channelCount === 0 &&
!hasYoutubeChannels;
const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete;
const showFollowIntro = hasVerifiedEmail && !hasSeenFollowList;
const showFollowIntro = step === 'channels' || (hasVerifiedEmail && !hasSeenFollowList);
const showTagsIntro = step === 'tags' || (hasVerifiedEmail && !hasSeenTagsList);
const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !getSyncError && !showFollowIntro;
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
const isWaitingForSomethingToFinish =
@ -136,6 +140,34 @@ function UserSignIn(props: Props) {
history.replace(url);
setHasSeenFollowList(true);
}}
onBack={() => {
let url = `/$/${PAGES.AUTH}?reset_scroll=1&step=tags`;
if (redirect) {
url += `&redirect=${redirect}`;
}
if (shouldRedirectImmediately) {
url += `&immediate=true`;
}
history.replace(url);
setHasSeenFollowList(false);
}}
/>
),
showTagsIntro && (
<UserTagFollowIntro
onContinue={() => {
let url = `/$/${PAGES.AUTH}?reset_scroll=1&step=channels`;
if (redirect) {
url += `&redirect=${redirect}`;
}
if (shouldRedirectImmediately) {
url += `&immediate=true`;
}
history.replace(url);
setHasSeenTagsList(true);
}}
/>
),
showYoutubeTransfer && (

View file

@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import { selectFollowedTags } from 'lbry-redux';
import UserTagFollowIntro from './view';
const select = state => ({
followedTags: selectFollowedTags(state),
});
export default connect(select)(UserTagFollowIntro);

View file

@ -0,0 +1,52 @@
// @flow
import React from 'react';
import Nag from 'component/common/nag';
import TagsSelect from 'component/tagsSelect';
import Button from 'component/button';
import { Form } from 'component/common/form';
type Props = {
subscribedChannels: Array<Subscription>,
onContinue: () => void,
followedTags: Array<Tag>,
};
function UserChannelFollowIntro(props: Props) {
const { onContinue, followedTags } = props;
const followingCount = (followedTags && followedTags.length) || 0;
return (
<React.Fragment>
<h1 className="section__title--large">{__('Tag Selection')}</h1>
<p className="section__subtitle">{__('Select some tags to help us show you interesting things.')}</p>
<Form onSubmit={onContinue} className="section__body">
<div className="card__actions">
<Button
button="primary"
type="Submit"
onClick={onContinue}
label={__('Continue')}
disabled={followedTags.length < 1}
/>
</div>
</Form>
<div className="section__body">
<TagsSelect hideHeader limitShow={300} help={false} showClose={false} title={__('Follow New Tags')} />
{followingCount > 0 && (
<Nag
type="helpful"
message={
followingCount === 1
? __('You are currently following %followingCount% tag', { followingCount })
: __('You are currently following %followingCount% tags', { followingCount })
}
actionText={__('Continue')}
onClick={onContinue}
/>
)}
</div>
</React.Fragment>
);
}
export default UserChannelFollowIntro;

View file

@ -6,6 +6,9 @@ export const DURATION_KEY = 'duration';
export const TAGS_KEY = 't';
export const CONTENT_KEY = 'content';
export const TAGS_ALL = 'tags_any';
export const TAGS_FOLLOWED = 'tags_followed';
export const FRESH_DAY = 'day';
export const FRESH_WEEK = 'week';
export const FRESH_MONTH = 'month';

View file

@ -119,12 +119,7 @@ function ChannelsFollowingDiscover(props: Props) {
</div>
))}
<h1 className="claim-grid__title">{__('More Channels')}</h1>
<ClaimListDiscover
defaultOrderBy={CS.ORDER_BY_TOP}
defaultFreshness={CS.FRESH_ALL}
claimType="channel"
hideFilter
/>
<ClaimListDiscover defaultOrderBy={CS.ORDER_BY_TOP} defaultFreshness={CS.FRESH_ALL} claimType="channel" />
</Page>
);
}

View file

@ -8,6 +8,7 @@ import useHover from 'effects/use-hover';
import analytics from 'analytics';
import HiddenNsfw from 'component/common/hidden-nsfw';
import Icon from 'component/common/icon';
import * as CS from 'constants/claim_search';
type Props = {
location: { search: string },
@ -26,11 +27,11 @@ function TagsPage(props: Props) {
const urlParams = new URLSearchParams(search);
const claimType = urlParams.get('claim_type');
const tagsQuery = urlParams.get('t') || '';
const tags = tagsQuery.split(',');
const tagsQuery = urlParams.get('t') || null;
const tags = tagsQuery ? tagsQuery.split(',') : null;
// Eventually allow more than one tag on this page
// Restricting to one to make follow/unfollow simpler
const tag = tags[0];
const tag = (tags && tags[0]) || null;
const isFollowing = followedTags.map(({ name }) => name).includes(tag);
let label = isFollowing ? __('Following') : __('Follow');
@ -39,10 +40,12 @@ function TagsPage(props: Props) {
}
function handleFollowClick() {
doToggleTagFollowDesktop(tag);
if (tag) {
doToggleTagFollowDesktop(tag);
const nowFollowing = !isFollowing;
analytics.tagFollowEvent(tag, nowFollowing, 'tag-page');
const nowFollowing = !isFollowing;
analytics.tagFollowEvent(tag, nowFollowing, 'tag-page');
}
}
return (
@ -53,7 +56,9 @@ function TagsPage(props: Props) {
tag ? (
<span>
<Icon icon={ICONS.TAG} size={10} />
{tag}
{(tag === CS.TAGS_ALL && __('All Content')) ||
(tag === CS.TAGS_FOLLOWED && __('Followed Tags')) ||
__(tag)}
</span>
) : (
<span>
@ -62,7 +67,7 @@ function TagsPage(props: Props) {
</span>
)
}
tags={tags}
defaultTags={CS.TAGS_ALL}
hiddenNsfwMessage={<HiddenNsfw type="page" />}
meta={
tag && (

View file

@ -7,18 +7,18 @@ import TagsSelect from 'component/tagsSelect';
import Page from 'component/page';
import Button from 'component/button';
import Icon from 'component/common/icon';
import * as CS from 'constants/claim_search';
type Props = {
followedTags: Array<Tag>,
email: string,
};
function DiscoverPage(props: Props) {
const { followedTags, email } = props;
const { email } = props;
return (
<Page>
{(email || !IS_WEB) && <TagsSelect showClose title={__('Find New Tags To Follow')} />}
{(email || !IS_WEB) && <TagsSelect showClose limitShow={300} title={__('Find New Tags To Follow')} />}
<ClaimListDiscover
headerLabel={
<span>
@ -28,7 +28,7 @@ function DiscoverPage(props: Props) {
}
hideCustomization={IS_WEB && !email}
personalView
tags={followedTags.map(tag => tag.name)}
defaultTags={CS.TAGS_FOLLOWED}
meta={
<Button
button="link"

View file

@ -6,7 +6,7 @@ import TagsSelect from 'component/tagsSelect';
function FollowingPage() {
return (
<Page>
<TagsSelect showClose={false} title={__('Follow New Tags')} />
<TagsSelect limitShow={300} showClose={false} title={__('Follow New Tags')} />
</Page>
);
}

View file

@ -44,6 +44,9 @@
}
}
.claim-search__input-special {
font-weight: var(--font-weight-bold);
}
.claim-search__extra {
display: flex;
flex-direction: row;

View file

@ -7081,9 +7081,9 @@ lazy-val@^1.0.4:
yargs "^13.2.2"
zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#8245b055746216f7e1a12744fe6fbda3e3e90705:
lbry-redux@lbryio/lbry-redux#6ed0dde5cbd7c25aa02631d5fa31fb6a4de76876:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/8245b055746216f7e1a12744fe6fbda3e3e90705"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/6ed0dde5cbd7c25aa02631d5fa31fb6a4de76876"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"