diff --git a/src/app/dashboard/[subdomain]/editor/portfolio-editor.tsx b/src/app/dashboard/[subdomain]/editor/portfolio-editor.tsx index 201fa74f..6695a517 100644 --- a/src/app/dashboard/[subdomain]/editor/portfolio-editor.tsx +++ b/src/app/dashboard/[subdomain]/editor/portfolio-editor.tsx @@ -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) => { + (val: Partial) => { 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) => void }) => { + ({ updateValue }: { updateValue: (val: Partial) => void }) => { const { t } = useTranslation("dashboard") return ( diff --git a/src/app/dashboard/[subdomain]/editor/post-editor.tsx b/src/app/dashboard/[subdomain]/editor/post-editor.tsx index 4f1d755b..c2709971 100644 --- a/src/app/dashboard/[subdomain]/editor/post-editor.tsx +++ b/src/app/dashboard/[subdomain]/editor/post-editor.tsx @@ -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) => { + (val: Partial) => { 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) => void + updateValue: (val: Partial) => void type: NoteType defaultSlug: string characterId?: number diff --git a/src/app/dashboard/[subdomain]/editor/short-editor.tsx b/src/app/dashboard/[subdomain]/editor/short-editor.tsx index 26322384..50c3649e 100644 --- a/src/app/dashboard/[subdomain]/editor/short-editor.tsx +++ b/src/app/dashboard/[subdomain]/editor/short-editor.tsx @@ -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) => { + (val: Partial) => { 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) => void }) => { + ({ updateValue }: { updateValue: (val: Partial) => void }) => { const { t } = useTranslation("dashboard") return ( diff --git a/src/components/dashboard/editor-properties/EditorAutofill.tsx b/src/components/dashboard/editor-properties/EditorAutofill.tsx index 53d92b62..3d9e60f7 100644 --- a/src/components/dashboard/editor-properties/EditorAutofill.tsx +++ b/src/components/dashboard/editor-properties/EditorAutofill.tsx @@ -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) => void + updateValue: (val: Partial) => void }) { const { t } = useTranslation("dashboard") const externalUrl = useEditorState((state) => state.externalUrl) diff --git a/src/components/dashboard/editor-properties/EditorContent.tsx b/src/components/dashboard/editor-properties/EditorContent.tsx index 3e100439..33a298de 100644 --- a/src/components/dashboard/editor-properties/EditorContent.tsx +++ b/src/components/dashboard/editor-properties/EditorContent.tsx @@ -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) => void + updateValue: (val: Partial) => void prompt?: string }) { const { t } = useTranslation("dashboard") diff --git a/src/components/dashboard/editor-properties/EditorCover.tsx b/src/components/dashboard/editor-properties/EditorCover.tsx index b9d583b5..933e8ac8 100644 --- a/src/components/dashboard/editor-properties/EditorCover.tsx +++ b/src/components/dashboard/editor-properties/EditorCover.tsx @@ -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) => void + updateValue: (val: Partial) => void prompt?: string }) { const { t } = useTranslation("dashboard") diff --git a/src/components/dashboard/editor-properties/EditorDisableAISummary.tsx b/src/components/dashboard/editor-properties/EditorDisableAISummary.tsx index df1b7156..dc2c8bac 100644 --- a/src/components/dashboard/editor-properties/EditorDisableAISummary.tsx +++ b/src/components/dashboard/editor-properties/EditorDisableAISummary.tsx @@ -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) => void + updateValue: (val: Partial) => void }) { const { t } = useTranslation("dashboard") const value = useEditorState((state) => state.disableAISummary) diff --git a/src/components/dashboard/editor-properties/EditorExcerpt.tsx b/src/components/dashboard/editor-properties/EditorExcerpt.tsx index 604130b3..84059cd9 100644 --- a/src/components/dashboard/editor-properties/EditorExcerpt.tsx +++ b/src/components/dashboard/editor-properties/EditorExcerpt.tsx @@ -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) => void + updateValue: (val: Partial) => void prompt?: string }) { const { t } = useTranslation("dashboard") diff --git a/src/components/dashboard/editor-properties/EditorExternalUrl.tsx b/src/components/dashboard/editor-properties/EditorExternalUrl.tsx index fd92c0c6..9873700c 100644 --- a/src/components/dashboard/editor-properties/EditorExternalUrl.tsx +++ b/src/components/dashboard/editor-properties/EditorExternalUrl.tsx @@ -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) => void + updateValue: (val: Partial) => void }) { const { t } = useTranslation("dashboard") const value = useEditorState((state) => state.externalUrl) diff --git a/src/components/dashboard/editor-properties/EditorImages.tsx b/src/components/dashboard/editor-properties/EditorImages.tsx index d40f1c46..74d58bb8 100644 --- a/src/components/dashboard/editor-properties/EditorImages.tsx +++ b/src/components/dashboard/editor-properties/EditorImages.tsx @@ -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) => void + updateValue: (val: Partial) => void prompt?: string }) { const { t } = useTranslation("dashboard") diff --git a/src/components/dashboard/editor-properties/EditorPublishAt.tsx b/src/components/dashboard/editor-properties/EditorPublishAt.tsx index 1b93422f..4cf5735d 100644 --- a/src/components/dashboard/editor-properties/EditorPublishAt.tsx +++ b/src/components/dashboard/editor-properties/EditorPublishAt.tsx @@ -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) => void + updateValue: (val: Partial) => void prompt?: string }) { const { t } = useTranslation("dashboard") diff --git a/src/components/dashboard/editor-properties/EditorSlug.tsx b/src/components/dashboard/editor-properties/EditorSlug.tsx index 7988976a..8e714ceb 100644 --- a/src/components/dashboard/editor-properties/EditorSlug.tsx +++ b/src/components/dashboard/editor-properties/EditorSlug.tsx @@ -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) => void + updateValue: (val: Partial) => void defaultValue?: string type: string siteLink?: string diff --git a/src/components/dashboard/editor-properties/EditorTags.tsx b/src/components/dashboard/editor-properties/EditorTags.tsx index 95011a91..cf27143c 100644 --- a/src/components/dashboard/editor-properties/EditorTags.tsx +++ b/src/components/dashboard/editor-properties/EditorTags.tsx @@ -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) => void + updateValue: (val: Partial) => void characterId?: number }) { const { t } = useTranslation("dashboard") diff --git a/src/components/dashboard/editor-properties/EditorTitle.tsx b/src/components/dashboard/editor-properties/EditorTitle.tsx index 4f421123..6d736689 100644 --- a/src/components/dashboard/editor-properties/EditorTitle.tsx +++ b/src/components/dashboard/editor-properties/EditorTitle.tsx @@ -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) => void + updateValue: (val: Partial) => void }) { const { t } = useTranslation("dashboard") const value = useEditorState((state) => state.title) diff --git a/src/hooks/useEditorState.ts b/src/hooks/useEditorState.ts index 575835be..92e05229 100644 --- a/src/hooks/useEditorState.ts +++ b/src/hooks/useEditorState.ts @@ -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) => void } >((set) => ({ diff --git a/src/lib/editor-converter.ts b/src/lib/editor-converter.ts new file mode 100644 index 00000000..3d409856 --- /dev/null +++ b/src/lib/editor-converter.ts @@ -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", + ) || [], + } +} diff --git a/src/lib/expand-unit.ts b/src/lib/expand-unit.ts index 45c99f69..07003545 100644 --- a/src/lib/expand-unit.ts +++ b/src/lib/expand-unit.ts @@ -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 } diff --git a/src/lib/types.ts b/src/lib/types.ts index 0cd5ce46..09df8325 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -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 + }[] +} diff --git a/src/models/page.model.ts b/src/models/page.model.ts index 41f0a557..05195230 100644 --- a/src/models/page.model.ts +++ b/src/models/page.model.ts @@ -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(input: { } // local page - const local = getLocalPages({ + const local = await getLocalPages({ characterId: input.characterId, handle: input.handle, }) diff --git a/src/queries/page.ts b/src/queries/page.ts index b94ec48c..f48e91ab 100644 --- a/src/queries/page.ts +++ b/src/queries/page.ts @@ -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")