✨ Notification indicator
This commit is contained in:
parent
068ddcdcdc
commit
7f58710c6f
@ -279,6 +279,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> {
|
|||||||
await ws.tryConnect();
|
await ws.tryConnect();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final notify = context.read<NotificationProvider>();
|
final notify = context.read<NotificationProvider>();
|
||||||
|
notify.listen();
|
||||||
await notify.registerPushNotifications();
|
await notify.registerPushNotifications();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
@ -8,14 +8,18 @@ import 'package:flutter_udid/flutter_udid.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
import 'package:surface/providers/websocket.dart';
|
||||||
|
import 'package:surface/types/notification.dart';
|
||||||
|
|
||||||
class NotificationProvider extends ChangeNotifier {
|
class NotificationProvider extends ChangeNotifier {
|
||||||
late final SnNetworkProvider _sn;
|
late final SnNetworkProvider _sn;
|
||||||
late final UserProvider _ua;
|
late final UserProvider _ua;
|
||||||
|
late final WebSocketProvider _ws;
|
||||||
|
|
||||||
NotificationProvider(BuildContext context) {
|
NotificationProvider(BuildContext context) {
|
||||||
_sn = context.read<SnNetworkProvider>();
|
_sn = context.read<SnNetworkProvider>();
|
||||||
_ua = context.read<UserProvider>();
|
_ua = context.read<UserProvider>();
|
||||||
|
_ws = context.read<WebSocketProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> registerPushNotifications() async {
|
Future<void> registerPushNotifications() async {
|
||||||
@ -62,4 +66,21 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<SnNotification> notifications = List.empty(growable: true);
|
||||||
|
|
||||||
|
void listen() {
|
||||||
|
_ws.stream.stream.listen((event) {
|
||||||
|
if (event.method == 'notifications.new') {
|
||||||
|
final notification = SnNotification.fromJson(event.payload!);
|
||||||
|
notifications.add(notification);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
notifications.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import 'package:surface/widgets/navigation/app_background.dart';
|
|||||||
import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
|
import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
|
||||||
import 'package:surface/widgets/navigation/app_drawer_navigation.dart';
|
import 'package:surface/widgets/navigation/app_drawer_navigation.dart';
|
||||||
import 'package:surface/widgets/navigation/app_rail_navigation.dart';
|
import 'package:surface/widgets/navigation/app_rail_navigation.dart';
|
||||||
|
import 'package:surface/widgets/notify_indicator.dart';
|
||||||
|
|
||||||
final globalRootScaffoldKey = GlobalKey<ScaffoldState>();
|
final globalRootScaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
@ -131,16 +132,20 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!Platform.isMacOS)
|
if (!Platform.isMacOS)
|
||||||
MoveWindow(
|
Expanded(
|
||||||
child: WindowTitleBarBox(
|
child: WindowTitleBarBox(
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
children: [
|
||||||
|
Expanded(child: MoveWindow()),
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
MinimizeWindowButton(colors: windowButtonColor),
|
MinimizeWindowButton(colors: windowButtonColor),
|
||||||
MaximizeWindowButton(colors: windowButtonColor),
|
MaximizeWindowButton(colors: windowButtonColor),
|
||||||
CloseWindowButton(colors: windowButtonColor),
|
CloseWindowButton(colors: windowButtonColor),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -149,7 +154,8 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
Expanded(child: innerWidget),
|
Expanded(child: innerWidget),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Positioned(top: safeTop > 0 ? safeTop : 16, right: 8, child: ConnectionIndicator()),
|
Positioned(top: safeTop > 0 ? safeTop : 16, right: 8, child: NotifyIndicator()),
|
||||||
|
Positioned(top: safeTop > 0 ? safeTop : 16, left: 8, child: ConnectionIndicator()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: !isExpandedDrawer ? AppNavigationDrawer() : null,
|
drawer: !isExpandedDrawer ? AppNavigationDrawer() : null,
|
||||||
|
58
lib/widgets/notify_indicator.dart
Normal file
58
lib/widgets/notify_indicator.dart
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/notification.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
|
||||||
|
class NotifyIndicator extends StatelessWidget {
|
||||||
|
const NotifyIndicator({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
|
final nty = context.watch<NotificationProvider>();
|
||||||
|
|
||||||
|
return ListenableBuilder(
|
||||||
|
listenable: nty,
|
||||||
|
builder: (context, _) {
|
||||||
|
return GestureDetector(
|
||||||
|
child: Material(
|
||||||
|
elevation: 2,
|
||||||
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
child: ua.isAuthorized
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
nty.notifications.lastOrNull?.title ??
|
||||||
|
'notificationUnreadCount'.plural(nty.notifications.length),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
if (nty.notifications.lastOrNull?.body != null)
|
||||||
|
Text(
|
||||||
|
nty.notifications.lastOrNull!.body,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).padding(left: 4),
|
||||||
|
const Gap(8),
|
||||||
|
const Icon(Symbols.notifications_unread, size: 18),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 8, vertical: 4)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
).opacity(nty.notifications.isNotEmpty && ua.isAuthorized ? 1 : 0, animate: true).animate(
|
||||||
|
const Duration(milliseconds: 300),
|
||||||
|
Curves.easeInOut,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
nty.clear();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user