Compare commits

..
5 changed files with 94 additions and 67 deletions

View file

@ -49,7 +49,7 @@ nexus = {
This tracks the latest commit on `main`. To stay on a release instead, pin the input to its tag: This tracks the latest commit on `main`. To stay on a release instead, pin the input to its tag:
```nix ```nix
url = "git+https://git.federated.nexus/Nexus/nexus?ref=refs/tags/v1.0.0-alpha5"; url = "git+https://git.federated.nexus/Nexus/nexus?ref=refs/tags/v1.0.0-alpha4";
``` ```
Then import the module and enable it in your NixOS configuration: Then import the module and enable it in your NixOS configuration:

View file

@ -10,8 +10,16 @@ import "package:nexus/main.dart";
import "package:flutter_local_notifications/flutter_local_notifications.dart"; import "package:flutter_local_notifications/flutter_local_notifications.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/notification_request.dart"; import "package:nexus/controllers/notification_request.dart";
import "package:nexus/models/push_notification.dart";
import "package:xdg_desktop_portal/xdg_desktop_portal.dart"; import "package:xdg_desktop_portal/xdg_desktop_portal.dart";
typedef NotificationMessage = ({
NotificationUser sender,
NotificationUser self,
File? senderAvatar,
bool isDm,
});
class NotificationController class NotificationController
extends AsyncNotifier<FlutterLocalNotificationsPlugin> { extends AsyncNotifier<FlutterLocalNotificationsPlugin> {
@override @override
@ -140,24 +148,24 @@ class NotificationController
return true; return true;
} }
/// Passing [message] shows it as a chat message in the conversation for
/// [threadId], naming the sender in group chats.
Future<void> send({ Future<void> send({
required int id, required int id,
required String title, required String title,
String? body, required String body,
File? icon,
String? payload, String? payload,
String? senderName,
File? senderAvatar,
String? selfName,
bool isGroup = false,
bool sound = true,
DateTime? timestamp,
String? threadId, String? threadId,
DateTime? timestamp,
File? icon,
bool sound = true,
NotificationMessage? message,
}) async { }) async {
debugPrint("Sending notification for $id"); debugPrint("Sending notification for $id");
// In DMs the title is already the sender, so only prefix in groups // In DMs the title is already the sender
final prefix = isGroup ? senderName : null; final sender = message == null || message.isDm ? null : message.sender;
final prefixedBody = sender == null ? body : "${sender.name}: $body";
if (Platform.isLinux) { if (Platform.isLinux) {
final portal = await ref.read(PortalController.provider.future); final portal = await ref.read(PortalController.provider.future);
@ -166,14 +174,14 @@ class NotificationController
await portal.notification.addNotification( await portal.notification.addNotification(
id.toString(), id.toString(),
title: title, title: title,
body: prefix == null || body == null ? body : "$prefix: $body", body: prefixedBody,
markupBody: prefix == null || body == null markupBody: sender == null
? null ? null
: "<b>${escape.convert(prefix)}</b>: ${escape.convert(body)}", : "<b>${escape.convert(sender.name)}</b>: ${escape.convert(body)}",
defaultAction: "app.event", defaultAction: "app.event",
defaultActionTarget: payload, defaultActionTarget: payload,
sound: sound ? null : const XdgNotificationSoundSilent(), sound: sound ? null : const XdgNotificationSoundSilent(),
category: XdgNotificationCategory.imReceived, category: message == null ? null : XdgNotificationCategory.imReceived,
priority: .normal, priority: .normal,
buttons: [ buttons: [
// TODO: Impl when notif daemons actualy support imReplyWithText // TODO: Impl when notif daemons actualy support imReplyWithText
@ -184,50 +192,74 @@ class NotificationController
// ), // ),
], ],
); );
} else { return;
final controller = await future; }
// Append to the conversation already shown under this id, if any final controller = await future;
final existingStyle = Platform.isAndroid
? await controller
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.getActiveNotificationMessagingStyle(id: id)
.onError((_, _) => null)
: null;
final darwin = DarwinNotificationDetails( // Append to the conversation already shown under this id, if any
presentSound: sound, final existingStyle = Platform.isAndroid && message != null
threadIdentifier: threadId, ? await controller
); .resolvePlatformSpecificImplementation<
final notificationDetails = NotificationDetails( AndroidFlutterLocalNotificationsPlugin
android: .new( >()
"messages", ?.getActiveNotificationMessagingStyle(id: id)
"Messages", .onError((_, _) => null)
silent: !sound, : null;
largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path),
styleInformation: senderName == null || body == null final darwin = DarwinNotificationDetails(
? null presentSound: sound,
: MessagingStyleInformation( threadIdentifier: threadId,
.new(name: selfName ?? "You"), );
conversationTitle: isGroup ? title : null,
groupConversation: isGroup, await controller.show(
id: id,
title: title,
body: prefixedBody,
payload: payload,
notificationDetails: .new(
android: message == null
? .new(
"general",
"General",
silent: !sound,
when: timestamp?.millisecondsSinceEpoch,
largeIcon: icon == null
? null
: FilePathAndroidBitmap(icon.path),
styleInformation: BigTextStyleInformation(body),
)
: .new(
"messages",
"Messages",
silent: !sound,
when: timestamp?.millisecondsSinceEpoch,
category: .message,
largeIcon: icon == null
? null
: FilePathAndroidBitmap(icon.path),
styleInformation: MessagingStyleInformation(
.new(key: message.self.id, name: message.self.name),
conversationTitle: message.isDm ? null : title,
groupConversation: !message.isDm,
messages: [ messages: [
...?existingStyle?.messages, ...?existingStyle?.messages,
.new( .new(
body, body,
timestamp ?? .now(), timestamp ?? .now(),
.new( .new(
name: senderName, key: message.sender.id,
icon: senderAvatar == null name: message.sender.name,
icon: message.senderAvatar == null
? null ? null
: BitmapFilePathAndroidIcon(senderAvatar.path), : BitmapFilePathAndroidIcon(
message.senderAvatar!.path,
),
), ),
), ),
], ],
), ),
), ),
iOS: darwin, iOS: darwin,
macOS: darwin, macOS: darwin,
windows: .new( windows: .new(
@ -237,16 +269,8 @@ class NotificationController
? null ? null
: .new(id: threadId, title: title, arguments: payload ?? ""), : .new(id: threadId, title: title, arguments: payload ?? ""),
), ),
); ),
);
await controller.show(
id: id,
title: title,
body: prefix == null || body == null ? body : "$prefix: $body",
notificationDetails: notificationDetails,
payload: payload,
);
}
} }
static final provider = static final provider =

View file

@ -122,17 +122,21 @@ class UnifiedPushController extends AsyncNotifier<String?> {
: notification.eventId) : notification.eventId)
.hashCode & .hashCode &
0x7fffffff, 0x7fffffff,
title: notification.roomName,
icon: icon,
payload: notification.eventRowId.toString(),
body: notification.text,
senderName: pmp?.name ?? notification.sender.name,
senderAvatar: senderAvatar,
selfName: notification.self.name,
isGroup: !notification.isDm,
sound: notification.sound,
timestamp: notification.timestamp,
threadId: notification.roomId, threadId: notification.roomId,
title: notification.roomName,
body: notification.text,
payload: notification.eventRowId.toString(),
timestamp: notification.timestamp,
icon: icon,
sound: notification.sound,
message: (
sender: pmp == null
? notification.sender
: pmp.copyWith(id: "${notification.sender.id}/${pmp.id}"),
self: notification.self,
senderAvatar: senderAvatar,
isDm: notification.isDm,
),
); );
if (isInBackground) exit(0); if (isInBackground) exit(0);

View file

@ -12,7 +12,7 @@
flutter.buildFlutterApplication { flutter.buildFlutterApplication {
pname = "nexus"; pname = "nexus";
version = "1.0.0-alpha5"; version = "1.0.0-alpha4";
inherit src; inherit src;
preBuild = '' preBuild = ''

View file

@ -1,11 +1,10 @@
name: nexus name: nexus
description: "A simple and user-friendly Matrix client" description: "A simple and user-friendly Matrix client"
version: 1.0.0-alpha5 version: 1.0.0-alpha4
publish_to: none publish_to: none
flutter: flutter:
fonts: fonts:
- family: bundled-emoji
- family: bundled-sans - family: bundled-sans
fonts: fonts:
- asset: assets/fonts/Inter.ttf - asset: assets/fonts/Inter.ttf