import 'dart:io' show Platform; import 'package:flutter/material.dart'; import 'package:material_symbols_icons/symbols.dart'; import '../../../core/push/push_service.dart'; import '../../../core/calls/call_bridge.dart'; import '../../../core/utils/haptics.dart'; import '../../../l10n/app_localizations.dart'; import '../../../main.dart' show accountModule, isOnemeFlavor; import '../../widgets/confirm_dialog.dart'; import '../../widgets/connection_status.dart'; import '../../widgets/reload_on_reconnect.dart'; import '../../widgets/custom_notification.dart'; import '../../widgets/section_header.dart'; import '../../widgets/settings_card.dart'; import '../../widgets/small_spinner.dart'; import 'web_push_screen.dart'; class NotificationsScreen extends StatefulWidget { const NotificationsScreen({super.key}); @override State createState() => _NotificationsScreenState(); } class _NotificationsScreenState extends State with ReloadOnReconnect { bool _loading = true; bool _saving = false; bool _allNotifications = true; bool _messagePreview = true; bool _sound = true; bool _callNotifications = true; bool _newContacts = false; bool _hapticsEnabled = Haptics.enabled; @override void initState() { super.initState(); _load(); } @override void reloadAfterReconnect() => _load(); Future _load() async { final config = await accountModule.getPrivacyConfig(); if (!mounted) return; setState(() { _allNotifications = config.chatsPushNotification == 'ON'; _messagePreview = config.pushDetails; _sound = config.pushSound.isNotEmpty || config.chatsPushSound.isNotEmpty; _callNotifications = config.mCallPushNotification == 'ON'; _newContacts = config.pushNewContacts; _loading = false; }); } Future _apply( bool value, Future Function() action, ValueChanged assign, ) async { if (_saving) return; setState(() { assign(value); _saving = true; }); try { await action(); } catch (e) { if (mounted) { setState(() => assign(!value)); showCustomNotification( context, AppLocalizations.of(context)!.notificationsSaveFailed(e.toString()), ); } } finally { if (mounted) setState(() => _saving = false); } } Future _setHaptics(bool value) async { await Haptics.setEnabled(value); if (value) Haptics.success(); if (mounted) setState(() => _hapticsEnabled = value); } Future _onAllNotificationsChanged(bool value) async { if (value && isOnemeFlavor) { final granted = await PushService.instance.requestPermissionFromUser(); if (!mounted) return; if (!granted) { showCustomNotification( context, AppLocalizations.of(context)!.notificationsFkmPermissionDenied, ); return; } } await _apply( value, () => accountModule.setChatsPushNotification(value), (enabled) => _allNotifications = enabled, ); } Future _onCallNotificationsChanged(bool value) async { if (value && !await CallBridge.instance.canUseFullScreenIntent()) { if (!mounted) return; final l10n = AppLocalizations.of(context)!; final confirmed = await showConfirmDialog( context, title: l10n.notificationsCallPermissionTitle, message: l10n.notificationsCallPermissionMessage, confirmLabel: l10n.notificationsCallPermissionAction, ); if (!confirmed) return; await CallBridge.instance.openFullScreenIntentSettings(); } await _apply( value, () => accountModule.setCallNotifications(value), (enabled) => _callNotifications = enabled, ); } void _openWebPush() { Navigator.of( context, ).push(MaterialPageRoute(builder: (context) => const WebPushScreen())); } @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final l10n = AppLocalizations.of(context)!; return Scaffold( backgroundColor: cs.surface, appBar: ConnectionTitleBar( titleText: l10n.notificationsTitle, backgroundColor: cs.surface, ), body: SafeArea( top: false, child: _loading ? const Center(child: SmallSpinner(size: 36)) : ListView( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.fromLTRB(16, 12, 16, 120), children: [ if (Platform.isIOS) ...[ SettingsCard( children: [ SettingsNavTile( icon: Symbols.install_mobile, label: l10n.webPushTitle, onTap: _openWebPush, isLast: true, ), ], ), const SizedBox(height: 20), ], SectionHeader( l10n.notificationsMainSectionTitle, padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), fontSize: 14, ), SettingsCard( children: [ SettingsToggleTile( icon: Symbols.notifications, label: l10n.notificationsAllLabel, value: _allNotifications, onChanged: _onAllNotificationsChanged, ), ], ), const SizedBox(height: 20), SectionHeader( l10n.notificationsNewSectionTitle, padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), fontSize: 14, ), SettingsCard( children: [ SettingsToggleTile( icon: Symbols.chat, label: l10n.notificationsPreviewLabel, value: _messagePreview, enabled: _allNotifications, onChanged: (v) => _apply( v, () => accountModule.setMessagePreview(v), (b) => _messagePreview = b, ), ), SettingsToggleTile( icon: Symbols.music_note, label: l10n.notificationsSoundLabel, value: _sound, enabled: _allNotifications, onChanged: (v) => _apply( v, () => accountModule.setNotificationSound(v), (b) => _sound = b, ), ), ], ), const SizedBox(height: 20), SectionHeader( l10n.notificationsAdditionalSectionTitle, padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), fontSize: 14, ), SettingsCard( children: [ SettingsToggleTile( icon: Symbols.call, label: l10n.notificationsCallsLabel, value: _callNotifications, onChanged: _onCallNotificationsChanged, ), if (Platform.isAndroid) SettingsNavTile( icon: Symbols.lock, label: l10n.notificationsCallPermissionTitle, onTap: () => CallBridge.instance .openFullScreenIntentSettings(), ), SettingsToggleTile( icon: Symbols.person_add, label: l10n.notificationsNewContactsLabel, value: _newContacts, onChanged: (v) => _apply( v, () => accountModule.setNewContacts(v), (b) => _newContacts = b, ), ), ], ), const SizedBox(height: 20), SectionHeader( l10n.notificationsHapticsSectionTitle, padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), fontSize: 14, ), SettingsCard( children: [ SettingsToggleTile( icon: Symbols.vibration, label: l10n.notificationsHapticsLabel, subtitle: l10n.notificationsHapticsSubtitle, value: _hapticsEnabled, onChanged: _setHaptics, ), ], ), ], ), ), ); } }