mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-09-02 18:25:12 +00:00
provide tags for disabling comments
This commit is contained in:
parent
d47d55098e
commit
a85c9a1f48
8 changed files with 126 additions and 60 deletions
|
@ -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),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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} />
|
||||||
|
|
|
@ -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, {
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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 => ({
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Reference in a new issue