From c3bc961a47b1f88e54878cc9c53945218acbcda8 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 26 Jan 2022 08:31:07 -0800 Subject: [PATCH 1/3] add window_manager plug in to get desktop active state to gate windows notifications; also add spam prevention to windows notifications --- lib/main.dart | 17 +++++++++++++++- lib/models/appstate.dart | 7 +++++++ lib/notification_manager.dart | 38 ++++++++++++++++++++++++++++------- pubspec.lock | 7 +++++++ 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index aea790ad..22946d4f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,6 +12,7 @@ import 'package:cwtch/settings.dart'; import 'package:cwtch/torstatus.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; +import 'package:window_manager/window_manager.dart'; import 'cwtch/cwtch.dart'; import 'cwtch/cwtchNotifier.dart'; import 'licenses.dart'; @@ -45,7 +46,7 @@ class Flwtch extends StatefulWidget { FlwtchState createState() => FlwtchState(); } -class FlwtchState extends State { +class FlwtchState extends State with WindowListener { final TextStyle biggerFont = const TextStyle(fontSize: 18); late Cwtch cwtch; late ProfileListState profs; @@ -56,6 +57,7 @@ class FlwtchState extends State { @override initState() { print("initState: running..."); + windowManager.addListener(this); super.initState(); print("initState: registering notification, shutdown handlers..."); @@ -203,8 +205,21 @@ class FlwtchState extends State { } } + // using windowManager flutter plugin until proper lifecycle management lands in desktop + + @override + void onWindowFocus() { + globalAppState.focus = true; + } + + @override + void onWindowBlur() { + globalAppState.focus = false; + } + @override void dispose() { + windowManager.removeListener(this); cwtch.dispose(); super.dispose(); } diff --git a/lib/models/appstate.dart b/lib/models/appstate.dart index 34d4c383..5a2293c3 100644 --- a/lib/models/appstate.dart +++ b/lib/models/appstate.dart @@ -14,6 +14,7 @@ class AppState extends ChangeNotifier { int? _selectedIndex; bool _unreadMessagesBelow = false; bool _disableFilePicker = false; + bool _focus = true; void SetCwtchInit() { cwtchInit = true; @@ -74,5 +75,11 @@ class AppState extends ChangeNotifier { notifyListeners(); } + bool get focus => _focus; + set focus(bool newVal) { + _focus = newVal; + notifyListeners(); + } + bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height; } diff --git a/lib/notification_manager.dart b/lib/notification_manager.dart index f01659be..aa7b84a1 100644 --- a/lib/notification_manager.dart +++ b/lib/notification_manager.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:cwtch/main.dart'; import 'package:desktoasts/desktoasts.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:path/path.dart' as path; @@ -22,9 +23,11 @@ class NullNotificationsManager implements NotificationsManager { class LinuxNotificationsManager implements NotificationsManager { int previous_id = 0; late NotificationsClient client; + LinuxNotificationsManager(NotificationsClient client) { this.client = client; } + Future notify(String message) async { var iconPath = Uri.file(path.join(path.current, "cwtch.png")); client.notify(message, appName: "cwtch", appIcon: iconPath.toString(), replacesId: this.previous_id).then((Notification value) => previous_id = value.id); @@ -35,22 +38,43 @@ class LinuxNotificationsManager implements NotificationsManager { // windows notifications class WindowsNotificationManager implements NotificationsManager { late ToastService service; + bool active = false; WindowsNotificationManager() { service = new ToastService( - appName: 'Cwtch', + appName: 'cwtch', companyName: 'Open Privacy Research Society', productName: 'Cwtch', ); + + service.stream.listen((event) { + if (event is ToastDismissed) { + print('Toast was dismissed.'); + active = false; + } + if (event is ToastActivated) { + print('Toast was clicked.'); + active = false; + } + if (event is ToastInteracted) { + print('${event.action} action in the toast was clicked.'); + active = false; + } + }); } Future notify(String message) async { - Toast toast = new Toast( - type: ToastType.text01, - title: 'Cwtch', - subtitle: message, - ); - service.show(toast); + if (!globalAppState.focus) { + if (!active) { + Toast toast = new Toast( + type: ToastType.text02, + title: 'Cwtch', + subtitle: message, + ); + service.show(toast); + active = true; + } + } } } diff --git a/pubspec.lock b/pubspec.lock index 8f1137b3..1e1a4bec 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -474,6 +474,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.4" + window_manager: + dependency: "direct main" + description: + name: window_manager + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" xdg_directories: dependency: transitive description: From 04cf1e16c26a7d2428dc627fa9e5b2632e031478 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 26 Jan 2022 08:42:25 -0800 Subject: [PATCH 2/3] pubspec for windows_manager --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 532fffce..c4dc007b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: file_picker_desktop: ^1.1.0 url_launcher: ^6.0.12 desktoasts: ^0.0.2 + window_manager: ^0.1.4 dev_dependencies: msix: ^2.1.3 From dc587f95f05d1f6fa9abe77779e8e90ad6f60139 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 26 Jan 2022 08:48:35 -0800 Subject: [PATCH 3/3] remove prints, add comments --- lib/notification_manager.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/notification_manager.dart b/lib/notification_manager.dart index aa7b84a1..c6463935 100644 --- a/lib/notification_manager.dart +++ b/lib/notification_manager.dart @@ -48,16 +48,16 @@ class WindowsNotificationManager implements NotificationsManager { ); service.stream.listen((event) { + // the user closed the notification of the OS timed it out if (event is ToastDismissed) { - print('Toast was dismissed.'); active = false; } + // clicked if (event is ToastActivated) { - print('Toast was clicked.'); - active = false; + active = false; } + // if a supplied action was clicked if (event is ToastInteracted) { - print('${event.action} action in the toast was clicked.'); active = false; } }); @@ -66,6 +66,8 @@ class WindowsNotificationManager implements NotificationsManager { Future notify(String message) async { if (!globalAppState.focus) { if (!active) { + // One string of bold text on the first line (title), + // one string (subtitle) of regular text wrapped across the second and third lines. Toast toast = new Toast( type: ToastType.text02, title: 'Cwtch',