1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-22 12:59:21 +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
class WSClosedEvent {}
/// New number of notifications
class NewNumberNotifsEvent {
final int newNum;
NewNumberNotifsEvent(this.newNum);
}
class EventsHelper {
static EventBus _mgr = EventBus();

View File

@ -1,6 +1,9 @@
import 'dart:convert';
import 'package:comunic/helpers/events_helper.dart';
import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/config.dart';
import 'package:comunic/models/ws_message.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
/// User web socket helper
@ -40,7 +43,10 @@ class WebSocketHelper {
_ws.stream.listen(
// When we got data
(onData) => print("WS New data: $onData"),
(data) {
print("WS New data: $data");
_processMessage(data.toString());
},
// Print errors on console
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
class CountUnreadNotifications {
final int notifications;
final int conversations;
int notifications;
int conversations;
CountUnreadNotifications({
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/models/count_unread_notifications.dart';
import 'package:comunic/ui/widgets/safe_state.dart';
@ -113,8 +114,7 @@ class ComunicAppBar extends StatefulWidget implements PreferredSizeWidget {
final OnSelectMenuAction onTap;
final BarCallbackActions selectedAction;
const ComunicAppBar(
{Key key, @required this.onTap, @required this.selectedAction})
const ComunicAppBar({Key key, @required this.onTap, @required this.selectedAction})
: assert(onTap != null),
super(key: key);
@ -126,12 +126,17 @@ class ComunicAppBar extends StatefulWidget implements PreferredSizeWidget {
}
class _ComunicAppBarState extends SafeState<ComunicAppBar> {
CountUnreadNotifications _unreadNotifications;
var _unreadNotifications =
CountUnreadNotifications(notifications: 0, conversations: 0);
@override
void initState() {
_refreshCountUnread();
super.initState();
// Listen to notifications number update
this.listenChangeState<NewNumberNotifsEvent>(
(d) => _unreadNotifications.notifications = d.newNum);
}
void _refreshCountUnread() async {
@ -171,7 +176,7 @@ class _ComunicAppBarState extends SafeState<ComunicAppBar> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: List.generate(
_menuItems.length,
(i) => _MenuItemWidget(
(i) => _MenuItemWidget(
item: _menuItems[i],
onTap: widget.onTap,
isSelected: _menuItems[i].action == widget.selectedAction,
@ -212,9 +217,9 @@ class _MenuItemWidget extends StatelessWidget {
color: isSelected ? _secondaryColor() : _primaryColor(),
child: !item.isMenu
? InkWell(
child: _buildIconContainer(),
onTap: () => onTap(item.action),
)
child: _buildIconContainer(),
onTap: () => onTap(item.action),
)
: _buildContextMenuPopupButton(),
),
);
@ -235,15 +240,15 @@ class _MenuItemWidget extends StatelessWidget {
newNotice == 0
? Container()
: Material(
color: Colors.red,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Text(" $newNotice ",
style: TextStyle(color: Colors.white)),
),
borderRadius: BorderRadius.all(
Radius.circular(50.0),
)),
color: Colors.red,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Text(" $newNotice ",
style: TextStyle(color: Colors.white)),
),
borderRadius: BorderRadius.all(
Radius.circular(50.0),
)),
Spacer(flex: 2),
],
);
@ -255,9 +260,9 @@ class _MenuItemWidget extends StatelessWidget {
child: _buildIconContainer(),
itemBuilder: (i) => _menuActionsItem
.map((f) => PopupMenuItem(
child: Text(f.label),
value: f.action,
))
child: Text(f.label),
value: f.action,
))
.toList(),
onSelected: onTap,
);

View File

@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
/// @author Pierre HUBERT
abstract class SafeState<T extends StatefulWidget> extends State<T> {
final _subscriptions = List<StreamSubscription>();
@override
@ -21,8 +20,7 @@ abstract class SafeState<T extends StatefulWidget> extends State<T> {
@override
void setState(fn) {
if(mounted)
super.setState(fn);
if (mounted) super.setState(fn);
}
/// 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)) {
_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));
}));
}
}