Files
pyramid/lib/core/settings_prefs.dart
T
Bernd Steckmeister 25ed765a03 wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.)
als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen
Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
2026-07-03 05:47:18 +02:00

137 lines
4.7 KiB
Dart

import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
// ── Generic SharedPreferences-backed bool notifier ──────────────────────────
class BoolPref extends StateNotifier<bool> {
final String _key;
BoolPref(this._key, bool defaultValue) : super(defaultValue) {
_load(defaultValue);
}
Future<void> _load(bool def) async {
final p = await SharedPreferences.getInstance();
if (mounted) state = p.getBool(_key) ?? def;
}
Future<void> set(bool v) async {
state = v;
final p = await SharedPreferences.getInstance();
await p.setBool(_key, v);
}
void toggle() => set(!state);
}
// ── Generic SharedPreferences-backed String-Set notifier ───────────────────
class StringSetPref extends StateNotifier<Set<String>> {
final String _key;
StringSetPref(this._key) : super({}) {
_load();
}
Future<void> _load() async {
final p = await SharedPreferences.getInstance();
final list = p.getStringList(_key);
if (mounted && list != null) state = list.toSet();
}
Future<void> add(String v) async {
if (state.contains(v)) return;
state = {...state, v};
final p = await SharedPreferences.getInstance();
await p.setStringList(_key, state.toList());
}
Future<void> remove(String v) async {
if (!state.contains(v)) return;
state = {...state}..remove(v);
final p = await SharedPreferences.getInstance();
await p.setStringList(_key, state.toList());
}
}
// ── Notification preferences ─────────────────────────────────────────────────
final notifDesktopProvider =
StateNotifierProvider<BoolPref, bool>((ref) => BoolPref('notif_desktop', true));
final notifSoundProvider =
StateNotifierProvider<BoolPref, bool>((ref) => BoolPref('notif_sound', true));
final notifPreviewProvider =
StateNotifierProvider<BoolPref, bool>((ref) => BoolPref('notif_preview', true));
final notifMentionOnlyDmProvider =
StateNotifierProvider<BoolPref, bool>((ref) => BoolPref('notif_mention_dm', false));
// ── Privacy preferences ──────────────────────────────────────────────────────
final privacyPresenceProvider =
StateNotifierProvider<BoolPref, bool>((ref) => BoolPref('privacy_presence', true));
final privacyReadReceiptsProvider =
StateNotifierProvider<BoolPref, bool>((ref) => BoolPref('privacy_read_receipts', true));
final privacyTypingProvider =
StateNotifierProvider<BoolPref, bool>((ref) => BoolPref('privacy_typing', true));
// ── Theme persistence helpers ────────────────────────────────────────────────
Future<void> saveThemePrefs({
bool? isDark,
int? accentIdx,
double? radius,
double? motion,
}) async {
final p = await SharedPreferences.getInstance();
if (isDark != null) await p.setBool('theme_dark', isDark);
if (accentIdx != null) await p.setInt('theme_accent', accentIdx);
if (radius != null) await p.setDouble('theme_radius', radius);
if (motion != null) await p.setDouble('theme_motion', motion);
}
Future<({bool isDark, int accentIdx, double radius, double motion})>
loadThemePrefs() async {
final p = await SharedPreferences.getInstance();
return (
isDark: p.getBool('theme_dark') ?? true,
accentIdx: p.getInt('theme_accent') ?? 0,
radius: p.getDouble('theme_radius') ?? 12.0,
motion: p.getDouble('theme_motion') ?? 1.0,
);
}
// ── Server banner persistence ─────────────────────────────────────────────────
// Stored as a single JSON map under 'server_banners': homeserverUrl → mxcUri
const _kServerBannersKey = 'server_banners';
Future<Map<String, String>> loadAllServerBanners() async {
final p = await SharedPreferences.getInstance();
final raw = p.getString(_kServerBannersKey);
if (raw == null) return {};
try {
final decoded = jsonDecode(raw) as Map<String, dynamic>;
return decoded.map((k, v) => MapEntry(k, v as String));
} catch (_) {
return {};
}
}
Future<void> saveServerBanner(String homeserverUrl, String? mxcUri) async {
final p = await SharedPreferences.getInstance();
final current = await loadAllServerBanners();
if (mxcUri == null || mxcUri.isEmpty) {
current.remove(homeserverUrl);
} else {
current[homeserverUrl] = mxcUri;
}
await p.setString(_kServerBannersKey, jsonEncode(current));
}