Add display frames and improve create-page page management.
Store phone/tablet/photo-frame shells with catalogs, append pages from extra images or PDFs, and reorder drafts with drag-and-drop. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+10
-3
@@ -1,6 +1,6 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import type { EffectId } from "../shared/types";
|
||||
import { DEFAULT_FLIP_MS } from "../shared/types";
|
||||
import type { EffectId, FrameId } from "../shared/types";
|
||||
import { DEFAULT_FLIP_MS, DEFAULT_FRAME, isFrameId } from "../shared/types";
|
||||
import type { DbBundle } from "./db";
|
||||
import { catalogsTable } from "./schema";
|
||||
|
||||
@@ -9,22 +9,26 @@ export type CatalogRow = {
|
||||
deleteToken: string;
|
||||
title: string;
|
||||
effect: EffectId;
|
||||
frame: FrameId;
|
||||
pageCount: number;
|
||||
flipMs: number;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type CatalogInsert = Omit<CatalogRow, "createdAt" | "flipMs"> & {
|
||||
export type CatalogInsert = Omit<CatalogRow, "createdAt" | "flipMs" | "frame"> & {
|
||||
createdAt?: string;
|
||||
flipMs?: number;
|
||||
frame?: FrameId;
|
||||
};
|
||||
|
||||
function toRow(record: typeof catalogsTable.$inferSelect): CatalogRow {
|
||||
const frameRaw = record.frame ?? DEFAULT_FRAME;
|
||||
return {
|
||||
id: record.id,
|
||||
deleteToken: record.deleteToken,
|
||||
title: record.title,
|
||||
effect: record.effect as EffectId,
|
||||
frame: isFrameId(frameRaw) ? frameRaw : DEFAULT_FRAME,
|
||||
pageCount: record.pageCount,
|
||||
flipMs: record.flipMs ?? DEFAULT_FLIP_MS,
|
||||
createdAt: record.createdAt,
|
||||
@@ -36,6 +40,7 @@ export function createCatalogRepo(db: DbBundle) {
|
||||
create(input: CatalogInsert): CatalogRow {
|
||||
const createdAt = input.createdAt ?? new Date().toISOString();
|
||||
const flipMs = input.flipMs ?? DEFAULT_FLIP_MS;
|
||||
const frame = input.frame ?? DEFAULT_FRAME;
|
||||
db.drizzle.insert(catalogsTable).values({
|
||||
id: input.id,
|
||||
deleteToken: input.deleteToken,
|
||||
@@ -43,6 +48,7 @@ export function createCatalogRepo(db: DbBundle) {
|
||||
effect: input.effect,
|
||||
pageCount: input.pageCount,
|
||||
flipMs,
|
||||
frame,
|
||||
createdAt,
|
||||
}).run();
|
||||
db.persist();
|
||||
@@ -51,6 +57,7 @@ export function createCatalogRepo(db: DbBundle) {
|
||||
deleteToken: input.deleteToken,
|
||||
title: input.title,
|
||||
effect: input.effect,
|
||||
frame,
|
||||
pageCount: input.pageCount,
|
||||
flipMs,
|
||||
createdAt,
|
||||
|
||||
@@ -33,6 +33,7 @@ export async function openDatabase(databasePath: string): Promise<DbBundle> {
|
||||
effect TEXT NOT NULL,
|
||||
page_count INTEGER NOT NULL,
|
||||
flip_ms INTEGER NOT NULL DEFAULT 1000,
|
||||
frame TEXT NOT NULL DEFAULT 'none',
|
||||
created_at TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
@@ -42,6 +43,10 @@ export async function openDatabase(databasePath: string): Promise<DbBundle> {
|
||||
if (!colNames.has("flip_ms")) {
|
||||
sqlite.run("ALTER TABLE catalogs ADD COLUMN flip_ms INTEGER NOT NULL DEFAULT 1000");
|
||||
}
|
||||
if (!colNames.has("frame")) {
|
||||
sqlite.run("ALTER TABLE catalogs ADD COLUMN frame TEXT NOT NULL DEFAULT 'none'");
|
||||
console.log("[db] added catalogs.frame column");
|
||||
}
|
||||
|
||||
const persist = () => {
|
||||
fs.mkdirSync(path.dirname(databasePath), { recursive: true });
|
||||
|
||||
@@ -12,6 +12,7 @@ export const messages = {
|
||||
tooLarge: "حجم یا تعداد صفحه بیش از حد مجاز است",
|
||||
badType: "نوع فایل پشتیبانی نمیشود",
|
||||
badEffect: "افکت نامعتبر است",
|
||||
badFrame: "قالب نامعتبر است",
|
||||
notFound: "کاتالوگ پیدا نشد",
|
||||
forbiddenDelete: "اجازه حذف ندارید",
|
||||
rateLimited: "تعداد ساخت از این آدرس بیش از حد مجاز است",
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
clampFlipMs,
|
||||
fileTooLargeMessage,
|
||||
isEffectId,
|
||||
isFrameId,
|
||||
DEFAULT_FRAME,
|
||||
LIMITS,
|
||||
tooManyPagesMessage,
|
||||
type CreateCatalogResponse,
|
||||
@@ -37,6 +39,7 @@ function toPublic(row: {
|
||||
id: string;
|
||||
title: string;
|
||||
effect: CatalogPublic["effect"];
|
||||
frame: CatalogPublic["frame"];
|
||||
pageCount: number;
|
||||
flipMs: number;
|
||||
createdAt: string;
|
||||
@@ -45,6 +48,7 @@ function toPublic(row: {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
effect: row.effect,
|
||||
frame: row.frame,
|
||||
pageCount: row.pageCount,
|
||||
flipMs: row.flipMs,
|
||||
createdAt: row.createdAt,
|
||||
@@ -102,6 +106,10 @@ export function catalogRoutes(opts: {
|
||||
throw new HttpError(400, messages.badEffect);
|
||||
}
|
||||
const flipMs = clampFlipMs(body.flipMs);
|
||||
const frameRaw = typeof body.frame === "string" && body.frame ? body.frame : DEFAULT_FRAME;
|
||||
if (!isFrameId(frameRaw)) {
|
||||
throw new HttpError(400, messages.badFrame);
|
||||
}
|
||||
|
||||
const pages = asFileArray(body.pages);
|
||||
if (pages.length < 1) {
|
||||
@@ -127,6 +135,7 @@ export function catalogRoutes(opts: {
|
||||
effect: effectRaw,
|
||||
pageCount: buffers.length,
|
||||
flipMs,
|
||||
frame: frameRaw,
|
||||
});
|
||||
|
||||
const payload: CreateCatalogResponse = {
|
||||
|
||||
@@ -7,5 +7,6 @@ export const catalogsTable = sqliteTable("catalogs", {
|
||||
effect: text("effect").notNull(),
|
||||
pageCount: integer("page_count").notNull(),
|
||||
flipMs: integer("flip_ms").notNull(),
|
||||
frame: text("frame").notNull(),
|
||||
createdAt: text("created_at").notNull(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user