mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 16: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/db_provider.dart';
|
||||
|
||||
late ApiProvider apiProvider;
|
||||
|
||||
// uses for background notification
|
||||
late DbProvider dbProvider;
|
||||
late TwonlyDatabase twonlyDatabase;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
import 'package:provider/provider.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:flutter/material.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/send_next_media_to.dart';
|
||||
import 'package:twonly/src/providers/settings_change_provider.dart';
|
||||
|
|
@ -39,14 +37,9 @@ void main() async {
|
|||
await initMediaStorage();
|
||||
await initFCMService();
|
||||
|
||||
dbProvider = DbProvider();
|
||||
await dbProvider.ready;
|
||||
|
||||
apiProvider = ApiProvider();
|
||||
twonlyDatabase = TwonlyDatabase();
|
||||
|
||||
FlutterForegroundTask.initCommunicationPort();
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:cv/cv.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
|
||||
|
|
@ -30,8 +29,7 @@ class EmojiAnimation extends StatelessWidget {
|
|||
|
||||
// Check if the emoji has a corresponding Lottie animation
|
||||
if (animatedIcons.containsKey(emoji)) {
|
||||
return Lottie.asset(
|
||||
"assets/animated_icons/${animatedIcons.getValue(emoji)}");
|
||||
return Lottie.asset("assets/animated_icons/${animatedIcons[emoji]}");
|
||||
} else {
|
||||
return Text(
|
||||
emoji,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import 'dart:collection';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/verified_shield.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/components/flame.dart';
|
||||
import 'package:twonly/src/components/headline.dart';
|
||||
|
|
@ -142,7 +142,8 @@ class UserCheckbox extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: twonlyDatabase.watchFlameCounter(user.userId),
|
||||
stream: twonlyDatabase.contactsDao
|
||||
.watchFlameCounter(user.userId),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData || snapshot.data! == 0) {
|
||||
return Container();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.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 {
|
||||
final Contact user;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import 'dart:collection';
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/messages_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
enum MessageSendState {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:pie_menu/pie_menu.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/utils/misc.dart';
|
||||
import 'package:twonly/src/views/chats/chat_item_details_view.dart';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.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 {
|
||||
final Contact contact;
|
||||
|
|
|
|||
|
|
@ -1,145 +1,15 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:logging/logging.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';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
part 'contacts_dao.g.dart';
|
||||
|
||||
// You can then create a database class that includes this table
|
||||
@DriftDatabase(tables: [Contacts, Messages])
|
||||
class TwonlyDatabase extends _$TwonlyDatabase {
|
||||
TwonlyDatabase() : super(_openConnection());
|
||||
|
||||
@override
|
||||
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));
|
||||
}
|
||||
|
||||
// ------------
|
||||
@DriftAccessor(tables: [Contacts])
|
||||
class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
|
||||
with _$ContactsDaoMixin {
|
||||
// this constructor is required so that the main database can create an instance
|
||||
// of this object.
|
||||
ContactsDao(TwonlyDatabase db) : super(db);
|
||||
|
||||
Future<int> insertContact(ContactsCompanion contact) async {
|
||||
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:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
|
||||
class Contacts extends Table {
|
||||
IntColumn get userId => integer()();
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
|
||||
enum DownloadState {
|
||||
pending,
|
||||
|
|
@ -8,6 +8,7 @@ enum DownloadState {
|
|||
downloaded,
|
||||
}
|
||||
|
||||
@DataClassName('Message')
|
||||
class Messages extends Table {
|
||||
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
|
||||
|
||||
part of 'user_data.dart';
|
||||
part of 'userdata.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// 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:logging/logging.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/messages_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
import 'package:twonly/src/proto/api/error.pb.dart';
|
||||
import 'package:twonly/src/providers/api/api_utils.dart';
|
||||
import 'package:twonly/src/providers/hive.dart';
|
||||
|
|
@ -70,7 +70,7 @@ Future<Result> encryptAndSendMessage(
|
|||
|
||||
if (resp.isSuccess) {
|
||||
if (messageId != null) {
|
||||
await twonlyDatabase.updateMessageByMessageId(
|
||||
await twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||
messageId,
|
||||
MessagesCompanion(acknowledgeByServer: Value(true)),
|
||||
);
|
||||
|
|
@ -86,7 +86,7 @@ Future sendTextMessage(int target, String message) async {
|
|||
|
||||
DateTime messageSendAt = DateTime.now();
|
||||
|
||||
int? messageId = await twonlyDatabase.insertMessage(
|
||||
int? messageId = await twonlyDatabase.messagesDao.insertMessage(
|
||||
MessagesCompanion(
|
||||
contactId: Value(target),
|
||||
kind: Value(MessageKind.textMessage),
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import 'package:drift/drift.dart';
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/app.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/messages_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.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_utils.dart';
|
||||
|
|
@ -19,7 +19,7 @@ Future tryDownloadAllMediaFiles() async {
|
|||
return;
|
||||
}
|
||||
List<Message> messages =
|
||||
await twonlyDatabase.getAllMessagesPendingDownloading();
|
||||
await twonlyDatabase.messagesDao.getAllMessagesPendingDownloading();
|
||||
|
||||
for (Message message in messages) {
|
||||
MessageContent? content =
|
||||
|
|
@ -163,7 +163,7 @@ class ImageUploader {
|
|||
|
||||
final downloadToken = uploadState.downloadTokens.removeLast();
|
||||
|
||||
twonlyDatabase.incFlameCounter(
|
||||
twonlyDatabase.contactsDao.incFlameCounter(
|
||||
targetUserId,
|
||||
false,
|
||||
metadata.messageSendAt,
|
||||
|
|
@ -215,7 +215,7 @@ Future sendImage(
|
|||
|
||||
// at this point it is safe inform the user about the process of sending the image..
|
||||
for (final userId in metadata.userIds) {
|
||||
int? messageId = await twonlyDatabase.insertMessage(
|
||||
int? messageId = await twonlyDatabase.messagesDao.insertMessage(
|
||||
MessagesCompanion(
|
||||
contactId: Value(userId),
|
||||
kind: Value(MessageKind.media),
|
||||
|
|
@ -284,7 +284,7 @@ Future tryDownloadMedia(
|
|||
|
||||
box.put("${content.downloadToken!}_messageId", messageId);
|
||||
|
||||
await twonlyDatabase.updateMessageByOtherUser(
|
||||
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||
fromUserId,
|
||||
messageId,
|
||||
MessagesCompanion(
|
||||
|
|
@ -307,7 +307,7 @@ Future<Uint8List?> getDownloadedMedia(
|
|||
|
||||
// await userOpenedOtherMessage(otherUserId, messageOtherId);
|
||||
notifyContactAboutOpeningMessage(message.contactId, message.messageOtherId!);
|
||||
twonlyDatabase.updateMessageByMessageId(
|
||||
twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||
message.messageId, MessagesCompanion(openedAt: Value(DateTime.now())));
|
||||
|
||||
box.delete(downloadToken.toString());
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/app.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/messages_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.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.pbserver.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) {
|
||||
// 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("${data.downloadToken}_downloaded");
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
|
|
@ -111,8 +111,9 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
|||
// Uint8List? rawBytes =
|
||||
// await SignalHelper.decryptBytes(downloadedBytes, fromUserId);
|
||||
|
||||
Message? msg =
|
||||
await twonlyDatabase.getMessageByMessageId(messageId).getSingleOrNull();
|
||||
Message? msg = await twonlyDatabase.messagesDao
|
||||
.getMessageByMessageId(messageId)
|
||||
.getSingleOrNull();
|
||||
if (msg == null) {
|
||||
Logger("server_messages")
|
||||
.info("messageId not found in database. Ignoring download");
|
||||
|
|
@ -141,13 +142,13 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
|||
} catch (e) {
|
||||
Logger("server_messages").info("Decryption error: $e");
|
||||
// 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
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
return client.Response()..ok = ok;
|
||||
}
|
||||
|
||||
await twonlyDatabase.updateMessageByOtherUser(
|
||||
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||
msg.contactId,
|
||||
messageId,
|
||||
MessagesCompanion(downloadState: Value(DownloadState.downloaded)),
|
||||
|
|
@ -168,7 +169,8 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
|||
if (username.isSuccess) {
|
||||
Uint8List name = username.value.userdata.username;
|
||||
|
||||
int added = await twonlyDatabase.insertContact(ContactsCompanion(
|
||||
int added =
|
||||
await twonlyDatabase.contactsDao.insertContact(ContactsCompanion(
|
||||
username: Value(utf8.decode(name)),
|
||||
userId: Value(fromUserId),
|
||||
requested: Value(true),
|
||||
|
|
@ -184,23 +186,23 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
|||
break;
|
||||
case MessageKind.opened:
|
||||
final update = MessagesCompanion(openedAt: Value(message.timestamp));
|
||||
await twonlyDatabase.updateMessageByOtherUser(
|
||||
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||
fromUserId,
|
||||
message.messageId!,
|
||||
update,
|
||||
);
|
||||
break;
|
||||
case MessageKind.rejectRequest:
|
||||
await twonlyDatabase.deleteContactByUserId(fromUserId);
|
||||
await twonlyDatabase.contactsDao.deleteContactByUserId(fromUserId);
|
||||
break;
|
||||
case MessageKind.acceptRequest:
|
||||
final update = ContactsCompanion(accepted: Value(true));
|
||||
twonlyDatabase.updateContact(fromUserId, update);
|
||||
twonlyDatabase.contactsDao.updateContact(fromUserId, update);
|
||||
localPushNotificationNewMessage(fromUserId.toInt(), message, 8888888);
|
||||
break;
|
||||
case MessageKind.ack:
|
||||
final update = MessagesCompanion(acknowledgeByUser: Value(true));
|
||||
await twonlyDatabase.updateMessageByOtherUser(
|
||||
await twonlyDatabase.messagesDao.updateMessageByOtherUser(
|
||||
fromUserId,
|
||||
message.messageId!,
|
||||
update,
|
||||
|
|
@ -226,7 +228,7 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
|||
sendAt: Value(message.timestamp),
|
||||
);
|
||||
|
||||
final messageId = await twonlyDatabase.insertMessage(
|
||||
final messageId = await twonlyDatabase.messagesDao.insertMessage(
|
||||
update,
|
||||
);
|
||||
|
||||
|
|
@ -246,7 +248,7 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
|||
);
|
||||
|
||||
if (message.kind == MessageKind.media) {
|
||||
twonlyDatabase.incFlameCounter(
|
||||
twonlyDatabase.contactsDao.incFlameCounter(
|
||||
fromUserId,
|
||||
true,
|
||||
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:twonly/globals.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/utils/misc.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/model/json/message.dart' as my;
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/json_models/message.dart' as my;
|
||||
|
||||
/// Streams are created so that app can respond to notification-related events
|
||||
/// since the plugin is initialized in the `main` function
|
||||
|
|
@ -167,8 +167,9 @@ String getPushNotificationText(String key, String userName) {
|
|||
|
||||
Future localPushNotificationNewMessage(
|
||||
int fromUserId, my.MessageJson message, int messageId) async {
|
||||
Contact? user =
|
||||
await twonlyDatabase.getContactByUserId(fromUserId).getSingleOrNull();
|
||||
Contact? user = await twonlyDatabase.contactsDao
|
||||
.getContactByUserId(fromUserId)
|
||||
.getSingleOrNull();
|
||||
|
||||
if (user == null) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:drift/drift.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';
|
||||
|
||||
bool eq<E>(List<E>? list1, List<E>? list2) =>
|
||||
ListEquality<E>().equals(list1, list2);
|
||||
|
||||
// make it easier to read
|
||||
typedef DB = DbSignalIdentityKeyStore;
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
|
||||
class ConnectIdentityKeyStore extends IdentityKeyStore {
|
||||
ConnectIdentityKeyStore(this.identityKeyPair, this.localRegistrationId);
|
||||
|
|
@ -18,15 +12,15 @@ class ConnectIdentityKeyStore extends IdentityKeyStore {
|
|||
|
||||
@override
|
||||
Future<IdentityKey?> getIdentity(SignalProtocolAddress address) async {
|
||||
var dbIdentityKey = (await dbProvider.db!.query(DB.tableName,
|
||||
columns: [DB.columnIdentityKey],
|
||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]));
|
||||
if (dbIdentityKey.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
Uint8List identityKey = dbIdentityKey.first.cast()[DB.columnIdentityKey];
|
||||
return IdentityKey.fromBytes(identityKey, 0);
|
||||
SignalIdentityKeyStore? identity =
|
||||
await (twonlyDatabase.select(twonlyDatabase.signalIdentityKeyStores)
|
||||
..where((t) =>
|
||||
t.deviceId.equals(address.getDeviceId()) &
|
||||
t.name.equals(address.getName())))
|
||||
.getSingleOrNull();
|
||||
|
||||
if (identity == null) return null;
|
||||
return IdentityKey.fromBytes(identity.identityKey, 0);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -42,7 +36,8 @@ class ConnectIdentityKeyStore extends IdentityKeyStore {
|
|||
if (identityKey == null) {
|
||||
return false;
|
||||
}
|
||||
return trusted == null || eq(trusted.serialize(), identityKey.serialize());
|
||||
return trusted == null ||
|
||||
ListEquality().equals(trusted.serialize(), identityKey.serialize());
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -52,16 +47,23 @@ class ConnectIdentityKeyStore extends IdentityKeyStore {
|
|||
return false;
|
||||
}
|
||||
if (await getIdentity(address) == null) {
|
||||
await dbProvider.db!.insert(DB.tableName, {
|
||||
DB.columnDeviceId: address.getDeviceId(),
|
||||
DB.columnName: address.getName(),
|
||||
DB.columnIdentityKey: identityKey.serialize()
|
||||
});
|
||||
await twonlyDatabase.into(twonlyDatabase.signalIdentityKeyStores).insert(
|
||||
SignalIdentityKeyStoresCompanion(
|
||||
deviceId: Value(address.getDeviceId()),
|
||||
name: Value(address.getName()),
|
||||
identityKey: Value(identityKey.serialize()),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await dbProvider.db!.update(
|
||||
DB.tableName, {DB.columnIdentityKey: identityKey.serialize()},
|
||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]);
|
||||
await (twonlyDatabase.update(twonlyDatabase.signalIdentityKeyStores)
|
||||
..where((t) =>
|
||||
t.deviceId.equals(address.getDeviceId()) &
|
||||
t.name.equals(address.getName())))
|
||||
.write(
|
||||
SignalIdentityKeyStoresCompanion(
|
||||
identityKey: Value(identityKey.serialize()),
|
||||
),
|
||||
);
|
||||
}
|
||||
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/src/model/pre_key_model.dart';
|
||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||
|
||||
typedef DB = DbSignalPreKeyStore;
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
|
||||
class ConnectPreKeyStore extends PreKeyStore {
|
||||
@override
|
||||
Future<bool> containsPreKey(int preKeyId) async {
|
||||
final dbPreKey = await dbProvider.db!.query(DB.tableName,
|
||||
columns: [DB.columnPreKey],
|
||||
where: '${DB.columnPreKeyId} = ?',
|
||||
whereArgs: <Object?>[preKeyId]);
|
||||
return dbPreKey.isNotEmpty;
|
||||
final preKeyRecord =
|
||||
await (twonlyDatabase.select(twonlyDatabase.signalPreKeyStores)
|
||||
..where((tbl) => tbl.preKeyId.equals(preKeyId)))
|
||||
.get();
|
||||
return preKeyRecord.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PreKeyRecord> loadPreKey(int preKeyId) async {
|
||||
final dbPreKey = await dbProvider.db!.query(DB.tableName,
|
||||
columns: [DB.columnPreKey],
|
||||
where: '${DB.columnPreKeyId} = ?',
|
||||
whereArgs: <Object?>[preKeyId]);
|
||||
if (dbPreKey.isEmpty) {
|
||||
final preKeyRecord =
|
||||
await (twonlyDatabase.select(twonlyDatabase.signalPreKeyStores)
|
||||
..where((tbl) => tbl.preKeyId.equals(preKeyId)))
|
||||
.get();
|
||||
if (preKeyRecord.isEmpty) {
|
||||
throw InvalidKeyIdException('No such preKey record! - $preKeyId');
|
||||
}
|
||||
Uint8List preKey = dbPreKey.first.cast()[DB.columnPreKey];
|
||||
Uint8List preKey = preKeyRecord.first.preKey;
|
||||
return PreKeyRecord.fromBuffer(preKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removePreKey(int preKeyId) async {
|
||||
await dbProvider.db!.delete(DB.tableName,
|
||||
where: '${DB.columnPreKeyId} = ?',
|
||||
whereArgs: <Object?>[DB.columnPreKeyId]);
|
||||
await (twonlyDatabase.delete(twonlyDatabase.signalPreKeyStores)
|
||||
..where((tbl) => tbl.preKeyId.equals(preKeyId)))
|
||||
.go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storePreKey(int preKeyId, PreKeyRecord record) async {
|
||||
if (!await containsPreKey(preKeyId)) {
|
||||
await dbProvider.db!.insert(DB.tableName,
|
||||
{DB.columnPreKeyId: preKeyId, DB.columnPreKey: record.serialize()});
|
||||
} else {
|
||||
await dbProvider.db!.update(
|
||||
DB.tableName, {DB.columnPreKey: record.serialize()},
|
||||
where: '${DB.columnPreKeyId} = ?', whereArgs: <Object?>[preKeyId]);
|
||||
final preKeyCompanion = SignalPreKeyStoresCompanion(
|
||||
preKeyId: Value(preKeyId),
|
||||
preKey: Value(record.serialize()),
|
||||
);
|
||||
|
||||
try {
|
||||
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/src/model/sender_key_store_model.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||
|
||||
typedef DB = DbSignalSenderKeyStore;
|
||||
|
||||
class ConnectSenderKeyStore extends SenderKeyStore {
|
||||
@override
|
||||
Future<SenderKeyRecord> loadSenderKey(SenderKeyName senderKeyName) async {
|
||||
final dbSenderKey = await dbProvider.db!.query(DB.tableName,
|
||||
columns: [DB.columnSenderKey],
|
||||
where: '${DB.columnSenderKeyName} = ?',
|
||||
whereArgs: <Object?>[senderKeyName.serialize()]);
|
||||
if (dbSenderKey.isEmpty) {
|
||||
SignalSenderKeyStore? identity =
|
||||
await (twonlyDatabase.select(twonlyDatabase.signalSenderKeyStores)
|
||||
..where((t) => t.senderKeyName.equals(senderKeyName.serialize())))
|
||||
.getSingleOrNull();
|
||||
if (identity == null) {
|
||||
throw InvalidKeyIdException(
|
||||
'No such sender key record! - $senderKeyName');
|
||||
}
|
||||
Uint8List preKey = dbSenderKey.first.cast()[DB.columnSenderKey];
|
||||
return SenderKeyRecord.fromSerialized(preKey);
|
||||
return SenderKeyRecord.fromSerialized(identity.senderKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storeSenderKey(
|
||||
SenderKeyName senderKeyName, SenderKeyRecord record) async {
|
||||
await dbProvider.db!.insert(DB.tableName, {
|
||||
DB.columnSenderKeyName: senderKeyName.serialize(),
|
||||
DB.columnSenderKey: record.serialize()
|
||||
});
|
||||
await twonlyDatabase.into(twonlyDatabase.signalSenderKeyStores).insert(
|
||||
SignalSenderKeyStoresCompanion(
|
||||
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/src/model/session_store_model.dart';
|
||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||
|
||||
// make it easier to read
|
||||
typedef DB = DbSignalSessionStore;
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
|
||||
class ConnectSessionStore extends SessionStore {
|
||||
ConnectSessionStore();
|
||||
|
||||
@override
|
||||
Future<bool> containsSession(SignalProtocolAddress address) async {
|
||||
var list = (await dbProvider.db!.query(DB.tableName,
|
||||
columns: [],
|
||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]));
|
||||
return list.isNotEmpty;
|
||||
final sessions =
|
||||
await (twonlyDatabase.select(twonlyDatabase.signalSessionStores)
|
||||
..where((tbl) =>
|
||||
tbl.deviceId.equals(address.getDeviceId()) &
|
||||
tbl.name.equals(address.getName())))
|
||||
.get();
|
||||
return sessions.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAllSessions(String name) async {
|
||||
await dbProvider.db!.delete(DB.tableName,
|
||||
where: '${DB.columnName} = ?', whereArgs: <Object?>[name]);
|
||||
await (twonlyDatabase.delete(twonlyDatabase.signalSessionStores)
|
||||
..where((tbl) => tbl.name.equals(name)))
|
||||
.go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteSession(SignalProtocolAddress address) async {
|
||||
await dbProvider.db!.delete(DB.tableName,
|
||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]);
|
||||
await (twonlyDatabase.delete(twonlyDatabase.signalSessionStores)
|
||||
..where((tbl) =>
|
||||
tbl.deviceId.equals(address.getDeviceId()) &
|
||||
tbl.name.equals(address.getName())))
|
||||
.go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<int>> getSubDeviceSessions(String name) async {
|
||||
var deviceIds = (await dbProvider.db!.query(DB.tableName,
|
||||
columns: [DB.columnDeviceId],
|
||||
where: '${DB.columnDeviceId} != 1 AND ${DB.columnName} = ?',
|
||||
whereArgs: <Object?>[name]));
|
||||
return deviceIds.cast();
|
||||
final deviceIds = await (twonlyDatabase
|
||||
.select(twonlyDatabase.signalSessionStores)
|
||||
..where(
|
||||
(tbl) => tbl.deviceId.equals(1).not() & tbl.name.equals(name)))
|
||||
.get();
|
||||
return deviceIds.map((row) => row.deviceId).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SessionRecord> loadSession(SignalProtocolAddress address) async {
|
||||
var dbSession = (await dbProvider.db!.query(DB.tableName,
|
||||
columns: [DB.columnSessionRecord],
|
||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]));
|
||||
final dbSession =
|
||||
await (twonlyDatabase.select(twonlyDatabase.signalSessionStores)
|
||||
..where((tbl) =>
|
||||
tbl.deviceId.equals(address.getDeviceId()) &
|
||||
tbl.name.equals(address.getName())))
|
||||
.get();
|
||||
|
||||
if (dbSession.isEmpty) {
|
||||
return SessionRecord();
|
||||
}
|
||||
Uint8List session = dbSession.first.cast()[DB.columnSessionRecord];
|
||||
Uint8List session = dbSession.first.sessionRecord;
|
||||
return SessionRecord.fromSerialized(session);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storeSession(
|
||||
SignalProtocolAddress address, SessionRecord record) async {
|
||||
final sessionCompanion = SignalSessionStoresCompanion(
|
||||
deviceId: Value(address.getDeviceId()),
|
||||
name: Value(address.getName()),
|
||||
sessionRecord: Value(record.serialize()),
|
||||
);
|
||||
|
||||
if (!await containsSession(address)) {
|
||||
await dbProvider.db!.insert(DB.tableName, {
|
||||
DB.columnDeviceId: address.getDeviceId(),
|
||||
DB.columnName: address.getName(),
|
||||
DB.columnSessionRecord: record.serialize()
|
||||
});
|
||||
await twonlyDatabase
|
||||
.into(twonlyDatabase.signalSessionStores)
|
||||
.insert(sessionCompanion);
|
||||
} else {
|
||||
await dbProvider.db!.update(
|
||||
DB.tableName, {DB.columnSessionRecord: record.serialize()},
|
||||
where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
|
||||
whereArgs: <Object?>[address.getDeviceId(), address.getName()]);
|
||||
await (twonlyDatabase.update(twonlyDatabase.signalSessionStores)
|
||||
..where((tbl) =>
|
||||
tbl.deviceId.equals(address.getDeviceId()) &
|
||||
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:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/proto/api/error.pb.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);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final logFile = File('${directory.path}/app.log');
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import 'dart:io';
|
|||
import 'dart:typed_data';
|
||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/model/json/signal_identity.dart';
|
||||
import 'package:twonly/src/model/json/user_data.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
import 'package:twonly/src/json_models/signal_identity.dart';
|
||||
import 'package:twonly/src/json_models/userdata.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/utils/misc.dart';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import 'dart:convert';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/globals.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';
|
||||
|
||||
Future<bool> isUserCreated() async {
|
||||
|
|
@ -15,7 +14,7 @@ Future<bool> isUserCreated() async {
|
|||
|
||||
Future<UserData?> getUser() async {
|
||||
final storage = getSecureStorage();
|
||||
String? userJson = await storage.read(key: "user_data");
|
||||
String? userJson = await storage.read(key: "userData");
|
||||
if (userJson == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -30,16 +29,11 @@ Future<UserData?> getUser() async {
|
|||
}
|
||||
|
||||
Future<bool> deleteLocalUserData() async {
|
||||
final storage = getSecureStorage();
|
||||
var password = await storage.read(key: "sqflite_database_password");
|
||||
await dbProvider.remove();
|
||||
|
||||
final appDir = await getApplicationSupportDirectory();
|
||||
if (appDir.existsSync()) {
|
||||
appDir.deleteSync(recursive: true);
|
||||
}
|
||||
|
||||
await storage.write(key: "sqflite_database_password", value: password);
|
||||
final storage = getSecureStorage();
|
||||
await storage.deleteAll();
|
||||
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/media_view_sizing.dart';
|
||||
import 'package:twonly/src/components/notification_badge.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/providers/api/media.dart';
|
||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
|
@ -55,8 +55,9 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
|
||||
Future updateAsync(int userId) async {
|
||||
if (sendNextMediaToUserName != null) return;
|
||||
Contact? contact =
|
||||
await twonlyDatabase.getContactByUserId(userId).getSingleOrNull();
|
||||
Contact? contact = await twonlyDatabase.contactsDao
|
||||
.getContactByUserId(userId)
|
||||
.getSingleOrNull();
|
||||
if (contact != null) {
|
||||
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/initialsavatar.dart';
|
||||
import 'package:twonly/src/components/verified_shield.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/providers/api/media.dart';
|
||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
|
@ -54,7 +54,7 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
}
|
||||
|
||||
Stream<List<Contact>> allContacts =
|
||||
twonlyDatabase.watchContactsForChatList();
|
||||
twonlyDatabase.contactsDao.watchContactsForChatList();
|
||||
|
||||
contactSub = allContacts.listen((allContacts) {
|
||||
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/message_send_state_icon.dart';
|
||||
import 'package:twonly/src/components/verified_shield.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/messages_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
import 'package:twonly/src/providers/api/api.dart';
|
||||
import 'package:twonly/src/providers/api/media.dart';
|
||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||
|
|
@ -158,8 +158,9 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
|||
}
|
||||
|
||||
Future initStreams() async {
|
||||
await twonlyDatabase.removeOldMessages();
|
||||
Stream<Contact> contact = twonlyDatabase.watchContact(widget.userid);
|
||||
await twonlyDatabase.messagesDao.removeOldMessages();
|
||||
Stream<Contact> contact =
|
||||
twonlyDatabase.contactsDao.watchContact(widget.userid);
|
||||
userSub = contact.listen((contact) {
|
||||
setState(() {
|
||||
user = contact;
|
||||
|
|
@ -167,7 +168,7 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
|||
});
|
||||
|
||||
Stream<List<Message>> msgStream =
|
||||
twonlyDatabase.watchAllMessagesFrom(widget.userid);
|
||||
twonlyDatabase.messagesDao.watchAllMessagesFrom(widget.userid);
|
||||
messageSub = msgStream.listen((msgs) {
|
||||
if (!context.mounted) return;
|
||||
var updated = false;
|
||||
|
|
@ -181,7 +182,7 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
|||
}
|
||||
}
|
||||
if (updated) {
|
||||
twonlyDatabase.openedAllTextMessages(widget.userid);
|
||||
twonlyDatabase.messagesDao.openedAllTextMessages(widget.userid);
|
||||
} else {
|
||||
// The stream should be get an update, so only update the UI when all are opened
|
||||
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/notification_badge.dart';
|
||||
import 'package:twonly/src/components/user_context_menu.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/messages_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
import 'package:twonly/src/providers/api/media.dart';
|
||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
|
@ -50,7 +50,7 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
// title:
|
||||
actions: [
|
||||
StreamBuilder(
|
||||
stream: twonlyDatabase.watchContactsRequested(),
|
||||
stream: twonlyDatabase.contactsDao.watchContactsRequested(),
|
||||
builder: (context, snapshot) {
|
||||
var count = 0;
|
||||
if (snapshot.hasData && snapshot.data != null) {
|
||||
|
|
@ -86,7 +86,7 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
],
|
||||
),
|
||||
body: StreamBuilder(
|
||||
stream: twonlyDatabase.watchContactsForChatList(),
|
||||
stream: twonlyDatabase.contactsDao.watchContactsForChatList(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData || snapshot.data == null) {
|
||||
return Container();
|
||||
|
|
@ -190,7 +190,8 @@ class _UserListItem extends State<UserListItem> {
|
|||
child: ListTile(
|
||||
title: Text(getContactDisplayName(widget.user)),
|
||||
subtitle: StreamBuilder(
|
||||
stream: twonlyDatabase.watchLastMessage(widget.user.userId),
|
||||
stream:
|
||||
twonlyDatabase.messagesDao.watchLastMessage(widget.user.userId),
|
||||
builder: (context, lastMessageSnapshot) {
|
||||
if (!lastMessageSnapshot.hasData) {
|
||||
return Container();
|
||||
|
|
@ -200,7 +201,8 @@ class _UserListItem extends State<UserListItem> {
|
|||
}
|
||||
final lastMessage = lastMessageSnapshot.data!.first;
|
||||
return StreamBuilder(
|
||||
stream: twonlyDatabase.watchMessageNotOpened(widget.user.userId),
|
||||
stream: twonlyDatabase.messagesDao
|
||||
.watchMessageNotOpened(widget.user.userId),
|
||||
builder: (context, notOpenedMessagesSnapshot) {
|
||||
if (!lastMessageSnapshot.hasData) {
|
||||
return Container();
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import 'package:provider/provider.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/animate_icon.dart';
|
||||
import 'package:twonly/src/components/media_view_sizing.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/messages_db.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
import 'package:twonly/src/providers/api/api.dart';
|
||||
import 'package:twonly/src/providers/api/media.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 {
|
||||
Stream<List<Message>> messages =
|
||||
twonlyDatabase.watchMediaMessageNotOpened(widget.userId);
|
||||
twonlyDatabase.messagesDao.watchMediaMessageNotOpened(widget.userId);
|
||||
|
||||
_subscription = messages.listen((messages) {
|
||||
for (Message msg in messages) {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import 'dart:async';
|
|||
import 'package:drift/drift.dart' hide Column;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/headline.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';
|
||||
// ignore: library_prefixes
|
||||
import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
||||
|
|
@ -49,7 +49,8 @@ class _SearchUsernameView extends State<SearchUsernameView> {
|
|||
return;
|
||||
}
|
||||
|
||||
int added = await twonlyDatabase.insertContact(ContactsCompanion(
|
||||
int added =
|
||||
await twonlyDatabase.contactsDao.insertContact(ContactsCompanion(
|
||||
username: Value(searchUserName.text),
|
||||
userId: Value(res.value.userdata.userId.toInt()),
|
||||
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(
|
||||
appBar: AppBar(
|
||||
|
|
@ -195,8 +197,8 @@ class _ContactsListViewState extends State<ContactsListView> {
|
|||
color: const Color.fromARGB(164, 244, 67, 54)),
|
||||
onPressed: () async {
|
||||
final update = ContactsCompanion(blocked: Value(true));
|
||||
await twonlyDatabase.updateContact(
|
||||
contact.userId, update);
|
||||
await twonlyDatabase.contactsDao
|
||||
.updateContact(contact.userId, update);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -205,7 +207,7 @@ class _ContactsListViewState extends State<ContactsListView> {
|
|||
child: IconButton(
|
||||
icon: Icon(Icons.close, color: Colors.red),
|
||||
onPressed: () async {
|
||||
await twonlyDatabase
|
||||
await twonlyDatabase.contactsDao
|
||||
.deleteContactByUserId(contact.userId);
|
||||
encryptAndSendMessage(
|
||||
null,
|
||||
|
|
@ -223,7 +225,8 @@ class _ContactsListViewState extends State<ContactsListView> {
|
|||
icon: Icon(Icons.check, color: Colors.green),
|
||||
onPressed: () async {
|
||||
final update = ContactsCompanion(accepted: Value(true));
|
||||
await twonlyDatabase.updateContact(contact.userId, update);
|
||||
await twonlyDatabase.contactsDao
|
||||
.updateContact(contact.userId, update);
|
||||
encryptAndSendMessage(
|
||||
null,
|
||||
contact.userId,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import 'package:qr_flutter/qr_flutter.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/format_long_string.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/signal.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
|
@ -37,7 +37,7 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Stream<Contact?> contact = twonlyDatabase
|
||||
Stream<Contact?> contact = twonlyDatabase.contactsDao
|
||||
.getContactByUserId(widget.contact.userId)
|
||||
.watchSingleOrNull();
|
||||
|
||||
|
|
@ -145,7 +145,8 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
|||
onPressed: () {
|
||||
final update =
|
||||
ContactsCompanion(verified: Value(false));
|
||||
twonlyDatabase.updateContact(contact.userId, update);
|
||||
twonlyDatabase.contactsDao
|
||||
.updateContact(contact.userId, update);
|
||||
},
|
||||
label: Text(
|
||||
context.lang.contactVerifyNumberClearVerification),
|
||||
|
|
@ -155,7 +156,8 @@ class _ContactVerifyViewState extends State<ContactVerifyView> {
|
|||
icon: FaIcon(FontAwesomeIcons.shieldHeart),
|
||||
onPressed: () {
|
||||
final update = ContactsCompanion(verified: Value(true));
|
||||
twonlyDatabase.updateContact(contact.userId, update);
|
||||
twonlyDatabase.contactsDao
|
||||
.updateContact(contact.userId, update);
|
||||
},
|
||||
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/verified_shield.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/contact/contact_verify_view.dart';
|
||||
|
||||
|
|
@ -24,8 +24,9 @@ class ContactView extends StatefulWidget {
|
|||
class _ContactViewState extends State<ContactView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Stream<Contact?> contact =
|
||||
twonlyDatabase.getContactByUserId(widget.userId).watchSingleOrNull();
|
||||
Stream<Contact?> contact = twonlyDatabase.contactsDao
|
||||
.getContactByUserId(widget.userId)
|
||||
.watchSingleOrNull();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
@ -79,7 +80,8 @@ class _ContactViewState extends State<ContactView> {
|
|||
|
||||
if (context.mounted && nickName != null && 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) {
|
||||
final update = ContactsCompanion(blocked: Value(true));
|
||||
if (context.mounted) {
|
||||
await twonlyDatabase.updateContact(
|
||||
contact.userId, update);
|
||||
await twonlyDatabase.contactsDao
|
||||
.updateContact(contact.userId, update);
|
||||
}
|
||||
if (context.mounted) {
|
||||
Navigator.popUntil(context, (route) => route.isFirst);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:twonly/globals.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.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/signal.dart';
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
username: username,
|
||||
displayName: username,
|
||||
);
|
||||
storage.write(key: "user_data", value: jsonEncode(userData));
|
||||
storage.write(key: "userData", value: jsonEncode(userData));
|
||||
}
|
||||
|
||||
setState(() {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class _PrivacyViewState extends State<PrivacyView> {
|
|||
ListTile(
|
||||
title: Text(context.lang.settingsPrivacyBlockUsers),
|
||||
subtitle: StreamBuilder(
|
||||
stream: twonlyDatabase.watchContactsBlocked(),
|
||||
stream: twonlyDatabase.contactsDao.watchContactsBlocked(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data != null) {
|
||||
return Text(
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import 'package:drift/drift.dart' hide Column;
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/database/contacts_db.dart';
|
||||
import 'package:twonly/src/database/database.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
class PrivacyViewBlockUsers extends StatefulWidget {
|
||||
|
|
@ -21,7 +21,7 @@ class _PrivacyViewBlockUsers extends State<PrivacyViewBlockUsers> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
allUsers = twonlyDatabase.watchAllContacts();
|
||||
allUsers = twonlyDatabase.contactsDao.watchAllContacts();
|
||||
loadAsync();
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ class UserList extends StatelessWidget {
|
|||
Future block(BuildContext context, int userId, bool? value) async {
|
||||
if (value != null) {
|
||||
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:twonly/src/components/better_list_title.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:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
|
|
|
|||
114
pubspec.lock
114
pubspec.lock
|
|
@ -265,14 +265,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -410,7 +402,7 @@ packages:
|
|||
source: hosted
|
||||
version: "3.10.2"
|
||||
fixnum:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
|
|
@ -422,14 +414,6 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -669,14 +653,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1189,14 +1165,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1221,62 +1189,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1330,22 +1242,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1402,14 +1298,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -15,16 +15,13 @@ dependencies:
|
|||
# path: ../CamerAwesome
|
||||
collection: ^1.18.0
|
||||
connectivity_plus: ^6.1.2
|
||||
cv: ^1.1.3
|
||||
drift: ^2.25.1
|
||||
drift_flutter: ^0.2.4
|
||||
exif: ^3.3.0
|
||||
firebase_core: ^3.11.0
|
||||
firebase_messaging: ^15.2.2
|
||||
fixnum: ^1.1.1
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_foreground_task: ^8.17.0
|
||||
flutter_image_compress: ^2.4.0
|
||||
flutter_local_notifications: ^18.0.1
|
||||
flutter_localizations:
|
||||
|
|
@ -32,7 +29,6 @@ dependencies:
|
|||
flutter_secure_storage: ^10.0.0-beta.4
|
||||
font_awesome_flutter: ^10.8.0
|
||||
gal: ^2.3.1
|
||||
google_fonts: ^6.2.1
|
||||
hand_signature: ^3.0.3
|
||||
hive: ^2.2.3
|
||||
image: ^4.3.0
|
||||
|
|
@ -54,10 +50,8 @@ dependencies:
|
|||
cryptography_flutter_plus: ^2.3.2
|
||||
provider: ^6.1.2
|
||||
qr_flutter: ^4.1.0
|
||||
reorderables: ^0.6.0
|
||||
restart_app: ^1.3.2
|
||||
screenshot: ^3.0.0
|
||||
sqflite_sqlcipher: ^3.1.0+1
|
||||
url_launcher: ^6.3.1
|
||||
web_socket_channel: ^3.0.1
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue