207 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Dart
		
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Dart
		
	
	
	
| import 'dart:convert';
 | |
| 
 | |
| import 'package:flutter/material.dart';
 | |
| 
 | |
| import 'package:fluffychat/generated/l10n/l10n.dart';
 | |
| import 'package:matrix/matrix.dart';
 | |
| 
 | |
| import 'package:fluffychat/config/app_config.dart';
 | |
| import 'package:fluffychat/pages/settings_notifications/push_rule_extensions.dart';
 | |
| import 'package:fluffychat/utils/localized_exception_extension.dart';
 | |
| import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
 | |
| import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
 | |
| import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
 | |
| import 'package:fluffychat/widgets/future_loading_dialog.dart';
 | |
| import '../../widgets/matrix.dart';
 | |
| import 'settings_notifications_view.dart';
 | |
| 
 | |
| class SettingsNotifications extends StatefulWidget {
 | |
|   const SettingsNotifications({super.key});
 | |
| 
 | |
|   @override
 | |
|   SettingsNotificationsController createState() =>
 | |
|       SettingsNotificationsController();
 | |
| }
 | |
| 
 | |
| class SettingsNotificationsController extends State<SettingsNotifications> {
 | |
|   bool isLoading = false;
 | |
| 
 | |
|   void onPusherTap(Pusher pusher) async {
 | |
|     final delete = await showModalActionPopup<bool>(
 | |
|       context: context,
 | |
|       title: pusher.deviceDisplayName,
 | |
|       message: '${pusher.appDisplayName} (${pusher.appId})',
 | |
|       cancelLabel: L10n.of(context).cancel,
 | |
|       actions: [
 | |
|         AdaptiveModalAction(
 | |
|           label: L10n.of(context).delete,
 | |
|           isDestructive: true,
 | |
|           value: true,
 | |
|         ),
 | |
|       ],
 | |
|     );
 | |
|     if (delete != true) return;
 | |
| 
 | |
|     final success = await showFutureLoadingDialog(
 | |
|       context: context,
 | |
|       future: () => Matrix.of(context).client.deletePusher(
 | |
|             PusherId(
 | |
|               appId: pusher.appId,
 | |
|               pushkey: pusher.pushkey,
 | |
|             ),
 | |
|           ),
 | |
|     );
 | |
| 
 | |
|     if (success.error != null) return;
 | |
| 
 | |
|     setState(() {
 | |
|       pusherFuture = null;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   Future<List<Pusher>?>? pusherFuture;
 | |
| 
 | |
|   void togglePushRule(PushRuleKind kind, PushRule pushRule) async {
 | |
|     setState(() {
 | |
|       isLoading = true;
 | |
|     });
 | |
|     try {
 | |
|       final updateFromSync = Matrix.of(context)
 | |
|           .client
 | |
|           .onSync
 | |
|           .stream
 | |
|           .where(
 | |
|             (syncUpdate) =>
 | |
|                 syncUpdate.accountData?.any(
 | |
|                   (accountData) => accountData.type == 'm.push_rules',
 | |
|                 ) ??
 | |
|                 false,
 | |
|           )
 | |
|           .first;
 | |
|       await Matrix.of(context).client.setPushRuleEnabled(
 | |
|             kind,
 | |
|             pushRule.ruleId,
 | |
|             !pushRule.enabled,
 | |
|           );
 | |
|       await updateFromSync;
 | |
|     } catch (e, s) {
 | |
|       Logs().w('Unable to toggle push rule', e, s);
 | |
|       if (!mounted) return;
 | |
|       ScaffoldMessenger.of(context)
 | |
|           .showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
 | |
|     } finally {
 | |
|       if (mounted) {
 | |
|         setState(() {
 | |
|           isLoading = false;
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void editPushRule(PushRule rule, PushRuleKind kind) async {
 | |
|     final theme = Theme.of(context);
 | |
|     final action = await showAdaptiveDialog<PushRuleDialogAction>(
 | |
|       context: context,
 | |
|       builder: (context) => ConstrainedBox(
 | |
|         constraints: const BoxConstraints(maxWidth: 256),
 | |
|         child: AlertDialog.adaptive(
 | |
|           title: Text(rule.getPushRuleName(L10n.of(context))),
 | |
|           content: Padding(
 | |
|             padding: const EdgeInsets.only(top: 16.0),
 | |
|             child: Material(
 | |
|               borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | |
|               color: theme.colorScheme.surfaceContainer,
 | |
|               child: SingleChildScrollView(
 | |
|                 padding: const EdgeInsets.all(16),
 | |
|                 scrollDirection: Axis.horizontal,
 | |
|                 child: SelectableText(
 | |
|                   prettyJson(rule.toJson()),
 | |
|                   style: TextStyle(
 | |
|                     color: theme.colorScheme.onSurface,
 | |
|                   ),
 | |
|                 ),
 | |
|               ),
 | |
|             ),
 | |
|           ),
 | |
|           actions: [
 | |
|             AdaptiveDialogAction(
 | |
|               onPressed: Navigator.of(context).pop,
 | |
|               child: Text(L10n.of(context).close),
 | |
|             ),
 | |
|             if (!rule.ruleId.startsWith('.m.'))
 | |
|               AdaptiveDialogAction(
 | |
|                 onPressed: () =>
 | |
|                     Navigator.of(context).pop(PushRuleDialogAction.delete),
 | |
|                 child: Text(
 | |
|                   L10n.of(context).delete,
 | |
|                   style: TextStyle(color: theme.colorScheme.error),
 | |
|                 ),
 | |
|               ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|     if (action == null) return;
 | |
|     if (!mounted) return;
 | |
|     switch (action) {
 | |
|       case PushRuleDialogAction.delete:
 | |
|         final consent = await showOkCancelAlertDialog(
 | |
|           context: context,
 | |
|           title: L10n.of(context).areYouSure,
 | |
|           message: L10n.of(context).deletePushRuleCanNotBeUndone,
 | |
|           okLabel: L10n.of(context).delete,
 | |
|           isDestructive: true,
 | |
|         );
 | |
|         if (consent != OkCancelResult.ok) return;
 | |
|         if (!mounted) return;
 | |
|         setState(() {
 | |
|           isLoading = true;
 | |
|         });
 | |
|         try {
 | |
|           final updateFromSync = Matrix.of(context)
 | |
|               .client
 | |
|               .onSync
 | |
|               .stream
 | |
|               .where(
 | |
|                 (syncUpdate) =>
 | |
|                     syncUpdate.accountData?.any(
 | |
|                       (accountData) => accountData.type == 'm.push_rules',
 | |
|                     ) ??
 | |
|                     false,
 | |
|               )
 | |
|               .first;
 | |
|           await Matrix.of(context).client.deletePushRule(
 | |
|                 kind,
 | |
|                 rule.ruleId,
 | |
|               );
 | |
|           await updateFromSync;
 | |
|         } catch (e, s) {
 | |
|           Logs().w('Unable to delete push rule', e, s);
 | |
|           if (!mounted) return;
 | |
|           ScaffoldMessenger.of(context).showSnackBar(
 | |
|             SnackBar(content: Text(e.toLocalizedString(context))),
 | |
|           );
 | |
|         } finally {
 | |
|           if (mounted) {
 | |
|             setState(() {
 | |
|               isLoading = false;
 | |
|             });
 | |
|           }
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) => SettingsNotificationsView(this);
 | |
| }
 | |
| 
 | |
| enum PushRuleDialogAction { delete }
 | |
| 
 | |
| String prettyJson(Map<String, Object?> json) {
 | |
|   const decoder = JsonDecoder();
 | |
|   const encoder = JsonEncoder.withIndent('    ');
 | |
|   final object = decoder.convert(jsonEncode(json));
 | |
|   return encoder.convert(object);
 | |
| }
 |