mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 11:18:41 +00:00
fix multiple race condition problems
This commit is contained in:
parent
6d67a16840
commit
e005d01177
12 changed files with 76 additions and 31 deletions
|
|
@ -41,7 +41,6 @@ void main() async {
|
|||
|
||||
apiProvider = ApiProvider();
|
||||
twonlyDatabase = TwonlyDatabase();
|
||||
setupNotificationWithUsers();
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:provider/provider.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/connection_state.dart';
|
||||
import 'package:twonly/src/providers/settings_change_provider.dart';
|
||||
import 'package:twonly/src/services/notification_service.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/onboarding/onboarding_view.dart';
|
||||
import 'package:twonly/src/views/home_view.dart';
|
||||
|
|
@ -44,6 +45,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||
setState(() {
|
||||
_isConnected = isConnected;
|
||||
});
|
||||
setupNotificationWithUsers();
|
||||
};
|
||||
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
|
|
|
|||
|
|
@ -182,8 +182,9 @@ Future sendTextMessage(
|
|||
}
|
||||
|
||||
Future notifyContactAboutOpeningMessage(
|
||||
int fromUserId, int messageOtherId) async {
|
||||
encryptAndSendMessage(
|
||||
int fromUserId, List<int> messageOtherIds) async {
|
||||
for (final messageOtherId in messageOtherIds) {
|
||||
await encryptAndSendMessage(
|
||||
null,
|
||||
fromUserId,
|
||||
MessageJson(
|
||||
|
|
@ -193,6 +194,7 @@ Future notifyContactAboutOpeningMessage(
|
|||
timestamp: DateTime.now(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future notifyContactsAboutProfileChange() async {
|
||||
|
|
|
|||
|
|
@ -462,7 +462,8 @@ Future<Uint8List?> getDownloadedMedia(
|
|||
if (media == null) return null;
|
||||
|
||||
// await userOpenedOtherMessage(otherUserId, messageOtherId);
|
||||
notifyContactAboutOpeningMessage(message.contactId, message.messageOtherId!);
|
||||
notifyContactAboutOpeningMessage(
|
||||
message.contactId, [message.messageOtherId!]);
|
||||
twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||
message.messageId, MessagesCompanion(openedAt: Value(DateTime.now())));
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,16 @@ import 'package:twonly/src/services/notification_service.dart';
|
|||
// ignore: library_prefixes
|
||||
import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
||||
|
||||
bool isBlocked = false;
|
||||
|
||||
Future handleServerMessage(server.ServerToClient msg) async {
|
||||
client.Response? response;
|
||||
int maxCounter = 0; // only block for 2 seconds
|
||||
while (isBlocked && maxCounter < 200) {
|
||||
await Future.delayed(Duration(milliseconds: 10));
|
||||
maxCounter += 1;
|
||||
}
|
||||
isBlocked = true;
|
||||
|
||||
try {
|
||||
if (msg.v0.hasRequestNewPreKeys()) {
|
||||
|
|
@ -38,12 +46,14 @@ Future handleServerMessage(server.ServerToClient msg) async {
|
|||
} else {
|
||||
Logger("handleServerMessage")
|
||||
.shout("Got a new message from the server: $msg");
|
||||
return;
|
||||
response = client.Response()..error = ErrorCode.InternalError;
|
||||
}
|
||||
} catch (e) {
|
||||
response = client.Response()..error = ErrorCode.InternalError;
|
||||
}
|
||||
|
||||
isBlocked = false;
|
||||
|
||||
var v0 = client.V0()
|
||||
..seq = msg.v0.seq
|
||||
..response = response;
|
||||
|
|
@ -68,17 +78,19 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
|||
|
||||
if (messageId == null) {
|
||||
Logger("server_messages")
|
||||
.info("download data received, but unknown messageID");
|
||||
.shout("download data received, but unknown messageID");
|
||||
// answers with ok, so the server will delete the message
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
return client.Response()..ok = ok;
|
||||
}
|
||||
|
||||
if (data.fin && data.data.isEmpty) {
|
||||
Logger("server_messages")
|
||||
.shout("Got an image message, but was already deleted by the server!");
|
||||
// media file was deleted by the server. remove the media from device
|
||||
await twonlyDatabase.messagesDao.deleteMessageById(messageId);
|
||||
box.delete(boxId);
|
||||
box.delete("${data.downloadToken}_downloaded");
|
||||
await box.delete(boxId);
|
||||
await box.delete("${data.downloadToken}_downloaded");
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
return client.Response()..ok = ok;
|
||||
}
|
||||
|
|
@ -103,7 +115,7 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
|||
|
||||
if (!data.fin) {
|
||||
// download not finished, so waiting for more data...
|
||||
box.put(boxId, downloadedBytes);
|
||||
await box.put(boxId, downloadedBytes);
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
return client.Response()..ok = ok;
|
||||
}
|
||||
|
|
@ -138,7 +150,7 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
|||
final rawBytes =
|
||||
await xchacha20.decrypt(secretBox, secretKey: secretKeyData);
|
||||
|
||||
box.put("${data.downloadToken}_downloaded", rawBytes);
|
||||
await box.put("${data.downloadToken}_downloaded", rawBytes);
|
||||
} catch (e) {
|
||||
Logger("server_messages").info("Decryption error: $e");
|
||||
// deleting message as this is an invalid image
|
||||
|
|
@ -148,13 +160,14 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
|||
return client.Response()..ok = ok;
|
||||
}
|
||||
|
||||
Logger("server_messages").info("Downloaded: $messageId");
|
||||
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||
msg.contactId,
|
||||
messageId,
|
||||
MessagesCompanion(downloadState: Value(DownloadState.downloaded)),
|
||||
);
|
||||
|
||||
box.delete(boxId);
|
||||
await box.delete(boxId);
|
||||
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
return client.Response()..ok = ok;
|
||||
|
|
@ -201,7 +214,6 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
|||
final update = ContactsCompanion(accepted: Value(true));
|
||||
await twonlyDatabase.contactsDao.updateContact(fromUserId, update);
|
||||
notifyContactsAboutProfileChange();
|
||||
setupNotificationWithUsers();
|
||||
break;
|
||||
|
||||
case MessageKind.profileChange:
|
||||
|
|
@ -344,7 +356,6 @@ Future<client.Response> handleContactRequest(
|
|||
Result username = await apiProvider.getUsername(fromUserId);
|
||||
if (username.isSuccess) {
|
||||
Uint8List name = username.value.userdata.username;
|
||||
|
||||
await twonlyDatabase.contactsDao.insertContact(
|
||||
ContactsCompanion(
|
||||
username: Value(utf8.decode(name)),
|
||||
|
|
@ -353,6 +364,7 @@ Future<client.Response> handleContactRequest(
|
|||
),
|
||||
);
|
||||
}
|
||||
await setupNotificationWithUsers();
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
return client.Response()..ok = ok;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Future initMediaStorage() async {
|
|||
value: base64UrlEncode(key),
|
||||
);
|
||||
}
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final dir = await getApplicationSupportDirectory();
|
||||
Hive.init(dir.path);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ Future setupNotificationWithUsers({bool force = false}) async {
|
|||
key: List<int>.generate(32, (index) => random.nextInt(256)),
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
sendNewPushKey(contact.userId, pushKey);
|
||||
await sendNewPushKey(contact.userId, pushKey);
|
||||
pushKeys[contact.userId]!.keys.add(pushKey);
|
||||
pushKeys[contact.userId]!.displayName = getContactDisplayName(contact);
|
||||
wasChanged = true;
|
||||
|
|
@ -109,7 +109,7 @@ Future setupNotificationWithUsers({bool force = false}) async {
|
|||
key: List<int>.generate(32, (index) => random.nextInt(256)),
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
sendNewPushKey(contact.userId, pushKey);
|
||||
await sendNewPushKey(contact.userId, pushKey);
|
||||
final pushUser = PushUser(
|
||||
displayName: getContactDisplayName(contact),
|
||||
keys: [pushKey],
|
||||
|
|
@ -588,7 +588,7 @@ Future<String?> getAvatarIcon(Contact user) async {
|
|||
final Uint8List pngBytes = byteData!.buffer.asUint8List();
|
||||
|
||||
// Get the directory to save the image
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final directory = await getApplicationCacheDirectory();
|
||||
final avatarsDirectory = Directory('${directory.path}/avatars');
|
||||
|
||||
// Create the avatars directory if it does not exist
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ extension ShortCutsExtension on BuildContext {
|
|||
}
|
||||
|
||||
Future<void> writeLogToFile(LogRecord record) async {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final directory = await getApplicationSupportDirectory();
|
||||
final logFile = File('${directory.path}/app.log');
|
||||
|
||||
// Prepare the log message
|
||||
|
|
@ -32,7 +32,7 @@ Future<void> writeLogToFile(LogRecord record) async {
|
|||
}
|
||||
|
||||
Future<bool> deleteLogFile() async {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final directory = await getApplicationSupportDirectory();
|
||||
final logFile = File('${directory.path}/app.log');
|
||||
|
||||
if (await logFile.exists()) {
|
||||
|
|
|
|||
|
|
@ -235,12 +235,14 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
|||
// should be cleared
|
||||
Map<int, List<Message>> tmpReactionsToMyMessages = {};
|
||||
Map<int, List<Message>> tmpTeactionsToOtherMessages = {};
|
||||
|
||||
List<int> openedMessageOtherIds = [];
|
||||
for (Message msg in msgs) {
|
||||
if (msg.kind == MessageKind.textMessage &&
|
||||
msg.messageOtherId != null &&
|
||||
msg.openedAt == null) {
|
||||
updated = true;
|
||||
notifyContactAboutOpeningMessage(widget.userid, msg.messageOtherId!);
|
||||
openedMessageOtherIds.add(msg.messageOtherId!);
|
||||
}
|
||||
|
||||
if (msg.responseToMessageId != null) {
|
||||
|
|
@ -261,7 +263,11 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
|||
displayedMessages.add(msg);
|
||||
}
|
||||
}
|
||||
if (openedMessageOtherIds.isNotEmpty) {
|
||||
notifyContactAboutOpeningMessage(widget.userid, openedMessageOtherIds);
|
||||
}
|
||||
twonlyDatabase.messagesDao.openedAllNonMediaMessages(widget.userid);
|
||||
// should be fixed with that
|
||||
if (!updated) {
|
||||
// The stream should be get an update, so only update the UI when all are opened
|
||||
setState(() {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,19 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
|
||||
_subscription = messages.listen((messages) {
|
||||
for (Message msg in messages) {
|
||||
if (!allMediaFiles.any((m) => m.messageId == msg.messageId)) {
|
||||
// if (!allMediaFiles.any((m) => m.messageId == msg.messageId)) {
|
||||
// allMediaFiles.add(msg);
|
||||
// }
|
||||
// Find the index of the existing message with the same messageId
|
||||
int index =
|
||||
allMediaFiles.indexWhere((m) => m.messageId == msg.messageId);
|
||||
|
||||
if (index >= 1) {
|
||||
// to not modify the first message
|
||||
// If the message exists, replace it
|
||||
allMediaFiles[index] = msg;
|
||||
} else {
|
||||
// If the message does not exist, add it
|
||||
allMediaFiles.add(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:drift/drift.dart' hide Column;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:twonly/src/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
|
|
@ -122,6 +123,16 @@ class _SearchUsernameView extends State<SearchUsernameView> {
|
|||
onSubmitted: (_) {
|
||||
_addNewUser(context);
|
||||
},
|
||||
onChanged: (value) {
|
||||
searchUserName.text = value.toLowerCase();
|
||||
searchUserName.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: searchUserName.text.length),
|
||||
);
|
||||
},
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(12),
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[a-z0-9A-Z]')),
|
||||
],
|
||||
controller: searchUserName,
|
||||
decoration:
|
||||
getInputDecoration(context.lang.searchUsernameInput),
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class DiagnosticsView extends StatelessWidget {
|
|||
}
|
||||
|
||||
Future<String> _loadLogFile() async {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final directory = await getApplicationSupportDirectory();
|
||||
final logFile = File('${directory.path}/app.log');
|
||||
|
||||
if (await logFile.exists()) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue