feat(stage-tamagotchi): Notice window

This commit is contained in:
Neko Ayaka
2025-11-24 00:22:47 +08:00
parent 8de81c041b
commit 22845805fd
3 changed files with 106 additions and 0 deletions
@@ -0,0 +1,69 @@
import type { BrowserWindow } from 'electron'
import type { RequestWindowPayload } from '../../../shared/eventa'
import { join, resolve } from 'node:path'
import { defineInvokeHandler } from '@moeru/eventa'
import { BrowserWindow as ElectronBrowserWindow, shell } from 'electron'
import icon from '../../../../resources/icon.png?asset'
import { noticeWindowEventa } from '../../../shared/eventa'
import { baseUrl, getElectronMainDirname, load, withHashRoute } from '../../libs/electron/location'
import { createReferencedWindowManager } from '../shared/referenced-window'
export interface NoticeWindowManager {
open: (payload: RequestWindowPayload) => Promise<boolean>
}
export function setupNoticeWindowManager(): NoticeWindowManager {
const rendererBase = baseUrl(resolve(getElectronMainDirname(), '..', 'renderer'))
function createWindow(_id: string): BrowserWindow {
const window = new ElectronBrowserWindow({
title: 'Notice',
width: 1020,
height: 600,
show: false,
icon,
webPreferences: {
preload: join(__dirname, '../preload/index.mjs'),
sandbox: false,
},
})
window.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
return window
}
async function loadNoticeRoute(window: BrowserWindow, payload: RequestWindowPayload & { id: string }) {
const routeWithId = `${payload.route}?id=${payload.id}`
await load(window, withHashRoute(rendererBase, routeWithId))
}
const manager = createReferencedWindowManager({
eventa: noticeWindowEventa,
createWindow,
loadRoute: loadNoticeRoute,
})
return {
open: async (payload: RequestWindowPayload) => {
const handle = await manager.open(payload)
return await new Promise<boolean>((resolve) => {
defineInvokeHandler(handle.context, noticeWindowEventa.windowAction, (action) => {
if (!action?.id || action.id !== handle.id)
return
resolve(action.action === 'confirm')
if (!handle.window.isDestroyed())
handle.window.close()
})
})
},
}
}
@@ -0,0 +1,34 @@
import type { BrowserWindow } from 'electron'
import type { RequestWindowActionDefault } from '../../../../shared/eventa'
import { defineInvokeHandler } from '@moeru/eventa'
import { createContext } from '@moeru/eventa/adapters/electron/main'
import { ipcMain } from 'electron'
import { noticeWindowEventa } from '../../../../shared/eventa'
import { createWindowService } from '../../../services/electron'
export function setupNoticeWindowInvokes(params: {
window: BrowserWindow
onAction: (payload: { id?: string, action: RequestWindowActionDefault }) => void
onPageMounted: () => { id: string, type?: string, payload?: Record<string, any> } | undefined
onPageUnmounted: () => void
}) {
// TODO: once we refactored eventa to support window-namespaced contexts,
// we can remove the setMaxListeners call below since eventa will be able to dispatch and
// manage events within eventa's context system.
ipcMain.setMaxListeners(0)
const { context } = createContext(ipcMain, params.window)
createWindowService({ context, window: params.window })
defineInvokeHandler(context, noticeWindowEventa.windowAction, (payload) => {
params.onAction({ id: payload?.id, action: payload?.action ?? 'close' })
})
defineInvokeHandler(context, noticeWindowEventa.pageMounted, () => params.onPageMounted())
defineInvokeHandler(context, noticeWindowEventa.pageUnmounted, () => params.onPageUnmounted())
return { context }
}
@@ -35,6 +35,9 @@ export function createRequestWindowEventa(namespace: string) {
}
}
// Notice window events built from generic factory
export const noticeWindowEventa = createRequestWindowEventa('notice')
// Widgets / Adhoc window events
export interface WidgetsAddPayload {
id?: string