mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 18:28:40 +00:00
maybe fix for #180
This commit is contained in:
parent
95ad761898
commit
a57a8051ef
9 changed files with 109 additions and 63 deletions
|
|
@ -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) {
|
||||||
|
try {
|
||||||
retransmit = jsonDecode(retransmitJson);
|
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(
|
||||||
var retransmit = await getAllMessagesForRetransmitting();
|
|
||||||
|
|
||||||
retransmit[stateId] = jsonEncode(RetransmitMessage(
|
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
pushData: pushData,
|
pushData: pushData,
|
||||||
).toJson());
|
);
|
||||||
|
|
||||||
box.put("messages-to-retransmit", jsonEncode(retransmit));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result resp = await apiProvider.sendTextMessage(userId, bytes, pushData);
|
|
||||||
|
|
||||||
if (resp.isSuccess) {
|
|
||||||
{
|
{
|
||||||
var retransmit = await getAllMessagesForRetransmitting();
|
var retransmit = await getAllMessagesForRetransmitting();
|
||||||
retransmit.remove(stateId);
|
|
||||||
|
retransmit[stateId] = jsonEncode(encryptedMessage.toJson());
|
||||||
|
|
||||||
box.put("messages-to-retransmit", jsonEncode(retransmit));
|
box.put("messages-to-retransmit", jsonEncode(retransmit));
|
||||||
}
|
}
|
||||||
if (messageId != null) {
|
|
||||||
await twonlyDatabase.messagesDao.updateMessageByMessageId(
|
|
||||||
messageId,
|
|
||||||
MessagesCompanion(acknowledgeByServer: Value(true)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp;
|
return (stateId, encryptedMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptAndSendMessage(
|
await encryptAndSendMessageAsync(
|
||||||
message.messageId!,
|
message.messageId!,
|
||||||
fromUserId,
|
fromUserId,
|
||||||
MessageJson(
|
MessageJson(
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
|
|
|
||||||
|
|
@ -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,15 +291,11 @@ class ChatListEntry extends StatelessWidget {
|
||||||
crossAxisAlignment:
|
crossAxisAlignment:
|
||||||
right ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
right ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
SlidingResponse(
|
||||||
|
child: Stack(
|
||||||
alignment: right ? Alignment.centerRight : Alignment.centerLeft,
|
alignment: right ? Alignment.centerRight : Alignment.centerLeft,
|
||||||
children: [
|
children: [
|
||||||
SlidingResponse(
|
child,
|
||||||
child: child,
|
|
||||||
onResponseTriggered: () {
|
|
||||||
onResponseTriggered(message);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 5,
|
bottom: 5,
|
||||||
left: 5,
|
left: 5,
|
||||||
|
|
@ -308,6 +304,10 @@ class ChatListEntry extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
onResponseTriggered: () {
|
||||||
|
onResponseTriggered(message);
|
||||||
|
},
|
||||||
|
),
|
||||||
getTextResponseColumns(context, !right)
|
getTextResponseColumns(context, !right)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue