Merge pull request #3844 from lbryio/feat-selectTagsFirstRun
select tags before channels and filter channels by tag
This commit is contained in:
commit
b5bb4b46d7
22 changed files with 325 additions and 93 deletions
|
@ -131,7 +131,7 @@
|
||||||
"imagesloaded": "^4.1.4",
|
"imagesloaded": "^4.1.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"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",
|
"lbryinc": "lbryio/lbryinc#275f35b31ec614e2b89689f860fe19e645deee68",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
|
|
|
@ -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.",
|
"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.",
|
"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",
|
"More Channels": "More Channels",
|
||||||
|
"Known Tags": "Known Tags",
|
||||||
|
"More Channels": "More Channels",
|
||||||
"You aren’t blocking any channels": "You aren’t blocking any channels",
|
"You aren’t blocking any channels": "You aren’t 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.",
|
"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 top claims for %normalized_uri%": "View top claims for %normalized_uri%",
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
selectUploadCount,
|
selectUploadCount,
|
||||||
selectUnclaimedRewards,
|
selectUnclaimedRewards,
|
||||||
doUserSetReferrer,
|
doUserSetReferrer,
|
||||||
|
selectUserVerifiedEmail,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
import { doFetchTransactions, doFetchChannelListMine } from 'lbry-redux';
|
import { doFetchTransactions, doFetchChannelListMine } from 'lbry-redux';
|
||||||
import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings';
|
||||||
|
@ -35,6 +36,7 @@ const select = state => ({
|
||||||
syncError: selectGetSyncErrorMessage(state),
|
syncError: selectGetSyncErrorMessage(state),
|
||||||
uploadCount: selectUploadCount(state),
|
uploadCount: selectUploadCount(state),
|
||||||
rewards: selectUnclaimedRewards(state),
|
rewards: selectUnclaimedRewards(state),
|
||||||
|
isAuthenticated: selectUserVerifiedEmail(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
@ -50,9 +52,4 @@ const perform = dispatch => ({
|
||||||
setReferrer: (referrer, doClaim) => dispatch(doUserSetReferrer(referrer, doClaim)),
|
setReferrer: (referrer, doClaim) => dispatch(doUserSetReferrer(referrer, doClaim)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default hot(
|
export default hot(connect(select, perform)(App));
|
||||||
connect(
|
|
||||||
select,
|
|
||||||
perform
|
|
||||||
)(App)
|
|
||||||
);
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ type Props = {
|
||||||
rewards: Array<Reward>,
|
rewards: Array<Reward>,
|
||||||
setReferrer: (string, boolean) => void,
|
setReferrer: (string, boolean) => void,
|
||||||
analyticsTagSync: () => void,
|
analyticsTagSync: () => void,
|
||||||
|
isAuthenticated: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function App(props: Props) {
|
function App(props: Props) {
|
||||||
|
@ -93,6 +94,7 @@ function App(props: Props) {
|
||||||
rewards,
|
rewards,
|
||||||
setReferrer,
|
setReferrer,
|
||||||
analyticsTagSync,
|
analyticsTagSync,
|
||||||
|
isAuthenticated,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const appRef = useRef();
|
const appRef = useRef();
|
||||||
|
@ -242,11 +244,11 @@ function App(props: Props) {
|
||||||
}, [hasVerifiedEmail, syncEnabled, checkSync]);
|
}, [hasVerifiedEmail, syncEnabled, checkSync]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (syncError) {
|
if (syncError && isAuthenticated) {
|
||||||
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
|
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [syncError, pathname]);
|
}, [syncError, pathname, isAuthenticated]);
|
||||||
|
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -5,12 +5,14 @@ import {
|
||||||
selectFetchingClaimSearch,
|
selectFetchingClaimSearch,
|
||||||
selectBlockedChannels,
|
selectBlockedChannels,
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
|
selectFollowedTags,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import ClaimListDiscover from './view';
|
import ClaimListDiscover from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
followedTags: selectFollowedTags(state),
|
||||||
claimSearchByQuery: selectClaimSearchByQuery(state),
|
claimSearchByQuery: selectClaimSearchByQuery(state),
|
||||||
loading: selectFetchingClaimSearch(state),
|
loading: selectFetchingClaimSearch(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
||||||
|
|
|
@ -32,7 +32,8 @@ type Props = {
|
||||||
hiddenUris: Array<string>,
|
hiddenUris: Array<string>,
|
||||||
hiddenNsfwMessage?: Node,
|
hiddenNsfwMessage?: Node,
|
||||||
channelIds?: Array<string>,
|
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>,
|
orderBy?: Array<string>,
|
||||||
defaultOrderBy?: string,
|
defaultOrderBy?: string,
|
||||||
freshness?: string,
|
freshness?: string,
|
||||||
|
@ -49,6 +50,7 @@ type Props = {
|
||||||
renderProperties?: Claim => Node,
|
renderProperties?: Claim => Node,
|
||||||
includeSupportAction?: boolean,
|
includeSupportAction?: boolean,
|
||||||
pageSize?: number,
|
pageSize?: number,
|
||||||
|
followedTags?: Array<Tag>,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimListDiscover(props: Props) {
|
function ClaimListDiscover(props: Props) {
|
||||||
|
@ -56,11 +58,12 @@ function ClaimListDiscover(props: Props) {
|
||||||
doClaimSearch,
|
doClaimSearch,
|
||||||
claimSearchByQuery,
|
claimSearchByQuery,
|
||||||
tags,
|
tags,
|
||||||
|
defaultTags,
|
||||||
loading,
|
loading,
|
||||||
meta,
|
meta,
|
||||||
channelIds,
|
channelIds,
|
||||||
showNsfw,
|
showNsfw,
|
||||||
showReposts,
|
// showReposts,
|
||||||
history,
|
history,
|
||||||
location,
|
location,
|
||||||
hiddenUris,
|
hiddenUris,
|
||||||
|
@ -81,6 +84,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
renderProperties,
|
renderProperties,
|
||||||
includeSupportAction,
|
includeSupportAction,
|
||||||
hideFilter,
|
hideFilter,
|
||||||
|
followedTags,
|
||||||
} = props;
|
} = props;
|
||||||
const didNavigateForward = history.action === 'PUSH';
|
const didNavigateForward = history.action === 'PUSH';
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
|
@ -88,9 +92,12 @@ function ClaimListDiscover(props: Props) {
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [forceRefresh, setForceRefresh] = useState();
|
const [forceRefresh, setForceRefresh] = useState();
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
const followed = (followedTags && followedTags.map(t => t.name)) || [];
|
||||||
const urlParams = new URLSearchParams(search);
|
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 orderParam = orderBy || urlParams.get(CS.ORDER_BY_KEY) || defaultOrderBy || CS.ORDER_BY_TRENDING;
|
||||||
const freshnessParam = freshness || urlParams.get(CS.FRESH_KEY) || defaultFreshness;
|
const freshnessParam = freshness || urlParams.get(CS.FRESH_KEY) || defaultFreshness;
|
||||||
const contentTypeParam = urlParams.get(CS.CONTENT_KEY);
|
const contentTypeParam = urlParams.get(CS.CONTENT_KEY);
|
||||||
|
@ -102,7 +109,12 @@ function ClaimListDiscover(props: Props) {
|
||||||
|
|
||||||
const showDuration = !(claimType && claimType === CS.CLAIM_CHANNEL);
|
const showDuration = !(claimType && claimType === CS.CLAIM_CHANNEL);
|
||||||
const isFiltered = () =>
|
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(() => {
|
useEffect(() => {
|
||||||
if (isFiltered()) setExpanded(true);
|
if (isFiltered()) setExpanded(true);
|
||||||
|
@ -113,7 +125,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
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>,
|
not_tags: Array<string>,
|
||||||
channel_ids: Array<string>,
|
channel_ids: Array<string>,
|
||||||
not_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
|
// 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: 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
|
||||||
|
@ -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
|
// https://github.com/lbryio/lbry-desktop/issues/3774
|
||||||
// if (!showReposts) {
|
// if (!showReposts) {
|
||||||
// if (Array.isArray(options.claim_type)) {
|
// 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 claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
|
||||||
const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
|
const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
|
||||||
const shouldPerformSearch =
|
const shouldPerformSearch =
|
||||||
|
@ -265,6 +287,14 @@ function ClaimListDiscover(props: Props) {
|
||||||
history.push(url);
|
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) {
|
function buildUrl(delta) {
|
||||||
const newUrlParams = new URLSearchParams();
|
const newUrlParams = new URLSearchParams();
|
||||||
CS.KEYS.forEach(k => {
|
CS.KEYS.forEach(k => {
|
||||||
|
@ -300,6 +330,23 @@ function ClaimListDiscover(props: Props) {
|
||||||
newUrlParams.set(CS.DURATION_KEY, delta.value);
|
newUrlParams.set(CS.DURATION_KEY, delta.value);
|
||||||
}
|
}
|
||||||
break;
|
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()}`;
|
return `?${newUrlParams.toString()}`;
|
||||||
}
|
}
|
||||||
|
@ -393,46 +440,48 @@ function ClaimListDiscover(props: Props) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* CONTENT_TYPES FIELD */}
|
{/* CONTENT_TYPES FIELD */}
|
||||||
<div
|
{!claimType && (
|
||||||
className={classnames('claim-search__input-container', {
|
<div
|
||||||
'claim-search__input-container--selected': contentTypeParam,
|
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 => {
|
<FormField
|
||||||
if (type !== CS.CLAIM_CHANNEL || (type === CS.CLAIM_CHANNEL && !channelIds)) {
|
className={classnames('claim-search__dropdown', {
|
||||||
return (
|
'claim-search__dropdown--selected': contentTypeParam,
|
||||||
<option key={type} value={type}>
|
})}
|
||||||
{/* i18fixme */}
|
type="select"
|
||||||
{type === CS.CLAIM_CHANNEL && __('Channel')}
|
name="claimType"
|
||||||
{type === CS.CLAIM_REPOST && __('Repost')}
|
label={__('Content Type')}
|
||||||
{type === CS.FILE_VIDEO && __('Video')}
|
value={contentTypeParam || CS.CONTENT_ALL}
|
||||||
{type === CS.FILE_AUDIO && __('Audio')}
|
onChange={e =>
|
||||||
{type === CS.FILE_IMAGE && __('Image')}
|
handleChange({
|
||||||
{type === CS.FILE_MODEL && __('Model')}
|
key: CS.CONTENT_KEY,
|
||||||
{type === CS.FILE_BINARY && __('Other')}
|
value: e.target.value,
|
||||||
{type === CS.FILE_DOCUMENT && __('Document')}
|
})
|
||||||
{type === CS.CONTENT_ALL && __('Any')}
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})}
|
>
|
||||||
</FormField>
|
{CS.CONTENT_TYPES.map(type => {
|
||||||
</div>
|
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 */}
|
{/* DURATIONS FIELD */}
|
||||||
{showDuration && (
|
{showDuration && (
|
||||||
<div className={'claim-search__input-container'}>
|
<div className={'claim-search__input-container'}>
|
||||||
|
@ -469,6 +518,50 @@ function ClaimListDiscover(props: Props) {
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ClaimPreviewTile from 'component/claimPreviewTile';
|
||||||
type Props = {
|
type Props = {
|
||||||
uris: Array<string>,
|
uris: Array<string>,
|
||||||
doClaimSearch: ({}) => void,
|
doClaimSearch: ({}) => void,
|
||||||
loading: boolean,
|
|
||||||
showNsfw: boolean,
|
showNsfw: boolean,
|
||||||
showReposts: boolean,
|
showReposts: boolean,
|
||||||
history: { action: string, push: string => void, replace: string => void },
|
history: { action: string, push: string => void, replace: string => void },
|
||||||
|
@ -29,9 +28,8 @@ function ClaimTilesDiscover(props: Props) {
|
||||||
const {
|
const {
|
||||||
doClaimSearch,
|
doClaimSearch,
|
||||||
claimSearchByQuery,
|
claimSearchByQuery,
|
||||||
loading,
|
|
||||||
showNsfw,
|
showNsfw,
|
||||||
showReposts,
|
// showReposts,
|
||||||
hiddenUris,
|
hiddenUris,
|
||||||
// Below are options to pass that are forwarded to claim_search
|
// Below are options to pass that are forwarded to claim_search
|
||||||
tags,
|
tags,
|
||||||
|
@ -95,7 +93,7 @@ function ClaimTilesDiscover(props: Props) {
|
||||||
|
|
||||||
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
|
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
|
||||||
const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
|
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
|
// 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);
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ function PublishForm(props: Props) {
|
||||||
hideHeader
|
hideHeader
|
||||||
label={__('Selected Tags')}
|
label={__('Selected Tags')}
|
||||||
empty={__('No tags added')}
|
empty={__('No tags added')}
|
||||||
limit={TAGS_LIMIT}
|
limitSelect={TAGS_LIMIT}
|
||||||
help={__(
|
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.'
|
'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.'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -18,7 +18,8 @@ type Props = {
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
label?: string,
|
label?: string,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
limit?: number,
|
limitSelect?: number,
|
||||||
|
limitShow?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -42,7 +43,8 @@ export default function TagsSearch(props: Props) {
|
||||||
placeholder,
|
placeholder,
|
||||||
label,
|
label,
|
||||||
disabled,
|
disabled,
|
||||||
limit,
|
limitSelect,
|
||||||
|
limitShow = 5,
|
||||||
} = props;
|
} = props;
|
||||||
const [newTag, setNewTag] = useState('');
|
const [newTag, setNewTag] = useState('');
|
||||||
const doesTagMatch = name => {
|
const doesTagMatch = name => {
|
||||||
|
@ -60,10 +62,10 @@ export default function TagsSearch(props: Props) {
|
||||||
const suggestedTagsSet = setUnion(remainingFollowedTagsSet, unfollowedTagsSet);
|
const suggestedTagsSet = setUnion(remainingFollowedTagsSet, unfollowedTagsSet);
|
||||||
|
|
||||||
const countWithoutMature = selectedTagsSet.has('mature') ? selectedTagsSet.size - 1 : selectedTagsSet.size;
|
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)
|
const suggestedTags = Array.from(suggestedTagsSet)
|
||||||
.filter(doesTagMatch)
|
.filter(doesTagMatch)
|
||||||
.slice(0, 5);
|
.slice(0, limitShow);
|
||||||
|
|
||||||
// tack 'mature' onto the end if it's not already in the list
|
// tack 'mature' onto the end if it's not already in the list
|
||||||
if (!newTag && suggestMature && !suggestedTags.some(tag => tag === 'mature')) {
|
if (!newTag && suggestMature && !suggestedTags.some(tag => tag === 'mature')) {
|
||||||
|
@ -116,7 +118,7 @@ export default function TagsSearch(props: Props) {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Form className="tags__input-wrapper" onSubmit={handleSubmit}>
|
<Form className="tags__input-wrapper" onSubmit={handleSubmit}>
|
||||||
<label>
|
<label>
|
||||||
{limit ? (
|
{limitSelect ? (
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{
|
tokens={{
|
||||||
number: 5 - countWithoutMature,
|
number: 5 - countWithoutMature,
|
||||||
|
|
|
@ -24,7 +24,8 @@ type Props = {
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
disableAutoFocus?: boolean,
|
disableAutoFocus?: boolean,
|
||||||
hideHeader?: boolean,
|
hideHeader?: boolean,
|
||||||
limit?: number,
|
limitShow?: number,
|
||||||
|
limitSelect?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -45,7 +46,8 @@ export default function TagsSelect(props: Props) {
|
||||||
placeholder,
|
placeholder,
|
||||||
hideHeader,
|
hideHeader,
|
||||||
label,
|
label,
|
||||||
limit,
|
limitShow,
|
||||||
|
limitSelect,
|
||||||
} = props;
|
} = props;
|
||||||
const [hasClosed, setHasClosed] = usePersistedState('tag-select:has-closed', false);
|
const [hasClosed, setHasClosed] = usePersistedState('tag-select:has-closed', false);
|
||||||
const tagsToDisplay = tagsChosen || followedTags;
|
const tagsToDisplay = tagsChosen || followedTags;
|
||||||
|
@ -107,7 +109,8 @@ export default function TagsSelect(props: Props) {
|
||||||
disableAutoFocus={disableAutoFocus}
|
disableAutoFocus={disableAutoFocus}
|
||||||
tagsPassedIn={tagsToDisplay}
|
tagsPassedIn={tagsToDisplay}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
limit={limit}
|
limitShow={limitShow}
|
||||||
|
limitSelect={limitSelect}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectFollowedTags } from 'lbry-redux';
|
import { selectFollowedTags } from 'lbry-redux';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
|
import { doChannelSubscribe } from 'redux/actions/subscriptions';
|
||||||
import UserChannelFollowIntro from './view';
|
import UserChannelFollowIntro from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -8,4 +9,11 @@ const select = state => ({
|
||||||
subscribedChannels: selectSubscriptions(state),
|
subscribedChannels: selectSubscriptions(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select)(UserChannelFollowIntro);
|
const perform = dispatch => ({
|
||||||
|
channelSubscribe: uri => dispatch(doChannelSubscribe(uri)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(UserChannelFollowIntro);
|
||||||
|
|
|
@ -1,18 +1,32 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import ClaimListDiscover from 'component/claimListDiscover';
|
import ClaimListDiscover from 'component/claimListDiscover';
|
||||||
import * as CS from 'constants/claim_search';
|
import * as CS from 'constants/claim_search';
|
||||||
import Nag from 'component/common/nag';
|
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 = {
|
type Props = {
|
||||||
subscribedChannels: Array<Subscription>,
|
subscribedChannels: Array<Subscription>,
|
||||||
onContinue: () => void,
|
onContinue: () => void,
|
||||||
|
onBack: () => void,
|
||||||
|
channelSubscribe: (sub: Subscription) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LBRYURI = 'lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a';
|
||||||
function UserChannelFollowIntro(props: Props) {
|
function UserChannelFollowIntro(props: Props) {
|
||||||
const { subscribedChannels, onContinue } = props;
|
const { subscribedChannels, channelSubscribe, onContinue, onBack } = props;
|
||||||
const followingCount = (subscribedChannels && subscribedChannels.length) || 0;
|
const followingCount = (subscribedChannels && subscribedChannels.length) || 0;
|
||||||
|
|
||||||
|
// subscribe to lbry
|
||||||
|
useEffect(() => {
|
||||||
|
channelSubscribe({
|
||||||
|
channelName: parseURI(LBRYURI).claimName,
|
||||||
|
uri: LBRYURI,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<h1 className="section__title--large">{__('Find Channels to Follow')}</h1>
|
<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.'
|
'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>
|
</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">
|
<div className="section__body">
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
defaultOrderBy={CS.ORDER_BY_TOP}
|
defaultOrderBy={CS.ORDER_BY_TOP}
|
||||||
defaultFreshness={CS.FRESH_ALL}
|
defaultFreshness={CS.FRESH_ALL}
|
||||||
claimType="channel"
|
claimType="channel"
|
||||||
hideBlock
|
hideBlock
|
||||||
hideFilter
|
defaultTags={CS.TAGS_FOLLOWED}
|
||||||
/>
|
/>
|
||||||
{followingCount > 0 && (
|
{followingCount > 0 && (
|
||||||
<Nag
|
<Nag
|
||||||
|
|
|
@ -6,6 +6,7 @@ import UserEmailNew from 'component/userEmailNew';
|
||||||
import UserEmailVerify from 'component/userEmailVerify';
|
import UserEmailVerify from 'component/userEmailVerify';
|
||||||
import UserFirstChannel from 'component/userFirstChannel';
|
import UserFirstChannel from 'component/userFirstChannel';
|
||||||
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
|
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
|
||||||
|
import UserTagFollowIntro from 'component/userTagFollowIntro';
|
||||||
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
|
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
|
||||||
import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc';
|
import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc';
|
||||||
import UserVerify from 'component/userVerify';
|
import UserVerify from 'component/userVerify';
|
||||||
|
@ -59,10 +60,12 @@ function UserSignIn(props: Props) {
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
const redirect = urlParams.get('redirect');
|
const redirect = urlParams.get('redirect');
|
||||||
|
const step = urlParams.get('step');
|
||||||
const shouldRedirectImmediately = urlParams.get('immediate');
|
const shouldRedirectImmediately = urlParams.get('immediate');
|
||||||
const [initialSignInStep, setInitialSignInStep] = React.useState();
|
const [initialSignInStep, setInitialSignInStep] = React.useState();
|
||||||
const [hasSeenFollowList, setHasSeenFollowList] = usePersistedState('channel-follow-intro', false);
|
const [hasSeenFollowList, setHasSeenFollowList] = usePersistedState('channel-follow-intro', false);
|
||||||
const [hasSkippedRewards, setHasSkippedRewards] = usePersistedState('skip-rewards-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 hasVerifiedEmail = user && user.has_verified_email;
|
||||||
const rewardsApproved = user && user.is_reward_approved;
|
const rewardsApproved = user && user.is_reward_approved;
|
||||||
const isIdentityVerified = user && user.is_identity_verified;
|
const isIdentityVerified = user && user.is_identity_verified;
|
||||||
|
@ -92,7 +95,8 @@ function UserSignIn(props: Props) {
|
||||||
channelCount === 0 &&
|
channelCount === 0 &&
|
||||||
!hasYoutubeChannels;
|
!hasYoutubeChannels;
|
||||||
const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete;
|
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 canHijackSignInFlowWithSpinner = hasVerifiedEmail && !getSyncError && !showFollowIntro;
|
||||||
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
|
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
|
||||||
const isWaitingForSomethingToFinish =
|
const isWaitingForSomethingToFinish =
|
||||||
|
@ -136,6 +140,34 @@ function UserSignIn(props: Props) {
|
||||||
history.replace(url);
|
history.replace(url);
|
||||||
setHasSeenFollowList(true);
|
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 && (
|
showYoutubeTransfer && (
|
||||||
|
|
9
ui/component/userTagFollowIntro/index.js
Normal file
9
ui/component/userTagFollowIntro/index.js
Normal 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);
|
52
ui/component/userTagFollowIntro/view.jsx
Normal file
52
ui/component/userTagFollowIntro/view.jsx
Normal 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;
|
|
@ -6,6 +6,9 @@ export const DURATION_KEY = 'duration';
|
||||||
export const TAGS_KEY = 't';
|
export const TAGS_KEY = 't';
|
||||||
export const CONTENT_KEY = 'content';
|
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_DAY = 'day';
|
||||||
export const FRESH_WEEK = 'week';
|
export const FRESH_WEEK = 'week';
|
||||||
export const FRESH_MONTH = 'month';
|
export const FRESH_MONTH = 'month';
|
||||||
|
|
|
@ -119,12 +119,7 @@ function ChannelsFollowingDiscover(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<h1 className="claim-grid__title">{__('More Channels')}</h1>
|
<h1 className="claim-grid__title">{__('More Channels')}</h1>
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover defaultOrderBy={CS.ORDER_BY_TOP} defaultFreshness={CS.FRESH_ALL} claimType="channel" />
|
||||||
defaultOrderBy={CS.ORDER_BY_TOP}
|
|
||||||
defaultFreshness={CS.FRESH_ALL}
|
|
||||||
claimType="channel"
|
|
||||||
hideFilter
|
|
||||||
/>
|
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import useHover from 'effects/use-hover';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import HiddenNsfw from 'component/common/hidden-nsfw';
|
import HiddenNsfw from 'component/common/hidden-nsfw';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
|
import * as CS from 'constants/claim_search';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
location: { search: string },
|
location: { search: string },
|
||||||
|
@ -26,11 +27,11 @@ function TagsPage(props: Props) {
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
const claimType = urlParams.get('claim_type');
|
const claimType = urlParams.get('claim_type');
|
||||||
const tagsQuery = urlParams.get('t') || '';
|
const tagsQuery = urlParams.get('t') || null;
|
||||||
const tags = tagsQuery.split(',');
|
const tags = tagsQuery ? tagsQuery.split(',') : null;
|
||||||
// Eventually allow more than one tag on this page
|
// Eventually allow more than one tag on this page
|
||||||
// Restricting to one to make follow/unfollow simpler
|
// 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);
|
const isFollowing = followedTags.map(({ name }) => name).includes(tag);
|
||||||
let label = isFollowing ? __('Following') : __('Follow');
|
let label = isFollowing ? __('Following') : __('Follow');
|
||||||
|
@ -39,10 +40,12 @@ function TagsPage(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFollowClick() {
|
function handleFollowClick() {
|
||||||
doToggleTagFollowDesktop(tag);
|
if (tag) {
|
||||||
|
doToggleTagFollowDesktop(tag);
|
||||||
|
|
||||||
const nowFollowing = !isFollowing;
|
const nowFollowing = !isFollowing;
|
||||||
analytics.tagFollowEvent(tag, nowFollowing, 'tag-page');
|
analytics.tagFollowEvent(tag, nowFollowing, 'tag-page');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -53,7 +56,9 @@ function TagsPage(props: Props) {
|
||||||
tag ? (
|
tag ? (
|
||||||
<span>
|
<span>
|
||||||
<Icon icon={ICONS.TAG} size={10} />
|
<Icon icon={ICONS.TAG} size={10} />
|
||||||
{tag}
|
{(tag === CS.TAGS_ALL && __('All Content')) ||
|
||||||
|
(tag === CS.TAGS_FOLLOWED && __('Followed Tags')) ||
|
||||||
|
__(tag)}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span>
|
<span>
|
||||||
|
@ -62,7 +67,7 @@ function TagsPage(props: Props) {
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
tags={tags}
|
defaultTags={CS.TAGS_ALL}
|
||||||
hiddenNsfwMessage={<HiddenNsfw type="page" />}
|
hiddenNsfwMessage={<HiddenNsfw type="page" />}
|
||||||
meta={
|
meta={
|
||||||
tag && (
|
tag && (
|
||||||
|
|
|
@ -7,18 +7,18 @@ import TagsSelect from 'component/tagsSelect';
|
||||||
import Page from 'component/page';
|
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 * as CS from 'constants/claim_search';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
followedTags: Array<Tag>,
|
|
||||||
email: string,
|
email: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
function DiscoverPage(props: Props) {
|
function DiscoverPage(props: Props) {
|
||||||
const { followedTags, email } = props;
|
const { email } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<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
|
<ClaimListDiscover
|
||||||
headerLabel={
|
headerLabel={
|
||||||
<span>
|
<span>
|
||||||
|
@ -28,7 +28,7 @@ function DiscoverPage(props: Props) {
|
||||||
}
|
}
|
||||||
hideCustomization={IS_WEB && !email}
|
hideCustomization={IS_WEB && !email}
|
||||||
personalView
|
personalView
|
||||||
tags={followedTags.map(tag => tag.name)}
|
defaultTags={CS.TAGS_FOLLOWED}
|
||||||
meta={
|
meta={
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="link"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import TagsSelect from 'component/tagsSelect';
|
||||||
function FollowingPage() {
|
function FollowingPage() {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<TagsSelect showClose={false} title={__('Follow New Tags')} />
|
<TagsSelect limitShow={300} showClose={false} title={__('Follow New Tags')} />
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.claim-search__input-special {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
}
|
||||||
.claim-search__extra {
|
.claim-search__extra {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -7081,9 +7081,9 @@ lazy-val@^1.0.4:
|
||||||
yargs "^13.2.2"
|
yargs "^13.2.2"
|
||||||
zstd-codec "^0.1.1"
|
zstd-codec "^0.1.1"
|
||||||
|
|
||||||
lbry-redux@lbryio/lbry-redux#8245b055746216f7e1a12744fe6fbda3e3e90705:
|
lbry-redux@lbryio/lbry-redux#6ed0dde5cbd7c25aa02631d5fa31fb6a4de76876:
|
||||||
version "0.0.1"
|
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:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
Loading…
Reference in a new issue