add initial support for comment API outside of SDK

This commit is contained in:
Sean Yesmunt 2021-02-11 00:12:41 -05:00
parent db87125dc8
commit 1f117e43bd
22 changed files with 421 additions and 118 deletions

View file

@ -9,6 +9,7 @@ WEB_SERVER_PORT=1337
LBRY_WEB_API=https://api.lbry.tv
LBRY_WEB_STREAMING_API=https://cdn.lbryplayer.xyz
LBRY_WEB_BUFFER_API=https://collector-service.api.lbry.tv/api/v1/events/video
COMMENT_SERVER_API=https://comments.lbry.com/api/v2
WELCOME_VERSION=1.0
# Custom Site info

View file

@ -33,6 +33,7 @@ module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/ui/analytics\1'
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/ui/rewards\1'
module.name_mapper='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\1'
module.name_mapper='^effects\(.*\)$' -> '<PROJECT_ROOT>/ui/effects\1'
module.name_mapper='^comments\(.*\)$' -> '<PROJECT_ROOT>/ui/comments\1'
module.name_mapper='^config\(.*\)$' -> '<PROJECT_ROOT>/config\1'
module.name_mapper='^web\/component\(.*\)$' -> '<PROJECT_ROOT>/web/component\1'
module.name_mapper='^web\/effects\(.*\)$' -> '<PROJECT_ROOT>/web/effects\1'

View file

@ -12,6 +12,7 @@ const config = {
LBRY_API_URL: process.env.LBRY_API_URL, //api.lbry.com',
LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //cdn.lbryplayer.xyz',
LBRY_WEB_BUFFER_API: process.env.LBRY_WEB_BUFFER_API,
COMMENT_SERVER_API: process.env.COMMENT_SERVER_API,
WELCOME_VERSION: process.env.WELCOME_VERSION,
DOMAIN: process.env.DOMAIN,
SHARE_DOMAIN_URL: process.env.SHARE_DOMAIN_URL,

16
flow-typed/comments.js vendored Normal file
View file

@ -0,0 +1,16 @@
// @flow
declare type CommentListParams = {
page: number,
page_size: number,
claim_id: string,
};
declare type CommentAbandonParams = {
comment_id: string,
creator_channel_id?: string,
creator_channel_name?: string,
channel_id?: string,
hexdata?: string,
};
declare type ModerationBlockParams = {};

View file

@ -142,7 +142,7 @@
"imagesloaded": "^4.1.4",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#f0849b4ce19e5e9600b74d61c6db82f0b853b9e8",
"lbry-redux": "lbryio/lbry-redux#d90cbd18925788b01fa9c4055c81300cb39e0b3a",
"lbryinc": "lbryio/lbryinc#eee2cb730ecec95a1344a755035755b0d4dad5cf",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",

37
ui/comments.js Normal file
View file

@ -0,0 +1,37 @@
// @flow
import { COMMENT_SERVER_API } from 'config';
const Comments = {
url: COMMENT_SERVER_API,
enabled: Boolean(COMMENT_SERVER_API),
moderation_block: (params: ModerationBlockParams) => fetchCommentsApi('moderation.Block', params),
comment_list: (params: CommentListParams) => fetchCommentsApi('comment.List', params),
comment_abandon: (params: CommentAbandonParams) => fetchCommentsApi('comment.Abandon', params),
};
function fetchCommentsApi(method: string, params: {}) {
if (!Comments.enabled) {
return Promise.reject('Comments are not currently enabled'); // eslint-disable-line
}
const url = `${Comments.url}?m=${method}`;
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method,
params,
}),
};
return fetch(url, options)
.then(res => res.json())
.then(res => res.result);
}
export default Comments;

View file

@ -16,7 +16,7 @@ import {
selectIsUpgradeAvailable,
selectAutoUpdateDownloaded,
selectModal,
selectActiveChannelId,
selectActiveChannelClaim,
} from 'redux/selectors/app';
import { doGetWalletSyncPreference, doSetLanguage } from 'redux/actions/settings';
import { doSyncLoop } from 'redux/actions/sync';
@ -44,7 +44,7 @@ const select = state => ({
isAuthenticated: selectUserVerifiedEmail(state),
currentModal: selectModal(state),
syncFatalError: selectSyncFatalError(state),
activeChannelId: selectActiveChannelId(state),
activeChannelClaim: selectActiveChannelClaim(state),
myChannelUrls: selectMyChannelUrls(state),
});

View file

@ -80,9 +80,9 @@ type Props = {
syncEnabled: boolean,
currentModal: any,
syncFatalError: boolean,
activeChannelId: ?string,
activeChannelClaim: ?ChannelClaim,
myChannelUrls: ?Array<string>,
setActiveChannelIfNotSet: (?string) => void,
setActiveChannelIfNotSet: () => void,
setIncognito: boolean => void,
};
@ -110,8 +110,8 @@ function App(props: Props) {
syncLoop,
currentModal,
syncFatalError,
activeChannelId,
myChannelUrls,
activeChannelClaim,
setActiveChannelIfNotSet,
setIncognito,
} = props;
@ -144,6 +144,7 @@ function App(props: Props) {
const hasMyChannels = myChannelUrls && myChannelUrls.length > 0;
const hasNoChannels = myChannelUrls && myChannelUrls.length === 0;
const shouldMigrateLanguage = LANGUAGE_MIGRATIONS[language];
const hasActiveChannelClaim = activeChannelClaim !== undefined;
let uri;
try {
@ -238,12 +239,12 @@ function App(props: Props) {
}, [theme]);
useEffect(() => {
if (hasMyChannels && !activeChannelId) {
if (hasMyChannels && !hasActiveChannelClaim) {
setActiveChannelIfNotSet();
} else if (hasNoChannels) {
setIncognito(true);
}
}, [hasMyChannels, activeChannelId, setActiveChannelIfNotSet]);
}, [hasMyChannels, hasNoChannels, hasActiveChannelClaim, setActiveChannelIfNotSet, setIncognito]);
useEffect(() => {
if (!languages.includes(language)) {

View file

@ -1,12 +1,11 @@
import { connect } from 'react-redux';
import { makeSelectThumbnailForUri, selectMyChannelClaims, makeSelectChannelPermUrlForClaimUri } from 'lbry-redux';
import { doCommentAbandon, doCommentUpdate, doCommentPin, doCommentList } from 'redux/actions/comments';
import { doToggleBlockChannel } from 'redux/actions/blocked';
import { makeSelectThumbnailForUri } from 'lbry-redux';
import { doCommentUpdate } from 'redux/actions/comments';
import { selectChannelIsBlocked } from 'redux/selectors/blocked';
import { doToast } from 'redux/actions/notifications';
import { doSetPlayingUri } from 'redux/actions/content';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectIsFetchingComments, makeSelectOthersReactionsForComment } from 'redux/selectors/comments';
import { makeSelectOthersReactionsForComment } from 'redux/selectors/comments';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import Comment from './view';
@ -14,21 +13,14 @@ const select = (state, props) => ({
thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state),
channelIsBlocked: props.authorUri && selectChannelIsBlocked(props.authorUri)(state),
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
isFetchingComments: selectIsFetchingComments(state),
myChannels: selectMyChannelClaims(state),
othersReacts: makeSelectOthersReactionsForComment(props.commentId)(state),
contentChannelPermanentUrl: makeSelectChannelPermUrlForClaimUri(props.uri)(state),
activeChannelClaim: selectActiveChannelClaim(state),
});
const perform = dispatch => ({
closeInlinePlayer: () => dispatch(doSetPlayingUri({ uri: null })),
updateComment: (commentId, comment) => dispatch(doCommentUpdate(commentId, comment)),
deleteComment: commentId => dispatch(doCommentAbandon(commentId)),
blockChannel: channelUri => dispatch(doToggleBlockChannel(channelUri)),
doToast: options => dispatch(doToast(options)),
pinComment: (commentId, remove) => dispatch(doCommentPin(commentId, remove)),
fetchComments: uri => dispatch(doCommentList(uri)),
});
export default connect(select, perform)(Comment);

View file

@ -10,7 +10,7 @@ import Button from 'component/button';
import Expandable from 'component/expandable';
import MarkdownPreview from 'component/common/markdown-preview';
import ChannelThumbnail from 'component/channelThumbnail';
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
import { Menu, MenuButton } from '@reach/menu-button';
import Icon from 'component/common/icon';
import { FormField, Form } from 'component/common/form';
import classnames from 'classnames';
@ -19,6 +19,7 @@ import CommentReactions from 'component/commentReactions';
import CommentsReplies from 'component/commentsReplies';
import { useHistory } from 'react-router';
import CommentCreate from 'component/commentCreate';
import CommentMenuList from 'component/commentMenuList';
type Props = {
closeInlinePlayer: () => void,
@ -32,8 +33,7 @@ type Props = {
claimIsMine: boolean, // if you control the claim which this comment was posted on
commentIsMine: boolean, // if this comment was signed by an owned channel
updateComment: (string, string) => void,
deleteComment: string => void,
blockChannel: string => void,
commentModBlock: string => void,
linkedComment?: any,
myChannels: ?Array<ChannelClaim>,
commentingEnabled: boolean,
@ -45,10 +45,7 @@ type Props = {
like: number,
dislike: number,
},
pinComment: (string, boolean) => Promise<any>,
fetchComments: string => void,
commentIdentityChannel: any,
contentChannelPermanentUrl: any,
activeChannelClaim: ?ChannelClaim,
};
@ -67,8 +64,6 @@ function Comment(props: Props) {
commentIsMine,
commentId,
updateComment,
deleteComment,
blockChannel,
linkedComment,
commentingEnabled,
myChannels,
@ -76,11 +71,7 @@ function Comment(props: Props) {
isTopLevel,
threadDepth,
isPinned,
pinComment,
fetchComments,
othersReacts,
contentChannelPermanentUrl,
activeChannelClaim,
} = props;
const {
push,
@ -133,20 +124,11 @@ function Comment(props: Props) {
setCommentValue(!SIMPLE_SITE && advancedEditor ? event : event.target.value);
}
function handlePinComment(commentId, remove) {
pinComment(commentId, remove).then(() => fetchComments(uri));
}
function handleEditComment() {
closeInlinePlayer();
setEditing(true);
}
function handleDeleteComment() {
closeInlinePlayer();
deleteComment(commentId);
}
function handleSubmit() {
updateComment(commentId, editedMessage);
setEditing(false);
@ -228,38 +210,15 @@ function Comment(props: Props) {
icon={ICONS.MORE_VERTICAL}
/>
</MenuButton>
<MenuList className="menu__list--comments">
{commentIsMine ? (
<>
<MenuItem className="comment__menu-option menu__link" onSelect={handleEditComment}>
<Icon aria-hidden icon={ICONS.EDIT} />
{__('Edit')}
</MenuItem>
<MenuItem className="comment__menu-option menu__link" onSelect={handleDeleteComment}>
<Icon aria-hidden icon={ICONS.DELETE} />
{__('Delete')}
</MenuItem>
</>
) : (
<MenuItem className="comment__menu-option menu__link" onSelect={() => blockChannel(authorUri)}>
<Icon aria-hidden icon={ICONS.NO} />
{__('Block Channel')}
</MenuItem>
)}
{activeChannelClaim && activeChannelClaim.permanent_url === contentChannelPermanentUrl && isTopLevel && (
<MenuItem
className="comment__menu-option menu__link"
onSelect={
isPinned ? () => handlePinComment(commentId, true) : () => handlePinComment(commentId, false)
}
>
<span className={'button__content'}>
<Icon aria-hidden icon={ICONS.PIN} className={'icon'} />
{isPinned ? __('Unpin') : __('Pin')}
</span>
</MenuItem>
)}
</MenuList>
<CommentMenuList
uri={uri}
isTopLevel={isTopLevel}
isPinned={isPinned}
commentId={commentId}
authorUri={authorUri}
commentIsMine={commentIsMine}
handleEditComment={handleEditComment}
/>
</Menu>
</div>
</div>

View file

@ -0,0 +1,32 @@
import { connect } from 'react-redux';
import { makeSelectChannelPermUrlForClaimUri, makeSelectClaimIsMine, makeSelectClaimForUri } from 'lbry-redux';
import {
doCommentAbandon,
doCommentPin,
doCommentList,
// doCommentModBlock,
} from 'redux/actions/comments';
import { doToggleBlockChannel } from 'redux/actions/blocked';
// import { doSetActiveChannel } from 'redux/actions/app';
import { doSetPlayingUri } from 'redux/actions/content';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import CommentMenuList from './view';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
contentChannelPermanentUrl: makeSelectChannelPermUrlForClaimUri(props.uri)(state),
activeChannelClaim: selectActiveChannelClaim(state),
});
const perform = dispatch => ({
closeInlinePlayer: () => dispatch(doSetPlayingUri({ uri: null })),
deleteComment: (commentId, creatorChannelUrl) => dispatch(doCommentAbandon(commentId, creatorChannelUrl)),
blockChannel: channelUri => dispatch(doToggleBlockChannel(channelUri)),
pinComment: (commentId, remove) => dispatch(doCommentPin(commentId, remove)),
fetchComments: uri => dispatch(doCommentList(uri)),
// setActiveChannel: channelId => dispatch(doSetActiveChannel(channelId)),
// commentModBlock: commentAuthor => dispatch(doCommentModBlock(commentAuthor)),
});
export default connect(select, perform)(CommentMenuList);

View file

@ -0,0 +1,149 @@
// @flow
import * as ICONS from 'constants/icons';
import React from 'react';
import { MenuList, MenuItem } from '@reach/menu-button';
import ChannelThumbnail from 'component/channelThumbnail';
import Icon from 'component/common/icon';
type Props = {
uri: string,
closeInlinePlayer: () => void,
authorUri: string, // full LBRY Channel URI: lbry://@channel#123...
commentId: string, // sha256 digest identifying the comment
claimIsMine: boolean, // if you control the claim which this comment was posted on
commentIsMine: boolean, // if this comment was signed by an owned channel
deleteComment: (string, ?string) => void,
linkedComment?: any,
isPinned: boolean,
pinComment: (string, boolean) => Promise<any>,
blockChannel: string => void,
fetchComments: string => void,
handleEditComment: () => void,
contentChannelPermanentUrl: any,
activeChannelClaim: ?ChannelClaim,
claimIsMine: boolean,
isTopLevel: boolean,
// commentModBlock: string => void,
};
function CommentMenuList(props: Props) {
const {
uri,
authorUri,
commentIsMine,
commentId,
deleteComment,
blockChannel,
pinComment,
claimIsMine,
closeInlinePlayer,
activeChannelClaim,
contentChannelPermanentUrl,
isTopLevel,
isPinned,
handleEditComment,
fetchComments,
// commentModBlock,
// setActiveChannel,
} = props;
const activeChannelIsCreator = activeChannelClaim && activeChannelClaim.permanent_url === contentChannelPermanentUrl;
// let authorChannel;
// try {
// const { claimName } = parseURI(authorUri);
// authorChannel = claimName;
// } catch (e) {}
function handlePinComment(commentId, remove) {
pinComment(commentId, remove).then(() => fetchComments(uri));
}
function handleDeleteComment() {
closeInlinePlayer();
deleteComment(commentId, commentIsMine ? undefined : contentChannelPermanentUrl);
}
function handleCommentBlock() {
if (claimIsMine) {
// Block them from commenting on future content
// commentModBlock(authorUri);
}
blockChannel(authorUri);
}
// function handleChooseChannel() {
// const { channelClaimId } = parseURI(authorUri);
// setActiveChannel(channelClaimId);
// }
return (
<MenuList className="menu__list--comments">
{/* {commentIsMine && activeChannelClaim && activeChannelClaim.permanent_url !== authorUri && (
<MenuItem className="comment__menu-option" onSelect={handleChooseChannel}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.CHANNEL} />
{__('Use this channel')}
</div>
<span className="comment__menu-help">
{__('Switch to %channel_name% to interact with this comment', { channel_name: authorChannel })}.
</span>
</MenuItem>
)} */}
{activeChannelIsCreator && <div className="comment__menu-title">{__('Creator tools')}</div>}
{activeChannelIsCreator && isTopLevel && (
<MenuItem
className="comment__menu-option menu__link"
onSelect={isPinned ? () => handlePinComment(commentId, true) : () => handlePinComment(commentId, false)}
>
<span className={'button__content'}>
<Icon aria-hidden icon={ICONS.PIN} className={'icon'} />
{isPinned ? __('Unpin') : __('Pin')}
</span>
</MenuItem>
)}
{activeChannelClaim &&
(activeChannelClaim.permanent_url === authorUri ||
activeChannelClaim.permanent_url === contentChannelPermanentUrl) && (
<MenuItem className="comment__menu-option" onSelect={handleDeleteComment}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.DELETE} />
{__('Remove')}
</div>
</MenuItem>
)}
{commentIsMine && activeChannelClaim && activeChannelClaim.permanent_url === authorUri && (
<MenuItem className="comment__menu-option menu__link" onSelect={handleEditComment}>
<Icon aria-hidden icon={ICONS.EDIT} />
{__('Edit')}
</MenuItem>
)}
{/* Disabled until we deal with current app blocklist parity */}
{!commentIsMine && (
<MenuItem className="comment__menu-option" onSelect={handleCommentBlock}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.BLOCK} />
{__('Block')}
</div>
{/* {activeChannelIsCreator && (
<span className="comment__menu-help">Hide this channel's comments and block them from commenting.</span>
)} */}
</MenuItem>
)}
{activeChannelClaim && (
<div className="comment__menu-active">
<ChannelThumbnail uri={activeChannelClaim.permanent_url} />
<div className="comment__menu-channel">Interacting as {activeChannelClaim.name}</div>
</div>
)}
</MenuList>
);
}
export default CommentMenuList;

View file

@ -1,11 +1,12 @@
import { connect } from 'react-redux';
import { selectBalance } from 'lbry-redux';
import { selectBalance, selectFetchingMyChannels } from 'lbry-redux';
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
import PublishPage from './view';
const select = state => ({
balance: selectBalance(state),
totalRewardValue: selectUnclaimedRewardValue(state),
fetchingChannels: selectFetchingMyChannels(state),
});
export default connect(select, null)(PublishPage);

View file

@ -3,13 +3,15 @@ import React from 'react';
import PublishForm from 'component/publishForm';
import Page from 'component/page';
import YrblWalletEmpty from 'component/yrblWalletEmpty';
import Spinner from 'component/spinner';
type Props = {
balance: number,
fetchingChannels: boolean,
};
function PublishPage(props: Props) {
const { balance } = props;
const { balance, fetchingChannels } = props;
function scrollToTop() {
const mainContent = document.querySelector('main');
@ -32,7 +34,13 @@ function PublishPage(props: Props) {
}}
>
{balance === 0 && <YrblWalletEmpty />}
<PublishForm scrollToTop={scrollToTop} disabled={balance === 0} />
{balance !== 0 && fetchingChannels ? (
<div className="main--empty">
<Spinner />
</div>
) : (
<PublishForm scrollToTop={scrollToTop} disabled={balance === 0} />
)}
</Page>
);
}

View file

@ -11,6 +11,8 @@ import {
} from 'redux/selectors/comments';
import { makeSelectNotificationForCommentId } from 'redux/selectors/notifications';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { toHex } from 'util/hex';
import Comments from 'comments';
export function doCommentList(uri: string, page: number = 1, pageSize: number = 99999) {
return (dispatch: Dispatch, getState: GetState) => {
@ -18,15 +20,23 @@ export function doCommentList(uri: string, page: number = 1, pageSize: number =
const claim = selectClaimsByUri(state)[uri];
const claimId = claim ? claim.claim_id : null;
if (!claimId) {
dispatch({
type: ACTIONS.COMMENT_LIST_FAILED,
data: 'unable to find claim for uri',
});
return;
}
dispatch({
type: ACTIONS.COMMENT_LIST_STARTED,
});
return Lbry.comment_list({
claim_id: claimId,
return Comments.comment_list({
page,
claim_id: claimId,
page_size: pageSize,
include_replies: true,
skip_validation: true,
})
.then((result: CommentListResponse) => {
const { items: comments } = result;
@ -238,35 +248,6 @@ export function doCommentCreate(comment: string = '', claim_id: string = '', par
};
}
export function doCommentHide(comment_id: string) {
return (dispatch: Dispatch) => {
dispatch({
type: ACTIONS.COMMENT_HIDE_STARTED,
});
return Lbry.comment_hide({
comment_ids: [comment_id],
})
.then((result: CommentHideResponse) => {
dispatch({
type: ACTIONS.COMMENT_HIDE_COMPLETED,
data: result,
});
})
.catch(error => {
dispatch({
type: ACTIONS.COMMENT_HIDE_FAILED,
data: error,
});
dispatch(
doToast({
message: 'Unable to hide this comment, please try again later.',
isError: true,
})
);
});
};
}
export function doCommentPin(commentId: string, remove: boolean) {
return (dispatch: Dispatch, getState: GetState) => {
const state = getState();
@ -308,13 +289,33 @@ export function doCommentPin(commentId: string, remove: boolean) {
};
}
export function doCommentAbandon(comment_id: string) {
return (dispatch: Dispatch) => {
export function doCommentAbandon(commentId: string, creatorChannelUri?: string) {
return async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const claim = creatorChannelUri ? selectClaimsByUri(state)[creatorChannelUri] : undefined;
const creatorChannelId = claim ? claim.claim_id : null;
const creatorChannelName = claim ? claim.name : null;
const activeChannelClaim = selectActiveChannelClaim(state);
dispatch({
type: ACTIONS.COMMENT_ABANDON_STARTED,
});
return Lbry.comment_abandon({
comment_id: comment_id,
let commentIdSignature;
if (activeChannelClaim) {
try {
commentIdSignature = await Lbry.channel_sign({
channel_id: activeChannelClaim.claim_id,
hexdata: toHex(commentId),
});
} catch (e) {}
}
return Comments.comment_abandon({
comment_id: commentId,
...(creatorChannelId ? { creator_channel_id: creatorChannelId } : {}),
...(creatorChannelName ? { creator_channel_name: creatorChannelName } : {}),
...(commentIdSignature || {}),
})
.then((result: CommentAbandonResponse) => {
// Comment may not be deleted if the signing channel can't be signed.
@ -323,7 +324,7 @@ export function doCommentAbandon(comment_id: string) {
dispatch({
type: ACTIONS.COMMENT_ABANDON_COMPLETED,
data: {
comment_id: comment_id,
comment_id: commentId,
},
});
} else {
@ -343,6 +344,7 @@ export function doCommentAbandon(comment_id: string) {
type: ACTIONS.COMMENT_ABANDON_FAILED,
data: error,
});
dispatch(
doToast({
message: 'Unable to delete this comment, please try again later.',
@ -402,3 +404,40 @@ export function doCommentUpdate(comment_id: string, comment: string) {
};
}
}
// Hides a users comments from all creator's claims and prevent them from commenting in the future
export function doCommentModBlock(commentAuthor: string) {
return async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const claim = selectClaimsByUri(state)[commentAuthor];
if (!claim) {
console.error("Can't find claim to block"); // eslint-disable-line
return;
}
const creatorIdToBan = claim ? claim.claim_id : null;
const creatorNameToBan = claim ? claim.name : null;
const activeChannelClaim = selectActiveChannelClaim(state);
let channelSignature = {};
if (activeChannelClaim) {
try {
channelSignature = await Lbry.channel_sign({
channel_id: activeChannelClaim.claim_id,
hexdata: toHex(activeChannelClaim.name),
});
} catch (e) {}
}
return Comments.moderation_block({
mod_channel_id: activeChannelClaim.claim_id,
mod_channel_name: activeChannelClaim.name,
signature: channelSignature.signature,
signing_ts: channelSignature.signing_ts,
banned_channel_id: creatorIdToBan,
banned_channel_name: creatorNameToBan,
delete_all: true,
});
};
}

View file

@ -310,6 +310,7 @@ reducers[ACTIONS.SET_ACTIVE_CHANNEL] = (state, action) => {
activeChannel: action.data.claimId,
};
};
reducers[ACTIONS.SET_INCOGNITO] = (state, action) => {
return {
...state,

View file

@ -212,6 +212,7 @@ $thumbnailWidthSmall: 0rem;
.comment__menu {
align-self: flex-end;
line-height: 1;
button {
border-radius: var(--border-radius);
@ -239,7 +240,13 @@ $thumbnailWidthSmall: 0rem;
display: flex;
align-items: center;
padding: var(--spacing-s);
font-size: var(--font-small);
font-size: var(--font-xsmall);
.menu__link {
padding-left: 0;
padding-right: 0;
padding: 0;
}
}
.comment__menu-icon--hovering {
@ -256,6 +263,26 @@ $thumbnailWidthSmall: 0rem;
padding: var(--spacing-s);
}
.comment__menu-title {
@extend .help;
display: flex;
justify-content: space-between;
font-size: var(--font-small);
border-bottom: 1px solid var(--color-border);
margin-top: 0;
padding-left: var(--spacing-s);
padding-bottom: var(--spacing-s);
padding-right: var(--spacing-xl);
}
.comment__menu-help {
@extend .help;
margin-top: var(--spacing-xs);
padding-left: calc(18px + var(--spacing-s));
max-width: 15rem;
white-space: pre-line;
}
.comment__actions {
display: flex;
margin-top: var(--spacing-s);
@ -332,3 +359,27 @@ $thumbnailWidthSmall: 0rem;
top: 0.4rem;
left: 0.4rem;
}
.comment__menu-active {
padding-left: var(--spacing-s);
padding-top: var(--spacing-s);
border-top: 1px solid var(--color-border);
margin-top: var(--spacing-s);
display: flex;
align-items: center;
.channel-thumbnail {
margin-right: var(--spacing-xs);
height: 1.8rem;
width: 1.8rem;
}
}
.comment__menu-channel {
@extend .help;
font-size: var(--font-xsmall);
margin-top: 0;
max-width: 10rem;
white-space: pre-line;
margin-right: var(--spacing-s);
}

View file

@ -446,6 +446,8 @@ fieldset-section {
}
.select--slim {
margin-bottom: var(--spacing-xxs);
@media (min-width: $breakpoint-small) {
max-width: none;
}

View file

@ -66,7 +66,11 @@
@extend .menu__list;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: var(--spacing-xxs);
padding: var(--spacing-s) 0;
[data-reach-menu-item] {
margin: 0 var(--spacing-xs);
}
}
.menu__link {

View file

@ -56,7 +56,7 @@
--color-placeholder-background: #f0f0f0;
--color-header-background: #ffffff;
--color-card-background: #ffffff;
--color-card-background-highlighted: #f0f7ff;
--color-card-background-highlighted: #f1f7fe;
--color-list-header: #fff;
--color-file-viewer-background: var(--color-card-background);
--color-tabs-background: var(--color-card-background);

8
ui/util/hex.js Normal file
View file

@ -0,0 +1,8 @@
// @flow
export function toHex(str: string): string {
var result = '';
for (var i = 0; i < str.length; i++) {
result += str.charCodeAt(i).toString(16);
}
return result;
}

View file

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