feat: unified editor data converter
This commit is contained in:
parent
215bd8fbc0
commit
3bf8c1d3ba
|
|
@ -17,20 +17,17 @@ import EditorExternalUrl from "~/components/dashboard/editor-properties/EditorEx
|
|||
import EditorPublishAt from "~/components/dashboard/editor-properties/EditorPublishAt"
|
||||
import EditorTitle from "~/components/dashboard/editor-properties/EditorTitle"
|
||||
import { useModalStack } from "~/components/ui/ModalStack"
|
||||
import {
|
||||
Values,
|
||||
initialEditorState,
|
||||
useEditorState,
|
||||
} from "~/hooks/useEditorState"
|
||||
import { initialEditorState, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useGetState } from "~/hooks/useGetState"
|
||||
import { useBeforeMounted } from "~/hooks/useSyncOnce"
|
||||
import { showConfetti } from "~/lib/confetti"
|
||||
import { crossbell2Editor } from "~/lib/editor-converter"
|
||||
import { CSB_SCAN } from "~/lib/env"
|
||||
import { getTwitterShareUrl } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { delStorage, setStorage } from "~/lib/storage"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { EditorValues, PageVisibilityEnum } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
import {
|
||||
useCreatePage,
|
||||
|
|
@ -107,7 +104,7 @@ export default function PortfolioEditor() {
|
|||
|
||||
const getValues = useGetState(values)
|
||||
const updateValue = useCallback(
|
||||
(val: Partial<Values>) => {
|
||||
(val: Partial<EditorValues>) => {
|
||||
if (visibility !== PageVisibilityEnum.Draft) {
|
||||
setVisibility(PageVisibilityEnum.Modified)
|
||||
}
|
||||
|
|
@ -232,19 +229,7 @@ export default function PortfolioEditor() {
|
|||
// Init
|
||||
useEffect(() => {
|
||||
if (!page.data?.metadata || !draftKey) return
|
||||
useEditorState.setState({
|
||||
published: !!page.data.noteId,
|
||||
title: page.data.metadata?.content?.title || "",
|
||||
publishedAt: page.data.metadata?.content?.date_published,
|
||||
excerpt: page.data.metadata?.content?.summary || "",
|
||||
cover: page.data.metadata?.content?.attachments?.find(
|
||||
(attachment) => attachment.name === "cover",
|
||||
) || {
|
||||
address: "",
|
||||
mime_type: "",
|
||||
},
|
||||
externalUrl: page.data.metadata?.content?.external_urls?.[0] || "",
|
||||
})
|
||||
useEditorState.setState(crossbell2Editor(page.data))
|
||||
}, [page.data, subdomain, draftKey, site.data?.characterId])
|
||||
|
||||
// Reset
|
||||
|
|
@ -315,7 +300,7 @@ export default function PortfolioEditor() {
|
|||
}
|
||||
|
||||
const EditorExtraProperties = memo(
|
||||
({ updateValue }: { updateValue: (val: Partial<Values>) => void }) => {
|
||||
({ updateValue }: { updateValue: (val: Partial<EditorValues>) => void }) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -22,23 +22,24 @@ import EditorSlug from "~/components/dashboard/editor-properties/EditorSlug"
|
|||
import EditorTags from "~/components/dashboard/editor-properties/EditorTags"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { useModalStack } from "~/components/ui/ModalStack"
|
||||
import {
|
||||
Values,
|
||||
initialEditorState,
|
||||
useEditorState,
|
||||
} from "~/hooks/useEditorState"
|
||||
import { initialEditorState, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useGetState } from "~/hooks/useGetState"
|
||||
import { useIsMobileLayout } from "~/hooks/useMobileLayout"
|
||||
import { useBeforeMounted } from "~/hooks/useSyncOnce"
|
||||
import { showConfetti } from "~/lib/confetti"
|
||||
import { RESERVED_TAGS } from "~/lib/constants"
|
||||
import { getDefaultSlug } from "~/lib/default-slug"
|
||||
import { crossbell2Editor } from "~/lib/editor-converter"
|
||||
import { CSB_SCAN } from "~/lib/env"
|
||||
import { getSiteLink, getTwitterShareUrl } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { delStorage, setStorage } from "~/lib/storage"
|
||||
import { ExpandedNote, NoteType, PageVisibilityEnum } from "~/lib/types"
|
||||
import {
|
||||
EditorValues,
|
||||
ExpandedNote,
|
||||
NoteType,
|
||||
PageVisibilityEnum,
|
||||
} from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { checkPageSlug } from "~/models/page.model"
|
||||
import {
|
||||
|
|
@ -132,7 +133,7 @@ export default function PostEditor() {
|
|||
const getDraftKey = useGetState(draftKey)
|
||||
|
||||
const updateValue = useCallback(
|
||||
(val: Partial<Values>) => {
|
||||
(val: Partial<EditorValues>) => {
|
||||
if (visibility !== PageVisibilityEnum.Draft) {
|
||||
setVisibility(PageVisibilityEnum.Modified)
|
||||
}
|
||||
|
|
@ -318,25 +319,7 @@ export default function PostEditor() {
|
|||
useEffect(() => {
|
||||
if (!page.data?.metadata?.content || !draftKey) return
|
||||
setInitialContent(page.data.metadata?.content?.content || "")
|
||||
useEditorState.setState({
|
||||
title: page.data.metadata?.content?.title || "",
|
||||
publishedAt: page.data.metadata?.content?.date_published,
|
||||
published: !!page.data.noteId,
|
||||
excerpt: page.data.metadata?.content?.summary || "",
|
||||
slug: page.data.metadata?.content?.slug || "",
|
||||
tags:
|
||||
page.data.metadata?.content?.tags
|
||||
?.filter((tag) => !RESERVED_TAGS.includes(tag))
|
||||
?.join(", ") || "",
|
||||
content: page.data.metadata?.content?.content || "",
|
||||
cover: page.data.metadata?.content?.attachments?.find(
|
||||
(attachment) => attachment.name === "cover",
|
||||
) || {
|
||||
address: "",
|
||||
mime_type: "",
|
||||
},
|
||||
disableAISummary: page.data.metadata?.content?.disableAISummary,
|
||||
})
|
||||
useEditorState.setState(crossbell2Editor(page.data))
|
||||
setDefaultSlug(
|
||||
getDefaultSlug(
|
||||
page.data.metadata?.content?.title || "",
|
||||
|
|
@ -533,7 +516,7 @@ const EditorExtraProperties = memo(
|
|||
characterId,
|
||||
siteLink,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
type: NoteType
|
||||
defaultSlug: string
|
||||
characterId?: number
|
||||
|
|
|
|||
|
|
@ -15,20 +15,17 @@ import EditorImages from "~/components/dashboard/editor-properties/EditorImages"
|
|||
import EditorPublishAt from "~/components/dashboard/editor-properties/EditorPublishAt"
|
||||
import EditorTitle from "~/components/dashboard/editor-properties/EditorTitle"
|
||||
import { useModalStack } from "~/components/ui/ModalStack"
|
||||
import {
|
||||
Values,
|
||||
initialEditorState,
|
||||
useEditorState,
|
||||
} from "~/hooks/useEditorState"
|
||||
import { initialEditorState, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useGetState } from "~/hooks/useGetState"
|
||||
import { useBeforeMounted } from "~/hooks/useSyncOnce"
|
||||
import { showConfetti } from "~/lib/confetti"
|
||||
import { crossbell2Editor } from "~/lib/editor-converter"
|
||||
import { CSB_SCAN } from "~/lib/env"
|
||||
import { getTwitterShareUrl } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { delStorage, setStorage } from "~/lib/storage"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { EditorValues, PageVisibilityEnum } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
import {
|
||||
useCreatePage,
|
||||
|
|
@ -105,7 +102,7 @@ export default function ShortEditor() {
|
|||
|
||||
const getValues = useGetState(values)
|
||||
const updateValue = useCallback(
|
||||
(val: Partial<Values>) => {
|
||||
(val: Partial<EditorValues>) => {
|
||||
if (visibility !== PageVisibilityEnum.Draft) {
|
||||
setVisibility(PageVisibilityEnum.Modified)
|
||||
}
|
||||
|
|
@ -230,16 +227,7 @@ export default function ShortEditor() {
|
|||
// Init
|
||||
useEffect(() => {
|
||||
if (!page.data?.metadata || !draftKey) return
|
||||
useEditorState.setState({
|
||||
published: !!page.data.noteId,
|
||||
title: page.data.metadata?.content?.title || "",
|
||||
publishedAt: page.data.metadata?.content?.date_published,
|
||||
content: page.data.metadata?.content?.content || "",
|
||||
images:
|
||||
page.data.metadata?.content?.attachments?.filter(
|
||||
(attachment) => attachment.name === "image",
|
||||
) || [],
|
||||
})
|
||||
useEditorState.setState(crossbell2Editor(page.data))
|
||||
}, [page.data, subdomain, draftKey, site.data?.characterId])
|
||||
|
||||
// Reset
|
||||
|
|
@ -310,7 +298,7 @@ export default function ShortEditor() {
|
|||
}
|
||||
|
||||
const EditorExtraProperties = memo(
|
||||
({ updateValue }: { updateValue: (val: Partial<Values>) => void }) => {
|
||||
({ updateValue }: { updateValue: (val: Partial<EditorValues>) => void }) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { useState } from "react"
|
||||
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorAutofill({
|
||||
updateValue,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const externalUrl = useEditorState((state) => state.externalUrl)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { ChangeEvent } from "react"
|
||||
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorContent({
|
||||
updateValue,
|
||||
prompt,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
prompt?: string
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { FieldLabel } from "~/components/ui/FieldLabel"
|
||||
import { ImageUploader } from "~/components/ui/ImageUploader"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorCover({
|
||||
updateValue,
|
||||
prompt,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
prompt?: string
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { Switch } from "~/components/ui/Switch"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorDisableAISummary({
|
||||
updateValue,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const value = useEditorState((state) => state.disableAISummary)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { ChangeEvent } from "react"
|
||||
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorExcerpt({
|
||||
updateValue,
|
||||
prompt,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
prompt?: string
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { ChangeEvent } from "react"
|
||||
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorExternalUrl({
|
||||
updateValue,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const value = useEditorState((state) => state.externalUrl)
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@ import { useState } from "react"
|
|||
|
||||
import { FieldLabel } from "~/components/ui/FieldLabel"
|
||||
import { ImageUploader } from "~/components/ui/ImageUploader"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorImages({
|
||||
updateValue,
|
||||
prompt,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
prompt?: string
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { DateInput } from "@mantine/dates"
|
||||
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorPublishAt({
|
||||
updateValue,
|
||||
prompt,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
prompt?: string
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import { ChangeEvent } from "react"
|
|||
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorSlug({
|
||||
updateValue,
|
||||
|
|
@ -11,7 +12,7 @@ export default function EditorSlug({
|
|||
type,
|
||||
siteLink,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
defaultValue?: string
|
||||
type: string
|
||||
siteLink?: string
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { Input } from "~/components/ui/Input"
|
||||
import { TagInput } from "~/components/ui/TagInput"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
import { useGetDistinctNoteTagsOfCharacter } from "~/queries/page"
|
||||
|
||||
export default function EditorTags({
|
||||
updateValue,
|
||||
characterId,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
characterId?: number
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { ChangeEvent } from "react"
|
||||
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { Values, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export default function EditorTitle({
|
||||
updateValue,
|
||||
}: {
|
||||
updateValue: (val: Partial<Values>) => void
|
||||
updateValue: (val: Partial<EditorValues>) => void
|
||||
}) {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const value = useEditorState((state) => state.title)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { create } from "zustand"
|
||||
|
||||
import { EditorValues } from "~/lib/types"
|
||||
|
||||
export const initialEditorState = {
|
||||
title: "",
|
||||
publishedAt: "",
|
||||
|
|
@ -17,28 +19,8 @@ export const initialEditorState = {
|
|||
images: [],
|
||||
}
|
||||
|
||||
export type Values = {
|
||||
title: string
|
||||
publishedAt: string
|
||||
published: boolean
|
||||
excerpt: string
|
||||
slug: string
|
||||
tags: string
|
||||
content: string
|
||||
cover: {
|
||||
address?: string
|
||||
mime_type?: string
|
||||
}
|
||||
disableAISummary: boolean
|
||||
externalUrl: string
|
||||
images: {
|
||||
address?: string
|
||||
mime_type?: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export const useEditorState = create<
|
||||
Values & {
|
||||
EditorValues & {
|
||||
setValues: (values: Partial<typeof initialEditorState>) => void
|
||||
}
|
||||
>((set) => ({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
import { NoteEntity } from "crossbell"
|
||||
import { nanoid } from "nanoid"
|
||||
import { zeroAddress } from "viem"
|
||||
|
||||
import { EditorValues, ExpandedNote } from "~/lib/types"
|
||||
|
||||
import { RESERVED_TAGS } from "./constants"
|
||||
|
||||
export const editor2Crossbell = ({
|
||||
values,
|
||||
type,
|
||||
autofill,
|
||||
}: {
|
||||
values: EditorValues
|
||||
type: string
|
||||
autofill?: boolean
|
||||
}): NoteEntity & {
|
||||
metadata: {
|
||||
content: {
|
||||
summary?: string
|
||||
}
|
||||
}
|
||||
} => {
|
||||
return {
|
||||
characterId: 0,
|
||||
noteId: 0,
|
||||
linkItemType: null,
|
||||
linkKey: "",
|
||||
toCharacterId: null,
|
||||
toAddress: null,
|
||||
toNoteId: null,
|
||||
toHeadCharacterId: null,
|
||||
toHeadNoteId: null,
|
||||
toContractAddress: null,
|
||||
toTokenId: null,
|
||||
toLinklistId: null,
|
||||
toUri: null,
|
||||
deleted: false,
|
||||
locked: false,
|
||||
contractAddress: null,
|
||||
uri: null,
|
||||
operator: zeroAddress,
|
||||
owner: zeroAddress,
|
||||
createdAt: "",
|
||||
publishedAt: "",
|
||||
updatedAt: "",
|
||||
deletedAt: null,
|
||||
transactionHash: zeroAddress,
|
||||
blockNumber: 0,
|
||||
logIndex: 0,
|
||||
updatedTransactionHash: zeroAddress,
|
||||
updatedBlockNumber: 0,
|
||||
updatedLogIndex: 0,
|
||||
metadata: {
|
||||
content: {
|
||||
title: values?.title,
|
||||
content: values?.content,
|
||||
date_published:
|
||||
values?.publishedAt ||
|
||||
(autofill ? new Date().toISOString() : undefined),
|
||||
summary: values?.excerpt,
|
||||
tags: [
|
||||
type,
|
||||
...(values?.tags
|
||||
?.split(",")
|
||||
.map((tag: string) => tag.trim())
|
||||
.filter((tag: string) => tag) || []),
|
||||
],
|
||||
sources: ["xlog"],
|
||||
attributes: [
|
||||
{
|
||||
trait_type: "xlog_slug",
|
||||
value: values.slug || (autofill ? nanoid() : ""),
|
||||
},
|
||||
...(values.disableAISummary
|
||||
? [
|
||||
{
|
||||
trait_type: "xlog_disable_ai_summary",
|
||||
value: values.disableAISummary,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
external_urls: values?.externalUrl ? [values?.externalUrl] : undefined,
|
||||
attachments: [
|
||||
...(values.cover?.address
|
||||
? [
|
||||
{
|
||||
name: "cover",
|
||||
address: values.cover.address,
|
||||
mime_type: values.cover.mime_type,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(values.images?.length
|
||||
? values.images?.map((image) => ({
|
||||
name: "image",
|
||||
address: image.address,
|
||||
mime_type: image.mime_type,
|
||||
}))
|
||||
: []),
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const crossbell2Editor = (note: ExpandedNote): EditorValues => {
|
||||
return {
|
||||
title: note?.metadata?.content?.title || "",
|
||||
publishedAt: note?.metadata?.content?.date_published || "",
|
||||
published: !!note?.noteId,
|
||||
excerpt: note?.metadata?.content?.summary || "",
|
||||
slug: note?.metadata?.content?.slug || "",
|
||||
tags:
|
||||
note?.metadata?.content?.tags
|
||||
?.filter((tag) => !RESERVED_TAGS.includes(tag))
|
||||
?.join(", ") || "",
|
||||
content: note?.metadata?.content?.content || "",
|
||||
cover: note?.metadata?.content?.attachments?.find(
|
||||
(attachment) => attachment.name === "cover",
|
||||
) || {
|
||||
address: "",
|
||||
mime_type: "",
|
||||
},
|
||||
disableAISummary: note?.metadata?.content?.disableAISummary || false,
|
||||
externalUrl: note?.metadata?.content?.external_urls?.[0] || "",
|
||||
images:
|
||||
note.metadata?.content?.attachments?.filter(
|
||||
(attachment) => attachment.name === "image",
|
||||
) || [],
|
||||
}
|
||||
}
|
||||
|
|
@ -15,12 +15,20 @@ export const expandCrossbellNote = async ({
|
|||
useScore,
|
||||
keyword,
|
||||
useHTML,
|
||||
disableAutofill,
|
||||
}: {
|
||||
note: NoteEntity
|
||||
note: NoteEntity & {
|
||||
metadata?: {
|
||||
content?: {
|
||||
summary?: string
|
||||
} | null
|
||||
} | null
|
||||
}
|
||||
useStat?: boolean
|
||||
useScore?: boolean
|
||||
keyword?: string
|
||||
useHTML?: boolean
|
||||
disableAutofill?: boolean
|
||||
}) => {
|
||||
const expandedNote: ExpandedNote = Object.assign(
|
||||
{
|
||||
|
|
@ -45,7 +53,7 @@ export const expandCrossbellNote = async ({
|
|||
position + 100,
|
||||
)}`
|
||||
} else {
|
||||
if (!expandedNote.metadata.content.summary) {
|
||||
if (!expandedNote.metadata.content.summary && !disableAutofill) {
|
||||
expandedNote.metadata.content.summary = rendered.excerpt
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +68,7 @@ export const expandCrossbellNote = async ({
|
|||
expandedNote.metadata.content.cover =
|
||||
expandedNote.metadata?.content?.attachments?.find(
|
||||
(attachment) => attachment.name === "cover",
|
||||
)?.address || rendered?.cover
|
||||
)?.address || (disableAutofill ? "" : rendered?.cover)
|
||||
|
||||
expandedNote.metadata.content.images = []
|
||||
|
||||
|
|
@ -86,8 +94,14 @@ export const expandCrossbellNote = async ({
|
|||
...new Set(expandedNote.metadata.content.images),
|
||||
]
|
||||
|
||||
expandedNote.metadata.content.slug = getNoteSlug(expandedNote)
|
||||
if (!expandedNote.metadata.content.date_published && expandedNote.noteId) {
|
||||
if (!disableAutofill) {
|
||||
expandedNote.metadata.content.slug = getNoteSlug(expandedNote)
|
||||
}
|
||||
if (
|
||||
!expandedNote.metadata.content.date_published &&
|
||||
expandedNote.noteId &&
|
||||
!disableAutofill
|
||||
) {
|
||||
expandedNote.metadata.content.date_published = expandedNote.createdAt
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,3 +125,23 @@ export type ExpandedCharacter = CharacterEntity & {
|
|||
export type ColorScheme = "dark" | "light"
|
||||
|
||||
export type NoteType = "post" | "page" | "portfolio" | "short"
|
||||
|
||||
export type EditorValues = {
|
||||
title: string
|
||||
publishedAt: string
|
||||
published: boolean
|
||||
excerpt: string
|
||||
slug: string
|
||||
tags: string
|
||||
content: string
|
||||
cover: {
|
||||
address?: string
|
||||
mime_type?: string
|
||||
}
|
||||
disableAISummary: boolean
|
||||
externalUrl: string
|
||||
images: {
|
||||
address?: string
|
||||
mime_type?: string
|
||||
}[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type {
|
|||
NoteEntity,
|
||||
NoteMetadata,
|
||||
} from "crossbell"
|
||||
import type { Address } from "viem"
|
||||
import { type Address } from "viem"
|
||||
|
||||
import { GeneralAccount } from "@crossbell/connect-kit"
|
||||
import { indexer } from "@crossbell/indexer"
|
||||
|
|
@ -14,6 +14,7 @@ import { extractCharacterAttribute } from "@crossbell/util-metadata"
|
|||
import { gql } from "@urql/core"
|
||||
|
||||
import { RESERVED_TAGS } from "~/lib/constants"
|
||||
import { editor2Crossbell } from "~/lib/editor-converter"
|
||||
import { expandCrossbellNote } from "~/lib/expand-unit"
|
||||
import { filterCommentCharacter } from "~/lib/filter-character"
|
||||
import { getNoteSlug } from "~/lib/helpers"
|
||||
|
|
@ -64,102 +65,47 @@ export async function postNotes(
|
|||
})
|
||||
}
|
||||
|
||||
const getLocalPages = (input: {
|
||||
const getLocalPages = async (input: {
|
||||
characterId: number
|
||||
isPost?: boolean // In order to be compatible with old drafts
|
||||
type?: NoteType[]
|
||||
handle?: string
|
||||
}) => {
|
||||
const pages: ExpandedNote[] = []
|
||||
getKeys([`draft-${input.characterId}-`, `draft-${input.handle}-`]).forEach(
|
||||
(key) => {
|
||||
const page = getStorage(key)
|
||||
if (
|
||||
page.isPost === input.isPost ||
|
||||
input.type?.includes(page.type) ||
|
||||
input.type === undefined
|
||||
) {
|
||||
const note: ExpandedNote = {
|
||||
characterId: input.characterId,
|
||||
noteId: 0,
|
||||
draftKey: key
|
||||
.replace(`draft-${input.characterId}-`, "")
|
||||
.replace(`draft-${input.handle}-${input.characterId}-`, ""), // In order to be compatible with old drafts
|
||||
linkItemType: null,
|
||||
linkKey: "",
|
||||
toCharacterId: null,
|
||||
toAddress: null,
|
||||
toNoteId: null,
|
||||
toHeadCharacterId: null,
|
||||
toHeadNoteId: null,
|
||||
toContractAddress: null,
|
||||
toTokenId: null,
|
||||
toLinklistId: null,
|
||||
toUri: null,
|
||||
deleted: false,
|
||||
locked: false,
|
||||
contractAddress: null,
|
||||
uri: null,
|
||||
operator: "" as Address, // TODO: check usage and replace it with viem's `zeroAddress`.
|
||||
owner: "" as Address, // TODO: check usage and replace it with viem's `zeroAddress`.
|
||||
createdAt: new Date(page.date).toISOString(),
|
||||
publishedAt: new Date(page.date).toISOString(),
|
||||
updatedAt: new Date(page.date).toISOString(),
|
||||
deletedAt: null,
|
||||
transactionHash: "" as Address, // TODO: check usage and replace it with viem's `zeroAddress`.
|
||||
blockNumber: 0,
|
||||
logIndex: 0,
|
||||
updatedTransactionHash: "" as Address, // TODO: check usage and replace it with viem's `zeroAddress`.
|
||||
updatedBlockNumber: 0,
|
||||
updatedLogIndex: 0,
|
||||
metadata: {
|
||||
content: {
|
||||
title: page.values?.title,
|
||||
content: page.values?.content,
|
||||
date_published: page.values?.publishedAt,
|
||||
summary: page.values?.excerpt,
|
||||
tags: [
|
||||
page.type,
|
||||
...(page.values?.tags
|
||||
?.split(",")
|
||||
.map((tag: string) => tag.trim())
|
||||
.filter((tag: string) => tag) || []),
|
||||
],
|
||||
slug: page.values?.slug,
|
||||
sources: ["xlog"],
|
||||
disableAISummary: page.values?.disableAISummary,
|
||||
external_urls: page.values?.externalUrl
|
||||
? [page.values?.externalUrl]
|
||||
: undefined,
|
||||
cover: page.values?.cover?.address,
|
||||
images: [page.values?.cover?.address],
|
||||
attachments: [
|
||||
...(page.values?.cover?.address
|
||||
? [
|
||||
{
|
||||
name: "cover",
|
||||
address: page.values?.cover.address,
|
||||
mime_type: page.values?.cover.mime_type,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(page.values.images?.length
|
||||
? page.values.images?.map((image: any) => ({
|
||||
name: "image",
|
||||
address: image?.address,
|
||||
mime_type: image?.mime_type,
|
||||
}))
|
||||
: []),
|
||||
],
|
||||
},
|
||||
},
|
||||
local: true,
|
||||
}
|
||||
pages.push(note)
|
||||
}
|
||||
},
|
||||
)
|
||||
return pages
|
||||
return (
|
||||
await Promise.all(
|
||||
getKeys([`draft-${input.characterId}-`, `draft-${input.handle}-`]).map(
|
||||
async (key) => {
|
||||
const page = getStorage(key)
|
||||
if (
|
||||
page.isPost === input.isPost ||
|
||||
input.type?.includes(page.type) ||
|
||||
input.type === undefined
|
||||
) {
|
||||
const note: ExpandedNote = Object.assign(
|
||||
await expandCrossbellNote({
|
||||
note: editor2Crossbell({
|
||||
values: page.values,
|
||||
type: page.type,
|
||||
}),
|
||||
disableAutofill: true,
|
||||
}),
|
||||
{
|
||||
draftKey: key
|
||||
.replace(`draft-${input.characterId}-`, "")
|
||||
.replace(`draft-${input.handle}-${input.characterId}-`, ""), // In order to be compatible with old drafts
|
||||
local: true,
|
||||
characterId: input.characterId,
|
||||
createdAt: new Date(page.date).toISOString(),
|
||||
publishedAt: new Date(page.date).toISOString(),
|
||||
updatedAt: new Date(page.date).toISOString(),
|
||||
},
|
||||
)
|
||||
return note
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
).filter((item): item is ExpandedNote => !!item)
|
||||
}
|
||||
|
||||
export async function getPagesBySite(input: {
|
||||
|
|
@ -349,7 +295,7 @@ export async function getPagesBySite(input: {
|
|||
),
|
||||
}
|
||||
|
||||
const local = getLocalPages({
|
||||
const local = await getLocalPages({
|
||||
characterId: input.characterId,
|
||||
isPost: input.type[0] === "post", // In order to be compatible with old drafts
|
||||
type: input.type,
|
||||
|
|
@ -363,7 +309,10 @@ export async function getPagesBySite(input: {
|
|||
if (index !== -1) {
|
||||
if (
|
||||
new Date(localPage.updatedAt) >
|
||||
new Date(expandedNotes.list[index].updatedAt)
|
||||
new Date(
|
||||
expandedNotes.list[index].updatedAt ||
|
||||
expandedNotes.list[index].createdAt,
|
||||
)
|
||||
) {
|
||||
expandedNotes.list[index] = {
|
||||
...expandedNotes.list[index],
|
||||
|
|
@ -591,7 +540,7 @@ export async function getPage<TRender extends boolean = false>(input: {
|
|||
}
|
||||
|
||||
// local page
|
||||
const local = getLocalPages({
|
||||
const local = await getLocalPages({
|
||||
characterId: input.characterId,
|
||||
handle: input.handle,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import type { NoteEntity, NoteMetadata } from "crossbell"
|
||||
import { nanoid } from "nanoid"
|
||||
import type { NoteEntity } from "crossbell"
|
||||
|
||||
import {
|
||||
useAccountState,
|
||||
|
|
@ -25,9 +24,9 @@ import {
|
|||
useQueryClient,
|
||||
} from "@tanstack/react-query"
|
||||
|
||||
import { type Values } from "~/hooks/useEditorState"
|
||||
import { editor2Crossbell } from "~/lib/editor-converter"
|
||||
import createSearchParams from "~/lib/search-params"
|
||||
import { NoteType } from "~/lib/types"
|
||||
import { EditorValues, NoteType } from "~/lib/types"
|
||||
import * as pageModel from "~/models/page.model"
|
||||
|
||||
export const useGetPagesBySiteLite = (
|
||||
|
|
@ -219,7 +218,7 @@ export function useCreatePage() {
|
|||
input: {
|
||||
characterId?: number
|
||||
type?: NoteType
|
||||
} & Values,
|
||||
} & EditorValues,
|
||||
) => {
|
||||
if (!input.characterId) {
|
||||
throw new Error("characterId is required")
|
||||
|
|
@ -228,55 +227,11 @@ export function useCreatePage() {
|
|||
return postNote.mutate(
|
||||
{
|
||||
characterId: input.characterId,
|
||||
metadata: {
|
||||
title: input.title,
|
||||
content: input.content,
|
||||
date_published: input.publishedAt || new Date().toISOString(),
|
||||
summary: input.excerpt,
|
||||
tags: [
|
||||
input.type,
|
||||
...(input.tags
|
||||
?.split(",")
|
||||
.map((tag) => tag.trim())
|
||||
.filter((tag) => tag) || []),
|
||||
],
|
||||
sources: ["xlog"],
|
||||
attributes: [
|
||||
{
|
||||
trait_type: "xlog_slug",
|
||||
value: input.slug || nanoid(),
|
||||
},
|
||||
...(input.disableAISummary
|
||||
? [
|
||||
{
|
||||
trait_type: "xlog_disable_ai_summary",
|
||||
value: input.disableAISummary,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
attachments: [
|
||||
...(input.cover?.address
|
||||
? [
|
||||
{
|
||||
name: "cover",
|
||||
address: input.cover.address,
|
||||
mime_type: input.cover.mime_type,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(input.images?.length
|
||||
? input.images?.map((image) => ({
|
||||
name: "image",
|
||||
address: image.address,
|
||||
mime_type: image.mime_type,
|
||||
}))
|
||||
: []),
|
||||
],
|
||||
external_urls: [input.externalUrl],
|
||||
} as NoteMetadata & {
|
||||
summary?: string
|
||||
},
|
||||
metadata: editor2Crossbell({
|
||||
values: input,
|
||||
autofill: true,
|
||||
type: input.type || "post",
|
||||
}).metadata.content,
|
||||
},
|
||||
{
|
||||
onSuccess: (data, variables) => {
|
||||
|
|
@ -304,7 +259,7 @@ export function useUpdatePage() {
|
|||
noteId?: number
|
||||
characterId?: number
|
||||
type?: NoteType
|
||||
} & Values,
|
||||
} & EditorValues,
|
||||
) => {
|
||||
if (!input.characterId || !input.noteId) {
|
||||
throw new Error("characterId and noteId are required")
|
||||
|
|
|
|||
Loading…
Reference in New Issue