import { useEffect, type JSX } from 'react' import type { TrayRendererCommand, TrayRendererState } from '../../types/desktopIntegration' import { useAudioStore } from '../stores/audioStore' import { useSettingsStore } from '../stores/settingsStore' import { useUiStore } from '../stores/uiStore' interface TrayControlBridgeProps { ready: boolean } const DEFAULT_SYSTEM_SOURCE_ID = '__default_system_output__' function getErrorMessage(error: unknown, fallback: string): string { return error instanceof Error && error.message ? error.message : fallback } async function handleTrayCommand(command: TrayRendererCommand): Promise { if (command.type === 'open-settings') { useUiStore.getState().setSettingsOpen(true) return } if (command.type === 'load-profile') { const settings = useSettingsStore.getState() try { await settings.guardProfileTransition(async () => { await useSettingsStore.getState().loadProfile(command.profileId) }) } catch (error) { useUiStore.getState().showBanner({ tone: 'error', message: getErrorMessage(error, 'Could not load the profile.'), actions: [], }) } return } const audio = useAudioStore.getState() if (command.type === 'select-system-source') { await audio.selectSystemSource(command.sourceId) await useAudioStore.getState().startCapture() return } if (command.type === 'select-input-source') { await audio.selectDevice(command.deviceId) await useAudioStore.getState().startCapture() return } if (command.type === 'set-capture-running') { if (command.running) { await audio.startCapture() } else { audio.stopCapture() } } } export default function TrayControlBridge({ ready }: TrayControlBridgeProps): JSX.Element | null { const profiles = useSettingsStore((state) => state.profiles) const activeProfileId = useSettingsStore((state) => state.activeProfileId) const hasUnsavedProfileChanges = useSettingsStore((state) => state.hasUnsavedProfileChanges) const captureStatus = useAudioStore((state) => state.captureStatus) const activeSourceLabel = useAudioStore((state) => state.activeSourceLabel) const captureMode = useAudioStore((state) => state.captureMode) const selectedSystemSourceId = useAudioStore((state) => state.selectedSystemSourceId) const selectedDeviceId = useAudioStore((state) => state.selectedDeviceId) const systemSources = useAudioStore((state) => state.systemSources) const devices = useAudioStore((state) => state.devices) useEffect(() => { const unsubscribe = window.electronAPI.trayControls.onCommand((command) => { void handleTrayCommand(command).catch((error) => { useUiStore.getState().showBanner({ tone: 'error', message: getErrorMessage(error, 'The tray action could not be completed.'), actions: [], }) }) }) return unsubscribe }, []) useEffect(() => { if (!ready) { window.electronAPI.trayControls.markNotReady() return } window.electronAPI.trayControls.markReady() return () => window.electronAPI.trayControls.markNotReady() }, [ready]) useEffect(() => { const visibleSystemSources = systemSources.length > 0 ? systemSources : [{ id: DEFAULT_SYSTEM_SOURCE_ID, label: 'Default Output', isDefault: true }] const state: TrayRendererState = { profiles: Object.entries(profiles).map(([id, profile]) => ({ id, name: profile.name })), activeProfileId, hasUnsavedProfileChanges, captureStatus, activeSourceLabel, captureMode, selectedSystemSourceId, selectedDeviceId, systemSources: visibleSystemSources.map((source) => ({ id: source.id, label: source.label, isDefault: source.isDefault, })), inputSources: [ { id: '', label: 'Default Input', isDefault: true }, ...devices .filter((device) => device.deviceId !== 'default') .map((device) => ({ id: device.deviceId, label: device.label || `Input ${device.deviceId.slice(0, 8)}`, })), ], } window.electronAPI.trayControls.publishState(state) }, [ activeProfileId, activeSourceLabel, captureMode, captureStatus, devices, hasUnsavedProfileChanges, profiles, selectedDeviceId, selectedSystemSourceId, systemSources, ]) return null }