maybe fix for #180

This commit is contained in:
otsmr 2025-05-20 20:35:21 +02:00
parent 95ad761898
commit a57a8051ef
9 changed files with 109 additions and 63 deletions

View file

@ -9,7 +9,6 @@ import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/tables/messages_table.dart'; import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/model/protobuf/api/error.pb.dart';
import 'package:twonly/src/providers/api/api_utils.dart'; import 'package:twonly/src/providers/api/api_utils.dart';
import 'package:twonly/src/providers/hive.dart'; import 'package:twonly/src/providers/hive.dart';
import 'package:twonly/src/services/notification_service.dart'; import 'package:twonly/src/services/notification_service.dart';
@ -29,9 +28,12 @@ Future tryTransmitMessages() async {
Map<String, dynamic> failed = {}; Map<String, dynamic> failed = {};
for (String key in retransmit.keys) { // List<MapEntry<String, dynamic>> sortedList = retransmit.entries.toList()
// ..sort((a, b) => int.parse(a.key).compareTo(int.parse(b.key)));
for (final element in retransmit.entries) {
RetransmitMessage msg = RetransmitMessage msg =
RetransmitMessage.fromJson(jsonDecode(retransmit[key])); RetransmitMessage.fromJson(jsonDecode(element.value));
Result resp = await apiProvider.sendTextMessage( Result resp = await apiProvider.sendTextMessage(
msg.userId, msg.userId,
@ -49,7 +51,7 @@ Future tryTransmitMessages() async {
); );
} }
} else { } else {
failed[key] = retransmit[key]; failed[element.key] = element.value;
} }
} }
Box box = await getMediaStorage(); Box box = await getMediaStorage();
@ -98,21 +100,49 @@ Future<Map<String, dynamic>> getAllMessagesForRetransmitting() async {
Map<String, dynamic> retransmit = {}; Map<String, dynamic> retransmit = {};
if (retransmitJson != null) { if (retransmitJson != null) {
retransmit = jsonDecode(retransmitJson); try {
retransmit = jsonDecode(retransmitJson);
} catch (e) {
Logger("api.dart").shout("Could not decode the retransmit messages: $e");
await box.delete("messages-to-retransmit");
}
} }
return retransmit; return retransmit;
} }
Future<Result> sendRetransmitMessage(
String stateId, RetransmitMessage msg) async {
Result resp =
await apiProvider.sendTextMessage(msg.userId, msg.bytes, msg.pushData);
if (resp.isSuccess) {
{
var retransmit = await getAllMessagesForRetransmitting();
retransmit.remove(stateId);
Box box = await getMediaStorage();
box.put("messages-to-retransmit", jsonEncode(retransmit));
}
if (msg.messageId != null) {
await twonlyDatabase.messagesDao.updateMessageByMessageId(
msg.messageId!,
MessagesCompanion(acknowledgeByServer: Value(true)),
);
}
}
return resp;
}
// this functions ensures that the message is received by the server and in case of errors will try again later // this functions ensures that the message is received by the server and in case of errors will try again later
Future<Result> encryptAndSendMessage( Future<(String, RetransmitMessage)?> encryptMessage(
int? messageId, int userId, MessageJson msg, int? messageId, int userId, MessageJson msg,
{PushKind? pushKind}) async { {PushKind? pushKind}) async {
return await lockSendingMessages.protect<Result>(() async { return await lockSendingMessages
.protect<(String, RetransmitMessage)?>(() async {
Uint8List? bytes = await SignalHelper.encryptMessage(msg, userId); Uint8List? bytes = await SignalHelper.encryptMessage(msg, userId);
if (bytes == null) { if (bytes == null) {
Logger("api.dart").shout("Error encryption message!"); Logger("api.dart").shout("Error encryption message!");
return Result.error(ErrorCode.InternalError); return null;
} }
String stateId = String stateId =
@ -124,39 +154,36 @@ Future<Result> encryptAndSendMessage(
pushData = await getPushData(userId, pushKind); pushData = await getPushData(userId, pushKind);
} }
RetransmitMessage encryptedMessage = RetransmitMessage(
messageId: messageId,
userId: userId,
bytes: bytes,
pushData: pushData,
);
{ {
var retransmit = await getAllMessagesForRetransmitting(); var retransmit = await getAllMessagesForRetransmitting();
retransmit[stateId] = jsonEncode(RetransmitMessage( retransmit[stateId] = jsonEncode(encryptedMessage.toJson());
messageId: messageId,
userId: userId,
bytes: bytes,
pushData: pushData,
).toJson());
box.put("messages-to-retransmit", jsonEncode(retransmit)); box.put("messages-to-retransmit", jsonEncode(retransmit));
} }
Result resp = await apiProvider.sendTextMessage(userId, bytes, pushData); return (stateId, encryptedMessage);
if (resp.isSuccess) {
{
var retransmit = await getAllMessagesForRetransmitting();
retransmit.remove(stateId);
box.put("messages-to-retransmit", jsonEncode(retransmit));
}
if (messageId != null) {
await twonlyDatabase.messagesDao.updateMessageByMessageId(
messageId,
MessagesCompanion(acknowledgeByServer: Value(true)),
);
}
}
return resp;
}); });
} }
// encrypts and stores the message and then sends it in the background
Future encryptAndSendMessageAsync(int? messageId, int userId, MessageJson msg,
{PushKind? pushKind}) async {
(String, RetransmitMessage)? stateData =
await encryptMessage(messageId, userId, msg);
if (stateData != null) {
final (stateId, message) = stateData;
sendRetransmitMessage(stateId, message);
}
}
Future sendTextMessage( Future sendTextMessage(
int target, TextMessageContent content, PushKind? pushKind) async { int target, TextMessageContent content, PushKind? pushKind) async {
DateTime messageSendAt = DateTime.now(); DateTime messageSendAt = DateTime.now();
@ -184,13 +211,13 @@ Future sendTextMessage(
timestamp: messageSendAt, timestamp: messageSendAt,
); );
encryptAndSendMessage(messageId, target, msg, pushKind: pushKind); await encryptAndSendMessageAsync(messageId, target, msg, pushKind: pushKind);
} }
Future notifyContactAboutOpeningMessage( Future notifyContactAboutOpeningMessage(
int fromUserId, List<int> messageOtherIds) async { int fromUserId, List<int> messageOtherIds) async {
for (final messageOtherId in messageOtherIds) { for (final messageOtherId in messageOtherIds) {
await encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
fromUserId, fromUserId,
MessageJson( MessageJson(
@ -216,7 +243,7 @@ Future notifyContactsAboutProfileChange() async {
if (contact.myAvatarCounter < user.avatarCounter!) { if (contact.myAvatarCounter < user.avatarCounter!) {
twonlyDatabase.contactsDao.updateContact(contact.userId, twonlyDatabase.contactsDao.updateContact(contact.userId,
ContactsCompanion(myAvatarCounter: Value(user.avatarCounter!))); ContactsCompanion(myAvatarCounter: Value(user.avatarCounter!)));
encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
contact.userId, contact.userId,
MessageJson( MessageJson(

View file

@ -451,7 +451,7 @@ Future<bool> handleNotifyReceiver(MediaUpload media) async {
); );
// Ensures the retransmit of the message // Ensures the retransmit of the message
await encryptAndSendMessage( await encryptAndSendMessageAsync(
messageId, messageId,
message.contactId, message.contactId,
MessageJson( MessageJson(

View file

@ -226,7 +226,7 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
} }
} }
encryptAndSendMessage( await encryptAndSendMessageAsync(
message.messageId!, message.messageId!,
fromUserId, fromUserId,
MessageJson( MessageJson(

View file

@ -36,7 +36,7 @@ Future syncFlameCounters() async {
// only sync when flame counter is higher than three days // only sync when flame counter is higher than three days
if (flameCounter < 1 && bestFriend.userId != contact.userId) continue; if (flameCounter < 1 && bestFriend.userId != contact.userId) continue;
encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
contact.userId, contact.userId,
my.MessageJson( my.MessageJson(

View file

@ -124,7 +124,7 @@ Future setupNotificationWithUsers({bool force = false}) async {
} }
Future sendNewPushKey(int userId, PushKeyMeta pushKey) async { Future sendNewPushKey(int userId, PushKeyMeta pushKey) async {
await encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
userId, userId,
my.MessageJson( my.MessageJson(

View file

@ -89,6 +89,17 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
Map<int, List<Message>> tmpEmojiReactionsToMessageId = {}; Map<int, List<Message>> tmpEmojiReactionsToMessageId = {};
List<int> openedMessageOtherIds = []; List<int> openedMessageOtherIds = [];
Map<int, int> messageOtherMessageIdToMyMessageId = {};
/// there is probably a better way...
for (Message msg in msgs) {
if (msg.messageOtherId != null) {
messageOtherMessageIdToMyMessageId[msg.messageOtherId!] =
msg.messageId;
}
}
for (Message msg in msgs) { for (Message msg in msgs) {
if (msg.kind == MessageKind.textMessage && if (msg.kind == MessageKind.textMessage &&
msg.messageOtherId != null && msg.messageOtherId != null &&
@ -96,8 +107,9 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
openedMessageOtherIds.add(msg.messageOtherId!); openedMessageOtherIds.add(msg.messageOtherId!);
} }
int? responseId = int? responseId = msg.responseToMessageId ??
msg.responseToMessageId ?? msg.responseToOtherMessageId; messageOtherMessageIdToMyMessageId[msg.responseToOtherMessageId];
if (responseId != null) { if (responseId != null) {
bool added = false; bool added = false;
MessageContent? content = MessageContent? content =
@ -243,9 +255,11 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
children: [ children: [
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: messages.length, itemCount: messages.length + 1,
reverse: true, reverse: true,
itemExtentBuilder: (index, dimensions) { itemExtentBuilder: (index, dimensions) {
if (index == 0) return 10; // empty padding
index -= 1;
double size = 44; double size = 44;
if (messages[index].kind == MessageKind.textMessage) { if (messages[index].kind == MessageKind.textMessage) {
MessageContent? content = MessageContent.fromJson( MessageContent? content = MessageContent.fromJson(
@ -277,6 +291,10 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
return size; return size;
}, },
itemBuilder: (context, i) { itemBuilder: (context, i) {
if (i == 0) {
return Container(); // just a padding
}
i -= 1;
return ChatListEntry( return ChatListEntry(
key: Key(messages[i].messageId.toString()), key: Key(messages[i].messageId.toString()),
messages[i], messages[i],

View file

@ -204,7 +204,7 @@ class ChatListEntry extends StatelessWidget {
return; return;
} }
if (await received.existsMediaFile(message.messageId, "png")) { if (await received.existsMediaFile(message.messageId, "png")) {
encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
contact.userId, contact.userId,
MessageJson( MessageJson(
@ -291,22 +291,22 @@ class ChatListEntry extends StatelessWidget {
crossAxisAlignment: crossAxisAlignment:
right ? CrossAxisAlignment.end : CrossAxisAlignment.start, right ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [ children: [
Stack( SlidingResponse(
alignment: right ? Alignment.centerRight : Alignment.centerLeft, child: Stack(
children: [ alignment: right ? Alignment.centerRight : Alignment.centerLeft,
SlidingResponse( children: [
child: child, child,
onResponseTriggered: () { Positioned(
onResponseTriggered(message); bottom: 5,
}, left: 5,
), right: 5,
Positioned( child: getReactionRow(),
bottom: 5, ),
left: 5, ],
right: 5, ),
child: getReactionRow(), onResponseTriggered: () {
), onResponseTriggered(message);
], },
), ),
getTextResponseColumns(context, !right) getTextResponseColumns(context, !right)
], ],

View file

@ -132,8 +132,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
Future loadCurrentMediaFile({bool showTwonly = false}) async { Future loadCurrentMediaFile({bool showTwonly = false}) async {
if (!isMounted) return; if (!isMounted) return;
await _noScreenshot.screenshotOff();
if (!context.mounted || allMediaFiles.isEmpty) return nextMediaOrExit(); if (!context.mounted || allMediaFiles.isEmpty) return nextMediaOrExit();
await _noScreenshot.screenshotOff();
setState(() { setState(() {
videoController = null; videoController = null;
@ -230,6 +230,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
}); });
} }
} }
imageBytes = await getImageBytes(current.messageId); imageBytes = await getImageBytes(current.messageId);
if ((imageBytes == null && !content.isVideo) || if ((imageBytes == null && !content.isVideo) ||
@ -300,7 +301,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
allMediaFiles.first.messageId, allMediaFiles.first.messageId,
MessagesCompanion(mediaStored: Value(true)), MessagesCompanion(mediaStored: Value(true)),
); );
encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
widget.contact.userId, widget.contact.userId,
MessageJson( MessageJson(

View file

@ -95,7 +95,7 @@ class _SearchUsernameView extends State<SearchUsernameView> {
if (await SignalHelper.addNewContact(res.value.userdata)) { if (await SignalHelper.addNewContact(res.value.userdata)) {
// before notifying the other party, add // before notifying the other party, add
await setupNotificationWithUsers(); await setupNotificationWithUsers();
encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
res.value.userdata.userId.toInt(), res.value.userdata.userId.toInt(),
MessageJson( MessageJson(
@ -267,7 +267,7 @@ class _ContactsListViewState extends State<ContactsListView> {
onPressed: () async { onPressed: () async {
await twonlyDatabase.contactsDao await twonlyDatabase.contactsDao
.deleteContactByUserId(contact.userId); .deleteContactByUserId(contact.userId);
encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
contact.userId, contact.userId,
MessageJson( MessageJson(
@ -285,7 +285,7 @@ class _ContactsListViewState extends State<ContactsListView> {
final update = ContactsCompanion(accepted: Value(true)); final update = ContactsCompanion(accepted: Value(true));
await twonlyDatabase.contactsDao await twonlyDatabase.contactsDao
.updateContact(contact.userId, update); .updateContact(contact.userId, update);
await encryptAndSendMessage( await encryptAndSendMessageAsync(
null, null,
contact.userId, contact.userId,
MessageJson( MessageJson(