List own comments (#7171)

* Add option to pass in url-search params.

Impetus: allow linked comment ID and setting the discussion tab when clicking on the `ClaimPreview`.

* comment.list: fix typos and renamed variables

- Switch from 'author' to 'creator' to disambiguate between comment author and content author. For comment author, we'll use 'commenter' from now on.
- Corrected 'commenterClaimId' to 'creatorClaimId' (just a typo, no functional change).

* doCommentReset: change param from uri to claimId

This reduces one lookup as clients will always have the claimID ready, but might not have the full URI.

It was using URI previously just to match the other APIs.

* Add doCommentListOwn -- command to fetch own comments

Since the redux slice is set up based on content or channel ID (for Channel Discussion page), re-use the channel ID for the case of "own comments". We always clear each ID when fetching page-0, so no worries of conflict when actually browsing the Channel Discussion page.

* Comment: add option to hide the actions section

* Implement own-comments page

* Use new param to remove sort-pins-first.

comment.List currently always pushes pins to the top to support pagination. This new param removes this behavior.
This commit is contained in:
infinite-persistence 2021-10-01 20:10:27 +08:00 committed by GitHub
parent d3be8726fc
commit de6c6f9bfd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 472 additions and 39 deletions

View file

@ -111,14 +111,14 @@ declare type ReactionListResponse = {
declare type CommentListParams = {
page: number, // pagination: which page of results
page_size: number, // pagination: nr of comments to show in a page (max 200)
claim_id: string, // claim id of claim being commented on
claim_id?: string, // claim id of claim being commented on
channel_name?: string, // signing channel name of claim (enables 'commentsEnabled' check)
channel_id?: string, // signing channel claim id of claim (enables 'commentsEnabled' check)
author_claim_id?: string, // filters comments to just this author
parent_id?: string, // filters comments to those under this thread
top_level?: boolean, // filters to only top level comments
hidden?: boolean, // if true, will show hidden comments as well
sort_by?: number, // NEWEST=0, OLDEST=1, CONTROVERSY=2, POPULARITY=3,
sort_by?: number, // @see: ui/constants/comments.js::SORT_BY
};
declare type CommentListResponse = {

View file

@ -209,6 +209,9 @@
"Failed to copy.": "Failed to copy.",
"The publisher has chosen to charge %lbc% to view this content. Your balance is currently too low to view it. Check out %reward_link% for free %lbc% or send more %lbc% to your wallet. You can also %buy_link% more %lbc%.": "The publisher has chosen to charge %lbc% to view this content. Your balance is currently too low to view it. Check out %reward_link% for free %lbc% or send more %lbc% to your wallet. You can also %buy_link% more %lbc%.",
"Connecting...": "Connecting...",
"Your comments": "Your comments",
"View your past comments.": "View your past comments.",
"Content or channel was deleted.": "Content or channel was deleted.",
"Comments": "Comments",
"Comment": "Comment",
"Comment --[button to submit something]--": "Comment",
@ -1460,6 +1463,7 @@
"Staked LBRY Credits": "Staked LBRY Credits",
"1 comment": "1 comment",
"%total_comments% comments": "%total_comments% comments",
"No comments": "No comments",
"Upvote": "Upvote",
"Downvote": "Downvote",
"You loved this": "You loved this",

View file

@ -70,6 +70,7 @@ type Props = {
streamingUrl: ?string,
getFile: (string) => void,
customShouldHide?: (Claim) => boolean,
searchParams?: { [string]: string },
showUnresolvedClaim?: boolean,
showNullPlaceholder?: boolean,
includeSupportAction?: boolean,
@ -125,6 +126,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
// modifiers
active,
customShouldHide,
searchParams,
showNullPlaceholder,
// value from show mature content user setting
// true if the user doesn't wanna see nsfw content
@ -221,6 +223,11 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
if (listId) {
navigateSearch.set(COLLECTIONS_CONSTS.COLLECTION_ID, listId);
}
if (searchParams) {
Object.keys(searchParams).forEach((key) => {
navigateSearch.set(key, searchParams[key]);
});
}
const handleNavLinkClick = (e) => {
if (onClick) {

View file

@ -52,6 +52,7 @@ type Props = {
doToast: ({ message: string }) => void,
isTopLevel?: boolean,
threadDepth: number,
hideActions?: boolean,
isPinned: boolean,
othersReacts: ?{
like: number,
@ -95,6 +96,7 @@ function Comment(props: Props) {
doToast,
isTopLevel,
threadDepth,
hideActions,
isPinned,
othersReacts,
playingUri,
@ -348,18 +350,20 @@ function Comment(props: Props) {
)}
</div>
<div className="comment__actions">
{threadDepth !== 0 && (
<Button
requiresAuth={IS_WEB}
label={commentingEnabled ? __('Reply') : __('Log in to reply')}
className="comment__action"
onClick={handleCommentReply}
icon={ICONS.REPLY}
/>
)}
{ENABLE_COMMENT_REACTIONS && <CommentReactions uri={uri} commentId={commentId} />}
</div>
{!hideActions && (
<div className="comment__actions">
{threadDepth !== 0 && (
<Button
requiresAuth={IS_WEB}
label={commentingEnabled ? __('Reply') : __('Log in to reply')}
className="comment__action"
onClick={handleCommentReply}
icon={ICONS.REPLY}
/>
)}
{ENABLE_COMMENT_REACTIONS && <CommentReactions uri={uri} commentId={commentId} />}
</div>
)}
{numDirectReplies > 0 && !showReplies && (
<div className="comment__actions">

View file

@ -48,7 +48,7 @@ const perform = (dispatch) => ({
fetchTopLevelComments: (uri, page, pageSize, sortBy) => dispatch(doCommentList(uri, '', page, pageSize, sortBy)),
fetchComment: (commentId) => dispatch(doCommentById(commentId)),
fetchReacts: (commentIds) => dispatch(doCommentReactList(commentIds)),
resetComments: (uri) => dispatch(doCommentReset(uri)),
resetComments: (claimId) => dispatch(doCommentReset(claimId)),
});
export default connect(select, perform)(CommentsList);

View file

@ -151,10 +151,13 @@ function CommentList(props: Props) {
// Reset comments
useEffect(() => {
if (page === 0) {
resetComments(uri);
if (claim) {
resetComments(claim.claim_id);
}
setPage(1);
}
}, [page, uri, resetComments]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [page, uri, resetComments]); // 'claim' is derived from 'uri'
// Fetch top-level comments
useEffect(() => {

View file

@ -59,6 +59,7 @@ const ListBlockedPage = lazyImport(() => import('page/listBlocked' /* webpackChu
const ListsPage = lazyImport(() => import('page/lists' /* webpackChunkName: "secondary" */));
const LiveStreamSetupPage = lazyImport(() => import('page/livestreamSetup' /* webpackChunkName: "secondary" */));
const LivestreamCurrentPage = lazyImport(() => import('page/livestreamCurrent' /* webpackChunkName: "secondary" */));
const OwnComments = lazyImport(() => import('page/ownComments' /* webpackChunkName: "ownComments" */));
const PasswordResetPage = lazyImport(() => import('page/passwordReset' /* webpackChunkName: "secondary" */));
const PasswordSetPage = lazyImport(() => import('page/passwordSet' /* webpackChunkName: "secondary" */));
const PublishPage = lazyImport(() => import('page/publish' /* webpackChunkName: "secondary" */));
@ -329,6 +330,7 @@ function AppRouter(props: Props) {
<PrivateRoute {...props} path={`/$/${PAGES.SWAP}`} component={SwapPage} />
<PrivateRoute {...props} path={`/$/${PAGES.NOTIFICATIONS}`} component={NotificationsPage} />
<PrivateRoute {...props} path={`/$/${PAGES.AUTH_WALLET_PASSWORD}`} component={SignInWalletPasswordPage} />
<PrivateRoute {...props} path={`/$/${PAGES.SETTINGS_OWN_COMMENTS}`} component={OwnComments} />
<Route path={`/$/${PAGES.EMBED}/:claimName`} exact component={EmbedWrapperPage} />
<Route path={`/$/${PAGES.EMBED}/:claimName/:claimId`} exact component={EmbedWrapperPage} />

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { doWalletStatus, selectWalletIsEncrypted } from 'lbry-redux';
import { doWalletStatus, selectMyChannelClaims, selectWalletIsEncrypted } from 'lbry-redux';
import { selectUser, selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectLanguage } from 'redux/selectors/settings';
@ -9,6 +9,7 @@ const select = (state) => ({
isAuthenticated: selectUserVerifiedEmail(state),
walletEncrypted: selectWalletIsEncrypted(state),
user: selectUser(state),
myChannels: selectMyChannelClaims(state),
language: selectLanguage(state),
});

View file

@ -15,12 +15,13 @@ type Props = {
isAuthenticated: boolean,
walletEncrypted: boolean,
user: User,
myChannels: ?Array<ChannelClaim>,
// --- perform ---
doWalletStatus: () => void,
};
export default function SettingAccount(props: Props) {
const { isAuthenticated, walletEncrypted, user, doWalletStatus } = props;
const { isAuthenticated, walletEncrypted, user, myChannels, doWalletStatus } = props;
const [storedPassword, setStoredPassword] = React.useState(false);
// Determine if password is stored.
@ -92,6 +93,17 @@ export default function SettingAccount(props: Props) {
</SettingsRow>
)}
{/* @endif */}
{myChannels && (
<SettingsRow title={__('Comments')} subtitle={__('View your past comments.')}>
<Button
button="inverse"
label={__('Manage')}
icon={ICONS.ARROW_RIGHT}
navigate={`/$/${PAGES.SETTINGS_OWN_COMMENTS}`}
/>
</SettingsRow>
)}
</>
}
/>

View file

@ -9,6 +9,7 @@ export const SORT_BY = {
OLDEST: 1,
CONTROVERSY: 2,
POPULARITY: 3,
NEWEST_NO_PINS: 4,
};
export const BLOCK_LEVEL = {

View file

@ -52,6 +52,7 @@ export const PAGE_TITLE = {
[PAGES.SETTINGS_STRIPE_ACCOUNT]: 'Bank Accounts',
[PAGES.SETTINGS_STRIPE_CARD]: 'Payment Methods',
[PAGES.SETTINGS_UPDATE_PWD]: 'Update password',
[PAGES.SETTINGS_OWN_COMMENTS]: 'Your comments',
[PAGES.SWAP]: 'Swap Credits',
[PAGES.TAGS_FOLLOWING]: 'Tags',
[PAGES.TAGS_FOLLOWING_MANAGE]: 'Manage tags',

View file

@ -46,6 +46,7 @@ exports.SETTINGS_NOTIFICATIONS = 'settings/notifications';
exports.SETTINGS_BLOCKED_MUTED = 'settings/block_and_mute';
exports.SETTINGS_CREATOR = 'settings/creator';
exports.SETTINGS_UPDATE_PWD = 'settings/update_password';
exports.SETTINGS_OWN_COMMENTS = 'settings/ownComments';
exports.SHOW = 'show';
exports.ACCOUNT = 'account';
exports.SEARCH = 'search';

View file

@ -0,0 +1,33 @@
import { connect } from 'react-redux';
import { doCommentListOwn, doCommentReset } from 'redux/actions/comments';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import {
selectIsFetchingComments,
makeSelectCommentsForUri,
makeSelectTotalCommentsCountForUri,
makeSelectTopLevelTotalPagesForUri,
} from 'redux/selectors/comments';
import { selectClaimsById } from 'lbry-redux';
import OwnComments from './view';
const select = (state) => {
const activeChannelClaim = selectActiveChannelClaim(state);
const uri = activeChannelClaim && activeChannelClaim.canonical_url;
return {
activeChannelClaim,
allComments: makeSelectCommentsForUri(uri)(state),
totalComments: makeSelectTotalCommentsCountForUri(uri)(state),
topLevelTotalPages: makeSelectTopLevelTotalPagesForUri(uri)(state),
isFetchingComments: selectIsFetchingComments(state),
claimsById: selectClaimsById(state),
};
};
const perform = (dispatch) => ({
doCommentReset: (a) => dispatch(doCommentReset(a)),
doCommentListOwn: (a, b, c) => dispatch(doCommentListOwn(a, b, c)),
});
export default connect(select, perform)(OwnComments);

View file

@ -0,0 +1,210 @@
// @flow
import React from 'react';
import Button from 'component/button';
import ChannelSelector from 'component/channelSelector';
import ClaimPreview from 'component/claimPreview';
import Comment from 'component/comment';
import Card from 'component/common/card';
import Empty from 'component/common/empty';
import Page from 'component/page';
import Spinner from 'component/spinner';
import { COMMENT_PAGE_SIZE_TOP_LEVEL } from 'constants/comment';
import * as ICONS from 'constants/icons';
import useFetched from 'effects/use-fetched';
import debounce from 'util/debounce';
function scaleToDevicePixelRatio(value) {
const devicePixelRatio = window.devicePixelRatio || 1.0;
if (devicePixelRatio < 1.0) {
return Math.ceil(value / devicePixelRatio);
}
return Math.ceil(value * devicePixelRatio);
}
type Props = {
activeChannelClaim: ?ChannelClaim,
allComments: Array<Comment>,
totalComments: number,
topLevelTotalPages: number,
isFetchingComments: boolean,
claimsById: any,
doCommentReset: (claimId: string) => void,
doCommentListOwn: (channelId: string, page: number, pageSize: number) => void,
};
export default function OwnComments(props: Props) {
const {
activeChannelClaim,
allComments,
totalComments,
isFetchingComments,
claimsById,
doCommentReset,
doCommentListOwn,
} = props;
const spinnerRef = React.useRef();
const [page, setPage] = React.useState(0);
const [activeChannelId, setActiveChannelId] = React.useState('');
// Since we are sharing the key for Discussion and MyComments, don't show
// the list until we've gone through the initial reset.
const wasResetAndReady = useFetched(isFetchingComments);
const totalPages = Math.ceil(totalComments / COMMENT_PAGE_SIZE_TOP_LEVEL);
const moreBelow = page < totalPages;
function getCommentsElem(comments) {
return comments.map((comment) => {
const contentClaim = claimsById[comment.claim_id];
const isChannel = contentClaim && contentClaim.value_type === 'channel';
const isLivestream = Boolean(contentClaim && contentClaim.value_type === 'stream' && !contentClaim.value.source);
return (
<div key={comment.comment_id} className="comments-own card__main-actions">
<div className="section__actions">
<div className="comments-own--claim">
{contentClaim && (
<ClaimPreview
uri={contentClaim.canonical_url}
searchParams={{
...(isChannel ? { view: 'discussion' } : {}),
...(isLivestream ? {} : { lc: comment.comment_id }),
}}
hideActions
hideMenu
properties={() => null}
/>
)}
{!contentClaim && <Empty text={__('Content or channel was deleted.')} />}
</div>
<Comment
isTopLevel
hideActions
authorUri={comment.channel_url}
author={comment.channel_name}
commentId={comment.comment_id}
message={comment.comment}
timePosted={comment.timestamp * 1000}
commentIsMine
supportAmount={comment.support_amount}
numDirectReplies={0} // Don't show replies here
isModerator={comment.is_moderator}
isGlobalMod={comment.is_global_mod}
isFiat={comment.is_fiat}
/>
</div>
</div>
);
});
}
// Active channel changed
React.useEffect(() => {
if (activeChannelClaim && activeChannelClaim.claim_id !== activeChannelId) {
setActiveChannelId(activeChannelClaim.claim_id);
setPage(0);
}
}, [activeChannelClaim, activeChannelId]);
// Reset comments
React.useEffect(() => {
if (page === 0 && activeChannelId) {
doCommentReset(activeChannelId);
setPage(1);
}
}, [page, activeChannelId]); // eslint-disable-line react-hooks/exhaustive-deps
// Fetch own comments
React.useEffect(() => {
if (page !== 0 && activeChannelId) {
doCommentListOwn(activeChannelId, page, COMMENT_PAGE_SIZE_TOP_LEVEL);
}
}, [page]); // eslint-disable-line react-hooks/exhaustive-deps
// Infinite scroll
React.useEffect(() => {
function shouldFetchNextPage(page, topLevelTotalPages, window, document, yPrefetchPx = 1000) {
if (!spinnerRef || !spinnerRef.current) {
return false;
}
const rect = spinnerRef.current.getBoundingClientRect(); // $FlowFixMe
const windowH = window.innerHeight || document.documentElement.clientHeight; // $FlowFixMe
const windowW = window.innerWidth || document.documentElement.clientWidth; // $FlowFixMe
const isApproachingViewport = yPrefetchPx !== 0 && rect.top < windowH + scaleToDevicePixelRatio(yPrefetchPx);
const isInViewport =
rect.width > 0 &&
rect.height > 0 &&
rect.bottom >= 0 &&
rect.right >= 0 &&
// $FlowFixMe
rect.top <= windowH &&
// $FlowFixMe
rect.left <= windowW;
return (isInViewport || isApproachingViewport) && page < topLevelTotalPages;
}
const handleCommentScroll = debounce(() => {
if (shouldFetchNextPage(page, totalPages, window, document)) {
setPage(page + 1);
}
}, 200);
if (!isFetchingComments && moreBelow && spinnerRef && spinnerRef.current) {
if (shouldFetchNextPage(page, totalPages, window, document, 0)) {
setPage(page + 1);
} else {
window.addEventListener('scroll', handleCommentScroll);
return () => window.removeEventListener('scroll', handleCommentScroll);
}
}
}, [page, spinnerRef, isFetchingComments, moreBelow, totalPages]);
// **************************************************************************
// **************************************************************************
if (!activeChannelClaim) {
return null;
}
return (
<Page noFooter noSideNavigation settingsPage backout={{ title: __('Your comments'), backLabel: __('Back') }}>
<ChannelSelector hideAnon />
<Card
isBodyList
title={
totalComments > 0
? totalComments === 1
? __('1 comment')
: __('%total_comments% comments', { total_comments: totalComments })
: isFetchingComments
? ''
: __('No comments')
}
titleActions={
<Button
button="alt"
icon={ICONS.REFRESH}
title={__('Refresh')}
onClick={() => {
setPage(0);
}}
/>
}
body={
<>
{wasResetAndReady && <ul className="comments">{allComments && getCommentsElem(allComments)}</ul>}
{(isFetchingComments || moreBelow) && (
<div className="main--empty" ref={spinnerRef}>
<Spinner type="small" />
</div>
)}
</>
}
/>
</Page>
);
}

View file

@ -3,7 +3,15 @@ import * as ACTIONS from 'constants/action_types';
import * as REACTION_TYPES from 'constants/reactions';
import * as PAGES from 'constants/pages';
import { SORT_BY, BLOCK_LEVEL } from 'constants/comment';
import { Lbry, parseURI, buildURI, selectClaimsByUri, selectMyChannelClaims, isURIEqual } from 'lbry-redux';
import {
Lbry,
parseURI,
buildURI,
selectClaimsByUri,
selectMyChannelClaims,
isURIEqual,
doClaimSearch,
} from 'lbry-redux';
import { doToast, doSeeNotifications } from 'redux/actions/notifications';
import {
makeSelectMyReactionsForComment,
@ -127,7 +135,6 @@ export function doCommentList(
type: ACTIONS.COMMENT_LIST_FAILED,
data: 'unable to find claim for uri',
});
return;
}
@ -139,7 +146,7 @@ export function doCommentList(
});
// Adding 'channel_id' and 'channel_name' enables "CreatorSettings > commentsEnabled".
const authorChannelClaim = claim.value_type === 'channel' ? claim : claim.signing_channel;
const creatorChannelClaim = claim.value_type === 'channel' ? claim : claim.signing_channel;
return Comments.comment_list({
page,
@ -147,8 +154,8 @@ export function doCommentList(
page_size: pageSize,
parent_id: parentId || undefined,
top_level: !parentId,
channel_id: authorChannelClaim ? authorChannelClaim.claim_id : undefined,
channel_name: authorChannelClaim ? authorChannelClaim.name : undefined,
channel_id: creatorChannelClaim ? creatorChannelClaim.claim_id : undefined,
channel_name: creatorChannelClaim ? creatorChannelClaim.name : undefined,
sort_by: sortBy,
})
.then((result: CommentListResponse) => {
@ -162,7 +169,7 @@ export function doCommentList(
totalFilteredItems: total_filtered_items,
totalPages: total_pages,
claimId: claimId,
commenterClaimId: authorChannelClaim ? authorChannelClaim.claim_id : undefined,
creatorClaimId: creatorChannelClaim ? creatorChannelClaim.claim_id : undefined,
uri: uri,
},
});
@ -175,7 +182,7 @@ export function doCommentList(
dispatch({
type: ACTIONS.COMMENT_LIST_COMPLETED,
data: {
authorClaimId: authorChannelClaim ? authorChannelClaim.claim_id : undefined,
creatorClaimId: creatorChannelClaim ? creatorChannelClaim.claim_id : undefined,
disabled: true,
},
});
@ -201,6 +208,103 @@ export function doCommentList(
};
}
export function doCommentListOwn(
channelId: string,
page: number = 1,
pageSize: number = 99999,
sortBy: number = SORT_BY.NEWEST_NO_PINS
) {
return async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const myChannelClaims = selectMyChannelClaims(state);
if (!myChannelClaims) {
console.error('Failed to fetch channel list.'); // eslint-disable-line
return;
}
const channelClaim = myChannelClaims.find((x) => x.claim_id === channelId);
if (!channelClaim) {
console.error('You do not own this channel.'); // eslint-disable-line
return;
}
const channelSignature = await channelSignName(channelClaim.claim_id, channelClaim.name);
if (!channelSignature) {
console.error('Failed to sign channel name.'); // eslint-disable-line
return;
}
dispatch({
type: ACTIONS.COMMENT_LIST_STARTED,
data: {},
});
return Comments.comment_list({
page,
page_size: pageSize,
sort_by: sortBy,
author_claim_id: channelId,
requestor_channel_name: channelClaim.name,
requestor_channel_id: channelClaim.claim_id,
signature: channelSignature.signature,
signing_ts: channelSignature.signing_ts,
})
.then((result: CommentListResponse) => {
const { items: comments, total_items, total_filtered_items, total_pages } = result;
if (!comments) {
dispatch({ type: ACTIONS.COMMENT_LIST_FAILED, data: 'No more comments.' });
return;
}
dispatch(
doClaimSearch({
page: 1,
page_size: 20,
no_totals: true,
claim_ids: comments.map((c) => c.claim_id),
})
)
.then((result) => {
dispatch({
type: ACTIONS.COMMENT_LIST_COMPLETED,
data: {
comments,
totalItems: total_items,
totalFilteredItems: total_filtered_items,
totalPages: total_pages,
uri: channelClaim.canonical_url, // hijack "Discussion Page"
claimId: channelClaim.claim_id, // hijack "Discussion Page"
},
});
})
.catch((err) => {
dispatch({ type: ACTIONS.COMMENT_LIST_FAILED, data: err });
});
})
.catch((error) => {
switch (error.message) {
case FETCH_API_FAILED_TO_FETCH:
dispatch(
doToast({
isError: true,
message: Comments.isCustomServer
? __('Failed to fetch comments. Verify custom server settings.')
: __('Failed to fetch comments.'),
})
);
dispatch(doToast({ isError: true, message: `${error.message}` }));
dispatch({ type: ACTIONS.COMMENT_LIST_FAILED, data: error });
break;
default:
dispatch(doToast({ isError: true, message: `${error.message}` }));
dispatch({ type: ACTIONS.COMMENT_LIST_FAILED, data: error });
}
});
};
}
export function doCommentById(commentId: string, toastIfNotFound: boolean = true) {
return (dispatch: Dispatch, getState: GetState) => {
dispatch({
@ -239,17 +343,10 @@ export function doCommentById(commentId: string, toastIfNotFound: boolean = true
};
}
export function doCommentReset(uri: string) {
return (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const claim = selectClaimsByUri(state)[uri];
const claimId = claim ? claim.claim_id : null;
export function doCommentReset(claimId: string) {
return (dispatch: Dispatch) => {
if (!claimId) {
dispatch({
type: ACTIONS.COMMENT_LIST_FAILED,
data: 'unable to find claim for uri',
});
console.error(`Failed to reset comments`); //eslint-disable-line
return;
}

View file

@ -246,7 +246,7 @@ export default handleActions(
claimId,
uri,
disabled,
commenterClaimId,
creatorClaimId,
} = action.data;
const commentById = Object.assign({}, state.commentById);
@ -262,8 +262,8 @@ export default handleActions(
const isLoadingByParentId = Object.assign({}, state.isLoadingByParentId);
const settingsByChannelId = Object.assign({}, state.settingsByChannelId);
settingsByChannelId[commenterClaimId] = {
...(settingsByChannelId[commenterClaimId] || {}),
settingsByChannelId[creatorClaimId] = {
...(settingsByChannelId[creatorClaimId] || {}),
comments_enabled: !disabled,
};

View file

@ -487,3 +487,60 @@ $thumbnailWidthSmall: 1rem;
margin-bottom: -3px; // TODO fix few instances of these (find "-2px")
}
}
.comments-own {
.section__actions {
align-items: flex-start;
}
.comments-own--claim {
min-width: 100%;
max-width: 100%;
@media (min-width: $breakpoint-medium) {
min-width: 40%;
max-width: 40%;
}
.media__thumb {
flex-shrink: 0;
overflow: hidden;
$width: 5rem;
@include handleClaimListGifThumbnail($width);
width: $width;
height: calc(#{$width} * (9 / 16));
margin-right: var(--spacing-s);
}
.channel-thumbnail {
@include handleChannelGif(calc(5rem * 9 / 16));
margin-right: var(--spacing-xs);
@media (min-width: $breakpoint-small) {
@include handleChannelGif(calc(5rem * 9 / 16));
margin-right: var(--spacing-s);
}
}
}
.claim-preview__wrapper {
margin: 0 0;
padding: 0;
@media (min-width: $breakpoint-medium) {
margin: 0 var(--spacing-xs);
}
}
.comment {
margin-top: var(--spacing-s);
margin-left: 0;
padding-left: var(--spacing-m);
border-left: 4px solid var(--color-border);
@media (min-width: $breakpoint-medium) {
margin-top: 0;
margin-left: var(--spacing-s);
}
}
}