From 69d69d0aa4666b958f778b9870a28a2ed289345a Mon Sep 17 00:00:00 2001 From: Nutcake Date: Wed, 17 May 2023 13:54:30 +0200 Subject: [PATCH] Remove unused code and restructure messaging client a little --- lib/clients/messaging_client.dart | 233 ++++++++++++-------------- lib/main.dart | 19 +-- lib/models/personal_profile.dart | 2 - lib/widgets/friends/friends_list.dart | 2 +- 4 files changed, 107 insertions(+), 149 deletions(-) diff --git a/lib/clients/messaging_client.dart b/lib/clients/messaging_client.dart index b6187d4..96868a6 100644 --- a/lib/clients/messaging_client.dart +++ b/lib/clients/messaging_client.dart @@ -1,21 +1,20 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; + +import 'package:flutter/widgets.dart'; +import 'package:hive_flutter/hive_flutter.dart'; +import 'package:http/http.dart' as http; +import 'package:logging/logging.dart'; + import 'package:contacts_plus_plus/apis/friend_api.dart'; import 'package:contacts_plus_plus/apis/message_api.dart'; import 'package:contacts_plus_plus/apis/user_api.dart'; import 'package:contacts_plus_plus/clients/notification_client.dart'; -import 'package:contacts_plus_plus/models/authentication_data.dart'; import 'package:contacts_plus_plus/models/friend.dart'; -import 'package:flutter/widgets.dart'; -import 'package:hive_flutter/hive_flutter.dart'; -import 'package:http/http.dart' as http; - import 'package:contacts_plus_plus/clients/api_client.dart'; import 'package:contacts_plus_plus/config.dart'; import 'package:contacts_plus_plus/models/message.dart'; -import 'package:logging/logging.dart'; -import 'package:workmanager/workmanager.dart'; enum EventType { unknown, @@ -43,35 +42,30 @@ enum EventTarget { } class MessagingClient extends ChangeNotifier { - static const String eofChar = ""; - static const String _negotiationPacket = "{\"protocol\":\"json\", \"version\":1}$eofChar"; + static const String _eofChar = ""; + static const String _negotiationPacket = "{\"protocol\":\"json\", \"version\":1}$_eofChar"; static const List _reconnectTimeoutsSeconds = [0, 5, 10, 20, 60]; - static const String taskName = "periodic-unread-check"; static const Duration _autoRefreshDuration = Duration(seconds: 10); static const Duration _unreadSafeguardDuration = Duration(seconds: 120); static const String _messageBoxKey = "message-box"; static const String _lastUpdateKey = "__last-update-time"; + final ApiClient _apiClient; final List _sortedFriendsCache = []; // Keep a sorted copy so as to not have to sort during build() final Map _messageCache = {}; final Map> _unreads = {}; final Logger _logger = Logger("NeosHub"); - final Workmanager _workmanager = Workmanager(); final NotificationClient _notificationClient; + Friend? selectedFriend; Timer? _notifyOnlineTimer; Timer? _autoRefresh; - Timer? _refreshTimeout; Timer? _unreadSafeguard; int _attempts = 0; WebSocket? _wsChannel; bool _isConnecting = false; String? _initStatus; - String? get initStatus => _initStatus; - - bool get websocketConnected => _wsChannel != null; - MessagingClient({required ApiClient apiClient, required NotificationClient notificationClient}) : _apiClient = apiClient, _notificationClient = notificationClient { Hive.openBox(_messageBoxKey).then((box) async { @@ -79,7 +73,7 @@ class MessagingClient extends ChangeNotifier { await refreshFriendsListWithErrorHandler(); await _refreshUnreads(); }); - startWebsocket(); + _startWebsocket(); _notifyOnlineTimer = Timer.periodic(const Duration(seconds: 60), (timer) async { // We should probably let the MessagingClient handle the entire state of USerStatus instead of mirroring like this // but I don't feel like implementing that right now. @@ -90,30 +84,29 @@ class MessagingClient extends ChangeNotifier { @override void dispose() { _autoRefresh?.cancel(); - _refreshTimeout?.cancel(); _notifyOnlineTimer?.cancel(); _wsChannel?.close(); super.dispose(); } - void _sendData(data) { - if (_wsChannel == null) throw "Neos Hub is not connected"; - _wsChannel!.add(jsonEncode(data)+eofChar); - } + String? get initStatus => _initStatus; - void resetStatus() { - _initStatus = null; - notifyListeners(); - } + bool get websocketConnected => _wsChannel != null; + + List get cachedFriends => _sortedFriendsCache; + + List getUnreadsForFriend(Friend friend) => _unreads[friend.id] ?? []; + + bool friendHasUnreads(Friend friend) => _unreads.containsKey(friend.id); + + bool messageIsUnread(Message message) => _unreads[message.senderId]?.any((element) => element.id == message.id) ?? false; + + Friend? getAsFriend(String userId) => Friend.fromMapOrNull(Hive.box(_messageBoxKey).get(userId)); + + MessageCache? getUserMessageCache(String userId) => _messageCache[userId]; + + MessageCache _createUserMessageCache(String userId) => MessageCache(apiClient: _apiClient, userId: userId); - Future _refreshUnreads() async { - _unreadSafeguard?.cancel(); - try { - final unreadMessages = await MessageApi.getUserMessages(_apiClient, unreadOnly: true); - updateAllUnreads(unreadMessages.toList()); - } catch (_) {} - _unreadSafeguard = Timer(_unreadSafeguardDuration, _refreshUnreads); - } Future refreshFriendsListWithErrorHandler () async { try { @@ -138,29 +131,32 @@ class MessagingClient extends ChangeNotifier { notifyListeners(); } - void _sortFriendsCache() { - _sortedFriendsCache.sort((a, b) { - var aVal = friendHasUnreads(a) ? -3 : 0; - var bVal = friendHasUnreads(b) ? -3 : 0; - - aVal -= a.latestMessageTime.compareTo(b.latestMessageTime); - aVal += a.userStatus.onlineStatus.compareTo(b.userStatus.onlineStatus) * 2; - return aVal.compareTo(bVal); - }); + void sendMessage(Message message) async { + final msgBody = message.toMap(); + final data = { + "type": EventType.message.index, + "target": "SendMessage", + "arguments": [ + msgBody + ], + }; + _sendData(data); + final cache = getUserMessageCache(message.recipientId) ?? _createUserMessageCache(message.recipientId); + cache.messages.add(message); + notifyListeners(); } - void updateAllUnreads(List 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 markMessagesRead(MarkReadBatch batch) { + final msgBody = batch.toMap(); + final data = { + "type": EventType.message.index, + "target": "MarkMessagesRead", + "arguments": [ + msgBody + ], + }; + _sendData(data); + clearUnreadsForUser(batch.senderId); } void addUnread(Message message) { @@ -177,25 +173,25 @@ class MessagingClient extends ChangeNotifier { notifyListeners(); } + void updateAllUnreads(List 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 clearUnreadsForUser(String userId) { _unreads[userId]?.clear(); notifyListeners(); } - List 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) => Friend.fromMapOrNull(Hive.box(_messageBoxKey).get(userId)); - - List get cachedFriends => _sortedFriendsCache; - - MessageCache _createUserMessageCache(String userId) => MessageCache(apiClient: _apiClient, userId: userId); - void deleteUserMessageCache(String userId) { _messageCache.remove(userId); } @@ -207,6 +203,39 @@ class MessagingClient extends ChangeNotifier { notifyListeners(); } + Future updateFriendStatus(String userId) async { + final friend = getAsFriend(userId); + if (friend == null) return; + final newStatus = await UserApi.getUserStatus(_apiClient, userId: userId); + await _updateFriend(friend.copyWith(userStatus: newStatus)); + notifyListeners(); + } + + void resetInitStatus() { + _initStatus = null; + notifyListeners(); + } + + Future _refreshUnreads() async { + _unreadSafeguard?.cancel(); + try { + final unreadMessages = await MessageApi.getUserMessages(_apiClient, unreadOnly: true); + updateAllUnreads(unreadMessages.toList()); + } catch (_) {} + _unreadSafeguard = Timer(_unreadSafeguardDuration, _refreshUnreads); + } + + void _sortFriendsCache() { + _sortedFriendsCache.sort((a, b) { + var aVal = friendHasUnreads(a) ? -3 : 0; + var bVal = friendHasUnreads(b) ? -3 : 0; + + aVal -= a.latestMessageTime.compareTo(b.latestMessageTime); + aVal += a.userStatus.onlineStatus.compareTo(b.userStatus.onlineStatus) * 2; + return aVal.compareTo(bVal); + }); + } + Future _updateFriend(Friend friend) async { final box = Hive.box(_messageBoxKey); box.put(friend.id, friend.toMap()); @@ -223,44 +252,15 @@ class MessagingClient extends ChangeNotifier { _sortFriendsCache(); } - Future updateFriendStatus(String userId) async { - final friend = getAsFriend(userId); - if (friend == null) return; - final newStatus = await UserApi.getUserStatus(_apiClient, userId: userId); - await _updateFriend(friend.copyWith(userStatus: newStatus)); - notifyListeners(); - } - - MessageCache? getUserMessageCache(String userId) => _messageCache[userId]; - - static Future backgroundCheckUnreads(Map? inputData) async { - if (inputData == null) return; - final auth = AuthenticationData.fromMap(inputData); - final unreads = await MessageApi.getUserMessages(ApiClient(authenticationData: auth), unreadOnly: true); - for (var message in unreads) { - throw UnimplementedError(); - } - } - - Future _updateNotificationTask(int minuteInterval) async { - final auth = _apiClient.authenticationData; - if (!auth.isAuthenticated) throw "Unauthenticated"; - await _workmanager.cancelByUniqueName(taskName); - _workmanager.registerPeriodicTask( - taskName, - taskName, - frequency: Duration(minutes: minuteInterval), - inputData: auth.toMap(), - ); - } + // ===== Websocket Stuff ===== void _onDisconnected(error) async { _wsChannel = null; _logger.warning("Neos Hub connection died with error '$error', reconnecting..."); - await startWebsocket(); + await _startWebsocket(); } - Future startWebsocket() async { + Future _startWebsocket() async { if (!_apiClient.isAuthenticated) { _logger.info("Tried to connect to Neos Hub without authentication, this is probably fine for now."); return; @@ -311,7 +311,7 @@ class MessagingClient extends ChangeNotifier { } void _handleEvent(event) { - final body = jsonDecode((event.toString().replaceAll(eofChar, ""))); + final body = jsonDecode((event.toString().replaceAll(_eofChar, ""))); final int rawType = body["type"] ?? 0; if (rawType > EventType.values.length) { _logger.info("Unhandled event type $rawType: $body"); @@ -378,31 +378,8 @@ class MessagingClient extends ChangeNotifier { } } - void sendMessage(Message message) async { - final msgBody = message.toMap(); - final data = { - "type": EventType.message.index, - "target": "SendMessage", - "arguments": [ - msgBody - ], - }; - _sendData(data); - final cache = getUserMessageCache(message.recipientId) ?? _createUserMessageCache(message.recipientId); - cache.messages.add(message); - notifyListeners(); - } - - void markMessagesRead(MarkReadBatch batch) { - final msgBody = batch.toMap(); - final data = { - "type": EventType.message.index, - "target": "MarkMessagesRead", - "arguments": [ - msgBody - ], - }; - _sendData(data); - clearUnreadsForUser(batch.senderId); + void _sendData(data) { + if (_wsChannel == null) throw "Neos Hub is not connected"; + _wsChannel!.add(jsonEncode(data)+_eofChar); } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 5119a0e..9073b58 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,17 +17,11 @@ import 'package:intl/intl.dart'; import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; -import 'package:workmanager/workmanager.dart'; import 'models/authentication_data.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - if (Platform.isAndroid) { - await Workmanager().initialize( - callbackDispatcher, // The top level function, aka callbackDispatcher - isInDebugMode: true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks - ); - } + await Hive.initFlutter(); final dateFormat = DateFormat.Hms(); Logger.root.onRecord.listen((event) => log("${dateFormat.format(event.time)}: ${event.message}", name: event.loggerName, time: event.time)); @@ -36,17 +30,6 @@ void main() async { runApp(Phoenix(child: ContactsPlusPlus(settingsClient: settingsClient,))); } -@pragma('vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+ -void callbackDispatcher() { - Workmanager().executeTask((String task, Map? inputData) async { - debugPrint("Native called background task: $task"); //simpleTask will be emitted here. - if (task == MessagingClient.taskName) { - final unreads = MessagingClient.backgroundCheckUnreads(inputData); - } - return Future.value(true); - }); -} - class ContactsPlusPlus extends StatefulWidget { const ContactsPlusPlus({required this.settingsClient, super.key}); diff --git a/lib/models/personal_profile.dart b/lib/models/personal_profile.dart index 829f6a8..b2833fd 100644 --- a/lib/models/personal_profile.dart +++ b/lib/models/personal_profile.dart @@ -1,5 +1,3 @@ - -import 'package:collection/collection.dart'; import 'package:contacts_plus_plus/models/user_profile.dart'; class PersonalProfile { diff --git a/lib/widgets/friends/friends_list.dart b/lib/widgets/friends/friends_list.dart index ce46132..7af63df 100644 --- a/lib/widgets/friends/friends_list.dart +++ b/lib/widgets/friends/friends_list.dart @@ -239,7 +239,7 @@ class _FriendsListState extends State { child: DefaultErrorWidget( message: mClient.initStatus, onRetry: () async { - mClient.resetStatus(); + mClient.resetInitStatus(); mClient.refreshFriendsListWithErrorHandler(); }, ),