mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-25 22:39:22 +00:00
Use Websocket to update number of unread notifications
This commit is contained in:
parent
36f89a9a53
commit
1b13a90615
@ -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();
|
||||||
|
|
||||||
|
@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
25
lib/models/ws_message.dart
Normal file
25
lib/models/ws_message.dart
Normal 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;
|
||||||
|
}
|
@ -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,
|
||||||
);
|
);
|
||||||
|
@ -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));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user