use comment component for livestream comments
This commit is contained in:
parent
d8a5ca082b
commit
64e8c8e095
7 changed files with 100 additions and 76 deletions
|
@ -50,6 +50,7 @@ type Props = {
|
|||
activeChannelClaim: ?ChannelClaim,
|
||||
playingUri: ?PlayingUri,
|
||||
stakedLevel: number,
|
||||
livestream?: boolean,
|
||||
};
|
||||
|
||||
const LENGTH_TO_COLLAPSE = 300;
|
||||
|
@ -77,6 +78,7 @@ function Comment(props: Props) {
|
|||
othersReacts,
|
||||
playingUri,
|
||||
stakedLevel,
|
||||
livestream,
|
||||
} = props;
|
||||
const {
|
||||
push,
|
||||
|
@ -162,6 +164,7 @@ function Comment(props: Props) {
|
|||
className={classnames('comment', {
|
||||
'comment--top-level': isTopLevel,
|
||||
'comment--reply': !isTopLevel,
|
||||
'comment--livestream': livestream,
|
||||
})}
|
||||
id={commentId}
|
||||
onMouseOver={() => setMouseHover(true)}
|
||||
|
@ -173,13 +176,20 @@ function Comment(props: Props) {
|
|||
'comment--slimed': slimedToDeath && !displayDeadComment,
|
||||
})}
|
||||
>
|
||||
<div className="comment__thumbnail-wrapper">
|
||||
{authorUri ? (
|
||||
<ChannelThumbnail uri={authorUri} obscure={channelIsBlocked} small className="comment__author-thumbnail" />
|
||||
) : (
|
||||
<ChannelThumbnail small className="comment__author-thumbnail" />
|
||||
)}
|
||||
</div>
|
||||
{!livestream && (
|
||||
<div className="comment__thumbnail-wrapper">
|
||||
{authorUri ? (
|
||||
<ChannelThumbnail
|
||||
uri={authorUri}
|
||||
obscure={channelIsBlocked}
|
||||
small
|
||||
className="comment__author-thumbnail"
|
||||
/>
|
||||
) : (
|
||||
<ChannelThumbnail small className="comment__author-thumbnail" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="comment__body_container">
|
||||
<div className="comment__meta">
|
||||
|
@ -187,13 +197,15 @@ function Comment(props: Props) {
|
|||
{!author ? (
|
||||
<span className="comment__author">{__('Anonymous')}</span>
|
||||
) : (
|
||||
<UriIndicator link uri={authorUri} />
|
||||
<UriIndicator link external={livestream} uri={authorUri} />
|
||||
)}
|
||||
{!livestream && (
|
||||
<Button
|
||||
className="comment__time"
|
||||
onClick={handleTimeClick}
|
||||
label={<DateTime date={timePosted} timeAgo />}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
className="comment__time"
|
||||
onClick={handleTimeClick}
|
||||
label={<DateTime date={timePosted} timeAgo />}
|
||||
/>
|
||||
|
||||
{isPinned && (
|
||||
<span className="comment__pin">
|
||||
|
@ -273,18 +285,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>
|
||||
{!livestream && (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{isReplying && (
|
||||
<CommentCreate
|
||||
|
|
|
@ -4,8 +4,7 @@ import classnames from 'classnames';
|
|||
import Card from 'component/common/card';
|
||||
import Spinner from 'component/spinner';
|
||||
import CommentCreate from 'component/commentCreate';
|
||||
import Button from 'component/button';
|
||||
import MarkdownPreview from 'component/common/markdown-preview';
|
||||
import CommentView from 'component/comment';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -114,20 +113,17 @@ export default function LivestreamFeed(props: Props) {
|
|||
<div className="livestream__comments">
|
||||
{comments.map((comment) => (
|
||||
<div key={comment.comment_id} className={classnames('livestream__comment')}>
|
||||
{comment.channel_url ? (
|
||||
<Button
|
||||
target="_blank"
|
||||
className={classnames('livestream__comment-author', {
|
||||
'livestream__comment-author--streamer':
|
||||
claim.signing_channel && claim.signing_channel.claim_id === comment.channel_id,
|
||||
})}
|
||||
navigate={comment.channel_url}
|
||||
label={comment.channel_name}
|
||||
/>
|
||||
) : (
|
||||
<div className="livestream__comment-author">{comment.channel_name}</div>
|
||||
)}
|
||||
<MarkdownPreview content={comment.comment} simpleLinks />
|
||||
<CommentView
|
||||
livestream
|
||||
isTopLevel
|
||||
uri={uri}
|
||||
authorUri={comment.channel_url}
|
||||
author={comment.channel_name}
|
||||
claimId={comment.claim_id}
|
||||
commentId={comment.comment_id}
|
||||
message={comment.comment}
|
||||
timePosted={comment.timestamp * 1000}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -17,6 +17,7 @@ type Props = {
|
|||
// to allow for other elements to be nested within the UriIndicator
|
||||
children: ?Node,
|
||||
inline: boolean,
|
||||
external?: boolean,
|
||||
};
|
||||
|
||||
class UriIndicator extends React.PureComponent<Props> {
|
||||
|
@ -37,7 +38,7 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { link, isResolvingUri, claim, children, inline, hideAnonymous = false } = this.props;
|
||||
const { link, isResolvingUri, claim, children, inline, hideAnonymous = false, external = false } = this.props;
|
||||
|
||||
if (!claim) {
|
||||
return <span className="empty">{isResolvingUri ? 'Validating...' : 'Unused'}</span>;
|
||||
|
@ -74,10 +75,14 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
if (children) {
|
||||
return <Button navigate={channelLink}>{children}</Button>;
|
||||
return (
|
||||
<Button target={external ? '_blank' : undefined} navigate={channelLink}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Button className="button--uri-indicator" navigate={channelLink}>
|
||||
<Button className="button--uri-indicator" navigate={channelLink} target={external ? '_blank' : undefined}>
|
||||
{inner}
|
||||
</Button>
|
||||
);
|
||||
|
|
|
@ -13,13 +13,13 @@ const NO_WALLET_ERROR = 'no wallet found for this user';
|
|||
const BAD_PASSWORD_ERROR_NAME = 'InvalidPasswordError';
|
||||
|
||||
export function doSetDefaultAccount(success, failure) {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_DEFAULT_ACCOUNT,
|
||||
});
|
||||
|
||||
Lbry.account_list()
|
||||
.then(accountList => {
|
||||
.then((accountList) => {
|
||||
const { lbc_mainnet: accounts } = accountList;
|
||||
let defaultId;
|
||||
for (let i = 0; i < accounts.length; ++i) {
|
||||
|
@ -43,7 +43,7 @@ export function doSetDefaultAccount(success, failure) {
|
|||
success();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
if (failure) {
|
||||
failure(err);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ export function doSetDefaultAccount(success, failure) {
|
|||
failure('Could not set a default account'); // fail
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
if (failure) {
|
||||
failure(err);
|
||||
}
|
||||
|
@ -62,13 +62,13 @@ export function doSetDefaultAccount(success, failure) {
|
|||
}
|
||||
|
||||
export function doSetSync(oldHash, newHash, data) {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_SYNC_STARTED,
|
||||
});
|
||||
|
||||
return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
if (!response.hash) {
|
||||
throw Error('No hash returned for sync/set.');
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ export function doSetSync(oldHash, newHash, data) {
|
|||
data: { syncHash: response.hash },
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_SYNC_FAILED,
|
||||
data: { error },
|
||||
|
@ -94,7 +94,7 @@ export const doGetSyncDesktop = (cb?, password) => (dispatch, getState) => {
|
|||
const setSyncPending = selectSetSyncIsPending(state);
|
||||
const syncLocked = selectSyncIsLocked(state);
|
||||
|
||||
return getSavedPassword().then(savedPassword => {
|
||||
return getSavedPassword().then((savedPassword) => {
|
||||
const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword;
|
||||
|
||||
if (syncEnabled && !getSyncPending && !setSyncPending && !syncLocked) {
|
||||
|
@ -128,7 +128,7 @@ export function doSyncLoop(noInterval) {
|
|||
}
|
||||
|
||||
export function doSyncUnsubscribe() {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
if (syncTimer) {
|
||||
clearInterval(syncTimer);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ export function doGetSync(passedPassword, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_STARTED,
|
||||
});
|
||||
|
@ -156,7 +156,7 @@ export function doGetSync(passedPassword, callback) {
|
|||
const data = {};
|
||||
|
||||
Lbry.wallet_status()
|
||||
.then(status => {
|
||||
.then((status) => {
|
||||
if (status.is_locked) {
|
||||
return Lbry.wallet_unlock({ password });
|
||||
}
|
||||
|
@ -164,15 +164,15 @@ export function doGetSync(passedPassword, callback) {
|
|||
// Wallet is already unlocked
|
||||
return true;
|
||||
})
|
||||
.then(isUnlocked => {
|
||||
.then((isUnlocked) => {
|
||||
if (isUnlocked) {
|
||||
return Lbry.sync_hash();
|
||||
}
|
||||
data.unlockFailed = true;
|
||||
throw new Error();
|
||||
})
|
||||
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||
.then(response => {
|
||||
.then((hash) => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||
.then((response) => {
|
||||
const syncHash = response.hash;
|
||||
data.syncHash = syncHash;
|
||||
data.syncData = response.data;
|
||||
|
@ -183,7 +183,7 @@ export function doGetSync(passedPassword, callback) {
|
|||
return Lbry.sync_apply({ password, data: response.data, blocking: true });
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
if (!response) {
|
||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||
handleCallback(null, data.changed);
|
||||
|
@ -200,7 +200,7 @@ export function doGetSync(passedPassword, callback) {
|
|||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||
handleCallback(null, data.changed);
|
||||
})
|
||||
.catch(syncAttemptError => {
|
||||
.catch((syncAttemptError) => {
|
||||
const badPasswordError =
|
||||
syncAttemptError && syncAttemptError.data && syncAttemptError.data.name === BAD_PASSWORD_ERROR_NAME;
|
||||
|
||||
|
@ -248,7 +248,7 @@ export function doGetSync(passedPassword, callback) {
|
|||
dispatch(doSetSync('', walletHash, syncApplyData, password));
|
||||
handleCallback();
|
||||
})
|
||||
.catch(syncApplyError => {
|
||||
.catch((syncApplyError) => {
|
||||
handleCallback(syncApplyError);
|
||||
});
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ export function doGetSync(passedPassword, callback) {
|
|||
}
|
||||
|
||||
export function doSyncApply(syncHash, syncData, password) {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.SYNC_APPLY_STARTED,
|
||||
});
|
||||
|
@ -286,14 +286,14 @@ export function doSyncApply(syncHash, syncData, password) {
|
|||
}
|
||||
|
||||
export function doCheckSync() {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_STARTED,
|
||||
});
|
||||
|
||||
Lbry.sync_hash().then(hash => {
|
||||
Lbry.sync_hash().then((hash) => {
|
||||
Lbryio.call('sync', 'get', { hash }, 'post')
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const data = {
|
||||
hasSyncedWallet: true,
|
||||
syncHash: response.hash,
|
||||
|
@ -314,19 +314,19 @@ export function doCheckSync() {
|
|||
}
|
||||
|
||||
export function doResetSync() {
|
||||
return dispatch =>
|
||||
new Promise(resolve => {
|
||||
return (dispatch) =>
|
||||
new Promise((resolve) => {
|
||||
dispatch({ type: ACTIONS.SYNC_RESET });
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
const data = {};
|
||||
return Lbry.sync_hash()
|
||||
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||
.then(syncGetResponse => {
|
||||
.then((hash) => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||
.then((syncGetResponse) => {
|
||||
data.oldHash = syncGetResponse.hash;
|
||||
|
||||
return Lbry.sync_apply({ password: oldPassword, data: syncGetResponse.data });
|
||||
|
@ -339,7 +339,7 @@ export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) {
|
|||
}
|
||||
})
|
||||
.then(() => Lbry.sync_apply({ password: newPassword }))
|
||||
.then(syncApplyResponse => {
|
||||
.then((syncApplyResponse) => {
|
||||
if (syncApplyResponse.hash !== data.oldHash) {
|
||||
return dispatch(doSetSync(data.oldHash, syncApplyResponse.hash, syncApplyResponse.data));
|
||||
}
|
||||
|
|
|
@ -105,12 +105,20 @@ export const makeSelectCommentIdsForUri = (uri: string) =>
|
|||
|
||||
export const makeSelectMyReactionsForComment = (commentId: string) =>
|
||||
createSelector(selectState, (state) => {
|
||||
if (!state.myReactsByCommentId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return state.myReactsByCommentId[commentId] || [];
|
||||
});
|
||||
|
||||
export const makeSelectOthersReactionsForComment = (commentId: string) =>
|
||||
createSelector(selectState, (state) => {
|
||||
return state.othersReactsByCommentId[commentId];
|
||||
if (!state.othersReactsByCommentId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return state.othersReactsByCommentId[commentId] || {};
|
||||
});
|
||||
|
||||
export const selectPendingCommentReacts = createSelector(selectState, (state) => state.pendingCommentReactions);
|
||||
|
|
|
@ -34,6 +34,7 @@ $thumbnailWidthSmall: 1rem;
|
|||
}
|
||||
|
||||
.comment {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: var(--font-small);
|
||||
|
@ -101,6 +102,10 @@ $thumbnailWidthSmall: 1rem;
|
|||
}
|
||||
}
|
||||
|
||||
.comment--livestream {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.comment--slimed {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
|
|
@ -48,10 +48,6 @@
|
|||
margin-top: var(--spacing-s);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> :first-child {
|
||||
margin-right: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
|
||||
.livestream__comment-author {
|
||||
|
|
Loading…
Reference in a new issue