import type { SavedQuery, TabData } from '../types'; import { ReadSQLFile, WriteSQLFile } from '../../wailsjs/go/app/App'; import { getSQLFileTabPath, hasSQLFileTabUnsavedChanges, isSQLFileMissingReadResult, isSQLFileQueryTab, normalizeSQLFileReadContent, } from './sqlFileTabDirty'; import { getQueryTabDraft, getSQLFileTabDraft } from './sqlFileTabDrafts'; type QueryResultLike = { success?: boolean; message?: string; data?: unknown; }; export type ApplicationQuitUnsavedSQLTarget = | { kind: 'sql-file'; tabId: string; title: string; filePath: string; draft: string; } | { kind: 'saved-query'; tabId: string; title: string; savedQuery: SavedQuery; draft: string; connectionId: string; dbName: string; }; export type ReadSQLFileForQuit = (filePath: string) => Promise; export type WriteSQLFileForQuit = (filePath: string, content: string) => Promise; export type SaveQueryForQuit = (query: SavedQuery) => Promise; const toTrimmedString = (value: unknown): string => String(value ?? '').trim(); const resolveTabTitle = (tab: TabData, fallback: string): string => toTrimmedString(tab.title) || fallback; const resolveSavedQueryForTab = ( tab: TabData, savedQueries: SavedQuery[], ): SavedQuery | null => { if (tab.type !== 'query' || isSQLFileQueryTab(tab)) return null; const savedId = toTrimmedString(tab.savedQueryId) || toTrimmedString(tab.id); if (!savedId) return null; return savedQueries.find((query) => query.id === savedId) || null; }; const hasSavedQueryUnsavedChanges = ( tab: TabData, savedQuery: SavedQuery, draft: string, ): boolean => { const connectionId = toTrimmedString(tab.connectionId || savedQuery.connectionId); const dbName = toTrimmedString(tab.dbName || savedQuery.dbName); return draft !== String(savedQuery.sql ?? '') || connectionId !== toTrimmedString(savedQuery.connectionId) || dbName !== toTrimmedString(savedQuery.dbName); }; export const buildApplicationQuitUnsavedSQLLabel = ( targets: ApplicationQuitUnsavedSQLTarget[], ): string => { if (targets.length === 0) return ''; if (targets.length === 1) return targets[0].title; return String(targets.length); }; export const collectApplicationQuitUnsavedSQLTargets = async ( tabs: TabData[], savedQueries: SavedQuery[], readSQLFile: ReadSQLFileForQuit = ReadSQLFile, ): Promise => { const targets: ApplicationQuitUnsavedSQLTarget[] = []; for (const tab of tabs) { if (tab.type !== 'query') continue; if (isSQLFileQueryTab(tab)) { const filePath = getSQLFileTabPath(tab); const draft = getSQLFileTabDraft(tab.id, String(tab.query ?? '')); const title = resolveTabTitle(tab, filePath); try { const res = await readSQLFile(filePath); if (res?.success) { if (hasSQLFileTabUnsavedChanges({ ...tab, query: draft }, normalizeSQLFileReadContent(res.data))) { targets.push({ kind: 'sql-file', tabId: tab.id, title, filePath, draft }); } continue; } if (isSQLFileMissingReadResult(res)) { targets.push({ kind: 'sql-file', tabId: tab.id, title, filePath, draft }); continue; } throw new Error(res?.message || filePath); } catch { targets.push({ kind: 'sql-file', tabId: tab.id, title, filePath, draft }); } continue; } const savedQuery = resolveSavedQueryForTab(tab, savedQueries); if (!savedQuery) continue; const draft = getQueryTabDraft(tab.id, String(tab.query ?? '')); if (!hasSavedQueryUnsavedChanges(tab, savedQuery, draft)) continue; targets.push({ kind: 'saved-query', tabId: tab.id, title: resolveTabTitle(tab, savedQuery.name), savedQuery, draft, connectionId: toTrimmedString(tab.connectionId || savedQuery.connectionId), dbName: toTrimmedString(tab.dbName || savedQuery.dbName), }); } return targets; }; export const saveApplicationQuitUnsavedSQLTargets = async ( targets: ApplicationQuitUnsavedSQLTarget[], saveQuery: SaveQueryForQuit, writeSQLFile: WriteSQLFileForQuit = WriteSQLFile, ): Promise => { for (const target of targets) { if (target.kind === 'sql-file') { const res = await writeSQLFile(target.filePath, target.draft); if (!res?.success) { throw new Error(res?.message || target.filePath); } continue; } await saveQuery({ ...target.savedQuery, sql: target.draft, connectionId: target.connectionId, dbName: target.dbName, }); } };