diff --git a/apps/chanora_flutter/lib/widgets/poke_notification_settings.dart b/apps/chanora_flutter/lib/widgets/poke_notification_settings.dart new file mode 100644 index 0000000..f876552 --- /dev/null +++ b/apps/chanora_flutter/lib/widgets/poke_notification_settings.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +import '../l10n/generated/app_localizations.dart'; +import '../services/poke_preferences_service.dart'; +import 'voice_settings_controls.dart'; + +class PokeNotificationSettingsDialog extends StatelessWidget { + const PokeNotificationSettingsDialog({super.key, required this.preferences}); + + final PokePreferencesService preferences; + + @override + Widget build(BuildContext context) { + final l10n = AppL10n.of(context); + final theme = Theme.of(context); + return AlertDialog( + title: Text(l10n.pokeSettingsTitle), + contentPadding: const EdgeInsets.fromLTRB(24, 16, 24, 0), + content: SizedBox( + width: 400, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ValueListenableBuilder( + valueListenable: preferences.pokesEnabled, + builder: (context, enabled, _) => SwitchListTile( + dense: true, + contentPadding: EdgeInsets.zero, + title: Text(l10n.pokeSettingsEnableLabel), + subtitle: Text(l10n.pokeSettingsEnableDescription), + value: enabled, + onChanged: (value) => preferences.setPokesEnabled(value), + ), + ), + const Divider(height: 24), + VoiceSectionHeader(l10n.pokeSettingsMutedSendersHeader), + ValueListenableBuilder>( + valueListenable: preferences.mutedSenders, + builder: (context, mutedSenders, _) { + final senders = mutedSenders.toList()..sort(); + if (senders.isEmpty) { + return Padding( + padding: const EdgeInsets.only(top: 8, bottom: 8), + child: Text( + l10n.pokeSettingsMutedSendersEmpty, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ); + } + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final senderId in senders) + ListTile( + dense: true, + contentPadding: EdgeInsets.zero, + leading: const Icon(Icons.notifications_off_outlined), + title: Text( + l10n.pokeSettingsMutedSenderLabel( + senderId.toString(), + ), + ), + trailing: TextButton( + onPressed: () => preferences.unmuteSender(senderId), + child: Text(l10n.pokeSettingsUnmuteSenderAction), + ), + ), + ], + ); + }, + ), + const SizedBox(height: 8), + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(l10n.closeAction), + ), + ], + ); + } +} diff --git a/apps/chanora_flutter/test/widgets/poke_notification_settings_test.dart b/apps/chanora_flutter/test/widgets/poke_notification_settings_test.dart new file mode 100644 index 0000000..c33b651 --- /dev/null +++ b/apps/chanora_flutter/test/widgets/poke_notification_settings_test.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:chanora_flutter/l10n/generated/app_localizations.dart'; +import 'package:chanora_flutter/services/poke_preferences_service.dart'; +import 'package:chanora_flutter/widgets/poke_notification_settings.dart'; + +void main() { + testWidgets('toggles poke notifications and unmutes senders', (tester) async { + SharedPreferences.setMockInitialValues({}); + final preferences = PokePreferencesService(); + await preferences.load(); + await preferences.muteSender(BigInt.from(42)); + addTearDown(preferences.dispose); + + await tester.pumpWidget( + MaterialApp( + localizationsDelegates: AppL10n.localizationsDelegates, + supportedLocales: AppL10n.supportedLocales, + home: PokeNotificationSettingsDialog(preferences: preferences), + ), + ); + + expect(find.text('Poke notifications'), findsOneWidget); + expect(find.text('Client ID 42'), findsOneWidget); + + await tester.tap(find.byType(Switch)); + await tester.pumpAndSettle(); + expect(preferences.pokesEnabled.value, isFalse); + + await tester.tap(find.text('Unmute')); + await tester.pumpAndSettle(); + expect(preferences.isMuted(BigInt.from(42)), isFalse); + expect(find.text('No muted poke senders.'), findsOneWidget); + }); +}