Redesign settings from scratch

This commit is contained in:
2025-11-03 11:21:08 +03:00
Unverified
parent 1c38bc39ec
commit dca0d319e9
10 changed files with 606 additions and 273 deletions
@@ -0,0 +1,128 @@
import { useState, useRef } from "react";
import { MaterialList, MaterialListItem, MaterialSwitch, type MDUISwitch } from "@/utils/material";
import { useAppState } from "@/pages/chat/state";
import { initialize, subscribe, unsubscribe, isSupported } from "@/core/push-notifications/push-notifications";
import { isElectron } from "@/core/electron/electron";
import { API_BASE_URL } from "@/core/config";
import { getAuthHeaders } from "@/core/api/authApi";
import styles from "@/pages/chat/css/settings-dialog.module.scss";
export function NotificationsPanel() {
const { user } = useAppState();
const authToken = user?.authToken ?? null;
const [pushEnabled, setPushEnabled] = useState(false);
const [loading, setLoading] = useState(false);
const [checking, setChecking] = useState(true);
const switchRef = useRef<MDUISwitch>(null);
async function checkPushStatus() {
if (!isSupported()) {
setPushEnabled(false);
setChecking(false);
return;
}
setChecking(true);
try {
let permission: string;
if (isElectron) {
permission = await window.electronInterface.notifications.requestPermission();
} else {
permission = Notification.permission;
}
console.log("checkPushStatus", permission);
setPushEnabled(permission === "granted");
} catch (error) {
console.error("Failed to check push status:", error);
setPushEnabled(false);
} finally {
setChecking(false);
}
}
async function handlePushToggle(enabled: boolean) {
console.log("handlePushToggle", enabled);
if (!authToken || !isSupported() || loading) return;
// Optimistic update
const previousState = pushEnabled;
setPushEnabled(enabled);
setLoading(true);
try {
if (enabled) {
// Initialize push notifications (creates service worker and requests permission)
const initResult = await initialize();
if (!initResult) {
throw new Error("Failed to initialize push notifications");
}
// Subscribe to push notifications (sends subscription to server)
// The subscribe() function will handle creating/getting the subscription if needed
const subscribeResult = await subscribe(authToken);
if (!subscribeResult) {
throw new Error("Failed to subscribe to push notifications");
}
// Verify the state after subscription - check permission to ensure it's actually granted
await checkPushStatus();
} else {
// Unsubscribe locally first
const unsubscribed = await unsubscribe();
if (!unsubscribed) {
throw new Error("Failed to unsubscribe locally");
}
// Then unsubscribe from server
const response = await fetch(`${API_BASE_URL}/push/unsubscribe`, {
method: "DELETE",
headers: getAuthHeaders(authToken)
});
if (!response.ok) {
throw new Error("Failed to unsubscribe from push notifications");
}
// After unsubscribing, permission is still granted but we're not subscribed
// So we keep the state as disabled (false)
setPushEnabled(false);
}
} catch (error) {
console.error("Failed to toggle push notifications:", error);
// Revert optimistic update
setPushEnabled(previousState);
// Re-check actual status to sync with reality
await checkPushStatus();
} finally {
setLoading(false);
}
}
function handleListItemClick(e: React.MouseEvent) {
if (checking || loading || !isSupported() || e.target === switchRef.current) return;
handlePushToggle(!pushEnabled);
}
return (
<>
<h3 className={styles.panelTitle}>Notifications</h3>
<MaterialList>
<MaterialListItem
className={styles.clickableItem}
headline="Push Notifications"
description="Receive notifications for new messages"
icon="notifications"
onClick={handleListItemClick}>
<MaterialSwitch
checked={pushEnabled}
disabled={!isSupported() || loading || checking}
onChange={(e) => handlePushToggle(e.target.checked)}
slot="end-icon"
ref={switchRef}
/>
</MaterialListItem>
</MaterialList>
</>
);
}