smarter notif logic

This commit is contained in:
Henry Hiles 2026-08-26 11:55:20 -04:00
commit 0122feae1d
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
2 changed files with 47 additions and 24 deletions

View file

@ -1,9 +1,11 @@
import "dart:convert";
import "dart:io"; import "dart:io";
import "package:flutter_local_notifications/flutter_local_notifications.dart"; import "package:flutter_local_notifications/flutter_local_notifications.dart";
// ignore: implementation_imports // ignore: implementation_imports
import "package:flutter_local_notifications_linux/src/model/hint.dart"; import "package:flutter_local_notifications_linux/src/model/hint.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/event.dart";
class NotificationsController class NotificationsController
extends AsyncNotifier<FlutterLocalNotificationsPlugin> { extends AsyncNotifier<FlutterLocalNotificationsPlugin> {
@ -26,7 +28,11 @@ class NotificationsController
linux: .new(defaultActionName: "Open"), linux: .new(defaultActionName: "Open"),
), ),
onDidReceiveNotificationResponse: (details) { onDidReceiveNotificationResponse: (details) {
print(details.data); if (details.payload case final payload?) {
final event = ref.watch(
EventController.provider(.fromJson(json.decode(payload))),
);
}
}, },
); );
@ -75,8 +81,6 @@ class NotificationsController
"Messages", "Messages",
largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path), largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path),
), ),
iOS: .new(),
macOS: .new(),
linux: .new( linux: .new(
customHints: icon == null customHints: icon == null
? null ? null
@ -87,6 +91,9 @@ class NotificationsController
), ),
], ],
), ),
// TODO: See if icons can be added to iOS, macOS, and Windows notifications (#68)
iOS: .new(),
macOS: .new(),
windows: .new(), windows: .new(),
); );

View file

@ -3,15 +3,19 @@ import "dart:io";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:intl/intl.dart"; import "package:intl/intl.dart";
import "package:nexus/controllers/key.dart";
import "package:nexus/controllers/notifications.dart"; import "package:nexus/controllers/notifications.dart";
import "package:nexus/controllers/rooms.dart"; import "package:nexus/controllers/rooms.dart";
import "package:nexus/main.dart"; import "package:nexus/main.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/client_state.dart"; import "package:nexus/controllers/client_state.dart";
import "package:nexus/models/content/message.dart"; import "package:nexus/models/content/message.dart";
import "package:nexus/models/content/sticker.dart";
import "package:nexus/models/requests/get_event.dart";
import "package:nexus/models/requests/register_pusher.dart"; import "package:nexus/models/requests/register_pusher.dart";
import "package:unifiedpush/unifiedpush.dart"; import "package:unifiedpush/unifiedpush.dart";
import "package:unifiedpush_storage_shared_preferences/storage.dart"; import "package:unifiedpush_storage_shared_preferences/storage.dart";
import "package:window_manager/window_manager.dart";
class UnifiedPushController extends AsyncNotifier<bool> { class UnifiedPushController extends AsyncNotifier<bool> {
@override @override
@ -23,8 +27,7 @@ class UnifiedPushController extends AsyncNotifier<bool> {
storage: UnifiedPushStorageSharedPreferences(), storage: UnifiedPushStorageSharedPreferences(),
background: isInBackground, background: isInBackground,
), ),
onNewEndpoint: (endpoint, instance) async { onNewEndpoint: (endpoint, instance) => client.registerPusher(
await client.registerPusher(
.new( .new(
appDisplayName: "Nexus", appDisplayName: "Nexus",
appId: "nexus.federated.nexus", appId: "nexus.federated.nexus",
@ -38,17 +41,25 @@ class UnifiedPushController extends AsyncNotifier<bool> {
lang: "en", lang: "en",
pushKey: endpoint.pubKeySet!.pubKey, pushKey: endpoint.pubKeySet!.pubKey,
), ),
); ),
},
onMessage: (message, instance) async { onMessage: (message, instance) async {
if (message.decrypted == false) { if (message.decrypted == false) {
// TODO: Handle more gracefully, show option to re-register pusher throw Exception(
throw Exception("Failed to decrypt notification"); "Failed to decrypt notification. Try toggling off and on UnifiedPush in settings.",
);
} }
final event = await client.handlePush( final event = await client.handlePush(
json.decode(String.fromCharCodes(message.content)), json.decode(String.fromCharCodes(message.content)),
); );
if (event.unreadType?.shouldNotify != true ||
(!isInBackground &&
await windowManager.isFocused() &&
ref.watch(KeyController.provider(KeyController.roomKey)) ==
event.roomId)) {
return;
}
final room = ref.read( final room = ref.read(
RoomsController.provider.select((rooms) => rooms[event.roomId]), RoomsController.provider.select((rooms) => rooms[event.roomId]),
); );
@ -64,14 +75,19 @@ class UnifiedPushController extends AsyncNotifier<bool> {
.read(NotificationsController.provider.notifier) .read(NotificationsController.provider.notifier)
.send( .send(
id: event.eventId.hashCode & 0x7fffffff, id: event.eventId.hashCode & 0x7fffffff,
title: room?.metadata?.name ?? "New Message", title: room?.metadata?.name ?? "New Event",
icon: icon, icon: icon,
payload: json.encode(
GetEventRequest(eventId: event.eventId, roomId: event.roomId),
),
body: switch (event.content) { body: switch (event.content) {
MessageContent(:final body) => body, MessageContent(:final body?) ||
// TODO: Handle more types StickerContent(:final body) => body,
_ => null, _ => null,
}, },
); );
if (isInBackground) exit(0);
}, },
onRegistrationFailed: (error, instance) => throw error, onRegistrationFailed: (error, instance) => throw error,
onUnregistered: (instance) => ref.invalidateSelf(), onUnregistered: (instance) => ref.invalidateSelf(),