provide tags for disabling comments

This commit is contained in:
zeppi 2021-02-04 00:45:49 -05:00 committed by jessopb
parent d47d55098e
commit a85c9a1f48
8 changed files with 126 additions and 60 deletions

View file

@ -3,6 +3,7 @@ import { withRouter } from 'react-router';
import { makeSelectCommentForCommentId } from 'redux/selectors/comments'; import { makeSelectCommentForCommentId } from 'redux/selectors/comments';
import ChannelDiscussion from './view'; import ChannelDiscussion from './view';
import { makeSelectTagsForUri } from 'lbry-redux';
const select = (state, props) => { const select = (state, props) => {
const { search } = props.location; const { search } = props.location;
@ -11,6 +12,7 @@ const select = (state, props) => {
return { return {
linkedComment: makeSelectCommentForCommentId(linkedCommentId)(state), linkedComment: makeSelectCommentForCommentId(linkedCommentId)(state),
tags: makeSelectTagsForUri(props.uri)(state),
}; };
}; };

View file

@ -1,15 +1,20 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import CommentsList from 'component/commentsList'; import CommentsList from 'component/commentsList';
import Empty from 'component/common/empty';
type Props = { type Props = {
uri: string, uri: string,
linkedComment: ?any, linkedComment: ?any,
tags: Array<string>,
}; };
function ChannelDiscussion(props: Props) { function ChannelDiscussion(props: Props) {
const { uri, linkedComment } = props; const { uri, linkedComment, tags } = props;
if (tags.includes('disable_comments')) {
return <Empty text={__('Comments are disabled here.')} />;
}
return ( return (
<section className="section"> <section className="section">
<CommentsList uri={uri} linkedComment={linkedComment} /> <CommentsList uri={uri} linkedComment={linkedComment} />

View file

@ -1,11 +1,13 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectUnfollowedTags, selectFollowedTags } from 'redux/selectors/tags'; import { selectUnfollowedTags, selectFollowedTags } from 'redux/selectors/tags';
import { doToggleTagFollowDesktop, doAddTag, doDeleteTag } from 'redux/actions/tags'; import { doToggleTagFollowDesktop, doAddTag, doDeleteTag } from 'redux/actions/tags';
import { selectUser } from 'redux/selectors/user';
import DiscoveryFirstRun from './view'; import DiscoveryFirstRun from './view';
const select = (state, props) => ({ const select = (state, props) => ({
unfollowedTags: selectUnfollowedTags(state), unfollowedTags: selectUnfollowedTags(state),
followedTags: selectFollowedTags(state), followedTags: selectFollowedTags(state),
user: selectUser(state),
}); });
export default connect(select, { export default connect(select, {

View file

@ -5,6 +5,7 @@ import Tag from 'component/tag';
import { setUnion, setDifference } from 'util/set-operations'; import { setUnion, setDifference } from 'util/set-operations';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import analytics from 'analytics'; import analytics from 'analytics';
import { UTILITY_TAGS } from 'constants/tags';
type Props = { type Props = {
tagsPassedIn: Array<Tag>, tagsPassedIn: Array<Tag>,
@ -21,6 +22,7 @@ type Props = {
disabled?: boolean, disabled?: boolean,
limitSelect?: number, limitSelect?: number,
limitShow?: number, limitShow?: number,
user: User,
}; };
const UNALLOWED_TAGS = ['lbry-first']; const UNALLOWED_TAGS = ['lbry-first'];
@ -49,6 +51,7 @@ export default function TagsSearch(props: Props) {
disabled, disabled,
limitSelect = TAG_FOLLOW_MAX, limitSelect = TAG_FOLLOW_MAX,
limitShow = 5, limitShow = 5,
user,
} = props; } = props;
const [newTag, setNewTag] = useState(''); const [newTag, setNewTag] = useState('');
const doesTagMatch = name => { const doesTagMatch = name => {
@ -58,7 +61,7 @@ export default function TagsSearch(props: Props) {
// Make sure there are no duplicates, then trim // Make sure there are no duplicates, then trim
// suggestedTags = (followedTags - tagsPassedIn) + unfollowedTags // suggestedTags = (followedTags - tagsPassedIn) + unfollowedTags
const experimentalFeature = user && user.experimental_ui;
const followedTagsSet = new Set(followedTags.map(tag => tag.name)); const followedTagsSet = new Set(followedTags.map(tag => tag.name));
const selectedTagsSet = new Set(tagsPassedIn.map(tag => tag.name)); const selectedTagsSet = new Set(tagsPassedIn.map(tag => tag.name));
const unfollowedTagsSet = new Set(unfollowedTags.map(tag => tag.name)); const unfollowedTagsSet = new Set(unfollowedTags.map(tag => tag.name));
@ -71,6 +74,12 @@ export default function TagsSearch(props: Props) {
countWithoutSpecialTags = countWithoutSpecialTags - 1; countWithoutSpecialTags = countWithoutSpecialTags - 1;
} }
UTILITY_TAGS.forEach(t => {
if (selectedTagsSet.has(t)) {
countWithoutSpecialTags--;
}
});
// const countWithoutLbryFirst = selectedTagsSet.has('lbry-first') ? selectedTagsSet.size - 1 : selectedTagsSet.size; // const countWithoutLbryFirst = selectedTagsSet.has('lbry-first') ? selectedTagsSet.size - 1 : selectedTagsSet.size;
const maxed = Boolean(limitSelect && countWithoutSpecialTags >= limitSelect); const maxed = Boolean(limitSelect && countWithoutSpecialTags >= limitSelect);
const suggestedTags = Array.from(suggestedTagsSet) const suggestedTags = Array.from(suggestedTagsSet)
@ -134,70 +143,103 @@ export default function TagsSearch(props: Props) {
analytics.tagFollowEvent(tag, !wasFollowing); analytics.tagFollowEvent(tag, !wasFollowing);
} }
} }
function handleUtilityTagCheckbox(tag: string) {
const selectedTag = tagsPassedIn.find(te => te.name === tag);
if (selectedTag) {
onRemove(selectedTag);
} else if (onSelect) {
onSelect([{ name: tag }]);
// call an api
}
}
return ( return (
<React.Fragment> <React.Fragment>
<Form className="tags__input-wrapper" onSubmit={handleSubmit}> <Form className="tags__input-wrapper" onSubmit={handleSubmit}>
<label> <fieldset-section>
{limitSelect < TAG_FOLLOW_MAX ? ( <label>
<I18nMessage {limitSelect < TAG_FOLLOW_MAX ? (
tokens={{ <I18nMessage
number: limitSelect - countWithoutSpecialTags, tokens={{
selectTagsLabel: label, number: limitSelect - countWithoutSpecialTags,
}} selectTagsLabel: label,
>
%selectTagsLabel% (%number% left)
</I18nMessage>
) : (
label || __('Following --[button label indicating a channel has been followed]--')
)}
</label>
<ul className="tags--remove">
{!tagsPassedIn.length && <Tag key={`placeholder-tag`} name={'example'} disabled type={'remove'} />}
{Boolean(tagsPassedIn.length) &&
tagsPassedIn.map(tag => (
<Tag
key={`passed${tag.name}`}
name={tag.name}
type="remove"
onClick={() => {
onRemove(tag);
}} }}
/> >
))} %selectTagsLabel% (%number% left)
</ul> </I18nMessage>
<FormField ) : (
autoFocus={!disableAutoFocus} label || __('Following --[button label indicating a channel has been followed]--')
className="tag__input"
onChange={onChange}
placeholder={placeholder || __('gaming, crypto')}
type="text"
value={newTag}
disabled={disabled}
label={__('Add Tags')}
/>
<section>
<label>{newTag.length ? __('Matching') : __('Known Tags')}</label>
<ul className="tags">
{Boolean(newTag.length) && !suggestedTags.includes(newTag) && (
<Tag
disabled={newTag !== 'mature' && maxed}
key={`entered${newTag}`}
name={newTag}
type="add"
onClick={newTag.includes('') ? e => handleSubmit(e) : e => handleTagClick(newTag)}
/>
)} )}
{suggestedTags.map(tag => ( </label>
<Tag <ul className="tags--remove">
disabled={tag !== 'mature' && maxed} {!tagsPassedIn.length && <Tag key={`placeholder-tag`} name={'example'} disabled type={'remove'} />}
key={`suggested${tag}`} {Boolean(tagsPassedIn.length) &&
name={tag} // .filter(t => !UTILITY_TAGS.includes(t.name))
type="add" tagsPassedIn.map(tag => (
onClick={() => handleTagClick(tag)} <Tag
key={`passed${tag.name}`}
name={tag.name}
type="remove"
onClick={() => {
onRemove(tag);
}}
/>
))}
</ul>
<FormField
autoFocus={!disableAutoFocus}
className="tag__input"
onChange={onChange}
placeholder={placeholder || __('gaming, crypto')}
type="text"
value={newTag}
disabled={disabled}
label={__('Add Tags')}
/>
<section>
<label>{newTag.length ? __('Matching') : __('Known Tags')}</label>
<ul className="tags">
{Boolean(newTag.length) && !suggestedTags.includes(newTag) && (
<Tag
disabled={newTag !== 'mature' && maxed}
key={`entered${newTag}`}
name={newTag}
type="add"
onClick={newTag.includes('') ? e => handleSubmit(e) : e => handleTagClick(newTag)}
/>
)}
{suggestedTags.map(tag => (
<Tag
disabled={tag !== 'mature' && maxed}
key={`suggested${tag}`}
name={tag}
type="add"
onClick={() => handleTagClick(tag)}
/>
))}
</ul>
</section>
</fieldset-section>
{experimentalFeature && onSelect && (
<fieldset-section>
<label>{__('Control Tags')}</label>
{UTILITY_TAGS.map(t => (
<FormField
key={t}
name={t}
type="checkbox"
blockWrap={false}
label={__(
t
.split('_')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
)}
checked={tagsPassedIn.some(te => te.name === t)}
onChange={() => handleUtilityTagCheckbox(t)}
/> />
))} ))}
</ul> </fieldset-section>
</section> )}
</Form> </Form>
</React.Fragment> </React.Fragment>
); );

View file

@ -13,6 +13,12 @@ export const DEFAULT_FOLLOWED_TAGS = [
'technology', 'technology',
]; ];
export const UTILITY_TAGS = [
// 'disable_supports',
'disable_comments',
// 'disable_reactions',
];
export const MATURE_TAGS = [ export const MATURE_TAGS = [
'porn', 'porn',
'porno', 'porno',

View file

@ -7,6 +7,7 @@ import {
selectCurrentChannelPage, selectCurrentChannelPage,
makeSelectClaimForUri, makeSelectClaimForUri,
makeSelectClaimIsPending, makeSelectClaimIsPending,
makeSelectTagsForUri,
} from 'lbry-redux'; } from 'lbry-redux';
import { selectChannelIsBlocked } from 'redux/selectors/blocked'; import { selectChannelIsBlocked } from 'redux/selectors/blocked';
import { selectBlackListedOutpoints, doFetchSubCount, makeSelectSubCountForUri } from 'lbryinc'; import { selectBlackListedOutpoints, doFetchSubCount, makeSelectSubCountForUri } from 'lbryinc';
@ -28,6 +29,7 @@ const select = (state, props) => ({
subCount: makeSelectSubCountForUri(props.uri)(state), subCount: makeSelectSubCountForUri(props.uri)(state),
pending: makeSelectClaimIsPending(props.uri)(state), pending: makeSelectClaimIsPending(props.uri)(state),
youtubeChannels: selectYoutubeChannels(state), youtubeChannels: selectYoutubeChannels(state),
tags: makeSelectTagsForUri(props.uri)(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({

View file

@ -7,6 +7,7 @@ import {
makeSelectMetadataForUri, makeSelectMetadataForUri,
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
SETTINGS, SETTINGS,
makeSelectTagInClaimOrChannelForUri,
} from 'lbry-redux'; } from 'lbry-redux';
import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc'; import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings'; import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
@ -18,6 +19,7 @@ const select = (state, props) => {
const { search } = props.location; const { search } = props.location;
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const linkedCommentId = urlParams.get('lc'); const linkedCommentId = urlParams.get('lc');
const DISABLE_COMMENTS_TAG = 'disable_comments';
return { return {
linkedComment: makeSelectCommentForCommentId(linkedCommentId)(state), linkedComment: makeSelectCommentForCommentId(linkedCommentId)(state),
@ -28,6 +30,7 @@ const select = (state, props) => {
fileInfo: makeSelectFileInfoForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state),
renderMode: makeSelectFileRenderModeForUri(props.uri)(state), renderMode: makeSelectFileRenderModeForUri(props.uri)(state),
videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state), videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state),
disableComments: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state),
}; };
}; };

View file

@ -9,6 +9,7 @@ import FileRenderInline from 'component/fileRenderInline';
import FileRenderDownload from 'component/fileRenderDownload'; import FileRenderDownload from 'component/fileRenderDownload';
import RecommendedContent from 'component/recommendedContent'; import RecommendedContent from 'component/recommendedContent';
import CommentsList from 'component/commentsList'; import CommentsList from 'component/commentsList';
import Empty from 'component/common/empty';
export const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container'; export const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container';
@ -25,6 +26,7 @@ type Props = {
linkedComment: any, linkedComment: any,
setPrimaryUri: (?string) => void, setPrimaryUri: (?string) => void,
videoTheaterMode: boolean, videoTheaterMode: boolean,
disableComments: boolean,
}; };
function FilePage(props: Props) { function FilePage(props: Props) {
@ -41,6 +43,7 @@ function FilePage(props: Props) {
linkedComment, linkedComment,
setPrimaryUri, setPrimaryUri,
videoTheaterMode, videoTheaterMode,
disableComments,
} = props; } = props;
const cost = costInfo ? costInfo.cost : null; const cost = costInfo ? costInfo.cost : null;
const hasFileInfo = fileInfo !== undefined; const hasFileInfo = fileInfo !== undefined;
@ -120,7 +123,8 @@ function FilePage(props: Props) {
<div className="file-page__secondary-content"> <div className="file-page__secondary-content">
<div> <div>
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitle uri={uri} />} {RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitle uri={uri} />}
<CommentsList uri={uri} linkedComment={linkedComment} /> {disableComments && <Empty text={__('Comments are disabled here.')} />}
{!disableComments && <CommentsList uri={uri} linkedComment={linkedComment} />}
</div> </div>
{videoTheaterMode && <RecommendedContent uri={uri} />} {videoTheaterMode && <RecommendedContent uri={uri} />}
</div> </div>