forked from cwtch.im/cwtch-ui
Merge pull request 'WIP make NixNotificationManager using flutter_local_notification' (#375) from macNotifications into trunk
Reviewed-on: cwtch.im/cwtch-ui#375 Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
This commit is contained in:
commit
ce1db17148
|
@ -1 +1 @@
|
|||
2022-02-10-15-43-v1.6.0
|
||||
2022-02-23-17-17-v1.6.0-2-ge8b2def
|
|
@ -1 +1 @@
|
|||
2022-02-10-20-44-v1.6.0
|
||||
2022-02-23-22-17-v1.6.0-2-ge8b2def
|
BIN
assets/knott.png
BIN
assets/knott.png
Binary file not shown.
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 51 KiB |
|
@ -165,10 +165,10 @@ class CwtchNotifier {
|
|||
var notification = data["notification"];
|
||||
|
||||
if (notification == "SimpleEvent") {
|
||||
notificationManager.notify(notificationSimple ?? "New Message");
|
||||
notificationManager.notify(notificationSimple ?? "New Message", "", 0);
|
||||
} else if (notification == "ContactInfo") {
|
||||
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier);
|
||||
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())));
|
||||
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())), data["ProfileOnion"], identifier);
|
||||
}
|
||||
|
||||
profileCN.getProfile(data["ProfileOnion"])?.newMessage(
|
||||
|
@ -242,10 +242,10 @@ class CwtchNotifier {
|
|||
profileCN.getProfile(data["ProfileOnion"])?.newMessage(identifier, idx, timestampSent, senderHandle, senderImage, isAuto, data["Data"], contenthash, selectedProfile, selectedConversation);
|
||||
|
||||
if (notification == "SimpleEvent") {
|
||||
notificationManager.notify(notificationSimple ?? "New Message");
|
||||
notificationManager.notify(notificationSimple ?? "New Message", "", 0);
|
||||
} else if (notification == "ContactInfo") {
|
||||
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier);
|
||||
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())));
|
||||
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())), data["ProfileOnion"], identifier);
|
||||
}
|
||||
appState.notifyProfileUnread();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||
import 'package:cwtch/config.dart';
|
||||
import 'package:cwtch/notification_manager.dart';
|
||||
import 'package:cwtch/themes/cwtch.dart';
|
||||
import 'package:cwtch/views/doublecolview.dart';
|
||||
import 'package:cwtch/views/messageview.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cwtch/cwtch/ffi.dart';
|
||||
|
@ -17,8 +18,11 @@ import 'cwtch/cwtch.dart';
|
|||
import 'cwtch/cwtchNotifier.dart';
|
||||
import 'licenses.dart';
|
||||
import 'models/appstate.dart';
|
||||
import 'models/contactlist.dart';
|
||||
import 'models/profile.dart';
|
||||
import 'models/profilelist.dart';
|
||||
import 'models/servers.dart';
|
||||
import 'views/contactsview.dart';
|
||||
import 'views/profilemgrview.dart';
|
||||
import 'views/splashView.dart';
|
||||
import 'dart:io' show Platform, exit, sleep;
|
||||
|
@ -78,10 +82,10 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
|||
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList);
|
||||
cwtch = CwtchGomobile(cwtchNotifier);
|
||||
} else if (Platform.isLinux) {
|
||||
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(), globalAppState, globalServersList);
|
||||
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList);
|
||||
cwtch = CwtchFfi(cwtchNotifier);
|
||||
} else {
|
||||
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(), globalAppState, globalServersList);
|
||||
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList);
|
||||
cwtch = CwtchFfi(cwtchNotifier);
|
||||
}
|
||||
print("initState: invoking cwtch.Start()");
|
||||
|
@ -182,36 +186,43 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
|||
// coder beware: args["RemotePeer"] is actually a handle, and could be eg a groupID
|
||||
Future<void> _externalNotificationClicked(MethodCall call) async {
|
||||
var args = jsonDecode(call.arguments);
|
||||
var profile = profs.getProfile(args["ProfileOnion"])!;
|
||||
var convo = profile.contactList.getContact(args["Handle"])!;
|
||||
_notificationSelectConvo(args["ProfileOnion"], args["Handle"]);
|
||||
}
|
||||
|
||||
Future<void> _notificationSelectConvo(String profileOnion, int convoId) async {
|
||||
var profile = profs.getProfile(profileOnion)!;
|
||||
var convo = profile.contactList.getContact(convoId)!;
|
||||
if (profileOnion.isEmpty) {
|
||||
return;
|
||||
}
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).initialScrollIndex = convo.unreadMessages;
|
||||
convo.unreadMessages = 0;
|
||||
|
||||
// single pane mode pushes; double pane mode reads AppState.selectedProfile/Conversation
|
||||
var isLandscape = Provider.of<AppState>(navKey.currentContext!, listen: false).isLandscape(navKey.currentContext!);
|
||||
if (Provider.of<Settings>(navKey.currentContext!, listen: false).uiColumns(isLandscape).length == 1) {
|
||||
// Clear nav path back to root
|
||||
while (navKey.currentState!.canPop()) {
|
||||
print("messageview already open; popping before pushing replacement");
|
||||
navKey.currentState!.pop();
|
||||
}
|
||||
navKey.currentState?.push(
|
||||
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = null;
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedProfile = profileOnion;
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = convoId;
|
||||
|
||||
Navigator.of(navKey.currentContext!).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (BuildContext builderContext) {
|
||||
settings: RouteSettings(name: "conversations"),
|
||||
builder: (BuildContext buildcontext) {
|
||||
return OrientationBuilder(builder: (orientationBuilderContext, orientation) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(value: profile),
|
||||
ChangeNotifierProvider.value(value: convo),
|
||||
],
|
||||
builder: (context, child) => MessageView(),
|
||||
);
|
||||
providers: [ChangeNotifierProvider<ProfileInfoState>.value(value: profile), ChangeNotifierProvider<ContactListState>.value(value: profile.contactList)],
|
||||
builder: (innercontext, widget) {
|
||||
var appState = Provider.of<AppState>(navKey.currentContext!);
|
||||
var settings = Provider.of<Settings>(navKey.currentContext!);
|
||||
return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : MessageView();
|
||||
});
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
//dual pane
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedProfile = args["ProfileOnion"];
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = args["Handle"];
|
||||
}
|
||||
}
|
||||
|
||||
// using windowManager flutter plugin until proper lifecycle management lands in desktop
|
||||
|
|
|
@ -1,38 +1,27 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cwtch/main.dart';
|
||||
import 'package:win_toast/win_toast.dart';
|
||||
import 'package:desktop_notifications/desktop_notifications.dart';
|
||||
//import 'package:desktop_notifications/desktop_notifications.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:flutter_local_notifications_linux/flutter_local_notifications_linux.dart';
|
||||
import 'package:flutter_local_notifications_linux/src/model/hint.dart';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'config.dart';
|
||||
|
||||
// NotificationsManager provides a wrapper around platform specific notifications logic.
|
||||
abstract class NotificationsManager {
|
||||
Future<void> notify(String message);
|
||||
Future<void> notify(String message, String profile, int conversationId);
|
||||
}
|
||||
|
||||
// NullNotificationsManager ignores all notification requests
|
||||
class NullNotificationsManager implements NotificationsManager {
|
||||
@override
|
||||
Future<void> notify(String message) async {}
|
||||
}
|
||||
|
||||
// LinuxNotificationsManager uses the desktop_notifications package to implement
|
||||
// the standard dbus-powered linux desktop notifications.
|
||||
class LinuxNotificationsManager implements NotificationsManager {
|
||||
int previous_id = 0;
|
||||
late NotificationsClient client;
|
||||
|
||||
LinuxNotificationsManager(NotificationsClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
Future<void> 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);
|
||||
}
|
||||
Future<void> notify(String message, String profile, int conversationId) async {}
|
||||
}
|
||||
|
||||
// Windows Notification Manager uses https://pub.dev/packages/desktoasts to implement
|
||||
|
@ -47,7 +36,7 @@ class WindowsNotificationManager implements NotificationsManager {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> notify(String message) async {
|
||||
Future<void> notify(String message, String profile, int conversationId) async {
|
||||
if (initialized && !globalAppState.focus) {
|
||||
if (!active) {
|
||||
active = true;
|
||||
|
@ -64,16 +53,75 @@ class WindowsNotificationManager implements NotificationsManager {
|
|||
}
|
||||
}
|
||||
|
||||
NotificationsManager newDesktopNotificationsManager() {
|
||||
if (Platform.isLinux) {
|
||||
class NotificationPayload {
|
||||
late String profileOnion;
|
||||
late int convoId;
|
||||
|
||||
NotificationPayload(String po, int cid) {
|
||||
profileOnion = po;
|
||||
convoId = cid;
|
||||
}
|
||||
|
||||
NotificationPayload.fromJson(Map<String, dynamic> json)
|
||||
: profileOnion = json['profileOnion'],
|
||||
convoId = json['convoId'];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'profileOnion': profileOnion,
|
||||
'convoId': convoId,
|
||||
};
|
||||
}
|
||||
|
||||
// FlutterLocalNotificationsPlugin based NotificationManager that handles Linux and MacOS
|
||||
// Todo: it can also handle Android, do we want to migrate away from our manual solution?
|
||||
class NixNotificationManager implements NotificationsManager {
|
||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
||||
late Future<void> Function(String, int) notificationSelectConvo;
|
||||
|
||||
NixNotificationManager(Future<void> Function(String, int) notificationSelectConvo) {
|
||||
this.notificationSelectConvo = notificationSelectConvo;
|
||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
final MacOSInitializationSettings initializationSettingsMacOS = MacOSInitializationSettings(defaultPresentSound: false);
|
||||
final LinuxInitializationSettings initializationSettingsLinux =
|
||||
LinuxInitializationSettings(defaultActionName: 'Open notification', defaultIcon: AssetsLinuxIcon('assets/knott.png'), defaultSuppressSound: true);
|
||||
|
||||
final InitializationSettings initializationSettings = InitializationSettings(android: null, iOS: null, macOS: initializationSettingsMacOS, linux: initializationSettingsLinux);
|
||||
|
||||
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<MacOSFlutterLocalNotificationsPlugin>()?.requestPermissions(
|
||||
alert: true,
|
||||
badge: false,
|
||||
sound: false,
|
||||
);
|
||||
|
||||
scheduleMicrotask(() async {
|
||||
await flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: selectNotification);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> notify(String message, String profile, int conversationId) async {
|
||||
if (!globalAppState.focus) {
|
||||
// Warning: Only use title field on Linux, body field will render links as clickable
|
||||
await flutterLocalNotificationsPlugin.show(0, message, '', NotificationDetails(linux: LinuxNotificationDetails(suppressSound: true, category: LinuxNotificationCategory.imReceived())),
|
||||
payload: jsonEncode(NotificationPayload(profile, conversationId)));
|
||||
}
|
||||
}
|
||||
|
||||
// Notification click response function, triggers ui jump to conversation
|
||||
void selectNotification(String? payloadJson) async {
|
||||
if (payloadJson != null) {
|
||||
Map<String, dynamic> payloadMap = jsonDecode(payloadJson);
|
||||
var payload = NotificationPayload.fromJson(payloadMap);
|
||||
notificationSelectConvo(payload.profileOnion, payload.convoId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsManager newDesktopNotificationsManager(Future<void> Function(String profileOnion, int convoId) notificationSelectConvo) {
|
||||
if (Platform.isLinux || Platform.isMacOS) {
|
||||
try {
|
||||
// Test that we can actually access DBUS. Otherwise return a null
|
||||
// notifications manager...
|
||||
NotificationsClient client = NotificationsClient();
|
||||
client.getCapabilities();
|
||||
return LinuxNotificationsManager(client);
|
||||
return NixNotificationManager(notificationSelectConvo);
|
||||
} catch (e) {
|
||||
EnvironmentConfig.debugLog("Attempted to access DBUS for notifications but failed. Switching off notifications.");
|
||||
EnvironmentConfig.debugLog("Failed to create NixNotificationManager. Switching off notifications.");
|
||||
}
|
||||
} else if (Platform.isWindows) {
|
||||
try {
|
||||
|
@ -82,5 +130,6 @@ NotificationsManager newDesktopNotificationsManager() {
|
|||
EnvironmentConfig.debugLog("Failed to create Windows desktoasts notification manager");
|
||||
}
|
||||
}
|
||||
|
||||
return NullNotificationsManager();
|
||||
}
|
||||
|
|
46
pubspec.lock
46
pubspec.lock
|
@ -189,14 +189,7 @@ packages:
|
|||
name: dbus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.6"
|
||||
desktop_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: desktop_notifications
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.7.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -256,6 +249,27 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0-rc.9"
|
||||
flutter_local_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "9.3.2"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
flutter_local_notifications_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -519,13 +533,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -671,6 +678,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.3"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timezone
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -803,7 +817,7 @@ packages:
|
|||
name: xdg_directories
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.2.0+1"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -34,7 +34,6 @@ dependencies:
|
|||
cupertino_icons: ^1.0.0
|
||||
ffi: ^1.0.0
|
||||
path_provider: ^2.0.0
|
||||
desktop_notifications: 0.5.0
|
||||
crypto: 3.0.1
|
||||
|
||||
glob: any
|
||||
|
@ -42,8 +41,9 @@ dependencies:
|
|||
file_picker: ^4.3.2
|
||||
file_picker_desktop: ^1.1.0
|
||||
url_launcher: ^6.0.18
|
||||
win_toast: ^0.0.2
|
||||
window_manager: ^0.1.4
|
||||
win_toast: ^0.0.2
|
||||
flutter_local_notifications: 9.3.2
|
||||
|
||||
dev_dependencies:
|
||||
msix: ^2.1.3
|
||||
|
|
Loading…
Reference in New Issue