Files
IKAR/lib/frontend/screens/profile/notifications_screen.dart
T
sevenhill 0508b70732
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Tests / test (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Release IKAR 1.0.8: fix incoming calls, cancellation and call history
2026-10-08 21:34:15 +03:00

272 lines
9.1 KiB
Dart

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<NotificationsScreen> createState() => _NotificationsScreenState();
}
class _NotificationsScreenState extends State<NotificationsScreen>
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<void> _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<void> _apply(
bool value,
Future<void> Function() action,
ValueChanged<bool> 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<void> _setHaptics(bool value) async {
await Haptics.setEnabled(value);
if (value) Haptics.success();
if (mounted) setState(() => _hapticsEnabled = value);
}
Future<void> _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<void> _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,
),
],
),
],
),
),
);
}
}