feat: support for posting anonymous comment
This commit is contained in:
parent
a5690eb03b
commit
99a1cadc09
|
|
@ -2,36 +2,39 @@ import { createContract } from "crossbell"
|
|||
import { NextResponse } from "next/server"
|
||||
|
||||
import { ANONYMOUS_ACCOUNT_PRIVATEKEY } from "~/lib/env.server"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { NextServerResponse } from "~/lib/server-helper"
|
||||
|
||||
const contract = createContract(ANONYMOUS_ACCOUNT_PRIVATEKEY)
|
||||
|
||||
// /api/anonymous/comment?target=1-1&content=hello&name=Me
|
||||
export async function GET(req: Request): Promise<Response> {
|
||||
const { target, content, name } = getQuery(req)
|
||||
// /api/anonymous/comment
|
||||
export async function POST(req: Request): Promise<Response> {
|
||||
const { targetCharacterId, targetNoteId, content, name, email } =
|
||||
await req.json()
|
||||
|
||||
if (!target || !content) {
|
||||
return NextResponse.json({ error: "Missing target or content" })
|
||||
if (!targetCharacterId || !targetNoteId || !content || !name || !email) {
|
||||
return NextResponse.json({ error: "Missing required fields" })
|
||||
}
|
||||
const res = new NextServerResponse()
|
||||
|
||||
const { data } = await contract.note.postForNote({
|
||||
targetCharacterId: target.split("-")[0],
|
||||
targetNoteId: target.split("-")[1],
|
||||
targetCharacterId,
|
||||
targetNoteId,
|
||||
characterId: 56592,
|
||||
metadataOrUri: {
|
||||
tags: ["comment"],
|
||||
sources: ["xlog"],
|
||||
content: content,
|
||||
},
|
||||
...(name && {
|
||||
content,
|
||||
attributes: [
|
||||
{
|
||||
trait_type: "xlog_display_name",
|
||||
trait_type: "xlog_sender_name",
|
||||
value: name,
|
||||
},
|
||||
{
|
||||
trait_type: "xlog_sender_email",
|
||||
value: email,
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
return res.status(200).json({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { CharacterEntity, NoteEntity } from "crossbell"
|
||||
import { useCallback, useEffect, useRef } from "react"
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
|
||||
import { EditorView } from "@codemirror/view"
|
||||
|
|
@ -11,7 +11,11 @@ import { Button } from "~/components/ui/Button"
|
|||
import { editorUpload } from "~/editor/Multimedia"
|
||||
import { useUploadFile } from "~/hooks/useUploadFile"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { useCommentPage, useUpdateComment } from "~/queries/page"
|
||||
import {
|
||||
useAnonymousComment,
|
||||
useCommentPage,
|
||||
useUpdateComment,
|
||||
} from "~/queries/page"
|
||||
|
||||
import { CodeMirrorEditor } from "../ui/CodeMirror"
|
||||
import { Input } from "../ui/Input"
|
||||
|
|
@ -38,6 +42,8 @@ export const CommentInput = ({
|
|||
const commentPage = useCommentPage()
|
||||
const updateComment = useUpdateComment()
|
||||
const { t } = useTranslation("site")
|
||||
const anonymousComment = useAnonymousComment()
|
||||
const [anonymous, setAnonymous] = useState(false)
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
|
|
@ -45,6 +51,16 @@ export const CommentInput = ({
|
|||
},
|
||||
})
|
||||
|
||||
const anonymousForm = useForm({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
email: "",
|
||||
} as {
|
||||
name: string
|
||||
email: string
|
||||
},
|
||||
})
|
||||
|
||||
const inputContent = form.watch("content").trim()
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (values) => {
|
||||
|
|
@ -61,24 +77,52 @@ export const CommentInput = ({
|
|||
})
|
||||
}
|
||||
} else {
|
||||
commentPage.mutate({
|
||||
characterId,
|
||||
noteId,
|
||||
content: values.content,
|
||||
externalUrl: window.location.href,
|
||||
originalCharacterId,
|
||||
originalNoteId,
|
||||
})
|
||||
if (anonymous) {
|
||||
if (
|
||||
values.content &&
|
||||
anonymousForm.getValues("name") &&
|
||||
anonymousForm.getValues("email")
|
||||
) {
|
||||
anonymousComment.mutate({
|
||||
targetCharacterId: characterId,
|
||||
targetNoteId: noteId,
|
||||
content: values.content,
|
||||
name: anonymousForm.getValues("name"),
|
||||
email: anonymousForm.getValues("email"),
|
||||
originalCharacterId,
|
||||
originalNoteId,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
commentPage.mutate({
|
||||
characterId,
|
||||
noteId,
|
||||
content: values.content,
|
||||
externalUrl: window.location.href,
|
||||
originalCharacterId,
|
||||
originalNoteId,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (commentPage.isSuccess || updateComment.isSuccess) {
|
||||
if (
|
||||
commentPage.isSuccess ||
|
||||
updateComment.isSuccess ||
|
||||
anonymousComment.isSuccess
|
||||
) {
|
||||
form.reset()
|
||||
onSubmitted?.()
|
||||
}
|
||||
}, [commentPage.isSuccess, updateComment.isSuccess, form, onSubmitted])
|
||||
}, [
|
||||
commentPage.isSuccess,
|
||||
updateComment.isSuccess,
|
||||
anonymousComment.isSuccess,
|
||||
form,
|
||||
onSubmitted,
|
||||
])
|
||||
|
||||
const cmViewRef = useRef<EditorView>()
|
||||
const onCreateEditor = useCallback((view: EditorView) => {
|
||||
|
|
@ -112,6 +156,33 @@ export const CommentInput = ({
|
|||
),
|
||||
[account?.character, t, form],
|
||||
)
|
||||
|
||||
let submitText = "Connect"
|
||||
if (anonymous) {
|
||||
submitText = "Submit"
|
||||
} else if (account) {
|
||||
if (!account.character) {
|
||||
submitText = "Create Character"
|
||||
} else if (comment) {
|
||||
submitText = "Confirm Modification"
|
||||
} else {
|
||||
submitText = "Submit"
|
||||
}
|
||||
}
|
||||
|
||||
let submitDisabled = false
|
||||
const name = anonymousForm.watch("name").trim()
|
||||
const email = anonymousForm.watch("email").trim()
|
||||
if (account?.character) {
|
||||
if (!inputContent || inputContent === comment?.metadata?.content?.content) {
|
||||
submitDisabled = true
|
||||
}
|
||||
} else if (anonymous) {
|
||||
if (!name || !email || !inputContent) {
|
||||
submitDisabled = true
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="xlog-comment-input flex">
|
||||
<Avatar
|
||||
|
|
@ -129,7 +200,7 @@ export const CommentInput = ({
|
|||
form.setValue("content", val)
|
||||
}}
|
||||
handleDropFile={handleDropFile}
|
||||
className="mb-2 p-3 h-[74px] border focus-within:border-accent border-[var(--border-color)] rounded-lg outline-2 outline-transparent cursor-text"
|
||||
className="mb-2 p-3 h-[74px] border focus-within:border-accent border-[var(--border-color)] rounded-lg outline-2 outline-transparent cursor-text transition-colors"
|
||||
placeholder={t("Write a comment on the blockchain") || ""}
|
||||
maxLength={600}
|
||||
onCreateEditor={onCreateEditor}
|
||||
|
|
@ -170,25 +241,49 @@ export const CommentInput = ({
|
|||
</>
|
||||
)}
|
||||
</Popover>
|
||||
<Button
|
||||
type="submit"
|
||||
isLoading={commentPage.isLoading || updateComment.isLoading}
|
||||
isDisabled={
|
||||
!!account?.character &&
|
||||
(!inputContent ||
|
||||
inputContent === comment?.metadata?.content?.content)
|
||||
}
|
||||
>
|
||||
{t(
|
||||
account
|
||||
? !account.character
|
||||
? "Create Character"
|
||||
: comment
|
||||
? "Confirm Modification"
|
||||
: "Submit"
|
||||
: "Connect",
|
||||
<div className="flex items-center relative">
|
||||
{!account && (
|
||||
<div>
|
||||
<div className="mr-6 flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="anonymous"
|
||||
name="anonymous"
|
||||
checked={anonymous}
|
||||
onChange={(e) => setAnonymous(e.target.checked)}
|
||||
/>
|
||||
<label className="text-gray-500 pl-1" htmlFor="anonymous">
|
||||
{t("Comment Without Login")}
|
||||
</label>
|
||||
</div>
|
||||
{anonymous && (
|
||||
<div className="absolute right-0 top-full border rounded-lg px-6 pt-4 pb-5 space-y-2 bg-white z-10 mt-4">
|
||||
<Input
|
||||
label={t("Name") || ""}
|
||||
id="name"
|
||||
{...anonymousForm.register("name")}
|
||||
/>
|
||||
<Input
|
||||
label={t("Email") || ""}
|
||||
id="email"
|
||||
{...anonymousForm.register("email")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
isLoading={
|
||||
commentPage.isLoading ||
|
||||
updateComment.isLoading ||
|
||||
anonymousComment.isLoading
|
||||
}
|
||||
isDisabled={submitDisabled}
|
||||
>
|
||||
{t(submitText)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -84,5 +84,8 @@
|
|||
"Search for your interest": "搜索你感兴趣的内容",
|
||||
"results": "条结果",
|
||||
"My xLog": "我的 xLog",
|
||||
"Click to select files": "点击选择文件"
|
||||
"Click to select files": "点击选择文件",
|
||||
"Comment Without Login": "免登录评论",
|
||||
"Name": "名称",
|
||||
"Email": "邮箱"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -754,6 +754,28 @@ export async function getComments({
|
|||
return res
|
||||
}
|
||||
|
||||
export async function anonymousComment(input: {
|
||||
targetCharacterId: number
|
||||
targetNoteId: number
|
||||
content: string
|
||||
name: string
|
||||
email: string
|
||||
}) {
|
||||
return await fetch("/api/anonymous/comment", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
targetCharacterId: input.targetCharacterId,
|
||||
targetNoteId: input.targetNoteId,
|
||||
content: input.content,
|
||||
name: input.name,
|
||||
email: input.email,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateComment(
|
||||
{
|
||||
content,
|
||||
|
|
|
|||
|
|
@ -340,6 +340,30 @@ export function useCommentPage() {
|
|||
}
|
||||
}
|
||||
|
||||
export function useAnonymousComment() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation(
|
||||
async (
|
||||
payload: Parameters<typeof pageModel.anonymousComment>[0] & {
|
||||
originalNoteId?: number
|
||||
originalCharacterId?: number
|
||||
},
|
||||
) => {
|
||||
return pageModel.anonymousComment(payload)
|
||||
},
|
||||
{
|
||||
onSuccess: (data, variables) => {
|
||||
queryClient.invalidateQueries([
|
||||
"getComments",
|
||||
variables.originalCharacterId || variables.targetCharacterId,
|
||||
variables.originalNoteId || variables.targetNoteId,
|
||||
])
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export function useUpdateComment() {
|
||||
const queryClient = useQueryClient()
|
||||
const contract = useContract()
|
||||
|
|
|
|||
Loading…
Reference in New Issue