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

comments: Add proposal author updates. #2549

Merged
merged 50 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c538acd
enable author updates on approved proposals
amass01 Aug 26, 2021
abda44a
bug fixes & cleanup
amass01 Aug 26, 2021
d81b762
allow replies only on latest author update
amass01 Aug 26, 2021
60395bb
allow replies only on the latest author update thread
amass01 Aug 28, 2021
d3c40cf
if author updates allowed display comment form only to record author
amass01 Aug 28, 2021
1ed1331
add an XXX as a reminder to consider the billing status when it's
amass01 Aug 28, 2021
b9ba6d9
enable comment votes only on latest author update thread
amass01 Aug 28, 2021
087bb2d
visually indicate author updates.
amass01 Aug 30, 2021
ebb26c7
[wip] display each author update in a separate comments section
amass01 Sep 1, 2021
e1ad910
styling, cleanup & bug fixes
amass01 Sep 2, 2021
3c9502a
yarn prettify
amass01 Sep 2, 2021
19a4e29
Merge branch 'master' into authoruodates
amass01 Sep 3, 2021
7c08970
single thread tweaks and fixes
amass01 Sep 3, 2021
812efeb
fix document.getElementById is undefined in scrollToElement.
amass01 Sep 3, 2021
108d345
add new author updates to the top
amass01 Sep 3, 2021
b2b3fce
map backend `extra data not allowed` error code
amass01 Sep 6, 2021
ffdba8a
replace `Comment $numofcomments` with `$updatetitle $numofcomments` for
amass01 Sep 6, 2021
cc91d16
display a tooltip next to the author update title with
amass01 Sep 6, 2021
56189bc
tooltip tweaks
amass01 Sep 6, 2021
e4a3cac
yarn prettify
amass01 Sep 6, 2021
e31a2fe
push missing title tooltip icon alignment changes
amass01 Sep 6, 2021
c5f6246
add `Proposal Update` as title and the icon next to it
amass01 Sep 7, 2021
6274530
hide comments section while processing comments
amass01 Sep 7, 2021
4c5e620
cleanup
amass01 Sep 7, 2021
5554f2c
add confirmation modal when the user is submitting subsequent updates
amass01 Sep 7, 2021
dd4d4ea
yarn prettify
amass01 Sep 7, 2021
a7f2f02
typo
amass01 Sep 7, 2021
6f94200
map error code, adding margin to form error message & fix author update
amass01 Sep 7, 2021
39e94ae
yarn prettify
amass01 Sep 7, 2021
82e5550
reset form bug fix
amass01 Sep 7, 2021
cc4c941
tiny cleanup
amass01 Sep 7, 2021
5ca9087
Merge branch 'master' into authoruodates
amass01 Sep 8, 2021
e321288
[wip] fix comments dep in Detail container which causes extra
amass01 Sep 10, 2021
29d6dae
fix flickering and infinite re-renders & mem optimaization & cleanup
amass01 Sep 10, 2021
e22d49f
enhance useScrollTo
amass01 Sep 11, 2021
15145c1
Normalize comments redux state.
amass01 Sep 13, 2021
b1056b2
show comments related errors in a separate row
amass01 Sep 13, 2021
6ffd9ae
yarn prettify
amass01 Sep 13, 2021
74c8ba4
re-add optimistic comment vote.
amass01 Sep 13, 2021
8d12c8f
Merge branch 'master' into authoruodates
amass01 Sep 13, 2021
491cb7a
move useComments to global hooks dir
amass01 Sep 14, 2021
95a7f44
typo
amass01 Sep 14, 2021
0faa540
Merge branch 'master' into authoruodates
amass01 Sep 14, 2021
dca90eb
add entity dirs for e2e tests.
amass01 Sep 14, 2021
c02c8ff
e2e: Add author update thread e2e tests.
amass01 Sep 15, 2021
562792c
opps
amass01 Sep 15, 2021
0f885ec
tiny cleanup
amass01 Sep 15, 2021
1fb3fe3
add summaries mock as middleware
amass01 Sep 15, 2021
0b63edc
code review nits
amass01 Sep 18, 2021
83446c9
cleanup unnecessary useMemo usage.
amass01 Sep 19, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/actions/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1063,15 +1063,24 @@ export const onSubmitComment = (
token,
commentText,
parentid,
state
state,
extraData,
extraDataHint
) =>
withCsrf((dispatch, getState, csrf) => {
const comment = api.makeComment(token, commentText, parentid, state);
const comment = api.makeComment(
token,
commentText,
parentid,
state,
extraData,
extraDataHint
);
dispatch(act.REQUEST_NEW_COMMENT(comment));
return Promise.resolve(api.signComment(currentUserID, comment))
.then((comment) => {
// make sure this is not a duplicate comment by comparing to the existent
// comments signatures
// make sure this is not a duplicate comment by comparing to the
// existen comments signatures.
amass01 marked this conversation as resolved.
Show resolved Hide resolved
const comments = sel.commentsByToken(getState())[token];
const signatureFound =
comments && comments.find((cm) => cm.signature === comment.signature);
Expand Down Expand Up @@ -1694,8 +1703,8 @@ export const onSubmitDccComment = (currentUserID, token, comment, parentid) =>
return Promise.resolve(api.makeDccComment(token, comment, parentid))
.then((comment) => api.signDccComment(currentUserID, comment))
.then((comment) => {
// make sure this is not a duplicate comment by comparing to the existent
// comments signatures
// make sure this is not a duplicate comment by comparing to the
// existent comments signatures.
const comments = sel.commentsByToken(getState())[token];
const signatureFound =
comments && comments.find((cm) => cm.signature === comment.signature);
Expand Down
12 changes: 10 additions & 2 deletions src/actions/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,19 @@ export const onEditProposal =
};

export const onSaveNewComment =
({ comment, token, parentID, state }) =>
({ comment, token, parentID, state, extraData, extraDataHint }) =>
(dispatch, getState) => {
const userid = sel.currentUserID(getState());
return dispatch(
onSubmitCommentApi(userid, token, comment, parentID, state)
onSubmitCommentApi(
userid,
token,
comment,
parentID,
state,
extraData,
extraDataHint
)
);
};

Expand Down
64 changes: 45 additions & 19 deletions src/components/CommentForm/CommentForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import React from "react";
import PropTypes from "prop-types";
import { Formik } from "formik";
import FormikPersist from "src/components/FormikPersist";
import { Button, Message } from "pi-ui";
import { Button, Message, BoxTextInput } from "pi-ui";
import { Row } from "../layout";
import MarkdownEditor from "src/components/MarkdownEditor";
import validationSchema from "./validation";
import { usePolicy } from "src/hooks";

const forbiddenCommentsMdElements = ["h1", "h2", "h3", "h4", "h5", "h6"];

Expand All @@ -15,14 +16,18 @@ const CommentForm = ({
onCommentSubmitted,
disableSubmit,
persistKey,
className
className,
canReceiveAuthorUpdates
}) => {
const {
policyPi: { namesupportedchars, namelengthmax, namelengthmin }
} = usePolicy();
async function handleSubmit(
values,
{ comment, title },
{ resetForm, setSubmitting, setFieldError }
) {
try {
await onSubmit(values.comment.trim());
await onSubmit({ comment: comment.trim(), title });
setSubmitting(false);
resetForm();
onCommentSubmitted && onCommentSubmitted();
Expand All @@ -34,29 +39,50 @@ const CommentForm = ({
return (
<Formik
initialValues={{
title: canReceiveAuthorUpdates ? "" : null,
comment: ""
}}
loading={!validationSchema}
validationSchema={validationSchema}
validationSchema={validationSchema({
namesupportedchars,
namelengthmax,
namelengthmin
})}
onSubmit={handleSubmit}>
{(formikProps) => {
const {
values,
handleBlur,
handleSubmit,
isSubmitting,
setFieldValue,
errors,
isValid
} = formikProps;
function handleCommentChange(v) {
setFieldValue("comment", v);
}
{({
values,
handleBlur,
handleSubmit,
handleChange,
isSubmitting,
setFieldTouched,
setFieldValue,
errors,
isValid,
touched
}) => {
const handleTitleChangeWithTouched = (e) => {
setFieldTouched("title", true);
handleChange(e);
};

const handleCommentChange = (v) => setFieldValue("comment", v);

return (
<form onSubmit={handleSubmit} className={className}>
{errors && errors.global && (
<Message kind="error">{errors.global.toString()}</Message>
)}
{canReceiveAuthorUpdates && (
<BoxTextInput
placeholder="Update title"
name="title"
tabIndex={1}
value={values.title}
onChange={handleTitleChangeWithTouched}
error={touched.title && errors.title}
/>
)}
<MarkdownEditor
allowImgs={false}
allowHeaders={false}
Expand All @@ -66,7 +92,7 @@ const CommentForm = ({
onChange={handleCommentChange}
onBlur={handleBlur}
disallowedElements={forbiddenCommentsMdElements}
placeholder={"Write a comment"}
placeholder="Write a comment"
/>
<Row justify="right" topMarginSize="s">
{!!onCancel && (
Expand Down
27 changes: 24 additions & 3 deletions src/components/CommentForm/validation.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
import * as Yup from "yup";
import { yupFieldMatcher } from "src/utils/validation";

const commentValidationSchema = Yup.object().shape({
comment: Yup.string().required("Required")
});
const commentValidationSchema = ({
namesupportedchars,
namelengthmax,
namelengthmin
}) =>
Yup.object().shape(
{
comment: Yup.string().required("required"),
title: Yup.string()
.nullable()
.notRequired()
.when("title", {
is: (value) => value?.length,
then: (rule) =>
rule
.min(namelengthmin)
.max(namelengthmax)
.matches(...yupFieldMatcher("Title", namesupportedchars))
})
},
// Add Cyclic deps here because when require itself
["title", "title"]
);

export default commentValidationSchema;
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const PROPOSAL_USER_FILTER_DRAFT_PROPOSALS = 2;

export const PROPOSAL_METADATA_FILENAME = "proposalmetadata.json";
export const VOTE_METADATA_FILENAME = "votemetadata.json";
export const PROPOSAL_UPDATE_HINT = "proposalupdate";

export const PROPOSAL_AMOUNT_UNIT = "$";

Expand Down
17 changes: 14 additions & 3 deletions src/containers/Comments/Comment/CommentWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Comment from "./Comment";
import {
PROPOSAL_STATE_UNVETTED,
PROPOSAL_COMMENT_UPVOTE,
PROPOSAL_COMMENT_DOWNVOTE
PROPOSAL_COMMENT_DOWNVOTE,
PROPOSAL_UPDATE_HINT
} from "src/constants";

const ContextLink = React.memo(({ parentid, recordToken }) => (
Expand Down Expand Up @@ -99,9 +100,13 @@ const CommentWrapper = ({
userid,
isNew,
sumOfNewDescendants,
parentid
parentid,
extradatahint,
extradata
} = comment;

const isAuthorUpdate = extradatahint === PROPOSAL_UPDATE_HINT;
const authorUpdateMetadata = isAuthorUpdate && JSON.parse(extradata);
const isRecordAuthor =
recordAuthorID === userid || recordAuthorUsername === username;
const censorable = isAdmin && !readOnly;
Expand Down Expand Up @@ -134,7 +139,7 @@ const CommentWrapper = ({
}, [showReplies]);

const handleSubmitComment = useCallback(
(comment) =>
({ comment }) =>
onSubmitComment({
comment,
token,
Expand Down Expand Up @@ -181,6 +186,12 @@ const CommentWrapper = ({

return (
<>
{authorUpdateMetadata && (
<div>
commentid: {commentid} timestamp: {timestamp} title:{" "}
{authorUpdateMetadata.title}
</div>
)}
<Comment
permalink={`${recordBaseLink}/comments/${commentid}`}
seeInContextLink={contextLink}
Expand Down
36 changes: 23 additions & 13 deletions src/containers/Comments/Comments.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ import CommentsListWrapper from "./CommentsList/CommentsListWrapper";
import CommentLoader from "./Comment/CommentLoader";
import Link from "src/components/Link";
import Or from "src/components/Or";
import useQueryString from "src/hooks/utils/useQueryString";
import useScrollTo from "src/hooks/utils/useScrollTo";
import { useQueryString, useScrollTo } from "src/hooks";
import {
getSortOptionsForSelect,
createSelectOptionFromSortOption,
commentSortOptions,
handleCommentCensoringInfo,
NUMBER_OF_LIST_PLACEHOLDERS
} from "./helpers";
import useIdentity from "src/hooks/api/useIdentity";
import usePaywall from "src/hooks/api/usePaywall";
import { useIdentity, usePaywall } from "src/hooks";
import { IdentityMessageError } from "src/components/IdentityErrorIndicators";
import ModalLogin from "src/components/ModalLogin";
import useModalContext from "src/hooks/utils/useModalContext";
Expand All @@ -35,6 +33,7 @@ import { commentsReducer, initialState, actions } from "./commentsReducer";
import { getQueryStringValue } from "src/lib/queryString";
import useLocalStorage from "src/hooks/utils/useLocalStorage";
import { debounce } from "lodash";
import { PROPOSAL_UPDATE_HINT } from "src/constants";

const COMMENTS_LOGIN_MODAL_ID = "commentsLoginModal";

Expand Down Expand Up @@ -288,7 +287,8 @@ const Comments = ({
className,
history,
proposalState,
recordBaseLink
recordBaseLink,
canReceiveAuthorUpdates
}) => {
const [, identityError] = useIdentity();
const { isPaid, paywallEnabled } = usePaywall();
Expand All @@ -310,21 +310,30 @@ const Comments = ({
const [handleOpenModal, handleCloseModal] = useModalContext();
const { userid } = currentUser || {};

const onRedirectToSignup = () => {
history.push("/user/signup");
};
const onRedirectToSignup = () => history.push("/user/signup");

const paywallMissing = paywallEnabled && !isPaid;
const isSingleThread = !!threadParentID;

const handleSubmitComment = useCallback(
(comment) =>
onSubmitComment({
({ comment, title }) => {
// If title is provided then we are dealing with an author
// update.
let extraData = "",
extraDataHint = "";
if (title) {
extraDataHint = PROPOSAL_UPDATE_HINT;
extraData = JSON.stringify({ title });
}
return onSubmitComment({
comment,
token: recordTokenFull,
parentID: 0,
state: proposalState
}),
state: proposalState,
extraData,
extraDataHint
});
},
[onSubmitComment, proposalState, recordTokenFull]
);

Expand Down Expand Up @@ -374,7 +383,7 @@ const Comments = ({
}>
<Or>
{readOnly && (
<Message kind="blocked" title={"Comments are not allowed"}>
<Message kind="blocked" title="Comments are not allowed">
{readOnlyReason}
</Message>
)}
Expand All @@ -395,6 +404,7 @@ const Comments = ({
persistKey={`commenting-on-${recordToken}`}
onSubmit={handleSubmitComment}
disableSubmit={!!identityError || paywallMissing}
canReceiveAuthorUpdates={canReceiveAuthorUpdates}
/>
)}
</LoggedInContent>
Expand Down
10 changes: 4 additions & 6 deletions src/containers/Comments/CommentsList/CommentsListWrapper.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { useState, useEffect } from "react";
import CommentsList from "./CommentsList";

const getChildren = (comments, commentId, lastTimeAccessed, currentUserID) => {
return (
comments.filter((comment) => +comment.parentid === +commentId) || []
).map((comment) =>
createComputedComment(comment, comments, lastTimeAccessed, currentUserID)
const getChildren = (comments, commentId, lastTimeAccessed, currentUserID) =>
(comments.filter((comment) => +comment.parentid === +commentId) || []).map(
amass01 marked this conversation as resolved.
Show resolved Hide resolved
(comment) =>
createComputedComment(comment, comments, lastTimeAccessed, currentUserID)
);
};

const createComputedComment = (
comment,
Expand Down
Loading