9eff69be75
* /dream api: Upload StableDiffusion image to Firestore * Minor tweaks * Set content type on uploaded image This makes it so the image doesn't auto-download when opened in a new tab * Allow users to dream directly from within Manifold * Remove unused import * Implement a /comment endpoint which supports html and markdown * Upgrade @tiptap/core to latest * Update all tiptap deps to beta.199 * Add @tiptap/suggestion * Import @tiptap/html in the right place * ... add deps everywhere So I have no idea how common deps work apparently * Add tiptap/suggestion too * Clean up dream * More cleanups * Rework /comment endpoint * Move API to /comment * Change imports in case that matters * Add a couple todos * Dynamically import micromark * Parallellize gsutil with -m option * Adding comments via api working, editor.tsx erroring out * Unused import * Remove disabled state from useTextEditor Co-authored-by: Ian Philips <iansphilips@gmail.com>
106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
import * as admin from 'firebase-admin'
|
|
|
|
import { getContract, getUser, log } from './utils'
|
|
import { APIError, newEndpoint, validate } from './api'
|
|
import { JSONContent } from '@tiptap/core'
|
|
import { z } from 'zod'
|
|
import { removeUndefinedProps } from '../../common/util/object'
|
|
import { htmlToRichText } from '../../common/util/parse'
|
|
import { marked } from 'marked'
|
|
|
|
const contentSchema: z.ZodType<JSONContent> = z.lazy(() =>
|
|
z.intersection(
|
|
z.record(z.any()),
|
|
z.object({
|
|
type: z.string().optional(),
|
|
attrs: z.record(z.any()).optional(),
|
|
content: z.array(contentSchema).optional(),
|
|
marks: z
|
|
.array(
|
|
z.intersection(
|
|
z.record(z.any()),
|
|
z.object({
|
|
type: z.string(),
|
|
attrs: z.record(z.any()).optional(),
|
|
})
|
|
)
|
|
)
|
|
.optional(),
|
|
text: z.string().optional(),
|
|
})
|
|
)
|
|
)
|
|
|
|
const postSchema = z.object({
|
|
contractId: z.string(),
|
|
content: contentSchema.optional(),
|
|
html: z.string().optional(),
|
|
markdown: z.string().optional(),
|
|
})
|
|
|
|
const MAX_COMMENT_JSON_LENGTH = 20000
|
|
|
|
// For now, only supports creating a new top-level comment on a contract.
|
|
// Replies, posts, chats are not supported yet.
|
|
export const createcomment = newEndpoint({}, async (req, auth) => {
|
|
const firestore = admin.firestore()
|
|
const { contractId, content, html, markdown } = validate(postSchema, req.body)
|
|
|
|
const creator = await getUser(auth.uid)
|
|
const contract = await getContract(contractId)
|
|
|
|
if (!creator) {
|
|
throw new APIError(400, 'No user exists with the authenticated user ID.')
|
|
}
|
|
if (!contract) {
|
|
throw new APIError(400, 'No contract exists with the given ID.')
|
|
}
|
|
|
|
let contentJson = null
|
|
if (content) {
|
|
contentJson = content
|
|
} else if (html) {
|
|
console.log('html', html)
|
|
contentJson = htmlToRichText(html)
|
|
} else if (markdown) {
|
|
const markedParse = marked.parse(markdown)
|
|
log('parsed', markedParse)
|
|
contentJson = htmlToRichText(markedParse)
|
|
log('json', contentJson)
|
|
}
|
|
|
|
if (!contentJson) {
|
|
throw new APIError(400, 'No comment content provided.')
|
|
}
|
|
|
|
if (JSON.stringify(contentJson).length > MAX_COMMENT_JSON_LENGTH) {
|
|
throw new APIError(
|
|
400,
|
|
`Comment is too long; should be less than ${MAX_COMMENT_JSON_LENGTH} as a JSON string.`
|
|
)
|
|
}
|
|
|
|
const ref = firestore.collection(`contracts/${contractId}/comments`).doc()
|
|
|
|
const comment = removeUndefinedProps({
|
|
id: ref.id,
|
|
content: contentJson,
|
|
createdTime: Date.now(),
|
|
|
|
userId: creator.id,
|
|
userName: creator.name,
|
|
userUsername: creator.username,
|
|
userAvatarUrl: creator.avatarUrl,
|
|
|
|
// OnContract fields
|
|
commentType: 'contract',
|
|
contractId: contractId,
|
|
contractSlug: contract.slug,
|
|
contractQuestion: contract.question,
|
|
})
|
|
|
|
await ref.set(comment)
|
|
|
|
return { status: 'success', comment }
|
|
})
|