mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-07-03 16:32:00 +02:00
158 lines
5.2 KiB
TypeScript
158 lines
5.2 KiB
TypeScript
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
import { api } from '../api';
|
|
import { takePrefetchOrFetch } from '../prefetch';
|
|
import { toast } from '../components/ui/sonner';
|
|
import { initLastMessageTimes } from '../utils/conversationState';
|
|
import type { AppSettings, AppSettingsUpdate } from '../types';
|
|
|
|
export function useAppSettings() {
|
|
const [appSettings, setAppSettings] = useState<AppSettings | null>(null);
|
|
|
|
// One-time migration guard
|
|
const hasMigratedRef = useRef(false);
|
|
|
|
const fetchAppSettings = useCallback(async () => {
|
|
try {
|
|
const data = await takePrefetchOrFetch('settings', api.getSettings);
|
|
setAppSettings(data);
|
|
initLastMessageTimes(data.last_message_times ?? {});
|
|
} catch (err) {
|
|
console.error('Failed to fetch app settings:', err);
|
|
}
|
|
}, []);
|
|
|
|
const handleSaveAppSettings = useCallback(
|
|
async (update: AppSettingsUpdate) => {
|
|
await api.updateSettings(update);
|
|
await fetchAppSettings();
|
|
},
|
|
[fetchAppSettings]
|
|
);
|
|
|
|
const handleToggleBlockedKey = useCallback(async (key: string) => {
|
|
const normalizedKey = key.toLowerCase();
|
|
setAppSettings((prev) => {
|
|
if (!prev) return prev;
|
|
const current = prev.blocked_keys ?? [];
|
|
const wasBlocked = current.includes(normalizedKey);
|
|
const optimistic = wasBlocked
|
|
? current.filter((k) => k !== normalizedKey)
|
|
: [...current, normalizedKey];
|
|
return { ...prev, blocked_keys: optimistic };
|
|
});
|
|
|
|
try {
|
|
const updatedSettings = await api.toggleBlockedKey(key);
|
|
setAppSettings(updatedSettings);
|
|
} catch (err) {
|
|
console.error('Failed to toggle blocked key:', err);
|
|
try {
|
|
const settings = await api.getSettings();
|
|
setAppSettings(settings);
|
|
} catch {
|
|
// If refetch also fails, leave optimistic state
|
|
}
|
|
toast.error('Failed to update blocked key');
|
|
}
|
|
}, []);
|
|
|
|
const handleToggleBlockedName = useCallback(async (name: string) => {
|
|
setAppSettings((prev) => {
|
|
if (!prev) return prev;
|
|
const current = prev.blocked_names ?? [];
|
|
const wasBlocked = current.includes(name);
|
|
const optimistic = wasBlocked ? current.filter((n) => n !== name) : [...current, name];
|
|
return { ...prev, blocked_names: optimistic };
|
|
});
|
|
|
|
try {
|
|
const updatedSettings = await api.toggleBlockedName(name);
|
|
setAppSettings(updatedSettings);
|
|
} catch (err) {
|
|
console.error('Failed to toggle blocked name:', err);
|
|
try {
|
|
const settings = await api.getSettings();
|
|
setAppSettings(settings);
|
|
} catch {
|
|
// If refetch also fails, leave optimistic state
|
|
}
|
|
toast.error('Failed to update blocked name');
|
|
}
|
|
}, []);
|
|
|
|
const handleToggleTrackedTelemetry = useCallback(async (publicKey: string) => {
|
|
const key = publicKey.toLowerCase();
|
|
setAppSettings((prev) => {
|
|
if (!prev) return prev;
|
|
const current = prev.tracked_telemetry_repeaters ?? [];
|
|
const wasTracked = current.includes(key);
|
|
const optimistic = wasTracked ? current.filter((k) => k !== key) : [...current, key];
|
|
return { ...prev, tracked_telemetry_repeaters: optimistic };
|
|
});
|
|
|
|
try {
|
|
const result = await api.toggleTrackedTelemetry(publicKey);
|
|
setAppSettings((prev) =>
|
|
prev ? { ...prev, tracked_telemetry_repeaters: result.tracked_telemetry_repeaters } : prev
|
|
);
|
|
} catch (err) {
|
|
console.error('Failed to toggle tracked telemetry:', err);
|
|
try {
|
|
const settings = await api.getSettings();
|
|
setAppSettings(settings);
|
|
} catch {
|
|
// If refetch also fails, leave optimistic state
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const detail = (err as any)?.body?.detail;
|
|
if (typeof detail === 'object' && detail?.message) {
|
|
toast.error(detail.message);
|
|
} else {
|
|
toast.error('Failed to update tracked telemetry');
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
// Legacy favorites migration: if pre-server-side favorites exist in
|
|
// localStorage, toggle each one via the existing API and clear the key.
|
|
useEffect(() => {
|
|
if (!appSettings || hasMigratedRef.current) return;
|
|
hasMigratedRef.current = true;
|
|
|
|
const FAVORITES_KEY = 'remoteterm-favorites';
|
|
let localFavorites: Array<{ type: 'channel' | 'contact'; id: string }> = [];
|
|
try {
|
|
const stored = localStorage.getItem(FAVORITES_KEY);
|
|
if (stored) localFavorites = JSON.parse(stored);
|
|
} catch {
|
|
// corrupt or unavailable
|
|
}
|
|
if (localFavorites.length === 0) return;
|
|
|
|
const migrate = async () => {
|
|
let migrated = 0;
|
|
for (const f of localFavorites) {
|
|
try {
|
|
await api.toggleFavorite(f.type, f.id);
|
|
migrated++;
|
|
} catch {
|
|
// Entity may have been deleted; skip and continue
|
|
}
|
|
}
|
|
localStorage.removeItem(FAVORITES_KEY);
|
|
// Reload so contacts/channels pick up the new favorite flags
|
|
if (migrated > 0) window.location.reload();
|
|
};
|
|
migrate();
|
|
}, [appSettings]);
|
|
|
|
return {
|
|
appSettings,
|
|
fetchAppSettings,
|
|
handleSaveAppSettings,
|
|
handleToggleBlockedKey,
|
|
handleToggleBlockedName,
|
|
handleToggleTrackedTelemetry,
|
|
};
|
|
}
|