Switch comments/chat to rich text editor
This commit is contained in:
parent
bdea739c55
commit
d8b2f14762
|
@ -1,3 +1,5 @@
|
|||
import type { JSONContent } from '@tiptap/core'
|
||||
|
||||
// Currently, comments are created after the bet, not atomically with the bet.
|
||||
// They're uniquely identified by the pair contractId/betId.
|
||||
export type Comment = {
|
||||
|
@ -9,7 +11,9 @@ export type Comment = {
|
|||
replyToCommentId?: string
|
||||
userId: string
|
||||
|
||||
text: string
|
||||
/** @deprecated - content now stored as JSON in content*/
|
||||
text?: string
|
||||
content: JSONContent
|
||||
createdTime: number
|
||||
|
||||
// Denormalized, for rendering comments
|
||||
|
|
|
@ -10,6 +10,7 @@ import { User } from 'common/user'
|
|||
import { Col } from './layout/col'
|
||||
import { Linkify } from './linkify'
|
||||
import { groupBy } from 'lodash'
|
||||
import { Content } from './editor'
|
||||
|
||||
export function UserCommentsList(props: {
|
||||
user: User
|
||||
|
@ -50,7 +51,8 @@ export function UserCommentsList(props: {
|
|||
|
||||
function ProfileComment(props: { comment: Comment; className?: string }) {
|
||||
const { comment, className } = props
|
||||
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
|
||||
const { text, content, userUsername, userName, userAvatarUrl, createdTime } =
|
||||
comment
|
||||
// TODO: find and attach relevant bets by comment betId at some point
|
||||
return (
|
||||
<Row className={className}>
|
||||
|
@ -64,7 +66,7 @@ function ProfileComment(props: { comment: Comment; className?: string }) {
|
|||
/>{' '}
|
||||
<RelativeTimestamp time={createdTime} />
|
||||
</p>
|
||||
<Linkify text={text} />
|
||||
<Content content={content || text} />
|
||||
</div>
|
||||
</Row>
|
||||
)
|
||||
|
|
|
@ -41,14 +41,16 @@ export function useTextEditor(props: {
|
|||
max?: number
|
||||
defaultValue?: Content
|
||||
disabled?: boolean
|
||||
simple?: boolean
|
||||
}) {
|
||||
const { placeholder, max, defaultValue = '', disabled } = props
|
||||
const { placeholder, max, defaultValue = '', disabled, simple } = props
|
||||
|
||||
const users = useUsers()
|
||||
|
||||
const editorClass = clsx(
|
||||
proseClass,
|
||||
'min-h-[6em] resize-none outline-none border-none pt-3 px-4 focus:ring-0'
|
||||
!simple && 'min-h-[6em]',
|
||||
'resize-none outline-none border-none pt-3 px-4 focus:ring-0'
|
||||
)
|
||||
|
||||
const editor = useEditor(
|
||||
|
@ -56,7 +58,8 @@ export function useTextEditor(props: {
|
|||
editorProps: { attributes: { class: editorClass } },
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
heading: { levels: [1, 2, 3] },
|
||||
heading: simple ? false : { levels: [1, 2, 3] },
|
||||
horizontalRule: simple ? false : {},
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder,
|
||||
|
|
|
@ -72,7 +72,7 @@ export function FeedAnswerCommentGroup(props: {
|
|||
(comment?: Comment, answer?: Answer) => {
|
||||
setReplyToUsername(comment?.userUsername ?? answer?.username ?? '')
|
||||
setShowReply(true)
|
||||
inputRef?.focus()
|
||||
// TODO: focus
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -80,7 +80,7 @@ export function FeedAnswerCommentGroup(props: {
|
|||
// Only show one comment input for a bet at a time
|
||||
if (
|
||||
betsByCurrentUser.length > 1 &&
|
||||
inputRef?.textContent?.length === 0 &&
|
||||
// inputRef?.textContent?.length === 0 && //TODO: editor.isEmpty
|
||||
betsByCurrentUser.sort((a, b) => b.createdTime - a.createdTime)[0]
|
||||
?.outcome !== answer.number.toString()
|
||||
)
|
||||
|
@ -173,7 +173,6 @@ export function FeedAnswerCommentGroup(props: {
|
|||
commentsByCurrentUser={commentsByCurrentUser}
|
||||
parentAnswerOutcome={answer.number.toString()}
|
||||
replyToUsername={replyToUsername}
|
||||
setRef={setInputRef}
|
||||
onSubmitComment={() => {
|
||||
setShowReply(false)
|
||||
setReplyToUsername('')
|
||||
|
|
|
@ -19,8 +19,6 @@ import {
|
|||
createCommentOnContract,
|
||||
MAX_COMMENT_LENGTH,
|
||||
} from 'web/lib/firebase/comments'
|
||||
import Textarea from 'react-expanding-textarea'
|
||||
import { Linkify } from 'web/components/linkify'
|
||||
import { SiteLink } from 'web/components/site-link'
|
||||
import { BetStatusText } from 'web/components/feed/feed-bets'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
|
@ -28,10 +26,11 @@ import { getProbability } from 'common/calculate'
|
|||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||
import { PaperAirplaneIcon } from '@heroicons/react/outline'
|
||||
import { track } from 'web/lib/service/analytics'
|
||||
import { useEvent } from 'web/hooks/use-event'
|
||||
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, JSONContent } from '@tiptap/react'
|
||||
|
||||
export function FeedCommentThread(props: {
|
||||
contract: Contract
|
||||
|
@ -60,15 +59,13 @@ export function FeedCommentThread(props: {
|
|||
parentComment.id && comment.replyToCommentId === parentComment.id
|
||||
)
|
||||
commentsList.unshift(parentComment)
|
||||
const [inputRef, setInputRef] = useState<HTMLTextAreaElement | null>(null)
|
||||
|
||||
function scrollAndOpenReplyInput(comment: Comment) {
|
||||
setReplyToUsername(comment.userUsername)
|
||||
setShowReply(true)
|
||||
inputRef?.focus()
|
||||
//TODO focus
|
||||
}
|
||||
useEffect(() => {
|
||||
if (showReply && inputRef) inputRef.focus()
|
||||
}, [inputRef, showReply])
|
||||
|
||||
return (
|
||||
<Col className={'w-full gap-3 pr-1'}>
|
||||
<span
|
||||
|
@ -100,7 +97,6 @@ export function FeedCommentThread(props: {
|
|||
parentCommentId={parentComment.id}
|
||||
replyToUsername={replyToUsername}
|
||||
parentAnswerOutcome={comments[0].answerOutcome}
|
||||
setRef={setInputRef}
|
||||
onSubmitComment={() => {
|
||||
setShowReply(false)
|
||||
setReplyToUsername('')
|
||||
|
@ -195,7 +191,8 @@ export function FeedComment(props: {
|
|||
truncate,
|
||||
onReplyClick,
|
||||
} = props
|
||||
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
|
||||
const { text, content, userUsername, userName, userAvatarUrl, createdTime } =
|
||||
comment
|
||||
let betOutcome: string | undefined,
|
||||
bought: string | undefined,
|
||||
money: string | undefined
|
||||
|
@ -277,7 +274,7 @@ export function FeedComment(props: {
|
|||
/>
|
||||
</div>
|
||||
<TruncatedComment
|
||||
comment={text}
|
||||
comment={content || text}
|
||||
moreHref={contractPath(contract)}
|
||||
shouldTruncate={truncate}
|
||||
/>
|
||||
|
@ -346,7 +343,6 @@ export function CommentInput(props: {
|
|||
betsByCurrentUser: Bet[]
|
||||
commentsByCurrentUser: Comment[]
|
||||
replyToUsername?: string
|
||||
setRef?: (ref: HTMLTextAreaElement) => void
|
||||
// Reply to a free response answer
|
||||
parentAnswerOutcome?: string
|
||||
// Reply to another comment
|
||||
|
@ -361,10 +357,16 @@ export function CommentInput(props: {
|
|||
parentCommentId,
|
||||
replyToUsername,
|
||||
onSubmitComment,
|
||||
setRef,
|
||||
} = props
|
||||
const user = useUser()
|
||||
const [comment, setComment] = useState('')
|
||||
const { editor, upload } = useTextEditor({
|
||||
simple: true,
|
||||
max: MAX_COMMENT_LENGTH,
|
||||
placeholder:
|
||||
!!parentCommentId || !!parentAnswerOutcome
|
||||
? 'Write a reply...'
|
||||
: 'Write a comment...',
|
||||
})
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
const mostRecentCommentableBet = getMostRecentCommentableBet(
|
||||
|
@ -380,18 +382,17 @@ export function CommentInput(props: {
|
|||
track('sign in to comment')
|
||||
return await firebaseLogin()
|
||||
}
|
||||
if (!comment || isSubmitting) return
|
||||
if (!editor || editor.isEmpty || isSubmitting) return
|
||||
setIsSubmitting(true)
|
||||
await createCommentOnContract(
|
||||
contract.id,
|
||||
comment,
|
||||
editor.getJSON(),
|
||||
user,
|
||||
betId,
|
||||
parentAnswerOutcome,
|
||||
parentCommentId
|
||||
)
|
||||
onSubmitComment?.()
|
||||
setComment('')
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
|
@ -446,14 +447,12 @@ export function CommentInput(props: {
|
|||
)}
|
||||
</div>
|
||||
<CommentInputTextArea
|
||||
commentText={comment}
|
||||
setComment={setComment}
|
||||
isReply={!!parentCommentId || !!parentAnswerOutcome}
|
||||
editor={editor}
|
||||
upload={upload}
|
||||
replyToUsername={replyToUsername ?? ''}
|
||||
user={user}
|
||||
submitComment={submitComment}
|
||||
isSubmitting={isSubmitting}
|
||||
setRef={setRef}
|
||||
presetId={id}
|
||||
/>
|
||||
</div>
|
||||
|
@ -465,81 +464,43 @@ export function CommentInput(props: {
|
|||
|
||||
export function CommentInputTextArea(props: {
|
||||
user: User | undefined | null
|
||||
isReply: boolean
|
||||
replyToUsername: string
|
||||
commentText: string
|
||||
setComment: (text: string) => void
|
||||
editor: Editor | null
|
||||
upload: any
|
||||
submitComment: (id?: string) => void
|
||||
isSubmitting: boolean
|
||||
setRef?: (ref: HTMLTextAreaElement) => void
|
||||
presetId?: string
|
||||
enterToSubmitOnDesktop?: boolean
|
||||
}) {
|
||||
const {
|
||||
isReply,
|
||||
setRef,
|
||||
user,
|
||||
commentText,
|
||||
setComment,
|
||||
editor,
|
||||
upload,
|
||||
submitComment,
|
||||
presetId,
|
||||
isSubmitting,
|
||||
replyToUsername,
|
||||
enterToSubmitOnDesktop,
|
||||
} = props
|
||||
const { width } = useWindowSize()
|
||||
const memoizedSetComment = useEvent(setComment)
|
||||
useEffect(() => {
|
||||
if (!replyToUsername || !user || replyToUsername === user.username) return
|
||||
const replacement = `@${replyToUsername} `
|
||||
memoizedSetComment(replacement + commentText.replace(replacement, ''))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user, replyToUsername, memoizedSetComment])
|
||||
editor?.setEditable(!isSubmitting)
|
||||
}, [isSubmitting, editor])
|
||||
|
||||
// TODO: make at mention show up at beginning
|
||||
return (
|
||||
<>
|
||||
<Row className="gap-1.5 text-gray-700">
|
||||
<Textarea
|
||||
ref={setRef}
|
||||
value={commentText}
|
||||
onChange={(e) => setComment(e.target.value)}
|
||||
className={clsx('textarea textarea-bordered w-full resize-none')}
|
||||
// Make room for floating submit button.
|
||||
style={{ paddingRight: 48 }}
|
||||
placeholder={
|
||||
isReply
|
||||
? 'Write a reply... '
|
||||
: enterToSubmitOnDesktop
|
||||
? 'Send a message'
|
||||
: 'Write a comment...'
|
||||
}
|
||||
autoFocus={false}
|
||||
maxLength={MAX_COMMENT_LENGTH}
|
||||
disabled={isSubmitting}
|
||||
onKeyDown={(e) => {
|
||||
if (
|
||||
(enterToSubmitOnDesktop &&
|
||||
e.key === 'Enter' &&
|
||||
!e.shiftKey &&
|
||||
width &&
|
||||
width > 768) ||
|
||||
(e.key === 'Enter' && (e.ctrlKey || e.metaKey))
|
||||
) {
|
||||
e.preventDefault()
|
||||
submitComment(presetId)
|
||||
e.currentTarget.blur()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextEditor editor={editor} upload={upload} />
|
||||
<Col className={clsx('relative justify-end')}>
|
||||
{user && !isSubmitting && (
|
||||
<button
|
||||
className={clsx(
|
||||
'btn btn-ghost btn-sm absolute right-2 bottom-2 flex-row pl-2 capitalize',
|
||||
!commentText && 'pointer-events-none text-gray-500'
|
||||
(!editor || editor.isEmpty) &&
|
||||
'pointer-events-none text-gray-500'
|
||||
)}
|
||||
onClick={() => {
|
||||
submitComment(presetId)
|
||||
editor?.commands.clearContent()
|
||||
}}
|
||||
>
|
||||
<PaperAirplaneIcon
|
||||
|
@ -568,28 +529,22 @@ export function CommentInputTextArea(props: {
|
|||
}
|
||||
|
||||
export function TruncatedComment(props: {
|
||||
comment: string
|
||||
comment: JSONContent
|
||||
moreHref: string
|
||||
shouldTruncate?: boolean
|
||||
}) {
|
||||
const { comment, moreHref, shouldTruncate } = props
|
||||
let truncated = comment
|
||||
|
||||
// Keep descriptions to at most 400 characters
|
||||
// TODO: Keep descriptions to at most 80 words (~400 characters)
|
||||
const MAX_CHARS = 400
|
||||
if (shouldTruncate && truncated.length > MAX_CHARS) {
|
||||
truncated = truncated.slice(0, MAX_CHARS)
|
||||
// Make sure to end on a space
|
||||
const i = truncated.lastIndexOf(' ')
|
||||
truncated = truncated.slice(0, i)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="mt-2 whitespace-pre-line break-words text-gray-700"
|
||||
style={{ fontSize: 15 }}
|
||||
>
|
||||
<Linkify text={truncated} />
|
||||
<Content content={comment} />
|
||||
{truncated != comment && (
|
||||
<SiteLink href={moreHref} className="text-indigo-700">
|
||||
... (show more)
|
||||
|
|
|
@ -23,6 +23,7 @@ import { Tipper } from 'web/components/tipper'
|
|||
import { sum } from 'lodash'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||
import { useTextEditor } from '../editor'
|
||||
|
||||
export function GroupChat(props: {
|
||||
messages: Comment[]
|
||||
|
@ -31,7 +32,10 @@ export function GroupChat(props: {
|
|||
tips: CommentTipMap
|
||||
}) {
|
||||
const { messages, user, group, tips } = props
|
||||
const [messageText, setMessageText] = useState('')
|
||||
const { editor, upload } = useTextEditor({
|
||||
simple: true,
|
||||
placeholder: 'Send a message',
|
||||
})
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [scrollToBottomRef, setScrollToBottomRef] =
|
||||
useState<HTMLDivElement | null>(null)
|
||||
|
@ -39,30 +43,30 @@ export function GroupChat(props: {
|
|||
const [scrollToMessageRef, setScrollToMessageRef] =
|
||||
useState<HTMLDivElement | null>(null)
|
||||
const [replyToUsername, setReplyToUsername] = useState('')
|
||||
const [inputRef, setInputRef] = useState<HTMLTextAreaElement | null>(null)
|
||||
const [groupedMessages, setGroupedMessages] = useState<Comment[]>([])
|
||||
|
||||
const router = useRouter()
|
||||
const isMember = user && group.memberIds.includes(user?.id)
|
||||
|
||||
useMemo(() => {
|
||||
// array of groups, where each group is an array of messages that are displayed as one
|
||||
const groupedMessages = useMemo(() => {
|
||||
// Group messages with createdTime within 2 minutes of each other.
|
||||
const tempMessages = []
|
||||
const tempGrouped: Comment[][] = []
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages[i]
|
||||
if (i === 0) tempMessages.push({ ...message })
|
||||
if (i === 0) tempGrouped.push([message])
|
||||
else {
|
||||
const prevMessage = messages[i - 1]
|
||||
const diff = message.createdTime - prevMessage.createdTime
|
||||
const creatorsMatch = message.userId === prevMessage.userId
|
||||
if (diff < 2 * 60 * 1000 && creatorsMatch) {
|
||||
tempMessages[tempMessages.length - 1].text += `\n${message.text}`
|
||||
tempGrouped.at(-1)?.push(message)
|
||||
} else {
|
||||
tempMessages.push({ ...message })
|
||||
tempGrouped.push([message])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setGroupedMessages(tempMessages)
|
||||
return tempGrouped
|
||||
}, [messages])
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -90,16 +94,16 @@ export function GroupChat(props: {
|
|||
track('sign in to comment')
|
||||
return await firebaseLogin()
|
||||
}
|
||||
if (!messageText || isSubmitting) return
|
||||
if (!editor || editor.isEmpty || isSubmitting) return
|
||||
setIsSubmitting(true)
|
||||
await createCommentOnGroup(group.id, messageText, user)
|
||||
setMessageText('')
|
||||
await createCommentOnGroup(group.id, editor.getJSON(), user)
|
||||
editor.commands.clearContent()
|
||||
setIsSubmitting(false)
|
||||
setReplyToUsername('')
|
||||
inputRef?.focus()
|
||||
focusInput()
|
||||
}
|
||||
function focusInput() {
|
||||
inputRef?.focus()
|
||||
editor?.commands.focus()
|
||||
}
|
||||
|
||||
const { width, height } = useWindowSize()
|
||||
|
@ -119,20 +123,20 @@ export function GroupChat(props: {
|
|||
}
|
||||
ref={setScrollToBottomRef}
|
||||
>
|
||||
{groupedMessages.map((message) => (
|
||||
{groupedMessages.map((messages) => (
|
||||
<GroupMessage
|
||||
user={user}
|
||||
key={message.id}
|
||||
comment={message}
|
||||
key={`group ${messages[0].id}`}
|
||||
comments={messages}
|
||||
group={group}
|
||||
onReplyClick={onReplyClick}
|
||||
highlight={message.id === scrollToMessageId}
|
||||
highlight={messages[0].id === scrollToMessageId}
|
||||
setRef={
|
||||
scrollToMessageId === message.id
|
||||
scrollToMessageId === messages[0].id
|
||||
? setScrollToMessageRef
|
||||
: undefined
|
||||
}
|
||||
tips={tips[message.id] ?? {}}
|
||||
tips={tips[messages[0].id] ?? {}}
|
||||
/>
|
||||
))}
|
||||
{messages.length === 0 && (
|
||||
|
@ -140,7 +144,7 @@ export function GroupChat(props: {
|
|||
No messages yet. Why not{isMember ? ` ` : ' join and '}
|
||||
<button
|
||||
className={'cursor-pointer font-bold text-gray-700'}
|
||||
onClick={() => focusInput()}
|
||||
onClick={focusInput}
|
||||
>
|
||||
add one?
|
||||
</button>
|
||||
|
@ -158,15 +162,12 @@ export function GroupChat(props: {
|
|||
</div>
|
||||
<div className={'flex-1'}>
|
||||
<CommentInputTextArea
|
||||
commentText={messageText}
|
||||
setComment={setMessageText}
|
||||
isReply={false}
|
||||
editor={editor}
|
||||
upload={upload}
|
||||
user={user}
|
||||
replyToUsername={replyToUsername}
|
||||
submitComment={submitMessage}
|
||||
isSubmitting={isSubmitting}
|
||||
enterToSubmitOnDesktop={true}
|
||||
setRef={setInputRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -177,16 +178,18 @@ export function GroupChat(props: {
|
|||
|
||||
const GroupMessage = memo(function GroupMessage_(props: {
|
||||
user: User | null | undefined
|
||||
comment: Comment
|
||||
comments: Comment[]
|
||||
group: Group
|
||||
onReplyClick?: (comment: Comment) => void
|
||||
setRef?: (ref: HTMLDivElement) => void
|
||||
highlight?: boolean
|
||||
tips: CommentTips
|
||||
}) {
|
||||
const { comment, onReplyClick, group, setRef, highlight, user, tips } = props
|
||||
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
|
||||
const isCreatorsComment = user && comment.userId === user.id
|
||||
const { comments, onReplyClick, group, setRef, highlight, user, tips } = props
|
||||
const first = comments[0]
|
||||
const { id, userUsername, userName, userAvatarUrl, createdTime } = first
|
||||
|
||||
const isCreatorsComment = user && comments[0].userId === user.id
|
||||
return (
|
||||
<Col
|
||||
ref={setRef}
|
||||
|
@ -216,15 +219,17 @@ const GroupMessage = memo(function GroupMessage_(props: {
|
|||
prefix={'group'}
|
||||
slug={group.slug}
|
||||
createdTime={createdTime}
|
||||
elementId={comment.id}
|
||||
elementId={id}
|
||||
/>
|
||||
</Row>
|
||||
<Row className={'text-black'}>
|
||||
<TruncatedComment
|
||||
comment={text}
|
||||
moreHref={groupPath(group.slug)}
|
||||
shouldTruncate={false}
|
||||
/>
|
||||
{comments.map((comment) => (
|
||||
<TruncatedComment
|
||||
comment={comment.content} // TODO: || comment.text
|
||||
moreHref={groupPath(group.slug)}
|
||||
shouldTruncate={false}
|
||||
/>
|
||||
))}
|
||||
</Row>
|
||||
<Row>
|
||||
{!isCreatorsComment && onReplyClick && (
|
||||
|
@ -232,7 +237,7 @@ const GroupMessage = memo(function GroupMessage_(props: {
|
|||
className={
|
||||
'self-start py-1 text-xs font-bold text-gray-500 hover:underline'
|
||||
}
|
||||
onClick={() => onReplyClick(comment)}
|
||||
onClick={() => onReplyClick(first)}
|
||||
>
|
||||
Reply
|
||||
</button>
|
||||
|
@ -242,7 +247,7 @@ const GroupMessage = memo(function GroupMessage_(props: {
|
|||
{formatMoney(sum(Object.values(tips)))}
|
||||
</span>
|
||||
)}
|
||||
{!isCreatorsComment && <Tipper comment={comment} tips={tips} />}
|
||||
{!isCreatorsComment && <Tipper comment={first} tips={tips} />}
|
||||
</Row>
|
||||
</Col>
|
||||
)
|
||||
|
|
|
@ -14,6 +14,7 @@ import { User } from 'common/user'
|
|||
import { Comment } from 'common/comment'
|
||||
import { removeUndefinedProps } from 'common/util/object'
|
||||
import { track } from '@amplitude/analytics-browser'
|
||||
import { JSONContent } from '@tiptap/react'
|
||||
|
||||
export type { Comment }
|
||||
|
||||
|
@ -21,7 +22,7 @@ export const MAX_COMMENT_LENGTH = 10000
|
|||
|
||||
export async function createCommentOnContract(
|
||||
contractId: string,
|
||||
text: string,
|
||||
content: JSONContent,
|
||||
commenter: User,
|
||||
betId?: string,
|
||||
answerOutcome?: string,
|
||||
|
@ -34,7 +35,7 @@ export async function createCommentOnContract(
|
|||
id: ref.id,
|
||||
contractId,
|
||||
userId: commenter.id,
|
||||
text: text.slice(0, MAX_COMMENT_LENGTH),
|
||||
content: content,
|
||||
createdTime: Date.now(),
|
||||
userName: commenter.name,
|
||||
userUsername: commenter.username,
|
||||
|
@ -53,7 +54,7 @@ export async function createCommentOnContract(
|
|||
}
|
||||
export async function createCommentOnGroup(
|
||||
groupId: string,
|
||||
text: string,
|
||||
content: JSONContent,
|
||||
user: User,
|
||||
replyToCommentId?: string
|
||||
) {
|
||||
|
@ -62,7 +63,7 @@ export async function createCommentOnGroup(
|
|||
id: ref.id,
|
||||
groupId,
|
||||
userId: user.id,
|
||||
text: text.slice(0, MAX_COMMENT_LENGTH),
|
||||
content: content,
|
||||
createdTime: Date.now(),
|
||||
userName: user.name,
|
||||
userUsername: user.username,
|
||||
|
|
Loading…
Reference in New Issue
Block a user