mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 14:48:41 +00:00
move signal to drift
This commit is contained in:
parent
8fbb5b001c
commit
0b084f795a
57 changed files with 2100 additions and 736 deletions
|
|
@ -1,9 +1,7 @@
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_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';
|
|
||||||
|
|
||||||
late ApiProvider apiProvider;
|
late ApiProvider apiProvider;
|
||||||
|
|
||||||
// uses for background notification
|
// uses for background notification
|
||||||
late DbProvider dbProvider;
|
|
||||||
late TwonlyDatabase twonlyDatabase;
|
late TwonlyDatabase twonlyDatabase;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/providers/api_provider.dart';
|
import 'package:twonly/src/providers/api_provider.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/src/providers/db_provider.dart';
|
|
||||||
import 'package:twonly/src/providers/hive.dart';
|
import 'package:twonly/src/providers/hive.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/providers/settings_change_provider.dart';
|
import 'package:twonly/src/providers/settings_change_provider.dart';
|
||||||
|
|
@ -39,14 +37,9 @@ void main() async {
|
||||||
await initMediaStorage();
|
await initMediaStorage();
|
||||||
await initFCMService();
|
await initFCMService();
|
||||||
|
|
||||||
dbProvider = DbProvider();
|
|
||||||
await dbProvider.ready;
|
|
||||||
|
|
||||||
apiProvider = ApiProvider();
|
apiProvider = ApiProvider();
|
||||||
twonlyDatabase = TwonlyDatabase();
|
twonlyDatabase = TwonlyDatabase();
|
||||||
|
|
||||||
FlutterForegroundTask.initCommunicationPort();
|
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:cv/cv.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lottie/lottie.dart';
|
import 'package:lottie/lottie.dart';
|
||||||
|
|
||||||
|
|
@ -30,8 +29,7 @@ class EmojiAnimation extends StatelessWidget {
|
||||||
|
|
||||||
// Check if the emoji has a corresponding Lottie animation
|
// Check if the emoji has a corresponding Lottie animation
|
||||||
if (animatedIcons.containsKey(emoji)) {
|
if (animatedIcons.containsKey(emoji)) {
|
||||||
return Lottie.asset(
|
return Lottie.asset("assets/animated_icons/${animatedIcons[emoji]}");
|
||||||
"assets/animated_icons/${animatedIcons.getValue(emoji)}");
|
|
||||||
} else {
|
} else {
|
||||||
return Text(
|
return Text(
|
||||||
emoji,
|
emoji,
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import 'dart:collection';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/globals.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/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.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';
|
||||||
|
|
@ -142,7 +142,8 @@ class UserCheckbox extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
StreamBuilder(
|
StreamBuilder(
|
||||||
stream: twonlyDatabase.watchFlameCounter(user.userId),
|
stream: twonlyDatabase.contactsDao
|
||||||
|
.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();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/components/animate_icon.dart';
|
import 'package:twonly/src/components/animate_icon.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
|
||||||
class FlameCounterWidget extends StatelessWidget {
|
class FlameCounterWidget extends StatelessWidget {
|
||||||
final Contact user;
|
final Contact user;
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import 'dart:collection';
|
||||||
import 'dart:convert';
|
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:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
enum MessageSendState {
|
enum MessageSendState {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
class PermissionHandlerView extends StatefulWidget {
|
class PermissionHandlerView extends StatefulWidget {
|
||||||
|
|
@ -48,28 +45,6 @@ class PermissionHandlerViewState extends State<PermissionHandlerView> {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
// Android 12+, there are restrictions on starting a foreground service.
|
|
||||||
//
|
|
||||||
// To restart the service on device reboot or unexpected problem, you need to allow below permission.
|
|
||||||
if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
|
|
||||||
// This function requires `android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` permission.
|
|
||||||
await FlutterForegroundTask.requestIgnoreBatteryOptimization();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use this utility only if you provide services that require long-term survival,
|
|
||||||
// such as exact alarm service, healthcare service, or Bluetooth communication.
|
|
||||||
//
|
|
||||||
// This utility requires the "android.permission.SCHEDULE_EXACT_ALARM" permission.
|
|
||||||
// Using this permission may make app distribution difficult due to Google policy.
|
|
||||||
// if (!await FlutterForegroundTask.canScheduleExactAlarms) {
|
|
||||||
// When you call this function, will be gone to the settings page.
|
|
||||||
// So you need to explain to the user why set it.
|
|
||||||
// await FlutterForegroundTask.openAlarmsAndRemindersSettings();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*{Permission.camera: PermissionStatus.granted, Permission.storage: PermissionStatus.granted}*/
|
|
||||||
return statuses;
|
return statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ 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:pie_menu/pie_menu.dart';
|
import 'package:pie_menu/pie_menu.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.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/chats/chat_item_details_view.dart';
|
import 'package:twonly/src/views/chats/chat_item_details_view.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
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:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
|
||||||
class VerifiedShield extends StatelessWidget {
|
class VerifiedShield extends StatelessWidget {
|
||||||
final Contact contact;
|
final Contact contact;
|
||||||
|
|
|
||||||
|
|
@ -1,145 +1,15 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift_flutter/drift_flutter.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:twonly/src/database/contacts_db.dart';
|
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
|
||||||
|
|
||||||
part 'database.g.dart';
|
part 'contacts_dao.g.dart';
|
||||||
|
|
||||||
// You can then create a database class that includes this table
|
@DriftAccessor(tables: [Contacts])
|
||||||
@DriftDatabase(tables: [Contacts, Messages])
|
class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
|
||||||
class TwonlyDatabase extends _$TwonlyDatabase {
|
with _$ContactsDaoMixin {
|
||||||
TwonlyDatabase() : super(_openConnection());
|
// this constructor is required so that the main database can create an instance
|
||||||
|
// of this object.
|
||||||
@override
|
ContactsDao(TwonlyDatabase db) : super(db);
|
||||||
int get schemaVersion => 1;
|
|
||||||
|
|
||||||
static QueryExecutor _openConnection() {
|
|
||||||
return driftDatabase(
|
|
||||||
name: 'twonly_main_db',
|
|
||||||
native: const DriftNativeOptions(
|
|
||||||
databaseDirectory: getApplicationSupportDirectory,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------
|
|
||||||
|
|
||||||
Stream<List<Message>> watchMessageNotOpened(int contactId) {
|
|
||||||
return (select(messages)
|
|
||||||
..where((t) => t.openedAt.isNull() & t.contactId.equals(contactId))
|
|
||||||
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
|
|
||||||
.watch();
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<List<Message>> watchMediaMessageNotOpened(int contactId) {
|
|
||||||
return (select(messages)
|
|
||||||
..where((t) =>
|
|
||||||
t.openedAt.isNull() &
|
|
||||||
t.contactId.equals(contactId) &
|
|
||||||
t.kind.equals(MessageKind.media.name))
|
|
||||||
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
|
|
||||||
.watch();
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<List<Message>> watchLastMessage(int contactId) {
|
|
||||||
return (select(messages)
|
|
||||||
..where((t) => t.contactId.equals(contactId))
|
|
||||||
..orderBy([(t) => OrderingTerm.desc(t.sendAt)])
|
|
||||||
..limit(1))
|
|
||||||
.watch();
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<List<Message>> watchAllMessagesFrom(int contactId) {
|
|
||||||
return (select(messages)
|
|
||||||
..where((t) =>
|
|
||||||
t.contactId.equals(contactId) &
|
|
||||||
t.contentJson.isNotNull() &
|
|
||||||
(t.openedAt.isNull() |
|
|
||||||
t.openedAt.isBiggerThanValue(
|
|
||||||
DateTime.now().subtract(Duration(days: 1)))))
|
|
||||||
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
|
|
||||||
.watch();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future removeOldMessages() {
|
|
||||||
return (update(messages)
|
|
||||||
..where((t) =>
|
|
||||||
t.openedAt.isSmallerThanValue(
|
|
||||||
DateTime.now().subtract(Duration(days: 1))) &
|
|
||||||
t.kind.equals(MessageKind.textMessage.name)))
|
|
||||||
.write(MessagesCompanion(contentJson: Value(null)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Message>> getAllMessagesPendingDownloading() {
|
|
||||||
return (select(messages)
|
|
||||||
..where(
|
|
||||||
(t) =>
|
|
||||||
t.downloadState.equals(DownloadState.downloaded.index).not() &
|
|
||||||
t.messageOtherId.isNotNull() &
|
|
||||||
t.kind.equals(MessageKind.media.name),
|
|
||||||
))
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Message>> getAllMessagesForRetransmitting() {
|
|
||||||
return (select(messages)..where((t) => t.acknowledgeByServer.equals(false)))
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future openedAllTextMessages(int contactId) {
|
|
||||||
final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
|
|
||||||
return (update(messages)
|
|
||||||
..where((t) =>
|
|
||||||
t.contactId.equals(contactId) &
|
|
||||||
t.openedAt.isNull() &
|
|
||||||
t.kind.equals(MessageKind.textMessage.name)))
|
|
||||||
.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
SingleOrNullSelectable<Message> getMessageByMessageId(int messageId) {
|
|
||||||
return select(messages)..where((t) => t.messageId.equals(messageId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------
|
|
||||||
|
|
||||||
Future<int> insertContact(ContactsCompanion contact) async {
|
Future<int> insertContact(ContactsCompanion contact) async {
|
||||||
try {
|
try {
|
||||||
8
lib/src/database/daos/contacts_dao.g.dart
Normal file
8
lib/src/database/daos/contacts_dao.g.dart
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'contacts_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$ContactsDaoMixin on DatabaseAccessor<TwonlyDatabase> {
|
||||||
|
$ContactsTable get contacts => attachedDatabase.contacts;
|
||||||
|
}
|
||||||
127
lib/src/database/daos/messages_dao.dart
Normal file
127
lib/src/database/daos/messages_dao.dart
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
|
|
||||||
|
part 'messages_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(tables: [Messages])
|
||||||
|
class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
|
||||||
|
with _$MessagesDaoMixin {
|
||||||
|
// this constructor is required so that the main database can create an instance
|
||||||
|
// of this object.
|
||||||
|
MessagesDao(TwonlyDatabase db) : super(db);
|
||||||
|
|
||||||
|
Stream<List<Message>> watchMessageNotOpened(int contactId) {
|
||||||
|
return (select(messages)
|
||||||
|
..where((t) => t.openedAt.isNull() & t.contactId.equals(contactId))
|
||||||
|
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
|
||||||
|
.watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Message>> watchMediaMessageNotOpened(int contactId) {
|
||||||
|
return (select(messages)
|
||||||
|
..where((t) =>
|
||||||
|
t.openedAt.isNull() &
|
||||||
|
t.contactId.equals(contactId) &
|
||||||
|
t.kind.equals(MessageKind.media.name))
|
||||||
|
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
|
||||||
|
.watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Message>> watchLastMessage(int contactId) {
|
||||||
|
return (select(messages)
|
||||||
|
..where((t) => t.contactId.equals(contactId))
|
||||||
|
..orderBy([(t) => OrderingTerm.desc(t.sendAt)])
|
||||||
|
..limit(1))
|
||||||
|
.watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Message>> watchAllMessagesFrom(int contactId) {
|
||||||
|
return (select(messages)
|
||||||
|
..where((t) =>
|
||||||
|
t.contactId.equals(contactId) &
|
||||||
|
t.contentJson.isNotNull() &
|
||||||
|
(t.openedAt.isNull() |
|
||||||
|
t.openedAt.isBiggerThanValue(
|
||||||
|
DateTime.now().subtract(Duration(days: 1)))))
|
||||||
|
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
|
||||||
|
.watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future removeOldMessages() {
|
||||||
|
return (update(messages)
|
||||||
|
..where((t) =>
|
||||||
|
t.openedAt.isSmallerThanValue(
|
||||||
|
DateTime.now().subtract(Duration(days: 1))) &
|
||||||
|
t.kind.equals(MessageKind.textMessage.name)))
|
||||||
|
.write(MessagesCompanion(contentJson: Value(null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Message>> getAllMessagesPendingDownloading() {
|
||||||
|
return (select(messages)
|
||||||
|
..where(
|
||||||
|
(t) =>
|
||||||
|
t.downloadState.equals(DownloadState.downloaded.index).not() &
|
||||||
|
t.messageOtherId.isNotNull() &
|
||||||
|
t.kind.equals(MessageKind.media.name),
|
||||||
|
))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Message>> getAllMessagesForRetransmitting() {
|
||||||
|
return (select(messages)..where((t) => t.acknowledgeByServer.equals(false)))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future openedAllTextMessages(int contactId) {
|
||||||
|
final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
|
||||||
|
return (update(messages)
|
||||||
|
..where((t) =>
|
||||||
|
t.contactId.equals(contactId) &
|
||||||
|
t.openedAt.isNull() &
|
||||||
|
t.kind.equals(MessageKind.textMessage.name)))
|
||||||
|
.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleOrNullSelectable<Message> getMessageByMessageId(int messageId) {
|
||||||
|
return select(messages)..where((t) => t.messageId.equals(messageId));
|
||||||
|
}
|
||||||
|
}
|
||||||
9
lib/src/database/daos/messages_dao.g.dart
Normal file
9
lib/src/database/daos/messages_dao.g.dart
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'messages_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$MessagesDaoMixin on DatabaseAccessor<TwonlyDatabase> {
|
||||||
|
$ContactsTable get contacts => attachedDatabase.contacts;
|
||||||
|
$MessagesTable get messages => attachedDatabase.messages;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
|
||||||
class Contacts extends Table {
|
class Contacts extends Table {
|
||||||
IntColumn get userId => integer()();
|
IntColumn get userId => integer()();
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/src/database/contacts_db.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
|
|
||||||
enum DownloadState {
|
enum DownloadState {
|
||||||
pending,
|
pending,
|
||||||
|
|
@ -8,6 +8,7 @@ enum DownloadState {
|
||||||
downloaded,
|
downloaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DataClassName('Message')
|
||||||
class Messages extends Table {
|
class Messages extends Table {
|
||||||
IntColumn get contactId => integer().references(Contacts, #userId)();
|
IntColumn get contactId => integer().references(Contacts, #userId)();
|
||||||
|
|
||||||
12
lib/src/database/tables/signal_identity_key_store_table.dart
Normal file
12
lib/src/database/tables/signal_identity_key_store_table.dart
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@DataClassName('SignalIdentityKeyStore')
|
||||||
|
class SignalIdentityKeyStores extends Table {
|
||||||
|
IntColumn get deviceId => integer()();
|
||||||
|
TextColumn get name => text()();
|
||||||
|
BlobColumn get identityKey => blob()();
|
||||||
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {deviceId, name};
|
||||||
|
}
|
||||||
11
lib/src/database/tables/signal_pre_key_store_table.dart
Normal file
11
lib/src/database/tables/signal_pre_key_store_table.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@DataClassName('SignalPreKeyStore')
|
||||||
|
class SignalPreKeyStores extends Table {
|
||||||
|
IntColumn get preKeyId => integer()();
|
||||||
|
BlobColumn get preKey => blob()();
|
||||||
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {preKeyId};
|
||||||
|
}
|
||||||
10
lib/src/database/tables/signal_sender_key_store_table.dart
Normal file
10
lib/src/database/tables/signal_sender_key_store_table.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@DataClassName('SignalSenderKeyStore')
|
||||||
|
class SignalSenderKeyStores extends Table {
|
||||||
|
TextColumn get senderKeyName => text()();
|
||||||
|
BlobColumn get senderKey => blob()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {senderKeyName};
|
||||||
|
}
|
||||||
12
lib/src/database/tables/signal_session_store_table.dart
Normal file
12
lib/src/database/tables/signal_session_store_table.dart
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@DataClassName('SignalSessionStore')
|
||||||
|
class SignalSessionStores extends Table {
|
||||||
|
IntColumn get deviceId => integer()();
|
||||||
|
TextColumn get name => text()();
|
||||||
|
BlobColumn get sessionRecord => blob()();
|
||||||
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {deviceId, name};
|
||||||
|
}
|
||||||
42
lib/src/database/twonly_database.dart
Normal file
42
lib/src/database/twonly_database.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:drift_flutter/drift_flutter.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||||
|
import 'package:twonly/src/database/daos/messages_dao.dart';
|
||||||
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/signal_identity_key_store_table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/signal_pre_key_store_table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/signal_sender_key_store_table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/signal_session_store_table.dart';
|
||||||
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
|
|
||||||
|
part 'twonly_database.g.dart';
|
||||||
|
|
||||||
|
// You can then create a database class that includes this table
|
||||||
|
@DriftDatabase(tables: [
|
||||||
|
Contacts,
|
||||||
|
Messages,
|
||||||
|
SignalIdentityKeyStores,
|
||||||
|
SignalPreKeyStores,
|
||||||
|
SignalSenderKeyStores,
|
||||||
|
SignalSessionStores
|
||||||
|
], daos: [
|
||||||
|
MessagesDao,
|
||||||
|
ContactsDao
|
||||||
|
])
|
||||||
|
class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
|
TwonlyDatabase() : super(_openConnection());
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get schemaVersion => 1;
|
||||||
|
|
||||||
|
static QueryExecutor _openConnection() {
|
||||||
|
return driftDatabase(
|
||||||
|
name: 'twonly_database',
|
||||||
|
native: const DriftNativeOptions(
|
||||||
|
databaseDirectory: getApplicationSupportDirectory,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'user_data.dart';
|
part of 'userdata.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:cv/cv.dart';
|
|
||||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
|
||||||
|
|
||||||
class DbSignalIdentityKeyStore extends CvModelBase {
|
|
||||||
static const tableName = "signal_identity_key_store";
|
|
||||||
|
|
||||||
static const columnDeviceId = "device_id";
|
|
||||||
final deviceId = CvField<int>(columnDeviceId);
|
|
||||||
|
|
||||||
static const columnName = "name";
|
|
||||||
final name = CvField<String>(columnName);
|
|
||||||
|
|
||||||
static const columnIdentityKey = "identity_key";
|
|
||||||
final identityKey = CvField<Uint8List>(columnIdentityKey);
|
|
||||||
|
|
||||||
static const columnCreatedAt = "created_at";
|
|
||||||
final createdAt = CvField<DateTime>(columnCreatedAt);
|
|
||||||
|
|
||||||
static Future setupDatabaseTable(Database db) async {
|
|
||||||
String createTableString = """
|
|
||||||
CREATE TABLE IF NOT EXISTS $tableName (
|
|
||||||
$columnDeviceId INTEGER NOT NULL,
|
|
||||||
$columnName TEXT NOT NULL,
|
|
||||||
$columnIdentityKey BLOB NOT NULL,
|
|
||||||
$columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY ($columnDeviceId, $columnName)
|
|
||||||
)
|
|
||||||
""";
|
|
||||||
await db.execute(createTableString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<CvField> get fields => [deviceId, name, identityKey, createdAt];
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:cv/cv.dart';
|
|
||||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
|
||||||
|
|
||||||
class DbSignalPreKeyStore extends CvModelBase {
|
|
||||||
static const tableName = "signal_pre_key_store";
|
|
||||||
|
|
||||||
static const columnPreKeyId = "pre_key_id";
|
|
||||||
final preKeyId = CvField<int>(columnPreKeyId);
|
|
||||||
|
|
||||||
static const columnPreKey = "pre_key";
|
|
||||||
final preKey = CvField<Uint8List>(columnPreKey);
|
|
||||||
|
|
||||||
static const columnCreatedAt = "created_at";
|
|
||||||
final createdAt = CvField<DateTime>(columnCreatedAt);
|
|
||||||
|
|
||||||
static Future setupDatabaseTable(Database db) async {
|
|
||||||
String createTableString = """
|
|
||||||
CREATE TABLE IF NOT EXISTS $tableName (
|
|
||||||
$columnPreKeyId INTEGER NOT NULL,
|
|
||||||
$columnPreKey BLOB NOT NULL,
|
|
||||||
$columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY ($columnPreKeyId)
|
|
||||||
)
|
|
||||||
""";
|
|
||||||
await db.execute(createTableString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<CvField> get fields => [preKeyId, preKey, createdAt];
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:cv/cv.dart';
|
|
||||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
|
||||||
|
|
||||||
class DbSignalSenderKeyStore extends CvModelBase {
|
|
||||||
static const tableName = "signal_sender_key_store";
|
|
||||||
|
|
||||||
static const columnSenderKeyName = "sender_key_name";
|
|
||||||
final senderKeyName = CvField<String>(columnSenderKeyName);
|
|
||||||
|
|
||||||
static const columnSenderKey = "sender_key";
|
|
||||||
final senderKey = CvField<Uint8List>(columnSenderKey);
|
|
||||||
|
|
||||||
static const columnCreatedAt = "created_at";
|
|
||||||
final createdAt = CvField<DateTime>(columnCreatedAt);
|
|
||||||
|
|
||||||
static Future setupDatabaseTable(Database db) async {
|
|
||||||
String createTableString = """
|
|
||||||
CREATE TABLE IF NOT EXISTS $tableName (
|
|
||||||
$columnSenderKeyName TEXT NOT NULL,
|
|
||||||
$columnSenderKey BLOB NOT NULL,
|
|
||||||
$columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY ($columnSenderKeyName)
|
|
||||||
)
|
|
||||||
""";
|
|
||||||
await db.execute(createTableString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<CvField> get fields => [senderKeyName, senderKey, createdAt];
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:cv/cv.dart';
|
|
||||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
|
||||||
|
|
||||||
class DbSignalSessionStore extends CvModelBase {
|
|
||||||
static const tableName = "signal_session_store";
|
|
||||||
|
|
||||||
static const columnDeviceId = "device_id";
|
|
||||||
final deviceId = CvField<int>(columnDeviceId);
|
|
||||||
|
|
||||||
static const columnName = "name";
|
|
||||||
final name = CvField<String>(columnName);
|
|
||||||
|
|
||||||
static const columnSessionRecord = "session_record";
|
|
||||||
final sessionRecord = CvField<Uint8List>(columnSessionRecord);
|
|
||||||
|
|
||||||
static const columnCreatedAt = "created_at";
|
|
||||||
final createdAt = CvField<DateTime>(columnCreatedAt);
|
|
||||||
|
|
||||||
static Future setupDatabaseTable(Database db) async {
|
|
||||||
String createTableString = """
|
|
||||||
CREATE TABLE IF NOT EXISTS $tableName (
|
|
||||||
$columnDeviceId INTEGER NOT NULL,
|
|
||||||
$columnName TEXT NOT NULL,
|
|
||||||
$columnSessionRecord BLOB NOT NULL,
|
|
||||||
$columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY ($columnDeviceId, $columnName)
|
|
||||||
)
|
|
||||||
""";
|
|
||||||
await db.execute(createTableString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<CvField> get fields => [deviceId, name, sessionRecord, createdAt];
|
|
||||||
}
|
|
||||||
|
|
@ -3,9 +3,9 @@ import 'package:drift/drift.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.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/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.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/providers/hive.dart';
|
import 'package:twonly/src/providers/hive.dart';
|
||||||
|
|
@ -70,7 +70,7 @@ Future<Result> encryptAndSendMessage(
|
||||||
|
|
||||||
if (resp.isSuccess) {
|
if (resp.isSuccess) {
|
||||||
if (messageId != null) {
|
if (messageId != null) {
|
||||||
await twonlyDatabase.updateMessageByMessageId(
|
await twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||||
messageId,
|
messageId,
|
||||||
MessagesCompanion(acknowledgeByServer: Value(true)),
|
MessagesCompanion(acknowledgeByServer: Value(true)),
|
||||||
);
|
);
|
||||||
|
|
@ -86,7 +86,7 @@ Future sendTextMessage(int target, String message) async {
|
||||||
|
|
||||||
DateTime messageSendAt = DateTime.now();
|
DateTime messageSendAt = DateTime.now();
|
||||||
|
|
||||||
int? messageId = await twonlyDatabase.insertMessage(
|
int? messageId = await twonlyDatabase.messagesDao.insertMessage(
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
contactId: Value(target),
|
contactId: Value(target),
|
||||||
kind: Value(MessageKind.textMessage),
|
kind: Value(MessageKind.textMessage),
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import 'package:drift/drift.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 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
||||||
import 'package:twonly/src/providers/api/api.dart';
|
import 'package:twonly/src/providers/api/api.dart';
|
||||||
import 'package:twonly/src/providers/api/api_utils.dart';
|
import 'package:twonly/src/providers/api/api_utils.dart';
|
||||||
|
|
@ -19,7 +19,7 @@ Future tryDownloadAllMediaFiles() async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<Message> messages =
|
List<Message> messages =
|
||||||
await twonlyDatabase.getAllMessagesPendingDownloading();
|
await twonlyDatabase.messagesDao.getAllMessagesPendingDownloading();
|
||||||
|
|
||||||
for (Message message in messages) {
|
for (Message message in messages) {
|
||||||
MessageContent? content =
|
MessageContent? content =
|
||||||
|
|
@ -163,7 +163,7 @@ class ImageUploader {
|
||||||
|
|
||||||
final downloadToken = uploadState.downloadTokens.removeLast();
|
final downloadToken = uploadState.downloadTokens.removeLast();
|
||||||
|
|
||||||
twonlyDatabase.incFlameCounter(
|
twonlyDatabase.contactsDao.incFlameCounter(
|
||||||
targetUserId,
|
targetUserId,
|
||||||
false,
|
false,
|
||||||
metadata.messageSendAt,
|
metadata.messageSendAt,
|
||||||
|
|
@ -215,7 +215,7 @@ Future sendImage(
|
||||||
|
|
||||||
// at this point it is safe inform the user about the process of sending the image..
|
// at this point it is safe inform the user about the process of sending the image..
|
||||||
for (final userId in metadata.userIds) {
|
for (final userId in metadata.userIds) {
|
||||||
int? messageId = await twonlyDatabase.insertMessage(
|
int? messageId = await twonlyDatabase.messagesDao.insertMessage(
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
contactId: Value(userId),
|
contactId: Value(userId),
|
||||||
kind: Value(MessageKind.media),
|
kind: Value(MessageKind.media),
|
||||||
|
|
@ -284,7 +284,7 @@ Future tryDownloadMedia(
|
||||||
|
|
||||||
box.put("${content.downloadToken!}_messageId", messageId);
|
box.put("${content.downloadToken!}_messageId", messageId);
|
||||||
|
|
||||||
await twonlyDatabase.updateMessageByOtherUser(
|
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
messageId,
|
messageId,
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
|
|
@ -307,7 +307,7 @@ Future<Uint8List?> getDownloadedMedia(
|
||||||
|
|
||||||
// await userOpenedOtherMessage(otherUserId, messageOtherId);
|
// await userOpenedOtherMessage(otherUserId, messageOtherId);
|
||||||
notifyContactAboutOpeningMessage(message.contactId, message.messageOtherId!);
|
notifyContactAboutOpeningMessage(message.contactId, message.messageOtherId!);
|
||||||
twonlyDatabase.updateMessageByMessageId(
|
twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||||
message.messageId, MessagesCompanion(openedAt: Value(DateTime.now())));
|
message.messageId, MessagesCompanion(openedAt: Value(DateTime.now())));
|
||||||
|
|
||||||
box.delete(downloadToken.toString());
|
box.delete(downloadToken.toString());
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ 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 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.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';
|
||||||
|
|
@ -76,7 +76,7 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
||||||
|
|
||||||
if (data.fin && data.data.isEmpty) {
|
if (data.fin && data.data.isEmpty) {
|
||||||
// media file was deleted by the server. remove the media from device
|
// media file was deleted by the server. remove the media from device
|
||||||
await twonlyDatabase.deleteMessageById(messageId);
|
await twonlyDatabase.messagesDao.deleteMessageById(messageId);
|
||||||
box.delete(boxId);
|
box.delete(boxId);
|
||||||
box.delete("${data.downloadToken}_downloaded");
|
box.delete("${data.downloadToken}_downloaded");
|
||||||
var ok = client.Response_Ok()..none = true;
|
var ok = client.Response_Ok()..none = true;
|
||||||
|
|
@ -111,8 +111,9 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
||||||
// Uint8List? rawBytes =
|
// Uint8List? rawBytes =
|
||||||
// await SignalHelper.decryptBytes(downloadedBytes, fromUserId);
|
// await SignalHelper.decryptBytes(downloadedBytes, fromUserId);
|
||||||
|
|
||||||
Message? msg =
|
Message? msg = await twonlyDatabase.messagesDao
|
||||||
await twonlyDatabase.getMessageByMessageId(messageId).getSingleOrNull();
|
.getMessageByMessageId(messageId)
|
||||||
|
.getSingleOrNull();
|
||||||
if (msg == null) {
|
if (msg == null) {
|
||||||
Logger("server_messages")
|
Logger("server_messages")
|
||||||
.info("messageId not found in database. Ignoring download");
|
.info("messageId not found in database. Ignoring download");
|
||||||
|
|
@ -141,13 +142,13 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger("server_messages").info("Decryption error: $e");
|
Logger("server_messages").info("Decryption error: $e");
|
||||||
// deleting message as this is an invalid image
|
// deleting message as this is an invalid image
|
||||||
await twonlyDatabase.deleteMessageById(messageId);
|
await twonlyDatabase.messagesDao.deleteMessageById(messageId);
|
||||||
// answers with ok, so the server will delete the message
|
// answers with ok, so the server will delete the message
|
||||||
var ok = client.Response_Ok()..none = true;
|
var ok = client.Response_Ok()..none = true;
|
||||||
return client.Response()..ok = ok;
|
return client.Response()..ok = ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
await twonlyDatabase.updateMessageByOtherUser(
|
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||||
msg.contactId,
|
msg.contactId,
|
||||||
messageId,
|
messageId,
|
||||||
MessagesCompanion(downloadState: Value(DownloadState.downloaded)),
|
MessagesCompanion(downloadState: Value(DownloadState.downloaded)),
|
||||||
|
|
@ -168,7 +169,8 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
if (username.isSuccess) {
|
if (username.isSuccess) {
|
||||||
Uint8List name = username.value.userdata.username;
|
Uint8List name = username.value.userdata.username;
|
||||||
|
|
||||||
int added = await twonlyDatabase.insertContact(ContactsCompanion(
|
int added =
|
||||||
|
await twonlyDatabase.contactsDao.insertContact(ContactsCompanion(
|
||||||
username: Value(utf8.decode(name)),
|
username: Value(utf8.decode(name)),
|
||||||
userId: Value(fromUserId),
|
userId: Value(fromUserId),
|
||||||
requested: Value(true),
|
requested: Value(true),
|
||||||
|
|
@ -184,23 +186,23 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
break;
|
break;
|
||||||
case MessageKind.opened:
|
case MessageKind.opened:
|
||||||
final update = MessagesCompanion(openedAt: Value(message.timestamp));
|
final update = MessagesCompanion(openedAt: Value(message.timestamp));
|
||||||
await twonlyDatabase.updateMessageByOtherUser(
|
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
message.messageId!,
|
message.messageId!,
|
||||||
update,
|
update,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case MessageKind.rejectRequest:
|
case MessageKind.rejectRequest:
|
||||||
await twonlyDatabase.deleteContactByUserId(fromUserId);
|
await twonlyDatabase.contactsDao.deleteContactByUserId(fromUserId);
|
||||||
break;
|
break;
|
||||||
case MessageKind.acceptRequest:
|
case MessageKind.acceptRequest:
|
||||||
final update = ContactsCompanion(accepted: Value(true));
|
final update = ContactsCompanion(accepted: Value(true));
|
||||||
twonlyDatabase.updateContact(fromUserId, update);
|
twonlyDatabase.contactsDao.updateContact(fromUserId, update);
|
||||||
localPushNotificationNewMessage(fromUserId.toInt(), message, 8888888);
|
localPushNotificationNewMessage(fromUserId.toInt(), message, 8888888);
|
||||||
break;
|
break;
|
||||||
case MessageKind.ack:
|
case MessageKind.ack:
|
||||||
final update = MessagesCompanion(acknowledgeByUser: Value(true));
|
final update = MessagesCompanion(acknowledgeByUser: Value(true));
|
||||||
await twonlyDatabase.updateMessageByOtherUser(
|
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
message.messageId!,
|
message.messageId!,
|
||||||
update,
|
update,
|
||||||
|
|
@ -226,7 +228,7 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
sendAt: Value(message.timestamp),
|
sendAt: Value(message.timestamp),
|
||||||
);
|
);
|
||||||
|
|
||||||
final messageId = await twonlyDatabase.insertMessage(
|
final messageId = await twonlyDatabase.messagesDao.insertMessage(
|
||||||
update,
|
update,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -246,7 +248,7 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (message.kind == MessageKind.media) {
|
if (message.kind == MessageKind.media) {
|
||||||
twonlyDatabase.incFlameCounter(
|
twonlyDatabase.contactsDao.incFlameCounter(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
true,
|
true,
|
||||||
message.timestamp,
|
message.timestamp,
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:math';
|
|
||||||
import 'package:twonly/src/model/identity_key_store_model.dart';
|
|
||||||
import 'package:twonly/src/model/pre_key_model.dart';
|
|
||||||
import 'package:twonly/src/model/sender_key_store_model.dart';
|
|
||||||
import 'package:twonly/src/model/session_store_model.dart';
|
|
||||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
|
|
||||||
class DbProvider {
|
|
||||||
Database? db;
|
|
||||||
|
|
||||||
static const String dbName = 'twonly.db';
|
|
||||||
|
|
||||||
Future openPath(String path) async {
|
|
||||||
// Read the password for the database from the secure storage. If there is no, then create a
|
|
||||||
// new cryptographically secure random password with 32 bytes and store them in the secure storage.
|
|
||||||
// So if someone dumps the app for example using a tool like Cellebrite including the apps
|
|
||||||
// content he is not able to access the databases content.
|
|
||||||
// (https://signal.org/blog/cellebrite-and-clickbait/)
|
|
||||||
//
|
|
||||||
// CHECK: Does this actually improve the security or is this just security through obscurity and
|
|
||||||
// can be removed as it does increase complexity?
|
|
||||||
// Current thoughts: As the database password is at some point in the memory the attacker could
|
|
||||||
// just dump it. Questions here: What capabilities must the attacker have to do that?
|
|
||||||
|
|
||||||
final storage = getSecureStorage();
|
|
||||||
var password = await storage.read(key: "sqflite_database_password");
|
|
||||||
if (password == null) {
|
|
||||||
var secureRandom = Random.secure();
|
|
||||||
password = "";
|
|
||||||
for (var i = 0; i < 32; i++) {
|
|
||||||
password = "$password${String.fromCharCode(secureRandom.nextInt(256))}";
|
|
||||||
}
|
|
||||||
await storage.write(key: "sqflite_database_password", value: password);
|
|
||||||
}
|
|
||||||
|
|
||||||
db = await openDatabase(path, password: password);
|
|
||||||
if (db != null) {
|
|
||||||
await setupDatabaseTable(db!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Database?> get ready async {
|
|
||||||
if (db == null) {
|
|
||||||
await open();
|
|
||||||
}
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future setupDatabaseTable(Database db) async {
|
|
||||||
await DbSignalSessionStore.setupDatabaseTable(db);
|
|
||||||
await DbSignalPreKeyStore.setupDatabaseTable(db);
|
|
||||||
await DbSignalSenderKeyStore.setupDatabaseTable(db);
|
|
||||||
await DbSignalIdentityKeyStore.setupDatabaseTable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future open() async {
|
|
||||||
await openPath(await fixPath(dbName));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future remove() async {
|
|
||||||
await deleteDatabase(await fixPath(dbName));
|
|
||||||
// await _createDb(db!);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> fixPath(String path) async => path;
|
|
||||||
|
|
||||||
Future close() async {
|
|
||||||
await db!.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:firebase_messaging/firebase_messaging.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 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/providers/api_provider.dart';
|
import 'package:twonly/src/providers/api_provider.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ 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/globals.dart';
|
||||||
import 'package:twonly/src/database/contacts_db.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart' as my;
|
import 'package:twonly/src/json_models/message.dart' as my;
|
||||||
|
|
||||||
/// Streams are created so that app can respond to notification-related events
|
/// Streams are created so that app can respond to notification-related events
|
||||||
/// since the plugin is initialized in the `main` function
|
/// since the plugin is initialized in the `main` function
|
||||||
|
|
@ -167,8 +167,9 @@ String getPushNotificationText(String key, String userName) {
|
||||||
|
|
||||||
Future localPushNotificationNewMessage(
|
Future localPushNotificationNewMessage(
|
||||||
int fromUserId, my.MessageJson message, int messageId) async {
|
int fromUserId, my.MessageJson message, int messageId) async {
|
||||||
Contact? user =
|
Contact? user = await twonlyDatabase.contactsDao
|
||||||
await twonlyDatabase.getContactByUserId(fromUserId).getSingleOrNull();
|
.getContactByUserId(fromUserId)
|
||||||
|
.getSingleOrNull();
|
||||||
|
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/model/identity_key_store_model.dart';
|
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
bool eq<E>(List<E>? list1, List<E>? list2) =>
|
|
||||||
ListEquality<E>().equals(list1, list2);
|
|
||||||
|
|
||||||
// make it easier to read
|
|
||||||
typedef DB = DbSignalIdentityKeyStore;
|
|
||||||
|
|
||||||
class ConnectIdentityKeyStore extends IdentityKeyStore {
|
class ConnectIdentityKeyStore extends IdentityKeyStore {
|
||||||
ConnectIdentityKeyStore(this.identityKeyPair, this.localRegistrationId);
|
ConnectIdentityKeyStore(this.identityKeyPair, this.localRegistrationId);
|
||||||
|
|
@ -18,15 +12,15 @@ class ConnectIdentityKeyStore extends IdentityKeyStore {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<IdentityKey?> getIdentity(SignalProtocolAddress address) async {
|
Future<IdentityKey?> getIdentity(SignalProtocolAddress address) async {
|
||||||
var dbIdentityKey = (await dbProvider.db!.query(DB.tableName,
|
SignalIdentityKeyStore? identity =
|
||||||
columns: [DB.columnIdentityKey],
|
await (twonlyDatabase.select(twonlyDatabase.signalIdentityKeyStores)
|
||||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
..where((t) =>
|
||||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]));
|
t.deviceId.equals(address.getDeviceId()) &
|
||||||
if (dbIdentityKey.isEmpty) {
|
t.name.equals(address.getName())))
|
||||||
return null;
|
.getSingleOrNull();
|
||||||
}
|
|
||||||
Uint8List identityKey = dbIdentityKey.first.cast()[DB.columnIdentityKey];
|
if (identity == null) return null;
|
||||||
return IdentityKey.fromBytes(identityKey, 0);
|
return IdentityKey.fromBytes(identity.identityKey, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -42,7 +36,8 @@ class ConnectIdentityKeyStore extends IdentityKeyStore {
|
||||||
if (identityKey == null) {
|
if (identityKey == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return trusted == null || eq(trusted.serialize(), identityKey.serialize());
|
return trusted == null ||
|
||||||
|
ListEquality().equals(trusted.serialize(), identityKey.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -52,16 +47,23 @@ class ConnectIdentityKeyStore extends IdentityKeyStore {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (await getIdentity(address) == null) {
|
if (await getIdentity(address) == null) {
|
||||||
await dbProvider.db!.insert(DB.tableName, {
|
await twonlyDatabase.into(twonlyDatabase.signalIdentityKeyStores).insert(
|
||||||
DB.columnDeviceId: address.getDeviceId(),
|
SignalIdentityKeyStoresCompanion(
|
||||||
DB.columnName: address.getName(),
|
deviceId: Value(address.getDeviceId()),
|
||||||
DB.columnIdentityKey: identityKey.serialize()
|
name: Value(address.getName()),
|
||||||
});
|
identityKey: Value(identityKey.serialize()),
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await dbProvider.db!.update(
|
await (twonlyDatabase.update(twonlyDatabase.signalIdentityKeyStores)
|
||||||
DB.tableName, {DB.columnIdentityKey: identityKey.serialize()},
|
..where((t) =>
|
||||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
t.deviceId.equals(address.getDeviceId()) &
|
||||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]);
|
t.name.equals(address.getName())))
|
||||||
|
.write(
|
||||||
|
SignalIdentityKeyStoresCompanion(
|
||||||
|
identityKey: Value(identityKey.serialize()),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,52 @@
|
||||||
import 'dart:typed_data';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/model/pre_key_model.dart';
|
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
typedef DB = DbSignalPreKeyStore;
|
|
||||||
|
|
||||||
class ConnectPreKeyStore extends PreKeyStore {
|
class ConnectPreKeyStore extends PreKeyStore {
|
||||||
@override
|
@override
|
||||||
Future<bool> containsPreKey(int preKeyId) async {
|
Future<bool> containsPreKey(int preKeyId) async {
|
||||||
final dbPreKey = await dbProvider.db!.query(DB.tableName,
|
final preKeyRecord =
|
||||||
columns: [DB.columnPreKey],
|
await (twonlyDatabase.select(twonlyDatabase.signalPreKeyStores)
|
||||||
where: '${DB.columnPreKeyId} = ?',
|
..where((tbl) => tbl.preKeyId.equals(preKeyId)))
|
||||||
whereArgs: <Object?>[preKeyId]);
|
.get();
|
||||||
return dbPreKey.isNotEmpty;
|
return preKeyRecord.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PreKeyRecord> loadPreKey(int preKeyId) async {
|
Future<PreKeyRecord> loadPreKey(int preKeyId) async {
|
||||||
final dbPreKey = await dbProvider.db!.query(DB.tableName,
|
final preKeyRecord =
|
||||||
columns: [DB.columnPreKey],
|
await (twonlyDatabase.select(twonlyDatabase.signalPreKeyStores)
|
||||||
where: '${DB.columnPreKeyId} = ?',
|
..where((tbl) => tbl.preKeyId.equals(preKeyId)))
|
||||||
whereArgs: <Object?>[preKeyId]);
|
.get();
|
||||||
if (dbPreKey.isEmpty) {
|
if (preKeyRecord.isEmpty) {
|
||||||
throw InvalidKeyIdException('No such preKey record! - $preKeyId');
|
throw InvalidKeyIdException('No such preKey record! - $preKeyId');
|
||||||
}
|
}
|
||||||
Uint8List preKey = dbPreKey.first.cast()[DB.columnPreKey];
|
Uint8List preKey = preKeyRecord.first.preKey;
|
||||||
return PreKeyRecord.fromBuffer(preKey);
|
return PreKeyRecord.fromBuffer(preKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removePreKey(int preKeyId) async {
|
Future<void> removePreKey(int preKeyId) async {
|
||||||
await dbProvider.db!.delete(DB.tableName,
|
await (twonlyDatabase.delete(twonlyDatabase.signalPreKeyStores)
|
||||||
where: '${DB.columnPreKeyId} = ?',
|
..where((tbl) => tbl.preKeyId.equals(preKeyId)))
|
||||||
whereArgs: <Object?>[DB.columnPreKeyId]);
|
.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> storePreKey(int preKeyId, PreKeyRecord record) async {
|
Future<void> storePreKey(int preKeyId, PreKeyRecord record) async {
|
||||||
if (!await containsPreKey(preKeyId)) {
|
final preKeyCompanion = SignalPreKeyStoresCompanion(
|
||||||
await dbProvider.db!.insert(DB.tableName,
|
preKeyId: Value(preKeyId),
|
||||||
{DB.columnPreKeyId: preKeyId, DB.columnPreKey: record.serialize()});
|
preKey: Value(record.serialize()),
|
||||||
} else {
|
);
|
||||||
await dbProvider.db!.update(
|
|
||||||
DB.tableName, {DB.columnPreKey: record.serialize()},
|
try {
|
||||||
where: '${DB.columnPreKeyId} = ?', whereArgs: <Object?>[preKeyId]);
|
await twonlyDatabase
|
||||||
|
.into(twonlyDatabase.signalPreKeyStores)
|
||||||
|
.insert(preKeyCompanion);
|
||||||
|
} catch (e) {
|
||||||
|
Logger("pre_key_store").shout("$e");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,30 @@
|
||||||
import 'dart:typed_data';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/model/sender_key_store_model.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
|
||||||
typedef DB = DbSignalSenderKeyStore;
|
|
||||||
|
|
||||||
class ConnectSenderKeyStore extends SenderKeyStore {
|
class ConnectSenderKeyStore extends SenderKeyStore {
|
||||||
@override
|
@override
|
||||||
Future<SenderKeyRecord> loadSenderKey(SenderKeyName senderKeyName) async {
|
Future<SenderKeyRecord> loadSenderKey(SenderKeyName senderKeyName) async {
|
||||||
final dbSenderKey = await dbProvider.db!.query(DB.tableName,
|
SignalSenderKeyStore? identity =
|
||||||
columns: [DB.columnSenderKey],
|
await (twonlyDatabase.select(twonlyDatabase.signalSenderKeyStores)
|
||||||
where: '${DB.columnSenderKeyName} = ?',
|
..where((t) => t.senderKeyName.equals(senderKeyName.serialize())))
|
||||||
whereArgs: <Object?>[senderKeyName.serialize()]);
|
.getSingleOrNull();
|
||||||
if (dbSenderKey.isEmpty) {
|
if (identity == null) {
|
||||||
throw InvalidKeyIdException(
|
throw InvalidKeyIdException(
|
||||||
'No such sender key record! - $senderKeyName');
|
'No such sender key record! - $senderKeyName');
|
||||||
}
|
}
|
||||||
Uint8List preKey = dbSenderKey.first.cast()[DB.columnSenderKey];
|
return SenderKeyRecord.fromSerialized(identity.senderKey);
|
||||||
return SenderKeyRecord.fromSerialized(preKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> storeSenderKey(
|
Future<void> storeSenderKey(
|
||||||
SenderKeyName senderKeyName, SenderKeyRecord record) async {
|
SenderKeyName senderKeyName, SenderKeyRecord record) async {
|
||||||
await dbProvider.db!.insert(DB.tableName, {
|
await twonlyDatabase.into(twonlyDatabase.signalSenderKeyStores).insert(
|
||||||
DB.columnSenderKeyName: senderKeyName.serialize(),
|
SignalSenderKeyStoresCompanion(
|
||||||
DB.columnSenderKey: record.serialize()
|
senderKey: Value(record.serialize()),
|
||||||
});
|
senderKeyName: Value(senderKeyName.serialize()),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,81 @@
|
||||||
import 'dart:typed_data';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/model/session_store_model.dart';
|
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
// make it easier to read
|
|
||||||
typedef DB = DbSignalSessionStore;
|
|
||||||
|
|
||||||
class ConnectSessionStore extends SessionStore {
|
class ConnectSessionStore extends SessionStore {
|
||||||
ConnectSessionStore();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> containsSession(SignalProtocolAddress address) async {
|
Future<bool> containsSession(SignalProtocolAddress address) async {
|
||||||
var list = (await dbProvider.db!.query(DB.tableName,
|
final sessions =
|
||||||
columns: [],
|
await (twonlyDatabase.select(twonlyDatabase.signalSessionStores)
|
||||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
..where((tbl) =>
|
||||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]));
|
tbl.deviceId.equals(address.getDeviceId()) &
|
||||||
return list.isNotEmpty;
|
tbl.name.equals(address.getName())))
|
||||||
|
.get();
|
||||||
|
return sessions.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deleteAllSessions(String name) async {
|
Future<void> deleteAllSessions(String name) async {
|
||||||
await dbProvider.db!.delete(DB.tableName,
|
await (twonlyDatabase.delete(twonlyDatabase.signalSessionStores)
|
||||||
where: '${DB.columnName} = ?', whereArgs: <Object?>[name]);
|
..where((tbl) => tbl.name.equals(name)))
|
||||||
|
.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deleteSession(SignalProtocolAddress address) async {
|
Future<void> deleteSession(SignalProtocolAddress address) async {
|
||||||
await dbProvider.db!.delete(DB.tableName,
|
await (twonlyDatabase.delete(twonlyDatabase.signalSessionStores)
|
||||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
..where((tbl) =>
|
||||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]);
|
tbl.deviceId.equals(address.getDeviceId()) &
|
||||||
|
tbl.name.equals(address.getName())))
|
||||||
|
.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<int>> getSubDeviceSessions(String name) async {
|
Future<List<int>> getSubDeviceSessions(String name) async {
|
||||||
var deviceIds = (await dbProvider.db!.query(DB.tableName,
|
final deviceIds = await (twonlyDatabase
|
||||||
columns: [DB.columnDeviceId],
|
.select(twonlyDatabase.signalSessionStores)
|
||||||
where: '${DB.columnDeviceId} != 1 AND ${DB.columnName} = ?',
|
..where(
|
||||||
whereArgs: <Object?>[name]));
|
(tbl) => tbl.deviceId.equals(1).not() & tbl.name.equals(name)))
|
||||||
return deviceIds.cast();
|
.get();
|
||||||
|
return deviceIds.map((row) => row.deviceId).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SessionRecord> loadSession(SignalProtocolAddress address) async {
|
Future<SessionRecord> loadSession(SignalProtocolAddress address) async {
|
||||||
var dbSession = (await dbProvider.db!.query(DB.tableName,
|
final dbSession =
|
||||||
columns: [DB.columnSessionRecord],
|
await (twonlyDatabase.select(twonlyDatabase.signalSessionStores)
|
||||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
..where((tbl) =>
|
||||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]));
|
tbl.deviceId.equals(address.getDeviceId()) &
|
||||||
|
tbl.name.equals(address.getName())))
|
||||||
|
.get();
|
||||||
|
|
||||||
if (dbSession.isEmpty) {
|
if (dbSession.isEmpty) {
|
||||||
return SessionRecord();
|
return SessionRecord();
|
||||||
}
|
}
|
||||||
Uint8List session = dbSession.first.cast()[DB.columnSessionRecord];
|
Uint8List session = dbSession.first.sessionRecord;
|
||||||
return SessionRecord.fromSerialized(session);
|
return SessionRecord.fromSerialized(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> storeSession(
|
Future<void> storeSession(
|
||||||
SignalProtocolAddress address, SessionRecord record) async {
|
SignalProtocolAddress address, SessionRecord record) async {
|
||||||
|
final sessionCompanion = SignalSessionStoresCompanion(
|
||||||
|
deviceId: Value(address.getDeviceId()),
|
||||||
|
name: Value(address.getName()),
|
||||||
|
sessionRecord: Value(record.serialize()),
|
||||||
|
);
|
||||||
|
|
||||||
if (!await containsSession(address)) {
|
if (!await containsSession(address)) {
|
||||||
await dbProvider.db!.insert(DB.tableName, {
|
await twonlyDatabase
|
||||||
DB.columnDeviceId: address.getDeviceId(),
|
.into(twonlyDatabase.signalSessionStores)
|
||||||
DB.columnName: address.getName(),
|
.insert(sessionCompanion);
|
||||||
DB.columnSessionRecord: record.serialize()
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
await dbProvider.db!.update(
|
await (twonlyDatabase.update(twonlyDatabase.signalSessionStores)
|
||||||
DB.tableName, {DB.columnSessionRecord: record.serialize()},
|
..where((tbl) =>
|
||||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
tbl.deviceId.equals(address.getDeviceId()) &
|
||||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]);
|
tbl.name.equals(address.getName())))
|
||||||
|
.write(sessionCompanion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ import 'package:logging/logging.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
|
||||||
import 'package:twonly/src/proto/api/error.pb.dart';
|
import 'package:twonly/src/proto/api/error.pb.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
@ -20,18 +19,6 @@ extension ShortCutsExtension on BuildContext {
|
||||||
TwonlyDatabase get db => Provider.of<TwonlyDatabase>(this);
|
TwonlyDatabase get db => Provider.of<TwonlyDatabase>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to check if a column exists
|
|
||||||
Future<bool> columnExists(
|
|
||||||
Database db, String tableName, String columnName) async {
|
|
||||||
final result = await db.rawQuery('PRAGMA table_info($tableName)');
|
|
||||||
for (var row in result) {
|
|
||||||
if (row['name'] == columnName) {
|
|
||||||
return true; // Column exists
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false; // Column does not exist
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> writeLogToFile(LogRecord record) async {
|
Future<void> writeLogToFile(LogRecord record) async {
|
||||||
final directory = await getApplicationDocumentsDirectory();
|
final directory = await getApplicationDocumentsDirectory();
|
||||||
final logFile = File('${directory.path}/app.log');
|
final logFile = File('${directory.path}/app.log');
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
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/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
import 'package:twonly/src/model/json/signal_identity.dart';
|
import 'package:twonly/src/json_models/signal_identity.dart';
|
||||||
import 'package:twonly/src/model/json/user_data.dart';
|
import 'package:twonly/src/json_models/userdata.dart';
|
||||||
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
||||||
import 'package:twonly/src/signal/connect_signal_protocol_store.dart';
|
import 'package:twonly/src/signal/connect_signal_protocol_store.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
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/src/json_models/userdata.dart';
|
||||||
import 'package:twonly/src/model/json/user_data.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
Future<bool> isUserCreated() async {
|
Future<bool> isUserCreated() async {
|
||||||
|
|
@ -15,7 +14,7 @@ Future<bool> isUserCreated() async {
|
||||||
|
|
||||||
Future<UserData?> getUser() async {
|
Future<UserData?> getUser() async {
|
||||||
final storage = getSecureStorage();
|
final storage = getSecureStorage();
|
||||||
String? userJson = await storage.read(key: "user_data");
|
String? userJson = await storage.read(key: "userData");
|
||||||
if (userJson == null) {
|
if (userJson == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -30,16 +29,11 @@ Future<UserData?> getUser() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteLocalUserData() async {
|
Future<bool> deleteLocalUserData() async {
|
||||||
final storage = getSecureStorage();
|
|
||||||
var password = await storage.read(key: "sqflite_database_password");
|
|
||||||
await dbProvider.remove();
|
|
||||||
|
|
||||||
final appDir = await getApplicationSupportDirectory();
|
final appDir = await getApplicationSupportDirectory();
|
||||||
if (appDir.existsSync()) {
|
if (appDir.existsSync()) {
|
||||||
appDir.deleteSync(recursive: true);
|
appDir.deleteSync(recursive: true);
|
||||||
}
|
}
|
||||||
|
final storage = getSecureStorage();
|
||||||
await storage.write(key: "sqflite_database_password", value: password);
|
|
||||||
await storage.deleteAll();
|
await storage.deleteAll();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ 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';
|
||||||
import 'package:twonly/src/database/contacts_db.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/providers/api/media.dart';
|
import 'package:twonly/src/providers/api/media.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';
|
||||||
|
|
@ -55,8 +55,9 @@ 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 twonlyDatabase.contactsDao
|
||||||
await twonlyDatabase.getContactByUserId(userId).getSingleOrNull();
|
.getContactByUserId(userId)
|
||||||
|
.getSingleOrNull();
|
||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
sendNextMediaToUserName = getContactDisplayName(contact);
|
sendNextMediaToUserName = getContactDisplayName(contact);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ 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 'package:twonly/src/database/contacts_db.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/providers/api/media.dart';
|
import 'package:twonly/src/providers/api/media.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';
|
||||||
|
|
@ -54,7 +54,7 @@ class _ShareImageView extends State<ShareImageView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<Contact>> allContacts =
|
Stream<List<Contact>> allContacts =
|
||||||
twonlyDatabase.watchContactsForChatList();
|
twonlyDatabase.contactsDao.watchContactsForChatList();
|
||||||
|
|
||||||
contactSub = allContacts.listen((allContacts) {
|
contactSub = allContacts.listen((allContacts) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ 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';
|
||||||
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/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
import 'package:twonly/src/providers/api/api.dart';
|
import 'package:twonly/src/providers/api/api.dart';
|
||||||
import 'package:twonly/src/providers/api/media.dart';
|
import 'package:twonly/src/providers/api/media.dart';
|
||||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||||
|
|
@ -158,8 +158,9 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future initStreams() async {
|
Future initStreams() async {
|
||||||
await twonlyDatabase.removeOldMessages();
|
await twonlyDatabase.messagesDao.removeOldMessages();
|
||||||
Stream<Contact> contact = twonlyDatabase.watchContact(widget.userid);
|
Stream<Contact> contact =
|
||||||
|
twonlyDatabase.contactsDao.watchContact(widget.userid);
|
||||||
userSub = contact.listen((contact) {
|
userSub = contact.listen((contact) {
|
||||||
setState(() {
|
setState(() {
|
||||||
user = contact;
|
user = contact;
|
||||||
|
|
@ -167,7 +168,7 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
||||||
});
|
});
|
||||||
|
|
||||||
Stream<List<Message>> msgStream =
|
Stream<List<Message>> msgStream =
|
||||||
twonlyDatabase.watchAllMessagesFrom(widget.userid);
|
twonlyDatabase.messagesDao.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;
|
||||||
|
|
@ -181,7 +182,7 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (updated) {
|
if (updated) {
|
||||||
twonlyDatabase.openedAllTextMessages(widget.userid);
|
twonlyDatabase.messagesDao.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(() {
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ 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';
|
||||||
import 'package:twonly/src/components/notification_badge.dart';
|
import 'package:twonly/src/components/notification_badge.dart';
|
||||||
import 'package:twonly/src/components/user_context_menu.dart';
|
import 'package:twonly/src/components/user_context_menu.dart';
|
||||||
import 'package:twonly/src/database/contacts_db.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
import 'package:twonly/src/providers/api/media.dart';
|
import 'package:twonly/src/providers/api/media.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';
|
||||||
|
|
@ -50,7 +50,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
// title:
|
// title:
|
||||||
actions: [
|
actions: [
|
||||||
StreamBuilder(
|
StreamBuilder(
|
||||||
stream: twonlyDatabase.watchContactsRequested(),
|
stream: twonlyDatabase.contactsDao.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) {
|
||||||
|
|
@ -86,7 +86,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: StreamBuilder(
|
body: StreamBuilder(
|
||||||
stream: twonlyDatabase.watchContactsForChatList(),
|
stream: twonlyDatabase.contactsDao.watchContactsForChatList(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData || snapshot.data == null) {
|
if (!snapshot.hasData || snapshot.data == null) {
|
||||||
return Container();
|
return Container();
|
||||||
|
|
@ -190,7 +190,8 @@ class _UserListItem extends State<UserListItem> {
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(getContactDisplayName(widget.user)),
|
title: Text(getContactDisplayName(widget.user)),
|
||||||
subtitle: StreamBuilder(
|
subtitle: StreamBuilder(
|
||||||
stream: twonlyDatabase.watchLastMessage(widget.user.userId),
|
stream:
|
||||||
|
twonlyDatabase.messagesDao.watchLastMessage(widget.user.userId),
|
||||||
builder: (context, lastMessageSnapshot) {
|
builder: (context, lastMessageSnapshot) {
|
||||||
if (!lastMessageSnapshot.hasData) {
|
if (!lastMessageSnapshot.hasData) {
|
||||||
return Container();
|
return Container();
|
||||||
|
|
@ -200,7 +201,8 @@ class _UserListItem extends State<UserListItem> {
|
||||||
}
|
}
|
||||||
final lastMessage = lastMessageSnapshot.data!.first;
|
final lastMessage = lastMessageSnapshot.data!.first;
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
stream: twonlyDatabase.watchMessageNotOpened(widget.user.userId),
|
stream: twonlyDatabase.messagesDao
|
||||||
|
.watchMessageNotOpened(widget.user.userId),
|
||||||
builder: (context, notOpenedMessagesSnapshot) {
|
builder: (context, notOpenedMessagesSnapshot) {
|
||||||
if (!lastMessageSnapshot.hasData) {
|
if (!lastMessageSnapshot.hasData) {
|
||||||
return Container();
|
return Container();
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/globals.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/twonly_database.dart';
|
||||||
import 'package:twonly/src/database/messages_db.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
import 'package:twonly/src/providers/api/api.dart';
|
import 'package:twonly/src/providers/api/api.dart';
|
||||||
import 'package:twonly/src/providers/api/media.dart';
|
import 'package:twonly/src/providers/api/media.dart';
|
||||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||||
|
|
@ -57,7 +57,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
|
|
||||||
Future asyncLoadNextMedia(bool firstRun) async {
|
Future asyncLoadNextMedia(bool firstRun) async {
|
||||||
Stream<List<Message>> messages =
|
Stream<List<Message>> messages =
|
||||||
twonlyDatabase.watchMediaMessageNotOpened(widget.userId);
|
twonlyDatabase.messagesDao.watchMediaMessageNotOpened(widget.userId);
|
||||||
|
|
||||||
_subscription = messages.listen((messages) {
|
_subscription = messages.listen((messages) {
|
||||||
for (Message msg in messages) {
|
for (Message msg in messages) {
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ import 'dart:async';
|
||||||
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/src/components/alert_dialog.dart';
|
import 'package:twonly/src/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/database/contacts_db.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.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/model/json/message.dart';
|
import 'package:twonly/src/json_models/message.dart';
|
||||||
import 'package:twonly/src/providers/api/api.dart';
|
import 'package:twonly/src/providers/api/api.dart';
|
||||||
// ignore: library_prefixes
|
// ignore: library_prefixes
|
||||||
import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
||||||
|
|
@ -49,7 +49,8 @@ class _SearchUsernameView extends State<SearchUsernameView> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int added = await twonlyDatabase.insertContact(ContactsCompanion(
|
int added =
|
||||||
|
await twonlyDatabase.contactsDao.insertContact(ContactsCompanion(
|
||||||
username: Value(searchUserName.text),
|
username: Value(searchUserName.text),
|
||||||
userId: Value(res.value.userdata.userId.toInt()),
|
userId: Value(res.value.userdata.userId.toInt()),
|
||||||
requested: Value(false),
|
requested: Value(false),
|
||||||
|
|
@ -97,7 +98,8 @@ class _SearchUsernameView extends State<SearchUsernameView> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<Contact>> contacts = twonlyDatabase.watchNotAcceptedContacts();
|
Stream<List<Contact>> contacts =
|
||||||
|
twonlyDatabase.contactsDao.watchNotAcceptedContacts();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|
@ -195,8 +197,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 twonlyDatabase.updateContact(
|
await twonlyDatabase.contactsDao
|
||||||
contact.userId, update);
|
.updateContact(contact.userId, update);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -205,7 +207,7 @@ 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 twonlyDatabase
|
await twonlyDatabase.contactsDao
|
||||||
.deleteContactByUserId(contact.userId);
|
.deleteContactByUserId(contact.userId);
|
||||||
encryptAndSendMessage(
|
encryptAndSendMessage(
|
||||||
null,
|
null,
|
||||||
|
|
@ -223,7 +225,8 @@ 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 twonlyDatabase.updateContact(contact.userId, update);
|
await twonlyDatabase.contactsDao
|
||||||
|
.updateContact(contact.userId, update);
|
||||||
encryptAndSendMessage(
|
encryptAndSendMessage(
|
||||||
null,
|
null,
|
||||||
contact.userId,
|
contact.userId,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:twonly/globals.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/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/signal.dart';
|
import 'package:twonly/src/utils/signal.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
@ -37,7 +37,7 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Stream<Contact?> contact = twonlyDatabase
|
Stream<Contact?> contact = twonlyDatabase.contactsDao
|
||||||
.getContactByUserId(widget.contact.userId)
|
.getContactByUserId(widget.contact.userId)
|
||||||
.watchSingleOrNull();
|
.watchSingleOrNull();
|
||||||
|
|
||||||
|
|
@ -145,7 +145,8 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final update =
|
final update =
|
||||||
ContactsCompanion(verified: Value(false));
|
ContactsCompanion(verified: Value(false));
|
||||||
twonlyDatabase.updateContact(contact.userId, update);
|
twonlyDatabase.contactsDao
|
||||||
|
.updateContact(contact.userId, update);
|
||||||
},
|
},
|
||||||
label: Text(
|
label: Text(
|
||||||
context.lang.contactVerifyNumberClearVerification),
|
context.lang.contactVerifyNumberClearVerification),
|
||||||
|
|
@ -155,7 +156,8 @@ 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));
|
||||||
twonlyDatabase.updateContact(contact.userId, update);
|
twonlyDatabase.contactsDao
|
||||||
|
.updateContact(contact.userId, update);
|
||||||
},
|
},
|
||||||
label: Text(context.lang.contactVerifyNumberMarkAsVerified),
|
label: Text(context.lang.contactVerifyNumberMarkAsVerified),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ 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/verified_shield.dart';
|
import 'package:twonly/src/components/verified_shield.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/database/contacts_db.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/contact/contact_verify_view.dart';
|
import 'package:twonly/src/views/contact/contact_verify_view.dart';
|
||||||
|
|
||||||
|
|
@ -24,8 +24,9 @@ class ContactView extends StatefulWidget {
|
||||||
class _ContactViewState extends State<ContactView> {
|
class _ContactViewState extends State<ContactView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Stream<Contact?> contact =
|
Stream<Contact?> contact = twonlyDatabase.contactsDao
|
||||||
twonlyDatabase.getContactByUserId(widget.userId).watchSingleOrNull();
|
.getContactByUserId(widget.userId)
|
||||||
|
.watchSingleOrNull();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|
@ -79,7 +80,8 @@ 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));
|
||||||
twonlyDatabase.updateContact(contact.userId, update);
|
twonlyDatabase.contactsDao
|
||||||
|
.updateContact(contact.userId, update);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -109,8 +111,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 twonlyDatabase.updateContact(
|
await twonlyDatabase.contactsDao
|
||||||
contact.userId, update);
|
.updateContact(contact.userId, update);
|
||||||
}
|
}
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.popUntil(context, (route) => route.isFirst);
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'package:twonly/globals.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:twonly/src/components/alert_dialog.dart';
|
import 'package:twonly/src/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/model/json/user_data.dart';
|
import 'package:twonly/src/json_models/userdata.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/signal.dart';
|
import 'package:twonly/src/utils/signal.dart';
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
username: username,
|
username: username,
|
||||||
displayName: username,
|
displayName: username,
|
||||||
);
|
);
|
||||||
storage.write(key: "user_data", value: jsonEncode(userData));
|
storage.write(key: "userData", value: jsonEncode(userData));
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
|
||||||
|
|
@ -27,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: twonlyDatabase.watchContactsBlocked(),
|
stream: twonlyDatabase.contactsDao.watchContactsBlocked(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData && snapshot.data != null) {
|
if (snapshot.hasData && snapshot.data != null) {
|
||||||
return Text(
|
return Text(
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ 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/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/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class PrivacyViewBlockUsers extends StatefulWidget {
|
class PrivacyViewBlockUsers extends StatefulWidget {
|
||||||
|
|
@ -21,7 +21,7 @@ class _PrivacyViewBlockUsers extends State<PrivacyViewBlockUsers> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
allUsers = twonlyDatabase.watchAllContacts();
|
allUsers = twonlyDatabase.contactsDao.watchAllContacts();
|
||||||
loadAsync();
|
loadAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,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 twonlyDatabase.updateContact(userId, update);
|
await twonlyDatabase.contactsDao.updateContact(userId, update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/src/components/better_list_title.dart';
|
import 'package:twonly/src/components/better_list_title.dart';
|
||||||
import 'package:twonly/src/components/initialsavatar.dart';
|
import 'package:twonly/src/components/initialsavatar.dart';
|
||||||
import 'package:twonly/src/model/json/user_data.dart';
|
import 'package:twonly/src/json_models/userdata.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
|
||||||
114
pubspec.lock
114
pubspec.lock
|
|
@ -265,14 +265,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.1"
|
version: "2.7.1"
|
||||||
cv:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: cv
|
|
||||||
sha256: b7ad2d39ebd8d0a610c69becbd9b0131c1b1544d42186fa025710f8ebff038a0
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.3"
|
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -410,7 +402,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.2"
|
version: "3.10.2"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fixnum
|
name: fixnum
|
||||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
|
@ -422,14 +414,6 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_foreground_task:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_foreground_task
|
|
||||||
sha256: "206017ee1bf864f34b8d7bce664a172717caa21af8da23f55866470dfe316644"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "8.17.0"
|
|
||||||
flutter_image_compress:
|
flutter_image_compress:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -669,14 +653,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
google_fonts:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: google_fonts
|
|
||||||
sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.2.1"
|
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1189,14 +1165,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.1.0"
|
||||||
reorderables:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: reorderables
|
|
||||||
sha256: "004a886e4878df1ee27321831c838bc1c976311f4ca6a74ce7d561e506540a77"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.6.0"
|
|
||||||
restart_app:
|
restart_app:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1221,62 +1189,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
shared_preferences:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences
|
|
||||||
sha256: "688ee90fbfb6989c980254a56cb26ebe9bb30a3a2dff439a78894211f73de67a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.1"
|
|
||||||
shared_preferences_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_android
|
|
||||||
sha256: "650584dcc0a39856f369782874e562efd002a9c94aec032412c9eb81419cce1f"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.4"
|
|
||||||
shared_preferences_foundation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_foundation
|
|
||||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.4"
|
|
||||||
shared_preferences_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_linux
|
|
||||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.1"
|
|
||||||
shared_preferences_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_platform_interface
|
|
||||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.1"
|
|
||||||
shared_preferences_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_web
|
|
||||||
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.2"
|
|
||||||
shared_preferences_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_windows
|
|
||||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.1"
|
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1330,22 +1242,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "7.0.0"
|
||||||
sqflite_common:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_common
|
|
||||||
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.4+6"
|
|
||||||
sqflite_sqlcipher:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: sqflite_sqlcipher
|
|
||||||
sha256: "16033fde6c7d7bd657b71a2bc42332ab02bc8001c3212f502d2e02714e735ec9"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0+1"
|
|
||||||
sqlite3:
|
sqlite3:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1402,14 +1298,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
synchronized:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: synchronized
|
|
||||||
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.3.0+3"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,13 @@ dependencies:
|
||||||
# path: ../CamerAwesome
|
# path: ../CamerAwesome
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
connectivity_plus: ^6.1.2
|
connectivity_plus: ^6.1.2
|
||||||
cv: ^1.1.3
|
|
||||||
drift: ^2.25.1
|
drift: ^2.25.1
|
||||||
drift_flutter: ^0.2.4
|
drift_flutter: ^0.2.4
|
||||||
exif: ^3.3.0
|
exif: ^3.3.0
|
||||||
firebase_core: ^3.11.0
|
firebase_core: ^3.11.0
|
||||||
firebase_messaging: ^15.2.2
|
firebase_messaging: ^15.2.2
|
||||||
fixnum: ^1.1.1
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_foreground_task: ^8.17.0
|
|
||||||
flutter_image_compress: ^2.4.0
|
flutter_image_compress: ^2.4.0
|
||||||
flutter_local_notifications: ^18.0.1
|
flutter_local_notifications: ^18.0.1
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
|
|
@ -32,7 +29,6 @@ dependencies:
|
||||||
flutter_secure_storage: ^10.0.0-beta.4
|
flutter_secure_storage: ^10.0.0-beta.4
|
||||||
font_awesome_flutter: ^10.8.0
|
font_awesome_flutter: ^10.8.0
|
||||||
gal: ^2.3.1
|
gal: ^2.3.1
|
||||||
google_fonts: ^6.2.1
|
|
||||||
hand_signature: ^3.0.3
|
hand_signature: ^3.0.3
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
image: ^4.3.0
|
image: ^4.3.0
|
||||||
|
|
@ -54,10 +50,8 @@ dependencies:
|
||||||
cryptography_flutter_plus: ^2.3.2
|
cryptography_flutter_plus: ^2.3.2
|
||||||
provider: ^6.1.2
|
provider: ^6.1.2
|
||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
reorderables: ^0.6.0
|
|
||||||
restart_app: ^1.3.2
|
restart_app: ^1.3.2
|
||||||
screenshot: ^3.0.0
|
screenshot: ^3.0.0
|
||||||
sqflite_sqlcipher: ^3.1.0+1
|
|
||||||
url_launcher: ^6.3.1
|
url_launcher: ^6.3.1
|
||||||
web_socket_channel: ^3.0.1
|
web_socket_channel: ^3.0.1
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue