feat: implement UnifiedPush on Linux
This commit is contained in:
parent
f0aa15843b
commit
44c627f607
12 changed files with 326 additions and 58 deletions
|
|
@ -17,11 +17,170 @@ import 'users.dart';
|
|||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Integration Test', () {
|
||||
setUpAll(() {
|
||||
// this random dialog popping up is super hard to cover in tests
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'chat.fluffy.show_no_google': false,
|
||||
group(
|
||||
'Integration Test',
|
||||
() {
|
||||
setUpAll(
|
||||
() async {
|
||||
// this random dialog popping up is super hard to cover in tests
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'chat.fluffy.show_no_google': false,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Start app, login and logout',
|
||||
(WidgetTester tester) async {
|
||||
app.main([]);
|
||||
await tester.ensureAppStartedHomescreen();
|
||||
await tester.ensureLoggedOut();
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Login again',
|
||||
(WidgetTester tester) async {
|
||||
app.main([]);
|
||||
await tester.ensureAppStartedHomescreen();
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Start chat and send message',
|
||||
(WidgetTester tester) async {
|
||||
app.main([]);
|
||||
await tester.ensureAppStartedHomescreen();
|
||||
await tester.waitFor(find.byType(TextField));
|
||||
await tester.enterText(find.byType(TextField), Users.user2.name);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('Chats').first,
|
||||
500,
|
||||
scrollable: find
|
||||
.descendant(
|
||||
of: find.byType(ChatListViewBody),
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Chats'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.waitFor(find.byType(SearchTitle));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.scrollUntilVisible(
|
||||
find.text(Users.user2.name).first,
|
||||
500,
|
||||
scrollable: find
|
||||
.descendant(
|
||||
of: find.byType(ChatListViewBody),
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text(Users.user2.name).first);
|
||||
|
||||
try {
|
||||
await tester.waitFor(
|
||||
find.byType(ChatView),
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
} catch (_) {
|
||||
// in case the homeserver sends the username as search result
|
||||
if (find.byIcon(Icons.send_outlined).evaluate().isNotEmpty) {
|
||||
await tester.tap(find.byIcon(Icons.send_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
}
|
||||
|
||||
await tester.waitFor(find.byType(ChatView));
|
||||
await tester.enterText(find.byType(TextField).last, 'Test');
|
||||
await tester.pumpAndSettle();
|
||||
try {
|
||||
await tester.waitFor(find.byIcon(Icons.send_outlined));
|
||||
await tester.tap(find.byIcon(Icons.send_outlined));
|
||||
} catch (_) {
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
await tester.waitFor(find.text('Test'));
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('Spaces', (tester) async {
|
||||
app.main([]);
|
||||
await tester.ensureAppStartedHomescreen();
|
||||
|
||||
await tester.waitFor(find.byTooltip('Show menu'));
|
||||
await tester.tap(find.byTooltip('Show menu'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.waitFor(find.byIcon(Icons.workspaces_outlined));
|
||||
await tester.tap(find.byIcon(Icons.workspaces_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.waitFor(find.byType(TextField));
|
||||
await tester.enterText(find.byType(TextField).last, 'Test Space');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.waitFor(find.text('Invite contact'));
|
||||
|
||||
await tester.tap(find.text('Invite contact'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.waitFor(
|
||||
find.descendant(
|
||||
of: find.byType(InvitationSelectionView),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
);
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(InvitationSelectionView),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
Users.user2.name,
|
||||
);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 250));
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 1000));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
.descendant(
|
||||
of: find.descendant(
|
||||
of: find.byType(InvitationSelectionView),
|
||||
matching: find.byType(ListTile),
|
||||
),
|
||||
matching: find.text(Users.user2.name),
|
||||
)
|
||||
.last,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.waitFor(find.maybeUppercaseText('Yes'));
|
||||
await tester.tap(find.maybeUppercaseText('Yes'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byTooltip('Back'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.waitFor(find.text('Load 2 more participants'));
|
||||
await tester.tap(find.text('Load 2 more participants'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text(Users.user2.name), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ abstract class AppConfig {
|
|||
static const String deepLinkPrefix = 'im.fluffychat://chat/';
|
||||
static const String schemePrefix = 'matrix:';
|
||||
static const String pushNotificationsChannelId = 'fluffychat_push';
|
||||
static const String pushNotificationsAppId = 'chat.fluffy.fluffychat';
|
||||
static const String pushNotificationsAppId = 'chat.fluffy.fluffychat'; // except for Linux!
|
||||
static const double borderRadius = 18.0;
|
||||
static const double spaceBorderRadius = 11.0;
|
||||
static const double columnWidth = 360.0;
|
||||
|
|
@ -32,6 +32,7 @@ abstract class AppConfig {
|
|||
'https://fluffy.chat/faq/#how_do_i_get_stickers';
|
||||
static const String appId = 'im.fluffychat.FluffyChat';
|
||||
static const String appOpenUrlScheme = 'chat.fluffy';
|
||||
static const String appIdFlatpak = 'im.fluffychat.Fluffychat';
|
||||
|
||||
static const String sourceCodeUrl =
|
||||
'https://github.com/krille-chan/fluffychat';
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import 'widgets/fluffy_chat_app.dart';
|
|||
|
||||
ReceivePort? mainIsolateReceivePort;
|
||||
|
||||
void main() async {
|
||||
void main(List<String> args) async {
|
||||
if (PlatformInfos.isAndroid) {
|
||||
final port = mainIsolateReceivePort = ReceivePort();
|
||||
IsolateNameServer.removePortNameMapping(AppConfig.mainIsolatePortName);
|
||||
|
|
@ -55,18 +55,29 @@ void main() async {
|
|||
|
||||
// If the app starts in detached mode, we assume that it is in
|
||||
// background fetch mode for processing push notifications. This is
|
||||
// currently only supported on Android.
|
||||
if (PlatformInfos.isAndroid &&
|
||||
AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState) {
|
||||
// currently only supported on Android and Linux.
|
||||
final backgroundMode = (() {
|
||||
if (PlatformInfos.isAndroid) {
|
||||
return WidgetsBinding.instance.lifecycleState ==
|
||||
AppLifecycleState.detached;
|
||||
}
|
||||
if (PlatformInfos.isLinux) {
|
||||
return args.contains('--unifiedpush-bg');
|
||||
}
|
||||
|
||||
return false;
|
||||
})();
|
||||
|
||||
if (backgroundMode) {
|
||||
// Do not send online presences when app is in background fetch mode.
|
||||
for (final client in clients) {
|
||||
client.backgroundSync = false;
|
||||
client.syncPresence = PresenceType.offline;
|
||||
}
|
||||
|
||||
// In the background fetch mode we do not want to waste ressources with
|
||||
// In the background fetch mode we do not want to waste resources with
|
||||
// starting the Flutter engine but process incoming push notifications.
|
||||
BackgroundPush.clientOnly(clients.first);
|
||||
BackgroundPush.clientOnly(clients.first, backgroundMode);
|
||||
// To start the flutter engine afterwards we add an custom observer.
|
||||
WidgetsBinding.instance.addObserver(AppStarter(clients, store));
|
||||
Logs().i(
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ import 'package:flutter_new_badger/flutter_new_badger.dart';
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:unifiedpush/unifiedpush.dart';
|
||||
import 'package:unifiedpush_platform_interface/unifiedpush_platform_interface.dart';
|
||||
import 'package:unifiedpush_storage_shared_preferences/storage.dart';
|
||||
import 'package:unifiedpush_ui/unifiedpush_ui.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
|
|
@ -71,8 +73,9 @@ class BackgroundPush {
|
|||
DateTime? lastReceivedPush;
|
||||
|
||||
bool upAction = false;
|
||||
Future<void>? _initializing;
|
||||
|
||||
Future<void> _init() async {
|
||||
Future<void> _init(bool isBackground) async {
|
||||
//<GOOGLE_SERVICES>firebaseEnabled = true;
|
||||
try {
|
||||
mainIsolateReceivePort?.listen((message) async {
|
||||
|
|
@ -111,6 +114,7 @@ class BackgroundPush {
|
|||
settings: const InitializationSettings(
|
||||
android: AndroidInitializationSettings('notifications_icon'),
|
||||
iOS: DarwinInitializationSettings(),
|
||||
linux: LinuxInitializationSettings(defaultActionName: "Open chat"),
|
||||
),
|
||||
onDidReceiveNotificationResponse: (response) => notificationTap(
|
||||
response,
|
||||
|
|
@ -132,12 +136,21 @@ class BackgroundPush {
|
|||
//<GOOGLE_SERVICES> flutterLocalNotificationsPlugin: _flutterLocalNotificationsPlugin,
|
||||
//<GOOGLE_SERVICES> ),
|
||||
//<GOOGLE_SERVICES>);
|
||||
if (Platform.isAndroid) {
|
||||
if (PlatformInfos.canUseUnifiedPush) {
|
||||
final linuxOptions = LinuxOptions(
|
||||
// NOTE: we don't use pushNotificationsAppId because it's different from
|
||||
// the actual app id, (im.fluffychat.Fluffychat), and appId has the wrong casing
|
||||
dbusName: AppConfig.appIdFlatpak,
|
||||
storage: UnifiedPushStorageSharedPreferences(),
|
||||
background: isBackground,
|
||||
);
|
||||
|
||||
await UnifiedPush.initialize(
|
||||
onNewEndpoint: _newUpEndpoint,
|
||||
onRegistrationFailed: (_, i) => _upUnregistered(i),
|
||||
onUnregistered: _upUnregistered,
|
||||
onMessage: _onUpMessage,
|
||||
linuxOptions: linuxOptions,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
|
|
@ -145,19 +158,20 @@ class BackgroundPush {
|
|||
}
|
||||
}
|
||||
|
||||
BackgroundPush._(this.client) {
|
||||
_init();
|
||||
BackgroundPush._(this.client, bool isBackground) {
|
||||
_initializing ??= _init(isBackground);
|
||||
}
|
||||
|
||||
factory BackgroundPush.clientOnly(Client client) {
|
||||
return _instance ??= BackgroundPush._(client);
|
||||
factory BackgroundPush.clientOnly(Client client, bool isBackground) {
|
||||
return _instance ??= BackgroundPush._(client, isBackground);
|
||||
}
|
||||
|
||||
factory BackgroundPush(
|
||||
MatrixState matrix, {
|
||||
isBackground = false,
|
||||
final void Function(String errorMsg, {Uri? link})? onFcmError,
|
||||
}) {
|
||||
final instance = BackgroundPush.clientOnly(matrix.client);
|
||||
final instance = BackgroundPush.clientOnly(matrix.client, isBackground);
|
||||
instance.matrix = matrix;
|
||||
// ignore: prefer_initializing_formals
|
||||
instance.onFcmError = onFcmError;
|
||||
|
|
@ -208,7 +222,7 @@ class BackgroundPush {
|
|||
[];
|
||||
var setNewPusher = false;
|
||||
// Just the plain app id, we add the .data_message suffix later
|
||||
var appId = AppConfig.pushNotificationsAppId;
|
||||
var appId = PlatformInfos.isLinux ? AppConfig.appIdFlatpak : AppConfig.pushNotificationsAppId;
|
||||
// we need the deviceAppId to remove potential legacy UP pusher
|
||||
var deviceAppId = '$appId.${client.deviceID}';
|
||||
// appId may only be up to 64 chars as per spec
|
||||
|
|
@ -292,7 +306,7 @@ class BackgroundPush {
|
|||
Future<void> setupPush() async {
|
||||
Logs().d('SetupPush');
|
||||
if (client.onLoginStateChanged.value != LoginState.loggedIn ||
|
||||
!PlatformInfos.isMobile ||
|
||||
!(PlatformInfos.isMobile || PlatformInfos.canUseUnifiedPush) ||
|
||||
matrix == null) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -301,33 +315,39 @@ class BackgroundPush {
|
|||
if (upAction) {
|
||||
return;
|
||||
}
|
||||
await _initializing;
|
||||
|
||||
if (!PlatformInfos.isIOS &&
|
||||
(await UnifiedPush.getDistributors()).isNotEmpty) {
|
||||
Logs().d((await UnifiedPush.getDistributors()).join(','));
|
||||
await setupUp();
|
||||
} else {
|
||||
await setupFirebase();
|
||||
}
|
||||
|
||||
// ignore: unawaited_futures
|
||||
_flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails().then((
|
||||
details,
|
||||
) {
|
||||
if (details == null ||
|
||||
!details.didNotificationLaunchApp ||
|
||||
_wentToRoomOnStartup) {
|
||||
return;
|
||||
}
|
||||
_wentToRoomOnStartup = true;
|
||||
final response = details.notificationResponse;
|
||||
if (response != null) {
|
||||
notificationTap(
|
||||
response,
|
||||
client: client,
|
||||
router: FluffyChatApp.router,
|
||||
l10n: l10n,
|
||||
);
|
||||
}
|
||||
});
|
||||
// NOTE: unsupported on Linux (oof)
|
||||
if (!PlatformInfos.isLinux) {
|
||||
// ignore: unawaited_futures
|
||||
_flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails().then((
|
||||
details,
|
||||
) {
|
||||
if (details == null ||
|
||||
!details.didNotificationLaunchApp ||
|
||||
_wentToRoomOnStartup) {
|
||||
return;
|
||||
}
|
||||
_wentToRoomOnStartup = true;
|
||||
final response = details.notificationResponse;
|
||||
if (response != null) {
|
||||
notificationTap(
|
||||
response,
|
||||
client: client,
|
||||
router: FluffyChatApp.router,
|
||||
l10n: l10n,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _noFcmWarning() async {
|
||||
|
|
@ -437,7 +457,7 @@ class BackgroundPush {
|
|||
}
|
||||
|
||||
Future<void> _onUpMessage(PushMessage pushMessage, String i) async {
|
||||
Logs().wtf('Push Notification from UP received', pushMessage);
|
||||
Logs().i('Push Notification from UP received', pushMessage);
|
||||
final message = pushMessage.content;
|
||||
upAction = true;
|
||||
final data = Map<String, dynamic>.from(
|
||||
|
|
@ -451,8 +471,8 @@ class BackgroundPush {
|
|||
l10n: l10n,
|
||||
activeRoomId: matrix?.activeRoomId,
|
||||
flutterLocalNotificationsPlugin: _flutterLocalNotificationsPlugin,
|
||||
useNotificationActions:
|
||||
false, // Buggy with UP: https://codeberg.org/UnifiedPush/flutter-connector/issues/34
|
||||
useNotificationActions: !PlatformInfos
|
||||
.isAndroid, // Buggy with UP: https://codeberg.org/UnifiedPush/flutter-connector/issues/34
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ abstract class ClientManager {
|
|||
settings: const InitializationSettings(
|
||||
android: AndroidInitializationSettings('notifications_icon'),
|
||||
iOS: DarwinInitializationSettings(),
|
||||
linux: LinuxInitializationSettings(defaultActionName: "Open chat"),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ abstract class PlatformInfos {
|
|||
static bool get isDesktop => isLinux || isWindows || isMacOS;
|
||||
|
||||
static bool get usesTouchscreen => !isMobile;
|
||||
static bool get canUseUnifiedPush => isAndroid || isLinux;
|
||||
|
||||
static bool get supportsVideoPlayer =>
|
||||
!PlatformInfos.isWindows && !PlatformInfos.isLinux;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:flutter_shortcuts_new/flutter_shortcuts_new.dart';
|
||||
import 'package:image/image.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
|
|
@ -252,6 +253,29 @@ Future<void> _tryPushHelper(
|
|||
>()
|
||||
?.createNotificationChannel(roomsChannel);
|
||||
|
||||
LinuxNotificationIcon? linuxIcon;
|
||||
if (PlatformInfos.isLinux) {
|
||||
if (roomAvatarFile != null) {
|
||||
final image = decodeImage(roomAvatarFile);
|
||||
if (image != null) {
|
||||
final realData = image.getBytes(order: ChannelOrder.rgba);
|
||||
linuxIcon = ByteDataLinuxIcon(
|
||||
LinuxRawIconData(
|
||||
data: realData,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
rowStride: image.rowStride,
|
||||
bitsPerSample: image.bitsPerChannel * image.numChannels,
|
||||
channels: image.numChannels,
|
||||
hasAlpha: true,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
linuxIcon = ThemeLinuxIcon("fluffychat");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final androidPlatformChannelSpecifics = AndroidNotificationDetails(
|
||||
AppConfig.pushNotificationsChannelId,
|
||||
l10n.incomingMessages,
|
||||
|
|
@ -305,9 +329,32 @@ Future<void> _tryPushHelper(
|
|||
],
|
||||
);
|
||||
const iOSPlatformChannelSpecifics = DarwinNotificationDetails();
|
||||
final linuxPlatformChannelSpecifics = LinuxNotificationDetails(
|
||||
icon: linuxIcon,
|
||||
sound: ThemeLinuxSound("message-new-instant"),
|
||||
category: LinuxNotificationCategory.imReceived,
|
||||
urgency: LinuxNotificationUrgency.normal,
|
||||
resident: false, // remove upon interaction
|
||||
suppressSound: false, // we don't play a sound ourselves
|
||||
actions: event.type == EventTypes.RoomMember || !useNotificationActions
|
||||
? []
|
||||
: <LinuxNotificationAction>[
|
||||
LinuxNotificationAction(
|
||||
key: FluffyChatNotificationActions.reply.name,
|
||||
label: l10n.reply,
|
||||
),
|
||||
LinuxNotificationAction(
|
||||
key: FluffyChatNotificationActions.markAsRead.name,
|
||||
label: l10n.markAsRead,
|
||||
),
|
||||
],
|
||||
// TODO: can we do KNotificationReplyAction?
|
||||
// Though, would be better if this were a standard.
|
||||
);
|
||||
final platformChannelSpecifics = NotificationDetails(
|
||||
android: androidPlatformChannelSpecifics,
|
||||
iOS: iOSPlatformChannelSpecifics,
|
||||
linux: linuxPlatformChannelSpecifics,
|
||||
);
|
||||
|
||||
final title = event.room.getLocalizedDisplayname(MatrixLocals(l10n));
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:collection/collection.dart';
|
|||
import 'package:desktop_notifications/desktop_notifications.dart';
|
||||
import 'package:image/image.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:unifiedpush/unifiedpush.dart';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
|
|
@ -82,9 +83,17 @@ extension LocalNotificationsExtension on MatrixState {
|
|||
icon: thumbnailUri?.toString(),
|
||||
tag: event.room.id,
|
||||
);
|
||||
} else if (Platform.isLinux) {
|
||||
} else if (Platform.isLinux &&
|
||||
(await UnifiedPush.getDistributor() != null)) {
|
||||
final avatarUrl = event.room.avatar;
|
||||
final hints = [NotificationHint.soundName('message-new-instant')];
|
||||
final hints = [
|
||||
NotificationHint.soundName('message-new-instant'),
|
||||
NotificationHint.category(
|
||||
event.type == EventTypes.Message
|
||||
? NotificationCategory.imReceived()
|
||||
: NotificationCategory.im(),
|
||||
),
|
||||
];
|
||||
|
||||
if (avatarUrl != null) {
|
||||
const size = notificationAvatarDimension;
|
||||
|
|
@ -119,16 +128,18 @@ extension LocalNotificationsExtension on MatrixState {
|
|||
replacesId: linuxNotificationIds[roomId] ?? 0,
|
||||
appName: AppSettings.applicationName.value,
|
||||
appIcon: 'fluffychat',
|
||||
actions: [
|
||||
NotificationAction(
|
||||
DesktopNotificationActions.openChat.name,
|
||||
L10n.of(context).openChat,
|
||||
),
|
||||
NotificationAction(
|
||||
DesktopNotificationActions.seen.name,
|
||||
L10n.of(context).markAsRead,
|
||||
),
|
||||
],
|
||||
actions: event.type == EventTypes.RoomMember
|
||||
? []
|
||||
: [
|
||||
NotificationAction(
|
||||
DesktopNotificationActions.openChat.name,
|
||||
L10n.of(context).openChat,
|
||||
),
|
||||
NotificationAction(
|
||||
DesktopNotificationActions.seen.name,
|
||||
L10n.of(context).markAsRead,
|
||||
),
|
||||
],
|
||||
hints: hints,
|
||||
);
|
||||
notification.action.then((actionStr) {
|
||||
|
|
@ -149,6 +160,7 @@ extension LocalNotificationsExtension on MatrixState {
|
|||
case DesktopNotificationActions.openChat:
|
||||
setActiveClient(event.room.client);
|
||||
|
||||
// TODO: raise window via token
|
||||
FluffyChatApp.router.go('/rooms/${event.room.id}');
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,9 +305,10 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
_registerSubs(c.clientName);
|
||||
}
|
||||
|
||||
if (PlatformInfos.isMobile) {
|
||||
if (PlatformInfos.isMobile || PlatformInfos.isLinux) {
|
||||
backgroundPush = BackgroundPush(
|
||||
this,
|
||||
isBackground: false,
|
||||
onFcmError: (errorMsg, {Uri? link}) async {
|
||||
final result = await showOkCancelAlertDialog(
|
||||
context:
|
||||
|
|
|
|||
|
|
@ -73,6 +73,11 @@ static void my_application_activate(GApplication* application) {
|
|||
gtk_widget_show(GTK_WIDGET(window));
|
||||
gtk_widget_show(GTK_WIDGET(view));
|
||||
gtk_widget_grab_focus(GTK_WIDGET(view));
|
||||
|
||||
if (g_getenv("FLUTTER_HEADLESS")) {
|
||||
printf("Hiding window...\n");
|
||||
gtk_widget_hide(GTK_WIDGET(window));
|
||||
}
|
||||
}
|
||||
|
||||
// Implements GApplication::local_command_line.
|
||||
|
|
|
|||
10
pubspec.lock
10
pubspec.lock
|
|
@ -1962,7 +1962,7 @@ packages:
|
|||
source: hosted
|
||||
version: "1.0.0"
|
||||
unifiedpush_platform_interface:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: unifiedpush_platform_interface
|
||||
sha256: "83372bc8d794b8b12ef6993b518d7be907dcfc2191bdf6de0ece5c4445d89880"
|
||||
|
|
@ -1977,6 +1977,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
unifiedpush_storage_shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: unifiedpush_storage_shared_preferences
|
||||
sha256: eda9c52bac0058f81e0d9c65e33eedc12c5901d2e68ad47fdf68175ddf00a3b4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
unifiedpush_ui:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ dependencies:
|
|||
sqlcipher_flutter_libs: ^0.6.8
|
||||
swipe_to_action: ^0.3.0
|
||||
unifiedpush: ^6.2.0
|
||||
unifiedpush_platform_interface: ^4.0.0
|
||||
unifiedpush_storage_shared_preferences: ^1.0.0
|
||||
unifiedpush_ui: ^0.2.0
|
||||
universal_html: ^2.3.0
|
||||
url_launcher: ^6.3.2
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue