import { useState, useEffect } from "react"; import { PRODUCT_NAME, API_BASE_URL } from "@/core/config"; import type { DialogProps } from "@/core/types"; import { StyledDialog } from "@/core/components/StyledDialog"; import { initialize, isSupported, startElectronReceiver, stopElectronReceiver, subscribe, unsubscribe } from "@/core/push-notifications/push-notifications"; import { isElectron } from "@/core/electron/electron"; import { useAppState } from "@/pages/chat/state"; import { getAuthHeaders } from "@/core/api/authApi"; import ChangePasswordDialog from "./ChangePasswordDialog"; import { listDevices, revokeDevice, logoutAllOtherDevices, type DeviceInfo } from "@/core/api/devicesApi"; import { useImmer } from "use-immer"; import { MaterialButton, MaterialIconButton, MaterialList, MaterialListItem, MaterialSwitch } from "@/utils/material"; import styles from "@/pages/chat/css/settings-dialog.module.scss"; export function SettingsDialog({ isOpen, onOpenChange }: DialogProps) { const [activePanel, setActivePanel] = useState("notifications-settings"); const [pushNotificationsEnabled, setPushNotificationsEnabled] = useState(false); const [pushSupported, setPushSupported] = useState(false); const user = useAppState(state => state.user); const logout = useAppState(state => state.logout); const [devices, updateDevices] = useImmer([]); const [cpOpen, setCpOpen] = useState(false); useEffect(() => { setPushSupported(isSupported()); // For Electron, we assume notifications are enabled if supported // For web browsers, we check if there's a subscription setPushNotificationsEnabled(isSupported()); }, []); useEffect(() => { if (activePanel === "devices-settings" && user.authToken) { listDevices(user.authToken) .then(list => updateDevices(() => list)) .catch(() => {}); } }, [activePanel, user.authToken, updateDevices]); const handlePanelChange = (panelId: string) => { setActivePanel(panelId); }; const handlePushNotificationToggle = async (enabled: boolean) => { if (!user.authToken) return; try { if (enabled) { const initialized = await initialize(); if (initialized) { await subscribe(user.authToken); // For Electron, start the notification receiver if (isElectron) { await startElectronReceiver(); } setPushNotificationsEnabled(true); } } else { await unsubscribe(); // For Electron, stop the notification receiver if (isElectron) { stopElectronReceiver(); } // Call API to unsubscribe on server (for web browsers) await fetch(`${API_BASE_URL}/push/unsubscribe`, { method: "DELETE", headers: getAuthHeaders(user.authToken) }); setPushNotificationsEnabled(false); } } catch (error) { console.error("Failed to toggle notifications:", error); } }; return ( <>
onOpenChange(false)}>
Настройки
handlePanelChange("notifications-settings")} style={{ cursor: "pointer" }} > Уведомления handlePanelChange("security-settings")} style={{ cursor: "pointer" }} > Безопасность handlePanelChange("devices-settings")} style={{ cursor: "pointer" }} > Устройства handlePanelChange("about-settings")} style={{ cursor: "pointer" }} > О приложении

Уведомления

{pushSupported && ( handlePushNotificationToggle(e.target.checked)}> Push уведомления )}

Безопасность

setCpOpen(true)}>Изменить пароль

Устройства

{ if (!user.authToken) return; await logoutAllOtherDevices(user.authToken); const list = await listDevices(user.authToken); updateDevices(() => list); }}>Выйти на всех остальных устройствах { if (!user.authToken) return; await fetch(`${API_BASE_URL}/logout`, { headers: getAuthHeaders(user.authToken) }); logout(); }}>Выйти на этом устройстве
{devices.map((d) => ( { if (!user.authToken || d.current) return; await revokeDevice(user.authToken, d.session_id); const list = await listDevices(user.authToken); updateDevices(() => list); }} >
{d.device_name || (d.browser_name || "Браузер")} на {d.os_name || "OS"} {d.current ? " (это устройство)" : ""}
Последняя активность: {d.last_seen || "—"}
))}

О приложении

100% open source. Репозиторий на GitHub.

{PRODUCT_NAME}

); }