fix: component and state separated, fixed #240

This commit is contained in:
Innei 2023-04-06 19:59:01 +08:00
parent afb3d6ee87
commit 5d5c437089
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
2 changed files with 178 additions and 136 deletions

View File

@ -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: <span className="i-mingcute:edit-line inline-block"></span>,
onClick() {
router.push(getPageEditLink(page))
},
},
{
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: "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,
})
}
},
},
]
}
const importFile = () => {
const input = document.createElement("input")
@ -380,27 +267,8 @@ export const PagesManager: React.FC<{
<i className="i-mingcute:more-1-line text-2xl" />
</button>
</Menu.Button>
<Menu.Items className="text-sm absolute z-20 right-0 bg-white shadow-modal rounded-lg overflow-hidden py-2 w-64">
{getPageMenuItems(page).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()
}}
>
<span className="inline-flex">
{item.icon}
</span>
<span>{t(item.text)}</span>
</button>
</Menu.Item>
)
})}
</Menu.Items>
<MenuItems isPost={isPost} page={page} />
</>
)}
</Menu>
@ -435,3 +303,169 @@ export const PagesManager: React.FC<{
</DashboardMain>
)
}
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 (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: <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: "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,
})
}
},
},
]
}
const MenuItems: FC<{
isPost: boolean
page: Note
}> = ({ isPost, page }) => {
const { t } = useTranslation(["dashboard", "site"])
return (
<Menu.Items className="text-sm absolute z-20 right-0 bg-white shadow-modal rounded-lg overflow-hidden py-2 w-64">
{usePageMenuItems({ page, isPost }).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()
}}
>
<span className="inline-flex">{item.icon}</span>
<span>{t(item.text)}</span>
</button>
</Menu.Item>
)
})}
</Menu.Items>
)
}

8
src/hooks/useGetState.ts Normal file
View File

@ -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
}