225 lines
6.5 KiB
TypeScript
225 lines
6.5 KiB
TypeScript
import { useTranslation } from "next-i18next"
|
|
import { useRouter } from "next/router"
|
|
import { FC, useEffect, useState } from "react"
|
|
import toast from "react-hot-toast"
|
|
import type { Note } from "unidata.js"
|
|
|
|
import { Menu } from "@headlessui/react"
|
|
import { useQueryClient } from "@tanstack/react-query"
|
|
|
|
import { useGetState } from "~/hooks/useGetState"
|
|
import { APP_NAME } from "~/lib/env"
|
|
import { getNoteSlugFromNote, getTwitterShareUrl } from "~/lib/helpers"
|
|
import { delStorage, getStorage, setStorage } from "~/lib/storage"
|
|
import { useCreateOrUpdatePage, useDeletePage } from "~/queries/page"
|
|
import { useGetSite } from "~/queries/site"
|
|
|
|
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"
|
|
}`
|
|
}
|
|
|
|
interface Item {
|
|
text: string
|
|
icon: JSX.Element
|
|
onClick: () => void
|
|
}
|
|
export const PagesManagerMenu: FC<{
|
|
isPost: boolean
|
|
page: Note
|
|
onClick: () => void
|
|
}> = ({ isPost, page, onClick: onClose }) => {
|
|
const { t } = useTranslation(["dashboard", "site"])
|
|
|
|
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)
|
|
|
|
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])
|
|
|
|
const site = useGetSite(subdomain)
|
|
|
|
const items: Item[] = [
|
|
{
|
|
text: "Edit",
|
|
icon: <span className="i-mingcute:edit-line inline-block"></span>,
|
|
onClick() {
|
|
router.push(editLink)
|
|
},
|
|
},
|
|
{
|
|
text:
|
|
"Convert to " +
|
|
(isCrossbell
|
|
? `${APP_NAME} ${isPost ? "Post" : "Page"}`
|
|
: isPost
|
|
? "Page"
|
|
: "Post"),
|
|
icon: <span className="i-mingcute:transfer-3-line inline-block"></span>,
|
|
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: "Preview",
|
|
icon: <span className="i-mingcute:eye-line inline-block"></span>,
|
|
onClick() {
|
|
const slug = getNoteSlugFromNote(page)
|
|
if (!slug) return
|
|
window.open(`/_site/${subdomain}/${slug}`)
|
|
},
|
|
},
|
|
{
|
|
text: "Share to Twitter",
|
|
icon: <span className="i-mingcute:twitter-line inline-block"></span>,
|
|
onClick() {
|
|
if (site.data) {
|
|
const twitterShareUrl = getTwitterShareUrl({
|
|
page,
|
|
site: site.data,
|
|
t,
|
|
})
|
|
window.open(twitterShareUrl)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
text: "Delete",
|
|
icon: <span className="i-mingcute:delete-2-line inline-block"></span>,
|
|
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,
|
|
})
|
|
}
|
|
},
|
|
},
|
|
]
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (getDeleteToastId()) {
|
|
toast.success(t("Deleted!"), {
|
|
id: getDeleteToastId(),
|
|
})
|
|
}
|
|
}
|
|
}, [])
|
|
|
|
return (
|
|
<Menu.Items className="text-sm absolute z-20 right-0 bg-white shadow-modal rounded-lg overflow-hidden py-2 w-64 ring-1 ring-border">
|
|
{items.map((item) => {
|
|
return (
|
|
<Menu.Item key={item.text}>
|
|
<button
|
|
type="button"
|
|
className="h-10 flex w-full space-x-2 items-center px-3 hover:bg-gray-100"
|
|
onClick={(e) => {
|
|
e.preventDefault()
|
|
item.onClick()
|
|
onClose()
|
|
}}
|
|
>
|
|
<span className="inline-flex">{item.icon}</span>
|
|
<span>{t(item.text)}</span>
|
|
</button>
|
|
</Menu.Item>
|
|
)
|
|
})}
|
|
</Menu.Items>
|
|
)
|
|
}
|