mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-01 17:10:24 +08:00
Compare commits
1 Commits
renovate/n
...
chore/remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d43bcfb87 |
@@ -12,7 +12,6 @@ import {
|
|||||||
import { useDiagram } from "@/contexts/diagram-context"
|
import { useDiagram } from "@/contexts/diagram-context"
|
||||||
import { type DrawioTheme, isDrawioTheme } from "@/lib/drawio-themes"
|
import { type DrawioTheme, isDrawioTheme } from "@/lib/drawio-themes"
|
||||||
import { i18n, type Locale } from "@/lib/i18n/config"
|
import { i18n, type Locale } from "@/lib/i18n/config"
|
||||||
import { isIndexedDBUsable } from "@/lib/session-storage"
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const {
|
const {
|
||||||
@@ -33,8 +32,6 @@ export default function Home() {
|
|||||||
const [isLoaded, setIsLoaded] = useState(false)
|
const [isLoaded, setIsLoaded] = useState(false)
|
||||||
const [isDrawioReady, setIsDrawioReady] = useState(false)
|
const [isDrawioReady, setIsDrawioReady] = useState(false)
|
||||||
const [isElectron, setIsElectron] = useState(false)
|
const [isElectron, setIsElectron] = useState(false)
|
||||||
const [canPersist, setCanPersist] = useState(false)
|
|
||||||
const [canPersistChecked, setCanPersistChecked] = useState(false)
|
|
||||||
const [drawioBaseUrl, setDrawioBaseUrl] = useState(
|
const [drawioBaseUrl, setDrawioBaseUrl] = useState(
|
||||||
process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net",
|
process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net",
|
||||||
)
|
)
|
||||||
@@ -85,11 +82,6 @@ export default function Home() {
|
|||||||
setDrawioBaseUrl(`${window.location.origin}/drawio/index.html`)
|
setDrawioBaseUrl(`${window.location.origin}/drawio/index.html`)
|
||||||
}
|
}
|
||||||
|
|
||||||
void (async () => {
|
|
||||||
const usable = await isIndexedDBUsable()
|
|
||||||
setCanPersist(usable)
|
|
||||||
setCanPersistChecked(true)
|
|
||||||
})()
|
|
||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
}, [pathname, router])
|
}, [pathname, router])
|
||||||
|
|
||||||
@@ -98,13 +90,6 @@ export default function Home() {
|
|||||||
onDrawioLoad()
|
onDrawioLoad()
|
||||||
}, [onDrawioLoad])
|
}, [onDrawioLoad])
|
||||||
|
|
||||||
const handleDrawioAutoSave = useCallback(
|
|
||||||
(data: { xml?: string }) => {
|
|
||||||
handleDiagramAutoSave(data)
|
|
||||||
},
|
|
||||||
[handleDiagramAutoSave],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleDarkModeChange = () => {
|
const handleDarkModeChange = () => {
|
||||||
const newValue = !darkMode
|
const newValue = !darkMode
|
||||||
setDarkMode(newValue)
|
setDarkMode(newValue)
|
||||||
@@ -187,7 +172,7 @@ export default function Home() {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="h-full rounded-xl overflow-hidden shadow-soft-lg border border-border/30 relative">
|
<div className="h-full rounded-xl overflow-hidden shadow-soft-lg border border-border/30 relative">
|
||||||
{isLoaded && canPersistChecked && (
|
{isLoaded && (
|
||||||
<div
|
<div
|
||||||
className={`h-full w-full ${isDrawioReady ? "" : "invisible absolute inset-0"}`}
|
className={`h-full w-full ${isDrawioReady ? "" : "invisible absolute inset-0"}`}
|
||||||
>
|
>
|
||||||
@@ -195,24 +180,14 @@ export default function Home() {
|
|||||||
key={`${drawioUi}-${darkMode}-${currentLang}-${isElectron}`}
|
key={`${drawioUi}-${darkMode}-${currentLang}-${isElectron}`}
|
||||||
ref={drawioRef}
|
ref={drawioRef}
|
||||||
autosave
|
autosave
|
||||||
onAutoSave={handleDrawioAutoSave}
|
onAutoSave={handleDiagramAutoSave}
|
||||||
onExport={handleDiagramExport}
|
onExport={handleDiagramExport}
|
||||||
onLoad={handleDrawioLoad}
|
onLoad={handleDrawioLoad}
|
||||||
baseUrl={drawioBaseUrl}
|
baseUrl={drawioBaseUrl}
|
||||||
configuration={
|
|
||||||
canPersist
|
|
||||||
? { confirmExit: false }
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
urlParameters={{
|
urlParameters={{
|
||||||
ui: drawioUi,
|
ui: drawioUi,
|
||||||
spin: false,
|
spin: false,
|
||||||
libraries: false,
|
libraries: false,
|
||||||
// Disable modified tracking only when persistence is available
|
|
||||||
...(canPersist && {
|
|
||||||
modified: false,
|
|
||||||
keepmodified: false,
|
|
||||||
}),
|
|
||||||
saveAndExit: false,
|
saveAndExit: false,
|
||||||
noSaveBtn: true,
|
noSaveBtn: true,
|
||||||
noExitBtn: true,
|
noExitBtn: true,
|
||||||
|
|||||||
@@ -58,33 +58,6 @@ interface ChatSessionDB extends DBSchema {
|
|||||||
|
|
||||||
// Database singleton
|
// Database singleton
|
||||||
let dbPromise: Promise<IDBPDatabase<ChatSessionDB>> | null = null
|
let dbPromise: Promise<IDBPDatabase<ChatSessionDB>> | null = null
|
||||||
const resetDBPromise = () => {
|
|
||||||
dbPromise = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const isClosingError = (error: unknown): boolean => {
|
|
||||||
return (
|
|
||||||
error instanceof DOMException &&
|
|
||||||
error.name === "InvalidStateError" &&
|
|
||||||
/closing/i.test(error.message)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const withDB = async <T>(
|
|
||||||
action: (db: IDBPDatabase<ChatSessionDB>) => Promise<T>,
|
|
||||||
): Promise<T> => {
|
|
||||||
try {
|
|
||||||
const db = await getDB()
|
|
||||||
return await action(db)
|
|
||||||
} catch (error) {
|
|
||||||
if (isClosingError(error)) {
|
|
||||||
resetDBPromise()
|
|
||||||
const db = await getDB()
|
|
||||||
return await action(db)
|
|
||||||
}
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getDB(): Promise<IDBPDatabase<ChatSessionDB>> {
|
async function getDB(): Promise<IDBPDatabase<ChatSessionDB>> {
|
||||||
if (!dbPromise) {
|
if (!dbPromise) {
|
||||||
@@ -115,23 +88,7 @@ async function getDB(): Promise<IDBPDatabase<ChatSessionDB>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
terminated() {
|
|
||||||
resetDBPromise()
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
dbPromise
|
|
||||||
.then((db) => {
|
|
||||||
db.onversionchange = () => {
|
|
||||||
db.close()
|
|
||||||
resetDBPromise()
|
|
||||||
}
|
|
||||||
db.onclose = () => {
|
|
||||||
resetDBPromise()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
resetDBPromise()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return dbPromise
|
return dbPromise
|
||||||
}
|
}
|
||||||
@@ -146,46 +103,31 @@ export function isIndexedDBAvailable(): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if IndexedDB is actually usable (not just present).
|
|
||||||
// Note: Do NOT close the db here - getDB() returns a shared singleton connection
|
|
||||||
// that other code depends on.
|
|
||||||
export async function isIndexedDBUsable(): Promise<boolean> {
|
|
||||||
if (!isIndexedDBAvailable()) return false
|
|
||||||
try {
|
|
||||||
await getDB()
|
|
||||||
return true
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRUD Operations
|
// CRUD Operations
|
||||||
export async function getAllSessionMetadata(): Promise<SessionMetadata[]> {
|
export async function getAllSessionMetadata(): Promise<SessionMetadata[]> {
|
||||||
if (!isIndexedDBAvailable()) return []
|
if (!isIndexedDBAvailable()) return []
|
||||||
try {
|
try {
|
||||||
return await withDB(async (db) => {
|
const db = await getDB()
|
||||||
const tx = db.transaction(STORE_NAME, "readonly")
|
const tx = db.transaction(STORE_NAME, "readonly")
|
||||||
const index = tx.store.index("by-updated")
|
const index = tx.store.index("by-updated")
|
||||||
const metadata: SessionMetadata[] = []
|
const metadata: SessionMetadata[] = []
|
||||||
|
|
||||||
// Use cursor to read only metadata fields (avoids loading full messages/XML)
|
// Use cursor to read only metadata fields (avoids loading full messages/XML)
|
||||||
let cursor = await index.openCursor(null, "prev") // newest first
|
let cursor = await index.openCursor(null, "prev") // newest first
|
||||||
while (cursor) {
|
while (cursor) {
|
||||||
const s = cursor.value
|
const s = cursor.value
|
||||||
metadata.push({
|
metadata.push({
|
||||||
id: s.id,
|
id: s.id,
|
||||||
title: s.title,
|
title: s.title,
|
||||||
createdAt: s.createdAt,
|
createdAt: s.createdAt,
|
||||||
updatedAt: s.updatedAt,
|
updatedAt: s.updatedAt,
|
||||||
messageCount: s.messages.length,
|
messageCount: s.messages.length,
|
||||||
hasDiagram:
|
hasDiagram: !!s.diagramXml && s.diagramXml.trim().length > 0,
|
||||||
!!s.diagramXml && s.diagramXml.trim().length > 0,
|
thumbnailDataUrl: s.thumbnailDataUrl,
|
||||||
thumbnailDataUrl: s.thumbnailDataUrl,
|
})
|
||||||
})
|
cursor = await cursor.continue()
|
||||||
cursor = await cursor.continue()
|
}
|
||||||
}
|
return metadata
|
||||||
return metadata
|
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to get session metadata:", error)
|
console.error("Failed to get session metadata:", error)
|
||||||
return []
|
return []
|
||||||
@@ -195,9 +137,8 @@ export async function getAllSessionMetadata(): Promise<SessionMetadata[]> {
|
|||||||
export async function getSession(id: string): Promise<ChatSession | null> {
|
export async function getSession(id: string): Promise<ChatSession | null> {
|
||||||
if (!isIndexedDBAvailable()) return null
|
if (!isIndexedDBAvailable()) return null
|
||||||
try {
|
try {
|
||||||
return await withDB(async (db) => {
|
const db = await getDB()
|
||||||
return (await db.get(STORE_NAME, id)) || null
|
return (await db.get(STORE_NAME, id)) || null
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to get session:", error)
|
console.error("Failed to get session:", error)
|
||||||
return null
|
return null
|
||||||
@@ -207,9 +148,8 @@ export async function getSession(id: string): Promise<ChatSession | null> {
|
|||||||
export async function saveSession(session: ChatSession): Promise<boolean> {
|
export async function saveSession(session: ChatSession): Promise<boolean> {
|
||||||
if (!isIndexedDBAvailable()) return false
|
if (!isIndexedDBAvailable()) return false
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
await db.put(STORE_NAME, session)
|
await db.put(STORE_NAME, session)
|
||||||
})
|
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle quota exceeded
|
// Handle quota exceeded
|
||||||
@@ -221,9 +161,8 @@ export async function saveSession(session: ChatSession): Promise<boolean> {
|
|||||||
await deleteOldestSession()
|
await deleteOldestSession()
|
||||||
// Retry once
|
// Retry once
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
await db.put(STORE_NAME, session)
|
await db.put(STORE_NAME, session)
|
||||||
})
|
|
||||||
return true
|
return true
|
||||||
} catch (retryError) {
|
} catch (retryError) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -242,9 +181,8 @@ export async function saveSession(session: ChatSession): Promise<boolean> {
|
|||||||
export async function deleteSession(id: string): Promise<void> {
|
export async function deleteSession(id: string): Promise<void> {
|
||||||
if (!isIndexedDBAvailable()) return
|
if (!isIndexedDBAvailable()) return
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
await db.delete(STORE_NAME, id)
|
await db.delete(STORE_NAME, id)
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete session:", error)
|
console.error("Failed to delete session:", error)
|
||||||
}
|
}
|
||||||
@@ -253,9 +191,8 @@ export async function deleteSession(id: string): Promise<void> {
|
|||||||
export async function getSessionCount(): Promise<number> {
|
export async function getSessionCount(): Promise<number> {
|
||||||
if (!isIndexedDBAvailable()) return 0
|
if (!isIndexedDBAvailable()) return 0
|
||||||
try {
|
try {
|
||||||
return await withDB(async (db) => {
|
const db = await getDB()
|
||||||
return await db.count(STORE_NAME)
|
return await db.count(STORE_NAME)
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to get session count:", error)
|
console.error("Failed to get session count:", error)
|
||||||
return 0
|
return 0
|
||||||
@@ -265,15 +202,14 @@ export async function getSessionCount(): Promise<number> {
|
|||||||
export async function deleteOldestSession(): Promise<void> {
|
export async function deleteOldestSession(): Promise<void> {
|
||||||
if (!isIndexedDBAvailable()) return
|
if (!isIndexedDBAvailable()) return
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
const tx = db.transaction(STORE_NAME, "readwrite")
|
const tx = db.transaction(STORE_NAME, "readwrite")
|
||||||
const index = tx.store.index("by-updated")
|
const index = tx.store.index("by-updated")
|
||||||
const cursor = await index.openCursor()
|
const cursor = await index.openCursor()
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
await cursor.delete()
|
await cursor.delete()
|
||||||
}
|
}
|
||||||
await tx.done
|
await tx.done
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete oldest session:", error)
|
console.error("Failed to delete oldest session:", error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,33 +57,6 @@ export function generateDefaultTitle(prompt: string): string {
|
|||||||
|
|
||||||
// Database singleton
|
// Database singleton
|
||||||
let dbPromise: Promise<IDBPDatabase<TemplateDB>> | null = null
|
let dbPromise: Promise<IDBPDatabase<TemplateDB>> | null = null
|
||||||
const resetDBPromise = () => {
|
|
||||||
dbPromise = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const isClosingError = (error: unknown): boolean => {
|
|
||||||
return (
|
|
||||||
error instanceof DOMException &&
|
|
||||||
error.name === "InvalidStateError" &&
|
|
||||||
/closing/i.test(error.message)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const withDB = async <T>(
|
|
||||||
action: (db: IDBPDatabase<TemplateDB>) => Promise<T>,
|
|
||||||
): Promise<T> => {
|
|
||||||
try {
|
|
||||||
const db = await getDB()
|
|
||||||
return await action(db)
|
|
||||||
} catch (error) {
|
|
||||||
if (isClosingError(error)) {
|
|
||||||
resetDBPromise()
|
|
||||||
const db = await getDB()
|
|
||||||
return await action(db)
|
|
||||||
}
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getDB(): Promise<IDBPDatabase<TemplateDB>> {
|
async function getDB(): Promise<IDBPDatabase<TemplateDB>> {
|
||||||
if (!dbPromise) {
|
if (!dbPromise) {
|
||||||
@@ -101,23 +74,7 @@ async function getDB(): Promise<IDBPDatabase<TemplateDB>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
terminated() {
|
|
||||||
resetDBPromise()
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
dbPromise
|
|
||||||
.then((db) => {
|
|
||||||
db.onversionchange = () => {
|
|
||||||
db.close()
|
|
||||||
resetDBPromise()
|
|
||||||
}
|
|
||||||
db.onclose = () => {
|
|
||||||
resetDBPromise()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
resetDBPromise()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return dbPromise
|
return dbPromise
|
||||||
}
|
}
|
||||||
@@ -137,10 +94,9 @@ export function isIndexedDBAvailable(): boolean {
|
|||||||
export async function getAllTemplates(): Promise<Template[]> {
|
export async function getAllTemplates(): Promise<Template[]> {
|
||||||
if (!isIndexedDBAvailable()) return []
|
if (!isIndexedDBAvailable()) return []
|
||||||
try {
|
try {
|
||||||
return await withDB(async (db) => {
|
const db = await getDB()
|
||||||
const templates = await db.getAll(STORE_NAME)
|
const templates = await db.getAll(STORE_NAME)
|
||||||
return sortTemplates(templates)
|
return sortTemplates(templates)
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to get templates:", error)
|
console.error("Failed to get templates:", error)
|
||||||
return []
|
return []
|
||||||
@@ -150,9 +106,8 @@ export async function getAllTemplates(): Promise<Template[]> {
|
|||||||
export async function getTemplate(id: string): Promise<Template | null> {
|
export async function getTemplate(id: string): Promise<Template | null> {
|
||||||
if (!isIndexedDBAvailable()) return null
|
if (!isIndexedDBAvailable()) return null
|
||||||
try {
|
try {
|
||||||
return await withDB(async (db) => {
|
const db = await getDB()
|
||||||
return (await db.get(STORE_NAME, id)) || null
|
return (await db.get(STORE_NAME, id)) || null
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to get template:", error)
|
console.error("Failed to get template:", error)
|
||||||
return null
|
return null
|
||||||
@@ -182,9 +137,8 @@ export async function createTemplate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
await db.put(STORE_NAME, template)
|
await db.put(STORE_NAME, template)
|
||||||
})
|
|
||||||
return template
|
return template
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to create template:", error)
|
console.error("Failed to create template:", error)
|
||||||
@@ -198,20 +152,19 @@ export async function updateTemplate(
|
|||||||
): Promise<Template | null> {
|
): Promise<Template | null> {
|
||||||
if (!isIndexedDBAvailable()) return null
|
if (!isIndexedDBAvailable()) return null
|
||||||
try {
|
try {
|
||||||
return await withDB(async (db) => {
|
const db = await getDB()
|
||||||
const existing = await db.get(STORE_NAME, id)
|
const existing = await db.get(STORE_NAME, id)
|
||||||
if (!existing) return null
|
if (!existing) return null
|
||||||
|
|
||||||
const updated: Template = {
|
const updated: Template = {
|
||||||
...existing,
|
...existing,
|
||||||
...updates,
|
...updates,
|
||||||
id: existing.id,
|
id: existing.id,
|
||||||
createdAt: existing.createdAt,
|
createdAt: existing.createdAt,
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
}
|
}
|
||||||
await db.put(STORE_NAME, updated)
|
await db.put(STORE_NAME, updated)
|
||||||
return updated
|
return updated
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to update template:", error)
|
console.error("Failed to update template:", error)
|
||||||
return null
|
return null
|
||||||
@@ -221,9 +174,8 @@ export async function updateTemplate(
|
|||||||
export async function deleteTemplate(id: string): Promise<boolean> {
|
export async function deleteTemplate(id: string): Promise<boolean> {
|
||||||
if (!isIndexedDBAvailable()) return false
|
if (!isIndexedDBAvailable()) return false
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
await db.delete(STORE_NAME, id)
|
await db.delete(STORE_NAME, id)
|
||||||
})
|
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete template:", error)
|
console.error("Failed to delete template:", error)
|
||||||
@@ -237,25 +189,24 @@ export async function duplicateTemplate(
|
|||||||
): Promise<Template | null> {
|
): Promise<Template | null> {
|
||||||
if (!isIndexedDBAvailable()) return null
|
if (!isIndexedDBAvailable()) return null
|
||||||
try {
|
try {
|
||||||
return await withDB(async (db) => {
|
const db = await getDB()
|
||||||
const existing = await db.get(STORE_NAME, id)
|
const existing = await db.get(STORE_NAME, id)
|
||||||
if (!existing) return null
|
if (!existing) return null
|
||||||
|
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const duplicate: Template = {
|
const duplicate: Template = {
|
||||||
...existing,
|
...existing,
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
title: `${existing.title} ${copySuffix}`,
|
title: `${existing.title} ${copySuffix}`,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
clickCount: 0,
|
clickCount: 0,
|
||||||
runCount: 0,
|
runCount: 0,
|
||||||
lastUsedAt: 0,
|
lastUsedAt: 0,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
}
|
}
|
||||||
await db.put(STORE_NAME, duplicate)
|
await db.put(STORE_NAME, duplicate)
|
||||||
return duplicate
|
return duplicate
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to duplicate template:", error)
|
console.error("Failed to duplicate template:", error)
|
||||||
return null
|
return null
|
||||||
@@ -267,13 +218,12 @@ export async function duplicateTemplate(
|
|||||||
export async function incrementClickCount(id: string): Promise<void> {
|
export async function incrementClickCount(id: string): Promise<void> {
|
||||||
if (!isIndexedDBAvailable()) return
|
if (!isIndexedDBAvailable()) return
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
const template = await db.get(STORE_NAME, id)
|
const template = await db.get(STORE_NAME, id)
|
||||||
if (!template) return
|
if (!template) return
|
||||||
template.clickCount += 1
|
template.clickCount += 1
|
||||||
template.updatedAt = Date.now()
|
template.updatedAt = Date.now()
|
||||||
await db.put(STORE_NAME, template)
|
await db.put(STORE_NAME, template)
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to increment click count:", error)
|
console.error("Failed to increment click count:", error)
|
||||||
}
|
}
|
||||||
@@ -282,15 +232,14 @@ export async function incrementClickCount(id: string): Promise<void> {
|
|||||||
export async function incrementRunCount(id: string): Promise<void> {
|
export async function incrementRunCount(id: string): Promise<void> {
|
||||||
if (!isIndexedDBAvailable()) return
|
if (!isIndexedDBAvailable()) return
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
const template = await db.get(STORE_NAME, id)
|
const template = await db.get(STORE_NAME, id)
|
||||||
if (!template) return
|
if (!template) return
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
template.runCount += 1
|
template.runCount += 1
|
||||||
template.lastUsedAt = now
|
template.lastUsedAt = now
|
||||||
template.updatedAt = now
|
template.updatedAt = now
|
||||||
await db.put(STORE_NAME, template)
|
await db.put(STORE_NAME, template)
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to increment run count:", error)
|
console.error("Failed to increment run count:", error)
|
||||||
}
|
}
|
||||||
@@ -423,9 +372,8 @@ export async function importTemplates(
|
|||||||
pinned: typeof t.pinned === "boolean" ? t.pinned : false,
|
pinned: typeof t.pinned === "boolean" ? t.pinned : false,
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await withDB(async (db) => {
|
const db = await getDB()
|
||||||
await db.put(STORE_NAME, newTemplate)
|
await db.put(STORE_NAME, newTemplate)
|
||||||
})
|
|
||||||
existingKeys.add(key)
|
existingKeys.add(key)
|
||||||
imported++
|
imported++
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user