mature tags

This commit is contained in:
Sean Yesmunt 2019-07-02 17:00:05 -04:00
parent fd181ebde2
commit 52bc0e7a1a
13 changed files with 58 additions and 37 deletions

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import Button from 'component/button'; import Tag from 'component/tag';
const SLIM_TAGS = 1; const SLIM_TAGS = 1;
const NORMAL_TAGS = 4; const NORMAL_TAGS = 4;
@ -45,7 +45,7 @@ export default function ClaimTags(props: Props) {
return ( return (
<div className={classnames('file-properties', { 'file-properties--large': type === 'large' })}> <div className={classnames('file-properties', { 'file-properties--large': type === 'large' })}>
{tagsToDisplay.map(tag => ( {tagsToDisplay.map(tag => (
<Button key={tag} title={tag} navigate={`$/tags?t=${tag}`} className="tag" label={tag} /> <Tag key={tag} title={tag} name={tag} />
))} ))}
</div> </div>
); );

View file

@ -44,7 +44,7 @@ class CreditAmount extends React.PureComponent<Props> {
let amountText; let amountText;
if (showFree && isFree) { if (showFree && isFree) {
amountText = __('FREE'); amountText = __('Free');
} else { } else {
amountText = formattedAmount; amountText = formattedAmount;

View file

@ -132,6 +132,7 @@ function PublishForm(props: Props) {
<div className="card"> <div className="card">
<TagSelect <TagSelect
title={false} title={false}
suggestMature
help={__('The better your tags are, the easier it will be for people to discover your content.')} help={__('The better your tags are, the easier it will be for people to discover your content.')}
empty={__('No tags added')} empty={__('No tags added')}
onSelect={tag => updatePublishForm({ tags: [...tags, tag] })} onSelect={tag => updatePublishForm({ tags: [...tags, tag] })}

View file

@ -2,6 +2,7 @@
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import React from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { MATURE_TAGS } from 'lbry-redux';
import Button from 'component/button'; import Button from 'component/button';
type Props = { type Props = {
@ -13,8 +14,9 @@ type Props = {
export default function Tag(props: Props) { export default function Tag(props: Props) {
const { name, onClick, type = 'link', disabled = false } = props; const { name, onClick, type = 'link', disabled = false } = props;
const isMature = MATURE_TAGS.includes(name);
const clickProps = onClick ? { onClick } : { navigate: `/$/tags?t=${name}` }; const clickProps = onClick ? { onClick } : { navigate: `/$/tags?t=${name}` };
let title; let title;
if (!onClick) { if (!onClick) {
title = __('View tag'); title = __('View tag');
@ -28,8 +30,10 @@ export default function Tag(props: Props) {
disabled={disabled} disabled={disabled}
title={title} title={title}
className={classnames('tag', { className={classnames('tag', {
'tag--add': type === 'add',
'tag--remove': type === 'remove', 'tag--remove': type === 'remove',
// tag--add only adjusts the color, which causes issues with mature tag color clashing
'tag--add': !isMature && type === 'add',
'tag--mature': isMature,
})} })}
label={name} label={name}
iconSize={12} iconSize={12}

View file

@ -16,10 +16,11 @@ type Props = {
doToggleTagFollow: string => void, doToggleTagFollow: string => void,
doAddTag: string => void, doAddTag: string => void,
onSelect?: Tag => void, onSelect?: Tag => void,
suggestMature?: boolean,
}; };
export default function TagSelect(props: Props) { export default function TagSelect(props: Props) {
const { unfollowedTags = [], followedTags = [], doToggleTagFollow, doAddTag, onSelect } = props; const { unfollowedTags = [], followedTags = [], doToggleTagFollow, doAddTag, onSelect, suggestMature } = props;
const [newTag, setNewTag] = useState(''); const [newTag, setNewTag] = useState('');
let tags = unfollowedTags.slice(); let tags = unfollowedTags.slice();
@ -34,6 +35,10 @@ export default function TagSelect(props: Props) {
.filter(doesTagMatch) .filter(doesTagMatch)
.slice(0, 5); .slice(0, 5);
if (!newTag && suggestMature) {
suggestedTags.push('mature');
}
const suggestedTransitions = useTransition(suggestedTags, tag => tag, unfollowedTagsAnimation); const suggestedTransitions = useTransition(suggestedTags, tag => tag, unfollowedTagsAnimation);
function onChange(e) { function onChange(e) {

View file

@ -11,6 +11,7 @@ type Props = {
showClose: boolean, showClose: boolean,
followedTags: Array<Tag>, followedTags: Array<Tag>,
doToggleTagFollow: string => void, doToggleTagFollow: string => void,
suggestMature: boolean,
// Ovverides // Ovverides
// The default component is for following tags // The default component is for following tags
@ -29,10 +30,22 @@ const tagsAnimation = {
}; };
export default function TagSelect(props: Props) { export default function TagSelect(props: Props) {
const { showClose, followedTags, doToggleTagFollow, title, help, empty, tagsChosen, onSelect, onRemove } = props; const {
showClose,
followedTags,
doToggleTagFollow,
title,
help,
empty,
tagsChosen,
onSelect,
onRemove,
suggestMature,
} = 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;
const transitions = useTransition(tagsToDisplay, tag => tag.name, tagsAnimation); const transitions = useTransition(tagsToDisplay, tag => tag.name, tagsAnimation);
const hasMatureTag = tagsToDisplay.map(tag => tag.name).includes('mature');
function handleClose() { function handleClose() {
setHasClosed(true); setHasClosed(true);
@ -73,7 +86,7 @@ export default function TagSelect(props: Props) {
<div className="empty">{empty || __("You aren't following any tags, try searching for one.")}</div> <div className="empty">{empty || __("You aren't following any tags, try searching for one.")}</div>
)} )}
</ul> </ul>
<TagsSearch onSelect={onSelect} /> <TagsSearch onSelect={onSelect} suggestMature={suggestMature && !hasMatureTag} />
{help !== false && ( {help !== false && (
<p className="help">{help || __("The tags you follow will change what's trending for you.")}</p> <p className="help">{help || __("The tags you follow will change what's trending for you.")}</p>
)} )}

View file

@ -1,14 +0,0 @@
export const defaultFollowedTags = [
'blockchain',
'news',
'learning',
'technology',
'automotive',
'economics',
'food',
'science',
'art',
'nature',
];
export const defaultKnownTags = ['beliefs', 'funny', 'gaming', 'pop culture', 'music', 'sports', 'weapons'];

View file

@ -312,7 +312,7 @@ class FilePage extends React.Component<Props> {
/> />
<div className="file-properties"> <div className="file-properties">
{isRewardContent && <Icon size={20} iconColor="red" icon={icons.FEATURED} />} {isRewardContent && <Icon size={20} iconColor="red" icon={icons.FEATURED} />}
{nsfw && <div className="badge badge--nsfw">MATURE</div>} {nsfw && <div className="badge badge--mature">{__('Mature')}</div>}
<FilePrice badge uri={normalizeURI(uri)} /> <FilePrice badge uri={normalizeURI(uri)} />
</div> </div>
</div> </div>

View file

@ -6,7 +6,7 @@ import {
searchReducer, searchReducer,
walletReducer, walletReducer,
notificationsReducer, notificationsReducer,
tagsReducerBuilder, tagsReducer,
commentReducer, commentReducer,
} from 'lbry-redux'; } from 'lbry-redux';
import { userReducer, rewardsReducer, costInfoReducer, blacklistReducer, homepageReducer, statsReducer } from 'lbryinc'; import { userReducer, rewardsReducer, costInfoReducer, blacklistReducer, homepageReducer, statsReducer } from 'lbryinc';
@ -16,17 +16,6 @@ import contentReducer from 'redux/reducers/content';
import settingsReducer from 'redux/reducers/settings'; import settingsReducer from 'redux/reducers/settings';
import subscriptionsReducer from 'redux/reducers/subscriptions'; import subscriptionsReducer from 'redux/reducers/subscriptions';
import publishReducer from 'redux/reducers/publish'; import publishReducer from 'redux/reducers/publish';
import { defaultKnownTags, defaultFollowedTags } from 'constants/tags';
function getDefaultKnownTags() {
return defaultFollowedTags.concat(defaultKnownTags).reduce(
(tagsMap, tag) => ({
...tagsMap,
[tag]: { name: tag },
}),
{}
);
}
export default history => export default history =>
combineReducers({ combineReducers({
@ -47,7 +36,7 @@ export default history =>
settings: settingsReducer, settings: settingsReducer,
stats: statsReducer, stats: statsReducer,
subscriptions: subscriptionsReducer, subscriptions: subscriptionsReducer,
tags: tagsReducerBuilder({ followedTags: defaultFollowedTags, knownTags: getDefaultKnownTags() }), tags: tagsReducer,
user: userReducer, user: userReducer,
wallet: walletReducer, wallet: walletReducer,
}); });

View file

@ -155,6 +155,7 @@ export const doPrepareEdit = (claim: StreamClaim, uri: string, fileInfo: FileLis
license_url: licenseUrl, license_url: licenseUrl,
thumbnail, thumbnail,
title, title,
tags,
} = value; } = value;
const publishData: UpdatePublishFormData = { const publishData: UpdatePublishFormData = {
@ -171,6 +172,7 @@ export const doPrepareEdit = (claim: StreamClaim, uri: string, fileInfo: FileLis
uploadThumbnailStatus: thumbnail ? THUMBNAIL_STATUSES.MANUAL : undefined, uploadThumbnailStatus: thumbnail ? THUMBNAIL_STATUSES.MANUAL : undefined,
licenseUrl, licenseUrl,
nsfw: isClaimNsfw(claim), nsfw: isClaimNsfw(claim),
tags: tags ? tags.map(tag => ({ name: tag })) : [],
}; };
if (channelName) { if (channelName) {

View file

@ -19,3 +19,12 @@
background-color: $lbry-red-2; background-color: $lbry-red-2;
color: $lbry-white; color: $lbry-white;
} }
.badge--mature {
background-color: lighten($lbry-grape-1, 10%);
color: $lbry-black;
svg {
stroke: $lbry-black;
}
}

View file

@ -34,6 +34,7 @@ $main: $lbry-teal-5;
} }
.tag { .tag {
@extend .badge;
@extend .badge--tag; @extend .badge--tag;
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
@ -61,12 +62,20 @@ $main: $lbry-teal-5;
} }
.tag--remove { .tag--remove {
@extend .tag;
max-width: 20rem; max-width: 20rem;
} }
.tag--add { .tag--add {
background-color: lighten($lbry-teal-5, 60%); background-color: lighten($lbry-teal-5, 60%);
&.tag--mature {
@extend .badge--mature;
}
}
.tag--mature {
@extend .badge--mature;
// Lighten the color a little so it doesn't stand out as much on claim previews
} }
.tag__action-label { .tag__action-label {

View file

@ -74,6 +74,7 @@ const appFilter = createFilter('app', ['hasClickedComment', 'searchOptionsExpand
// We only need to persist the receiveAddress for the wallet // We only need to persist the receiveAddress for the wallet
const walletFilter = createFilter('wallet', ['receiveAddress']); const walletFilter = createFilter('wallet', ['receiveAddress']);
const searchFilter = createFilter('search', ['options']); const searchFilter = createFilter('search', ['options']);
const tagsFilter = createFilter('tags', ['followedTags']);
const whiteListedReducers = [ const whiteListedReducers = [
// @if TARGET='app' // @if TARGET='app'
'publish', 'publish',
@ -86,6 +87,7 @@ const whiteListedReducers = [
'search', 'search',
'tags', 'tags',
]; ];
const persistOptions = { const persistOptions = {
whitelist: whiteListedReducers, whitelist: whiteListedReducers,
// Order is important. Needs to be compressed last or other transforms can't // Order is important. Needs to be compressed last or other transforms can't
@ -98,6 +100,7 @@ const persistOptions = {
// @endif // @endif
appFilter, appFilter,
searchFilter, searchFilter,
tagsFilter,
compressor, compressor,
], ],
debounce: 5000, debounce: 5000,