UNPKG

nitropage

Version:

A free and open source, extensible visual page builder based on SolidStart.

179 lines (151 loc) 4.84 kB
import { State } from "#../types"; import { createId } from "@paralleldrive/cuid2"; import { NitroPage } from "@prisma/client"; import type { Prisma } from "@prisma/client/extension"; import { isEqual, pick } from "es-toolkit"; import { logger } from "../../log"; import { useDatabase } from "../../prisma"; import { DEBUG_PAGE_UPDATE } from "../../util"; import { normalizeUrlPath } from "../../util/string"; import { omitDisabledElementData } from "./elementData"; type CreateMany<T> = Exclude<Prisma.Args<T, "createMany">["data"], any[]>[]; const createPageRevision = async ({ state: state, prevState, page, deletedPresets, }: { state: State; prevState?: State; page: NitroPage; deletedPresets: string[]; }) => { const db = useDatabase(); for (const id of Object.keys(state.elements)) { const item = state.elements[id]; item.data = omitDisabledElementData(item.data); } const revisionKeys = [ "elements", "slots", "title", "urlPath", ] as const satisfies (keyof State)[]; const localState = pick(state, revisionKeys); if (isEqual(localState, pick(prevState ?? ({} as never), revisionKeys))) return; const layouts = ( await db.nitroPage.findMany({ select: { id: true }, where: { type: "layout", }, }) ).map((p) => p.id); const layoutSlots = ( await db.nitroLayoutSlot.findMany({ select: { id: true } }) ).map((p) => p.id); if (DEBUG_PAGE_UPDATE) logger.info("Create page revision"); const pageRevision = await db.nitroPageRevision.create({ data: { title: localState.title, urlPath: normalizeUrlPath(localState.urlPath), savedAt: new Date(), page: { connect: { id: page.id, }, }, }, }); const insertSlots = async ( items: [id: string, parentElementId?: string][], ) => { const slotDataItems: CreateMany<typeof db.nitroElementSlot> = []; const idMap: Record<string, string> = {}; for (const [id, parentElementId] of items) { const stateSlot = localState.slots[id]; if (!stateSlot.elements.length && !stateSlot.layoutSlotId) continue; const parentElement = localState.elements[stateSlot.parentElementId]; const parentLayoutSlotId = parentElement.layoutId ? stateSlot.key : undefined; if (parentLayoutSlotId && !layoutSlots.includes(parentLayoutSlotId)) { logger.warn("Skipped saving slot, parent layout slot didn't exist"); continue; } const nextId = createId(); idMap[id] = nextId; slotDataItems.push({ id: nextId, key: stateSlot.key, layoutSlotId: stateSlot.layoutSlotId, parentElementId: parentElementId, parentPageRevisionId: !parentElementId ? pageRevision.id : undefined, parentLayoutSlotId, }); } await db.nitroElementSlot.createMany({ data: slotDataItems, }); for (const [id] of items) { const nextId = idMap[id]; if (!nextId) continue; const stateSlot = localState.slots[id]; await insertSlotElements(stateSlot.elements, nextId); } }; const insertSlotElements = async function ( elementIds: string[], slotId?: string, ) { if (!elementIds.length) return; const elementDataItems: CreateMany<typeof db.nitroElement> = []; const slots: [string, string][] = []; let position = -1; for (const prevId of elementIds) { const element = localState.elements[prevId]; if (element.layoutId && !layouts.includes(element.layoutId)) { logger.warn("Skipped saving layout element, layout didn't exist"); continue; } if (element.presetId && deletedPresets.includes(element.presetId)) { element.presetId = undefined; } if (!element.historyId) { const history = await db.nitroElementHistory.create({ data: { pageId: page.id, }, }); element.historyId = history.id; } else { element.id = createId(); } elementDataItems.push({ id: element.id, blueprintId: element.blueprintId, layoutId: element.layoutId, position: position++, parentSlotId: slotId, presetId: element.presetId, historyId: element.historyId!, pageId: pageRevision.id, data: JSON.stringify(element.data), }); const newSlots = Object.values(element.slots).map( (s) => [s, element.id] as [string, string], ); slots.push(...newSlots); } await db.nitroElement.createMany({ data: elementDataItems, }); await insertSlots(slots); }; await insertSlots( Object.values(localState.elements.root.slots).map((s) => [s]), ); return pageRevision; }; export default createPageRevision;