1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-26 06:49:22 +00:00

Use Websocket to update number of unread notifications

This commit is contained in:
Pierre HUBERT 2020-04-18 14:14:54 +02:00
parent 36f89a9a53
commit 1b13a90615
6 changed files with 104 additions and 26 deletions

View File

@ -9,6 +9,13 @@ import 'package:event_bus/event_bus.dart';
/// Main WebSocket closed /// Main WebSocket closed
class WSClosedEvent {} class WSClosedEvent {}
/// New number of notifications
class NewNumberNotifsEvent {
final int newNum;
NewNumberNotifsEvent(this.newNum);
}
class EventsHelper { class EventsHelper {
static EventBus _mgr = EventBus(); static EventBus _mgr = EventBus();

View File

@ -1,6 +1,9 @@
import 'dart:convert';
import 'package:comunic/helpers/events_helper.dart'; import 'package:comunic/helpers/events_helper.dart';
import 'package:comunic/models/api_request.dart'; import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/config.dart'; import 'package:comunic/models/config.dart';
import 'package:comunic/models/ws_message.dart';
import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/web_socket_channel.dart';
/// User web socket helper /// User web socket helper
@ -40,7 +43,10 @@ class WebSocketHelper {
_ws.stream.listen( _ws.stream.listen(
// When we got data // When we got data
(onData) => print("WS New data: $onData"), (data) {
print("WS New data: $data");
_processMessage(data.toString());
},
// Print errors on console // Print errors on console
onError: (e, stack) { onError: (e, stack) {
@ -55,4 +61,31 @@ class WebSocketHelper {
}, },
); );
} }
/// Process incoming message
static _processMessage(String msgStr) {
try {
final msg = WsMessage.fromJSON(jsonDecode(msgStr));
if (!msg.hasId)
_processUnattendedMessage(msg);
else
throw Exception("Do not know how to process attended message!");
} catch (e, stack) {
print("WS could not process message: $e");
print(stack);
}
}
/// Process an unattended message
static _processUnattendedMessage(WsMessage msg) {
switch (msg.title) {
case "number_notifs":
EventsHelper.emit(NewNumberNotifsEvent(msg.data));
break;
default:
throw Exception("Unknown message type: ${msg.title}");
}
}
} }

View File

@ -3,8 +3,8 @@
/// @author Pierre Hubert /// @author Pierre Hubert
class CountUnreadNotifications { class CountUnreadNotifications {
final int notifications; int notifications;
final int conversations; int conversations;
CountUnreadNotifications({ CountUnreadNotifications({
this.notifications, this.notifications,

View File

@ -0,0 +1,25 @@
import 'package:flutter/widgets.dart';
/// WebSocket message
///
/// @author Pierre Hubert
class WsMessage {
final String id;
final String title;
final dynamic data;
const WsMessage({
@required this.id,
@required this.title,
@required this.data,
}) : assert(id != null),
assert(title != null);
/// Construct a message from a JSON document (messages coming from the server)
static WsMessage fromJSON(final Map<dynamic, dynamic> m) {
return WsMessage(id: m["id"], title: m["title"], data: m["data"]);
}
bool get hasId => this.id != null && this.id.isNotEmpty;
}

View File

@ -1,3 +1,4 @@
import 'package:comunic/helpers/events_helper.dart';
import 'package:comunic/helpers/notifications_helper.dart'; import 'package:comunic/helpers/notifications_helper.dart';
import 'package:comunic/models/count_unread_notifications.dart'; import 'package:comunic/models/count_unread_notifications.dart';
import 'package:comunic/ui/widgets/safe_state.dart'; import 'package:comunic/ui/widgets/safe_state.dart';
@ -113,8 +114,7 @@ class ComunicAppBar extends StatefulWidget implements PreferredSizeWidget {
final OnSelectMenuAction onTap; final OnSelectMenuAction onTap;
final BarCallbackActions selectedAction; final BarCallbackActions selectedAction;
const ComunicAppBar( const ComunicAppBar({Key key, @required this.onTap, @required this.selectedAction})
{Key key, @required this.onTap, @required this.selectedAction})
: assert(onTap != null), : assert(onTap != null),
super(key: key); super(key: key);
@ -126,12 +126,17 @@ class ComunicAppBar extends StatefulWidget implements PreferredSizeWidget {
} }
class _ComunicAppBarState extends SafeState<ComunicAppBar> { class _ComunicAppBarState extends SafeState<ComunicAppBar> {
CountUnreadNotifications _unreadNotifications; var _unreadNotifications =
CountUnreadNotifications(notifications: 0, conversations: 0);
@override @override
void initState() { void initState() {
_refreshCountUnread(); _refreshCountUnread();
super.initState(); super.initState();
// Listen to notifications number update
this.listenChangeState<NewNumberNotifsEvent>(
(d) => _unreadNotifications.notifications = d.newNum);
} }
void _refreshCountUnread() async { void _refreshCountUnread() async {
@ -171,7 +176,7 @@ class _ComunicAppBarState extends SafeState<ComunicAppBar> {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: List.generate( children: List.generate(
_menuItems.length, _menuItems.length,
(i) => _MenuItemWidget( (i) => _MenuItemWidget(
item: _menuItems[i], item: _menuItems[i],
onTap: widget.onTap, onTap: widget.onTap,
isSelected: _menuItems[i].action == widget.selectedAction, isSelected: _menuItems[i].action == widget.selectedAction,
@ -212,9 +217,9 @@ class _MenuItemWidget extends StatelessWidget {
color: isSelected ? _secondaryColor() : _primaryColor(), color: isSelected ? _secondaryColor() : _primaryColor(),
child: !item.isMenu child: !item.isMenu
? InkWell( ? InkWell(
child: _buildIconContainer(), child: _buildIconContainer(),
onTap: () => onTap(item.action), onTap: () => onTap(item.action),
) )
: _buildContextMenuPopupButton(), : _buildContextMenuPopupButton(),
), ),
); );
@ -235,15 +240,15 @@ class _MenuItemWidget extends StatelessWidget {
newNotice == 0 newNotice == 0
? Container() ? Container()
: Material( : Material(
color: Colors.red, color: Colors.red,
child: Padding( child: Padding(
padding: const EdgeInsets.all(2.0), padding: const EdgeInsets.all(2.0),
child: Text(" $newNotice ", child: Text(" $newNotice ",
style: TextStyle(color: Colors.white)), style: TextStyle(color: Colors.white)),
), ),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(50.0), Radius.circular(50.0),
)), )),
Spacer(flex: 2), Spacer(flex: 2),
], ],
); );
@ -255,9 +260,9 @@ class _MenuItemWidget extends StatelessWidget {
child: _buildIconContainer(), child: _buildIconContainer(),
itemBuilder: (i) => _menuActionsItem itemBuilder: (i) => _menuActionsItem
.map((f) => PopupMenuItem( .map((f) => PopupMenuItem(
child: Text(f.label), child: Text(f.label),
value: f.action, value: f.action,
)) ))
.toList(), .toList(),
onSelected: onTap, onSelected: onTap,
); );

View File

@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
/// @author Pierre HUBERT /// @author Pierre HUBERT
abstract class SafeState<T extends StatefulWidget> extends State<T> { abstract class SafeState<T extends StatefulWidget> extends State<T> {
final _subscriptions = List<StreamSubscription>(); final _subscriptions = List<StreamSubscription>();
@override @override
@ -21,8 +20,7 @@ abstract class SafeState<T extends StatefulWidget> extends State<T> {
@override @override
void setState(fn) { void setState(fn) {
if(mounted) if (mounted) super.setState(fn);
super.setState(fn);
} }
/// Register to a new subscription /// Register to a new subscription
@ -30,4 +28,14 @@ abstract class SafeState<T extends StatefulWidget> extends State<T> {
void listen<T>(void onEvent(T event)) { void listen<T>(void onEvent(T event)) {
_subscriptions.add(EventsHelper.on<T>(onEvent)); _subscriptions.add(EventsHelper.on<T>(onEvent));
} }
/// Register to a new subscription
///
/// Callback will we called inside of setState
@protected
void listenChangeState<T>(void onEvent(T event)) {
_subscriptions.add(EventsHelper.on<T>((d) {
setState(() => onEvent(d));
}));
}
} }