diff --git a/firestore.rules b/firestore.rules index c0d17dac..4b17be29 100644 --- a/firestore.rules +++ b/firestore.rules @@ -90,9 +90,11 @@ service cloud.firestore { .hasOnly(['description', 'closeTime', 'question']) && resource.data.creatorId == request.auth.uid; allow update: if isAdmin(); - match /comments/{commentId} { - allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data); - } + + // TODO: This runs afoul of FirebaseError: Missing or insufficient permissions + match /comments/{commentId} { + allow create, update: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data); + } } match /{somePath=**}/bets/{betId} { diff --git a/web/components/feed/feed-comments.tsx b/web/components/feed/feed-comments.tsx index 0541a7ba..188fa093 100644 --- a/web/components/feed/feed-comments.tsx +++ b/web/components/feed/feed-comments.tsx @@ -16,6 +16,7 @@ import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-ti import { firebaseLogin } from 'web/lib/firebase/users' import { createCommentOnContract, + editCommentOnContract, MAX_COMMENT_LENGTH, } from 'web/lib/firebase/comments' import { BetStatusText } from 'web/components/feed/feed-bets' @@ -28,7 +29,7 @@ import { Tipper } from '../tipper' import { CommentTipMap, CommentTips } from 'web/hooks/use-tip-txns' import { useWindowSize } from 'web/hooks/use-window-size' import { Content, TextEditor, useTextEditor } from '../editor' -import { Editor } from '@tiptap/react' +import { Editor, JSONContent } from '@tiptap/react' export function FeedCommentThread(props: { contract: Contract @@ -113,6 +114,8 @@ export function CommentRepliesList(props: { scrollAndOpenReplyInput, treatFirstIndexEqually, } = props + const [editCommentId, setEditCommentId] = useState() + const user = useUser() return ( <> {commentsList.map((comment, commentIdx) => ( @@ -131,23 +134,36 @@ export function CommentRepliesList(props: { aria-hidden="true" /> )} - { - return bet.createdTime < comment.createdTime - ? comment.createdTime - bet.createdTime - : comment.createdTime - })?.probAfter - : undefined - } - smallAvatar={smallAvatar} - /> + {editCommentId === comment.id ? ( + c.userId === user?.id + )} + toEdit={comment} + /> + ) : ( + setEditCommentId(comment.id)} + probAtCreatedTime={ + contract.outcomeType === 'BINARY' + ? minBy(bets, (bet) => { + return bet.createdTime < comment.createdTime + ? comment.createdTime - bet.createdTime + : comment.createdTime + })?.probAfter + : undefined + } + smallAvatar={smallAvatar} + /> + )} ))} @@ -162,6 +178,7 @@ export function FeedComment(props: { probAtCreatedTime?: number smallAvatar?: boolean onReplyClick?: (comment: ContractComment) => void + onEditClick?: (comment: ContractComment) => void }) { const { contract, @@ -170,6 +187,7 @@ export function FeedComment(props: { betsBySameUser, probAtCreatedTime, onReplyClick, + onEditClick, } = props const { text, content, userUsername, userName, userAvatarUrl, createdTime } = comment @@ -199,6 +217,9 @@ export function FeedComment(props: { matchedBet ? [] : betsBySameUser ) + const self = useUser() + const canEdit = self?.id === comment.userId + return ( )} + {canEdit && onEditClick ? ( + + ) : null} @@ -326,6 +355,7 @@ export function CommentInput(props: { // Reply to another comment parentCommentId?: string onSubmitComment?: () => void + toEdit?: ContractComment }) { const { contract, @@ -335,6 +365,7 @@ export function CommentInput(props: { parentCommentId, replyToUser, onSubmitComment, + toEdit, } = props const user = useUser() const { editor, upload } = useTextEditor({ @@ -344,6 +375,7 @@ export function CommentInput(props: { !!parentCommentId || !!parentAnswerOutcome ? 'Write a reply...' : 'Write a comment...', + defaultValue: toEdit?.content, }) const [isSubmitting, setIsSubmitting] = useState(false) @@ -362,14 +394,19 @@ export function CommentInput(props: { } if (!editor || editor.isEmpty || isSubmitting) return setIsSubmitting(true) - await createCommentOnContract( - contract.id, - editor.getJSON(), - user, - betId, - parentAnswerOutcome, - parentCommentId - ) + if (toEdit) { + await editCommentOnContract(toEdit, editor.getJSON()) + } else { + await createCommentOnContract( + contract.id, + editor.getJSON(), + user, + betId, + parentAnswerOutcome, + parentCommentId + ) + } + onSubmitComment?.() setIsSubmitting(false) } diff --git a/web/lib/firebase/comments.ts b/web/lib/firebase/comments.ts index f7c947fe..c702501f 100644 --- a/web/lib/firebase/comments.ts +++ b/web/lib/firebase/comments.ts @@ -20,6 +20,15 @@ export type { Comment } export const MAX_COMMENT_LENGTH = 10000 +export async function editCommentOnContract( + toEdit: ContractComment, + newContent: JSONContent +) { + const { id, contractId } = toEdit + const ref = doc(getCommentsCollection(contractId), id) + await setDoc(ref, { content: newContent }) +} + export async function createCommentOnContract( contractId: string, content: JSONContent,