Compare commits
1 Commits
main
...
edit-comme
Author | SHA1 | Date | |
---|---|---|---|
|
3b575080b0 |
|
@ -90,9 +90,11 @@ service cloud.firestore {
|
||||||
.hasOnly(['description', 'closeTime', 'question'])
|
.hasOnly(['description', 'closeTime', 'question'])
|
||||||
&& resource.data.creatorId == request.auth.uid;
|
&& resource.data.creatorId == request.auth.uid;
|
||||||
allow update: if isAdmin();
|
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} {
|
match /{somePath=**}/bets/{betId} {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-ti
|
||||||
import { firebaseLogin } from 'web/lib/firebase/users'
|
import { firebaseLogin } from 'web/lib/firebase/users'
|
||||||
import {
|
import {
|
||||||
createCommentOnContract,
|
createCommentOnContract,
|
||||||
|
editCommentOnContract,
|
||||||
MAX_COMMENT_LENGTH,
|
MAX_COMMENT_LENGTH,
|
||||||
} from 'web/lib/firebase/comments'
|
} from 'web/lib/firebase/comments'
|
||||||
import { BetStatusText } from 'web/components/feed/feed-bets'
|
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 { CommentTipMap, CommentTips } from 'web/hooks/use-tip-txns'
|
||||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||||
import { Content, TextEditor, useTextEditor } from '../editor'
|
import { Content, TextEditor, useTextEditor } from '../editor'
|
||||||
import { Editor } from '@tiptap/react'
|
import { Editor, JSONContent } from '@tiptap/react'
|
||||||
|
|
||||||
export function FeedCommentThread(props: {
|
export function FeedCommentThread(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -113,6 +114,8 @@ export function CommentRepliesList(props: {
|
||||||
scrollAndOpenReplyInput,
|
scrollAndOpenReplyInput,
|
||||||
treatFirstIndexEqually,
|
treatFirstIndexEqually,
|
||||||
} = props
|
} = props
|
||||||
|
const [editCommentId, setEditCommentId] = useState<string | undefined>()
|
||||||
|
const user = useUser()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{commentsList.map((comment, commentIdx) => (
|
{commentsList.map((comment, commentIdx) => (
|
||||||
|
@ -131,23 +134,36 @@ export function CommentRepliesList(props: {
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<FeedComment
|
{editCommentId === comment.id ? (
|
||||||
contract={contract}
|
<CommentInput
|
||||||
comment={comment}
|
contract={contract}
|
||||||
tips={tips[comment.id]}
|
// TODO: These were Copilot-generated, examine more closely
|
||||||
betsBySameUser={betsByUserId[comment.userId] ?? []}
|
betsByCurrentUser={(user && betsByUserId[user.id]) ?? []}
|
||||||
onReplyClick={scrollAndOpenReplyInput}
|
commentsByCurrentUser={commentsList.filter(
|
||||||
probAtCreatedTime={
|
(c) => c.userId === user?.id
|
||||||
contract.outcomeType === 'BINARY'
|
)}
|
||||||
? minBy(bets, (bet) => {
|
toEdit={comment}
|
||||||
return bet.createdTime < comment.createdTime
|
/>
|
||||||
? comment.createdTime - bet.createdTime
|
) : (
|
||||||
: comment.createdTime
|
<FeedComment
|
||||||
})?.probAfter
|
contract={contract}
|
||||||
: undefined
|
comment={comment}
|
||||||
}
|
tips={tips[comment.id]}
|
||||||
smallAvatar={smallAvatar}
|
betsBySameUser={betsByUserId[comment.userId] ?? []}
|
||||||
/>
|
onReplyClick={scrollAndOpenReplyInput}
|
||||||
|
onEditClick={() => 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}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -162,6 +178,7 @@ export function FeedComment(props: {
|
||||||
probAtCreatedTime?: number
|
probAtCreatedTime?: number
|
||||||
smallAvatar?: boolean
|
smallAvatar?: boolean
|
||||||
onReplyClick?: (comment: ContractComment) => void
|
onReplyClick?: (comment: ContractComment) => void
|
||||||
|
onEditClick?: (comment: ContractComment) => void
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
contract,
|
contract,
|
||||||
|
@ -170,6 +187,7 @@ export function FeedComment(props: {
|
||||||
betsBySameUser,
|
betsBySameUser,
|
||||||
probAtCreatedTime,
|
probAtCreatedTime,
|
||||||
onReplyClick,
|
onReplyClick,
|
||||||
|
onEditClick,
|
||||||
} = props
|
} = props
|
||||||
const { text, content, userUsername, userName, userAvatarUrl, createdTime } =
|
const { text, content, userUsername, userName, userAvatarUrl, createdTime } =
|
||||||
comment
|
comment
|
||||||
|
@ -199,6 +217,9 @@ export function FeedComment(props: {
|
||||||
matchedBet ? [] : betsBySameUser
|
matchedBet ? [] : betsBySameUser
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const self = useUser()
|
||||||
|
const canEdit = self?.id === comment.userId
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
@ -266,6 +287,14 @@ export function FeedComment(props: {
|
||||||
Reply
|
Reply
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{canEdit && onEditClick ? (
|
||||||
|
<button
|
||||||
|
className="font-bold hover:underline"
|
||||||
|
onClick={() => onEditClick(comment)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -326,6 +355,7 @@ export function CommentInput(props: {
|
||||||
// Reply to another comment
|
// Reply to another comment
|
||||||
parentCommentId?: string
|
parentCommentId?: string
|
||||||
onSubmitComment?: () => void
|
onSubmitComment?: () => void
|
||||||
|
toEdit?: ContractComment
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
contract,
|
contract,
|
||||||
|
@ -335,6 +365,7 @@ export function CommentInput(props: {
|
||||||
parentCommentId,
|
parentCommentId,
|
||||||
replyToUser,
|
replyToUser,
|
||||||
onSubmitComment,
|
onSubmitComment,
|
||||||
|
toEdit,
|
||||||
} = props
|
} = props
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const { editor, upload } = useTextEditor({
|
const { editor, upload } = useTextEditor({
|
||||||
|
@ -344,6 +375,7 @@ export function CommentInput(props: {
|
||||||
!!parentCommentId || !!parentAnswerOutcome
|
!!parentCommentId || !!parentAnswerOutcome
|
||||||
? 'Write a reply...'
|
? 'Write a reply...'
|
||||||
: 'Write a comment...',
|
: 'Write a comment...',
|
||||||
|
defaultValue: toEdit?.content,
|
||||||
})
|
})
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
|
||||||
|
@ -362,14 +394,19 @@ export function CommentInput(props: {
|
||||||
}
|
}
|
||||||
if (!editor || editor.isEmpty || isSubmitting) return
|
if (!editor || editor.isEmpty || isSubmitting) return
|
||||||
setIsSubmitting(true)
|
setIsSubmitting(true)
|
||||||
await createCommentOnContract(
|
if (toEdit) {
|
||||||
contract.id,
|
await editCommentOnContract(toEdit, editor.getJSON())
|
||||||
editor.getJSON(),
|
} else {
|
||||||
user,
|
await createCommentOnContract(
|
||||||
betId,
|
contract.id,
|
||||||
parentAnswerOutcome,
|
editor.getJSON(),
|
||||||
parentCommentId
|
user,
|
||||||
)
|
betId,
|
||||||
|
parentAnswerOutcome,
|
||||||
|
parentCommentId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
onSubmitComment?.()
|
onSubmitComment?.()
|
||||||
setIsSubmitting(false)
|
setIsSubmitting(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,15 @@ export type { Comment }
|
||||||
|
|
||||||
export const MAX_COMMENT_LENGTH = 10000
|
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(
|
export async function createCommentOnContract(
|
||||||
contractId: string,
|
contractId: string,
|
||||||
content: JSONContent,
|
content: JSONContent,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user