Notifications page
Doesn't yet allow for jumping to events
This commit is contained in:
parent
9e7fcdbef7
commit
23e01c7a0d
12 changed files with 296 additions and 139 deletions
|
|
@ -25,6 +25,7 @@ import "package:nexus/models/paginate.dart";
|
|||
import "package:nexus/models/requests/deregister_pusher.dart";
|
||||
import "package:nexus/models/requests/download_media.dart";
|
||||
import "package:nexus/models/requests/get_event.dart";
|
||||
import "package:nexus/models/requests/get_mentions.dart";
|
||||
import "package:nexus/models/requests/get_related_events.dart";
|
||||
import "package:nexus/models/requests/get_room_state.dart";
|
||||
import "package:nexus/models/requests/join_room.dart";
|
||||
|
|
@ -272,6 +273,12 @@ class ClientController extends AsyncNotifier<int> {
|
|||
return .new(response?.map((event) => .fromJson(event)));
|
||||
}
|
||||
|
||||
Future<IList<Event>> getMentions(GetMentionsRequest request) async => .new(
|
||||
((await _sendCommand("get_mentions", request.toJson())) as List).map(
|
||||
(event) => .fromJson(event),
|
||||
),
|
||||
);
|
||||
|
||||
Future<Event?> getEvent(GetEventRequest request) async {
|
||||
final json = await _sendCommand("get_event", request.toJson());
|
||||
return json == null ? null : .fromJson(json);
|
||||
|
|
|
|||
120
lib/controllers/notification.dart
Normal file
120
lib/controllers/notification.dart
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import "dart:io";
|
||||
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:nexus/main.dart";
|
||||
import "package:flutter_local_notifications/flutter_local_notifications.dart";
|
||||
// ignore: implementation_imports
|
||||
import "package:flutter_local_notifications_linux/src/model/hint.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/pages/notifications.dart";
|
||||
|
||||
class NotificationController
|
||||
extends AsyncNotifier<FlutterLocalNotificationsPlugin> {
|
||||
@override
|
||||
Future<FlutterLocalNotificationsPlugin> build() async {
|
||||
final notifications = FlutterLocalNotificationsPlugin();
|
||||
|
||||
final darwin = DarwinInitializationSettings();
|
||||
|
||||
await notifications.initialize(
|
||||
settings: .new(
|
||||
windows: .new(
|
||||
appName: "Nexus",
|
||||
appUserModelId: "nexus.federated.nexus",
|
||||
guid: "dde78daf-130f-4e46-a80a-e31deeab45d7",
|
||||
),
|
||||
android: .new("ic_launcher_foreground"),
|
||||
iOS: darwin,
|
||||
macOS: darwin,
|
||||
linux: .new(defaultActionName: "Open"),
|
||||
),
|
||||
onDidReceiveNotificationResponse: (details) {
|
||||
if (details.payload case final eventId?) {
|
||||
if (navigatorKey.currentContext case final context?) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => NotificationsPage(eventId: eventId),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return notifications;
|
||||
}
|
||||
|
||||
Future<bool> requestPermissions() async {
|
||||
final controller = await future;
|
||||
|
||||
if (Platform.isIOS) {
|
||||
return await controller
|
||||
.resolvePlatformSpecificImplementation<
|
||||
IOSFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.requestPermissions(alert: true, badge: true, sound: true) ??
|
||||
true;
|
||||
} else if (Platform.isMacOS) {
|
||||
return await controller
|
||||
.resolvePlatformSpecificImplementation<
|
||||
MacOSFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.requestPermissions(alert: true, badge: true, sound: true) ??
|
||||
true;
|
||||
} else if (Platform.isAndroid) {
|
||||
return await controller
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.requestNotificationsPermission() ??
|
||||
true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> send({
|
||||
required int id,
|
||||
required String title,
|
||||
String? body,
|
||||
File? icon,
|
||||
String? payload,
|
||||
}) async {
|
||||
final notificationDetails = NotificationDetails(
|
||||
android: .new(
|
||||
"messages",
|
||||
"Messages",
|
||||
largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path),
|
||||
),
|
||||
linux: .new(
|
||||
customHints: icon == null
|
||||
? null
|
||||
: [
|
||||
.new(
|
||||
name: "image-path",
|
||||
value: LinuxHintStringValue(icon.path),
|
||||
),
|
||||
],
|
||||
),
|
||||
// TODO: See if icons can be added to iOS, macOS, and Windows notifications (#68)
|
||||
iOS: .new(),
|
||||
macOS: .new(),
|
||||
windows: .new(),
|
||||
);
|
||||
|
||||
final controller = await future;
|
||||
await controller.show(
|
||||
id: id,
|
||||
title: title,
|
||||
body: body,
|
||||
notificationDetails: notificationDetails,
|
||||
payload: payload,
|
||||
);
|
||||
}
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider<
|
||||
NotificationController,
|
||||
FlutterLocalNotificationsPlugin
|
||||
>(NotificationController.new);
|
||||
}
|
||||
|
|
@ -1,120 +1,59 @@
|
|||
import "dart:io";
|
||||
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:nexus/main.dart";
|
||||
import "package:flutter_local_notifications/flutter_local_notifications.dart";
|
||||
// ignore: implementation_imports
|
||||
import "package:flutter_local_notifications_linux/src/model/hint.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/pages/notifications.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/models/event.dart";
|
||||
|
||||
typedef NotificationsRequest = (UnreadType? unreadType, String? roomId);
|
||||
|
||||
class NotificationsController([final NotificationsRequest? request])
|
||||
extends AsyncNotifier<IList<Event>> {
|
||||
static const limit = 20;
|
||||
|
||||
class NotificationsController
|
||||
extends AsyncNotifier<FlutterLocalNotificationsPlugin> {
|
||||
@override
|
||||
Future<FlutterLocalNotificationsPlugin> build() async {
|
||||
final notifications = FlutterLocalNotificationsPlugin();
|
||||
Future<IList<Event>> build() async {
|
||||
final client = ref.watch(ClientController.provider.notifier);
|
||||
|
||||
final darwin = DarwinInitializationSettings();
|
||||
final (unreadType, roomId) = request ?? (null, null);
|
||||
|
||||
await notifications.initialize(
|
||||
settings: .new(
|
||||
windows: .new(
|
||||
appName: "Nexus",
|
||||
appUserModelId: "nexus.federated.nexus",
|
||||
guid: "dde78daf-130f-4e46-a80a-e31deeab45d7",
|
||||
final mentions = await client.getMentions(
|
||||
.new(
|
||||
maxTimestamp: .now(),
|
||||
unreadType: unreadType ?? .highlight,
|
||||
limit: limit,
|
||||
roomId: roomId,
|
||||
),
|
||||
);
|
||||
|
||||
return mentions;
|
||||
}
|
||||
|
||||
Future<void> loadOlder() async {
|
||||
final currentNotifications = await future;
|
||||
state = .loading();
|
||||
state = await .guard(() async {
|
||||
final lastTs = currentNotifications.lastOrNull?.timestamp;
|
||||
if (lastTs == null) return const .empty();
|
||||
|
||||
final client = ref.watch(ClientController.provider.notifier);
|
||||
final (unreadType, roomId) = request ?? (null, null);
|
||||
|
||||
final newNotifications = await client.getMentions(
|
||||
.new(
|
||||
maxTimestamp: lastTs,
|
||||
unreadType: unreadType ?? .highlight,
|
||||
limit: limit,
|
||||
roomId: roomId,
|
||||
),
|
||||
android: .new("ic_launcher_foreground"),
|
||||
iOS: darwin,
|
||||
macOS: darwin,
|
||||
linux: .new(defaultActionName: "Open"),
|
||||
),
|
||||
onDidReceiveNotificationResponse: (details) {
|
||||
if (details.payload case final eventId?) {
|
||||
if (navigatorKey.currentContext case final context?) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => NotificationsPage(eventId: eventId),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
);
|
||||
|
||||
return notifications;
|
||||
return currentNotifications.addAll(newNotifications);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> requestPermissions() async {
|
||||
final controller = await future;
|
||||
|
||||
if (Platform.isIOS) {
|
||||
return await controller
|
||||
.resolvePlatformSpecificImplementation<
|
||||
IOSFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.requestPermissions(alert: true, badge: true, sound: true) ??
|
||||
true;
|
||||
} else if (Platform.isMacOS) {
|
||||
return await controller
|
||||
.resolvePlatformSpecificImplementation<
|
||||
MacOSFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.requestPermissions(alert: true, badge: true, sound: true) ??
|
||||
true;
|
||||
} else if (Platform.isAndroid) {
|
||||
return await controller
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.requestNotificationsPermission() ??
|
||||
true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> send({
|
||||
required int id,
|
||||
required String title,
|
||||
String? body,
|
||||
File? icon,
|
||||
String? payload,
|
||||
}) async {
|
||||
final notificationDetails = NotificationDetails(
|
||||
android: .new(
|
||||
"messages",
|
||||
"Messages",
|
||||
largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path),
|
||||
),
|
||||
linux: .new(
|
||||
customHints: icon == null
|
||||
? null
|
||||
: [
|
||||
.new(
|
||||
name: "image-path",
|
||||
value: LinuxHintStringValue(icon.path),
|
||||
),
|
||||
],
|
||||
),
|
||||
// TODO: See if icons can be added to iOS, macOS, and Windows notifications (#68)
|
||||
iOS: .new(),
|
||||
macOS: .new(),
|
||||
windows: .new(),
|
||||
);
|
||||
|
||||
final controller = await future;
|
||||
await controller.show(
|
||||
id: id,
|
||||
title: title,
|
||||
body: body,
|
||||
notificationDetails: notificationDetails,
|
||||
payload: payload,
|
||||
);
|
||||
}
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider<
|
||||
static final provider = AsyncNotifierProvider.family
|
||||
.autoDispose<
|
||||
NotificationsController,
|
||||
FlutterLocalNotificationsPlugin
|
||||
IList<Event>,
|
||||
NotificationsRequest?
|
||||
>(NotificationsController.new);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class RoomChatController(final String roomId)
|
|||
);
|
||||
|
||||
Future<bool> loadOlder() async {
|
||||
state = AsyncLoading();
|
||||
state = .loading();
|
||||
final timelineKeys = ref
|
||||
.read(RoomsController.provider.select((value) => value[roomId]))
|
||||
?.timeline
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import "package:m3e_buttons/m3e_buttons.dart";
|
|||
import "package:nexus/controllers/account_data.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/client_state.dart";
|
||||
import "package:nexus/controllers/notifications.dart";
|
||||
import "package:nexus/controllers/notification.dart";
|
||||
import "package:nexus/controllers/settings.dart";
|
||||
import "package:nexus/controllers/unified_push.dart";
|
||||
import "package:nexus/controllers/spec_versions.dart";
|
||||
|
|
@ -143,7 +143,7 @@ class SettingsSectionsController
|
|||
if (value) {
|
||||
if (await ref
|
||||
.watch(
|
||||
NotificationsController
|
||||
NotificationController
|
||||
.provider
|
||||
.notifier,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import "package:flutter/foundation.dart";
|
|||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:intl/intl.dart";
|
||||
import "package:nexus/controllers/key.dart";
|
||||
import "package:nexus/controllers/notifications.dart";
|
||||
import "package:nexus/controllers/notification.dart";
|
||||
import "package:nexus/controllers/push_key.dart";
|
||||
import "package:nexus/controllers/rooms.dart";
|
||||
import "package:nexus/main.dart";
|
||||
|
|
@ -61,7 +61,7 @@ class UnifiedPushController extends AsyncNotifier<bool> {
|
|||
);
|
||||
|
||||
if (event == null ||
|
||||
event.unreadType?.shouldNotify != true ||
|
||||
event.unreadType?.shouldNotify() != true ||
|
||||
(!isInBackground &&
|
||||
await windowManager.isFocused().onError((_, _) => false) &&
|
||||
ref.watch(KeyController.provider(KeyController.roomKey)) ==
|
||||
|
|
@ -81,7 +81,7 @@ class UnifiedPushController extends AsyncNotifier<bool> {
|
|||
.downloadMedia(.new(mxc: avatar, isAvatar: true));
|
||||
|
||||
await ref
|
||||
.read(NotificationsController.provider.notifier)
|
||||
.read(NotificationController.provider.notifier)
|
||||
.send(
|
||||
id: event.eventId.hashCode & 0x7fffffff,
|
||||
title: room?.metadata?.name ?? "New Event",
|
||||
|
|
|
|||
Loading…
Reference in a new issue