Files
prism/src/main/windowStateStore.ts
T
2026-06-11 17:30:07 -04:00

114 lines
3.4 KiB
TypeScript

import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { dirname } from 'node:path'
import { createEmptyWindowLocalState, normalizeWindowLocalState } from '../shared/windowState'
import type { WindowBounds } from '../types/popout'
import type { ScopeKind } from '../types/scope'
import type { PrismWindowLocalStateV1, WindowBackgroundState } from '../types/windowState'
export class FileBackedWindowStateStore {
private state: PrismWindowLocalStateV1 = createEmptyWindowLocalState()
private initialized = false
private writeChain: Promise<void> = Promise.resolve()
constructor(private readonly localStatePath: string) {}
async initialize(): Promise<void> {
if (this.initialized) return
this.state = await this.readLocalState()
this.initialized = true
}
getMainAlwaysOnTop(): boolean {
return this.state.mainAlwaysOnTop
}
getPopoutAlwaysOnTop(kind: ScopeKind): boolean {
return this.state.popoutAlwaysOnTop[kind] === true
}
getWindowBackground(): WindowBackgroundState {
return { ...this.state.windowBackground }
}
getNowPlayingConfigWindowBounds(): WindowBounds | undefined {
return this.state.nowPlayingConfigWindowBounds
? { ...this.state.nowPlayingConfigWindowBounds }
: undefined
}
async setMainAlwaysOnTop(alwaysOnTop: boolean): Promise<void> {
await this.ensureInitialized()
this.state = normalizeWindowLocalState({
...this.state,
mainAlwaysOnTop: alwaysOnTop,
popoutAlwaysOnTop: { ...this.state.popoutAlwaysOnTop },
})
await this.persistState()
}
async setPopoutAlwaysOnTop(kind: ScopeKind, alwaysOnTop: boolean): Promise<void> {
await this.ensureInitialized()
const popoutAlwaysOnTop = { ...this.state.popoutAlwaysOnTop }
if (alwaysOnTop) {
popoutAlwaysOnTop[kind] = true
} else {
delete popoutAlwaysOnTop[kind]
}
this.state = normalizeWindowLocalState({
...this.state,
popoutAlwaysOnTop,
})
await this.persistState()
}
async setWindowBackground(background: WindowBackgroundState): Promise<void> {
await this.ensureInitialized()
this.state = normalizeWindowLocalState({
...this.state,
windowBackground: background,
popoutAlwaysOnTop: { ...this.state.popoutAlwaysOnTop },
})
await this.persistState()
}
async setNowPlayingConfigWindowBounds(bounds?: WindowBounds): Promise<void> {
await this.ensureInitialized()
this.state = normalizeWindowLocalState({
...this.state,
nowPlayingConfigWindowBounds: bounds,
})
await this.persistState()
}
private async ensureInitialized(): Promise<void> {
if (!this.initialized) {
await this.initialize()
}
}
private async readLocalState(): Promise<PrismWindowLocalStateV1> {
try {
const raw = await readFile(this.localStatePath, 'utf8')
return normalizeWindowLocalState(JSON.parse(raw) as unknown)
} catch {
return createEmptyWindowLocalState()
}
}
private async persistState(): Promise<void> {
const state = normalizeWindowLocalState(this.state)
const payload = `${JSON.stringify(state, null, 2)}\n`
this.writeChain = this.writeChain
.catch(() => {})
.then(async () => {
await mkdir(dirname(this.localStatePath), { recursive: true })
await writeFile(this.localStatePath, payload, 'utf8')
})
await this.writeChain
}
}