Skip to content

Commit 8e8229e

Browse files
authored
fix(settings): enhance navigation handling and normalize payload structure (#1513)
1 parent 7e85c7c commit 8e8229e

2 files changed

Lines changed: 148 additions & 25 deletions

File tree

src/main/presenter/windowPresenter/index.ts

Lines changed: 121 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,10 @@ export class WindowPresenter implements IWindowPresenter {
483483
!this.settingsWindow.isDestroyed() &&
484484
this.settingsWindow.id === windowId
485485
) {
486+
if (this.tryNavigateSettingsWindowByUrl(channel, args)) {
487+
return true
488+
}
489+
486490
if (this.shouldQueueSettingsMessage(channel)) {
487491
this.pendingSettingsMessages.push({ channel, args })
488492
return true
@@ -1244,7 +1248,22 @@ export class WindowPresenter implements IWindowPresenter {
12441248
this.settingsWindow.show()
12451249
this.settingsWindow.focus()
12461250
if (navigation) {
1247-
this.sendToWindow(this.settingsWindow.id, SETTINGS_EVENTS.NAVIGATE, navigation)
1251+
if (this.settingsWindowReady) {
1252+
this.sendToWindow(this.settingsWindow.id, SETTINGS_EVENTS.NAVIGATE, navigation)
1253+
} else {
1254+
this.pendingSettingsMessages.push({
1255+
channel: SETTINGS_EVENTS.NAVIGATE,
1256+
args: [navigation]
1257+
})
1258+
1259+
const targetUrl = this.getSettingsWindowTargetUrl(navigation)
1260+
console.log(`Settings window is not ready, reloading to target URL: ${targetUrl}`)
1261+
console.info('[Startup][Settings][Main] loadURL start', targetUrl)
1262+
await this.settingsWindow.loadURL(targetUrl)
1263+
console.info(
1264+
`[Startup][Settings][Main] loadURL end windowId=${this.settingsWindow.id} elapsed=${Date.now() - settingsStartupStart}ms`
1265+
)
1266+
}
12481267
}
12491268
return this.settingsWindow.id
12501269
}
@@ -1368,29 +1387,10 @@ export class WindowPresenter implements IWindowPresenter {
13681387
})
13691388

13701389
// Load settings renderer HTML
1371-
const initialNavigationPath = navigation
1372-
? resolveSettingsNavigationPath(navigation.routeName, navigation.params)
1373-
: null
1374-
1375-
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
1376-
const settingsUrl = new URL('/settings/index.html', process.env['ELECTRON_RENDERER_URL'])
1377-
if (initialNavigationPath) {
1378-
settingsUrl.hash = initialNavigationPath
1379-
}
1380-
console.log(`Loading settings renderer URL in dev mode: ${settingsUrl.toString()}`)
1381-
console.info('[Startup][Settings][Main] loadURL start', settingsUrl.toString())
1382-
await settingsWindow.loadURL(settingsUrl.toString())
1383-
} else {
1384-
const packagedSettingsUrl = pathToFileURL(
1385-
join(__dirname, '../renderer/settings/index.html')
1386-
).toString()
1387-
const targetUrl = initialNavigationPath
1388-
? `${packagedSettingsUrl}#${initialNavigationPath}`
1389-
: packagedSettingsUrl
1390-
console.log(`Loading packaged settings renderer URL: ${targetUrl}`)
1391-
console.info('[Startup][Settings][Main] loadURL start', targetUrl)
1392-
await settingsWindow.loadURL(targetUrl)
1393-
}
1390+
const targetUrl = this.getSettingsWindowTargetUrl(navigation)
1391+
console.log(`Loading settings renderer URL: ${targetUrl}`)
1392+
console.info('[Startup][Settings][Main] loadURL start', targetUrl)
1393+
await settingsWindow.loadURL(targetUrl)
13941394

13951395
console.info(
13961396
`[Startup][Settings][Main] loadURL end windowId=${windowId} elapsed=${Date.now() - settingsStartupStart}ms`
@@ -1488,6 +1488,103 @@ export class WindowPresenter implements IWindowPresenter {
14881488
this.settingsWindowReady = false
14891489
}
14901490

1491+
private tryNavigateSettingsWindowByUrl(channel: string, args: unknown[]): boolean {
1492+
if (
1493+
channel !== SETTINGS_EVENTS.NAVIGATE ||
1494+
!this.settingsWindow ||
1495+
this.settingsWindow.isDestroyed() ||
1496+
this.settingsWindow.webContents.isDestroyed()
1497+
) {
1498+
return false
1499+
}
1500+
1501+
const navigation = this.toSettingsNavigationPayload(args[0])
1502+
if (!navigation || navigation.routeName !== 'settings-provider') {
1503+
return false
1504+
}
1505+
1506+
const targetUrl = this.getSettingsWindowTargetUrl(navigation)
1507+
const currentUrl = this.settingsWindow.webContents.getURL()
1508+
1509+
if (currentUrl === targetUrl && this.settingsWindowReady) {
1510+
return false
1511+
}
1512+
1513+
this.pendingSettingsMessages.push({ channel, args: [navigation] })
1514+
console.log(`Reloading settings window to target URL: ${targetUrl}`)
1515+
console.info('[Startup][Settings][Main] loadURL start', targetUrl)
1516+
void this.settingsWindow.webContents
1517+
.loadURL(targetUrl)
1518+
.then(() => {
1519+
if (!this.settingsWindow || this.settingsWindow.isDestroyed()) {
1520+
return
1521+
}
1522+
1523+
console.info(
1524+
`[Startup][Settings][Main] loadURL end windowId=${this.settingsWindow.id} target=${targetUrl}`
1525+
)
1526+
})
1527+
.catch((error) => {
1528+
console.error(`Failed to reload settings window for navigation: ${targetUrl}`, error)
1529+
})
1530+
return true
1531+
}
1532+
1533+
private toSettingsNavigationPayload(raw: unknown): SettingsNavigationPayload | null {
1534+
if (!raw || typeof raw !== 'object') {
1535+
return null
1536+
}
1537+
1538+
const candidate = raw as {
1539+
routeName?: unknown
1540+
params?: unknown
1541+
section?: unknown
1542+
}
1543+
1544+
if (typeof candidate.routeName !== 'string') {
1545+
return null
1546+
}
1547+
1548+
const params =
1549+
candidate.params && typeof candidate.params === 'object'
1550+
? Object.entries(candidate.params as Record<string, unknown>).reduce<
1551+
Record<string, string>
1552+
>((acc, [key, value]) => {
1553+
if (typeof value === 'string' && value.trim().length > 0) {
1554+
acc[key] = value
1555+
}
1556+
return acc
1557+
}, {})
1558+
: undefined
1559+
1560+
return {
1561+
routeName: candidate.routeName as SettingsNavigationPayload['routeName'],
1562+
params: params && Object.keys(params).length > 0 ? params : undefined,
1563+
section: typeof candidate.section === 'string' ? candidate.section : undefined
1564+
}
1565+
}
1566+
1567+
private getSettingsWindowTargetUrl(navigation?: SettingsNavigationPayload): string {
1568+
const initialNavigationPath = navigation
1569+
? resolveSettingsNavigationPath(navigation.routeName, navigation.params)
1570+
: null
1571+
1572+
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
1573+
const settingsUrl = new URL('/settings/index.html', process.env['ELECTRON_RENDERER_URL'])
1574+
if (initialNavigationPath) {
1575+
settingsUrl.hash = initialNavigationPath
1576+
}
1577+
return settingsUrl.toString()
1578+
}
1579+
1580+
const packagedSettingsUrl = pathToFileURL(
1581+
join(__dirname, '../renderer/settings/index.html')
1582+
).toString()
1583+
return initialNavigationPath
1584+
? `${packagedSettingsUrl}#${initialNavigationPath}`
1585+
: packagedSettingsUrl
1586+
}
1587+
14911588
private flushPendingSettingsMessages(): void {
14921589
if (
14931590
!this.settingsWindow ||

src/renderer/api/SettingsClient.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,29 @@ import {
1717
} from '@shared/contracts/routes'
1818
import { getDeepchatBridge } from './core'
1919

20+
const normalizeSettingsNavigationPayload = (
21+
navigation?: SettingsNavigationPayload
22+
): SettingsNavigationPayload | undefined => {
23+
if (!navigation) {
24+
return undefined
25+
}
26+
27+
const params = navigation.params
28+
? Object.entries(navigation.params).reduce<Record<string, string>>((acc, [key, value]) => {
29+
if (typeof value === 'string') {
30+
acc[key] = value
31+
}
32+
return acc
33+
}, {})
34+
: undefined
35+
36+
return {
37+
routeName: navigation.routeName,
38+
params: params && Object.keys(params).length > 0 ? params : undefined,
39+
section: navigation.section
40+
}
41+
}
42+
2043
export function createSettingsClient(bridge: DeepchatBridge = getDeepchatBridge()) {
2144
async function getSnapshot(keys?: SettingsKey[]): Promise<Partial<SettingsSnapshotValues>> {
2245
const result = await bridge.invoke(settingsGetSnapshotRoute.name, { keys })
@@ -54,7 +77,10 @@ export function createSettingsClient(bridge: DeepchatBridge = getDeepchatBridge(
5477
}
5578

5679
async function openSettings(navigation?: SettingsNavigationPayload) {
57-
return await bridge.invoke(systemOpenSettingsRoute.name, navigation ?? {})
80+
return await bridge.invoke(
81+
systemOpenSettingsRoute.name,
82+
normalizeSettingsNavigationPayload(navigation) ?? {}
83+
)
5884
}
5985

6086
function onChanged(

0 commit comments

Comments
 (0)