feat: Implement linux desktop notifications
This commit is contained in:
parent
a9ad310952
commit
75cd6f1f23
|
@ -1,5 +1,4 @@
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
|
||||||
import 'package:fluffychat/utils/string_color.dart';
|
import 'package:fluffychat/utils/string_color.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -28,31 +27,49 @@ class Avatar extends StatelessWidget {
|
||||||
Matrix.of(context).client,
|
Matrix.of(context).client,
|
||||||
width: size * MediaQuery.of(context).devicePixelRatio,
|
width: size * MediaQuery.of(context).devicePixelRatio,
|
||||||
height: size * MediaQuery.of(context).devicePixelRatio,
|
height: size * MediaQuery.of(context).devicePixelRatio,
|
||||||
method: ThumbnailMethod.scale,
|
|
||||||
);
|
);
|
||||||
final src = thumbnail;
|
final src = thumbnail;
|
||||||
var fallbackLetters = '@';
|
var fallbackLetters = '@';
|
||||||
if ((name?.length ?? 0) >= 2) {
|
if ((name?.length ?? 0) >= 2) {
|
||||||
fallbackLetters = name.substring(0, 2);
|
fallbackLetters = String.fromCharCodes(name.runes, 0, 2);
|
||||||
} else if ((name?.length ?? 0) == 1) {
|
} else if ((name?.length ?? 0) == 1) {
|
||||||
fallbackLetters = name;
|
fallbackLetters = name;
|
||||||
}
|
}
|
||||||
|
final textWidget = Center(
|
||||||
|
child: Text(
|
||||||
|
fallbackLetters,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
final noPic = mxContent == null || mxContent.toString().isEmpty;
|
final noPic = mxContent == null || mxContent.toString().isEmpty;
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: CircleAvatar(
|
child: ClipRRect(
|
||||||
radius: size / 2,
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
backgroundImage: !noPic
|
child: Container(
|
||||||
? PlatformInfos.isBetaDesktop
|
width: size,
|
||||||
? NetworkImage(src)
|
height: size,
|
||||||
: CachedNetworkImageProvider(src)
|
color: noPic
|
||||||
: null,
|
? name?.lightColor ?? Theme.of(context).secondaryHeaderColor
|
||||||
backgroundColor: noPic
|
: Theme.of(context).secondaryHeaderColor,
|
||||||
? name?.lightColor ?? Theme.of(context).secondaryHeaderColor
|
child: noPic
|
||||||
: Theme.of(context).secondaryHeaderColor,
|
? textWidget
|
||||||
child: noPic
|
: CachedNetworkImage(
|
||||||
? Text(fallbackLetters, style: TextStyle(color: Colors.white))
|
imageUrl: src,
|
||||||
: null,
|
fit: BoxFit.cover,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
placeholder: (c, s) => Stack(
|
||||||
|
children: [
|
||||||
|
Center(child: CircularProgressIndicator(strokeWidth: 2)),
|
||||||
|
textWidget,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
@ -49,17 +48,11 @@ class ContentBanner extends StatelessWidget {
|
||||||
opacity: 0.75,
|
opacity: 0.75,
|
||||||
child: !loading
|
child: !loading
|
||||||
? mxContent != null
|
? mxContent != null
|
||||||
? PlatformInfos.isBetaDesktop
|
? CachedNetworkImage(
|
||||||
? Image.network(
|
imageUrl: src,
|
||||||
src,
|
height: 300,
|
||||||
height: 300,
|
fit: BoxFit.cover,
|
||||||
fit: BoxFit.cover,
|
)
|
||||||
)
|
|
||||||
: CachedNetworkImage(
|
|
||||||
imageUrl: src,
|
|
||||||
height: 300,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
)
|
|
||||||
: Icon(defaultIcon, size: 300)
|
: Icon(defaultIcon, size: 300)
|
||||||
: Icon(defaultIcon, size: 300),
|
: Icon(defaultIcon, size: 300),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/utils/app_route.dart';
|
import 'package:fluffychat/utils/app_route.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
|
||||||
import 'package:fluffychat/views/image_view.dart';
|
import 'package:fluffychat/views/image_view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -127,17 +126,11 @@ class _ImageBubbleState extends State<ImageBubble> {
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 800,
|
height: 800,
|
||||||
method: ThumbnailMethod.scale);
|
method: ThumbnailMethod.scale);
|
||||||
renderWidget = PlatformInfos.isBetaDesktop
|
renderWidget = CachedNetworkImage(
|
||||||
? Image.network(
|
imageUrl: src,
|
||||||
src,
|
placeholder: (context, url) => generatePlaceholderWidget(),
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
)
|
);
|
||||||
: CachedNetworkImage(
|
|
||||||
imageUrl: src,
|
|
||||||
placeholder: (context, url) =>
|
|
||||||
generatePlaceholderWidget(),
|
|
||||||
fit: widget.fit,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
renderWidget = generatePlaceholderWidget();
|
renderWidget = generatePlaceholderWidget();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
@ -149,17 +148,11 @@ class InputBar extends StatelessWidget {
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
PlatformInfos.isBetaDesktop
|
CachedNetworkImage(
|
||||||
? Image.network(
|
imageUrl: url,
|
||||||
url,
|
width: size,
|
||||||
width: size,
|
height: size,
|
||||||
height: size,
|
),
|
||||||
)
|
|
||||||
: CachedNetworkImage(
|
|
||||||
imageUrl: url,
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
),
|
|
||||||
SizedBox(width: 6),
|
SizedBox(width: 6),
|
||||||
Text(suggestion['name']),
|
Text(suggestion['name']),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|
|
@ -13,6 +13,10 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:universal_html/prefer_universal/html.dart' as html;
|
import 'package:universal_html/prefer_universal/html.dart' as html;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
/*import 'package:fluffychat/views/chat.dart';
|
||||||
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
|
import 'package:dbus/dbus.dart';
|
||||||
|
import 'package:desktop_notifications/desktop_notifications.dart';*/
|
||||||
|
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../utils/app_route.dart';
|
import '../utils/app_route.dart';
|
||||||
|
@ -179,9 +183,10 @@ class MatrixState extends State<Matrix> {
|
||||||
|
|
||||||
bool webHasFocus = true;
|
bool webHasFocus = true;
|
||||||
|
|
||||||
void _showWebNotification(EventUpdate eventUpdate) async {
|
void _showLocalNotification(EventUpdate eventUpdate) async {
|
||||||
if (webHasFocus && activeRoomId == eventUpdate.roomID) return;
|
final roomId = eventUpdate.roomID;
|
||||||
final room = client.getRoomById(eventUpdate.roomID);
|
if (webHasFocus && activeRoomId == roomId) return;
|
||||||
|
final room = client.getRoomById(roomId);
|
||||||
if (room.notificationCount == 0) return;
|
if (room.notificationCount == 0) return;
|
||||||
final event = Event.fromJson(eventUpdate.content, room);
|
final event = Event.fromJson(eventUpdate.content, room);
|
||||||
final body = event.getLocalizedBody(
|
final body = event.getLocalizedBody(
|
||||||
|
@ -189,20 +194,41 @@ class MatrixState extends State<Matrix> {
|
||||||
withSenderNamePrefix:
|
withSenderNamePrefix:
|
||||||
!room.isDirectChat || room.lastEvent.senderId == client.userID,
|
!room.isDirectChat || room.lastEvent.senderId == client.userID,
|
||||||
);
|
);
|
||||||
html.AudioElement()
|
final icon = event.sender.avatarUrl?.getThumbnail(client,
|
||||||
..src = 'assets/assets/sounds/notification.wav'
|
width: 64, height: 64, method: ThumbnailMethod.crop) ??
|
||||||
..autoplay = true
|
room.avatar?.getThumbnail(client,
|
||||||
..load();
|
width: 64, height: 64, method: ThumbnailMethod.crop);
|
||||||
html.Notification(
|
if (kIsWeb) {
|
||||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
html.AudioElement()
|
||||||
body: body,
|
..src = 'assets/assets/sounds/notification.wav'
|
||||||
icon: event.sender.avatarUrl?.getThumbnail(client,
|
..autoplay = true
|
||||||
width: 64, height: 64, method: ThumbnailMethod.crop) ??
|
..load();
|
||||||
room.avatar?.getThumbnail(client,
|
html.Notification(
|
||||||
width: 64, height: 64, method: ThumbnailMethod.crop),
|
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||||
);
|
body: body,
|
||||||
|
icon: icon,
|
||||||
|
);
|
||||||
|
} else if (Platform.isLinux) {
|
||||||
|
/*var sessionBus = DBusClient.session();
|
||||||
|
var client = NotificationClient(sessionBus);
|
||||||
|
_linuxNotificationIds[roomId] = await client.notify(
|
||||||
|
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||||
|
body: body,
|
||||||
|
replacesID: _linuxNotificationIds[roomId] ?? -1,
|
||||||
|
appName: AppConfig.applicationName,
|
||||||
|
actionCallback: (_) => Navigator.of(context).pushAndRemoveUntil(
|
||||||
|
AppRoute.defaultRoute(
|
||||||
|
context,
|
||||||
|
ChatView(roomId),
|
||||||
|
),
|
||||||
|
(r) => r.isFirst),
|
||||||
|
);
|
||||||
|
await sessionBus.close();*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//final Map<String, int> _linuxNotificationIds = {};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
store = widget.store ?? Store();
|
store = widget.store ?? Store();
|
||||||
|
@ -289,7 +315,8 @@ class MatrixState extends State<Matrix> {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);
|
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);
|
||||||
onBlurSub = html.window.onBlur.listen((_) => webHasFocus = false);
|
onBlurSub = html.window.onBlur.listen((_) => webHasFocus = false);
|
||||||
|
}
|
||||||
|
if (kIsWeb || Platform.isLinux) {
|
||||||
client.onSync.stream.first.then((s) {
|
client.onSync.stream.first.then((s) {
|
||||||
html.Notification.requestPermission();
|
html.Notification.requestPermission();
|
||||||
onNotification ??= client.onEvent.stream
|
onNotification ??= client.onEvent.stream
|
||||||
|
@ -298,7 +325,7 @@ class MatrixState extends State<Matrix> {
|
||||||
[EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted]
|
[EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted]
|
||||||
.contains(e.eventType) &&
|
.contains(e.eventType) &&
|
||||||
e.content['sender'] != client.userID)
|
e.content['sender'] != client.userID)
|
||||||
.listen(_showWebNotification);
|
.listen(_showLocalNotification);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
|
||||||
|
@ -93,15 +92,10 @@ class _Reaction extends StatelessWidget {
|
||||||
content = Row(
|
content = Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
PlatformInfos.isBetaDesktop
|
CachedNetworkImage(
|
||||||
? Image.network(
|
imageUrl: src,
|
||||||
src,
|
height: fontSize,
|
||||||
height: fontSize,
|
),
|
||||||
)
|
|
||||||
: CachedNetworkImage(
|
|
||||||
imageUrl: src,
|
|
||||||
height: fontSize,
|
|
||||||
),
|
|
||||||
Container(width: 4),
|
Container(width: 4),
|
||||||
Text(count.toString(),
|
Text(count.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|
|
@ -417,19 +417,12 @@ class _EmoteImage extends StatelessWidget {
|
||||||
height: size * devicePixelRatio,
|
height: size * devicePixelRatio,
|
||||||
method: ThumbnailMethod.scale,
|
method: ThumbnailMethod.scale,
|
||||||
);
|
);
|
||||||
return PlatformInfos.isBetaDesktop
|
return CachedNetworkImage(
|
||||||
? Image.network(
|
imageUrl: url,
|
||||||
url,
|
fit: BoxFit.contain,
|
||||||
fit: BoxFit.contain,
|
width: size,
|
||||||
width: size,
|
height: size,
|
||||||
height: size,
|
);
|
||||||
)
|
|
||||||
: CachedNetworkImage(
|
|
||||||
imageUrl: url,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ packages:
|
||||||
name: cached_network_image
|
name: cached_network_image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2+1"
|
version: "2.3.3"
|
||||||
canonical_json:
|
canonical_json:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -287,7 +287,7 @@ packages:
|
||||||
name: flutter_cache_manager
|
name: flutter_cache_manager
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "2.0.0"
|
||||||
flutter_keyboard_visibility:
|
flutter_keyboard_visibility:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -33,9 +33,10 @@ dependencies:
|
||||||
file_picker_cross: ^4.2.2
|
file_picker_cross: ^4.2.2
|
||||||
image_picker: ^0.6.7+11
|
image_picker: ^0.6.7+11
|
||||||
url_launcher: ^5.7.2
|
url_launcher: ^5.7.2
|
||||||
cached_network_image: ^2.3.2+1
|
cached_network_image: ^2.3.3
|
||||||
firebase_messaging: ^7.0.2
|
firebase_messaging: ^7.0.2
|
||||||
flutter_local_notifications: ^1.4.3
|
flutter_local_notifications: ^1.4.3
|
||||||
|
# desktop_notifications: ^0.0.0-dev.4 // Currently blocked by: https://github.com/canonical/desktop_notifications.dart/issues/5
|
||||||
matrix_link_text: ^0.1.5
|
matrix_link_text: ^0.1.5
|
||||||
path_provider: ^1.5.1
|
path_provider: ^1.5.1
|
||||||
webview_flutter: ^0.3.19+9
|
webview_flutter: ^0.3.19+9
|
||||||
|
|
Loading…
Reference in a new issue