mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-08-31 01:11:26 +00:00
initial comment pin PR with new commit
This commit is contained in:
parent
2f84d94c66
commit
106f5a0ac2
16 changed files with 183 additions and 41 deletions
|
@ -136,7 +136,7 @@
|
||||||
"imagesloaded": "^4.1.4",
|
"imagesloaded": "^4.1.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||||
"lbry-redux": "lbryio/lbry-redux#ba5d6b84bec6bdb2f0a1a6b23e695212c65f650e",
|
"lbry-redux": "lbryio/lbry-redux#16c6ba1a249476889bd8d3888b7b920a5943c421",
|
||||||
"lbryinc": "lbryio/lbryinc#db0663fcc4a64cb082b6edc5798fafa67eb4300f",
|
"lbryinc": "lbryio/lbryinc#db0663fcc4a64cb082b6edc5798fafa67eb4300f",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
|
|
|
@ -1453,6 +1453,9 @@
|
||||||
"lbry.com": "lbry.com",
|
"lbry.com": "lbry.com",
|
||||||
"lbry.tech": "lbry.tech",
|
"lbry.tech": "lbry.tech",
|
||||||
"GitHub": "GitHub",
|
"GitHub": "GitHub",
|
||||||
|
"Pin": "Pin",
|
||||||
|
"Unpin": "Unpin",
|
||||||
|
"LBRY leveled up": "LBRY leveled up",
|
||||||
"This link leads to an external website.": "This link leads to an external website.",
|
"This link leads to an external website.": "This link leads to an external website.",
|
||||||
"--end--": "--end--"
|
"--end--": "--end--"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,26 +6,39 @@ import {
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectIsUriResolving,
|
makeSelectIsUriResolving,
|
||||||
selectMyChannelClaims,
|
selectMyChannelClaims,
|
||||||
|
makeSelectMyChannelPermUrlForName,
|
||||||
|
makeSelectChannelPermUrlForClaimUri,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doCommentAbandon, doCommentUpdate } from 'redux/actions/comments';
|
import { doCommentAbandon, doCommentUpdate, doCommentPin, doCommentList } from 'redux/actions/comments';
|
||||||
import { doToggleBlockChannel } from 'redux/actions/blocked';
|
import { doToggleBlockChannel } from 'redux/actions/blocked';
|
||||||
import { selectChannelIsBlocked } from 'redux/selectors/blocked';
|
import { selectChannelIsBlocked } from 'redux/selectors/blocked';
|
||||||
import { doToast } from 'redux/actions/notifications';
|
import { doToast } from 'redux/actions/notifications';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { selectIsFetchingComments, makeSelectOthersReactionsForComment } from 'redux/selectors/comments';
|
import {
|
||||||
|
selectIsFetchingComments,
|
||||||
|
makeSelectOthersReactionsForComment,
|
||||||
|
selectCommentChannel,
|
||||||
|
} from 'redux/selectors/comments';
|
||||||
import Comment from './view';
|
import Comment from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => {
|
||||||
pending: props.authorUri && makeSelectClaimIsPending(props.authorUri)(state),
|
const channel = selectCommentChannel(state);
|
||||||
channel: props.authorUri && makeSelectClaimForUri(props.authorUri)(state),
|
|
||||||
isResolvingUri: props.authorUri && makeSelectIsUriResolving(props.authorUri)(state),
|
return {
|
||||||
thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state),
|
activeChannel: channel,
|
||||||
channelIsBlocked: props.authorUri && selectChannelIsBlocked(props.authorUri)(state),
|
pending: props.authorUri && makeSelectClaimIsPending(props.authorUri)(state),
|
||||||
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
|
channel: props.authorUri && makeSelectClaimForUri(props.authorUri)(state),
|
||||||
isFetchingComments: selectIsFetchingComments(state),
|
isResolvingUri: props.authorUri && makeSelectIsUriResolving(props.authorUri)(state),
|
||||||
myChannels: selectMyChannelClaims(state),
|
thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state),
|
||||||
othersReacts: makeSelectOthersReactionsForComment(props.commentId)(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),
|
||||||
|
commentIdentityChannel: makeSelectMyChannelPermUrlForName(channel)(state),
|
||||||
|
contentChannel: makeSelectChannelPermUrlForClaimUri(props.uri)(state),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
|
@ -33,6 +46,8 @@ const perform = dispatch => ({
|
||||||
deleteComment: commentId => dispatch(doCommentAbandon(commentId)),
|
deleteComment: commentId => dispatch(doCommentAbandon(commentId)),
|
||||||
blockChannel: channelUri => dispatch(doToggleBlockChannel(channelUri)),
|
blockChannel: channelUri => dispatch(doToggleBlockChannel(channelUri)),
|
||||||
doToast: options => dispatch(doToast(options)),
|
doToast: options => dispatch(doToast(options)),
|
||||||
|
pinComment: (commentId, remove) => dispatch(doCommentPin(commentId, remove)),
|
||||||
|
fetchComments: uri => dispatch(doCommentList(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(Comment);
|
export default connect(select, perform)(Comment);
|
||||||
|
|
|
@ -49,6 +49,10 @@ type Props = {
|
||||||
like: number,
|
like: number,
|
||||||
dislike: number,
|
dislike: number,
|
||||||
},
|
},
|
||||||
|
pinComment: (string, boolean) => Promise<any>,
|
||||||
|
fetchComments: string => void,
|
||||||
|
commentIdentityChannel: any,
|
||||||
|
contentChannel: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
const LENGTH_TO_COLLAPSE = 300;
|
const LENGTH_TO_COLLAPSE = 300;
|
||||||
|
@ -78,7 +82,11 @@ function Comment(props: Props) {
|
||||||
isTopLevel,
|
isTopLevel,
|
||||||
threadDepth,
|
threadDepth,
|
||||||
isPinned,
|
isPinned,
|
||||||
|
pinComment,
|
||||||
|
fetchComments,
|
||||||
othersReacts,
|
othersReacts,
|
||||||
|
commentIdentityChannel,
|
||||||
|
contentChannel,
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
push,
|
push,
|
||||||
|
@ -139,6 +147,10 @@ function Comment(props: Props) {
|
||||||
setCommentValue(!SIMPLE_SITE && advancedEditor ? event : event.target.value);
|
setCommentValue(!SIMPLE_SITE && advancedEditor ? event : event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePinComment(commentId, remove) {
|
||||||
|
pinComment(commentId, remove).then(() => fetchComments(uri));
|
||||||
|
}
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
updateComment(commentId, editedMessage);
|
updateComment(commentId, editedMessage);
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
|
@ -204,7 +216,7 @@ function Comment(props: Props) {
|
||||||
|
|
||||||
{isPinned && (
|
{isPinned && (
|
||||||
<span className="comment__pin">
|
<span className="comment__pin">
|
||||||
<Icon icon={ICONS.PIN} />
|
<Icon icon={ICONS.PIN} size={14} />
|
||||||
{channelOwnerOfContent
|
{channelOwnerOfContent
|
||||||
? __('Pinned by @%channel%', { channel: channelOwnerOfContent })
|
? __('Pinned by @%channel%', { channel: channelOwnerOfContent })
|
||||||
: __('Pinned by creator')}
|
: __('Pinned by creator')}
|
||||||
|
@ -223,18 +235,34 @@ function Comment(props: Props) {
|
||||||
<MenuList className="menu__list--comments">
|
<MenuList className="menu__list--comments">
|
||||||
{commentIsMine ? (
|
{commentIsMine ? (
|
||||||
<>
|
<>
|
||||||
<MenuItem className="comment__menu-option" onSelect={() => setEditing(true)}>
|
<MenuItem className="comment__menu-option menu__link" onSelect={() => setEditing(true)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.EDIT} />
|
||||||
{__('Edit')}
|
{__('Edit')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem className="comment__menu-option" onSelect={() => deleteComment(commentId)}>
|
<MenuItem className="comment__menu-option menu__link" onSelect={() => deleteComment(commentId)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.DELETE} />
|
||||||
{__('Delete')}
|
{__('Delete')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<MenuItem className="comment__menu-option" onSelect={() => blockChannel(authorUri)}>
|
<MenuItem className="comment__menu-option menu__link" onSelect={() => blockChannel(authorUri)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.NO} />
|
||||||
{__('Block Channel')}
|
{__('Block Channel')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
{commentIdentityChannel === contentChannel && (
|
||||||
|
<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>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri, selectMyChannelClaims, selectFetchingMyChannels } from 'lbry-redux';
|
import { makeSelectClaimForUri, selectMyChannelClaims, selectFetchingMyChannels } from 'lbry-redux';
|
||||||
import { selectIsPostingComment } from 'redux/selectors/comments';
|
import { selectIsPostingComment, selectCommentChannel } from 'redux/selectors/comments';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import { doCommentCreate } from 'redux/actions/comments';
|
import { doCommentCreate, doSetCommentChannel } from 'redux/actions/comments';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { CommentCreate } from './view';
|
import { CommentCreate } from './view';
|
||||||
|
|
||||||
|
@ -12,12 +12,14 @@ const select = (state, props) => ({
|
||||||
channels: selectMyChannelClaims(state),
|
channels: selectMyChannelClaims(state),
|
||||||
isFetchingChannels: selectFetchingMyChannels(state),
|
isFetchingChannels: selectFetchingMyChannels(state),
|
||||||
isPostingComment: selectIsPostingComment(state),
|
isPostingComment: selectIsPostingComment(state),
|
||||||
|
activeChannel: selectCommentChannel(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = (dispatch, ownProps) => ({
|
const perform = (dispatch, ownProps) => ({
|
||||||
createComment: (comment, claimId, channel, parentId) =>
|
createComment: (comment, claimId, channel, parentId) =>
|
||||||
dispatch(doCommentCreate(comment, claimId, channel, parentId, ownProps.uri)),
|
dispatch(doCommentCreate(comment, claimId, channel, parentId, ownProps.uri)),
|
||||||
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||||
|
setCommentChannel: name => dispatch(doSetCommentChannel(name)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(CommentCreate);
|
export default connect(select, perform)(CommentCreate);
|
||||||
|
|
|
@ -24,6 +24,8 @@ type Props = {
|
||||||
parentId: string,
|
parentId: string,
|
||||||
isReply: boolean,
|
isReply: boolean,
|
||||||
isPostingComment: boolean,
|
isPostingComment: boolean,
|
||||||
|
activeChannel: string,
|
||||||
|
setCommentChannel: string => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CommentCreate(props: Props) {
|
export function CommentCreate(props: Props) {
|
||||||
|
@ -38,30 +40,32 @@ export function CommentCreate(props: Props) {
|
||||||
isReply,
|
isReply,
|
||||||
parentId,
|
parentId,
|
||||||
isPostingComment,
|
isPostingComment,
|
||||||
|
activeChannel,
|
||||||
|
setCommentChannel,
|
||||||
} = props;
|
} = props;
|
||||||
const buttonref: ElementRef<any> = React.useRef();
|
const buttonref: ElementRef<any> = React.useRef();
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const { claim_id: claimId } = claim;
|
const { claim_id: claimId } = claim;
|
||||||
const [commentValue, setCommentValue] = React.useState('');
|
const [commentValue, setCommentValue] = React.useState('');
|
||||||
const [channel, setChannel] = usePersistedState('comment-channel', '');
|
// const [activeChannel, setCommentChannel] = usePersistedState('comment-channel', '');
|
||||||
const [charCount, setCharCount] = useState(commentValue.length);
|
const [charCount, setCharCount] = useState(commentValue.length);
|
||||||
const [advancedEditor, setAdvancedEditor] = usePersistedState('comment-editor-mode', false);
|
const [advancedEditor, setAdvancedEditor] = usePersistedState('comment-editor-mode', false);
|
||||||
const hasChannels = channels && channels.length;
|
const hasChannels = channels && channels.length;
|
||||||
const disabled = isPostingComment || channel === CHANNEL_NEW || !commentValue.length;
|
const disabled = isPostingComment || activeChannel === CHANNEL_NEW || !commentValue.length;
|
||||||
const topChannel =
|
const topChannel =
|
||||||
channels &&
|
channels &&
|
||||||
channels.reduce((top, channel) => {
|
channels.reduce((top, channel) => {
|
||||||
const topClaimCount = (top && top.meta && top.meta.claims_in_channel) || 0;
|
const topClaimCount = (top && top.meta && top.meta.claims_in_channel) || 0;
|
||||||
const currentClaimCount = (channel && channel.meta && channel.meta.claims_in_channel) || 0;
|
const currentClaimCount = (activeChannel && channel.meta && channel.meta.claims_in_channel) || 0;
|
||||||
return topClaimCount >= currentClaimCount ? top : channel;
|
return topClaimCount >= currentClaimCount ? top : channel;
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// set default channel
|
// set default channel
|
||||||
if ((channel === '' || channel === 'anonymous') && topChannel) {
|
if ((activeChannel === '' || activeChannel === 'anonymous') && topChannel) {
|
||||||
setChannel(topChannel.name);
|
setCommentChannel(topChannel.name);
|
||||||
}
|
}
|
||||||
}, [channel, topChannel, setChannel]);
|
}, [activeChannel, topChannel, setCommentChannel]);
|
||||||
|
|
||||||
function handleCommentChange(event) {
|
function handleCommentChange(event) {
|
||||||
let commentValue;
|
let commentValue;
|
||||||
|
@ -91,8 +95,8 @@ export function CommentCreate(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
if (channel !== CHANNEL_NEW && commentValue.length) {
|
if (activeChannel !== CHANNEL_NEW && commentValue.length) {
|
||||||
createComment(commentValue, claimId, channel, parentId).then(res => {
|
createComment(commentValue, claimId, activeChannel, parentId).then(res => {
|
||||||
if (res && res.signature) {
|
if (res && res.signature) {
|
||||||
setCommentValue('');
|
setCommentValue('');
|
||||||
|
|
||||||
|
@ -135,13 +139,13 @@ export function CommentCreate(props: Props) {
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
disabled={channel === CHANNEL_NEW}
|
disabled={activeChannel === CHANNEL_NEW}
|
||||||
type={SIMPLE_SITE ? 'textarea' : advancedEditor && !isReply ? 'markdown' : 'textarea'}
|
type={SIMPLE_SITE ? 'textarea' : advancedEditor && !isReply ? 'markdown' : 'textarea'}
|
||||||
name={isReply ? 'content_reply' : 'content_description'}
|
name={isReply ? 'content_reply' : 'content_description'}
|
||||||
label={
|
label={
|
||||||
<span className="comment-new__label-wrapper">
|
<span className="comment-new__label-wrapper">
|
||||||
<div className="comment-new__label">{isReply ? __('Replying as') + ' ' : __('Comment as') + ' '}</div>
|
<div className="comment-new__label">{isReply ? __('Replying as') + ' ' : __('Comment as') + ' '}</div>
|
||||||
<ChannelSelection channel={channel} hideAnon tiny hideNew onChannelChange={setChannel} />
|
<ChannelSelection channel={activeChannel} hideAnon tiny hideNew onChannelChange={setCommentChannel} />
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
quickActionLabel={
|
quickActionLabel={
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Comment from './view';
|
import Comment from './view';
|
||||||
import { makeSelectMyReactionsForComment, makeSelectOthersReactionsForComment } from 'redux/selectors/comments';
|
import {
|
||||||
|
makeSelectMyReactionsForComment,
|
||||||
|
makeSelectOthersReactionsForComment,
|
||||||
|
selectCommentChannel,
|
||||||
|
} from 'redux/selectors/comments';
|
||||||
import { doCommentReact } from 'redux/actions/comments';
|
import { doCommentReact } from 'redux/actions/comments';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
myReacts: makeSelectMyReactionsForComment(props.commentId)(state),
|
myReacts: makeSelectMyReactionsForComment(props.commentId)(state),
|
||||||
othersReacts: makeSelectOthersReactionsForComment(props.commentId)(state),
|
othersReacts: makeSelectOthersReactionsForComment(props.commentId)(state),
|
||||||
|
activeChannel: selectCommentChannel(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
selectIsFetchingComments,
|
selectIsFetchingComments,
|
||||||
makeSelectTotalCommentsCountForUri,
|
makeSelectTotalCommentsCountForUri,
|
||||||
selectOthersReactsById,
|
selectOthersReactsById,
|
||||||
|
selectCommentChannel,
|
||||||
} from 'redux/selectors/comments';
|
} from 'redux/selectors/comments';
|
||||||
import { doCommentList, doCommentReactList } from 'redux/actions/comments';
|
import { doCommentList, doCommentReactList } from 'redux/actions/comments';
|
||||||
import CommentsList from './view';
|
import CommentsList from './view';
|
||||||
|
@ -19,6 +20,7 @@ const select = (state, props) => ({
|
||||||
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
|
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
|
||||||
fetchingChannels: selectFetchingMyChannels(state),
|
fetchingChannels: selectFetchingMyChannels(state),
|
||||||
reactionsById: selectOthersReactsById(state),
|
reactionsById: selectOthersReactsById(state),
|
||||||
|
activeChannel: selectCommentChannel(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -25,6 +25,7 @@ type Props = {
|
||||||
totalComments: number,
|
totalComments: number,
|
||||||
fetchingChannels: boolean,
|
fetchingChannels: boolean,
|
||||||
reactionsById: { [string]: { [REACTION_TYPES.LIKE | REACTION_TYPES.DISLIKE]: number } },
|
reactionsById: { [string]: { [REACTION_TYPES.LIKE | REACTION_TYPES.DISLIKE]: number } },
|
||||||
|
activeChannel: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
function CommentList(props: Props) {
|
function CommentList(props: Props) {
|
||||||
|
@ -40,11 +41,14 @@ function CommentList(props: Props) {
|
||||||
totalComments,
|
totalComments,
|
||||||
fetchingChannels,
|
fetchingChannels,
|
||||||
reactionsById,
|
reactionsById,
|
||||||
|
activeChannel,
|
||||||
} = props;
|
} = props;
|
||||||
const commentRef = React.useRef();
|
const commentRef = React.useRef();
|
||||||
const spinnerRef = React.useRef();
|
const spinnerRef = React.useRef();
|
||||||
const [sort, setSort] = usePersistedState('comment-sort', SORT_COMMENTS_BEST);
|
const [sort, setSort] = usePersistedState(
|
||||||
const [activeChannel] = usePersistedState('comment-channel', '');
|
'comment-sort',
|
||||||
|
ENABLE_COMMENT_REACTIONS ? SORT_COMMENTS_BEST : SORT_COMMENTS_NEW
|
||||||
|
);
|
||||||
const [start] = React.useState(0);
|
const [start] = React.useState(0);
|
||||||
const [end, setEnd] = React.useState(9);
|
const [end, setEnd] = React.useState(9);
|
||||||
// Display comments immediately if not fetching reactions
|
// Display comments immediately if not fetching reactions
|
||||||
|
@ -138,9 +142,8 @@ function CommentList(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to newest first for apps that don't have comment reactions
|
// Default to newest first for apps that don't have comment reactions
|
||||||
const sortedComments = ENABLE_COMMENT_REACTIONS
|
const sortedComments = sortComments({ comments, reactionsById, sort, isMyComment });
|
||||||
? sortComments({ comments, reactionsById, sort, isMyComment })
|
|
||||||
: comments;
|
|
||||||
const displayedComments = readyToDisplayComments
|
const displayedComments = readyToDisplayComments
|
||||||
? prepareComments(sortedComments, linkedComment).slice(start, end)
|
? prepareComments(sortedComments, linkedComment).slice(start, end)
|
||||||
: [];
|
: [];
|
||||||
|
|
|
@ -271,6 +271,10 @@ export const COMMENT_REACTION_LIST_FAILED = 'COMMENT_REACTION_LIST_FAILED';
|
||||||
export const COMMENT_REACT_STARTED = 'COMMENT_REACT_STARTED';
|
export const COMMENT_REACT_STARTED = 'COMMENT_REACT_STARTED';
|
||||||
export const COMMENT_REACT_COMPLETED = 'COMMENT_REACT_COMPLETED';
|
export const COMMENT_REACT_COMPLETED = 'COMMENT_REACT_COMPLETED';
|
||||||
export const COMMENT_REACT_FAILED = 'COMMENT_REACT_FAILED';
|
export const COMMENT_REACT_FAILED = 'COMMENT_REACT_FAILED';
|
||||||
|
export const COMMENT_PIN_STARTED = 'COMMENT_PIN_STARTED';
|
||||||
|
export const COMMENT_PIN_COMPLETED = 'COMMENT_PIN_COMPLETED';
|
||||||
|
export const COMMENT_PIN_FAILED = 'COMMENT_PIN_FAILED';
|
||||||
|
export const COMMENT_SET_CHANNEL = 'COMMENT_SET_CHANNEL';
|
||||||
|
|
||||||
// Blocked channels
|
// Blocked channels
|
||||||
export const TOGGLE_BLOCK_CHANNEL = 'TOGGLE_BLOCK_CHANNEL';
|
export const TOGGLE_BLOCK_CHANNEL = 'TOGGLE_BLOCK_CHANNEL';
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
makeSelectMyReactionsForComment,
|
makeSelectMyReactionsForComment,
|
||||||
makeSelectOthersReactionsForComment,
|
makeSelectOthersReactionsForComment,
|
||||||
selectPendingCommentReacts,
|
selectPendingCommentReacts,
|
||||||
|
selectCommentChannel,
|
||||||
} from 'redux/selectors/comments';
|
} from 'redux/selectors/comments';
|
||||||
|
|
||||||
export function doCommentList(uri: string, page: number = 1, pageSize: number = 99999) {
|
export function doCommentList(uri: string, page: number = 1, pageSize: number = 99999) {
|
||||||
|
@ -19,7 +20,7 @@ export function doCommentList(uri: string, page: number = 1, pageSize: number =
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.COMMENT_LIST_STARTED,
|
type: ACTIONS.COMMENT_LIST_STARTED,
|
||||||
});
|
});
|
||||||
Lbry.comment_list({
|
return Lbry.comment_list({
|
||||||
claim_id: claimId,
|
claim_id: claimId,
|
||||||
page,
|
page,
|
||||||
page_size: pageSize,
|
page_size: pageSize,
|
||||||
|
@ -36,6 +37,7 @@ export function doCommentList(uri: string, page: number = 1, pageSize: number =
|
||||||
uri: uri,
|
uri: uri,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
return result;
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -46,10 +48,19 @@ export function doCommentList(uri: string, page: number = 1, pageSize: number =
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doSetCommentChannel(channelName: string) {
|
||||||
|
return (dispatch: Dispatch) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_SET_CHANNEL,
|
||||||
|
data: channelName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doCommentReactList(uri: string | null, commentId?: string) {
|
export function doCommentReactList(uri: string | null, commentId?: string) {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const channel = localStorage.getItem('comment-channel');
|
const channel = selectCommentChannel(state);
|
||||||
const commentIds = uri ? makeSelectCommentIdsForUri(uri)(state) : [commentId];
|
const commentIds = uri ? makeSelectCommentIdsForUri(uri)(state) : [commentId];
|
||||||
const myChannels = selectMyChannelClaims(state);
|
const myChannels = selectMyChannelClaims(state);
|
||||||
|
|
||||||
|
@ -90,7 +101,7 @@ export function doCommentReactList(uri: string | null, commentId?: string) {
|
||||||
export function doCommentReact(commentId: string, type: string) {
|
export function doCommentReact(commentId: string, type: string) {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const channel = localStorage.getItem('comment-channel');
|
const channel = selectCommentChannel(state);
|
||||||
const pendingReacts = selectPendingCommentReacts(state);
|
const pendingReacts = selectPendingCommentReacts(state);
|
||||||
const myChannels = selectMyChannelClaims(state);
|
const myChannels = selectMyChannelClaims(state);
|
||||||
const exclusiveTypes = {
|
const exclusiveTypes = {
|
||||||
|
@ -273,6 +284,56 @@ export function doCommentHide(comment_id: string) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doCommentPin(commentId: string, remove: boolean) {
|
||||||
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
const state = getState();
|
||||||
|
// const channel = localStorage.getItem('comment-channel');
|
||||||
|
const channel = selectCommentChannel(state);
|
||||||
|
const myChannels = selectMyChannelClaims(state);
|
||||||
|
const claimForChannelName = myChannels && myChannels.find(chan => chan.name === channel);
|
||||||
|
const channelId = claimForChannelName && claimForChannelName.claim_id;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_PIN_STARTED,
|
||||||
|
});
|
||||||
|
if (!channelId || !channel || !commentId) {
|
||||||
|
return dispatch({
|
||||||
|
type: ACTIONS.COMMENT_PIN_FAILED,
|
||||||
|
data: { message: 'missing params - unable to pin' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const params: { comment_id: string, channel_name: string, channel_id: string, remove?: boolean } = {
|
||||||
|
comment_id: commentId,
|
||||||
|
channel_name: channel,
|
||||||
|
channel_id: channelId,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (remove) {
|
||||||
|
params['remove'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbry.comment_pin(params)
|
||||||
|
.then((result: CommentPinResponse) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_PIN_COMPLETED,
|
||||||
|
data: result,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_PIN_FAILED,
|
||||||
|
data: error,
|
||||||
|
});
|
||||||
|
dispatch(
|
||||||
|
doToast({
|
||||||
|
message: 'Unable to pin this comment, please try again later.',
|
||||||
|
isError: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doCommentAbandon(comment_id: string) {
|
export function doCommentAbandon(comment_id: string) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|
|
@ -16,6 +16,7 @@ const defaultState: CommentsState = {
|
||||||
typesReacting: [],
|
typesReacting: [],
|
||||||
myReactsByCommentId: {},
|
myReactsByCommentId: {},
|
||||||
othersReactsByCommentId: {},
|
othersReactsByCommentId: {},
|
||||||
|
commentChannel: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions(
|
export default handleActions(
|
||||||
|
@ -30,6 +31,11 @@ export default handleActions(
|
||||||
isCommenting: false,
|
isCommenting: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_SET_CHANNEL]: (state: CommentsState, action: any) => ({
|
||||||
|
...state,
|
||||||
|
commentChannel: action.data,
|
||||||
|
}),
|
||||||
|
|
||||||
[ACTIONS.COMMENT_CREATE_COMPLETED]: (state: CommentsState, action: any): CommentsState => {
|
[ACTIONS.COMMENT_CREATE_COMPLETED]: (state: CommentsState, action: any): CommentsState => {
|
||||||
const { comment, claimId, uri }: { comment: Comment, claimId: string, uri: string } = action.data;
|
const { comment, claimId, uri }: { comment: Comment, claimId: string, uri: string } = action.data;
|
||||||
const commentById = Object.assign({}, state.commentById);
|
const commentById = Object.assign({}, state.commentById);
|
||||||
|
|
|
@ -16,6 +16,8 @@ export const selectIsPostingComment = createSelector(selectState, state => state
|
||||||
|
|
||||||
export const selectIsFetchingReacts = createSelector(selectState, state => state.isFetchingReacts);
|
export const selectIsFetchingReacts = createSelector(selectState, state => state.isFetchingReacts);
|
||||||
|
|
||||||
|
export const selectCommentChannel = createSelector(selectState, state => state.commentChannel);
|
||||||
|
|
||||||
export const selectOthersReactsById = createSelector(selectState, state => state.othersReactsByCommentId);
|
export const selectOthersReactsById = createSelector(selectState, state => state.othersReactsByCommentId);
|
||||||
|
|
||||||
export const selectCommentsByClaimId = createSelector(selectState, selectCommentsById, (state, byId) => {
|
export const selectCommentsByClaimId = createSelector(selectState, selectCommentsById, (state, byId) => {
|
||||||
|
|
|
@ -170,6 +170,10 @@ $thumbnailWidthSmall: 0rem;
|
||||||
|
|
||||||
.comment__pin {
|
.comment__pin {
|
||||||
margin-left: var(--spacing-s);
|
margin-left: var(--spacing-s);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
padding-top: 1px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment__message {
|
.comment__message {
|
||||||
|
|
|
@ -60,12 +60,14 @@ const appFilter = createFilter('app', [
|
||||||
]);
|
]);
|
||||||
// We only need to persist the receiveAddress for the wallet
|
// We only need to persist the receiveAddress for the wallet
|
||||||
const walletFilter = createFilter('wallet', ['receiveAddress']);
|
const walletFilter = createFilter('wallet', ['receiveAddress']);
|
||||||
|
const commentsFilter = createFilter('comments', ['commentChannel']);
|
||||||
const searchFilter = createFilter('search', ['options']);
|
const searchFilter = createFilter('search', ['options']);
|
||||||
const tagsFilter = createFilter('tags', ['followedTags']);
|
const tagsFilter = createFilter('tags', ['followedTags']);
|
||||||
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
|
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
|
||||||
const blockedFilter = createFilter('blocked', ['blockedChannels']);
|
const blockedFilter = createFilter('blocked', ['blockedChannels']);
|
||||||
const settingsFilter = createBlacklistFilter('settings', ['loadedLanguages', 'language']);
|
const settingsFilter = createBlacklistFilter('settings', ['loadedLanguages', 'language']);
|
||||||
const whiteListedReducers = [
|
const whiteListedReducers = [
|
||||||
|
'comments',
|
||||||
'fileInfo',
|
'fileInfo',
|
||||||
'publish',
|
'publish',
|
||||||
'wallet',
|
'wallet',
|
||||||
|
@ -79,6 +81,7 @@ const whiteListedReducers = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const transforms = [
|
const transforms = [
|
||||||
|
commentsFilter,
|
||||||
fileInfoFilter,
|
fileInfoFilter,
|
||||||
walletFilter,
|
walletFilter,
|
||||||
blockedFilter,
|
blockedFilter,
|
||||||
|
|
|
@ -7391,9 +7391,9 @@ lazy-val@^1.0.4:
|
||||||
yargs "^13.2.2"
|
yargs "^13.2.2"
|
||||||
zstd-codec "^0.1.1"
|
zstd-codec "^0.1.1"
|
||||||
|
|
||||||
lbry-redux@lbryio/lbry-redux#ba5d6b84bec6bdb2f0a1a6b23e695212c65f650e:
|
lbry-redux@lbryio/lbry-redux#16c6ba1a249476889bd8d3888b7b920a5943c421:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/ba5d6b84bec6bdb2f0a1a6b23e695212c65f650e"
|
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/16c6ba1a249476889bd8d3888b7b920a5943c421"
|
||||||
dependencies:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
Loading…
Add table
Reference in a new issue