feat: crossbell renovation - support navigation

This commit is contained in:
DIYgod 2022-06-29 22:43:26 +01:00
parent 9a67856d6e
commit 3bed9051e5
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
4 changed files with 48 additions and 30 deletions

View File

@ -43,7 +43,7 @@ export const SitePage: React.FC<{
contentHTML={page.body?.content || ""}
className="my-10"
/>
<div className="bg-gray-100 rounded-lg py-8 px-5">
<div className="bg-gray-100 rounded-lg py-8 px-5 break-all">
<p className="text-gray-700 mb-4">🔔 This post has been permanently stored on-chain and signed by its creator.</p>
<ul className="text-gray-600 text-sm leading-6">
<li className="mt-2">

View File

@ -1,4 +1,4 @@
import type { Note as UniNote } from "unidata.js"
import type { Note as UniNote, Profile as UniProfile } from "unidata.js"
export type Site = {
id: string
@ -78,4 +78,13 @@ export type Note = UniNote & {
export type Notes = {
total: number,
list: Note[],
}
export type Profile = UniProfile & {
navigation?: SiteNavigationItem[]
}
export type Profiles = {
total: number,
list: Note[],
}

View File

@ -3,7 +3,7 @@ import { isUUID } from "~/lib/uuid"
import { MembershipRole, PageType, type Site } from "~/lib/db.server"
import { Gate } from "~/lib/gate.server"
import { sendLoginEmail } from "~/lib/mailgun.server"
import { SiteNavigationItem } from "~/lib/types"
import { SiteNavigationItem, Profile } from "~/lib/types"
import { nanoid } from "nanoid"
import { getMembership } from "./membership"
import { checkReservedWords } from "~/lib/reserved-words"
@ -60,7 +60,11 @@ export const getSite = async (input: string) => {
platform: 'Crossbell',
});
const site = profiles.list?.sort((a, b) => +new Date(b.date_updated || 0) - +new Date(a.date_updated || 0))?.[0]
const site: Profile = profiles.list?.sort((a, b) => +new Date(b.date_updated || 0) - +new Date(a.date_updated || 0))?.[0]
const navigation = site.tags?.find(tag => tag.startsWith('navigation:'))?.replace('navigation:', "")
if (navigation) {
site.navigation = JSON.parse(navigation)
}
if (!site) return null
@ -92,7 +96,7 @@ export async function updateSite(
description?: string
icon?: string | null
subdomain?: string
navigation?: SiteNavigationItem[] // TODO
navigation?: SiteNavigationItem[]
},
) {
return await unidata.profiles.set({
@ -105,6 +109,7 @@ export async function updateSite(
...(payload.description && { bio: payload.description }),
...(payload.icon && { avatars: [payload.icon] }),
...(payload.subdomain && { username: payload.subdomain }),
...(payload.navigation && { tags: ['navigation:' + JSON.stringify(payload.navigation)] }),
})
}

View File

@ -6,10 +6,11 @@ import { SettingsLayout } from "~/components/dashboard/SettingsLayout"
import { useRouter } from "next/router"
import { DashboardLayout } from "~/components/dashboard/DashboardLayout"
import { trpc } from "~/lib/trpc"
import { SiteNavigationItem } from "~/lib/types"
import { SiteNavigationItem, Profile } from "~/lib/types"
import { nanoid } from "nanoid"
import { ReactSortable } from "react-sortablejs"
import equal from "fast-deep-equal"
import { getSite, updateSite } from "~/models/site.model"
type UpdateItem = (id: string, newItem: Partial<SiteNavigationItem>) => void
@ -69,20 +70,23 @@ export default function SiteSettingsNavigationPage() {
const router = useRouter()
const subdomain = router.query.subdomain as string
const trpcContext = trpc.useContext()
const siteResult = trpc.useQuery(["site", { site: subdomain }], {
enabled: !!subdomain,
refetchOnWindowFocus: false,
})
const updateSite = trpc.useMutation("site.updateSite")
let [site, setSite] = useState<Profile | null>(null)
useEffect(() => {
if (subdomain) {
getSite(subdomain).then((site) => {
setSite(site)
})
}
}, [subdomain])
const [items, setItems] = useState<SiteNavigationItem[]>([])
const itemsModified = useMemo(() => {
if (!siteResult.data) return false
return !equal(items, siteResult.data.navigation)
}, [items, siteResult.data])
if (!site) return false
return !equal(items, site.navigation)
}, [items, site])
const updateItem: UpdateItem = (id, newItem) => {
setItems((items) => {
@ -99,11 +103,20 @@ export default function SiteSettingsNavigationPage() {
setItems((items) => [...items, { id: nanoid(), label: "", url: "" }])
}
let [loading, setLoading] = useState<boolean>(false)
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
updateSite.mutate({
site: siteResult.data!.id,
setLoading(true)
updateSite({
site: site!.username!,
navigation: items,
}).then((result) => {
if (result.code === 0) {
toast.success("Saved")
} else {
toast.error("Failed to save" + ": " + result.message)
}
setLoading(false)
})
}
@ -112,18 +125,10 @@ export default function SiteSettingsNavigationPage() {
}
useEffect(() => {
if (siteResult.data?.navigation) {
setItems(siteResult.data.navigation)
if (site?.navigation) {
setItems(site.navigation)
}
}, [siteResult.data?.navigation])
useEffect(() => {
if (updateSite.isSuccess) {
updateSite.reset()
toast.success("Saved")
trpcContext.invalidateQueries(["site", { site: subdomain }])
}
}, [updateSite, trpcContext, subdomain])
}, [site?.navigation])
return (
<DashboardLayout title="Navigation">
@ -135,7 +140,6 @@ export default function SiteSettingsNavigationPage() {
No navigation items yet
</div>
)}
{/** @ts-expect-error */}
<ReactSortable
list={items}
setList={setItems}
@ -161,7 +165,7 @@ export default function SiteSettingsNavigationPage() {
<div className="border-t pt-5 mt-10 space-x-3 flex items-center">
<Button
type="submit"
isLoading={updateSite.isLoading}
isLoading={loading}
isDisabled={!itemsModified}
>
Save