Change notifications to be only triggered by websocket stream
This commit is contained in:
parent
3eaf6ea9c6
commit
b1a945a501
3 changed files with 78 additions and 34 deletions
|
@ -42,10 +42,12 @@ class MessagingClient {
|
||||||
final ApiClient _apiClient;
|
final ApiClient _apiClient;
|
||||||
final Map<String, Friend> _friendsCache = {};
|
final Map<String, Friend> _friendsCache = {};
|
||||||
final Map<String, MessageCache> _messageCache = {};
|
final Map<String, MessageCache> _messageCache = {};
|
||||||
final Map<String, Function> _updateListeners = {};
|
final Map<String, Function> _messageUpdateListeners = {};
|
||||||
|
final Map<String, List<Message>> _unreads = {};
|
||||||
final Logger _logger = Logger("NeosHub");
|
final Logger _logger = Logger("NeosHub");
|
||||||
final Workmanager _workmanager = Workmanager();
|
final Workmanager _workmanager = Workmanager();
|
||||||
final NotificationClient _notificationClient;
|
final NotificationClient _notificationClient;
|
||||||
|
Function? _unreadsUpdateListener;
|
||||||
WebSocket? _wsChannel;
|
WebSocket? _wsChannel;
|
||||||
bool _isConnecting = false;
|
bool _isConnecting = false;
|
||||||
|
|
||||||
|
@ -66,6 +68,46 @@ class MessagingClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateAllUnreads(List<Message> messages) {
|
||||||
|
_unreads.clear();
|
||||||
|
for (final msg in messages) {
|
||||||
|
if (msg.senderId != _apiClient.userId) {
|
||||||
|
final value = _unreads[msg.senderId];
|
||||||
|
if (value == null) {
|
||||||
|
_unreads[msg.senderId] = [msg];
|
||||||
|
} else {
|
||||||
|
value.add(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addUnread(Message message) {
|
||||||
|
var messages = _unreads[message.senderId];
|
||||||
|
if (messages == null) {
|
||||||
|
messages = [message];
|
||||||
|
_unreads[message.senderId] = messages;
|
||||||
|
} else {
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
messages.sort();
|
||||||
|
_notificationClient.showUnreadMessagesNotification(messages.reversed);
|
||||||
|
notifyUnreadListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearUnreadsForFriend(Friend friend) {
|
||||||
|
_unreads[friend.id]?.clear();
|
||||||
|
notifyUnreadListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Message> getUnreadsForFriend(Friend friend) => _unreads[friend.id] ?? [];
|
||||||
|
|
||||||
|
bool friendHasUnreads(Friend friend) => _unreads.containsKey(friend.id);
|
||||||
|
|
||||||
|
bool messageIsUnread(Message message) {
|
||||||
|
return _unreads[message.senderId]?.any((element) => element.id == message.id) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
Friend? getAsFriend(String userId) => _friendsCache[userId];
|
Friend? getAsFriend(String userId) => _friendsCache[userId];
|
||||||
|
|
||||||
Future<MessageCache> getMessageCache(String userId) async {
|
Future<MessageCache> getMessageCache(String userId) async {
|
||||||
|
@ -153,9 +195,13 @@ class MessagingClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerListener(String userId, Function function) => _updateListeners[userId] = function;
|
void registerMessageListener(String userId, Function function) => _messageUpdateListeners[userId] = function;
|
||||||
void unregisterListener(String userId) => _updateListeners.remove(userId);
|
void unregisterMessageListener(String userId) => _messageUpdateListeners.remove(userId);
|
||||||
void notifyListener(String userId) => _updateListeners[userId]?.call();
|
void notifyMessageListener(String userId) => _messageUpdateListeners[userId]?.call();
|
||||||
|
|
||||||
|
void registerUnreadListener(Function function) => _unreadsUpdateListener = function;
|
||||||
|
void unregisterUnreadListener() => _unreadsUpdateListener = null;
|
||||||
|
void notifyUnreadListener() => _unreadsUpdateListener?.call();
|
||||||
|
|
||||||
void _handleEvent(event) {
|
void _handleEvent(event) {
|
||||||
final body = jsonDecode((event.toString().replaceAll(eofChar, "")));
|
final body = jsonDecode((event.toString().replaceAll(eofChar, "")));
|
||||||
|
@ -186,17 +232,17 @@ class MessagingClient {
|
||||||
final message = Message.fromMap(msg, withState: MessageState.sent);
|
final message = Message.fromMap(msg, withState: MessageState.sent);
|
||||||
final cache = await getMessageCache(message.recipientId);
|
final cache = await getMessageCache(message.recipientId);
|
||||||
cache.addMessage(message);
|
cache.addMessage(message);
|
||||||
notifyListener(message.recipientId);
|
notifyMessageListener(message.recipientId);
|
||||||
break;
|
break;
|
||||||
case EventTarget.receiveMessage:
|
case EventTarget.receiveMessage:
|
||||||
final msg = args[0];
|
final msg = args[0];
|
||||||
final message = Message.fromMap(msg);
|
final message = Message.fromMap(msg);
|
||||||
final cache = await getMessageCache(message.senderId);
|
final cache = await getMessageCache(message.senderId);
|
||||||
cache.addMessage(message);
|
cache.addMessage(message);
|
||||||
if (!_updateListeners.containsKey(message.senderId)) {
|
if (!_messageUpdateListeners.containsKey(message.senderId)) {
|
||||||
_notificationClient.showUnreadMessagesNotification([message]);
|
addUnread(message);
|
||||||
}
|
}
|
||||||
notifyListener(message.senderId);
|
notifyMessageListener(message.senderId);
|
||||||
break;
|
break;
|
||||||
case EventTarget.messagesRead:
|
case EventTarget.messagesRead:
|
||||||
final messageIds = args[0]["ids"] as List;
|
final messageIds = args[0]["ids"] as List;
|
||||||
|
@ -205,7 +251,7 @@ class MessagingClient {
|
||||||
for (var id in messageIds) {
|
for (var id in messageIds) {
|
||||||
cache.setMessageState(id, MessageState.read);
|
cache.setMessageState(id, MessageState.read);
|
||||||
}
|
}
|
||||||
notifyListener(recipientId);
|
notifyMessageListener(recipientId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,7 +268,7 @@ class MessagingClient {
|
||||||
_sendData(data);
|
_sendData(data);
|
||||||
final cache = await getMessageCache(message.recipientId);
|
final cache = await getMessageCache(message.recipientId);
|
||||||
cache.messages.add(message);
|
cache.messages.add(message);
|
||||||
notifyListener(message.recipientId);
|
notifyMessageListener(message.recipientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void markMessagesRead(MarkReadBatch batch) {
|
void markMessagesRead(MarkReadBatch batch) {
|
||||||
|
@ -260,13 +306,12 @@ class NotificationClient {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> showUnreadMessagesNotification(List<Message> messages) async {
|
Future<void> showUnreadMessagesNotification(Iterable<Message> messages) async {
|
||||||
if (messages.isEmpty) return;
|
if (messages.isEmpty) return;
|
||||||
|
|
||||||
final bySender = groupBy(messages, (p0) => p0.senderId);
|
final bySender = groupBy(messages, (p0) => p0.senderId);
|
||||||
|
|
||||||
for (final entry in bySender.entries) {
|
for (final entry in bySender.entries) {
|
||||||
|
|
||||||
final uname = entry.key.stripUid();
|
final uname = entry.key.stripUid();
|
||||||
await _notifier.show(
|
await _notifier.show(
|
||||||
uname.hashCode,
|
uname.hashCode,
|
||||||
|
|
|
@ -31,7 +31,6 @@ class FriendsList extends StatefulWidget {
|
||||||
class _FriendsListState extends State<FriendsList> {
|
class _FriendsListState extends State<FriendsList> {
|
||||||
static const Duration _autoRefreshDuration = Duration(seconds: 90);
|
static const Duration _autoRefreshDuration = Duration(seconds: 90);
|
||||||
static const Duration _refreshTimeoutDuration = Duration(seconds: 30);
|
static const Duration _refreshTimeoutDuration = Duration(seconds: 30);
|
||||||
final _unreads = <String, List<Message>>{};
|
|
||||||
Future<List<Friend>>? _friendsFuture;
|
Future<List<Friend>>? _friendsFuture;
|
||||||
ClientHolder? _clientHolder;
|
ClientHolder? _clientHolder;
|
||||||
Timer? _autoRefresh;
|
Timer? _autoRefresh;
|
||||||
|
@ -51,6 +50,14 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
final clientHolder = ClientHolder.of(context);
|
final clientHolder = ClientHolder.of(context);
|
||||||
if (_clientHolder != clientHolder) {
|
if (_clientHolder != clientHolder) {
|
||||||
_clientHolder = clientHolder;
|
_clientHolder = clientHolder;
|
||||||
|
final mClient = _clientHolder!.messagingClient;
|
||||||
|
mClient.registerUnreadListener(() {
|
||||||
|
if (context.mounted) {
|
||||||
|
setState(() {});
|
||||||
|
} else {
|
||||||
|
mClient.unregisterUnreadListener();
|
||||||
|
}
|
||||||
|
});
|
||||||
_refreshFriendsList();
|
_refreshFriendsList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,22 +66,14 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
if (_refreshTimeout?.isActive == true) return;
|
if (_refreshTimeout?.isActive == true) return;
|
||||||
_friendsFuture = FriendApi.getFriendsList(_clientHolder!.apiClient).then((Iterable<Friend> value) async {
|
_friendsFuture = FriendApi.getFriendsList(_clientHolder!.apiClient).then((Iterable<Friend> value) async {
|
||||||
final unreadMessages = await MessageApi.getUserMessages(_clientHolder!.apiClient, unreadOnly: true);
|
final unreadMessages = await MessageApi.getUserMessages(_clientHolder!.apiClient, unreadOnly: true);
|
||||||
_unreads.clear();
|
final mClient = _clientHolder?.messagingClient;
|
||||||
for (final msg in unreadMessages) {
|
if (mClient == null) return [];
|
||||||
if (msg.senderId != _clientHolder!.apiClient.userId) {
|
mClient.updateAllUnreads(unreadMessages.toList());
|
||||||
final value = _unreads[msg.senderId];
|
|
||||||
if (value == null) {
|
|
||||||
_unreads[msg.senderId] = [msg];
|
|
||||||
} else {
|
|
||||||
value.add(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final friends = value.toList()
|
final friends = value.toList()
|
||||||
..sort((a, b) {
|
..sort((a, b) {
|
||||||
var aVal = _unreads.containsKey(a.id) ? -3 : 0;
|
var aVal = mClient.friendHasUnreads(a) ? -3 : 0;
|
||||||
var bVal = _unreads.containsKey(b.id) ? -3 : 0;
|
var bVal = mClient.friendHasUnreads(b) ? -3 : 0;
|
||||||
|
|
||||||
aVal -= a.userStatus.lastStatusChange.compareTo(b.userStatus.lastStatusChange);
|
aVal -= a.userStatus.lastStatusChange.compareTo(b.userStatus.lastStatusChange);
|
||||||
aVal += a.userStatus.onlineStatus.compareTo(b.userStatus.onlineStatus) * 2;
|
aVal += a.userStatus.onlineStatus.compareTo(b.userStatus.onlineStatus) * 2;
|
||||||
|
@ -164,21 +163,21 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
itemCount: friends.length,
|
itemCount: friends.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final friend = friends[index];
|
final friend = friends[index];
|
||||||
final unread = _unreads[friend.id] ?? [];
|
final unreads = _clientHolder?.messagingClient.getUnreadsForFriend(friend) ?? [];
|
||||||
return FriendListTile(
|
return FriendListTile(
|
||||||
friend: friend,
|
friend: friend,
|
||||||
unreads: unread.length,
|
unreads: unreads.length,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (unread.isNotEmpty) {
|
if (unreads.isNotEmpty) {
|
||||||
final readBatch = MarkReadBatch(
|
final readBatch = MarkReadBatch(
|
||||||
senderId: _clientHolder!.apiClient.userId,
|
senderId: _clientHolder!.apiClient.userId,
|
||||||
ids: unread.map((e) => e.id).toList(),
|
ids: unreads.map((e) => e.id).toList(),
|
||||||
readTime: DateTime.now(),
|
readTime: DateTime.now(),
|
||||||
);
|
);
|
||||||
_clientHolder!.messagingClient.markMessagesRead(readBatch);
|
_clientHolder!.messagingClient.markMessagesRead(readBatch);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
unread.clear();
|
unreads.clear();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -51,18 +51,18 @@ class _MessagesListState extends State<MessagesList> {
|
||||||
.whenComplete(() => _messageCacheFutureComplete = true);
|
.whenComplete(() => _messageCacheFutureComplete = true);
|
||||||
final mClient = _clientHolder?.messagingClient;
|
final mClient = _clientHolder?.messagingClient;
|
||||||
final id = widget.friend.id;
|
final id = widget.friend.id;
|
||||||
mClient?.registerListener(id, () {
|
mClient?.registerMessageListener(id, () {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
} else {
|
} else {
|
||||||
mClient.unregisterListener(id);
|
mClient.unregisterMessageListener(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_clientHolder?.messagingClient.unregisterListener(widget.friend.id);
|
_clientHolder?.messagingClient.unregisterMessageListener(widget.friend.id);
|
||||||
_messageTextController.dispose();
|
_messageTextController.dispose();
|
||||||
_sessionListScrollController.dispose();
|
_sessionListScrollController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
Loading…
Reference in a new issue