add style for comments with is_pinned=true
This commit is contained in:
parent
d808d91e72
commit
761b93d811
7 changed files with 66 additions and 6 deletions
1
flow-typed/Comment.js
vendored
1
flow-typed/Comment.js
vendored
|
@ -11,6 +11,7 @@ declare type Comment = {
|
||||||
signing_ts?: string, // timestamp used when signing this comment
|
signing_ts?: string, // timestamp used when signing this comment
|
||||||
is_channel_signature_valid?: boolean, // whether or not the signature could be validated
|
is_channel_signature_valid?: boolean, // whether or not the signature could be validated
|
||||||
parent_id?: number, // comment_id of comment this is in reply to
|
parent_id?: number, // comment_id of comment this is in reply to
|
||||||
|
is_pinned: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo: relate individual comments to their commentId
|
// todo: relate individual comments to their commentId
|
||||||
|
|
|
@ -4,6 +4,7 @@ import * as PAGES from 'constants/pages';
|
||||||
import { FF_MAX_CHARS_IN_COMMENT } from 'constants/form-field';
|
import { FF_MAX_CHARS_IN_COMMENT } from 'constants/form-field';
|
||||||
import { SITE_NAME, SIMPLE_SITE, ENABLE_COMMENT_REACTIONS } from 'config';
|
import { SITE_NAME, SIMPLE_SITE, ENABLE_COMMENT_REACTIONS } from 'config';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { parseURI } from 'lbry-redux';
|
||||||
import { isEmpty } from 'util/object';
|
import { isEmpty } from 'util/object';
|
||||||
import DateTime from 'component/dateTime';
|
import DateTime from 'component/dateTime';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
@ -43,6 +44,7 @@ type Props = {
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
isTopLevel?: boolean,
|
isTopLevel?: boolean,
|
||||||
threadDepth: number,
|
threadDepth: number,
|
||||||
|
isPinned: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const LENGTH_TO_COLLAPSE = 300;
|
const LENGTH_TO_COLLAPSE = 300;
|
||||||
|
@ -71,10 +73,12 @@ function Comment(props: Props) {
|
||||||
doToast,
|
doToast,
|
||||||
isTopLevel,
|
isTopLevel,
|
||||||
threadDepth,
|
threadDepth,
|
||||||
|
isPinned,
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
push,
|
push,
|
||||||
location: { pathname },
|
replace,
|
||||||
|
location: { pathname, search },
|
||||||
} = useHistory();
|
} = useHistory();
|
||||||
const [isReplying, setReplying] = React.useState(false);
|
const [isReplying, setReplying] = React.useState(false);
|
||||||
const [isEditing, setEditing] = useState(false);
|
const [isEditing, setEditing] = useState(false);
|
||||||
|
@ -84,11 +88,17 @@ function Comment(props: Props) {
|
||||||
const [mouseIsHovering, setMouseHover] = useState(false);
|
const [mouseIsHovering, setMouseHover] = useState(false);
|
||||||
const [advancedEditor] = usePersistedState('comment-editor-mode', false);
|
const [advancedEditor] = usePersistedState('comment-editor-mode', false);
|
||||||
const hasChannels = myChannels && myChannels.length > 0;
|
const hasChannels = myChannels && myChannels.length > 0;
|
||||||
|
|
||||||
// to debounce subsequent requests
|
// to debounce subsequent requests
|
||||||
const shouldFetch =
|
const shouldFetch =
|
||||||
channel === undefined ||
|
channel === undefined ||
|
||||||
(channel !== null && channel.value_type === 'channel' && isEmpty(channel.meta) && !pending);
|
(channel !== null && channel.value_type === 'channel' && isEmpty(channel.meta) && !pending);
|
||||||
|
let channelOwnerOfContent;
|
||||||
|
try {
|
||||||
|
const { channelName } = parseURI(uri);
|
||||||
|
if (channelName) {
|
||||||
|
channelOwnerOfContent = channelName;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If author was extracted from the URI, then it must be valid.
|
// If author was extracted from the URI, then it must be valid.
|
||||||
|
@ -133,18 +143,28 @@ function Comment(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTimeClick() {
|
||||||
|
const urlParams = new URLSearchParams(search);
|
||||||
|
urlParams.delete('lc');
|
||||||
|
urlParams.append('lc', commentId);
|
||||||
|
replace(`${pathname}?${urlParams.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className={classnames('comment', {
|
className={classnames('comment', {
|
||||||
'comment--top-level': isTopLevel,
|
'comment--top-level': isTopLevel,
|
||||||
'comment--reply': !isTopLevel,
|
'comment--reply': !isTopLevel,
|
||||||
'comment--highlighted': linkedComment && linkedComment.comment_id === commentId,
|
|
||||||
})}
|
})}
|
||||||
id={commentId}
|
id={commentId}
|
||||||
onMouseOver={() => setMouseHover(true)}
|
onMouseOver={() => setMouseHover(true)}
|
||||||
onMouseOut={() => setMouseHover(false)}
|
onMouseOut={() => setMouseHover(false)}
|
||||||
>
|
>
|
||||||
<div className="comment__content">
|
<div
|
||||||
|
className={classnames('comment__content', {
|
||||||
|
'comment--highlighted': linkedComment && linkedComment.comment_id === commentId,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div className="comment__author-thumbnail">
|
<div className="comment__author-thumbnail">
|
||||||
{authorUri ? (
|
{authorUri ? (
|
||||||
<ChannelThumbnail uri={authorUri} obscure={channelIsBlocked} small />
|
<ChannelThumbnail uri={authorUri} obscure={channelIsBlocked} small />
|
||||||
|
@ -167,9 +187,18 @@ function Comment(props: Props) {
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
className="comment__time"
|
className="comment__time"
|
||||||
navigate={`${uri}?lc=${commentId}`}
|
onClick={handleTimeClick}
|
||||||
label={<DateTime date={timePosted} timeAgo />}
|
label={<DateTime date={timePosted} timeAgo />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{isPinned && (
|
||||||
|
<span className="comment__pin">
|
||||||
|
<Icon icon={ICONS.PIN} />
|
||||||
|
{channelOwnerOfContent
|
||||||
|
? __('Pinned by @%channel%', { channel: channelOwnerOfContent })
|
||||||
|
: __('Pinned by creator')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="comment__menu">
|
<div className="comment__menu">
|
||||||
<Menu>
|
<Menu>
|
||||||
|
|
|
@ -216,6 +216,7 @@ function CommentList(props: Props) {
|
||||||
claimIsMine={claimIsMine}
|
claimIsMine={claimIsMine}
|
||||||
commentIsMine={comment.channel_id && isMyComment(comment.channel_id)}
|
commentIsMine={comment.channel_id && isMyComment(comment.channel_id)}
|
||||||
linkedComment={linkedComment}
|
linkedComment={linkedComment}
|
||||||
|
isPinned={comment.is_pinned}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -892,4 +892,22 @@ export const icons = {
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
[ICONS.PIN]: (props: CustomProps) => (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="-6 -5 26 24"
|
||||||
|
width={props.size || '18'}
|
||||||
|
height={props.size || '18'}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M11.3333333,6 C11.3333333,8.94666667 8.94666667,11.3333333 6,11.3333333 C3.056,11.3333333 0.666666667,8.94666667 0.666666667,6 C0.666666667,3.05466667 3.056,0.666666667 6,0.666666667 C8.94666667,0.666666667 11.3333333,3.05466667 11.3333333,6 L11.3333333,6 Z" />
|
||||||
|
<line x1="6" y1="11.3333333" x2="6" y2="23.3333333" />
|
||||||
|
<path d="M6,3.33333333 C7.47333333,3.33333333 8.66666667,4.528 8.66666667,6" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
|
@ -124,3 +124,4 @@ export const FIRE_ACTIVE = 'FireActive';
|
||||||
export const SLIME_ACTIVE = 'SlimeActive';
|
export const SLIME_ACTIVE = 'SlimeActive';
|
||||||
export const FIRE = 'Fire';
|
export const FIRE = 'Fire';
|
||||||
export const SLIME = 'Slime';
|
export const SLIME = 'Slime';
|
||||||
|
export const PIN = 'Pin';
|
||||||
|
|
|
@ -127,7 +127,7 @@ $thumbnailWidthSmall: 0rem;
|
||||||
|
|
||||||
.comment--highlighted {
|
.comment--highlighted {
|
||||||
background: var(--color-comment-highlighted);
|
background: var(--color-comment-highlighted);
|
||||||
box-shadow: 0 0 0 var(--spacing-xs) var(--color-comment-highlighted);
|
box-shadow: 0 0 0 5px var(--color-comment-highlighted);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,10 @@ $thumbnailWidthSmall: 0rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment__pin {
|
||||||
|
margin-left: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
.comment__message {
|
.comment__message {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
max-width: 35rem;
|
max-width: 35rem;
|
||||||
|
|
|
@ -20,6 +20,12 @@ export function sortComments(sortProps: SortProps): Array<Comment> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return comments.slice().sort((a: Comment, b: Comment) => {
|
return comments.slice().sort((a: Comment, b: Comment) => {
|
||||||
|
if (a.is_pinned) {
|
||||||
|
return -1;
|
||||||
|
} else if (b.is_pinned) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (sort === SORT_COMMENTS_NEW) {
|
if (sort === SORT_COMMENTS_NEW) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue