diff --git a/apps/chanora_flutter/lib/services/poke_preferences_service.dart b/apps/chanora_flutter/lib/services/poke_preferences_service.dart new file mode 100644 index 0000000..7e930a7 --- /dev/null +++ b/apps/chanora_flutter/lib/services/poke_preferences_service.dart @@ -0,0 +1,58 @@ +import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class PokePreferencesService { + static const _enabledKey = 'pokes.enabled'; + static const _mutedSendersKey = 'pokes.muted_senders'; + + final ValueNotifier _pokesEnabled = ValueNotifier(true); + final ValueNotifier> _mutedSenders = ValueNotifier>( + const {}, + ); + + ValueListenable get pokesEnabled => _pokesEnabled; + ValueListenable> get mutedSenders => _mutedSenders; + + Future load() async { + final prefs = await SharedPreferences.getInstance(); + _pokesEnabled.value = prefs.getBool(_enabledKey) ?? true; + _mutedSenders.value = (prefs.getStringList(_mutedSendersKey) ?? const []) + .map(BigInt.parse) + .toSet(); + } + + Future setPokesEnabled(bool enabled) async { + _pokesEnabled.value = enabled; + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_enabledKey, enabled); + } + + Future muteSender(BigInt senderId) async { + if (_mutedSenders.value.contains(senderId)) return; + _mutedSenders.value = {..._mutedSenders.value, senderId}; + await _saveMutedSenders(); + } + + Future unmuteSender(BigInt senderId) async { + if (!_mutedSenders.value.contains(senderId)) return; + _mutedSenders.value = _mutedSenders.value + .where((mutedSender) => mutedSender != senderId) + .toSet(); + await _saveMutedSenders(); + } + + bool isMuted(BigInt senderId) => _mutedSenders.value.contains(senderId); + + Future _saveMutedSenders() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setStringList( + _mutedSendersKey, + _mutedSenders.value.map((senderId) => senderId.toString()).toList(), + ); + } + + void dispose() { + _pokesEnabled.dispose(); + _mutedSenders.dispose(); + } +} diff --git a/apps/chanora_flutter/test/services/poke_preferences_service_test.dart b/apps/chanora_flutter/test/services/poke_preferences_service_test.dart new file mode 100644 index 0000000..4854dbc --- /dev/null +++ b/apps/chanora_flutter/test/services/poke_preferences_service_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:chanora_flutter/services/poke_preferences_service.dart'; + +void main() { + late PokePreferencesService service; + + setUp(() { + SharedPreferences.setMockInitialValues({}); + service = PokePreferencesService(); + }); + + test('loads defaults when preferences are unset', () async { + await service.load(); + + expect(service.pokesEnabled.value, isTrue); + expect(service.mutedSenders.value, isEmpty); + }); + + test('persists global enabled state', () async { + await service.load(); + await service.setPokesEnabled(false); + + final reloaded = PokePreferencesService(); + await reloaded.load(); + + expect(reloaded.pokesEnabled.value, isFalse); + }); + + test('persists muted senders and removes them on unmute', () async { + await service.load(); + final alice = BigInt.from(42); + final bob = BigInt.from(7); + + await service.muteSender(alice); + await service.muteSender(bob); + await service.unmuteSender(alice); + + final reloaded = PokePreferencesService(); + await reloaded.load(); + + expect(reloaded.mutedSenders.value, {bob}); + }); + + test('isMuted reflects in-memory changes synchronously', () async { + await service.load(); + final sender = BigInt.from(99); + + expect(service.isMuted(sender), isFalse); + + await service.muteSender(sender); + expect(service.isMuted(sender), isTrue); + + await service.unmuteSender(sender); + expect(service.isMuted(sender), isFalse); + }); +}