102 lines
3.0 KiB
TypeScript
102 lines
3.0 KiB
TypeScript
import './forceServerApi';
|
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import { emit } from '@tauri-apps/api/event';
|
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
|
import { initStorage, refreshApiBaseFromStorage } from '@goldenchart/shared';
|
|
import { ensureDesktopApiBase } from './ensureDesktopApiBase';
|
|
import { fitPickerWindowToScreen } from './bridge/pickerWindow';
|
|
import WidgetPickerModal from '@frontend/components/widgetDashboard/WidgetPickerModal';
|
|
import {
|
|
WIDGET_PICKER_SELECT_EVENT,
|
|
type WidgetPickerSelectPayload,
|
|
} from './widgetPickerEvents';
|
|
import { readDesktopSync, subscribeDesktopSync } from '@frontend/utils/desktopSync';
|
|
import { syncDocumentTheme } from '@frontend/utils/documentTheme';
|
|
import type { Theme } from '@frontend/types';
|
|
import '@frontend/App.css';
|
|
import '@frontend/styles/appPopup.css';
|
|
import '@frontend/styles/widgetDashboard.css';
|
|
|
|
document.documentElement.classList.add('fw-native-picker-window');
|
|
|
|
function parseParams(): { instanceId: string; slotId: string } {
|
|
const params = new URLSearchParams(window.location.search);
|
|
return {
|
|
instanceId: params.get('instanceId') ?? '',
|
|
slotId: params.get('slotId') ?? '',
|
|
};
|
|
}
|
|
|
|
const PickerApp: React.FC = () => {
|
|
const { instanceId, slotId } = useMemo(() => parseParams(), []);
|
|
const sync = readDesktopSync();
|
|
const [theme, setTheme] = useState<Theme>((sync?.theme as Theme | undefined) ?? 'dark');
|
|
const [ready, setReady] = useState(false);
|
|
|
|
useEffect(() => {
|
|
syncDocumentTheme(theme);
|
|
}, [theme]);
|
|
|
|
useEffect(() => {
|
|
return subscribeDesktopSync(state => {
|
|
if (state.theme) setTheme(state.theme as Theme);
|
|
});
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
void fitPickerWindowToScreen()
|
|
.catch(err => console.warn('[picker-main] window fit failed', err))
|
|
.finally(() => setReady(true));
|
|
}, []);
|
|
|
|
const handleClose = useCallback(() => {
|
|
void getCurrentWindow().close();
|
|
}, []);
|
|
|
|
const handleSelect = useCallback(async (widgetType: string) => {
|
|
if (!instanceId || !slotId) {
|
|
await getCurrentWindow().close();
|
|
return;
|
|
}
|
|
const payload: WidgetPickerSelectPayload = { instanceId, slotId, widgetType };
|
|
await emit(WIDGET_PICKER_SELECT_EVENT, payload);
|
|
await getCurrentWindow().close();
|
|
}, [instanceId, slotId]);
|
|
|
|
if (!instanceId || !slotId) {
|
|
return <p className="wd-widget-empty">위젯 선택 정보가 없습니다.</p>;
|
|
}
|
|
|
|
if (!ready) {
|
|
return (
|
|
<div className="wd-picker-native-loading" aria-live="polite">
|
|
위젯 목록 준비 중…
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<WidgetPickerModal
|
|
open
|
|
category="all"
|
|
onClose={handleClose}
|
|
onSelect={widgetType => { void handleSelect(widgetType); }}
|
|
/>
|
|
);
|
|
};
|
|
|
|
async function bootstrap() {
|
|
await initStorage();
|
|
ensureDesktopApiBase();
|
|
refreshApiBaseFromStorage();
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
<React.StrictMode>
|
|
<PickerApp />
|
|
</React.StrictMode>,
|
|
);
|
|
}
|
|
|
|
void bootstrap();
|