Stop using key-based lookups for messages, use the message cache instead.

Always update alignment to allow for message row objects to be reused
This commit is contained in:
Sarah Jamie Lewis 2022-03-04 11:30:19 -08:00
parent 7457246a01
commit 1fea540f9d
26 changed files with 60 additions and 66 deletions

View File

@ -192,25 +192,14 @@ class CwtchNotifier {
var conversation = int.parse(data["ConversationID"]); var conversation = int.parse(data["ConversationID"]);
var messageID = int.parse(data["Index"]); var messageID = int.parse(data["Index"]);
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation); // We only ever see acks from authenticated peers.
// We return -1 for protocol message acks if there is no message // If the contact is marked as offline then override this - can happen when the contact is removed from the front
if (messageID == -1) break; // end during syncing.
var key = contact!.getMessageKeyOrFail(conversation, messageID); if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation)!.isOnline() == false) {
if (key == null) break; profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation)!.status = "Authenticated";
try {
var message = Provider.of<MessageMetadata>(key.currentContext!, listen: false);
message.ackd = true;
// We only ever see acks from authenticated peers.
// If the contact is marked as offline then override this - can happen when the contact is removed from the front
// end during syncing.
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation)!.isOnline() == false) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation)!.status = "Authenticated";
}
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation)!.ackCache(messageID);
} catch (e) {
// ignore, most likely cause is the key got optimized out...
} }
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation)!.ackCache(messageID);
break; break;
case "NewMessageFromGroup": case "NewMessageFromGroup":
var identifier = int.parse(data["ConversationID"]); var identifier = int.parse(data["ConversationID"]);
@ -259,12 +248,8 @@ class CwtchNotifier {
case "IndexedFailure": case "IndexedFailure":
var identifier = int.parse(data["ConversationID"]); var identifier = int.parse(data["ConversationID"]);
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier); var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier);
var idx = int.parse(data["Index"]); var messageID = int.parse(data["Index"]);
var key = contact?.getMessageKeyOrFail(contact.identifier, idx); contact!.errCache(messageID);
if (key != null) {
var message = Provider.of<MessageMetadata>(key.currentContext!, listen: false);
message.error = true;
}
break; break;
case "AppError": case "AppError":
EnvironmentConfig.debugLog("New App Error: $data"); EnvironmentConfig.debugLog("New App Error: $data");

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "cy", "@@locale": "cy",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"deleteConfirmLabel": "Teipiwch DILEU i gadarnhau",
"deleteConfirmText": "DILEU",
"localeDa": "Daneg", "localeDa": "Daneg",
"successfullAddedContact": "Wedi llwyddo i ychwanegu: ", "successfullAddedContact": "Wedi llwyddo i ychwanegu: ",
"serverMetricsLabel": "Metrigau Gweinydd", "serverMetricsLabel": "Metrigau Gweinydd",
@ -281,9 +283,7 @@
"settingLanguage": "Laith", "settingLanguage": "Laith",
"password": "Cyfrinair", "password": "Cyfrinair",
"addNewProfileBtn": "Ychwanegu proffil newydd", "addNewProfileBtn": "Ychwanegu proffil newydd",
"deleteConfirmText": "DELETE",
"deleteProfileConfirmBtn": "Dileu Proffil yn Wirioneddol", "deleteProfileConfirmBtn": "Dileu Proffil yn Wirioneddol",
"deleteConfirmLabel": "Teipiwch DELETE i gadarnhau",
"deleteProfileBtn": "Dileu Proffil", "deleteProfileBtn": "Dileu Proffil",
"passwordErrorMatch": "Nid yw cyfrineiriau'n cyfateb", "passwordErrorMatch": "Nid yw cyfrineiriau'n cyfateb",
"saveProfileBtn": "Cadw Proffil", "saveProfileBtn": "Cadw Proffil",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "da", "@@locale": "da",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"serverLabel": "Server", "serverLabel": "Server",
"profileOnionLabel": "Send denne adresse til personer du ønsker forbindelse med", "profileOnionLabel": "Send denne adresse til personer du ønsker forbindelse med",
"saveBtn": "Gem", "saveBtn": "Gem",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "de", "@@locale": "de",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"deleteConfirmLabel": "Gib LÖSCHEN ein, um zu bestätigen",
"localeDa": "Dänisch", "localeDa": "Dänisch",
"localeCy": "Walisisch", "localeCy": "Walisisch",
"pasteAddressToAddContact": "Cwtch Adresse, Einladung oder Schlüssel hier hinzufügen, um eine neue Konversation hinzuzufügen", "pasteAddressToAddContact": "Cwtch Adresse, Einladung oder Schlüssel hier hinzufügen, um eine neue Konversation hinzuzufügen",
@ -176,7 +177,6 @@
"yourServers": "Deine Server", "yourServers": "Deine Server",
"yourProfiles": "Deine Profile", "yourProfiles": "Deine Profile",
"enterProfilePassword": "Gib ein Passwort ein, um deine Profile anzuzeigen", "enterProfilePassword": "Gib ein Passwort ein, um deine Profile anzuzeigen",
"deleteConfirmLabel": "Gib LÖSCHEN ein um zu bestätigen",
"notificationNewMessageFromGroup": "Neue Nachricht in einer Gruppe!", "notificationNewMessageFromGroup": "Neue Nachricht in einer Gruppe!",
"notificationNewMessageFromPeer": "Neue Nachricht von einem Kontakt!", "notificationNewMessageFromPeer": "Neue Nachricht von einem Kontakt!",
"tooltipHidePassword": "Password verstecken", "tooltipHidePassword": "Password verstecken",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "el", "@@locale": "el",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeCy": "Ουαλικά", "localeCy": "Ουαλικά",
"localeDa": "Δανικά", "localeDa": "Δανικά",
"server": "Διακομιστής", "server": "Διακομιστής",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "en", "@@locale": "en",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeDa": "Danish", "localeDa": "Danish",
"localeCy": "Welsh", "localeCy": "Welsh",
"localeEl": "Greek", "localeEl": "Greek",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "es", "@@locale": "es",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeDa": "Danés", "localeDa": "Danés",
"groupInviteSettingsWarning": "¡Has recibido una invitación para unirte a un grupo! Por favor habilita el experimento de chat grupal en Configuración para ver esta invitación", "groupInviteSettingsWarning": "¡Has recibido una invitación para unirte a un grupo! Por favor habilita el experimento de chat grupal en Configuración para ver esta invitación",
"plainServerDescription": "Te recomendamos que protejas tus servidores de Cwtch con una contraseña. Si no estableces una contraseña en este servidor, cualquiera que tenga acceso a este dispositivo podrá acceder a la información sobre este servidor incluyendo claves criptográficas confidenciales", "plainServerDescription": "Te recomendamos que protejas tus servidores de Cwtch con una contraseña. Si no estableces una contraseña en este servidor, cualquiera que tenga acceso a este dispositivo podrá acceder a la información sobre este servidor incluyendo claves criptográficas confidenciales",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeDa": "Danois", "localeDa": "Danois",
"localeCy": "Gallois", "localeCy": "Gallois",
"conversationNotificationPolicySettingDescription": "Contrôler le comportement de notification de cette conversation", "conversationNotificationPolicySettingDescription": "Contrôler le comportement de notification de cette conversation",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "it", "@@locale": "it",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeDa": "Danese", "localeDa": "Danese",
"localeCy": "Gallese", "localeCy": "Gallese",
"settingTheme": "Usa Temi Leggeri", "settingTheme": "Usa Temi Leggeri",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "lb", "@@locale": "lb",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeDa": "Danish", "localeDa": "Danish",
"localeCy": "Welsh", "localeCy": "Welsh",
"serverSynced": "Synchroniséiert", "serverSynced": "Synchroniséiert",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "no", "@@locale": "no",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeDa": "Dansk", "localeDa": "Dansk",
"localeCy": "Walisisk", "localeCy": "Walisisk",
"serverLabel": "Tjener", "serverLabel": "Tjener",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "pl", "@@locale": "pl",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"deleteConfirmLabel": "Wpisz USUŃ aby potwierdzić",
"localeLb": "Luksemburski", "localeLb": "Luksemburski",
"localeNo": "Norweski", "localeNo": "Norweski",
"localeEl": "Grecki", "localeEl": "Grecki",
@ -63,7 +64,6 @@
"addNewProfileBtn": "Dodaj", "addNewProfileBtn": "Dodaj",
"deleteConfirmText": "USUŃ", "deleteConfirmText": "USUŃ",
"deleteProfileConfirmBtn": "Usuń", "deleteProfileConfirmBtn": "Usuń",
"deleteConfirmLabel": "Wpisz USUŃ aby potwierdzić",
"deleteProfileBtn": "Usuń profil", "deleteProfileBtn": "Usuń profil",
"passwordChangeError": "Zmiana hasła nie powiodła się: hasło niepoprawne", "passwordChangeError": "Zmiana hasła nie powiodła się: hasło niepoprawne",
"passwordErrorMatch": "Hasła nie są identyczne", "passwordErrorMatch": "Hasła nie są identyczne",

View File

@ -1,6 +1,6 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"localeDa": "Danish", "localeDa": "Danish",
"localeCy": "Welsh", "localeCy": "Welsh",
"localeEl": "Greek", "localeEl": "Greek",

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "ro", "@@locale": "ro",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"deleteProfileConfirmBtn": "Sigur ștergeti profilul",
"deleteConfirmLabel": "Tastați ȘTERGE pentru a confirma",
"localeDa": "Daneză", "localeDa": "Daneză",
"localeCy": "Velşă", "localeCy": "Velşă",
"conversationNotificationPolicySettingDescription": "Controlați comportamentul de notificare al acestei conversații", "conversationNotificationPolicySettingDescription": "Controlați comportamentul de notificare al acestei conversații",
@ -89,8 +91,6 @@
"passwordErrorMatch": "Parolele nu se potrivesc", "passwordErrorMatch": "Parolele nu se potrivesc",
"passwordChangeError": "Eroare la schimbarea parolei: Parola furnizată a fost respinsă", "passwordChangeError": "Eroare la schimbarea parolei: Parola furnizată a fost respinsă",
"deleteProfileBtn": "Ștergeți profilul", "deleteProfileBtn": "Ștergeți profilul",
"deleteConfirmLabel": "Tastați DELETE pentru a confirma",
"deleteProfileConfirmBtn": "Sigur ștergeti profilul",
"deleteConfirmText": "ȘTERGE", "deleteConfirmText": "ȘTERGE",
"addNewProfileBtn": "Adăugați un profil nou", "addNewProfileBtn": "Adăugați un profil nou",
"enterProfilePassword": "Introduceți o parolă pentru a vă vizualiza profilurile", "enterProfilePassword": "Introduceți o parolă pentru a vă vizualiza profilurile",

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "ru", "@@locale": "ru",
"@@last_modified": "2022-03-03T19:10:17+01:00", "@@last_modified": "2022-03-04T17:33:01+01:00",
"deleteConfirmLabel": "Введите УДАЛИТЬ, чтобы продолжить",
"deleteConfirmText": "УДАЛИТЬ",
"localeCy": "Валлийский", "localeCy": "Валлийский",
"localeDa": "Датский", "localeDa": "Датский",
"localeEl": "Греческий", "localeEl": "Греческий",
@ -241,9 +243,7 @@
"password": "Пароль", "password": "Пароль",
"enterProfilePassword": "Введите пароль для просмотра ваших профилей", "enterProfilePassword": "Введите пароль для просмотра ваших профилей",
"addNewProfileBtn": "Добавить новый профиль", "addNewProfileBtn": "Добавить новый профиль",
"deleteConfirmText": "УДАЛИТЬ",
"deleteProfileConfirmBtn": "Действительно удалить профиль?", "deleteProfileConfirmBtn": "Действительно удалить профиль?",
"deleteConfirmLabel": "Введите DELETE чтобы продолжить",
"deleteProfileBtn": "Удалить профиль", "deleteProfileBtn": "Удалить профиль",
"passwordChangeError": "Ошибка при смене пароля: Введенный пароль отклонен", "passwordChangeError": "Ошибка при смене пароля: Введенный пароль отклонен",
"passwordErrorMatch": "Пароли не совпадают", "passwordErrorMatch": "Пароли не совпадают",

View File

@ -276,6 +276,11 @@ class ContactInfoState extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void errCache(int messageID) {
this.messageCache.errCache(messageID);
notifyListeners();
}
static ConversationNotificationPolicy notificationPolicyFromString(String val) { static ConversationNotificationPolicy notificationPolicyFromString(String val) {
switch (val) { switch (val) {
case "ConversationNotificationPolicy.Default": case "ConversationNotificationPolicy.Default":

View File

@ -32,7 +32,7 @@ const GroupConversationHandleLength = 32;
abstract class Message { abstract class Message {
MessageMetadata getMetadata(); MessageMetadata getMetadata();
Widget getWidget(BuildContext context, Key key); Widget getWidget(BuildContext context);
Widget getPreviewWidget(BuildContext context); Widget getPreviewWidget(BuildContext context);
} }

View File

@ -1,3 +1,5 @@
import 'package:flutter/foundation.dart';
import 'message.dart'; import 'message.dart';
class MessageInfo { class MessageInfo {
@ -6,7 +8,7 @@ class MessageInfo {
MessageInfo(this.metadata, this.wrapper); MessageInfo(this.metadata, this.wrapper);
} }
class MessageCache { class MessageCache extends ChangeNotifier {
late Map<int, MessageInfo> cache; late Map<int, MessageInfo> cache;
late List<int?> cacheByIndex; late List<int?> cacheByIndex;
late Map<String, int> cacheByHash; late Map<String, int> cacheByHash;
@ -35,6 +37,7 @@ class MessageCache {
if (contenthash != null && contenthash != "") { if (contenthash != null && contenthash != "") {
this.cacheByHash[contenthash] = messageID; this.cacheByHash[contenthash] = messageID;
} }
notifyListeners();
} }
void add(MessageInfo messageInfo, int index, String? contenthash) { void add(MessageInfo messageInfo, int index, String? contenthash) {
@ -43,6 +46,7 @@ class MessageCache {
if (contenthash != null && contenthash != "") { if (contenthash != null && contenthash != "") {
this.cacheByHash[contenthash] = messageInfo.metadata.messageID; this.cacheByHash[contenthash] = messageInfo.metadata.messageID;
} }
notifyListeners();
} }
void addUnindexed(MessageInfo messageInfo, String? contenthash) { void addUnindexed(MessageInfo messageInfo, String? contenthash) {
@ -50,9 +54,16 @@ class MessageCache {
if (contenthash != null && contenthash != "") { if (contenthash != null && contenthash != "") {
this.cacheByHash[contenthash] = messageInfo.metadata.messageID; this.cacheByHash[contenthash] = messageInfo.metadata.messageID;
} }
notifyListeners();
} }
void ackCache(int messageID) { void ackCache(int messageID) {
cache[messageID]?.metadata.ackd = true; cache[messageID]?.metadata.ackd = true;
notifyListeners();
}
void errCache(int messageID) {
cache[messageID]?.metadata.error = true;
notifyListeners();
} }
} }

View File

@ -18,7 +18,7 @@ class FileMessage extends Message {
FileMessage(this.metadata, this.content); FileMessage(this.metadata, this.content);
@override @override
Widget getWidget(BuildContext context, Key key) { Widget getWidget(BuildContext context) {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: this.metadata, value: this.metadata,
builder: (bcontext, child) { builder: (bcontext, child) {
@ -42,7 +42,7 @@ class FileMessage extends Message {
return MessageRow(MalformedBubble()); return MessageRow(MalformedBubble());
} }
return MessageRow(FileBubble(nameSuggestion, rootHash, nonce, fileSize, isAuto: metadata.isAuto), key: key); return MessageRow(FileBubble(nameSuggestion, rootHash, nonce, fileSize, isAuto: metadata.isAuto));
}); });
} }

View File

@ -17,7 +17,7 @@ class InviteMessage extends Message {
InviteMessage(this.overlay, this.metadata, this.content); InviteMessage(this.overlay, this.metadata, this.content);
@override @override
Widget getWidget(BuildContext context, Key key) { Widget getWidget(BuildContext context) {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: this.metadata, value: this.metadata,
builder: (bcontext, child) { builder: (bcontext, child) {
@ -39,7 +39,7 @@ class InviteMessage extends Message {
return MessageRow(MalformedBubble()); return MessageRow(MalformedBubble());
} }
} }
return MessageRow(InvitationBubble(overlay, inviteTarget, inviteNick, invite), key: key); return MessageRow(InvitationBubble(overlay, inviteTarget, inviteNick, invite));
}); });
} }

View File

@ -9,11 +9,11 @@ class MalformedMessage extends Message {
MalformedMessage(this.metadata); MalformedMessage(this.metadata);
@override @override
Widget getWidget(BuildContext context, Key key) { Widget getWidget(BuildContext context) {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: this.metadata, value: this.metadata,
builder: (context, child) { builder: (context, child) {
return MessageRow(MalformedBubble(), key: key); return MessageRow(MalformedBubble());
}); });
} }

View File

@ -48,7 +48,7 @@ class QuotedMessage extends Message {
} }
@override @override
Widget getWidget(BuildContext context, Key key) { Widget getWidget(BuildContext context) {
try { try {
dynamic message = jsonDecode(this.content); dynamic message = jsonDecode(this.content);
@ -59,7 +59,7 @@ class QuotedMessage extends Message {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: this.metadata, value: this.metadata,
builder: (bcontext, child) { builder: (bcontext, child) {
return MessageRow(QuotedMessageBubble(message["body"], messageHandler(bcontext, metadata.profileOnion, metadata.conversationIdentifier, ByContentHash(message["quotedHash"]))), key: key); return MessageRow(QuotedMessageBubble(message["body"], messageHandler(bcontext, metadata.profileOnion, metadata.conversationIdentifier, ByContentHash(message["quotedHash"]))));
}); });
} catch (e) { } catch (e) {
return MalformedBubble(); return MalformedBubble();

View File

@ -29,13 +29,12 @@ class TextMessage extends Message {
} }
@override @override
Widget getWidget(BuildContext context, Key key) { Widget getWidget(BuildContext context) {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: this.metadata, value: this.metadata,
builder: (bcontext, child) { builder: (bcontext, child) {
return MessageRow( return MessageRow(
MessageBubble(this.content), MessageBubble(this.content),
key: key,
); );
}); });
} }

View File

@ -30,9 +30,7 @@ class MessageBubbleState extends State<MessageBubble> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion; var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
var prettyDate = "";
var borderRadiousEh = 15.0; var borderRadiousEh = 15.0;
// var myKey = Provider.of<MessageState>(context).profileOnion + "::" + Provider.of<MessageState>(context).contactHandle + "::" + Provider.of<MessageState>(context).messageIndex.toString();
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment); var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
DateTime messageDate = Provider.of<MessageMetadata>(context).timestamp; DateTime messageDate = Provider.of<MessageMetadata>(context).timestamp;

View File

@ -87,8 +87,7 @@ class _MessageListState extends State<MessageList> {
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
var message = snapshot.data as Message; var message = snapshot.data as Message;
var key = Provider.of<ContactInfoState>(outerContext, listen: false).getMessageKey(contactHandle, message.getMetadata().messageID); return message.getWidget(context);
return message.getWidget(context, key);
} else { } else {
return MessageLoadingBubble(); return MessageLoadingBubble();
} }

View File

@ -63,10 +63,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
var actualMessage = Flexible(flex: Platform.isAndroid ? 10 : 3, fit: FlexFit.loose, child: widget.child); var actualMessage = Flexible(flex: Platform.isAndroid ? 10 : 3, fit: FlexFit.loose, child: widget.child);
_dragAffinity = fromMe ? Alignment.centerRight : Alignment.centerLeft; _dragAffinity = fromMe ? Alignment.centerRight : Alignment.centerLeft;
_dragAlignment = fromMe ? Alignment.centerRight : Alignment.centerLeft;
if (_dragAlignment == Alignment.center) {
_dragAlignment = fromMe ? Alignment.centerRight : Alignment.centerLeft;
}
var senderDisplayStr = ""; var senderDisplayStr = "";
if (!fromMe) { if (!fromMe) {