drift ready for contacts and messages

This commit is contained in:
otsmr 2025-03-09 21:46:06 +01:00
parent cfd6bd92cb
commit 5866d3e7e4
21 changed files with 314 additions and 173 deletions

View file

@ -1,5 +1,9 @@
import 'package:twonly/src/database/database.dart';
import 'package:twonly/src/providers/api_provider.dart'; import 'package:twonly/src/providers/api_provider.dart';
import 'package:twonly/src/providers/db_provider.dart'; import 'package:twonly/src/providers/db_provider.dart';
late ApiProvider apiProvider; late ApiProvider apiProvider;
// uses for background notification
late DbProvider dbProvider; late DbProvider dbProvider;
late TwonlyDatabase twonlyDatabase;

View file

@ -41,16 +41,13 @@ void main() async {
await dbProvider.ready; await dbProvider.ready;
apiProvider = ApiProvider(); apiProvider = ApiProvider();
twonlyDatabase = TwonlyDatabase();
FlutterForegroundTask.initCommunicationPort(); FlutterForegroundTask.initCommunicationPort();
runApp( runApp(
MultiProvider( MultiProvider(
providers: [ providers: [
Provider<TwonlyDatabase>(
create: (context) => TwonlyDatabase(),
dispose: (context, db) => db.close(),
),
ChangeNotifierProvider(create: (_) => SendNextMediaTo()), ChangeNotifierProvider(create: (_) => SendNextMediaTo()),
ChangeNotifierProvider(create: (_) => settingsController), ChangeNotifierProvider(create: (_) => settingsController),
], ],

View file

@ -1,5 +1,6 @@
import 'dart:collection'; import 'dart:collection';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/verified_shield.dart'; import 'package:twonly/src/components/verified_shield.dart';
import 'package:twonly/src/database/contacts_db.dart'; import 'package:twonly/src/database/contacts_db.dart';
import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/database/database.dart';
@ -141,7 +142,7 @@ class UserCheckbox extends StatelessWidget {
], ],
), ),
StreamBuilder( StreamBuilder(
stream: context.db.watchFlameCounter(user.userId), stream: twonlyDatabase.watchFlameCounter(user.userId),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData && snapshot.data! != 0) { if (!snapshot.hasData && snapshot.data! != 0) {
return Container(); return Container();

View file

@ -1,5 +1,6 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'; import 'package:drift_flutter/drift_flutter.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/database/contacts_db.dart'; import 'package:twonly/src/database/contacts_db.dart';
import 'package:twonly/src/database/messages_db.dart'; import 'package:twonly/src/database/messages_db.dart';
@ -45,6 +46,11 @@ class TwonlyDatabase extends _$TwonlyDatabase {
.watch(); .watch();
} }
Future<List<Message>> getAllMessagesForRetransmitting() {
return (select(messages)..where((t) => t.acknowledgeByServer.equals(false)))
.get();
}
Future openedAllTextMessages(int contactId) { Future openedAllTextMessages(int contactId) {
final updates = MessagesCompanion(openedAt: Value(DateTime.now())); final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
return (update(messages) return (update(messages)
@ -55,12 +61,61 @@ class TwonlyDatabase extends _$TwonlyDatabase {
.write(updates); .write(updates);
} }
Future updateMessageByOtherUser(
int userId, int messageId, MessagesCompanion updatedValues) {
return (update(messages)
..where((c) =>
c.contactId.equals(userId) & c.messageId.equals(messageId)))
.write(updatedValues);
}
Future updateMessageByOtherMessageId(
int userId, int messageOtherId, MessagesCompanion updatedValues) {
return (update(messages)
..where((c) =>
c.contactId.equals(userId) &
c.messageOtherId.equals(messageOtherId)))
.write(updatedValues);
}
Future updateMessageByMessageId(
int messageId, MessagesCompanion updatedValues) {
return (update(messages)..where((c) => c.messageId.equals(messageId)))
.write(updatedValues);
}
Future<int?> insertMessage(MessagesCompanion message) async {
try {
return await into(messages).insert(message);
} catch (e) {
Logger("twonlyDatabase").shout("Error while inserting message: $e");
return null;
}
}
Future deleteMessageById(int messageId) {
return (delete(messages)..where((t) => t.messageId.equals(messageId))).go();
}
// ------------ // ------------
Future<int> insertContact(ContactsCompanion contact) { Future<int> insertContact(ContactsCompanion contact) {
return into(contacts).insert(contact); return into(contacts).insert(contact);
} }
Future incTotalMediaCounter(int contactId) async {
return (update(contacts)..where((t) => t.userId.equals(contactId)))
.write(ContactsCompanion(
totalMediaCounter: Value(
(await (select(contacts)..where((t) => t.userId.equals(contactId)))
.get())
.first
.totalMediaCounter +
1,
),
));
}
SingleOrNullSelectable<Contact> getContactByUserId(int userId) { SingleOrNullSelectable<Contact> getContactByUserId(int userId) {
return select(contacts)..where((t) => t.userId.equals(userId)); return select(contacts)..where((t) => t.userId.equals(userId));
} }

View file

@ -18,7 +18,8 @@ class Messages extends Table {
IntColumn get responseToOtherMessageId => integer().nullable()(); IntColumn get responseToOtherMessageId => integer().nullable()();
BoolColumn get acknowledgeByUser => boolean().withDefault(Constant(false))(); BoolColumn get acknowledgeByUser => boolean().withDefault(Constant(false))();
IntColumn get downloadState => intEnum<DownloadState>()(); IntColumn get downloadState => intEnum<DownloadState>()
.withDefault(Constant(DownloadState.pending.index))();
BoolColumn get acknowledgeByServer => BoolColumn get acknowledgeByServer =>
boolean().withDefault(Constant(false))(); boolean().withDefault(Constant(false))();

View file

@ -1,15 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:fixnum/fixnum.dart'; import 'package:drift/drift.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/app.dart'; import 'package:twonly/src/app.dart';
import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/database/database.dart';
import 'package:twonly/src/database/messages_db.dart';
import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/json/message.dart';
import '../../../../.blocked/archives/messages_model.dart';
import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:twonly/src/proto/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/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
@ -17,8 +16,10 @@ import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/signal.dart' as SignalHelper; import 'package:twonly/src/utils/signal.dart' as SignalHelper;
Future tryTransmitMessages() async { Future tryTransmitMessages() async {
List<DbMessage> retransmit = List<Message> retransmit =
await DbMessages.getAllMessagesForRetransmitting(); await twonlyDatabase.getAllMessagesForRetransmitting();
if (retransmit.isEmpty) return; if (retransmit.isEmpty) return;
Logger("api.dart").info("try sending messages: ${retransmit.length}"); Logger("api.dart").info("try sending messages: ${retransmit.length}");
@ -30,12 +31,16 @@ Future tryTransmitMessages() async {
Uint8List? bytes = box.get("retransmit-$msgId-textmessage"); Uint8List? bytes = box.get("retransmit-$msgId-textmessage");
if (bytes != null) { if (bytes != null) {
Result resp = await apiProvider.sendTextMessage( Result resp = await apiProvider.sendTextMessage(
retransmit[i].otherUserId, retransmit[i].contactId,
bytes, bytes,
); );
if (resp.isSuccess) { if (resp.isSuccess) {
DbMessages.acknowledgeMessageByServer(msgId); await twonlyDatabase.updateMessageByMessageId(
msgId,
MessagesCompanion(acknowledgeByServer: Value(true))
);
box.delete("retransmit-$msgId-textmessage"); box.delete("retransmit-$msgId-textmessage");
} else { } else {
// in case of error do nothing. As the message is not removed the app will try again when relaunched // in case of error do nothing. As the message is not removed the app will try again when relaunched
@ -44,9 +49,9 @@ Future tryTransmitMessages() async {
Uint8List? encryptedMedia = await box.get("retransmit-$msgId-media"); Uint8List? encryptedMedia = await box.get("retransmit-$msgId-media");
if (encryptedMedia != null) { if (encryptedMedia != null) {
final content = retransmit[i].messageContent; final content = MessageJson.fromJson(jsonDecode(retransmit[i].contentJson!)).content;
if (content is MediaMessageContent) { if (content is MediaMessageContent) {
uploadMediaFile(msgId, retransmit[i].otherUserId, encryptedMedia, uploadMediaFile(msgId, retransmit[i].contactId, encryptedMedia,
content.isRealTwonly, content.maxShowTime, retransmit[i].sendAt); content.isRealTwonly, content.maxShowTime, retransmit[i].sendAt);
} }
} }
@ -54,7 +59,7 @@ Future tryTransmitMessages() async {
} }
// 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(int userId, Message msg) async { Future<Result> encryptAndSendMessage(int userId, MessageJson msg) async {
Uint8List? bytes = await SignalHelper.encryptMessage(msg, userId); Uint8List? bytes = await SignalHelper.encryptMessage(msg, userId);
if (bytes == null) { if (bytes == null) {
@ -71,7 +76,12 @@ Future<Result> encryptAndSendMessage(int userId, Message msg) async {
if (resp.isSuccess) { if (resp.isSuccess) {
if (msg.messageId != null) { if (msg.messageId != null) {
DbMessages.acknowledgeMessageByServer(msg.messageId!);
await twonlyDatabase.updateMessageByMessageId(
msg.messageId!,
MessagesCompanion(acknowledgeByServer: Value(true))
);
box.delete("retransmit-${msg.messageId}-textmessage"); box.delete("retransmit-${msg.messageId}-textmessage");
} }
} }
@ -84,15 +94,18 @@ Future sendTextMessage(int target, String message) async {
DateTime messageSendAt = DateTime.now(); DateTime messageSendAt = DateTime.now();
int? messageId = await DbMessages.insertMyMessage( int? messageId = await twonlyDatabase.insertMessage(MessagesCompanion(
target.toInt(), contactId: Value(target),
MessageKind.textMessage, kind: Value(MessageKind.textMessage),
content, sendAt: Value(messageSendAt),
messageSendAt, downloadState: Value(DownloadState.downloaded),
); contentJson: Value(jsonEncode(content.toJson()))
),);
if (messageId == null) return; if (messageId == null) return;
Message msg = Message( MessageJson msg = MessageJson(
kind: MessageKind.textMessage, kind: MessageKind.textMessage,
messageId: messageId, messageId: messageId,
content: content, content: content,
@ -160,13 +173,19 @@ Future uploadMediaFile(
box.delete("retransmit-$messageId-media"); box.delete("retransmit-$messageId-media");
box.delete("retransmit-$messageId-uploadtoken"); box.delete("retransmit-$messageId-uploadtoken");
await DbContacts.updateTotalMediaCounter(target.toInt()); twonlyDatabase.incTotalMediaCounter(target);
twonlyDatabase.updateContact(
target,
ContactsCompanion(
lastMessageReceived: Value(messageSendAt),
),
);
// Ensures the retransmit of the message // Ensures the retransmit of the message
await encryptAndSendMessage( await encryptAndSendMessage(
target, target,
Message( MessageJson(
kind: MessageKind.image, kind: MessageKind.media,
messageId: messageId, messageId: messageId,
content: MediaMessageContent( content: MediaMessageContent(
downloadToken: uploadToken, downloadToken: uploadToken,
@ -210,17 +229,19 @@ class SendImage {
} }
messageSendAt = DateTime.now(); messageSendAt = DateTime.now();
messageId = await DbMessages.insertMyMessage( int? messageId = await twonlyDatabase.insertMessage(MessagesCompanion(
userId.toInt(), contactId: Value(userId),
MessageKind.image, kind: Value(MessageKind.media),
MediaMessageContent( sendAt: Value(messageSendAt!),
downloadState: Value(DownloadState.pending),
contentJson: Value(jsonEncode(MediaMessageContent(
downloadToken: [], downloadToken: [],
maxShowTime: maxShowTime, maxShowTime: maxShowTime,
isRealTwonly: isRealTwonly, isRealTwonly: isRealTwonly,
isVideo: false, isVideo: false,
), ).toJson()))
messageSendAt!, ));
);
// should only happen when there is no space left on the smartphone -> abort message // should only happen when there is no space left on the smartphone -> abort message
if (messageId == null) return; if (messageId == null) return;
@ -295,9 +316,15 @@ Future tryDownloadMedia(int messageId, int fromUserId, List<int> mediaToken,
if (media != null && media.isNotEmpty) { if (media != null && media.isNotEmpty) {
offset = media.length; offset = media.length;
} }
globalCallBackOnDownloadChange(mediaToken, true);
box.put("${mediaToken}_messageId", messageId); box.put("${mediaToken}_messageId", messageId);
box.put("${mediaToken}_fromUserId", fromUserId); box.put("${mediaToken}_fromUserId", fromUserId);
final update =
MessagesCompanion(downloadState: Value(DownloadState.downloading));
await twonlyDatabase.updateMessageByOtherUser(
fromUserId,
messageId,
update
);
apiProvider.triggerDownload(mediaToken, offset); apiProvider.triggerDownload(mediaToken, offset);
} }
@ -326,7 +353,12 @@ Future<Uint8List?> getDownloadedMedia(
} }
if (media == null) return null; if (media == null) return null;
await userOpenedOtherMessage(otherUserId, messageOtherId); // await userOpenedOtherMessage(otherUserId, messageOtherId);
notifyContactAboutOpeningMessage(otherUserId, messageOtherId);
twonlyDatabase.updateMessageByOtherMessageId(otherUserId, messageOtherId, MessagesCompanion(
openedAt: Value(DateTime.now())
));
box.delete(mediaToken.toString()); box.delete(mediaToken.toString());
box.put("${mediaToken}_downloaded", "deleted"); box.put("${mediaToken}_downloaded", "deleted");
box.delete("${mediaToken}_messageId"); box.delete("${mediaToken}_messageId");

View file

@ -1,13 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:drift/drift.dart';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/app.dart'; import 'package:twonly/src/app.dart';
import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/database/database.dart';
import 'package:twonly/src/database/messages_db.dart';
import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/json/message.dart';
import '../../../../.blocked/archives/messages_model.dart';
import 'package:twonly/src/proto/api/client_to_server.pb.dart' as client; import 'package:twonly/src/proto/api/client_to_server.pb.dart' as client;
import 'package:twonly/src/proto/api/client_to_server.pbserver.dart'; import 'package:twonly/src/proto/api/client_to_server.pbserver.dart';
import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:twonly/src/proto/api/error.pb.dart';
@ -27,7 +28,7 @@ Future handleServerMessage(server.ServerToClient msg) async {
response = await handleRequestNewPreKey(); response = await handleRequestNewPreKey();
} else if (msg.v0.hasNewMessage()) { } else if (msg.v0.hasNewMessage()) {
Uint8List body = Uint8List.fromList(msg.v0.newMessage.body); Uint8List body = Uint8List.fromList(msg.v0.newMessage.body);
Int64 fromUserId = msg.v0.newMessage.fromUserId; int fromUserId = msg.v0.newMessage.fromUserId.toInt();
response = await handleNewMessage(fromUserId, body); response = await handleNewMessage(fromUserId, body);
} else if (msg.v0.hasDownloaddata()) { } else if (msg.v0.hasDownloaddata()) {
response = await handleDownloadData(msg.v0.downloaddata); response = await handleDownloadData(msg.v0.downloaddata);
@ -62,18 +63,13 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
// media file was deleted by the server. remove the media from device // media file was deleted by the server. remove the media from device
if (messageId != null) { if (messageId != null) {
int? fromUserId = await DbMessages.deleteMessageById(messageId); await twonlyDatabase.deleteMessageById(messageId);
box.delete(boxId); box.delete(boxId);
if (fromUserId != null) {
globalCallBackOnMessageChange(fromUserId, messageId);
}
box.delete("${data.uploadToken}_fromUserId"); box.delete("${data.uploadToken}_fromUserId");
box.delete("${data.uploadToken}_downloaded"); box.delete("${data.uploadToken}_downloaded");
globalCallBackOnDownloadChange(data.uploadToken, false);
var ok = client.Response_Ok()..none = true; var ok = client.Response_Ok()..none = true;
return client.Response()..ok = ok; return client.Response()..ok = ok;
} else { } else {
globalCallBackOnDownloadChange(data.uploadToken, false);
var ok = client.Response_Ok()..none = true; var ok = client.Response_Ok()..none = true;
return client.Response()..ok = ok; return client.Response()..ok = ok;
} }
@ -111,9 +107,15 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
.shout("error decrypting the message: ${data.uploadToken}"); .shout("error decrypting the message: ${data.uploadToken}");
} }
final update =
MessagesCompanion(downloadState: Value(DownloadState.downloaded));
await twonlyDatabase.updateMessageByOtherUser(
fromUserId,
messageId!,
update,
);
box.delete(boxId); box.delete(boxId);
await globalCallBackOnMessageChange(fromUserId, messageId);
globalCallBackOnDownloadChange(data.uploadToken, false);
} }
} else { } else {
box.put(boxId, downloadedBytes); box.put(boxId, downloadedBytes);
@ -123,52 +125,72 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
return client.Response()..ok = ok; return client.Response()..ok = ok;
} }
Future<client.Response> handleNewMessage( Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
Int64 fromUserId, Uint8List body) async { MessageJson? message = await SignalHelper.getDecryptedText(fromUserId, body);
Message? message = await SignalHelper.getDecryptedText(fromUserId, body);
if (message != null) { if (message != null) {
switch (message.kind) { switch (message.kind) {
case MessageKind.contactRequest: case MessageKind.contactRequest:
Result username = await apiProvider.getUsername(fromUserId); Result username = await apiProvider.getUsername(fromUserId);
if (username.isSuccess) { if (username.isSuccess) {
Uint8List name = username.value.userdata.username; Uint8List name = username.value.userdata.username;
DbContacts.insertNewContact(
utf8.decode(name), fromUserId.toInt(), true); int added = await twonlyDatabase.insertContact(ContactsCompanion(
localPushNotificationNewMessage(fromUserId.toInt(), message, 999999); username: Value(utf8.decode(name)),
userId: Value(fromUserId),
requested: Value(true),
));
if (added > 0) {
localPushNotificationNewMessage(
fromUserId.toInt(),
message,
999999,
);
}
} }
break; break;
case MessageKind.opened: case MessageKind.opened:
await DbMessages.otherUserOpenedMyMessage( final update = MessagesCompanion(openedAt: Value(message.timestamp));
fromUserId.toInt(), await twonlyDatabase.updateMessageByOtherUser(
fromUserId,
message.messageId!, message.messageId!,
message.timestamp, update,
); );
break; break;
case MessageKind.rejectRequest: case MessageKind.rejectRequest:
DbContacts.deleteUser(fromUserId.toInt()); await twonlyDatabase.deleteContactByUserId(fromUserId);
break; break;
case MessageKind.acceptRequest: case MessageKind.acceptRequest:
DbContacts.acceptUser(fromUserId.toInt()); final update = ContactsCompanion(accepted: Value(true));
twonlyDatabase.updateContact(fromUserId, update);
localPushNotificationNewMessage(fromUserId.toInt(), message, 8888888); localPushNotificationNewMessage(fromUserId.toInt(), message, 8888888);
break; break;
case MessageKind.ack: case MessageKind.ack:
DbMessages.acknowledgeMessageByUser( final update = MessagesCompanion(acknowledgeByUser: Value(true));
fromUserId.toInt(), message.messageId!); await twonlyDatabase.updateMessageByOtherUser(
fromUserId,
message.messageId!,
update,
);
break; break;
default: default:
if (message.kind != MessageKind.textMessage && if (message.kind != MessageKind.textMessage &&
message.kind != MessageKind.video && message.kind != MessageKind.media) {
message.kind != MessageKind.image) {
Logger("handleServerMessages") Logger("handleServerMessages")
.shout("Got unknown MessageKind $message"); .shout("Got unknown MessageKind $message");
} else { } else {
String content = jsonEncode(message.content.toJson()); String content = jsonEncode(message.content!.toJson());
int? messageId = await DbMessages.insertOtherMessage(
fromUserId.toInt(), final update = MessagesCompanion(
message.kind, contactId: Value(fromUserId),
message.messageId!, kind: Value(message.kind),
content, messageOtherId: Value(message.messageId),
message.timestamp); contentJson: Value(content),
sendAt: Value(message.timestamp),
);
final messageId = await twonlyDatabase.insertMessage(
update,
);
if (messageId == null) { if (messageId == null) {
return client.Response()..error = ErrorCode.InternalError; return client.Response()..error = ErrorCode.InternalError;
@ -176,7 +198,7 @@ Future<client.Response> handleNewMessage(
encryptAndSendMessage( encryptAndSendMessage(
fromUserId, fromUserId,
Message( MessageJson(
kind: MessageKind.ack, kind: MessageKind.ack,
messageId: message.messageId!, messageId: message.messageId!,
content: MessageContent(), content: MessageContent(),
@ -184,19 +206,25 @@ Future<client.Response> handleNewMessage(
), ),
); );
if (message.kind == MessageKind.video || if (message.kind == MessageKind.media) {
message.kind == MessageKind.image) { twonlyDatabase.updateContact(
await DbContacts.updateTotalMediaCounter(fromUserId.toInt()); fromUserId,
ContactsCompanion(
lastMessageReceived: Value(message.timestamp),
),
);
twonlyDatabase.incTotalMediaCounter(fromUserId);
if (!globalIsAppInBackground) { if (!globalIsAppInBackground) {
final content = message.content; final content = message.content;
if (content is MediaMessageContent) { if (content is MediaMessageContent) {
List<int> downloadToken = content.downloadToken; List<int> downloadToken = content.downloadToken;
tryDownloadMedia(messageId, fromUserId.toInt(), downloadToken); tryDownloadMedia(messageId, fromUserId, downloadToken);
} }
} }
} }
localPushNotificationNewMessage( localPushNotificationNewMessage(fromUserId, message, messageId);
fromUserId.toInt(), message, messageId);
} }
} }
} }

View file

@ -39,7 +39,7 @@ class ApiProvider {
// reconnection params // reconnection params
Timer? reconnectionTimer; Timer? reconnectionTimer;
int _reconnectionDelay = 5; // int _reconnectionDelay = 5;
final HashMap<Int64, server.ServerToClient?> messagesV0 = HashMap(); final HashMap<Int64, server.ServerToClient?> messagesV0 = HashMap();
IOWebSocketChannel? _channel; IOWebSocketChannel? _channel;
@ -68,7 +68,7 @@ class ApiProvider {
Future onConnected() async { Future onConnected() async {
await authenticate(); await authenticate();
globalCallbackConnectionState(true); globalCallbackConnectionState(true);
_reconnectionDelay = 5; // _reconnectionDelay = 5;
if (!globalIsAppInBackground) { if (!globalIsAppInBackground) {
tryTransmitMessages(); tryTransmitMessages();
@ -131,24 +131,24 @@ class ApiProvider {
void tryToReconnect() { void tryToReconnect() {
return; return;
if (globalIsAppInBackground) return; // if (globalIsAppInBackground) return;
if (reconnectionTimer != null) { // if (reconnectionTimer != null) {
reconnectionTimer!.cancel(); // reconnectionTimer!.cancel();
} // }
final int randomDelay = Random().nextInt(20); // final int randomDelay = Random().nextInt(20);
final int delay = _reconnectionDelay + randomDelay; // final int delay = _reconnectionDelay + randomDelay;
debugPrint("Delay reconnection $delay"); // debugPrint("Delay reconnection $delay");
reconnectionTimer = Timer(Duration(seconds: delay), () async { // reconnectionTimer = Timer(Duration(seconds: delay), () async {
// increase delay but set a maximum of 60 seconds (including the random delay) // // increase delay but set a maximum of 60 seconds (including the random delay)
_reconnectionDelay = _reconnectionDelay * 2; // _reconnectionDelay = _reconnectionDelay * 2;
if (_reconnectionDelay > 40) { // if (_reconnectionDelay > 40) {
_reconnectionDelay = 40; // _reconnectionDelay = 40;
} // }
await connect(); // await connect();
}); // });
} }
void _onData(dynamic msgBuffer) { void _onData(dynamic msgBuffer) {
@ -289,7 +289,7 @@ class ApiProvider {
..username = username ..username = username
..publicIdentityKey = ..publicIdentityKey =
(await signalStore.getIdentityKeyPair()).getPublicKey().serialize() (await signalStore.getIdentityKeyPair()).getPublicKey().serialize()
..registrationId = signalIdentity.registrationId ..registrationId = Int64(signalIdentity.registrationId)
..signedPrekey = signedPreKey.getKeyPair().publicKey.serialize() ..signedPrekey = signedPreKey.getKeyPair().publicKey.serialize()
..signedPrekeySignature = signedPreKey.signature ..signedPrekeySignature = signedPreKey.signature
..signedPrekeyId = Int64(signedPreKey.id); ..signedPrekeyId = Int64(signedPreKey.id);

View file

@ -65,8 +65,6 @@ Future initFCMService() async {
}); });
} }
late TwonlyDatabase bgTwonlyDB;
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// Wenn Tasks länger als 30 Sekunden ausgeführt werden, wird der Prozess möglicherweise automatisch vom Gerät beendet. // Wenn Tasks länger als 30 Sekunden ausgeführt werden, wird der Prozess möglicherweise automatisch vom Gerät beendet.
@ -75,7 +73,7 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
Logger("firebase-background") Logger("firebase-background")
.shout('Handling a background message: ${message.messageId}'); .shout('Handling a background message: ${message.messageId}');
bgTwonlyDB = TwonlyDatabase(); twonlyDatabase = TwonlyDatabase();
apiProvider = ApiProvider(); apiProvider = ApiProvider();
await apiProvider.connect(); await apiProvider.connect();

View file

@ -3,6 +3,8 @@ import 'dart:io';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/contacts_db.dart';
import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/database/database.dart';
import 'package:twonly/src/model/json/message.dart' as my; import 'package:twonly/src/model/json/message.dart' as my;
@ -164,10 +166,9 @@ String getPushNotificationText(String key, String userName) {
} }
Future localPushNotificationNewMessage( Future localPushNotificationNewMessage(
int fromUserId, my.Message message, int messageId) async { int fromUserId, my.MessageJson message, int messageId) async {
Contact? user = await TwonlyDatabase.provider Contact? user =
.getContactByUserId(fromUserId) await twonlyDatabase.getContactByUserId(fromUserId).getSingleOrNull();
.getSingleOrNull();
if (user == null) return; if (user == null) return;
@ -176,23 +177,25 @@ Future localPushNotificationNewMessage(
final content = message.content; final content = message.content;
if (content is my.TextMessageContent) { if (content is my.TextMessageContent) {
msg = getPushNotificationText("newTextMessage", user.displayName); msg =
getPushNotificationText("newTextMessage", getContactDisplayName(user));
} else if (content is my.MediaMessageContent) { } else if (content is my.MediaMessageContent) {
if (content.isRealTwonly) { if (content.isRealTwonly) {
msg = getPushNotificationText("newTwonly", user.displayName); msg = getPushNotificationText("newTwonly", getContactDisplayName(user));
} else if (content.isVideo) { } else if (content.isVideo) {
msg = getPushNotificationText("newVideo", user.displayName); msg = getPushNotificationText("newVideo", getContactDisplayName(user));
} else { } else {
msg = getPushNotificationText("newImage", user.displayName); msg = getPushNotificationText("newImage", getContactDisplayName(user));
} }
} }
if (message.kind == my.MessageKind.contactRequest) { if (message.kind == my.MessageKind.contactRequest) {
msg = getPushNotificationText("contactRequest", user.displayName); msg =
getPushNotificationText("contactRequest", getContactDisplayName(user));
} }
if (message.kind == my.MessageKind.acceptRequest) { if (message.kind == my.MessageKind.acceptRequest) {
msg = getPushNotificationText("acceptRequest", user.displayName); msg = getPushNotificationText("acceptRequest", getContactDisplayName(user));
} }
if (msg == "") { if (msg == "") {
@ -213,7 +216,7 @@ Future localPushNotificationNewMessage(
NotificationDetails(android: androidNotificationDetails); NotificationDetails(android: androidNotificationDetails);
await flutterLocalNotificationsPlugin.show( await flutterLocalNotificationsPlugin.show(
messageId, messageId,
user.displayName, getContactDisplayName(user),
msg, msg,
notificationDetails, notificationDetails,
payload: message.kind.index.toString(), payload: message.kind.index.toString(),

View file

@ -195,7 +195,7 @@ List<Uint8List>? removeLastFourBytes(Uint8List original) {
return [newList, lastFourBytes]; return [newList, lastFourBytes];
} }
Future<Uint8List?> encryptBytes(Uint8List bytes, Int64 target) async { Future<Uint8List?> encryptBytes(Uint8List bytes, int target) async {
try { try {
ConnectSignalProtocolStore signalStore = (await getSignalStore())!; ConnectSignalProtocolStore signalStore = (await getSignalStore())!;
@ -248,7 +248,7 @@ Future<Uint8List?> decryptBytes(Uint8List bytes, int target) async {
} }
} }
Future<Uint8List?> encryptMessage(Message msg, int target) async { Future<Uint8List?> encryptMessage(MessageJson msg, int target) async {
try { try {
ConnectSignalProtocolStore signalStore = (await getSignalStore())!; ConnectSignalProtocolStore signalStore = (await getSignalStore())!;
@ -269,7 +269,7 @@ Future<Uint8List?> encryptMessage(Message msg, int target) async {
} }
} }
Future<Message?> getDecryptedText(Int64 source, Uint8List msg) async { Future<MessageJson?> getDecryptedText(int source, Uint8List msg) async {
try { try {
ConnectSignalProtocolStore signalStore = (await getSignalStore())!; ConnectSignalProtocolStore signalStore = (await getSignalStore())!;
@ -293,8 +293,8 @@ Future<Message?> getDecryptedText(Int64 source, Uint8List msg) async {
} else { } else {
return null; return null;
} }
Message dectext = MessageJson dectext =
Message.fromJson(jsonDecode(utf8.decode(gzip.decode(plaintext)))); MessageJson.fromJson(jsonDecode(utf8.decode(gzip.decode(plaintext))));
return dectext; return dectext;
} catch (e) { } catch (e) {
Logger("utils/signal").shout(e.toString()); Logger("utils/signal").shout(e.toString());

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/image_editor/action_button.dart'; import 'package:twonly/src/components/image_editor/action_button.dart';
import 'package:twonly/src/components/media_view_sizing.dart'; import 'package:twonly/src/components/media_view_sizing.dart';
import 'package:twonly/src/components/notification_badge.dart'; import 'package:twonly/src/components/notification_badge.dart';
@ -55,7 +56,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
Future updateAsync(int userId) async { Future updateAsync(int userId) async {
if (sendNextMediaToUserName != null) return; if (sendNextMediaToUserName != null) return;
Contact? contact = Contact? contact =
await context.db.getContactByUserId(userId).getSingleOrNull(); await twonlyDatabase.getContactByUserId(userId).getSingleOrNull();
if (contact != null) { if (contact != null) {
sendNextMediaToUserName = getContactDisplayName(contact); sendNextMediaToUserName = getContactDisplayName(contact);
} }

View file

@ -1,17 +1,18 @@
import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/best_friends_selector.dart'; import 'package:twonly/src/components/best_friends_selector.dart';
import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/headline.dart'; import 'package:twonly/src/components/headline.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/components/verified_shield.dart'; import 'package:twonly/src/components/verified_shield.dart';
import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/database/contacts_db.dart';
import 'package:twonly/src/database/database.dart';
import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/api/api.dart';
import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/providers/send_next_media_to.dart'; import 'package:twonly/src/providers/send_next_media_to.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/home_view.dart'; import 'package:twonly/src/views/home_view.dart';
@ -31,43 +32,55 @@ class ShareImageView extends StatefulWidget {
} }
class _ShareImageView extends State<ShareImageView> { class _ShareImageView extends State<ShareImageView> {
List<Contact> _users = []; List<Contact> contacts = [];
List<Contact> _otherUsers = []; List<Contact> _otherUsers = [];
List<Contact> _bestFriends = []; List<Contact> _bestFriends = [];
int maxTotalMediaCounter = 0; int maxTotalMediaCounter = 0;
Uint8List? imageBytes; Uint8List? imageBytes;
bool sendingImage = false; bool sendingImage = false;
final HashSet<Int64> _selectedUserIds = HashSet<Int64>(); final HashSet<int> _selectedUserIds = HashSet<int>();
final TextEditingController searchUserName = TextEditingController(); final TextEditingController searchUserName = TextEditingController();
bool showRealTwonlyWarning = false; bool showRealTwonlyWarning = false;
late StreamSubscription<List<Contact>> contactSub;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadAsync();
}
Future<void> _loadAsync() async {
int? sendNextMediaToUserId = int? sendNextMediaToUserId =
context.read<SendNextMediaTo>().sendNextMediaToUserId; context.read<SendNextMediaTo>().sendNextMediaToUserId;
if (sendNextMediaToUserId != null) { if (sendNextMediaToUserId != null) {
_selectedUserIds.add(Int64(sendNextMediaToUserId)); _selectedUserIds.add(sendNextMediaToUserId);
}
_users = await DbContacts.getActiveUsers();
_updateUsers(_users);
imageBytes = await widget.imageBytesFuture;
setState(() {});
} }
Future _updateUsers(List<Contact> users) async { Stream<List<Contact>> allContacts =
Map<int, int> flameCounters = twonlyDatabase.watchContactsForChatList();
context.read<MessagesChangeProvider>().flamesCounter;
contactSub = allContacts.listen((allContacts) {
setState(() {
contacts = allContacts;
});
updateUsers(allContacts);
});
//_users = await DbContacts.getActiveUsers();
// _updateUsers(_users);
// imageBytes = await widget.imageBytesFuture;
// setState(() {});
}
@override
void dispose() {
super.dispose();
contactSub.cancel();
}
Future updateUsers(List<Contact> users) async {
// Sort contacts by flameCounter and then by totalMediaCounter // Sort contacts by flameCounter and then by totalMediaCounter
users.sort((a, b) { users.sort((a, b) {
// First, compare by flameCounter // First, compare by flameCounter
int flameComparison = (flameCounters[b.userId.toInt()] ?? 0) int flameComparison = (getFlameCounterFromContact(b))
.compareTo((flameCounters[a.userId.toInt()] ?? 0)); .compareTo((getFlameCounterFromContact(a)));
if (flameComparison != 0) { if (flameComparison != 0) {
return flameComparison; // Sort by flameCounter in descending order return flameComparison; // Sort by flameCounter in descending order
} }
@ -87,8 +100,7 @@ class _ShareImageView extends State<ShareImageView> {
List<Contact> otherUsers = []; List<Contact> otherUsers = [];
for (var contact in users) { for (var contact in users) {
if ((flameCounters[contact.userId.toInt()] ?? 0) > 0 && if ((getFlameCounterFromContact(contact)) > 0 && bestFriends.length < 6) {
bestFriends.length < 6) {
bestFriends.add(contact); bestFriends.add(contact);
} else { } else {
otherUsers.add(contact); otherUsers.add(contact);
@ -103,19 +115,20 @@ class _ShareImageView extends State<ShareImageView> {
Future _filterUsers(String query) async { Future _filterUsers(String query) async {
if (query.isEmpty) { if (query.isEmpty) {
_updateUsers(_users); updateUsers(contacts);
return; return;
} }
List<Contact> usersFiltered = _users List<Contact> usersFiltered = contacts
.where((user) => .where((user) => getContactDisplayName(user)
user.displayName.toLowerCase().contains(query.toLowerCase())) .toLowerCase()
.contains(query.toLowerCase()))
.toList(); .toList();
_updateUsers(usersFiltered); updateUsers(usersFiltered);
} }
void updateStatus(Int64 userId, bool checked) { void updateStatus(int userId, bool checked) {
if (widget.isRealTwonly) { if (widget.isRealTwonly) {
Contact user = _users.firstWhere((x) => x.userId == userId); Contact user = contacts.firstWhere((x) => x.userId == userId);
if (!user.verified) { if (!user.verified) {
showRealTwonlyWarning = true; showRealTwonlyWarning = true;
setState(() {}); setState(() {});
@ -248,26 +261,24 @@ class UserList extends StatelessWidget {
required this.updateStatus, required this.updateStatus,
required this.isRealTwonly, required this.isRealTwonly,
}); });
final Function(Int64, bool) updateStatus; final Function(int, bool) updateStatus;
final List<Contact> users; final List<Contact> users;
final int maxTotalMediaCounter; final int maxTotalMediaCounter;
final bool isRealTwonly; final bool isRealTwonly;
final HashSet<Int64> selectedUserIds; final HashSet<int> selectedUserIds;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Step 1: Sort the users alphabetically // Step 1: Sort the users alphabetically
users.sort((a, b) => a.displayName.compareTo(b.displayName)); users.sort(
(a, b) => getContactDisplayName(a).compareTo(getContactDisplayName(b)));
Map<int, int> flameCounters =
context.watch<MessagesChangeProvider>().flamesCounter;
return ListView.builder( return ListView.builder(
restorationId: 'new_message_users_list', restorationId: 'new_message_users_list',
itemCount: users.length, itemCount: users.length,
itemBuilder: (BuildContext context, int i) { itemBuilder: (BuildContext context, int i) {
Contact user = users[i]; Contact user = users[i];
int flameCounter = flameCounters[user.userId.toInt()] ?? 0; int flameCounter = getFlameCounterFromContact(user);
return ListTile( return ListTile(
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.start, // Center horizontally mainAxisAlignment: MainAxisAlignment.start, // Center horizontally
@ -278,7 +289,7 @@ class UserList extends StatelessWidget {
padding: const EdgeInsets.only(right: 1), padding: const EdgeInsets.only(right: 1),
child: VerifiedShield(user), child: VerifiedShield(user),
), ),
Text(user.displayName), Text(getContactDisplayName(user)),
if (flameCounter >= 1) if (flameCounter >= 1)
FlameCounterWidget( FlameCounterWidget(
user, user,
@ -289,7 +300,7 @@ class UserList extends StatelessWidget {
], ],
), ),
leading: InitialsAvatar( leading: InitialsAvatar(
displayName: user.displayName, getContactDisplayName(user),
fontSize: 15, fontSize: 15,
), ),
trailing: Checkbox( trailing: Checkbox(

View file

@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/animate_icon.dart'; import 'package:twonly/src/components/animate_icon.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/components/message_send_state_icon.dart'; import 'package:twonly/src/components/message_send_state_icon.dart';
@ -164,7 +165,7 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
} }
Future initStreams() async { Future initStreams() async {
Stream<Contact> contact = context.db.watchContact(widget.userid); Stream<Contact> contact = twonlyDatabase.watchContact(widget.userid);
userSub = contact.listen((contact) { userSub = contact.listen((contact) {
setState(() { setState(() {
user = contact; user = contact;
@ -172,7 +173,7 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
}); });
Stream<List<Message>> msgStream = Stream<List<Message>> msgStream =
context.db.watchAllMessagesFrom(widget.userid); twonlyDatabase.watchAllMessagesFrom(widget.userid);
messageSub = msgStream.listen((msgs) { messageSub = msgStream.listen((msgs) {
if (!context.mounted) return; if (!context.mounted) return;
var updated = false; var updated = false;
@ -186,7 +187,7 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
} }
} }
if (updated) { if (updated) {
context.db.openedAllTextMessages(widget.userid); twonlyDatabase.openedAllTextMessages(widget.userid);
} else { } else {
// The stream should be get an update, so only update the UI when all are opened // The stream should be get an update, so only update the UI when all are opened
setState(() { setState(() {

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/components/message_send_state_icon.dart'; import 'package:twonly/src/components/message_send_state_icon.dart';
@ -32,7 +33,7 @@ class ChatListView extends StatefulWidget {
class _ChatListViewState extends State<ChatListView> { class _ChatListViewState extends State<ChatListView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Stream<List<Contact>> contacts = context.db.watchContactsForChatList(); Stream<List<Contact>> contacts = twonlyDatabase.watchContactsForChatList();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -50,7 +51,7 @@ class _ChatListViewState extends State<ChatListView> {
// title: // title:
actions: [ actions: [
StreamBuilder( StreamBuilder(
stream: context.db.watchContactsRequested(), stream: twonlyDatabase.watchContactsRequested(),
builder: (context, snapshot) { builder: (context, snapshot) {
var count = 0; var count = 0;
if (snapshot.hasData && snapshot.data != null) { if (snapshot.hasData && snapshot.data != null) {
@ -193,8 +194,8 @@ class _UserListItem extends State<UserListItem> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final notOpenedMessages = final notOpenedMessages =
context.db.watchMessageNotOpened(widget.user.userId); twonlyDatabase.watchMessageNotOpened(widget.user.userId);
final lastMessage = context.db.watchLastMessage(widget.user.userId); final lastMessage = twonlyDatabase.watchLastMessage(widget.user.userId);
// if (widget.lastMessage != null) { // if (widget.lastMessage != null) {
// state = widget.lastMessage!.getSendState(); // state = widget.lastMessage!.getSendState();

View file

@ -6,6 +6,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
import 'package:no_screenshot/no_screenshot.dart'; import 'package:no_screenshot/no_screenshot.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/animate_icon.dart'; import 'package:twonly/src/components/animate_icon.dart';
import 'package:twonly/src/components/media_view_sizing.dart'; import 'package:twonly/src/components/media_view_sizing.dart';
import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/database/database.dart';
@ -56,7 +57,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
Future asyncLoadNextMedia() async { Future asyncLoadNextMedia() async {
Stream<List<Message>> messages = Stream<List<Message>> messages =
context.db.watchMessageNotOpened(widget.userId); twonlyDatabase.watchMessageNotOpened(widget.userId);
_subscription = messages.listen((messages) { _subscription = messages.listen((messages) {
for (Message msg in messages) { for (Message msg in messages) {

View file

@ -42,7 +42,7 @@ class _SearchUsernameView extends State<SearchUsernameView> {
return; return;
} }
int added = await context.db.insertContact(ContactsCompanion( int added = await twonlyDatabase.insertContact(ContactsCompanion(
username: Value(searchUserName.text), username: Value(searchUserName.text),
userId: Value(res.value.userdata.userId), userId: Value(res.value.userdata.userId),
requested: Value(false), requested: Value(false),
@ -89,7 +89,7 @@ class _SearchUsernameView extends State<SearchUsernameView> {
); );
} }
Stream<List<Contact>> contacts = context.db.watchNotAcceptedContacts(); Stream<List<Contact>> contacts = twonlyDatabase.watchNotAcceptedContacts();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -189,7 +189,8 @@ class _ContactsListViewState extends State<ContactsListView> {
color: const Color.fromARGB(164, 244, 67, 54)), color: const Color.fromARGB(164, 244, 67, 54)),
onPressed: () async { onPressed: () async {
final update = ContactsCompanion(blocked: Value(true)); final update = ContactsCompanion(blocked: Value(true));
await context.db.updateContact(contact.userId, update); await twonlyDatabase.updateContact(
contact.userId, update);
}, },
), ),
), ),
@ -198,7 +199,8 @@ class _ContactsListViewState extends State<ContactsListView> {
child: IconButton( child: IconButton(
icon: Icon(Icons.close, color: Colors.red), icon: Icon(Icons.close, color: Colors.red),
onPressed: () async { onPressed: () async {
await context.db.deleteContactByUserId(contact.userId); await twonlyDatabase
.deleteContactByUserId(contact.userId);
encryptAndSendMessage( encryptAndSendMessage(
contact.userId, contact.userId,
MessageJson( MessageJson(
@ -214,7 +216,7 @@ class _ContactsListViewState extends State<ContactsListView> {
icon: Icon(Icons.check, color: Colors.green), icon: Icon(Icons.check, color: Colors.green),
onPressed: () async { onPressed: () async {
final update = ContactsCompanion(accepted: Value(true)); final update = ContactsCompanion(accepted: Value(true));
await context.db.updateContact(contact.userId, update); await twonlyDatabase.updateContact(contact.userId, update);
encryptAndSendMessage( encryptAndSendMessage(
contact.userId, contact.userId,
MessageJson( MessageJson(

View file

@ -3,6 +3,7 @@ import 'package:drift/drift.dart' hide Column;
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/format_long_string.dart'; import 'package:twonly/src/components/format_long_string.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/database/contacts_db.dart'; import 'package:twonly/src/database/contacts_db.dart';
@ -36,7 +37,7 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Stream<Contact?> contact = context.db Stream<Contact?> contact = twonlyDatabase
.getContactByUserId(widget.contact.userId) .getContactByUserId(widget.contact.userId)
.watchSingleOrNull(); .watchSingleOrNull();
@ -144,7 +145,7 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
onPressed: () { onPressed: () {
final update = final update =
ContactsCompanion(verified: Value(false)); ContactsCompanion(verified: Value(false));
context.db.updateContact(contact.userId, update); twonlyDatabase.updateContact(contact.userId, update);
}, },
label: Text( label: Text(
context.lang.contactVerifyNumberClearVerification), context.lang.contactVerifyNumberClearVerification),
@ -154,7 +155,7 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
icon: FaIcon(FontAwesomeIcons.shieldHeart), icon: FaIcon(FontAwesomeIcons.shieldHeart),
onPressed: () { onPressed: () {
final update = ContactsCompanion(verified: Value(true)); final update = ContactsCompanion(verified: Value(true));
context.db.updateContact(contact.userId, update); twonlyDatabase.updateContact(contact.userId, update);
}, },
label: Text(context.lang.contactVerifyNumberMarkAsVerified), label: Text(context.lang.contactVerifyNumberMarkAsVerified),
); );

View file

@ -1,5 +1,6 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/alert_dialog.dart'; import 'package:twonly/src/components/alert_dialog.dart';
import 'package:twonly/src/components/better_list_title.dart'; import 'package:twonly/src/components/better_list_title.dart';
import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/flame.dart';
@ -24,7 +25,7 @@ class _ContactViewState extends State<ContactView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Stream<Contact?> contact = Stream<Contact?> contact =
context.db.getContactByUserId(widget.userId).watchSingleOrNull(); twonlyDatabase.getContactByUserId(widget.userId).watchSingleOrNull();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -76,7 +77,7 @@ class _ContactViewState extends State<ContactView> {
if (context.mounted && nickName != null && nickName != "") { if (context.mounted && nickName != null && nickName != "") {
final update = ContactsCompanion(nickName: Value(nickName)); final update = ContactsCompanion(nickName: Value(nickName));
context.db.updateContact(contact.userId, update); twonlyDatabase.updateContact(contact.userId, update);
} }
}, },
), ),
@ -106,7 +107,8 @@ class _ContactViewState extends State<ContactView> {
if (block) { if (block) {
final update = ContactsCompanion(blocked: Value(true)); final update = ContactsCompanion(blocked: Value(true));
if (context.mounted) { if (context.mounted) {
await context.db.updateContact(contact.userId, update); await twonlyDatabase.updateContact(
contact.userId, update);
} }
if (context.mounted) { if (context.mounted) {
Navigator.popUntil(context, (route) => route.isFirst); Navigator.popUntil(context, (route) => route.isFirst);

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/settings/privacy_view_block_users.dart'; import 'package:twonly/src/views/settings/privacy_view_block_users.dart';
@ -26,7 +27,7 @@ class _PrivacyViewState extends State<PrivacyView> {
ListTile( ListTile(
title: Text(context.lang.settingsPrivacyBlockUsers), title: Text(context.lang.settingsPrivacyBlockUsers),
subtitle: StreamBuilder( subtitle: StreamBuilder(
stream: context.db.watchContactsBlocked(), stream: twonlyDatabase.watchContactsBlocked(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data != null) { if (snapshot.hasData && snapshot.data != null) {
return Text( return Text(

View file

@ -1,5 +1,6 @@
import 'package:drift/drift.dart' hide Column; import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/database/contacts_db.dart'; import 'package:twonly/src/database/contacts_db.dart';
import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/database/database.dart';
@ -20,7 +21,7 @@ class _PrivacyViewBlockUsers extends State<PrivacyViewBlockUsers> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
allUsers = context.db.watchAllContacts(); allUsers = twonlyDatabase.watchAllContacts();
loadAsync(); loadAsync();
} }
@ -104,7 +105,7 @@ class UserList extends StatelessWidget {
Future block(BuildContext context, int userId, bool? value) async { Future block(BuildContext context, int userId, bool? value) async {
if (value != null) { if (value != null) {
final update = ContactsCompanion(blocked: Value(!value)); final update = ContactsCompanion(blocked: Value(!value));
await context.db.updateContact(userId, update); await twonlyDatabase.updateContact(userId, update);
} }
} }