From 8aa82df455af49dd007a7336ab74033eb1ac3f2a Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Thu, 26 Feb 2026 13:06:57 -0800 Subject: [PATCH] UI updates & browser window handling. --- src/main/db/scrub-history-db.ts | 3 ++ src/main/decoder/decoder.ts | 5 --- src/main/ipc/ipcMainConfig.ts | 14 ++++---- src/preload/index.ts | 5 ++- src/renderer/src/components/Home/Home.tsx | 39 +++++++++++++---------- src/util/ipcTypes.json | 1 + src/util/newWindow.ts | 12 +++++-- 7 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/main/db/scrub-history-db.ts b/src/main/db/scrub-history-db.ts index 2b174a6..0777bd9 100644 --- a/src/main/db/scrub-history-db.ts +++ b/src/main/db/scrub-history-db.ts @@ -35,6 +35,7 @@ export type ScrubHistoryItem = { claimNumber: string; pdfUrl: string | null; results: Array<{ + id: number; createdAt: number; anchor: string | null; category: string | null; @@ -239,6 +240,7 @@ export function getScrubHistory(): ScrubHistoryItem[] { for (const r of results) { const bucket = resultsByJobId.get(r.job_id) ?? []; bucket.push({ + id: r.id, createdAt: r.created_at, anchor: r.anchor, category: r.category, @@ -341,6 +343,7 @@ export function getScrubHistoryPage(params: { for (const r of results) { const bucket = resultsByJobId.get(r.job_id) ?? []; bucket.push({ + id: r.id, createdAt: r.created_at, anchor: r.anchor, category: r.category, diff --git a/src/main/decoder/decoder.ts b/src/main/decoder/decoder.ts index 6658da8..7c32dc7 100644 --- a/src/main/decoder/decoder.ts +++ b/src/main/decoder/decoder.ts @@ -181,11 +181,6 @@ async function ImportJob(filepath: string): Promise { } } }); - // uploadNotification.on("click", () => { - // if (scrubPdfURL) { - // shell.openExternal(scrubPdfURL); - // } - // }); uploadNotification.show(); } catch (error) { diff --git a/src/main/ipc/ipcMainConfig.ts b/src/main/ipc/ipcMainConfig.ts index 9267534..9363d9d 100644 --- a/src/main/ipc/ipcMainConfig.ts +++ b/src/main/ipc/ipcMainConfig.ts @@ -1,10 +1,7 @@ -import { app, ipcMain } from "electron"; +import { ipcMain } from "electron"; import log from "electron-log/main"; import { autoUpdater } from "electron-updater"; -import path from "path"; import ipcTypes from "../../util/ipcTypes.json"; -import ImportJob from "../decoder/decoder"; -import store from "../store/store"; import { StartWatcher, StopWatcher } from "../watcher/watcher"; import { ScrubHistoryClearAll, @@ -14,16 +11,13 @@ import { import { getSetting, setSetting, - SettingEmsOutFilePathGet, - SettingEmsOutFilePathSet, - SettingsPpcFilePathGet, - SettingsPpcFilePathSet, SettingsWatchedFilePathsAdd, SettingsWatchedFilePathsGet, SettingsWatchedFilePathsRemove, SettingsWatcherPollingGet, SettingsWatcherPollingSet, } from "./ipcMainHandler.settings"; +import newWindow from "../../util/newWindow"; // Log all IPC messages and their payloads const logIpcMessages = (): void => { @@ -102,4 +96,8 @@ ipcMain.on(ipcTypes.toMain.updates.download, () => { }); }); +ipcMain.on(ipcTypes.toMain.openExternal, async (event, url: string) => { + newWindow(url); +}); + logIpcMessages(); diff --git a/src/preload/index.ts b/src/preload/index.ts index 234da7e..859e07e 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,8 +1,7 @@ import { electronAPI } from "@electron-toolkit/preload"; -import { contextBridge } from "electron"; +import { contextBridge, shell } from "electron"; import "electron-log/preload"; import store from "../main/store/store"; -import newWindow from "../util/newWindow"; // Custom APIs for renderer interface Api { @@ -13,7 +12,7 @@ interface Api { const api: Api = { isTest: (): boolean => store.get("app.isTest") || false, openExternal: async (url: string): Promise => { - newWindow(url); + await shell.openExternal(url); }, }; diff --git a/src/renderer/src/components/Home/Home.tsx b/src/renderer/src/components/Home/Home.tsx index b824fa2..0dadac4 100644 --- a/src/renderer/src/components/Home/Home.tsx +++ b/src/renderer/src/components/Home/Home.tsx @@ -1,4 +1,13 @@ // renderer/Home.tsx +import { + CheckCircleOutlined, + ClockCircleOutlined, + DatabaseOutlined, + DeleteOutlined, + DeleteRowOutlined, + FileTextOutlined, + SettingOutlined, +} from "@ant-design/icons"; import { Button, Card, @@ -14,16 +23,9 @@ import { theme, } from "antd"; import { FC, useCallback, useEffect, useMemo, useState } from "react"; -import { useNavigate } from "react-router"; -import { - FileTextOutlined, - CheckCircleOutlined, - ClockCircleOutlined, - SettingOutlined, - DatabaseOutlined, -} from "@ant-design/icons"; -import ipcTypes from "../../../../util/ipcTypes.json"; import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router"; +import ipcTypes from "../../../../util/ipcTypes.json"; const { Title, Text } = Typography; @@ -47,6 +49,7 @@ type ScrubHistoryItem = { }; type ScrubHistoryResultItem = { + id: number; createdAt: number; anchor: string | null; category: string | null; @@ -212,7 +215,7 @@ const Home: FC = () => { disabled={!record.pdfUrl} onClick={() => { if (!record.pdfUrl) return; - window.api.openExternal(record.pdfUrl); + ipcRenderer.send(ipcTypes.toMain.openExternal, record.pdfUrl); }} > Open @@ -243,7 +246,7 @@ const Home: FC = () => { ), }, { - title: "", + title: "Actions", key: "actions", width: 110, render: (_: unknown, record: ScrubHistoryItem) => ( @@ -254,9 +257,7 @@ const Home: FC = () => { cancelText="Cancel" onConfirm={() => deleteJob(record.id)} > - + ), }, @@ -401,7 +402,11 @@ const Home: FC = () => { onConfirm={clearAll} disabled={history.length === 0} > - @@ -497,8 +502,8 @@ const Home: FC = () => { - `${category}-${row.createdAt}-${idx}` + rowKey={(row) => + `${category}-${row.createdAt}-${row.id}` } pagination={false} size="small" diff --git a/src/util/ipcTypes.json b/src/util/ipcTypes.json index 993755f..4e952e1 100644 --- a/src/util/ipcTypes.json +++ b/src/util/ipcTypes.json @@ -2,6 +2,7 @@ "toMain": { "test": "toMain_test", "authStateChanged": "toMain_authStateChanged", + "openExternal": "toMain_openExternal", "scrubHistory": { "getAll": "toMain_scrubHistory_getAll", "deleteJob": "toMain_scrubHistory_deleteJob", diff --git a/src/util/newWindow.ts b/src/util/newWindow.ts index da9de74..f823f83 100644 --- a/src/util/newWindow.ts +++ b/src/util/newWindow.ts @@ -1,14 +1,20 @@ -import { app, BrowserWindow } from "electron"; +import { BrowserWindow, shell } from "electron"; async function newWindow(url: string): Promise { + // BrowserWindow is a main-process API. If this gets called from preload/renderer + // (or a non-Electron context), fall back to the OS default handler. + if (typeof process === "undefined" || process.type !== "browser") { + await shell.openExternal(url); + return; + } + const pdfWindow = new BrowserWindow({ - title: app.name, webPreferences: { plugins: true, // Enable PDF viewing }, }); - pdfWindow.loadURL(url); + await pdfWindow.loadURL(url); pdfWindow.focus(); }