diff --git a/public/locales/ja/dashboard.json b/public/locales/ja/dashboard.json index f46406c3..2950c2a3 100644 --- a/public/locales/ja/dashboard.json +++ b/public/locales/ja/dashboard.json @@ -13,6 +13,7 @@ "Site Duration": "サイトの運営期間", "days": "日", "Deleted!": "削除されました!", + "Fail to Deleted." : "削除できませんでした。", "Converted!": "変換されました!", "Failed to convert.": "変換に失敗しました。", "All Posts": "すべての記事", @@ -24,7 +25,7 @@ "Scheduled": "予約済み", "scheduled": "予約済み", "published and local modified": "公開済みで、ローカルでの編集あり", - "Others on Crossbell": "Crossbellに関するその他の情報", + "Others on Crossbell": "Crossbell に関するその他の情報", "link post-vs-page": "https://wordpress.com/ja/support/post-vs-page/", "posts description": "記事は、時間順に表示されます。読者に新しいコンテンツを提供し続けることで、ブログのフォローワが増えるでしょう。<2>記事", "pages description": "ページは日付に影響を受けることがありません。「私たちについて」「お問い合わせ」などのようにページを活用しましょう。<2>ページ", @@ -54,7 +55,7 @@ "Link": "リンク", "Image": "画像", "Upload Image": "画像をアップロード", - "Tip: xLog Flavored Markdown": "ヒント:xLog風のマークダウン", + "Tip: xLog Flavored Markdown": "ヒント:xLog 風のマークダウン", "Preview": "プレビュー", "Publish": "公開", "Update": "更新", @@ -73,7 +74,7 @@ "Social Platforms": "ソーシャルプラットフォーム", "Navigation": "ナビゲーション", "Domains": "ドメイン", - "Custom CSS": "カスタムCSS", + "Custom CSS": "カスタム CSS", "Operators": "オペレーター", "Export data": "データのエクスポート", "Site Settings": "サイト設定", @@ -82,37 +83,37 @@ "Supports both pictures and videos.": "画像と動画の両方に対応しています。", "Name": "名前", "Description": "説明", - "Integrate Google Analytics": "Google Analyticsと紐付けします。Measurement IDの検索方法については、<2>こちらの文章をご覧ください。", + "Integrate Google Analytics": "Google Analytics と紐付けします。Measurement ID の検索方法については、<2>こちらの文章をご覧ください。", "Save": "保存", "Tips": "ヒント", "social tips": { "p1": "紐付けしたプラットフォームの標識は、画面の右下隅に表示されます。", - "p2": "xLogは<2>こちらのプラットフォームと紐付け可能です。(紐付けに成功すると自動的にそのロゴとリンクが表示されます。)その他のプラットフォームの場合、デフォルトのロゴが表示されます。もしサポートして欲しいプラットフォームがありましたら、是非IssueあるいはPRを作成して、xLogをより完成度の高いプラットフォームにさせましょう。", - "p3": "<2>xSyncを使用して、Twitter、Telegram、Medium、Substackなどのプラットフォームに紐付けし、コンテンツを自動的に同期することもできます。同期設定を行った場合、ここにも表示されます。" + "p2": "xLog は<2>こちらのプラットフォームと紐付け可能です。(紐付けに成功すると自動的にそのロゴとリンクが表示されます。)その他のプラットフォームの場合、デフォルトのロゴが表示されます。もしサポートして欲しいプラットフォームがありましたら、是非 Issue あるいは PR を作成して、xLog をより完成度の高いプラットフォームにさせましょう。", + "p3": "<2>xSyncを使用して、Twitter、Telegram、Medium、Substack などのプラットフォームに紐付けし、コンテンツを自動的に同期することもできます。同期設定を行った場合、ここにも表示されます。" }, "Platform": "プラットフォーム", "Identity": "アイデンティティ", "Remove": "削除", "New Item": "新しいアイテム", - "xLog provides some out-of-the-box built-in pages": "xLogにはいくつすぐ使えるページテンプレートがあります。", + "xLog provides some out-of-the-box built-in pages": "xLog にはいくつすぐ使えるページテンプレートがあります。", "Home page": "ホームページ", "Archives page": "アーカイブページ", "Tag page": "タグページ", - "NFT Showcase page": "NFTショーケースページ", + "NFT Showcase page": "NFT ショーケースページ", "Label": "ラベル", "URL": "URL", "subdomain": "サブドメイン", "Custom Domain": "カスタムドメイン", - "Set the following record on your DNS provider to active your custom domain": "カスタムドメインを有効にするには、DNSプロバイダーに次のレコードを設定してください。", + "Set the following record on your DNS provider to active your custom domain": "カスタムドメインを有効にするには、DNS プロバイダーに次のレコードを設定してください。", "Scope: These styles will be applied to your entire blog, including this dashboard.": "適用範囲:これらのスタイルは、このダッシュボードを含むブログ全体に適用されます。", "Support": "サポート", - "CSS variables: xLog provides some built-in CSS variables": "CSS変数:xLogはいくつかのビルトインCSS変数を提供しています。", + "CSS variables: xLog provides some built-in CSS variables": "CSS 変数:xLog はいくつかのビルトイン CSS 変数を提供しています。", "Address": "住所", "Character": "キャラクター", "New operator": "新しいオペレーター", "Operator Address": "オペレーターアドレス", "Operator Character Check": "オペレーターキャラクターチェック", "Warning": "警告", - "Operators have permissions to enter your dashboard, change your settings(excluding xLog subdomain) and post, modify, delete contents on your site.": "シャドー認証では、ダッシュボードへのアクセス、設定の変更(xLogサブドメインを除く)、サイトのコンテンツの公開・修正・削除が可能です。", + "Operators have permissions to enter your dashboard, change your settings(excluding xLog subdomain) and post, modify, delete contents on your site.": "シャドー認証では、ダッシュボードへのアクセス、設定の変更(xLog サブドメインを除く)、サイトのコンテンツの公開・修正・削除が可能です。", "Add": "新規" } diff --git a/public/locales/zh-TW/dashboard.json b/public/locales/zh-TW/dashboard.json index 503e9976..bbd0e14e 100644 --- a/public/locales/zh-TW/dashboard.json +++ b/public/locales/zh-TW/dashboard.json @@ -15,6 +15,7 @@ "Site Duration": "網站運作時間", "days": "天", "Deleted!": "刪掉了哦!", + "Fail to Deleted." : "删除失败", "Converted!": "轉換成功!", "Failed to convert.": "轉換失敗。", "All Posts": "所有文章", diff --git a/public/locales/zh/dashboard.json b/public/locales/zh/dashboard.json index ec768302..a6fee648 100644 --- a/public/locales/zh/dashboard.json +++ b/public/locales/zh/dashboard.json @@ -15,6 +15,7 @@ "Site Duration": "站点运行时间", "days": "天", "Deleted!" : "已删除!", + "Fail to Deleted." : "删除失败", "Converted!" : "已转换!", "Failed to convert." : "转换失败。", "All Posts": "所有文章", diff --git a/src/components/dashboard/PagesManager.tsx b/src/components/dashboard/PagesManager.tsx index baf94f3b..3d84a10f 100644 --- a/src/components/dashboard/PagesManager.tsx +++ b/src/components/dashboard/PagesManager.tsx @@ -1,4 +1,4 @@ -import { Fragment, useEffect, useMemo, useState } from "react" +import { FC, Fragment, useEffect, useMemo, useState } from "react" import { getPageVisibility } from "~/lib/page-helpers" import { useDate } from "~/hooks/useDate" import { TabItem, Tabs } from "../ui/Tabs" @@ -25,6 +25,7 @@ import { Tooltip } from "../ui/Tooltip" import { APP_NAME } from "~/lib/env" import { Trans, useTranslation } from "next-i18next" import { readFiles } from "~/lib/read-files" +import { useGetState } from "~/hooks/useGetState" export const PagesManager: React.FC<{ isPost: boolean @@ -40,38 +41,9 @@ export const PagesManager: React.FC<{ [router.query.visibility], ) - const deletePage = useDeletePage() - const createOrUpdatePage = useCreateOrUpdatePage() - const [convertToastId, setConvertToastId] = useState("") - const [deleteToastId, setDeleteToastId] = useState("") const { t } = useTranslation(["dashboard", "site"]) const date = useDate() - useEffect(() => { - if (deletePage.isSuccess) { - toast.success(t("Deleted!"), { - id: deleteToastId, - }) - } - }, [deletePage.isSuccess, deleteToastId, t]) - - useEffect(() => { - if (createOrUpdatePage.isSuccess) { - toast.success(t("Converted!"), { - id: convertToastId, - }) - } else if (createOrUpdatePage.isError) { - toast.error(t("Failed to convert."), { - id: convertToastId, - }) - } - }, [ - createOrUpdatePage.isSuccess, - createOrUpdatePage.isError, - convertToastId, - t, - ]) - const pages = useGetPagesBySite({ type: isPost ? "post" : "page", site: subdomain!, @@ -125,91 +97,6 @@ export const PagesManager: React.FC<{ } const queryClient = useQueryClient() - const getPageMenuItems = (page: Note) => { - const isCrossbell = !page.applications?.includes("xlog") - return [ - { - text: "Edit", - icon: , - onClick() { - router.push(getPageEditLink(page)) - }, - }, - { - text: - "Convert to " + - (isCrossbell - ? `${APP_NAME} ${isPost ? "Post" : "Page"}` - : isPost - ? "Page" - : "Post"), - icon: , - onClick() { - const toastId = toast.loading("Converting...") - if (isCrossbell) { - setConvertToastId(toastId) - createOrUpdatePage.mutate({ - published: true, - pageId: page.id, - siteId: subdomain, - tags: page.tags - ?.filter((tag) => tag !== "post" && tag !== "page") - ?.join(", "), - isPost: isPost, - applications: page.applications, - }) - } else { - if (!page.metadata) { - const data = getStorage(`draft-${subdomain}-${page.id}`) - data.isPost = !isPost - setStorage(`draft-${subdomain}-${page.id}`, data) - queryClient.invalidateQueries(["getPagesBySite", subdomain]) - queryClient.invalidateQueries(["getPage", page.id]) - toast.success("Converted!", { - id: toastId, - }) - } else { - setConvertToastId(toastId) - createOrUpdatePage.mutate({ - published: true, - pageId: page.id, - siteId: subdomain, - tags: page.tags - ?.filter((tag) => tag !== "post" && tag !== "page") - ?.join(", "), - isPost: !isPost, - applications: page.applications, - }) - } - } - }, - }, - { - text: "Delete", - icon: , - onClick() { - if (!page.metadata) { - const toastId = toast.loading("Deleting...") - delStorage(`draft-${subdomain}-${page.id}`) - Promise.all([ - queryClient.refetchQueries(["getPagesBySite", subdomain]), - queryClient.refetchQueries(["getPage", page.id]), - ]).then(() => { - toast.success("Deleted!", { - id: toastId, - }) - }) - } else { - setDeleteToastId(toast.loading("Deleting...")) - deletePage.mutate({ - site: subdomain, - id: page.id, - }) - } - }, - }, - ] - } const importFile = () => { const input = document.createElement("input") @@ -365,7 +252,7 @@ export const PagesManager: React.FC<{
- {({ open }: { open: boolean }) => ( + {({ open, close }) => ( <> - - {getPageMenuItems(page).map((item) => { - return ( - - - - ) - })} - + + )} @@ -435,3 +307,181 @@ export const PagesManager: React.FC<{ ) } + +const usePageEditLink = (page: { id: string }, isPost: boolean) => { + const router = useRouter() + const subdomain = router.query.subdomain as string + + return `/dashboard/${subdomain}/editor?id=${page.id}&type=${ + isPost ? "post" : "page" + }` +} + +const usePageMenuItems = ({ + page, + isPost, +}: { + page: Note + isPost: boolean +}) => { + const isCrossbell = !page.applications?.includes("xlog") + const router = useRouter() + const createOrUpdatePage = useCreateOrUpdatePage() + + const editLink = usePageEditLink(page, isPost) + const subdomain = router.query.subdomain as string + const queryClient = useQueryClient() + const deletePage = useDeletePage() + + const [convertToastId, setConvertToastId] = useState("") + const [deleteToastId, setDeleteToastId] = useState("") + + const getDeleteToastId = useGetState(deleteToastId) + const getCurrentToastId = useGetState(convertToastId) + const { t } = useTranslation(["dashboard", "site"]) + + useEffect(() => { + if (deletePage.isSuccess) { + toast.success(t("Deleted!"), { + id: getDeleteToastId(), + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [deletePage.isSuccess]) + + useEffect(() => { + if (deletePage.isError) { + toast.error(t("Fail to Deleted."), { + id: getDeleteToastId(), + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [deletePage.isError]) + + useEffect(() => { + if (createOrUpdatePage.isSuccess) { + toast.success(t("Converted!"), { + id: getCurrentToastId(), + }) + } else if (createOrUpdatePage.isError) { + toast.error(t("Failed to convert."), { + id: getCurrentToastId(), + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [createOrUpdatePage.isSuccess, createOrUpdatePage.isError]) + + return [ + { + text: "Edit", + icon: , + onClick() { + router.push(editLink) + }, + }, + { + text: + "Convert to " + + (isCrossbell + ? `${APP_NAME} ${isPost ? "Post" : "Page"}` + : isPost + ? "Page" + : "Post"), + icon: , + onClick() { + const toastId = toast.loading("Converting...") + if (isCrossbell) { + setConvertToastId(toastId) + createOrUpdatePage.mutate({ + published: true, + pageId: page.id, + siteId: subdomain, + tags: page.tags + ?.filter((tag) => tag !== "post" && tag !== "page") + ?.join(", "), + isPost: isPost, + applications: page.applications, + }) + } else { + if (!page.metadata) { + const data = getStorage(`draft-${subdomain}-${page.id}`) + data.isPost = !isPost + setStorage(`draft-${subdomain}-${page.id}`, data) + queryClient.invalidateQueries(["getPagesBySite", subdomain]) + queryClient.invalidateQueries(["getPage", page.id]) + toast.success("Converted!", { + id: toastId, + }) + } else { + setConvertToastId(toastId) + createOrUpdatePage.mutate({ + published: true, + pageId: page.id, + siteId: subdomain, + tags: page.tags + ?.filter((tag) => tag !== "post" && tag !== "page") + ?.join(", "), + isPost: !isPost, + applications: page.applications, + }) + } + } + }, + }, + { + text: "Delete", + icon: , + onClick() { + if (!page.metadata) { + const toastId = toast.loading("Deleting...") + delStorage(`draft-${subdomain}-${page.id}`) + Promise.all([ + queryClient.refetchQueries(["getPagesBySite", subdomain]), + queryClient.refetchQueries(["getPage", page.id]), + ]).then(() => { + toast.success("Deleted!", { + id: toastId, + }) + }) + } else { + setDeleteToastId(toast.loading("Deleting...")) + deletePage.mutate({ + site: subdomain, + id: page.id, + }) + } + }, + }, + ] +} + +const MenuItems: FC<{ + isPost: boolean + page: Note + onClick: () => void +}> = ({ isPost, page, onClick: onClose }) => { + const { t } = useTranslation(["dashboard", "site"]) + + return ( + + {usePageMenuItems({ page, isPost }).map((item) => { + return ( + + + + ) + })} + + ) +} diff --git a/src/hooks/useGetState.ts b/src/hooks/useGetState.ts new file mode 100644 index 00000000..c9bd43a0 --- /dev/null +++ b/src/hooks/useGetState.ts @@ -0,0 +1,8 @@ +import { useEffect, useRef } from "react" + +export const useGetState = (state: any) => { + const ref = useRef(state) + + useEffect(() => void (ref.current = state), [state]) + return () => ref.current +}