use xdg_desktop_portal over flutter_local_notifications on linux

This commit is contained in:
Henry Hiles 2026-09-21 15:43:01 -04:00
commit fea39e4191
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
6 changed files with 132 additions and 76 deletions

View file

@ -1,10 +1,13 @@
import "dart:io"; import "dart:io";
import "package:flutter/foundation.dart";
import "package:material_ui/material_ui.dart"; import "package:material_ui/material_ui.dart";
import "package:nexus/controllers/portal.dart";
import "package:nexus/main.dart"; 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/pages/notifications.dart"; import "package:nexus/pages/notifications.dart";
import "package:xdg_desktop_portal/xdg_desktop_portal.dart";
class NotificationController class NotificationController
extends AsyncNotifier<FlutterLocalNotificationsPlugin> { extends AsyncNotifier<FlutterLocalNotificationsPlugin> {
@ -12,6 +15,7 @@ class NotificationController
Future<FlutterLocalNotificationsPlugin> build() async { Future<FlutterLocalNotificationsPlugin> build() async {
final notifications = FlutterLocalNotificationsPlugin(); final notifications = FlutterLocalNotificationsPlugin();
if (!Platform.isLinux) {
final darwin = DarwinInitializationSettings(); final darwin = DarwinInitializationSettings();
await notifications.initialize( await notifications.initialize(
@ -24,7 +28,6 @@ class NotificationController
android: .new("ic_launcher_foreground"), android: .new("ic_launcher_foreground"),
iOS: darwin, iOS: darwin,
macOS: darwin, macOS: darwin,
linux: .new(defaultActionName: "Open"),
), ),
onDidReceiveNotificationResponse: (details) { onDidReceiveNotificationResponse: (details) {
if (details.payload case final eventId?) { if (details.payload case final eventId?) {
@ -41,11 +44,48 @@ class NotificationController
} }
}, },
); );
}
if (Platform.isLinux) {
final portal = await ref.watch(PortalController.provider.future);
portal.notification.actionInvoked.listen((event) {
if (event.action != "app.event") {
return;
}
if (navigatorKey.currentContext case final context?) {
if (context.mounted) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => NotificationsPage(
highlightedEventId: event.id,
defaultToAllNotifications: true,
),
),
);
}
}
});
}
ref.onDispose(() {
// The portal client owns its D-Bus connection.
if (Platform.isLinux) {
ref.read(PortalController.provider.future).then((portal) {
portal.close();
});
}
});
return notifications; return notifications;
} }
Future<bool> requestPermissions() async { Future<bool> requestPermissions() async {
if (Platform.isLinux) {
return true;
}
final controller = await future; final controller = await future;
if (Platform.isIOS) { if (Platform.isIOS) {
@ -82,24 +122,33 @@ class NotificationController
String? payload, String? payload,
}) async { }) async {
debugPrint("Sending notification for $id"); debugPrint("Sending notification for $id");
if (Platform.isLinux) {
final portal = await ref.read(PortalController.provider.future);
await portal.notification.addNotification(
id.toString(),
title: title,
body: body,
icon: icon == null ? null : XdgNotificationIconFile(icon.path),
defaultAction: "app.event",
);
} else {
final notificationDetails = NotificationDetails( final notificationDetails = NotificationDetails(
android: .new( android: .new(
"messages", "messages",
"Messages", "Messages",
largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path), largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path),
), ),
linux: .new( // TODO: See if icons can be added to iOS, macOS, and Windows
category: LinuxNotificationCategory.imReceived, // notifications (#68)
defaultActionName: "app.event",
icon: icon == null ? null : FilePathLinuxIcon(icon.path),
),
// TODO: See if icons can be added to iOS, macOS, and Windows notifications (#68)
iOS: .new(), iOS: .new(),
macOS: .new(), macOS: .new(),
windows: .new(), windows: .new(),
); );
final controller = await future; final controller = await future;
await controller.show( await controller.show(
id: id, id: id,
title: title, title: title,
@ -108,6 +157,7 @@ class NotificationController
payload: payload, payload: payload,
); );
} }
}
static final provider = static final provider =
AsyncNotifierProvider< AsyncNotifierProvider<

View file

@ -0,0 +1,17 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:xdg_desktop_portal/xdg_desktop_portal.dart";
class PortalController extends AsyncNotifier<XdgDesktopPortalClient> {
@override
Future<XdgDesktopPortalClient> build() async {
final portal = XdgDesktopPortalClient();
await portal.registerApplication("nexus.federated.nexus");
return portal;
}
static final provider =
AsyncNotifierProvider<PortalController, XdgDesktopPortalClient>(
PortalController.new,
);
}

View file

@ -8,6 +8,4 @@ Terminal=false
Type=Application Type=Application
Categories=Chat;Network;InstantMessaging; Categories=Chat;Network;InstantMessaging;
MimeType=x-scheme-handler/nexus.federated.nexus; MimeType=x-scheme-handler/nexus.federated.nexus;
StartupNotify=true
DBusActivatable=true DBusActivatable=true
X-GNOME-UsesNotifications=true

View file

@ -1,3 +1,3 @@
[D-BUS Service] [D-BUS Service]
Name=nexus.federated.nexus Name=nexus.federated.nexus
Exec=/usr/bin/env FLUTTER_HEADLESS=1 nexus Exec=/usr/bin/env nexus

View file

@ -495,23 +495,21 @@ packages:
source: hosted source: hosted
version: "22.3.0" version: "22.3.0"
flutter_local_notifications_linux: flutter_local_notifications_linux:
dependency: "direct main" dependency: transitive
description: description:
path: flutter_local_notifications_linux name: flutter_local_notifications_linux
ref: "quad/feat/xdg-portal-notifs" sha256: "9ca97e63776f29ab1b955725c09999fc2c150523269db150c39274f2a43c5a8b"
resolved-ref: d853ff4997f6dd2b9ecdc53d37fb337cebe63863 url: "https://pub.dev"
url: "https://github.com/Henry-Hiles/flutter_local_notifications" source: hosted
source: git version: "8.0.1"
version: "9.0.0-dev.1"
flutter_local_notifications_platform_interface: flutter_local_notifications_platform_interface:
dependency: "direct overridden" dependency: transitive
description: description:
path: flutter_local_notifications_platform_interface name: flutter_local_notifications_platform_interface
ref: "quad/feat/xdg-portal-notifs" sha256: "43c3761d916c9bd3d5c7ebbc44d82f4990329840c0c5d62ad5260cc1b5d399bd"
resolved-ref: d853ff4997f6dd2b9ecdc53d37fb337cebe63863 url: "https://pub.dev"
url: "https://github.com/Henry-Hiles/flutter_local_notifications" source: hosted
source: git version: "12.2.0"
version: "13.0.0-dev.1"
flutter_local_notifications_web: flutter_local_notifications_web:
dependency: transitive dependency: transitive
description: description:
@ -1745,12 +1743,13 @@ packages:
source: hosted source: hosted
version: "0.5.2" version: "0.5.2"
xdg_desktop_portal: xdg_desktop_portal:
dependency: transitive dependency: "direct main"
description: description:
name: xdg_desktop_portal path: "."
sha256: "4fae74b6ed63a449d4c796a74265cebdd912c390336dedc06d705142f3b939d7" ref: HEAD
url: "https://pub.dev" resolved-ref: "6d070b677f908b01065b4dce79ddc9a208daadbf"
source: hosted url: "https://github.com/Henry-Hiles/xdg_desktop_portal.dart"
source: git
version: "0.1.14" version: "0.1.14"
xdg_directories: xdg_directories:
dependency: "direct main" dependency: "direct main"

View file

@ -35,16 +35,6 @@ dependency_overrides:
git: git:
url: https://github.com/appelladev/linkify url: https://github.com/appelladev/linkify
ref: fix/consecutive-periods-loose-url ref: fix/consecutive-periods-loose-url
flutter_local_notifications_linux:
git:
url: https://github.com/Henry-Hiles/flutter_local_notifications
ref: quad/feat/xdg-portal-notifs
path: flutter_local_notifications_linux
flutter_local_notifications_platform_interface:
git:
url: https://github.com/Henry-Hiles/flutter_local_notifications
ref: quad/feat/xdg-portal-notifs
path: flutter_local_notifications_platform_interface
dependencies: dependencies:
flutter: flutter:
@ -99,8 +89,10 @@ dependencies:
unifiedpush: 6.2.0 unifiedpush: 6.2.0
unifiedpush_storage_shared_preferences: 1.0.0 unifiedpush_storage_shared_preferences: 1.0.0
flutter_local_notifications: 22.3.0 flutter_local_notifications: 22.3.0
flutter_local_notifications_linux: 8.0.1
material_emoji_picker: 1.1.1 material_emoji_picker: 1.1.1
xdg_desktop_portal:
git:
url: https://github.com/Henry-Hiles/xdg_desktop_portal.dart
dev_dependencies: dev_dependencies:
build_runner: 2.16.1 build_runner: 2.16.1