From 8ef36c065bded338b85c04c444a115a6834a198c Mon Sep 17 00:00:00 2001 From: Nya Candy Date: Thu, 8 Jun 2023 18:13:39 +0800 Subject: [PATCH] feat: editor advanced options & disable AI summary (#600) --- src/app/dashboard/[subdomain]/editor/page.tsx | 354 +++++++++++------- src/components/site/PostMeta.tsx | 12 +- src/components/ui/Switch.tsx | 35 ++ src/hooks/useEdtiorState.ts | 2 + src/lib/expand-unit.ts | 6 + src/lib/i18n/locales/ja/dashboard.json | 6 +- src/lib/i18n/locales/zh-TW/dashboard.json | 6 +- src/lib/i18n/locales/zh/dashboard.json | 6 +- src/lib/types.ts | 1 + src/models/page.model.ts | 28 +- 10 files changed, 300 insertions(+), 156 deletions(-) create mode 100644 src/components/ui/Switch.tsx diff --git a/src/app/dashboard/[subdomain]/editor/page.tsx b/src/app/dashboard/[subdomain]/editor/page.tsx index f96d393e..148f6577 100644 --- a/src/app/dashboard/[subdomain]/editor/page.tsx +++ b/src/app/dashboard/[subdomain]/editor/page.tsx @@ -32,6 +32,7 @@ import { FieldLabel } from "~/components/ui/FieldLabel" import { ImageUploader } from "~/components/ui/ImageUploader" import { Input } from "~/components/ui/Input" import { Modal } from "~/components/ui/Modal" +import { Switch } from "~/components/ui/Switch" import { TagInput } from "~/components/ui/TagInput" import { UniLink } from "~/components/ui/UniLink" import { toolbars } from "~/editor" @@ -242,6 +243,7 @@ export default function SubdomainEditor() { applications: page.data?.metadata?.content?.sources, characterId: site.data?.characterId, cover: values.cover, + disableAISummary: values.disableAISummary, }) } } @@ -301,6 +303,7 @@ export default function SubdomainEditor() { address: "", mime_type: "", }, + disableAISummary: page.data.metadata?.content?.disableAISummary, }) setDefaultSlug( getDefaultSlug( @@ -465,6 +468,9 @@ export default function SubdomainEditor() { ) }, [draftKey, subdomain, site.data?.characterId]) + const [isAdvancedOptionsOpen, setIsAdvancedOptionsOpen] = + useState(false) + const extraProperties = ( setIsAdvancedOptionsOpen(true)} /> ) @@ -650,12 +657,18 @@ export default function SubdomainEditor() { isPost={isPost} userTags={userTags} subdomain={subdomain} + openAdvancedOptions={() => setIsAdvancedOptionsOpen(true)} /> )} )} + = memo(({ isPost, updateValue, subdomain, defaultSlug, userTags }) => { - const values = useEditorState( - (state) => pick(state, ["publishedAt", "slug", "excerpt", "tags", "cover"]), - shallow, - ) - const { t } = useTranslation("dashboard") - const site = useGetSite(subdomain) - return ( -
-
- - { - if (value) { - updateValue("publishedAt", value.toISOString()) - } else { - updateValue("publishedAt", "") + openAdvancedOptions: () => void +}> = memo( + ({ + isPost, + updateValue, + subdomain, + defaultSlug, + userTags, + openAdvancedOptions, + }) => { + const values = useEditorState( + (state) => + pick(state, ["publishedAt", "slug", "excerpt", "tags", "cover"]), + shallow, + ) + const { t } = useTranslation("dashboard") + const site = useGetSite(subdomain) + + return ( +
+
+ + { + if (value) { + updateValue("publishedAt", value.toISOString()) + } else { + updateValue("publishedAt", "") + } + }} + styles={{ + input: { + borderRadius: "0.5rem", + borderColor: "var(--border-color)", + height: "2.5rem", + "&:focus-within": { + borderColor: "var(--theme-color)", + }, }, - }, - }} - /> -
- {t( - `This ${ - isPost ? "post" : "page" - } will be accessible from this time. Leave blank to use the current time.`, - )} -
- {values.publishedAt > new Date().toISOString() && ( -
+ }} + /> +
{t( - "The post is currently unavailable as its publication date has been scheduled for a future time.", + `This ${ + isPost ? "post" : "page" + } will be accessible from this time. Leave blank to use the current time.`, )}
- )} -
-
- ) => - updateValue("slug", e.target.value) - } - help={ - <> - {(values.slug || defaultSlug) && ( - <> - {t(`This ${isPost ? "post" : "page"} will be accessible at`)}{" "} - - {getSiteLink({ - subdomain, - domain: site.data?.metadata?.content?.custom_domain, - noProtocol: true, - })} - /{encodeURIComponent(values.slug || defaultSlug)} - - + {values.publishedAt > new Date().toISOString() && ( +
+ {t( + "The post is currently unavailable as its publication date has been scheduled for a future time.", )} - - } - /> -
-
- ( - updateValue("tags", value)} - /> +
)} - /> -
-
- ) => { - updateValue("excerpt", e.target.value) - }} - help={t("Leave it blank to use auto-generated excerpt")} - /> -
-
- - { - const { address, mime_type } = key as { - address?: string - mime_type?: string +
+
+ ) => + updateValue("slug", e.target.value) } - updateValue("cover", { - address, - mime_type, - }) - }} - accept="image/*" - /> -
- {t("Leave blank to use the first image in the post")} + help={ + <> + {(values.slug || defaultSlug) && ( + <> + {t( + `This ${isPost ? "post" : "page"} will be accessible at`, + )}{" "} + + {getSiteLink({ + subdomain, + domain: site.data?.metadata?.content?.custom_domain, + noProtocol: true, + })} + /{encodeURIComponent(values.slug || defaultSlug)} + + + )} + + } + /> +
+
+ ( + updateValue("tags", value)} + /> + )} + /> +
+
+ ) => { + updateValue("excerpt", e.target.value) + }} + help={t("Leave it blank to use auto-generated excerpt")} + /> +
+
+ + { + const { address, mime_type } = key as { + address?: string + mime_type?: string + } + updateValue("cover", { + address, + mime_type, + }) + }} + accept="image/*" + /> +
+ {t("Leave blank to use the first image in the post")} +
+
+
+
-
- ) -}) + ) + }, +) EditorExtraProperties.displayName = "EditorExtraProperties" + +const EditorAdvancedOptions: FC<{ + updateValue: (key: K, value: Values[K]) => void + + isAdvancedOptionsOpen: boolean + setIsAdvancedOptionsOpen: (state: boolean) => void +}> = memo( + ({ updateValue, isAdvancedOptionsOpen, setIsAdvancedOptionsOpen }) => { + const values = useEditorState( + (state) => pick(state, ["disableAISummary"]), + shallow, + ) + const { t } = useTranslation("dashboard") + + return ( + +
+
+ updateValue("disableAISummary", state)} + /> +
+
+
+ ) + }, +) + +EditorAdvancedOptions.displayName = "EditorAdvancedOptions" diff --git a/src/components/site/PostMeta.tsx b/src/components/site/PostMeta.tsx index 08231879..b4bfb874 100644 --- a/src/components/site/PostMeta.tsx +++ b/src/components/site/PostMeta.tsx @@ -17,10 +17,14 @@ export default async function PostMeta({ }) { const { t } = await useTranslation("common") const { i18n } = await useTranslation() - const summary = await getSummary({ - cid: toCid(page.metadata?.uri || ""), - lang: i18n.resolvedLanguage, - }) + + let summary: string | undefined + if (!page.metadata.content.disableAISummary) { + summary = await getSummary({ + cid: toCid(page.metadata?.uri || ""), + lang: i18n.resolvedLanguage, + }) + } return (
diff --git a/src/components/ui/Switch.tsx b/src/components/ui/Switch.tsx new file mode 100644 index 00000000..e9de03ef --- /dev/null +++ b/src/components/ui/Switch.tsx @@ -0,0 +1,35 @@ +import { Fragment } from "react" + +import { Switch as HUISwitch } from "@headlessui/react" + +interface SwitchProps { + label: string + checked: boolean + setChecked: (state: boolean) => void +} +export const Switch = ({ label, checked, setChecked }: SwitchProps) => ( + +
+ {/*Switch self*/} + + {({ checked }) => ( + + )} + + + {/*Switch label*/} + {label} +
+
+) diff --git a/src/hooks/useEdtiorState.ts b/src/hooks/useEdtiorState.ts index ec32d37e..96ab9550 100644 --- a/src/hooks/useEdtiorState.ts +++ b/src/hooks/useEdtiorState.ts @@ -12,6 +12,7 @@ export const initialEditorState = { address: "", mime_type: "", }, + disableAISummary: false, } export type Values = { title: string @@ -25,6 +26,7 @@ export type Values = { address?: string mime_type?: string } + disableAISummary: boolean } export const useEditorState = create< diff --git a/src/lib/expand-unit.ts b/src/lib/expand-unit.ts index 4f300488..980791dd 100644 --- a/src/lib/expand-unit.ts +++ b/src/lib/expand-unit.ts @@ -65,6 +65,12 @@ export const expandCrossbellNote = async ({ expandedNote.metadata.content.date_published = expandedNote.createdAt } + expandedNote.metadata.content.disableAISummary = Boolean( + expandedNote.metadata.content.attributes?.find( + (attribute) => attribute.trait_type === "xlog_disable_ai_summary", + )?.value, + ) + if (useStat) { const stat = await ( await fetch( diff --git a/src/lib/i18n/locales/ja/dashboard.json b/src/lib/i18n/locales/ja/dashboard.json index 20acd9d3..af98f67c 100644 --- a/src/lib/i18n/locales/ja/dashboard.json +++ b/src/lib/i18n/locales/ja/dashboard.json @@ -122,5 +122,7 @@ "Operators have permissions to enter your dashboard, change your settings(excluding xLog subdomain) and post, modify, delete contents on your site.": "シャドー認証では、ダッシュボードへのアクセス、設定の変更(xLog サブドメインを除く)、サイトのコンテンツの公開・修正・削除が可能です。", "Add": "新規", "delete_confirmation_post": "この記事を削除してもよろしいですか?", - "delete_confirmation_page": "このページを削除してもよろしいですか?" -} \ No newline at end of file + "delete_confirmation_page": "このページを削除してもよろしいですか?", + "Advanced Settings": "詳細設定", + "Disable AI-generated summary": "AIが生成した要約を無効化する" +} diff --git a/src/lib/i18n/locales/zh-TW/dashboard.json b/src/lib/i18n/locales/zh-TW/dashboard.json index ed528688..1f04915c 100644 --- a/src/lib/i18n/locales/zh-TW/dashboard.json +++ b/src/lib/i18n/locales/zh-TW/dashboard.json @@ -200,5 +200,7 @@ "Help: Markdown syntax and components used by xLog": "幫助:xLog 使用的 Markdown 語法和組件", "Align Center": "居中", "Align Right": "靠右", - "Formula": "公式" -} \ No newline at end of file + "Formula": "公式", + "Advanced Settings": "高級設置", + "Disable AI-generated summary": "禁用AI生成的摘要" +} diff --git a/src/lib/i18n/locales/zh/dashboard.json b/src/lib/i18n/locales/zh/dashboard.json index d5c67536..aef1ea5c 100644 --- a/src/lib/i18n/locales/zh/dashboard.json +++ b/src/lib/i18n/locales/zh/dashboard.json @@ -211,5 +211,7 @@ "Help: Markdown syntax and components used by xLog": "帮助:xLog 使用的 Markdown 语法和组件", "Align Center": "居中对齐", "Align Right": "右对齐", - "Formula": "公式" -} \ No newline at end of file + "Formula": "公式", + "Advanced Settings": "高级设置", + "Disable AI-generated summary": "禁用AI生成的摘要" +} diff --git a/src/lib/types.ts b/src/lib/types.ts index 55367f8c..8f80be9c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -87,6 +87,7 @@ export type ExpandedNote = NoteEntity & { reason?: string } contentHTML?: string + disableAISummary?: boolean } } stat?: { diff --git a/src/models/page.model.ts b/src/models/page.model.ts index aed28a03..bc440046 100644 --- a/src/models/page.model.ts +++ b/src/models/page.model.ts @@ -61,6 +61,7 @@ export async function createOrUpdatePage( address?: string mime_type?: string } + disableAISummary?: boolean }, customUnidata?: Unidata, newbieToken?: string, @@ -118,14 +119,24 @@ export async function createOrUpdatePage( "xlog", ...(input.applications?.filter((app) => app !== "xlog") || []), ], - ...(input.slug && { - attributes: [ - { - trait_type: "xlog_slug", - value: input.slug, - }, - ], - }), + attributes: [ + ...(input.slug + ? [ + { + trait_type: "xlog_slug", + value: input.slug, + }, + ] + : []), + ...(input.disableAISummary + ? [ + { + trait_type: "xlog_disable_ai_summary", + value: input.disableAISummary, + }, + ] + : []), + ], attachments: input.cover?.address ? [ { @@ -215,6 +226,7 @@ const getLocalPages = (input: { ], slug: page.values?.slug, sources: ["xlog"], + disableAISummary: page.values?.disableAISummary, }, }, local: true,