Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Quill Markdown #3246

Merged
merged 12 commits into from
Apr 1, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { User } from '../user/user';
import { EditComment } from './edit_comment';
import { clearEditingLocalStorage } from './helpers';
import { AnonymousUser } from '../user/anonymous_user';
import { QuillRenderer } from '../react_quill_editor/quill_renderer';

type CommentAuthorProps = {
comment: CommentType<any>;
Expand All @@ -29,21 +30,14 @@ const CommentAuthor = (props: CommentAuthorProps) => {
// Check for accounts on forums that originally signed up on a different base chain,
// Render them as anonymous as the forum is unable to support them.
if (app.chain.meta.type === ChainType.Offchain) {
if (
comment.authorChain !== app.chain.id &&
comment.authorChain !== app.chain.base
) {
if (comment.authorChain !== app.chain.id && comment.authorChain !== app.chain.base) {
return <AnonymousUser distinguishingKey={comment.author} />;
}
}

const author: Account = app.chain.accounts.get(comment.author);

return comment.deleted ? (
<span>[deleted]</span>
) : (
<User avatarSize={24} user={author} popover linkify />
);
return comment.deleted ? <span>[deleted]</span> : <User avatarSize={24} user={author} popover linkify />;
};

type CommentProps = {
Expand All @@ -58,20 +52,11 @@ type CommentProps = {
};

export const Comment = (props: CommentProps) => {
const {
comment,
handleIsReplying,
isLast,
isLocked,
setIsGloballyEditing,
threadLevel,
updatedCommentsCallback,
} = props;

const [isEditingComment, setIsEditingComment] =
React.useState<boolean>(false);
const [shouldRestoreEdits, setShouldRestoreEdits] =
React.useState<boolean>(false);
const { comment, handleIsReplying, isLast, isLocked, setIsGloballyEditing, threadLevel, updatedCommentsCallback } =
props;

const [isEditingComment, setIsEditingComment] = React.useState<boolean>(false);
const [shouldRestoreEdits, setShouldRestoreEdits] = React.useState<boolean>(false);
const [savedEdits, setSavedEdits] = React.useState<string>('');

const handleSetIsEditingComment = (status: boolean) => {
Expand All @@ -83,24 +68,21 @@ export const Comment = (props: CommentProps) => {
app.user.isSiteAdmin ||
app.roles.isRoleOfCommunity({
role: 'admin',
chain: app.activeChainId(),
chain: app.activeChainId()
}) ||
app.roles.isRoleOfCommunity({
role: 'moderator',
chain: app.activeChainId(),
chain: app.activeChainId()
});

const canReply =
!isLast && !isLocked && app.isLoggedIn() && app.user.activeAccount;
const canReply = !isLast && !isLocked && app.isLoggedIn() && app.user.activeAccount;

const canEditAndDelete =
!isLocked &&
(comment.author === app.user.activeAccount?.address || isAdminOrMod);
const canEditAndDelete = !isLocked && (comment.author === app.user.activeAccount?.address || isAdminOrMod);

const deleteComment = async () => {
await app.comments.delete(comment);
updatedCommentsCallback();
}
};

return (
<div className={`Comment comment-${comment.id}`}>
Expand All @@ -120,12 +102,7 @@ export const Comment = (props: CommentProps) => {
{/* <CWText type="caption" className="published-text">
published on
</CWText> */}
<CWText
key={comment.id}
type="caption"
fontWeight="medium"
className="published-text"
>
<CWText key={comment.id} type="caption" fontWeight="medium" className="published-text">
{moment(comment.createdAt).format('l')}
</CWText>
</div>
Expand All @@ -140,7 +117,7 @@ export const Comment = (props: CommentProps) => {
) : (
<>
<CWText className="comment-text">
{renderQuillTextBody(comment.text)}
<QuillRenderer doc={comment.text} />
</CWText>
{!comment.deleted && (
<div className="comment-footer">
Expand All @@ -165,11 +142,7 @@ export const Comment = (props: CommentProps) => {
{canEditAndDelete && (
<PopoverMenu
renderTrigger={(onclick) => (
<CWIconButton
iconName="dotsVertical"
iconSize="small"
onClick={onclick}
/>
<CWIconButton iconName="dotsVertical" iconSize="small" onClick={onclick} />
)}
menuItems={[
{
Expand All @@ -178,32 +151,23 @@ export const Comment = (props: CommentProps) => {
onClick: async (e) => {
e.preventDefault();
setSavedEdits(
localStorage.getItem(
`${app.activeChainId()}-edit-comment-${
comment.id
}-storedText`
)
localStorage.getItem(`${app.activeChainId()}-edit-comment-${comment.id}-storedText`)
);
if (savedEdits) {
clearEditingLocalStorage(
comment.id,
ContentType.Comment
);
clearEditingLocalStorage(comment.id, ContentType.Comment);

const confirmationResult = window.confirm(
'Previous changes found. Restore edits?'
);
const confirmationResult = window.confirm('Previous changes found. Restore edits?');

setShouldRestoreEdits(confirmationResult);
}
handleSetIsEditingComment(true);
},
}
},
{
label: 'Delete',
iconLeft: 'trash',
onClick: deleteComment,
},
onClick: deleteComment
}
]}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ import { CWButton } from '../component_kit/cw_button';
import { CWText } from '../component_kit/cw_text';
import { CWValidationText } from '../component_kit/cw_validation_text';
import { jumpHighlightComment } from './helpers';
import {
createDeltaFromText,
getTextFromDelta,
ReactQuillEditor,
} from '../react_quill_editor';
import { createDeltaFromText, getTextFromDelta, ReactQuillEditor } from '../react_quill_editor';
import { serializeDelta } from '../react_quill_editor/utils';

type CreateCommmentProps = {
handleIsReplying?: (isReplying: boolean, id?: number) => void;
Expand All @@ -32,19 +29,12 @@ type CreateCommmentProps = {

export const CreateComment = (props: CreateCommmentProps) => {
const [errorMsg, setErrorMsg] = React.useState<string | null>(null);
const [contentDelta, setContentDelta] = React.useState<DeltaStatic>(
createDeltaFromText('')
);
const [contentDelta, setContentDelta] = React.useState<DeltaStatic>(createDeltaFromText(''));
const [sendingComment, setSendingComment] = React.useState<boolean>(false);

const editorValue = getTextFromDelta(contentDelta);

const {
handleIsReplying,
parentCommentId,
rootProposal,
updatedCommentsCallback,
} = props;
const { handleIsReplying, parentCommentId, rootProposal, updatedCommentsCallback } = props;

const author = app.user.activeAccount;

Expand All @@ -61,7 +51,7 @@ export const CreateComment = (props: CreateCommmentProps) => {
author.address,
rootProposal.uniqueIdentifier,
chainId,
JSON.stringify(contentDelta),
serializeDelta(contentDelta),
parentCommentId
);

Expand All @@ -88,79 +78,54 @@ export const CreateComment = (props: CreateCommmentProps) => {
}
};

const activeTopicName =
rootProposal instanceof Thread ? rootProposal?.topic?.name : null;
const activeTopicName = rootProposal instanceof Thread ? rootProposal?.topic?.name : null;

// token balance check if needed
const tokenPostingThreshold: BN =
TopicGateCheck.getTopicThreshold(activeTopicName);
const tokenPostingThreshold: BN = TopicGateCheck.getTopicThreshold(activeTopicName);

const userBalance: BN = TopicGateCheck.getUserBalance();
const userFailsThreshold =
tokenPostingThreshold?.gtn(0) &&
userBalance?.gtn(0) &&
userBalance.lt(tokenPostingThreshold);
tokenPostingThreshold?.gtn(0) && userBalance?.gtn(0) && userBalance.lt(tokenPostingThreshold);

const disabled =
editorValue.length === 0 || sendingComment || userFailsThreshold;
const disabled = editorValue.length === 0 || sendingComment || userFailsThreshold;

const decimals = getDecimals(app.chain);

const cancel = (e) => {
e.preventDefault();
setContentDelta(createDeltaFromText(''))
setContentDelta(createDeltaFromText(''));
if (handleIsReplying) {
handleIsReplying(false)
handleIsReplying(false);
}
}
};

return (
<div className="CreateComment">
<div className="attribution-row">
<div className="attribution-left-content">
<CWText type="caption">
{parentType === ContentType.Comment ? 'Reply as' : 'Comment as'}
</CWText>
<CWText type="caption">{parentType === ContentType.Comment ? 'Reply as' : 'Comment as'}</CWText>
<CWText type="caption" fontWeight="medium" className="user-link-text">
<User user={author} hideAvatar linkify />
</CWText>
</div>
{errorMsg && <CWValidationText message={errorMsg} status="failure" />}
</div>
<ReactQuillEditor
className="editor"
contentDelta={contentDelta}
setContentDelta={setContentDelta}
/>
<ReactQuillEditor className="editor" contentDelta={contentDelta} setContentDelta={setContentDelta} />
{tokenPostingThreshold && tokenPostingThreshold.gt(new BN(0)) && (
<CWText className="token-req-text">
Commenting in {activeTopicName} requires{' '}
{weiToTokens(tokenPostingThreshold.toString(), decimals)}{' '}
Commenting in {activeTopicName} requires {weiToTokens(tokenPostingThreshold.toString(), decimals)}{' '}
{app.chain.meta.default_symbol}.{' '}
{userBalance && app.user.activeAccount && (
<>
You have {weiToTokens(userBalance.toString(), decimals)}{' '}
{app.chain.meta.default_symbol}.
You have {weiToTokens(userBalance.toString(), decimals)} {app.chain.meta.default_symbol}.
</>
)}
</CWText>
)}
<div className="form-bottom">
<div className="form-buttons">
{
editorValue.length > 0 && (
<CWButton
buttonType="secondary-blue"
onClick={cancel}
label="Cancel"
/>
)
}
<CWButton
disabled={disabled}
onClick={handleSubmitComment}
label="Submit"
/>
{editorValue.length > 0 && <CWButton buttonType="secondary-blue" onClick={cancel} label="Cancel" />}
<CWButton disabled={disabled} onClick={handleSubmitComment} label="Submit" />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { CWButton } from '../component_kit/cw_button';
import { clearEditingLocalStorage } from './helpers';
import type { DeltaStatic } from 'quill';
import { ReactQuillEditor } from '../react_quill_editor';
import { parseDeltaString } from '../react_quill_editor/utils';
import { deserializeDelta, serializeDelta } from '../react_quill_editor/utils';

type EditCommentProps = {
comment: Comment<any>;
Expand All @@ -20,16 +20,10 @@ type EditCommentProps = {
};

export const EditComment = (props: EditCommentProps) => {
const {
comment,
savedEdits,
setIsEditing,
shouldRestoreEdits,
updatedCommentsCallback,
} = props;
const { comment, savedEdits, setIsEditing, shouldRestoreEdits, updatedCommentsCallback } = props;

const commentBody = (shouldRestoreEdits && savedEdits) ? savedEdits : comment.text;
const body = parseDeltaString(commentBody)
const commentBody = shouldRestoreEdits && savedEdits ? savedEdits : comment.text;
const body = deserializeDelta(commentBody);

const [contentDelta, setContentDelta] = React.useState<DeltaStatic>(body);
const [saving, setSaving] = React.useState<boolean>();
Expand All @@ -40,53 +34,38 @@ export const EditComment = (props: EditCommentProps) => {
let cancelConfirmed = true;

if (JSON.stringify(body) !== JSON.stringify(contentDelta)) {
cancelConfirmed = window.confirm(
'Cancel editing? Changes will not be saved.'
);
cancelConfirmed = window.confirm('Cancel editing? Changes will not be saved.');
}

if (cancelConfirmed) {
setIsEditing(false);
clearEditingLocalStorage(comment.id, ContentType.Comment);
}
}
};

const save = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();

setSaving(true);

try {
await app.comments.edit(comment, JSON.stringify(contentDelta))
await app.comments.edit(comment, serializeDelta(contentDelta));
setIsEditing(false);
clearEditingLocalStorage(comment.id, ContentType.Comment);
updatedCommentsCallback();
} catch (err) {
console.error(err)
console.error(err);
} finally {
setSaving(false);
}

}
};

return (
<div className="EditComment">
<ReactQuillEditor
contentDelta={contentDelta}
setContentDelta={setContentDelta}
/>
<ReactQuillEditor contentDelta={contentDelta} setContentDelta={setContentDelta} />
<div className="buttons-row">
<CWButton
label="Cancel"
disabled={saving}
buttonType="secondary-blue"
onClick={cancel}
/>
<CWButton
label="Save"
disabled={saving}
onClick={save}
/>
<CWButton label="Cancel" disabled={saving} buttonType="secondary-blue" onClick={cancel} />
<CWButton label="Save" disabled={saving} onClick={save} />
</div>
</div>
);
Expand Down
Loading