diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index f8ac337c..0367c7ee 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -5,6 +5,7 @@ import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/profilelist.dart'; import 'package:cwtch/models/profileservers.dart'; +import 'package:cwtch/models/remoteserver.dart'; import 'package:cwtch/models/servers.dart'; import 'package:cwtch/notification_manager.dart'; import 'package:provider/provider.dart'; @@ -193,7 +194,8 @@ class CwtchNotifier { var senderHandle = data['RemotePeer']; var senderImage = data['Picture']; var timestampSent = DateTime.tryParse(data['TimestampSent'])!; - var currentTotal = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.totalMessages; + var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier); + var currentTotal = contact!.totalMessages; var isAuto = data['Auto'] == "true"; String? contenthash = data['ContentHash']; var selectedConversation = appState.selectedProfile == data["ProfileOnion"] && appState.selectedConversation == identifier; @@ -214,6 +216,8 @@ class CwtchNotifier { notificationManager.notify("New Message From Group!"); } + RemoteServerInfoState? server = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(contact.server); + server?.updateSyncProgressFor(timestampSent); } else { // This is dealt with by IndexedAcknowledgment EnvironmentConfig.debugLog("new message from group from yourself - this should not happen"); diff --git a/lib/models/contact.dart b/lib/models/contact.dart index d39aa193..5ac0edd4 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -211,6 +211,7 @@ class ContactInfoState extends ChangeNotifier { newMarker++; } + this._lastMessageTime = timestamp; this.messageCache.addNew(profileOnion, identifier, messageID, timestamp, senderHandle, senderImage, isAuto, data, contenthash); this.totalMessages += 1; diff --git a/lib/models/profile.dart b/lib/models/profile.dart index d79617ef..268eeb78 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:cwtch/models/remoteserver.dart'; import 'package:flutter/widgets.dart'; import 'contact.dart'; @@ -72,7 +73,7 @@ class ProfileInfoState extends ChangeNotifier { List servers = jsonDecode(serversJson); this._servers.replace(servers.map((server) { // TODO Keys... - return RemoteServerInfoState(onion: server["onion"], identifier: server["identifier"], description: server["description"], status: server["status"]); + return RemoteServerInfoState(server["onion"], server["identifier"], server["description"], server["status"]); })); this._contacts.contacts.forEach((contact) { diff --git a/lib/models/profileservers.dart b/lib/models/profileservers.dart index 9dd7af49..fbc02203 100644 --- a/lib/models/profileservers.dart +++ b/lib/models/profileservers.dart @@ -1,3 +1,4 @@ +import 'package:cwtch/models/remoteserver.dart'; import 'package:flutter/material.dart'; import 'contact.dart'; @@ -33,12 +34,17 @@ class ProfileServerListState extends ChangeNotifier { // return -1 = a first in list // return 1 = b first in list - // online v offline + // online v syncing v offline if (a.status == "Synced" && b.status != "Synced") { return -1; } else if (a.status != "Synced" && b.status == "Synced") { return 1; } + if (a.status == "Authenticated" && b.status != "Authenticated") { + return -1; + } else if (a.status != "Authenticated" && b.status == "Authenticated") { + return 1; + } // num of groups if (a.groups.length > b.groups.length) { @@ -65,30 +71,3 @@ class ProfileServerListState extends ChangeNotifier { List get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier } - -class RemoteServerInfoState extends ChangeNotifier { - final String onion; - final int identifier; - String status; - String description; - List _groups = []; - - RemoteServerInfoState({required this.onion, required this.identifier, required this.description, required this.status}); - - void updateDescription(String newDescription) { - this.description = newDescription; - notifyListeners(); - } - - void clearGroups() { - _groups = []; - } - - void addGroup(ContactInfoState group) { - _groups.add(group); - notifyListeners(); - } - - List get groups => _groups.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier - -} diff --git a/lib/models/remoteserver.dart b/lib/models/remoteserver.dart new file mode 100644 index 00000000..f9e658dd --- /dev/null +++ b/lib/models/remoteserver.dart @@ -0,0 +1,55 @@ +import 'package:flutter/cupertino.dart'; + +import 'contact.dart'; + +class RemoteServerInfoState extends ChangeNotifier { + final String onion; + final int identifier; + String _status; + String description; + List _groups = []; + + double syncProgress = 0; + DateTime lastPreSyncMessagTime = new DateTime(2020); + + RemoteServerInfoState(this.onion, this.identifier, this.description, this._status); + + void updateDescription(String newDescription) { + this.description = newDescription; + notifyListeners(); + } + + void clearGroups() { + _groups = []; + } + + void addGroup(ContactInfoState group) { + _groups.add(group); + notifyListeners(); + } + + String get status => _status; + set status(String newStatus) { + _status = newStatus; + if (status == "Authenticated") { + // syncing, set lastPreSyncMessageTime + _groups.forEach((g) { + if(g.lastMessageTime.isAfter(lastPreSyncMessagTime)) { + lastPreSyncMessagTime = g.lastMessageTime; + } + }); + } + notifyListeners(); + } + + // updateSyncProgressFor point takes a message's time, and updates the server sync progress, + // based on that point in time between the precalculated lastPreSyncMessagTime and Now + void updateSyncProgressFor(DateTime point) { + var range = lastPreSyncMessagTime.difference(DateTime.now()); + var pointFromStart = lastPreSyncMessagTime.difference(point); + syncProgress = pointFromStart.inSeconds / range.inSeconds; + notifyListeners(); + } + + List get groups => _groups.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier +} \ No newline at end of file diff --git a/lib/views/addcontactview.dart b/lib/views/addcontactview.dart index 2d58c43f..3cee78c1 100644 --- a/lib/views/addcontactview.dart +++ b/lib/views/addcontactview.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/profile.dart'; +import 'package:cwtch/models/remoteserver.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:cwtch/errorHandler.dart'; diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 4e7b6ade..7f6b2236 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -3,6 +3,7 @@ import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/contactlist.dart'; import 'package:cwtch/models/profile.dart'; +import 'package:cwtch/models/profilelist.dart'; import 'package:cwtch/views/profileserversview.dart'; import 'package:flutter/material.dart'; import 'package:cwtch/widgets/contactrow.dart'; @@ -154,8 +155,15 @@ class _ContactsViewState extends State { Widget _buildContactList() { final tiles = Provider.of(context).filteredList().map((ContactInfoState contact) { - return ChangeNotifierProvider.value(key: ValueKey(contact.profileOnion + "" + contact.onion), value: contact, builder: (_, __) => RepaintBoundary(child: ContactRow())); + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: contact), + ChangeNotifierProvider.value(value: Provider.of(context).serverList), + ], + builder: (context, child) => RepaintBoundary(child: ContactRow()), + ); }); + final divided = ListTile.divideTiles( context: context, tiles: tiles, diff --git a/lib/views/profileserversview.dart b/lib/views/profileserversview.dart index 6a4d3ebd..2c4e8aab 100644 --- a/lib/views/profileserversview.dart +++ b/lib/views/profileserversview.dart @@ -1,5 +1,6 @@ import 'package:cwtch/models/profile.dart'; import 'package:cwtch/models/profileservers.dart'; +import 'package:cwtch/models/remoteserver.dart'; import 'package:cwtch/models/servers.dart'; import 'package:cwtch/widgets/remoteserverrow.dart'; import 'package:flutter/material.dart'; diff --git a/lib/views/remoteserverview.dart b/lib/views/remoteserverview.dart index 46ac8567..22e0a6f2 100644 --- a/lib/views/remoteserverview.dart +++ b/lib/views/remoteserverview.dart @@ -4,6 +4,7 @@ import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/models/profileservers.dart'; +import 'package:cwtch/models/remoteserver.dart'; import 'package:cwtch/models/servers.dart'; import 'package:cwtch/widgets/buttontextfield.dart'; import 'package:cwtch/widgets/contactrow.dart'; diff --git a/lib/widgets/contactrow.dart b/lib/widgets/contactrow.dart index 53dfe32c..b61b8ffa 100644 --- a/lib/widgets/contactrow.dart +++ b/lib/widgets/contactrow.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/profile.dart'; +import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/views/contactsview.dart'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; @@ -66,6 +67,7 @@ class _ContactRowState extends State { visible: contact.isGroup && contact.status == "Authenticated", child: LinearProgressIndicator( color: Provider.of(context).theme.defaultButtonActiveColor, + value: Provider.of(context).serverList.getServer(contact.server)?.syncProgress, )), Visibility( visible: !Provider.of(context).streamerMode, diff --git a/lib/widgets/profilerow.dart b/lib/widgets/profilerow.dart index 6d2c8e8e..acbdce0a 100644 --- a/lib/widgets/profilerow.dart +++ b/lib/widgets/profilerow.dart @@ -91,7 +91,7 @@ class _ProfileRowState extends State { return MultiProvider( providers: [ ChangeNotifierProvider.value(value: profile), - ChangeNotifierProvider.value(value: profile.contactList), + ChangeNotifierProvider.value(value: profile.contactList) ], builder: (innercontext, widget) { var appState = Provider.of(context); diff --git a/lib/widgets/remoteserverrow.dart b/lib/widgets/remoteserverrow.dart index c35b0e9a..fab0076f 100644 --- a/lib/widgets/remoteserverrow.dart +++ b/lib/widgets/remoteserverrow.dart @@ -1,6 +1,7 @@ import 'package:cwtch/main.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/models/profileservers.dart'; +import 'package:cwtch/models/remoteserver.dart'; import 'package:cwtch/models/servers.dart'; import 'package:cwtch/views/addeditservers.dart'; import 'package:cwtch/views/remoteserverview.dart'; @@ -55,7 +56,13 @@ class _RemoteServerRowState extends State { softWrap: true, overflow: TextOverflow.ellipsis, style: TextStyle(color: running ? Provider.of(context).theme.portraitOnlineBorderColor : Provider.of(context).theme.portraitOfflineBorderColor), - ))) + ))), + Visibility( + visible: server.status == "Authenticated", + child: LinearProgressIndicator( + color: Provider.of(context).theme.defaultButtonActiveColor, + value: server.syncProgress, + )), ], )), ]),