rework cache android resume based off message count totals, force pre fetch on load message list, tweak new messages bubble behaviour

This commit is contained in:
Dan Ballard 2022-04-28 08:57:31 -07:00
parent ecdcef2192
commit 0bcfe75a63
6 changed files with 59 additions and 44 deletions

View File

@ -43,7 +43,6 @@ class ContactInfoState extends ChangeNotifier {
late DateTime _lastMessageTime;
late Map<String, GlobalKey<MessageRowState>> keys;
int _newMarkerMsgId = -1;
DateTime _newMarkerClearAt = DateTime.now();
late MessageCache messageCache;
// todo: a nicer way to model contacts, groups and other "entities"
@ -145,24 +144,22 @@ class ContactInfoState extends ChangeNotifier {
notifyListeners();
}
void selected() {
this._unreadMessages = 0;
}
void unselected() {
this._newMarkerMsgId = -1;
}
int get unreadMessages => this._unreadMessages;
set unreadMessages(int newVal) {
if (newVal == 0 && this._unreadMessages != 0) {
// conversation has been selected, start the countdown for the New Messager marker to be reset
this._newMarkerClearAt = DateTime.now().add(const Duration(minutes: 2));
}
this._unreadMessages = newVal;
notifyListeners();
}
int get newMarkerMsgId {
if (DateTime.now().isAfter(this._newMarkerClearAt)) {
// perform heresy
this._newMarkerMsgId = -1;
// no need to notifyListeners() because presumably this getter is
// being called from a renderer anyway
}
return this._newMarkerMsgId;
}

View File

@ -78,7 +78,7 @@ class ByIndex implements CacheHandler {
Future<MessageInfo?> get(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) async {
// if in cache, get. But if the cache has unsynced or not in cache, we'll have to do a fetch
if (cache.indexUnsynced == 0 && index < cache.cacheByIndex.length) {
if (index < cache.cacheByIndex.length) {
return cache.getByIndex(index);
}
@ -92,12 +92,6 @@ class ByIndex implements CacheHandler {
amount += index - start;
}
// on android we may have recieved messages on the backend that we didn't process in the UI, get them
// override the index chunk setting, the index math is wrong will we fetch these and these are all that should be missing
if (cache.indexUnsynced > 0) {
start = 0;
amount = cache.indexUnsynced;
}
// check that we aren't asking for messages beyond stored messages
if (start + amount >= cache.storageMessageCount) {
@ -108,6 +102,27 @@ class ByIndex implements CacheHandler {
}
cache.lockIndexes(start, start + amount);
await fetchAndProcess(start, amount, cwtch, profileOnion, conversationIdentifier, cache);
return cache.getByIndex(index);
}
void loadUnsynced(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) {
// return if inadvertently called when no unsynced messages
if (cache.indexUnsynced == 0) {
return;
}
// otherwise we are going to fetch, so we'll fetch a chunk of messages
var start = 0;
var amount = cache.indexUnsynced;
cache.lockIndexes(start, start + amount);
fetchAndProcess(start, amount, cwtch, profileOnion, conversationIdentifier, cache);
return;
}
Future<void> fetchAndProcess(int start, int amount, Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) async {
var msgs = await cwtch.GetMessages(profileOnion, conversationIdentifier, start, amount);
int i = 0; // i used to loop through returned messages. if doesn't reach the requested count, we will use it in the finally stanza to error out the remaining asked for messages in the cache
try {
@ -124,7 +139,6 @@ class ByIndex implements CacheHandler {
cache.malformIndexes(start + i, start + amount);
}
}
return cache.getByIndex(index);
}
void add(MessageCache cache, MessageInfo messageInfo) {

View File

@ -98,25 +98,16 @@ class MessageCache extends ChangeNotifier {
this._storageMessageCount = newval;
}
// On android reconnect, get unread message cound and the last seen Id
// sync this data with what we have cached to determine if/how many messages are now at the front of the index that we don't have cached
void addFrontIndexGap(int count, int lastSeenId) {
// scan across indexed message the unread count amount (that's the last time UI/BE acked a message)
// if we find the last seen ID, the diff of unread count is what's unsynced
for (var i = 0; i < (count + 1) && i < cacheByIndex.length; i++) {
if (this.cacheByIndex[i].messageId == lastSeenId) {
// we have found the matching lastSeenId so we can calculate the unsynced as the unread messages before it
this._indexUnsynced = count - i;
notifyListeners();
return;
}
}
// we did not find a matching index, diff to the back end is too great, reset index cache
resetIndexCache();
// On android reconnect, if backend supplied message count > UI message count, add the differnce to the front of the index
void addFrontIndexGap(int count) {
this._indexUnsynced = count;
// TODO: needed? maybe not? since after totalmessatges and profile will notify
//notifyListeners();
}
int get indexUnsynced => _indexUnsynced;
// TODO: unused? delete?
void resetIndexCache() {
this._indexUnsynced = 0;
cacheByIndex = List.empty(growable: true);
@ -144,7 +135,6 @@ class MessageCache extends ChangeNotifier {
if (contenthash != null && contenthash != "") {
this.cacheByHash[contenthash] = messageID;
}
notifyListeners();
}
// inserts place holder values into the index cache that will block on .get() until .finishLoad() is called on them with message contents
@ -175,7 +165,6 @@ class MessageCache extends ChangeNotifier {
this.cacheByIndex.insert(index, LocalIndexMessage(messageInfo.metadata.messageID));
}
this.cacheByHash[messageInfo.metadata.contenthash] = messageInfo.metadata.messageID;
notifyListeners();
}
void addUnindexed(MessageInfo messageInfo) {
@ -183,7 +172,6 @@ class MessageCache extends ChangeNotifier {
if (messageInfo.metadata.contenthash != "") {
this.cacheByHash[messageInfo.metadata.contenthash] = messageInfo.metadata.messageID;
}
notifyListeners();
}
void ackCache(int messageID) {

View File

@ -176,14 +176,13 @@ class ProfileInfoState extends ChangeNotifier {
this._unreadMessages += contact["numUnread"] as int;
if (profileContact != null) {
profileContact.status = contact["status"];
profileContact.totalMessages = contact["numMessages"];
profileContact.unreadMessages = contact["numUnread"];
if (contact["numUnread"] > MaxUnreadBeforeCacheReset || (contact["numUnread"] > 0 && contact["lastSeenMessageId"] == -1)) {
profileContact.messageCache.resetIndexCache();
} else if (contact["numUnread"] > 0) {
profileContact.messageCache.addFrontIndexGap(contact["numUnread"], contact["lastSeenMessageId"]);
var newCount = contact["numMessages"];
if (newCount != profileContact.totalMessages) {
profileContact.messageCache.addFrontIndexGap(newCount - profileContact.totalMessages);
}
profileContact.totalMessages = newCount;
profileContact.unreadMessages = contact["numUnread"];
profileContact.lastMessageTime = DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"]));
} else {
this._contacts.add(ContactInfoState(

View File

@ -30,7 +30,15 @@ class ContactsView extends StatefulWidget {
void selectConversation(BuildContext context, int handle) {
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
var initialIndex = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages;
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages = 0;
var previouslySelected = Provider.of<AppState>(context, listen: false).selectedConversation;
if (previouslySelected != null) {
Provider
.of<ProfileInfoState>(context, listen: false)
.contactList
.getContact(previouslySelected)!
.unselected();
}
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.selected();
// triggers update in Double/TripleColumnView
Provider.of<AppState>(context, listen: false).initialScrollIndex = initialIndex;
Provider.of<AppState>(context, listen: false).selectedConversation = handle;

View File

@ -1,6 +1,7 @@
import 'package:cwtch/models/appstate.dart';
import 'package:cwtch/models/contact.dart';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/models/messagecache.dart';
import 'package:cwtch/models/profile.dart';
import 'package:cwtch/widgets/messageloadingbubble.dart';
import 'package:flutter/material.dart';
@ -8,6 +9,7 @@ import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../settings.dart';
class MessageList extends StatefulWidget {
@ -22,6 +24,13 @@ class MessageList extends StatefulWidget {
class _MessageListState extends State<MessageList> {
@override
Widget build(BuildContext outerContext) {
// On Android we can have unsynced messages at the front of the index from when the UI was asleep, if there are some, kick off sync of those first
if (Provider.of<ContactInfoState>(outerContext).messageCache.indexUnsynced != 0) {
var conversationId = Provider.of<AppState>(context, listen: false).selectedConversation!;
MessageCache? cache = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(conversationId)?.messageCache;
ByIndex(Provider.of<AppState>(context, listen: false).selectedIndex!).loadUnsynced(Provider.of<FlwtchState>(context, listen: false).cwtch, Provider.of<AppState>(context, listen: false).selectedProfile!, conversationId, cache!);
}
var initi = Provider.of<AppState>(outerContext, listen: false).initialScrollIndex;
bool isP2P = !Provider.of<ContactInfoState>(context).isGroup;
bool isGroupAndSyncing = Provider.of<ContactInfoState>(context).isGroup == true && Provider.of<ContactInfoState>(context).status == "Authenticated";