From f7306fe7db03d7810dc78a17ae189c6e1ebd7da5 Mon Sep 17 00:00:00 2001 From: otsmr Date: Sun, 9 Mar 2025 00:06:57 +0100 Subject: [PATCH] start using drift --- .../archives}/contacts_model.dart | 0 .../archives}/messages_model.dart | 0 lib/globals.dart | 2 +- lib/main.dart | 13 +- lib/src/app.dart | 29 - lib/src/components/best_friends_selector.dart | 38 +- lib/src/components/flame.dart | 2 +- lib/src/components/initialsavatar.dart | 3 +- .../components/message_send_state_icon.dart | 90 +- lib/src/components/user_context_menu.dart | 26 +- lib/src/components/verified_shield.dart | 2 +- lib/src/database/contacts_db.dart | 53 + lib/src/database/database.dart | 113 + lib/src/database/database.g.dart | 2186 +++++++++++++++++ lib/src/database/messages_db.dart | 32 + lib/src/localization/app_de.arb | 3 + lib/src/localization/app_en.arb | 3 + lib/src/model/json/message.dart | 95 +- lib/src/model/json/signal_identity.dart | 4 +- lib/src/model/json/signal_identity.g.dart | 5 +- lib/src/model/json/user_data.dart | 5 +- lib/src/model/json/user_data.g.dart | 4 +- lib/src/providers/api/api.dart | 18 +- lib/src/providers/api/server_messages.dart | 6 +- lib/src/providers/api_provider.dart | 10 +- .../providers/contacts_change_provider.dart | 33 - lib/src/providers/db_provider.dart | 4 - .../providers/download_change_provider.dart | 20 - .../providers/messages_change_provider.dart | 76 - lib/src/services/fcm_service.dart | 25 +- lib/src/services/notification_service.dart | 14 +- lib/src/tasks/websocket_foreground_task.dart | 65 - lib/src/utils/json.dart | 15 - lib/src/utils/misc.dart | 80 +- lib/src/utils/signal.dart | 13 +- .../share_image_editor_view.dart | 2 +- .../camera_to_share/share_image_view.dart | 2 +- .../views/chats/chat_item_details_view.dart | 4 +- lib/src/views/chats/chat_list_view.dart | 323 +-- lib/src/views/chats/media_viewer_view.dart | 4 +- lib/src/views/chats/search_username_view.dart | 92 +- .../views/contact/contact_verify_view.dart | 73 +- lib/src/views/contact/contact_view.dart | 170 +- lib/src/views/settings/privacy_view.dart | 25 +- .../settings/privacy_view_block_users.dart | 84 +- .../views/settings/settings_main_view.dart | 2 +- pubspec.lock | 72 +- pubspec.yaml | 5 +- 48 files changed, 3138 insertions(+), 807 deletions(-) rename {lib/src/model => .blocked/archives}/contacts_model.dart (100%) rename {lib/src/model => .blocked/archives}/messages_model.dart (100%) create mode 100644 lib/src/database/contacts_db.dart create mode 100644 lib/src/database/database.dart create mode 100644 lib/src/database/database.g.dart create mode 100644 lib/src/database/messages_db.dart delete mode 100644 lib/src/providers/contacts_change_provider.dart delete mode 100644 lib/src/providers/download_change_provider.dart delete mode 100644 lib/src/providers/messages_change_provider.dart delete mode 100644 lib/src/tasks/websocket_foreground_task.dart diff --git a/lib/src/model/contacts_model.dart b/.blocked/archives/contacts_model.dart similarity index 100% rename from lib/src/model/contacts_model.dart rename to .blocked/archives/contacts_model.dart diff --git a/lib/src/model/messages_model.dart b/.blocked/archives/messages_model.dart similarity index 100% rename from lib/src/model/messages_model.dart rename to .blocked/archives/messages_model.dart diff --git a/lib/globals.dart b/lib/globals.dart index 802bafe..0d801e1 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -1,5 +1,5 @@ import 'package:twonly/src/providers/api_provider.dart'; import 'package:twonly/src/providers/db_provider.dart'; -late DbProvider dbProvider; late ApiProvider apiProvider; +late DbProvider dbProvider; diff --git a/lib/main.dart b/lib/main.dart index d443f8e..1a03b08 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,14 +2,12 @@ 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/providers/api/api.dart'; import 'package:twonly/src/providers/api_provider.dart'; -import 'package:twonly/src/providers/db_provider.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; -import 'package:twonly/src/providers/download_change_provider.dart'; -import 'package:twonly/src/providers/messages_change_provider.dart'; -import 'package:twonly/src/providers/contacts_change_provider.dart'; +import 'package:twonly/src/providers/db_provider.dart'; import 'package:twonly/src/providers/send_next_media_to.dart'; import 'package:twonly/src/providers/settings_change_provider.dart'; import 'package:twonly/src/services/fcm_service.dart'; @@ -49,9 +47,10 @@ void main() async { runApp( MultiProvider( providers: [ - ChangeNotifierProvider(create: (_) => MessagesChangeProvider()), - ChangeNotifierProvider(create: (_) => DownloadChangeProvider()), - ChangeNotifierProvider(create: (_) => ContactChangeProvider()), + Provider( + create: (context) => TwonlyDatabase(), + dispose: (context, db) => db.close(), + ), ChangeNotifierProvider(create: (_) => SendNextMediaTo()), ChangeNotifierProvider(create: (_) => settingsController), ], diff --git a/lib/src/app.dart b/lib/src/app.dart index 6bae247..e091031 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -1,9 +1,6 @@ import 'package:provider/provider.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/src/components/connection_state.dart'; -import 'package:twonly/src/providers/contacts_change_provider.dart'; -import 'package:twonly/src/providers/download_change_provider.dart'; -import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/providers/settings_change_provider.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/onboarding/onboarding_view.dart'; @@ -23,9 +20,6 @@ Function(bool) globalCallbackConnectionState = (a) {}; bool globalIsAppInBackground = true; // these two callbacks are called on updated to the corresponding database -Function globalCallBackOnContactChange = () {}; -Future Function(int, int?) globalCallBackOnMessageChange = (a, b) async {}; -Function(List, bool) globalCallBackOnDownloadChange = (a, b) {}; /// The Widget that configures your application. class MyApp extends StatefulWidget { @@ -45,10 +39,6 @@ class _MyAppState extends State with WidgetsBindingObserver { globalIsAppInBackground = false; WidgetsBinding.instance.addObserver(this); - // init change provider to load data from the database - context.read().update(); - context.read().init(); - // register global callbacks to the widget tree globalCallbackConnectionState = (isConnected) { setState(() { @@ -56,20 +46,6 @@ class _MyAppState extends State with WidgetsBindingObserver { }); }; - globalCallBackOnContactChange = () { - context.read().update(); - }; - - globalCallBackOnDownloadChange = (token, add) { - context.read().update(token, add); - }; - - globalCallBackOnMessageChange = (userId, messageId) async { - await context - .read() - .updateLastMessageFor(userId, messageId); - }; - // WidgetsBinding.instance.addPostFrameCallback((_) { // _requestPermissions(); // _initService(); @@ -124,8 +100,6 @@ class _MyAppState extends State with WidgetsBindingObserver { if (wasPaused) { globalIsAppInBackground = false; apiProvider.connect(); - context.read().update(); - context.read().init(); // _stopService(); } } else if (state == AppLifecycleState.paused) { @@ -145,9 +119,6 @@ class _MyAppState extends State with WidgetsBindingObserver { WidgetsBinding.instance.removeObserver(this); // disable globalCallbacks to the flutter tree globalCallbackConnectionState = (a) {}; - globalCallBackOnDownloadChange = (a, b) {}; - globalCallBackOnContactChange = () {}; - globalCallBackOnMessageChange = (a, b) async {}; super.dispose(); } diff --git a/lib/src/components/best_friends_selector.dart b/lib/src/components/best_friends_selector.dart index d5ea1d6..bc35b77 100644 --- a/lib/src/components/best_friends_selector.dart +++ b/lib/src/components/best_friends_selector.dart @@ -1,19 +1,17 @@ import 'dart:collection'; -import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:twonly/src/components/verified_shield.dart'; -import 'package:twonly/src/providers/messages_change_provider.dart'; +import 'package:twonly/src/database/contacts_db.dart'; +import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/utils/misc.dart'; 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/model/contacts_model.dart'; class BestFriendsSelector extends StatelessWidget { final List users; - final Function(Int64, bool) updateStatus; - final HashSet selectedUserIds; + final Function(int, bool) updateStatus; + final HashSet selectedUserIds; final int maxTotalMediaCounter; final bool isRealTwonly; @@ -80,7 +78,7 @@ class BestFriendsSelector extends StatelessWidget { class UserCheckbox extends StatelessWidget { final Contact user; - final Function(Int64, bool) onChanged; + final Function(int, bool) onChanged; final bool isChecked; final bool isRealTwonly; final int maxTotalMediaCounter; @@ -96,10 +94,7 @@ class UserCheckbox extends StatelessWidget { @override Widget build(BuildContext context) { - int flameCounter = context - .watch() - .flamesCounter[user.userId.toInt()] ?? - 0; + String displayName = getContactDisplayName(user); return Container( padding: @@ -120,8 +115,8 @@ class UserCheckbox extends StatelessWidget { child: Row( children: [ InitialsAvatar( + displayName, fontSize: 12, - displayName: user.displayName, ), SizedBox(width: 8), Column( @@ -138,16 +133,23 @@ class UserCheckbox extends StatelessWidget { size: 12, )), Text( - user.displayName.length > 10 - ? '${user.displayName.substring(0, 10)}...' - : user.displayName, + displayName.length > 10 + ? '${displayName.substring(0, 10)}...' + : displayName, overflow: TextOverflow.ellipsis, ), ], ), - if (flameCounter > 0) - FlameCounterWidget( - user, flameCounter, maxTotalMediaCounter), + StreamBuilder( + stream: context.db.watchFlameCounter(user.userId), + builder: (context, snapshot) { + if (!snapshot.hasData && snapshot.data! != 0) { + return Container(); + } + return FlameCounterWidget( + user, snapshot.data!, maxTotalMediaCounter); + }, + ) ], ), Expanded(child: Container()), diff --git a/lib/src/components/flame.dart b/lib/src/components/flame.dart index 5ed8ebd..d7cba3e 100644 --- a/lib/src/components/flame.dart +++ b/lib/src/components/flame.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:twonly/src/components/animate_icon.dart'; -import 'package:twonly/src/model/contacts_model.dart'; +import 'package:twonly/src/database/database.dart'; class FlameCounterWidget extends StatelessWidget { final Contact user; diff --git a/lib/src/components/initialsavatar.dart b/lib/src/components/initialsavatar.dart index f8ccedf..7636c49 100644 --- a/lib/src/components/initialsavatar.dart +++ b/lib/src/components/initialsavatar.dart @@ -4,8 +4,7 @@ class InitialsAvatar extends StatelessWidget { final String displayName; final double? fontSize; - const InitialsAvatar( - {super.key, required this.displayName, this.fontSize = 20}); + const InitialsAvatar(this.displayName, {super.key, this.fontSize = 20}); @override Widget build(BuildContext context) { diff --git a/lib/src/components/message_send_state_icon.dart b/lib/src/components/message_send_state_icon.dart index a60c8c0..61c0bac 100644 --- a/lib/src/components/message_send_state_icon.dart +++ b/lib/src/components/message_send_state_icon.dart @@ -1,9 +1,9 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:provider/provider.dart'; +import 'package:twonly/src/database/database.dart'; +import 'package:twonly/src/database/messages_db.dart'; import 'package:twonly/src/model/json/message.dart'; -import 'package:twonly/src/model/messages_model.dart'; -import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/utils/misc.dart'; enum MessageSendState { @@ -15,21 +15,75 @@ enum MessageSendState { sending, } -class MessageSendStateIcon extends StatelessWidget { - final DbMessage message; +MessageSendState messageSendStateFromMessage(Message msg) { + MessageSendState state; + + if (!msg.acknowledgeByServer) { + state = MessageSendState.sending; + } else { + if (msg.messageOtherId == null) { + // message send + if (msg.openedAt == null) { + state = MessageSendState.send; + } else { + state = MessageSendState.sendOpened; + } + } else { + // message received + if (msg.openedAt == null) { + state = MessageSendState.received; + } else { + state = MessageSendState.receivedOpened; + } + } + } + return state; +} + +class MessageSendStateIcon extends StatefulWidget { + final List messages; final MainAxisAlignment mainAxisAlignment; - const MessageSendStateIcon(this.message, + const MessageSendStateIcon(this.messages, {super.key, this.mainAxisAlignment = MainAxisAlignment.end}); + @override + State createState() => _MessageSendStateIconState(); +} + +class _MessageSendStateIconState extends State { + bool containsVideo = false; + bool containsText = false; + bool containsImage = false; + + @override + void initState() { + super.initState(); + + for (Message msg in widget.messages) { + if (msg.kind == MessageKind.textMessage) { + containsText = true; + } + if (msg.kind == MessageKind.media) { + MessageJson message = + MessageJson.fromJson(jsonDecode(msg.contentJson!)); + final content = message.content; + if (content is MediaMessageContent) { + if (content.isVideo) { + containsVideo = true; + } else { + containsImage = true; + } + } + } + } + } + @override Widget build(BuildContext context) { Widget icon = Placeholder(); String text = ""; - Color color = - message.messageContent.getColor(Theme.of(context).colorScheme.primary); - Widget loaderIcon = Row( children: [ SizedBox( @@ -41,7 +95,9 @@ class MessageSendStateIcon extends StatelessWidget { ], ); - switch (message.getSendState()) { + MessageSendState state = messageSendStateFromMessage(message); + + switch (state) { case MessageSendState.receivedOpened: icon = Icon(Icons.crop_square, size: 14, color: color); text = context.lang.messageSendState_Received; @@ -65,24 +121,16 @@ class MessageSendStateIcon extends StatelessWidget { break; } - if (!message.isDownloaded) { + if (message.downloadState == DownloadState.pending) { text = context.lang.messageSendState_TapToLoad; } - - bool isDownloading = false; - final content = message.messageContent; - if (message.messageReceived && content is MediaMessageContent) { - final test = context.watch().currentlyDownloading; - isDownloading = test.contains(content.downloadToken.toString()); - } - - if (isDownloading) { + if (message.downloadState == DownloadState.downloaded) { text = context.lang.messageSendState_Loading; icon = loaderIcon; } return Row( - mainAxisAlignment: mainAxisAlignment, + mainAxisAlignment: widget.mainAxisAlignment, children: [ icon, const SizedBox(width: 3), diff --git a/lib/src/components/user_context_menu.dart b/lib/src/components/user_context_menu.dart index 60d3722..3158b72 100644 --- a/lib/src/components/user_context_menu.dart +++ b/lib/src/components/user_context_menu.dart @@ -2,17 +2,19 @@ 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/model/contacts_model.dart'; +import 'package:twonly/src/database/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'; import 'package:twonly/src/views/contact/contact_verify_view.dart'; import 'package:twonly/src/views/home_view.dart'; class UserContextMenu extends StatefulWidget { final Widget child; - final Contact user; + final Contact contact; - const UserContextMenu({super.key, required this.user, required this.child}); + const UserContextMenu( + {super.key, required this.contact, required this.child}); @override State createState() => _UserContextMenuState(); @@ -22,38 +24,38 @@ class _UserContextMenuState extends State { @override Widget build(BuildContext context) { return PieMenu( - onPressed: () => print('pressed'), + onPressed: () => (), actions: [ PieAction( - tooltip: const Text('Verify user'), + tooltip: Text(context.lang.contextMenuVerifyUser), onSelect: () { Navigator.push(context, MaterialPageRoute( builder: (context) { - return ContactVerifyView(widget.user); + return ContactVerifyView(widget.contact); }, )); }, - child: widget.user.verified + child: widget.contact.verified ? FaIcon(FontAwesomeIcons.shieldHeart) - : const Icon(Icons.gpp_maybe_rounded), // Can be any widget + : const Icon(Icons.gpp_maybe_rounded), ), PieAction( - tooltip: const Text('Open chat'), + tooltip: Text(context.lang.contextMenuOpenChat), onSelect: () { Navigator.push(context, MaterialPageRoute( builder: (context) { - return ChatItemDetailsView(user: widget.user); + return ChatItemDetailsView(user: widget.contact); }, )); }, child: const FaIcon(FontAwesomeIcons.solidComments), ), PieAction( - tooltip: const Text('Send image'), + tooltip: Text(context.lang.contextMenuSendImage), onSelect: () { context .read() - .updateSendNextMediaTo(widget.user.userId.toInt()); + .updateSendNextMediaTo(widget.contact.userId.toInt()); globalUpdateOfHomeViewPageIndex(0); }, child: const FaIcon(FontAwesomeIcons.camera), diff --git a/lib/src/components/verified_shield.dart b/lib/src/components/verified_shield.dart index 7a4f45a..0891651 100644 --- a/lib/src/components/verified_shield.dart +++ b/lib/src/components/verified_shield.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:twonly/src/model/contacts_model.dart'; +import 'package:twonly/src/database/database.dart'; class VerifiedShield extends StatelessWidget { final Contact contact; diff --git a/lib/src/database/contacts_db.dart b/lib/src/database/contacts_db.dart new file mode 100644 index 0000000..1899212 --- /dev/null +++ b/lib/src/database/contacts_db.dart @@ -0,0 +1,53 @@ +import 'package:drift/drift.dart'; +import 'package:twonly/src/database/database.dart'; + +class Contacts extends Table { + IntColumn get userId => integer()(); + + TextColumn get username => text().unique()(); + TextColumn get displayName => text().nullable()(); + TextColumn get nickName => text().nullable()(); + + BoolColumn get accepted => boolean().withDefault(Constant(false))(); + BoolColumn get requested => boolean().withDefault(Constant(false))(); + BoolColumn get blocked => boolean().withDefault(Constant(false))(); + BoolColumn get verified => boolean().withDefault(Constant(false))(); + + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); + + IntColumn get totalMediaCounter => integer().withDefault(Constant(0))(); + + DateTimeColumn get lastMessageSend => dateTime().nullable()(); + DateTimeColumn get lastMessageReceived => dateTime().nullable()(); + DateTimeColumn get lastMessage => dateTime().nullable()(); + + IntColumn get flameCounter => integer().withDefault(Constant(0))(); + + @override + Set get primaryKey => {userId}; +} + +String getContactDisplayName(Contact user) { + if (user.nickName != null) { + return user.nickName!; + } + if (user.displayName != null) { + return user.displayName!; + } + return user.username; +} + +int getFlameCounterFromContact(Contact contact) { + if (contact.lastMessageSend == null || contact.lastMessageReceived == null) { + return 0; + } + final now = DateTime.now(); + final startOfToday = DateTime(now.year, now.month, now.day); + final twoDaysAgo = startOfToday.subtract(Duration(days: 2)); + if (contact.lastMessageSend!.isBefore(twoDaysAgo) && + contact.lastMessageReceived!.isBefore(twoDaysAgo)) { + return contact.flameCounter; + } else { + return 0; + } +} diff --git a/lib/src/database/database.dart b/lib/src/database/database.dart new file mode 100644 index 0000000..8a328c9 --- /dev/null +++ b/lib/src/database/database.dart @@ -0,0 +1,113 @@ +import 'package:drift/drift.dart'; +import 'package:drift_flutter/drift_flutter.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'; + +// 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> watchMessageNotOpened(int userId) { + return (select(messages) + ..where((t) => t.openedAt.isNull() & t.contactId.equals(userId))) + .watch(); + } + + Stream watchLastMessage(int userId) { + return (select(messages) + ..where((t) => t.contactId.equals(userId)) + ..orderBy([(t) => OrderingTerm.desc(t.sendAt)]) + ..limit(1)) + .watchSingleOrNull(); + } + + // ------------ + + Future insertContact(ContactsCompanion contact) { + return into(contacts).insert(contact); + } + + SingleOrNullSelectable getContactByUserId(int userId) { + return select(contacts)..where((t) => t.userId.equals(userId)); + } + + // Stream getMaxTotalMediaCounter() { + // return customSelect( + // 'SELECT MAX(totalMediaCounter) AS maxTotal FROM contacts', + // readsFrom: {contacts}, + // ).watchSingle().asyncMap((result) { + // return result.read('maxTotal'); + // }); + // } + + Future deleteContactByUserId(int userId) { + return (delete(contacts)..where((t) => t.userId.equals(userId))).go(); + } + + Future updateContact(int userId, ContactsCompanion updatedValues) { + return (update(contacts)..where((c) => c.userId.equals(userId))) + .write(updatedValues); + } + + Stream> watchNotAcceptedContacts() { + return (select(contacts)..where((t) => t.accepted.equals(false))).watch(); + } + + Stream> watchContactsForChatList() { + return (select(contacts) + ..where((t) => t.accepted.equals(true) & t.blocked.equals(false)) + ..orderBy([(t) => OrderingTerm.asc(t.lastMessage)])) + .watch(); + } + + Stream watchContactsBlocked() { + final count = contacts.blocked.count(distinct: true); + final query = selectOnly(contacts)..where(contacts.blocked.equals(true)); + query.addColumns([count]); + return query.map((row) => row.read(count)).watchSingle(); + } + + Stream watchContactsRequested() { + final count = contacts.requested.count(distinct: true); + final query = selectOnly(contacts)..where(contacts.requested.equals(true)); + query.addColumns([count]); + return query.map((row) => row.read(count)).watchSingle(); + } + + Stream> watchAllContacts() { + return select(contacts).watch(); + } + + Stream watchFlameCounter(int userId) { + return (select(contacts) + ..where( + (u) => + u.userId.equals(userId) & + u.lastMessageReceived.isNotNull() & + u.lastMessageSend.isNotNull(), + )) + .watchSingle() + .asyncMap((contact) { + return getFlameCounterFromContact(contact); + }); + } +} diff --git a/lib/src/database/database.g.dart b/lib/src/database/database.g.dart new file mode 100644 index 0000000..389ccb3 --- /dev/null +++ b/lib/src/database/database.g.dart @@ -0,0 +1,2186 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ignore_for_file: type=lint +class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $ContactsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); + @override + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: false); + static const VerificationMeta _usernameMeta = + const VerificationMeta('username'); + @override + late final GeneratedColumn username = GeneratedColumn( + 'username', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE')); + static const VerificationMeta _displayNameMeta = + const VerificationMeta('displayName'); + @override + late final GeneratedColumn displayName = GeneratedColumn( + 'display_name', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + static const VerificationMeta _nickNameMeta = + const VerificationMeta('nickName'); + @override + late final GeneratedColumn nickName = GeneratedColumn( + 'nick_name', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + static const VerificationMeta _acceptedMeta = + const VerificationMeta('accepted'); + @override + late final GeneratedColumn accepted = GeneratedColumn( + 'accepted', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("accepted" IN (0, 1))'), + defaultValue: Constant(false)); + static const VerificationMeta _requestedMeta = + const VerificationMeta('requested'); + @override + late final GeneratedColumn requested = GeneratedColumn( + 'requested', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("requested" IN (0, 1))'), + defaultValue: Constant(false)); + static const VerificationMeta _blockedMeta = + const VerificationMeta('blocked'); + @override + late final GeneratedColumn blocked = GeneratedColumn( + 'blocked', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("blocked" IN (0, 1))'), + defaultValue: Constant(false)); + static const VerificationMeta _verifiedMeta = + const VerificationMeta('verified'); + @override + late final GeneratedColumn verified = GeneratedColumn( + 'verified', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("verified" IN (0, 1))'), + defaultValue: Constant(false)); + static const VerificationMeta _createdAtMeta = + const VerificationMeta('createdAt'); + @override + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + static const VerificationMeta _totalMediaCounterMeta = + const VerificationMeta('totalMediaCounter'); + @override + late final GeneratedColumn totalMediaCounter = GeneratedColumn( + 'total_media_counter', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: Constant(0)); + static const VerificationMeta _lastMessageSendMeta = + const VerificationMeta('lastMessageSend'); + @override + late final GeneratedColumn lastMessageSend = + GeneratedColumn('last_message_send', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + static const VerificationMeta _lastMessageReceivedMeta = + const VerificationMeta('lastMessageReceived'); + @override + late final GeneratedColumn lastMessageReceived = + GeneratedColumn('last_message_received', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + static const VerificationMeta _lastMessageMeta = + const VerificationMeta('lastMessage'); + @override + late final GeneratedColumn lastMessage = GeneratedColumn( + 'last_message', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + static const VerificationMeta _flameCounterMeta = + const VerificationMeta('flameCounter'); + @override + late final GeneratedColumn flameCounter = GeneratedColumn( + 'flame_counter', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: Constant(0)); + @override + List get $columns => [ + userId, + username, + displayName, + nickName, + accepted, + requested, + blocked, + verified, + createdAt, + totalMediaCounter, + lastMessageSend, + lastMessageReceived, + lastMessage, + flameCounter + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'contacts'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + } + if (data.containsKey('username')) { + context.handle(_usernameMeta, + username.isAcceptableOrUnknown(data['username']!, _usernameMeta)); + } else if (isInserting) { + context.missing(_usernameMeta); + } + if (data.containsKey('display_name')) { + context.handle( + _displayNameMeta, + displayName.isAcceptableOrUnknown( + data['display_name']!, _displayNameMeta)); + } + if (data.containsKey('nick_name')) { + context.handle(_nickNameMeta, + nickName.isAcceptableOrUnknown(data['nick_name']!, _nickNameMeta)); + } + if (data.containsKey('accepted')) { + context.handle(_acceptedMeta, + accepted.isAcceptableOrUnknown(data['accepted']!, _acceptedMeta)); + } + if (data.containsKey('requested')) { + context.handle(_requestedMeta, + requested.isAcceptableOrUnknown(data['requested']!, _requestedMeta)); + } + if (data.containsKey('blocked')) { + context.handle(_blockedMeta, + blocked.isAcceptableOrUnknown(data['blocked']!, _blockedMeta)); + } + if (data.containsKey('verified')) { + context.handle(_verifiedMeta, + verified.isAcceptableOrUnknown(data['verified']!, _verifiedMeta)); + } + if (data.containsKey('created_at')) { + context.handle(_createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + } + if (data.containsKey('total_media_counter')) { + context.handle( + _totalMediaCounterMeta, + totalMediaCounter.isAcceptableOrUnknown( + data['total_media_counter']!, _totalMediaCounterMeta)); + } + if (data.containsKey('last_message_send')) { + context.handle( + _lastMessageSendMeta, + lastMessageSend.isAcceptableOrUnknown( + data['last_message_send']!, _lastMessageSendMeta)); + } + if (data.containsKey('last_message_received')) { + context.handle( + _lastMessageReceivedMeta, + lastMessageReceived.isAcceptableOrUnknown( + data['last_message_received']!, _lastMessageReceivedMeta)); + } + if (data.containsKey('last_message')) { + context.handle( + _lastMessageMeta, + lastMessage.isAcceptableOrUnknown( + data['last_message']!, _lastMessageMeta)); + } + if (data.containsKey('flame_counter')) { + context.handle( + _flameCounterMeta, + flameCounter.isAcceptableOrUnknown( + data['flame_counter']!, _flameCounterMeta)); + } + return context; + } + + @override + Set get $primaryKey => {userId}; + @override + Contact map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return Contact( + userId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}user_id'])!, + username: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}username'])!, + displayName: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}display_name']), + nickName: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}nick_name']), + accepted: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}accepted'])!, + requested: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}requested'])!, + blocked: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}blocked'])!, + verified: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}verified'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + totalMediaCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}total_media_counter'])!, + lastMessageSend: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}last_message_send']), + lastMessageReceived: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}last_message_received']), + lastMessage: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}last_message']), + flameCounter: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}flame_counter'])!, + ); + } + + @override + $ContactsTable createAlias(String alias) { + return $ContactsTable(attachedDatabase, alias); + } +} + +class Contact extends DataClass implements Insertable { + final int userId; + final String username; + final String? displayName; + final String? nickName; + final bool accepted; + final bool requested; + final bool blocked; + final bool verified; + final DateTime createdAt; + final int totalMediaCounter; + final DateTime? lastMessageSend; + final DateTime? lastMessageReceived; + final DateTime? lastMessage; + final int flameCounter; + const Contact( + {required this.userId, + required this.username, + this.displayName, + this.nickName, + required this.accepted, + required this.requested, + required this.blocked, + required this.verified, + required this.createdAt, + required this.totalMediaCounter, + this.lastMessageSend, + this.lastMessageReceived, + this.lastMessage, + required this.flameCounter}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['username'] = Variable(username); + if (!nullToAbsent || displayName != null) { + map['display_name'] = Variable(displayName); + } + if (!nullToAbsent || nickName != null) { + map['nick_name'] = Variable(nickName); + } + map['accepted'] = Variable(accepted); + map['requested'] = Variable(requested); + map['blocked'] = Variable(blocked); + map['verified'] = Variable(verified); + map['created_at'] = Variable(createdAt); + map['total_media_counter'] = Variable(totalMediaCounter); + if (!nullToAbsent || lastMessageSend != null) { + map['last_message_send'] = Variable(lastMessageSend); + } + if (!nullToAbsent || lastMessageReceived != null) { + map['last_message_received'] = Variable(lastMessageReceived); + } + if (!nullToAbsent || lastMessage != null) { + map['last_message'] = Variable(lastMessage); + } + map['flame_counter'] = Variable(flameCounter); + return map; + } + + ContactsCompanion toCompanion(bool nullToAbsent) { + return ContactsCompanion( + userId: Value(userId), + username: Value(username), + displayName: displayName == null && nullToAbsent + ? const Value.absent() + : Value(displayName), + nickName: nickName == null && nullToAbsent + ? const Value.absent() + : Value(nickName), + accepted: Value(accepted), + requested: Value(requested), + blocked: Value(blocked), + verified: Value(verified), + createdAt: Value(createdAt), + totalMediaCounter: Value(totalMediaCounter), + lastMessageSend: lastMessageSend == null && nullToAbsent + ? const Value.absent() + : Value(lastMessageSend), + lastMessageReceived: lastMessageReceived == null && nullToAbsent + ? const Value.absent() + : Value(lastMessageReceived), + lastMessage: lastMessage == null && nullToAbsent + ? const Value.absent() + : Value(lastMessage), + flameCounter: Value(flameCounter), + ); + } + + factory Contact.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return Contact( + userId: serializer.fromJson(json['userId']), + username: serializer.fromJson(json['username']), + displayName: serializer.fromJson(json['displayName']), + nickName: serializer.fromJson(json['nickName']), + accepted: serializer.fromJson(json['accepted']), + requested: serializer.fromJson(json['requested']), + blocked: serializer.fromJson(json['blocked']), + verified: serializer.fromJson(json['verified']), + createdAt: serializer.fromJson(json['createdAt']), + totalMediaCounter: serializer.fromJson(json['totalMediaCounter']), + lastMessageSend: serializer.fromJson(json['lastMessageSend']), + lastMessageReceived: + serializer.fromJson(json['lastMessageReceived']), + lastMessage: serializer.fromJson(json['lastMessage']), + flameCounter: serializer.fromJson(json['flameCounter']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'username': serializer.toJson(username), + 'displayName': serializer.toJson(displayName), + 'nickName': serializer.toJson(nickName), + 'accepted': serializer.toJson(accepted), + 'requested': serializer.toJson(requested), + 'blocked': serializer.toJson(blocked), + 'verified': serializer.toJson(verified), + 'createdAt': serializer.toJson(createdAt), + 'totalMediaCounter': serializer.toJson(totalMediaCounter), + 'lastMessageSend': serializer.toJson(lastMessageSend), + 'lastMessageReceived': serializer.toJson(lastMessageReceived), + 'lastMessage': serializer.toJson(lastMessage), + 'flameCounter': serializer.toJson(flameCounter), + }; + } + + Contact copyWith( + {int? userId, + String? username, + Value displayName = const Value.absent(), + Value nickName = const Value.absent(), + bool? accepted, + bool? requested, + bool? blocked, + bool? verified, + DateTime? createdAt, + int? totalMediaCounter, + Value lastMessageSend = const Value.absent(), + Value lastMessageReceived = const Value.absent(), + Value lastMessage = const Value.absent(), + int? flameCounter}) => + Contact( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName.present ? displayName.value : this.displayName, + nickName: nickName.present ? nickName.value : this.nickName, + accepted: accepted ?? this.accepted, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + createdAt: createdAt ?? this.createdAt, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + lastMessageSend: lastMessageSend.present + ? lastMessageSend.value + : this.lastMessageSend, + lastMessageReceived: lastMessageReceived.present + ? lastMessageReceived.value + : this.lastMessageReceived, + lastMessage: lastMessage.present ? lastMessage.value : this.lastMessage, + flameCounter: flameCounter ?? this.flameCounter, + ); + Contact copyWithCompanion(ContactsCompanion data) { + return Contact( + userId: data.userId.present ? data.userId.value : this.userId, + username: data.username.present ? data.username.value : this.username, + displayName: + data.displayName.present ? data.displayName.value : this.displayName, + nickName: data.nickName.present ? data.nickName.value : this.nickName, + accepted: data.accepted.present ? data.accepted.value : this.accepted, + requested: data.requested.present ? data.requested.value : this.requested, + blocked: data.blocked.present ? data.blocked.value : this.blocked, + verified: data.verified.present ? data.verified.value : this.verified, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + totalMediaCounter: data.totalMediaCounter.present + ? data.totalMediaCounter.value + : this.totalMediaCounter, + lastMessageSend: data.lastMessageSend.present + ? data.lastMessageSend.value + : this.lastMessageSend, + lastMessageReceived: data.lastMessageReceived.present + ? data.lastMessageReceived.value + : this.lastMessageReceived, + lastMessage: + data.lastMessage.present ? data.lastMessage.value : this.lastMessage, + flameCounter: data.flameCounter.present + ? data.flameCounter.value + : this.flameCounter, + ); + } + + @override + String toString() { + return (StringBuffer('Contact(') + ..write('userId: $userId, ') + ..write('username: $username, ') + ..write('displayName: $displayName, ') + ..write('nickName: $nickName, ') + ..write('accepted: $accepted, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('createdAt: $createdAt, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastMessage: $lastMessage, ') + ..write('flameCounter: $flameCounter') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + userId, + username, + displayName, + nickName, + accepted, + requested, + blocked, + verified, + createdAt, + totalMediaCounter, + lastMessageSend, + lastMessageReceived, + lastMessage, + flameCounter); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is Contact && + other.userId == this.userId && + other.username == this.username && + other.displayName == this.displayName && + other.nickName == this.nickName && + other.accepted == this.accepted && + other.requested == this.requested && + other.blocked == this.blocked && + other.verified == this.verified && + other.createdAt == this.createdAt && + other.totalMediaCounter == this.totalMediaCounter && + other.lastMessageSend == this.lastMessageSend && + other.lastMessageReceived == this.lastMessageReceived && + other.lastMessage == this.lastMessage && + other.flameCounter == this.flameCounter); +} + +class ContactsCompanion extends UpdateCompanion { + final Value userId; + final Value username; + final Value displayName; + final Value nickName; + final Value accepted; + final Value requested; + final Value blocked; + final Value verified; + final Value createdAt; + final Value totalMediaCounter; + final Value lastMessageSend; + final Value lastMessageReceived; + final Value lastMessage; + final Value flameCounter; + const ContactsCompanion({ + this.userId = const Value.absent(), + this.username = const Value.absent(), + this.displayName = const Value.absent(), + this.nickName = const Value.absent(), + this.accepted = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.createdAt = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastMessage = const Value.absent(), + this.flameCounter = const Value.absent(), + }); + ContactsCompanion.insert({ + this.userId = const Value.absent(), + required String username, + this.displayName = const Value.absent(), + this.nickName = const Value.absent(), + this.accepted = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.createdAt = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastMessage = const Value.absent(), + this.flameCounter = const Value.absent(), + }) : username = Value(username); + static Insertable custom({ + Expression? userId, + Expression? username, + Expression? displayName, + Expression? nickName, + Expression? accepted, + Expression? requested, + Expression? blocked, + Expression? verified, + Expression? createdAt, + Expression? totalMediaCounter, + Expression? lastMessageSend, + Expression? lastMessageReceived, + Expression? lastMessage, + Expression? flameCounter, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (username != null) 'username': username, + if (displayName != null) 'display_name': displayName, + if (nickName != null) 'nick_name': nickName, + if (accepted != null) 'accepted': accepted, + if (requested != null) 'requested': requested, + if (blocked != null) 'blocked': blocked, + if (verified != null) 'verified': verified, + if (createdAt != null) 'created_at': createdAt, + if (totalMediaCounter != null) 'total_media_counter': totalMediaCounter, + if (lastMessageSend != null) 'last_message_send': lastMessageSend, + if (lastMessageReceived != null) + 'last_message_received': lastMessageReceived, + if (lastMessage != null) 'last_message': lastMessage, + if (flameCounter != null) 'flame_counter': flameCounter, + }); + } + + ContactsCompanion copyWith( + {Value? userId, + Value? username, + Value? displayName, + Value? nickName, + Value? accepted, + Value? requested, + Value? blocked, + Value? verified, + Value? createdAt, + Value? totalMediaCounter, + Value? lastMessageSend, + Value? lastMessageReceived, + Value? lastMessage, + Value? flameCounter}) { + return ContactsCompanion( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName ?? this.displayName, + nickName: nickName ?? this.nickName, + accepted: accepted ?? this.accepted, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + createdAt: createdAt ?? this.createdAt, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + lastMessageSend: lastMessageSend ?? this.lastMessageSend, + lastMessageReceived: lastMessageReceived ?? this.lastMessageReceived, + lastMessage: lastMessage ?? this.lastMessage, + flameCounter: flameCounter ?? this.flameCounter, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (displayName.present) { + map['display_name'] = Variable(displayName.value); + } + if (nickName.present) { + map['nick_name'] = Variable(nickName.value); + } + if (accepted.present) { + map['accepted'] = Variable(accepted.value); + } + if (requested.present) { + map['requested'] = Variable(requested.value); + } + if (blocked.present) { + map['blocked'] = Variable(blocked.value); + } + if (verified.present) { + map['verified'] = Variable(verified.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (totalMediaCounter.present) { + map['total_media_counter'] = Variable(totalMediaCounter.value); + } + if (lastMessageSend.present) { + map['last_message_send'] = Variable(lastMessageSend.value); + } + if (lastMessageReceived.present) { + map['last_message_received'] = + Variable(lastMessageReceived.value); + } + if (lastMessage.present) { + map['last_message'] = Variable(lastMessage.value); + } + if (flameCounter.present) { + map['flame_counter'] = Variable(flameCounter.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ContactsCompanion(') + ..write('userId: $userId, ') + ..write('username: $username, ') + ..write('displayName: $displayName, ') + ..write('nickName: $nickName, ') + ..write('accepted: $accepted, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('createdAt: $createdAt, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastMessage: $lastMessage, ') + ..write('flameCounter: $flameCounter') + ..write(')')) + .toString(); + } +} + +class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $MessagesTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _contactIdMeta = + const VerificationMeta('contactId'); + @override + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)')); + static const VerificationMeta _messageIdMeta = + const VerificationMeta('messageId'); + @override + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _messageOtherIdMeta = + const VerificationMeta('messageOtherId'); + @override + late final GeneratedColumn messageOtherId = GeneratedColumn( + 'message_other_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + static const VerificationMeta _responseToMessageIdMeta = + const VerificationMeta('responseToMessageId'); + @override + late final GeneratedColumn responseToMessageId = GeneratedColumn( + 'response_to_message_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + static const VerificationMeta _responseToOtherMessageIdMeta = + const VerificationMeta('responseToOtherMessageId'); + @override + late final GeneratedColumn responseToOtherMessageId = + GeneratedColumn('response_to_other_message_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + static const VerificationMeta _acknowledgeByUserMeta = + const VerificationMeta('acknowledgeByUser'); + @override + late final GeneratedColumn acknowledgeByUser = GeneratedColumn( + 'acknowledge_by_user', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("acknowledge_by_user" IN (0, 1))'), + defaultValue: Constant(false)); + static const VerificationMeta _downloadStateMeta = + const VerificationMeta('downloadState'); + @override + late final GeneratedColumnWithTypeConverter + downloadState = GeneratedColumn('download_state', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true) + .withConverter($MessagesTable.$converterdownloadState); + static const VerificationMeta _acknowledgeByServerMeta = + const VerificationMeta('acknowledgeByServer'); + @override + late final GeneratedColumn acknowledgeByServer = GeneratedColumn( + 'acknowledge_by_server', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("acknowledge_by_server" IN (0, 1))'), + defaultValue: Constant(false)); + static const VerificationMeta _kindMeta = const VerificationMeta('kind'); + @override + late final GeneratedColumnWithTypeConverter kind = + GeneratedColumn('kind', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true) + .withConverter($MessagesTable.$converterkind); + static const VerificationMeta _contentJsonMeta = + const VerificationMeta('contentJson'); + @override + late final GeneratedColumn contentJson = GeneratedColumn( + 'content_json', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + static const VerificationMeta _openedAtMeta = + const VerificationMeta('openedAt'); + @override + late final GeneratedColumn openedAt = GeneratedColumn( + 'opened_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + static const VerificationMeta _sendAtMeta = const VerificationMeta('sendAt'); + @override + late final GeneratedColumn sendAt = GeneratedColumn( + 'send_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + static const VerificationMeta _updatedAtMeta = + const VerificationMeta('updatedAt'); + @override + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + @override + List get $columns => [ + contactId, + messageId, + messageOtherId, + responseToMessageId, + responseToOtherMessageId, + acknowledgeByUser, + downloadState, + acknowledgeByServer, + kind, + contentJson, + openedAt, + sendAt, + updatedAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'messages'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('contact_id')) { + context.handle(_contactIdMeta, + contactId.isAcceptableOrUnknown(data['contact_id']!, _contactIdMeta)); + } else if (isInserting) { + context.missing(_contactIdMeta); + } + if (data.containsKey('message_id')) { + context.handle(_messageIdMeta, + messageId.isAcceptableOrUnknown(data['message_id']!, _messageIdMeta)); + } + if (data.containsKey('message_other_id')) { + context.handle( + _messageOtherIdMeta, + messageOtherId.isAcceptableOrUnknown( + data['message_other_id']!, _messageOtherIdMeta)); + } + if (data.containsKey('response_to_message_id')) { + context.handle( + _responseToMessageIdMeta, + responseToMessageId.isAcceptableOrUnknown( + data['response_to_message_id']!, _responseToMessageIdMeta)); + } + if (data.containsKey('response_to_other_message_id')) { + context.handle( + _responseToOtherMessageIdMeta, + responseToOtherMessageId.isAcceptableOrUnknown( + data['response_to_other_message_id']!, + _responseToOtherMessageIdMeta)); + } + if (data.containsKey('acknowledge_by_user')) { + context.handle( + _acknowledgeByUserMeta, + acknowledgeByUser.isAcceptableOrUnknown( + data['acknowledge_by_user']!, _acknowledgeByUserMeta)); + } + context.handle(_downloadStateMeta, const VerificationResult.success()); + if (data.containsKey('acknowledge_by_server')) { + context.handle( + _acknowledgeByServerMeta, + acknowledgeByServer.isAcceptableOrUnknown( + data['acknowledge_by_server']!, _acknowledgeByServerMeta)); + } + context.handle(_kindMeta, const VerificationResult.success()); + if (data.containsKey('content_json')) { + context.handle( + _contentJsonMeta, + contentJson.isAcceptableOrUnknown( + data['content_json']!, _contentJsonMeta)); + } + if (data.containsKey('opened_at')) { + context.handle(_openedAtMeta, + openedAt.isAcceptableOrUnknown(data['opened_at']!, _openedAtMeta)); + } + if (data.containsKey('send_at')) { + context.handle(_sendAtMeta, + sendAt.isAcceptableOrUnknown(data['send_at']!, _sendAtMeta)); + } + if (data.containsKey('updated_at')) { + context.handle(_updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + } + return context; + } + + @override + Set get $primaryKey => {messageId}; + @override + Message map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return Message( + contactId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}contact_id'])!, + messageId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}message_id'])!, + messageOtherId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}message_other_id']), + responseToMessageId: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}response_to_message_id']), + responseToOtherMessageId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}response_to_other_message_id']), + acknowledgeByUser: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}acknowledge_by_user'])!, + downloadState: $MessagesTable.$converterdownloadState.fromSql( + attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}download_state'])!), + acknowledgeByServer: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}acknowledge_by_server'])!, + kind: $MessagesTable.$converterkind.fromSql(attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}kind'])!), + contentJson: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}content_json']), + openedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}opened_at']), + sendAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}send_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ); + } + + @override + $MessagesTable createAlias(String alias) { + return $MessagesTable(attachedDatabase, alias); + } + + static JsonTypeConverter2 $converterdownloadState = + const EnumIndexConverter(DownloadState.values); + static JsonTypeConverter2 $converterkind = + const EnumNameConverter(MessageKind.values); +} + +class Message extends DataClass implements Insertable { + final int contactId; + final int messageId; + final int? messageOtherId; + final int? responseToMessageId; + final int? responseToOtherMessageId; + final bool acknowledgeByUser; + final DownloadState downloadState; + final bool acknowledgeByServer; + final MessageKind kind; + final String? contentJson; + final DateTime? openedAt; + final DateTime sendAt; + final DateTime updatedAt; + const Message( + {required this.contactId, + required this.messageId, + this.messageOtherId, + this.responseToMessageId, + this.responseToOtherMessageId, + required this.acknowledgeByUser, + required this.downloadState, + required this.acknowledgeByServer, + required this.kind, + this.contentJson, + this.openedAt, + required this.sendAt, + required this.updatedAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['contact_id'] = Variable(contactId); + map['message_id'] = Variable(messageId); + if (!nullToAbsent || messageOtherId != null) { + map['message_other_id'] = Variable(messageOtherId); + } + if (!nullToAbsent || responseToMessageId != null) { + map['response_to_message_id'] = Variable(responseToMessageId); + } + if (!nullToAbsent || responseToOtherMessageId != null) { + map['response_to_other_message_id'] = + Variable(responseToOtherMessageId); + } + map['acknowledge_by_user'] = Variable(acknowledgeByUser); + { + map['download_state'] = Variable( + $MessagesTable.$converterdownloadState.toSql(downloadState)); + } + map['acknowledge_by_server'] = Variable(acknowledgeByServer); + { + map['kind'] = Variable($MessagesTable.$converterkind.toSql(kind)); + } + if (!nullToAbsent || contentJson != null) { + map['content_json'] = Variable(contentJson); + } + if (!nullToAbsent || openedAt != null) { + map['opened_at'] = Variable(openedAt); + } + map['send_at'] = Variable(sendAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + MessagesCompanion toCompanion(bool nullToAbsent) { + return MessagesCompanion( + contactId: Value(contactId), + messageId: Value(messageId), + messageOtherId: messageOtherId == null && nullToAbsent + ? const Value.absent() + : Value(messageOtherId), + responseToMessageId: responseToMessageId == null && nullToAbsent + ? const Value.absent() + : Value(responseToMessageId), + responseToOtherMessageId: responseToOtherMessageId == null && nullToAbsent + ? const Value.absent() + : Value(responseToOtherMessageId), + acknowledgeByUser: Value(acknowledgeByUser), + downloadState: Value(downloadState), + acknowledgeByServer: Value(acknowledgeByServer), + kind: Value(kind), + contentJson: contentJson == null && nullToAbsent + ? const Value.absent() + : Value(contentJson), + openedAt: openedAt == null && nullToAbsent + ? const Value.absent() + : Value(openedAt), + sendAt: Value(sendAt), + updatedAt: Value(updatedAt), + ); + } + + factory Message.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return Message( + contactId: serializer.fromJson(json['contactId']), + messageId: serializer.fromJson(json['messageId']), + messageOtherId: serializer.fromJson(json['messageOtherId']), + responseToMessageId: + serializer.fromJson(json['responseToMessageId']), + responseToOtherMessageId: + serializer.fromJson(json['responseToOtherMessageId']), + acknowledgeByUser: serializer.fromJson(json['acknowledgeByUser']), + downloadState: $MessagesTable.$converterdownloadState + .fromJson(serializer.fromJson(json['downloadState'])), + acknowledgeByServer: + serializer.fromJson(json['acknowledgeByServer']), + kind: $MessagesTable.$converterkind + .fromJson(serializer.fromJson(json['kind'])), + contentJson: serializer.fromJson(json['contentJson']), + openedAt: serializer.fromJson(json['openedAt']), + sendAt: serializer.fromJson(json['sendAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'contactId': serializer.toJson(contactId), + 'messageId': serializer.toJson(messageId), + 'messageOtherId': serializer.toJson(messageOtherId), + 'responseToMessageId': serializer.toJson(responseToMessageId), + 'responseToOtherMessageId': + serializer.toJson(responseToOtherMessageId), + 'acknowledgeByUser': serializer.toJson(acknowledgeByUser), + 'downloadState': serializer.toJson( + $MessagesTable.$converterdownloadState.toJson(downloadState)), + 'acknowledgeByServer': serializer.toJson(acknowledgeByServer), + 'kind': + serializer.toJson($MessagesTable.$converterkind.toJson(kind)), + 'contentJson': serializer.toJson(contentJson), + 'openedAt': serializer.toJson(openedAt), + 'sendAt': serializer.toJson(sendAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + Message copyWith( + {int? contactId, + int? messageId, + Value messageOtherId = const Value.absent(), + Value responseToMessageId = const Value.absent(), + Value responseToOtherMessageId = const Value.absent(), + bool? acknowledgeByUser, + DownloadState? downloadState, + bool? acknowledgeByServer, + MessageKind? kind, + Value contentJson = const Value.absent(), + Value openedAt = const Value.absent(), + DateTime? sendAt, + DateTime? updatedAt}) => + Message( + contactId: contactId ?? this.contactId, + messageId: messageId ?? this.messageId, + messageOtherId: + messageOtherId.present ? messageOtherId.value : this.messageOtherId, + responseToMessageId: responseToMessageId.present + ? responseToMessageId.value + : this.responseToMessageId, + responseToOtherMessageId: responseToOtherMessageId.present + ? responseToOtherMessageId.value + : this.responseToOtherMessageId, + acknowledgeByUser: acknowledgeByUser ?? this.acknowledgeByUser, + downloadState: downloadState ?? this.downloadState, + acknowledgeByServer: acknowledgeByServer ?? this.acknowledgeByServer, + kind: kind ?? this.kind, + contentJson: contentJson.present ? contentJson.value : this.contentJson, + openedAt: openedAt.present ? openedAt.value : this.openedAt, + sendAt: sendAt ?? this.sendAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + Message copyWithCompanion(MessagesCompanion data) { + return Message( + contactId: data.contactId.present ? data.contactId.value : this.contactId, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + messageOtherId: data.messageOtherId.present + ? data.messageOtherId.value + : this.messageOtherId, + responseToMessageId: data.responseToMessageId.present + ? data.responseToMessageId.value + : this.responseToMessageId, + responseToOtherMessageId: data.responseToOtherMessageId.present + ? data.responseToOtherMessageId.value + : this.responseToOtherMessageId, + acknowledgeByUser: data.acknowledgeByUser.present + ? data.acknowledgeByUser.value + : this.acknowledgeByUser, + downloadState: data.downloadState.present + ? data.downloadState.value + : this.downloadState, + acknowledgeByServer: data.acknowledgeByServer.present + ? data.acknowledgeByServer.value + : this.acknowledgeByServer, + kind: data.kind.present ? data.kind.value : this.kind, + contentJson: + data.contentJson.present ? data.contentJson.value : this.contentJson, + openedAt: data.openedAt.present ? data.openedAt.value : this.openedAt, + sendAt: data.sendAt.present ? data.sendAt.value : this.sendAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('Message(') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('messageOtherId: $messageOtherId, ') + ..write('responseToMessageId: $responseToMessageId, ') + ..write('responseToOtherMessageId: $responseToOtherMessageId, ') + ..write('acknowledgeByUser: $acknowledgeByUser, ') + ..write('downloadState: $downloadState, ') + ..write('acknowledgeByServer: $acknowledgeByServer, ') + ..write('kind: $kind, ') + ..write('contentJson: $contentJson, ') + ..write('openedAt: $openedAt, ') + ..write('sendAt: $sendAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + contactId, + messageId, + messageOtherId, + responseToMessageId, + responseToOtherMessageId, + acknowledgeByUser, + downloadState, + acknowledgeByServer, + kind, + contentJson, + openedAt, + sendAt, + updatedAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is Message && + other.contactId == this.contactId && + other.messageId == this.messageId && + other.messageOtherId == this.messageOtherId && + other.responseToMessageId == this.responseToMessageId && + other.responseToOtherMessageId == this.responseToOtherMessageId && + other.acknowledgeByUser == this.acknowledgeByUser && + other.downloadState == this.downloadState && + other.acknowledgeByServer == this.acknowledgeByServer && + other.kind == this.kind && + other.contentJson == this.contentJson && + other.openedAt == this.openedAt && + other.sendAt == this.sendAt && + other.updatedAt == this.updatedAt); +} + +class MessagesCompanion extends UpdateCompanion { + final Value contactId; + final Value messageId; + final Value messageOtherId; + final Value responseToMessageId; + final Value responseToOtherMessageId; + final Value acknowledgeByUser; + final Value downloadState; + final Value acknowledgeByServer; + final Value kind; + final Value contentJson; + final Value openedAt; + final Value sendAt; + final Value updatedAt; + const MessagesCompanion({ + this.contactId = const Value.absent(), + this.messageId = const Value.absent(), + this.messageOtherId = const Value.absent(), + this.responseToMessageId = const Value.absent(), + this.responseToOtherMessageId = const Value.absent(), + this.acknowledgeByUser = const Value.absent(), + this.downloadState = const Value.absent(), + this.acknowledgeByServer = const Value.absent(), + this.kind = const Value.absent(), + this.contentJson = const Value.absent(), + this.openedAt = const Value.absent(), + this.sendAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + MessagesCompanion.insert({ + required int contactId, + this.messageId = const Value.absent(), + this.messageOtherId = const Value.absent(), + this.responseToMessageId = const Value.absent(), + this.responseToOtherMessageId = const Value.absent(), + this.acknowledgeByUser = const Value.absent(), + required DownloadState downloadState, + this.acknowledgeByServer = const Value.absent(), + required MessageKind kind, + this.contentJson = const Value.absent(), + this.openedAt = const Value.absent(), + this.sendAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : contactId = Value(contactId), + downloadState = Value(downloadState), + kind = Value(kind); + static Insertable custom({ + Expression? contactId, + Expression? messageId, + Expression? messageOtherId, + Expression? responseToMessageId, + Expression? responseToOtherMessageId, + Expression? acknowledgeByUser, + Expression? downloadState, + Expression? acknowledgeByServer, + Expression? kind, + Expression? contentJson, + Expression? openedAt, + Expression? sendAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (contactId != null) 'contact_id': contactId, + if (messageId != null) 'message_id': messageId, + if (messageOtherId != null) 'message_other_id': messageOtherId, + if (responseToMessageId != null) + 'response_to_message_id': responseToMessageId, + if (responseToOtherMessageId != null) + 'response_to_other_message_id': responseToOtherMessageId, + if (acknowledgeByUser != null) 'acknowledge_by_user': acknowledgeByUser, + if (downloadState != null) 'download_state': downloadState, + if (acknowledgeByServer != null) + 'acknowledge_by_server': acknowledgeByServer, + if (kind != null) 'kind': kind, + if (contentJson != null) 'content_json': contentJson, + if (openedAt != null) 'opened_at': openedAt, + if (sendAt != null) 'send_at': sendAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + MessagesCompanion copyWith( + {Value? contactId, + Value? messageId, + Value? messageOtherId, + Value? responseToMessageId, + Value? responseToOtherMessageId, + Value? acknowledgeByUser, + Value? downloadState, + Value? acknowledgeByServer, + Value? kind, + Value? contentJson, + Value? openedAt, + Value? sendAt, + Value? updatedAt}) { + return MessagesCompanion( + contactId: contactId ?? this.contactId, + messageId: messageId ?? this.messageId, + messageOtherId: messageOtherId ?? this.messageOtherId, + responseToMessageId: responseToMessageId ?? this.responseToMessageId, + responseToOtherMessageId: + responseToOtherMessageId ?? this.responseToOtherMessageId, + acknowledgeByUser: acknowledgeByUser ?? this.acknowledgeByUser, + downloadState: downloadState ?? this.downloadState, + acknowledgeByServer: acknowledgeByServer ?? this.acknowledgeByServer, + kind: kind ?? this.kind, + contentJson: contentJson ?? this.contentJson, + openedAt: openedAt ?? this.openedAt, + sendAt: sendAt ?? this.sendAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (messageOtherId.present) { + map['message_other_id'] = Variable(messageOtherId.value); + } + if (responseToMessageId.present) { + map['response_to_message_id'] = Variable(responseToMessageId.value); + } + if (responseToOtherMessageId.present) { + map['response_to_other_message_id'] = + Variable(responseToOtherMessageId.value); + } + if (acknowledgeByUser.present) { + map['acknowledge_by_user'] = Variable(acknowledgeByUser.value); + } + if (downloadState.present) { + map['download_state'] = Variable( + $MessagesTable.$converterdownloadState.toSql(downloadState.value)); + } + if (acknowledgeByServer.present) { + map['acknowledge_by_server'] = Variable(acknowledgeByServer.value); + } + if (kind.present) { + map['kind'] = + Variable($MessagesTable.$converterkind.toSql(kind.value)); + } + if (contentJson.present) { + map['content_json'] = Variable(contentJson.value); + } + if (openedAt.present) { + map['opened_at'] = Variable(openedAt.value); + } + if (sendAt.present) { + map['send_at'] = Variable(sendAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessagesCompanion(') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('messageOtherId: $messageOtherId, ') + ..write('responseToMessageId: $responseToMessageId, ') + ..write('responseToOtherMessageId: $responseToOtherMessageId, ') + ..write('acknowledgeByUser: $acknowledgeByUser, ') + ..write('downloadState: $downloadState, ') + ..write('acknowledgeByServer: $acknowledgeByServer, ') + ..write('kind: $kind, ') + ..write('contentJson: $contentJson, ') + ..write('openedAt: $openedAt, ') + ..write('sendAt: $sendAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +abstract class _$TwonlyDatabase extends GeneratedDatabase { + _$TwonlyDatabase(QueryExecutor e) : super(e); + $TwonlyDatabaseManager get managers => $TwonlyDatabaseManager(this); + late final $ContactsTable contacts = $ContactsTable(this); + late final $MessagesTable messages = $MessagesTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [contacts, messages]; +} + +typedef $$ContactsTableCreateCompanionBuilder = ContactsCompanion Function({ + Value userId, + required String username, + Value displayName, + Value nickName, + Value accepted, + Value requested, + Value blocked, + Value verified, + Value createdAt, + Value totalMediaCounter, + Value lastMessageSend, + Value lastMessageReceived, + Value lastMessage, + Value flameCounter, +}); +typedef $$ContactsTableUpdateCompanionBuilder = ContactsCompanion Function({ + Value userId, + Value username, + Value displayName, + Value nickName, + Value accepted, + Value requested, + Value blocked, + Value verified, + Value createdAt, + Value totalMediaCounter, + Value lastMessageSend, + Value lastMessageReceived, + Value lastMessage, + Value flameCounter, +}); + +final class $$ContactsTableReferences + extends BaseReferences<_$TwonlyDatabase, $ContactsTable, Contact> { + $$ContactsTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static MultiTypedResultKey<$MessagesTable, List> _messagesRefsTable( + _$TwonlyDatabase db) => + MultiTypedResultKey.fromTable(db.messages, + aliasName: + $_aliasNameGenerator(db.contacts.userId, db.messages.contactId)); + + $$MessagesTableProcessedTableManager get messagesRefs { + final manager = $$MessagesTableTableManager($_db, $_db.messages).filter( + (f) => f.contactId.userId.sqlEquals($_itemColumn('user_id')!)); + + final cache = $_typedResult.readTableOrNull(_messagesRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } +} + +class $$ContactsTableFilterComposer + extends Composer<_$TwonlyDatabase, $ContactsTable> { + $$ContactsTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get userId => $composableBuilder( + column: $table.userId, builder: (column) => ColumnFilters(column)); + + ColumnFilters get username => $composableBuilder( + column: $table.username, builder: (column) => ColumnFilters(column)); + + ColumnFilters get displayName => $composableBuilder( + column: $table.displayName, builder: (column) => ColumnFilters(column)); + + ColumnFilters get nickName => $composableBuilder( + column: $table.nickName, builder: (column) => ColumnFilters(column)); + + ColumnFilters get accepted => $composableBuilder( + column: $table.accepted, builder: (column) => ColumnFilters(column)); + + ColumnFilters get requested => $composableBuilder( + column: $table.requested, builder: (column) => ColumnFilters(column)); + + ColumnFilters get blocked => $composableBuilder( + column: $table.blocked, builder: (column) => ColumnFilters(column)); + + ColumnFilters get verified => $composableBuilder( + column: $table.verified, builder: (column) => ColumnFilters(column)); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnFilters(column)); + + ColumnFilters get totalMediaCounter => $composableBuilder( + column: $table.totalMediaCounter, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get lastMessageSend => $composableBuilder( + column: $table.lastMessageSend, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get lastMessageReceived => $composableBuilder( + column: $table.lastMessageReceived, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get lastMessage => $composableBuilder( + column: $table.lastMessage, builder: (column) => ColumnFilters(column)); + + ColumnFilters get flameCounter => $composableBuilder( + column: $table.flameCounter, builder: (column) => ColumnFilters(column)); + + Expression messagesRefs( + Expression Function($$MessagesTableFilterComposer f) f) { + final $$MessagesTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.messages, + getReferencedColumn: (t) => t.contactId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$MessagesTableFilterComposer( + $db: $db, + $table: $db.messages, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } +} + +class $$ContactsTableOrderingComposer + extends Composer<_$TwonlyDatabase, $ContactsTable> { + $$ContactsTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get userId => $composableBuilder( + column: $table.userId, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get username => $composableBuilder( + column: $table.username, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get displayName => $composableBuilder( + column: $table.displayName, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get nickName => $composableBuilder( + column: $table.nickName, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get accepted => $composableBuilder( + column: $table.accepted, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get requested => $composableBuilder( + column: $table.requested, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get blocked => $composableBuilder( + column: $table.blocked, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get verified => $composableBuilder( + column: $table.verified, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get totalMediaCounter => $composableBuilder( + column: $table.totalMediaCounter, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get lastMessageSend => $composableBuilder( + column: $table.lastMessageSend, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get lastMessageReceived => $composableBuilder( + column: $table.lastMessageReceived, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get lastMessage => $composableBuilder( + column: $table.lastMessage, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get flameCounter => $composableBuilder( + column: $table.flameCounter, + builder: (column) => ColumnOrderings(column)); +} + +class $$ContactsTableAnnotationComposer + extends Composer<_$TwonlyDatabase, $ContactsTable> { + $$ContactsTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get userId => + $composableBuilder(column: $table.userId, builder: (column) => column); + + GeneratedColumn get username => + $composableBuilder(column: $table.username, builder: (column) => column); + + GeneratedColumn get displayName => $composableBuilder( + column: $table.displayName, builder: (column) => column); + + GeneratedColumn get nickName => + $composableBuilder(column: $table.nickName, builder: (column) => column); + + GeneratedColumn get accepted => + $composableBuilder(column: $table.accepted, builder: (column) => column); + + GeneratedColumn get requested => + $composableBuilder(column: $table.requested, builder: (column) => column); + + GeneratedColumn get blocked => + $composableBuilder(column: $table.blocked, builder: (column) => column); + + GeneratedColumn get verified => + $composableBuilder(column: $table.verified, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + GeneratedColumn get totalMediaCounter => $composableBuilder( + column: $table.totalMediaCounter, builder: (column) => column); + + GeneratedColumn get lastMessageSend => $composableBuilder( + column: $table.lastMessageSend, builder: (column) => column); + + GeneratedColumn get lastMessageReceived => $composableBuilder( + column: $table.lastMessageReceived, builder: (column) => column); + + GeneratedColumn get lastMessage => $composableBuilder( + column: $table.lastMessage, builder: (column) => column); + + GeneratedColumn get flameCounter => $composableBuilder( + column: $table.flameCounter, builder: (column) => column); + + Expression messagesRefs( + Expression Function($$MessagesTableAnnotationComposer a) f) { + final $$MessagesTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.messages, + getReferencedColumn: (t) => t.contactId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$MessagesTableAnnotationComposer( + $db: $db, + $table: $db.messages, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } +} + +class $$ContactsTableTableManager extends RootTableManager< + _$TwonlyDatabase, + $ContactsTable, + Contact, + $$ContactsTableFilterComposer, + $$ContactsTableOrderingComposer, + $$ContactsTableAnnotationComposer, + $$ContactsTableCreateCompanionBuilder, + $$ContactsTableUpdateCompanionBuilder, + (Contact, $$ContactsTableReferences), + Contact, + PrefetchHooks Function({bool messagesRefs})> { + $$ContactsTableTableManager(_$TwonlyDatabase db, $ContactsTable table) + : super(TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$ContactsTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$ContactsTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$ContactsTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + Value userId = const Value.absent(), + Value username = const Value.absent(), + Value displayName = const Value.absent(), + Value nickName = const Value.absent(), + Value accepted = const Value.absent(), + Value requested = const Value.absent(), + Value blocked = const Value.absent(), + Value verified = const Value.absent(), + Value createdAt = const Value.absent(), + Value totalMediaCounter = const Value.absent(), + Value lastMessageSend = const Value.absent(), + Value lastMessageReceived = const Value.absent(), + Value lastMessage = const Value.absent(), + Value flameCounter = const Value.absent(), + }) => + ContactsCompanion( + userId: userId, + username: username, + displayName: displayName, + nickName: nickName, + accepted: accepted, + requested: requested, + blocked: blocked, + verified: verified, + createdAt: createdAt, + totalMediaCounter: totalMediaCounter, + lastMessageSend: lastMessageSend, + lastMessageReceived: lastMessageReceived, + lastMessage: lastMessage, + flameCounter: flameCounter, + ), + createCompanionCallback: ({ + Value userId = const Value.absent(), + required String username, + Value displayName = const Value.absent(), + Value nickName = const Value.absent(), + Value accepted = const Value.absent(), + Value requested = const Value.absent(), + Value blocked = const Value.absent(), + Value verified = const Value.absent(), + Value createdAt = const Value.absent(), + Value totalMediaCounter = const Value.absent(), + Value lastMessageSend = const Value.absent(), + Value lastMessageReceived = const Value.absent(), + Value lastMessage = const Value.absent(), + Value flameCounter = const Value.absent(), + }) => + ContactsCompanion.insert( + userId: userId, + username: username, + displayName: displayName, + nickName: nickName, + accepted: accepted, + requested: requested, + blocked: blocked, + verified: verified, + createdAt: createdAt, + totalMediaCounter: totalMediaCounter, + lastMessageSend: lastMessageSend, + lastMessageReceived: lastMessageReceived, + lastMessage: lastMessage, + flameCounter: flameCounter, + ), + withReferenceMapper: (p0) => p0 + .map((e) => + (e.readTable(table), $$ContactsTableReferences(db, table, e))) + .toList(), + prefetchHooksCallback: ({messagesRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [if (messagesRefs) db.messages], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (messagesRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: + $$ContactsTableReferences._messagesRefsTable(db), + managerFromTypedResult: (p0) => + $$ContactsTableReferences(db, table, p0) + .messagesRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems + .where((e) => e.contactId == item.userId), + typedResults: items) + ]; + }, + ); + }, + )); +} + +typedef $$ContactsTableProcessedTableManager = ProcessedTableManager< + _$TwonlyDatabase, + $ContactsTable, + Contact, + $$ContactsTableFilterComposer, + $$ContactsTableOrderingComposer, + $$ContactsTableAnnotationComposer, + $$ContactsTableCreateCompanionBuilder, + $$ContactsTableUpdateCompanionBuilder, + (Contact, $$ContactsTableReferences), + Contact, + PrefetchHooks Function({bool messagesRefs})>; +typedef $$MessagesTableCreateCompanionBuilder = MessagesCompanion Function({ + required int contactId, + Value messageId, + Value messageOtherId, + Value responseToMessageId, + Value responseToOtherMessageId, + Value acknowledgeByUser, + required DownloadState downloadState, + Value acknowledgeByServer, + required MessageKind kind, + Value contentJson, + Value openedAt, + Value sendAt, + Value updatedAt, +}); +typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({ + Value contactId, + Value messageId, + Value messageOtherId, + Value responseToMessageId, + Value responseToOtherMessageId, + Value acknowledgeByUser, + Value downloadState, + Value acknowledgeByServer, + Value kind, + Value contentJson, + Value openedAt, + Value sendAt, + Value updatedAt, +}); + +final class $$MessagesTableReferences + extends BaseReferences<_$TwonlyDatabase, $MessagesTable, Message> { + $$MessagesTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static $ContactsTable _contactIdTable(_$TwonlyDatabase db) => + db.contacts.createAlias( + $_aliasNameGenerator(db.messages.contactId, db.contacts.userId)); + + $$ContactsTableProcessedTableManager get contactId { + final $_column = $_itemColumn('contact_id')!; + + final manager = $$ContactsTableTableManager($_db, $_db.contacts) + .filter((f) => f.userId.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_contactIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$MessagesTableFilterComposer + extends Composer<_$TwonlyDatabase, $MessagesTable> { + $$MessagesTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get messageId => $composableBuilder( + column: $table.messageId, builder: (column) => ColumnFilters(column)); + + ColumnFilters get messageOtherId => $composableBuilder( + column: $table.messageOtherId, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get responseToMessageId => $composableBuilder( + column: $table.responseToMessageId, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get responseToOtherMessageId => $composableBuilder( + column: $table.responseToOtherMessageId, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get acknowledgeByUser => $composableBuilder( + column: $table.acknowledgeByUser, + builder: (column) => ColumnFilters(column)); + + ColumnWithTypeConverterFilters + get downloadState => $composableBuilder( + column: $table.downloadState, + builder: (column) => ColumnWithTypeConverterFilters(column)); + + ColumnFilters get acknowledgeByServer => $composableBuilder( + column: $table.acknowledgeByServer, + builder: (column) => ColumnFilters(column)); + + ColumnWithTypeConverterFilters get kind => + $composableBuilder( + column: $table.kind, + builder: (column) => ColumnWithTypeConverterFilters(column)); + + ColumnFilters get contentJson => $composableBuilder( + column: $table.contentJson, builder: (column) => ColumnFilters(column)); + + ColumnFilters get openedAt => $composableBuilder( + column: $table.openedAt, builder: (column) => ColumnFilters(column)); + + ColumnFilters get sendAt => $composableBuilder( + column: $table.sendAt, builder: (column) => ColumnFilters(column)); + + ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, builder: (column) => ColumnFilters(column)); + + $$ContactsTableFilterComposer get contactId { + final $$ContactsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.contactId, + referencedTable: $db.contacts, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$ContactsTableFilterComposer( + $db: $db, + $table: $db.contacts, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MessagesTableOrderingComposer + extends Composer<_$TwonlyDatabase, $MessagesTable> { + $$MessagesTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get messageId => $composableBuilder( + column: $table.messageId, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get messageOtherId => $composableBuilder( + column: $table.messageOtherId, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get responseToMessageId => $composableBuilder( + column: $table.responseToMessageId, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get responseToOtherMessageId => $composableBuilder( + column: $table.responseToOtherMessageId, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get acknowledgeByUser => $composableBuilder( + column: $table.acknowledgeByUser, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get downloadState => $composableBuilder( + column: $table.downloadState, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get acknowledgeByServer => $composableBuilder( + column: $table.acknowledgeByServer, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get kind => $composableBuilder( + column: $table.kind, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get contentJson => $composableBuilder( + column: $table.contentJson, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get openedAt => $composableBuilder( + column: $table.openedAt, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get sendAt => $composableBuilder( + column: $table.sendAt, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, builder: (column) => ColumnOrderings(column)); + + $$ContactsTableOrderingComposer get contactId { + final $$ContactsTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.contactId, + referencedTable: $db.contacts, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$ContactsTableOrderingComposer( + $db: $db, + $table: $db.contacts, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MessagesTableAnnotationComposer + extends Composer<_$TwonlyDatabase, $MessagesTable> { + $$MessagesTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get messageId => + $composableBuilder(column: $table.messageId, builder: (column) => column); + + GeneratedColumn get messageOtherId => $composableBuilder( + column: $table.messageOtherId, builder: (column) => column); + + GeneratedColumn get responseToMessageId => $composableBuilder( + column: $table.responseToMessageId, builder: (column) => column); + + GeneratedColumn get responseToOtherMessageId => $composableBuilder( + column: $table.responseToOtherMessageId, builder: (column) => column); + + GeneratedColumn get acknowledgeByUser => $composableBuilder( + column: $table.acknowledgeByUser, builder: (column) => column); + + GeneratedColumnWithTypeConverter get downloadState => + $composableBuilder( + column: $table.downloadState, builder: (column) => column); + + GeneratedColumn get acknowledgeByServer => $composableBuilder( + column: $table.acknowledgeByServer, builder: (column) => column); + + GeneratedColumnWithTypeConverter get kind => + $composableBuilder(column: $table.kind, builder: (column) => column); + + GeneratedColumn get contentJson => $composableBuilder( + column: $table.contentJson, builder: (column) => column); + + GeneratedColumn get openedAt => + $composableBuilder(column: $table.openedAt, builder: (column) => column); + + GeneratedColumn get sendAt => + $composableBuilder(column: $table.sendAt, builder: (column) => column); + + GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + $$ContactsTableAnnotationComposer get contactId { + final $$ContactsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.contactId, + referencedTable: $db.contacts, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$ContactsTableAnnotationComposer( + $db: $db, + $table: $db.contacts, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$MessagesTableTableManager extends RootTableManager< + _$TwonlyDatabase, + $MessagesTable, + Message, + $$MessagesTableFilterComposer, + $$MessagesTableOrderingComposer, + $$MessagesTableAnnotationComposer, + $$MessagesTableCreateCompanionBuilder, + $$MessagesTableUpdateCompanionBuilder, + (Message, $$MessagesTableReferences), + Message, + PrefetchHooks Function({bool contactId})> { + $$MessagesTableTableManager(_$TwonlyDatabase db, $MessagesTable table) + : super(TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$MessagesTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$MessagesTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$MessagesTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + Value contactId = const Value.absent(), + Value messageId = const Value.absent(), + Value messageOtherId = const Value.absent(), + Value responseToMessageId = const Value.absent(), + Value responseToOtherMessageId = const Value.absent(), + Value acknowledgeByUser = const Value.absent(), + Value downloadState = const Value.absent(), + Value acknowledgeByServer = const Value.absent(), + Value kind = const Value.absent(), + Value contentJson = const Value.absent(), + Value openedAt = const Value.absent(), + Value sendAt = const Value.absent(), + Value updatedAt = const Value.absent(), + }) => + MessagesCompanion( + contactId: contactId, + messageId: messageId, + messageOtherId: messageOtherId, + responseToMessageId: responseToMessageId, + responseToOtherMessageId: responseToOtherMessageId, + acknowledgeByUser: acknowledgeByUser, + downloadState: downloadState, + acknowledgeByServer: acknowledgeByServer, + kind: kind, + contentJson: contentJson, + openedAt: openedAt, + sendAt: sendAt, + updatedAt: updatedAt, + ), + createCompanionCallback: ({ + required int contactId, + Value messageId = const Value.absent(), + Value messageOtherId = const Value.absent(), + Value responseToMessageId = const Value.absent(), + Value responseToOtherMessageId = const Value.absent(), + Value acknowledgeByUser = const Value.absent(), + required DownloadState downloadState, + Value acknowledgeByServer = const Value.absent(), + required MessageKind kind, + Value contentJson = const Value.absent(), + Value openedAt = const Value.absent(), + Value sendAt = const Value.absent(), + Value updatedAt = const Value.absent(), + }) => + MessagesCompanion.insert( + contactId: contactId, + messageId: messageId, + messageOtherId: messageOtherId, + responseToMessageId: responseToMessageId, + responseToOtherMessageId: responseToOtherMessageId, + acknowledgeByUser: acknowledgeByUser, + downloadState: downloadState, + acknowledgeByServer: acknowledgeByServer, + kind: kind, + contentJson: contentJson, + openedAt: openedAt, + sendAt: sendAt, + updatedAt: updatedAt, + ), + withReferenceMapper: (p0) => p0 + .map((e) => + (e.readTable(table), $$MessagesTableReferences(db, table, e))) + .toList(), + prefetchHooksCallback: ({contactId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (contactId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.contactId, + referencedTable: + $$MessagesTableReferences._contactIdTable(db), + referencedColumn: + $$MessagesTableReferences._contactIdTable(db).userId, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$MessagesTableProcessedTableManager = ProcessedTableManager< + _$TwonlyDatabase, + $MessagesTable, + Message, + $$MessagesTableFilterComposer, + $$MessagesTableOrderingComposer, + $$MessagesTableAnnotationComposer, + $$MessagesTableCreateCompanionBuilder, + $$MessagesTableUpdateCompanionBuilder, + (Message, $$MessagesTableReferences), + Message, + PrefetchHooks Function({bool contactId})>; + +class $TwonlyDatabaseManager { + final _$TwonlyDatabase _db; + $TwonlyDatabaseManager(this._db); + $$ContactsTableTableManager get contacts => + $$ContactsTableTableManager(_db, _db.contacts); + $$MessagesTableTableManager get messages => + $$MessagesTableTableManager(_db, _db.messages); +} diff --git a/lib/src/database/messages_db.dart b/lib/src/database/messages_db.dart new file mode 100644 index 0000000..e1a0700 --- /dev/null +++ b/lib/src/database/messages_db.dart @@ -0,0 +1,32 @@ +import 'package:drift/drift.dart'; +import 'package:twonly/src/database/contacts_db.dart'; +import 'package:twonly/src/model/json/message.dart'; + +enum DownloadState { + pending, + downloading, + downloaded, +} + +class Messages extends Table { + IntColumn get contactId => integer().references(Contacts, #userId)(); + + IntColumn get messageId => integer().autoIncrement()(); + IntColumn get messageOtherId => integer().nullable()(); + + IntColumn get responseToMessageId => integer().nullable()(); + IntColumn get responseToOtherMessageId => integer().nullable()(); + + BoolColumn get acknowledgeByUser => boolean().withDefault(Constant(false))(); + IntColumn get downloadState => intEnum()(); + + BoolColumn get acknowledgeByServer => + boolean().withDefault(Constant(false))(); + + TextColumn get kind => textEnum()(); + TextColumn get contentJson => text().nullable()(); + + DateTimeColumn get openedAt => dateTime().nullable()(); + DateTimeColumn get sendAt => dateTime().withDefault(currentDateAndTime)(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); +} diff --git a/lib/src/localization/app_de.arb b/lib/src/localization/app_de.arb index fb9b3d6..2d88213 100644 --- a/lib/src/localization/app_de.arb +++ b/lib/src/localization/app_de.arb @@ -40,6 +40,9 @@ "chatListViewSearchUserNameBtn": "Füge deinen ersten twonly-Kontakt hinzu!", "chatListViewSendFirstTwonly": "Sende dein erstes twonly!", "chatListDetailInput": "Nachricht eingeben", + "contextMenuVerifyUser": "Kontakt verifizieren", + "contextMenuOpenChat": "Chat öffnen", + "contextMenuSendImage": "Bild senden", "messageSendState_Received": "Empfangen", "messageSendState_Opened": "Geöffnet", "messageSendState_Send": "Gesendet", diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index 860fc0e..da7cdc8 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -40,6 +40,9 @@ "chatListViewSearchUserNameBtn": "Add your first twonly contact!", "chatListViewSendFirstTwonly": "Send your first twonly!", "chatListDetailInput": "Type a message", + "contextMenuVerifyUser": "Verify user", + "contextMenuOpenChat": "Open chat", + "contextMenuSendImage": "Send image", "mediaViewerAuthReason": "Please authenticate to see this twonly!", "messageSendState_Received": "Received", "messageSendState_Opened": "Opened", diff --git a/lib/src/model/json/message.dart b/lib/src/model/json/message.dart index 17e8e56..9622865 100644 --- a/lib/src/model/json/message.dart +++ b/lib/src/model/json/message.dart @@ -2,8 +2,7 @@ import 'package:flutter/material.dart'; enum MessageKind { textMessage, - image, - video, + media, contactRequest, rejectRequest, acceptRequest, @@ -11,28 +10,45 @@ enum MessageKind { ack } +Color getMessageColorFromType(MessageJson msg, Color primary) { + Color color; + + final content = msg.content; + if (content is TextMessageContent) { + color = Colors.lightBlue; + } else { + if (content is MediaMessageContent) { + if (content.isRealTwonly) { + color = primary; + } else { + if (content.isVideo) { + color = Colors.deepPurple; + } else { + color = const Color.fromARGB(255, 214, 47, 47); + } + } + } else { + return Colors.black; // this should not happen + } + } + return color; +} + extension MessageKindExtension on MessageKind { String get name => toString().split('.').last; static MessageKind fromString(String name) { return MessageKind.values.firstWhere((e) => e.name == name); } - - int get index => this.index; - - static MessageKind fromIndex(int index) { - return MessageKind.values[index]; - } } -// TODO: use message as base class, remove kind and flatten content -class Message { +class MessageJson { final MessageKind kind; - final MessageContent content; + final MessageContent? content; final int? messageId; DateTime timestamp; - Message( + MessageJson( {required this.kind, this.messageId, required this.content, @@ -43,17 +59,21 @@ class Message { return 'Message(kind: $kind, content: $content, timestamp: $timestamp)'; } - static Message fromJson(Map json) => Message( - kind: MessageKindExtension.fromString(json["kind"]), - messageId: (json['messageId'] as num?)?.toInt(), - content: - MessageContent.fromJson(json['content'] as Map), - timestamp: DateTime.parse(json['timestamp'] as String), - ); + static MessageJson fromJson(Map json) { + final kind = MessageKindExtension.fromString(json["kind"]); + + return MessageJson( + kind: kind, + messageId: (json['messageId'] as num?)?.toInt(), + content: MessageContent.fromJson( + kind, json['content'] as Map), + timestamp: DateTime.parse(json['timestamp'] as String), + ); + } Map toJson() => { 'kind': kind.name, - 'content': content.toJson(), + 'content': content?.toJson(), 'messageId': messageId, 'timestamp': timestamp.toIso8601String(), }; @@ -62,37 +82,14 @@ class Message { class MessageContent { MessageContent(); - Color getColor(Color primary) { - Color color; - if (this is TextMessageContent) { - color = Colors.lightBlue; - } else { - final content = this; - if (content is MediaMessageContent) { - if (content.isRealTwonly) { - color = primary; - } else { - if (content.isVideo) { - color = Colors.deepPurple; - } else { - color = const Color.fromARGB(255, 214, 47, 47); - } - } - } else { - return Colors.black; // this should not happen - } - } - return color; - } - - static MessageContent fromJson(Map json) { - switch (json['type']) { - case 'MediaMessageContent': + static MessageContent? fromJson(MessageKind kind, Map json) { + switch (kind) { + case MessageKind.media: return MediaMessageContent.fromJson(json); - case 'TextMessageContent': + case MessageKind.textMessage: return TextMessageContent.fromJson(json); default: - return MessageContent(); + return null; } } @@ -125,7 +122,6 @@ class MediaMessageContent extends MessageContent { @override Map toJson() { return { - 'type': 'MediaMessageContent', 'downloadToken': downloadToken, 'isRealTwonly': isRealTwonly, 'maxShowTime': maxShowTime, @@ -146,7 +142,6 @@ class TextMessageContent extends MessageContent { @override Map toJson() { return { - 'type': 'TextMessageContent', 'text': text, }; } diff --git a/lib/src/model/json/signal_identity.dart b/lib/src/model/json/signal_identity.dart index c8413e6..c3e4966 100644 --- a/lib/src/model/json/signal_identity.dart +++ b/lib/src/model/json/signal_identity.dart @@ -1,5 +1,4 @@ import 'dart:typed_data'; -import 'package:fixnum/fixnum.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:twonly/src/utils/json.dart'; part 'signal_identity.g.dart'; @@ -9,8 +8,7 @@ class SignalIdentity { const SignalIdentity( {required this.identityKeyPairU8List, required this.registrationId}); - @Int64Converter() - final Int64 registrationId; + final int registrationId; @Uint8ListConverter() final Uint8List identityKeyPairU8List; diff --git a/lib/src/model/json/signal_identity.g.dart b/lib/src/model/json/signal_identity.g.dart index 7c0c868..a5fde87 100644 --- a/lib/src/model/json/signal_identity.g.dart +++ b/lib/src/model/json/signal_identity.g.dart @@ -10,13 +10,12 @@ SignalIdentity _$SignalIdentityFromJson(Map json) => SignalIdentity( identityKeyPairU8List: const Uint8ListConverter() .fromJson(json['identityKeyPairU8List'] as String), - registrationId: - const Int64Converter().fromJson(json['registrationId'] as String), + registrationId: (json['registrationId'] as num).toInt(), ); Map _$SignalIdentityToJson(SignalIdentity instance) => { - 'registrationId': const Int64Converter().toJson(instance.registrationId), + 'registrationId': instance.registrationId, 'identityKeyPairU8List': const Uint8ListConverter().toJson(instance.identityKeyPairU8List), }; diff --git a/lib/src/model/json/user_data.dart b/lib/src/model/json/user_data.dart index d18476f..4499060 100644 --- a/lib/src/model/json/user_data.dart +++ b/lib/src/model/json/user_data.dart @@ -1,6 +1,4 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:fixnum/fixnum.dart'; -import 'package:twonly/src/utils/json.dart'; part 'user_data.g.dart'; @JsonSerializable() @@ -12,8 +10,7 @@ class UserData { final String username; final String displayName; - @Int64Converter() - final Int64 userId; + final int userId; factory UserData.fromJson(Map json) => _$UserDataFromJson(json); diff --git a/lib/src/model/json/user_data.g.dart b/lib/src/model/json/user_data.g.dart index d8217b4..bc123e2 100644 --- a/lib/src/model/json/user_data.g.dart +++ b/lib/src/model/json/user_data.g.dart @@ -7,7 +7,7 @@ part of 'user_data.dart'; // ************************************************************************** UserData _$UserDataFromJson(Map json) => UserData( - userId: const Int64Converter().fromJson(json['userId'] as String), + userId: (json['userId'] as num).toInt(), username: json['username'] as String, displayName: json['displayName'] as String, ); @@ -15,5 +15,5 @@ UserData _$UserDataFromJson(Map json) => UserData( Map _$UserDataToJson(UserData instance) => { 'username': instance.username, 'displayName': instance.displayName, - 'userId': const Int64Converter().toJson(instance.userId), + 'userId': instance.userId, }; diff --git a/lib/src/providers/api/api.dart b/lib/src/providers/api/api.dart index 1ddfa75..40b3a42 100644 --- a/lib/src/providers/api/api.dart +++ b/lib/src/providers/api/api.dart @@ -7,9 +7,9 @@ import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/src/app.dart'; -import 'package:twonly/src/model/contacts_model.dart'; +import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/model/json/message.dart'; -import 'package:twonly/src/model/messages_model.dart'; +import '../../../../.blocked/archives/messages_model.dart'; import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:twonly/src/providers/api/api_utils.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -30,7 +30,7 @@ Future tryTransmitMessages() async { Uint8List? bytes = box.get("retransmit-$msgId-textmessage"); if (bytes != null) { Result resp = await apiProvider.sendTextMessage( - Int64(retransmit[i].otherUserId), + retransmit[i].otherUserId, bytes, ); @@ -46,7 +46,7 @@ Future tryTransmitMessages() async { if (encryptedMedia != null) { final content = retransmit[i].messageContent; if (content is MediaMessageContent) { - uploadMediaFile(msgId, Int64(retransmit[i].otherUserId), encryptedMedia, + uploadMediaFile(msgId, retransmit[i].otherUserId, encryptedMedia, content.isRealTwonly, content.maxShowTime, retransmit[i].sendAt); } } @@ -54,7 +54,7 @@ Future tryTransmitMessages() async { } // this functions ensures that the message is received by the server and in case of errors will try again later -Future encryptAndSendMessage(Int64 userId, Message msg) async { +Future encryptAndSendMessage(int userId, Message msg) async { Uint8List? bytes = await SignalHelper.encryptMessage(msg, userId); if (bytes == null) { @@ -79,7 +79,7 @@ Future encryptAndSendMessage(Int64 userId, Message msg) async { return resp; } -Future sendTextMessage(Int64 target, String message) async { +Future sendTextMessage(int target, String message) async { MessageContent content = TextMessageContent(text: message); DateTime messageSendAt = DateTime.now(); @@ -105,7 +105,7 @@ Future sendTextMessage(Int64 target, String message) async { // this will send the media file and ensures retransmission when errors occur Future uploadMediaFile( int messageId, - Int64 target, + int target, Uint8List encryptedMedia, bool isRealTwonly, int maxShowTime, @@ -179,7 +179,7 @@ Future uploadMediaFile( } class SendImage { - final Int64 userId; + final int userId; final Uint8List imageBytes; final bool isRealTwonly; final int maxShowTime; @@ -231,7 +231,7 @@ class SendImage { } Future sendImage( - List userIds, + List userIds, Uint8List imageBytes, bool isRealTwonly, int maxShowTime, diff --git a/lib/src/providers/api/server_messages.dart b/lib/src/providers/api/server_messages.dart index 636d499..2411c96 100644 --- a/lib/src/providers/api/server_messages.dart +++ b/lib/src/providers/api/server_messages.dart @@ -5,9 +5,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/model/contacts_model.dart'; +import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/model/json/message.dart'; -import 'package:twonly/src/model/messages_model.dart'; +import '../../../../.blocked/archives/messages_model.dart'; import 'package:twonly/src/proto/api/client_to_server.pb.dart' as client; import 'package:twonly/src/proto/api/client_to_server.pbserver.dart'; import 'package:twonly/src/proto/api/error.pb.dart'; @@ -102,7 +102,7 @@ Future handleDownloadData(DownloadData data) async { int? fromUserId = box.get("${data.uploadToken}_fromUserId"); if (fromUserId != null) { Uint8List? rawBytes = - await SignalHelper.decryptBytes(downloadedBytes, Int64(fromUserId)); + await SignalHelper.decryptBytes(downloadedBytes, fromUserId); if (rawBytes != null) { box.put("${data.uploadToken}_downloaded", rawBytes); diff --git a/lib/src/providers/api_provider.dart b/lib/src/providers/api_provider.dart index 2b4d4ef..94b4c3a 100644 --- a/lib/src/providers/api_provider.dart +++ b/lib/src/providers/api_provider.dart @@ -256,7 +256,7 @@ class ApiProvider { var open = Handshake_OpenSession() ..response = signature - ..userId = userData.userId; + ..userId = Int64(userData.userId); var opensession = Handshake()..opensession = open; @@ -304,8 +304,8 @@ class ApiProvider { return await _sendRequestV0(req); } - Future getUsername(Int64 userId) async { - var get = ApplicationData_GetUserById()..userId = userId; + Future getUsername(int userId) async { + var get = ApplicationData_GetUserById()..userId = Int64(userId); var appData = ApplicationData()..getuserbyid = get; var req = createClientToServerFromApplicationData(appData); return await _sendRequestV0(req); @@ -353,9 +353,9 @@ class ApiProvider { return await _sendRequestV0(req); } - Future sendTextMessage(Int64 target, Uint8List msg) async { + Future sendTextMessage(int target, Uint8List msg) async { var testMessage = ApplicationData_TextMessage() - ..userId = target + ..userId = Int64(target) ..body = msg; var appData = ApplicationData()..textmessage = testMessage; diff --git a/lib/src/providers/contacts_change_provider.dart b/lib/src/providers/contacts_change_provider.dart deleted file mode 100644 index 5a511d6..0000000 --- a/lib/src/providers/contacts_change_provider.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:twonly/src/model/contacts_model.dart'; -import 'package:twonly/src/model/messages_model.dart'; - -// This provider will update the UI on changes in the contact list -class ContactChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { - List _allContacts = []; - final Map _lastMessagesGroupedByUser = {}; - - int get newContactRequests => _allContacts - .where((contact) => !contact.accepted && contact.requested) - .length; - List get allContacts => _allContacts; - - void update() async { - _allContacts = await DbContacts.getUsers(); - for (Contact contact in _allContacts) { - DbMessage? last = await DbMessages.getLastMessagesForPreviewForUser( - contact.userId.toInt()); - if (last != null) { - _lastMessagesGroupedByUser[last.otherUserId] = last; - } - } - notifyListeners(); - } - - /// Makes `Counter` readable inside the devtools by listing all of its properties - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(IntProperty('count', newContactRequests)); - } -} diff --git a/lib/src/providers/db_provider.dart b/lib/src/providers/db_provider.dart index 05c1bd9..2160718 100644 --- a/lib/src/providers/db_provider.dart +++ b/lib/src/providers/db_provider.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'dart:math'; -import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/identity_key_store_model.dart'; -import 'package:twonly/src/model/messages_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'; @@ -55,8 +53,6 @@ class DbProvider { await DbSignalPreKeyStore.setupDatabaseTable(db); await DbSignalSenderKeyStore.setupDatabaseTable(db); await DbSignalIdentityKeyStore.setupDatabaseTable(db); - await DbContacts.setupDatabaseTable(db); - await DbMessages.setupDatabaseTable(db); } Future open() async { diff --git a/lib/src/providers/download_change_provider.dart b/lib/src/providers/download_change_provider.dart deleted file mode 100644 index 954319e..0000000 --- a/lib/src/providers/download_change_provider.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:collection'; -import 'package:flutter/foundation.dart'; - -class DownloadChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { - final HashSet _currentlyDownloading = HashSet(); - - HashSet get currentlyDownloading => _currentlyDownloading; - - void update(List token, bool add) { - debugPrint("Downloading: $add : $token"); - - if (add) { - _currentlyDownloading.add(token.toString()); - } else { - _currentlyDownloading.remove(token.toString()); - } - debugPrint("Downloading: $add : ${_currentlyDownloading.toList()}"); - notifyListeners(); - } -} diff --git a/lib/src/providers/messages_change_provider.dart b/lib/src/providers/messages_change_provider.dart deleted file mode 100644 index 93fa0b9..0000000 --- a/lib/src/providers/messages_change_provider.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:twonly/src/model/contacts_model.dart'; -import 'package:twonly/src/model/messages_model.dart'; -import 'package:twonly/src/utils/misc.dart'; - -/// This provider does always contains the latest messages send or received -/// for every contact. -class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { - final Map _lastMessage = {}; - final Map> _allMessagesFromUser = - >{}; - final Map _changeCounter = {}; - final Map _flamesCounter = {}; - - Map get lastMessage => _lastMessage; - Map> get allMessagesFromUser => _allMessagesFromUser; - Map get changeCounter => _changeCounter; - Map get flamesCounter => _flamesCounter; - - Future updateLastMessageFor(int targetUserId, int? messageId) async { - DbMessage? last = - await DbMessages.getLastMessagesForPreviewForUser(targetUserId); - if (last != null) { - _lastMessage[last.otherUserId] = last; - } - flamesCounter[targetUserId] = await getFlamesForOtherUser(targetUserId); - // notifyListeners(); - - if (messageId == null || _allMessagesFromUser[targetUserId] == null) { - loadMessagesForUser(targetUserId, force: true); - } else { - DbMessage? msg = await DbMessages.getMessageById(messageId); - if (msg != null) { - int index = _allMessagesFromUser[targetUserId]! - .indexWhere((x) => x.messageId == messageId); - if (index == -1) { - print("should be indexed by time!!"); - - _allMessagesFromUser[targetUserId]!.insert(0, msg); - // reload all messages but async - loadMessagesForUser(targetUserId, force: true); - } else { - _allMessagesFromUser[targetUserId]![index] = msg; - } - } - } - notifyListeners(); - } - - Future loadMessagesForUser(int targetUserId, {bool force = false}) async { - if (!force && _allMessagesFromUser[targetUserId] != null) return; - _allMessagesFromUser[targetUserId] = - await DbMessages.getAllMessagesForUser(targetUserId); - notifyListeners(); - } - - void init({bool afterPaused = false}) async { - // load everything from the database - List allContacts = await DbContacts.getUsers(); - for (Contact contact in allContacts) { - DbMessage? last = await DbMessages.getLastMessagesForPreviewForUser( - contact.userId.toInt()); - if (last != null) { - _lastMessage[last.otherUserId] = last; - } - flamesCounter[contact.userId.toInt()] = - await getFlamesForOtherUser(contact.userId.toInt()); - } - notifyListeners(); - if (afterPaused) { - for (int targetUserId in _allMessagesFromUser.keys) { - loadMessagesForUser(targetUserId, force: true); - } - } - } -} diff --git a/lib/src/services/fcm_service.dart b/lib/src/services/fcm_service.dart index 7d8823a..7e189cf 100644 --- a/lib/src/services/fcm_service.dart +++ b/lib/src/services/fcm_service.dart @@ -3,8 +3,8 @@ 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/providers/api_provider.dart'; -import 'package:twonly/src/providers/db_provider.dart'; import 'package:twonly/src/utils/misc.dart'; import '../../firebase_options.dart'; @@ -65,33 +65,26 @@ Future initFCMService() async { }); } +late TwonlyDatabase bgTwonlyDB; + @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { - // If you're going to use other Firebase services in the background, such as Firestore, - // make sure you call `initializeApp` before using other Firebase services. - // Wenn Tasks länger als 30 Sekunden ausgeführt werden, wird der Prozess möglicherweise automatisch vom Gerät beendet. // -> offer backend via http? - print("Handling a background message: ${message.messageId}"); + Logger("firebase-background") + .shout('Handling a background message: ${message.messageId}'); - bool gotMessage = false; + bgTwonlyDB = TwonlyDatabase(); - globalCallBackOnMessageChange = (a, b) async { - gotMessage = true; - print("Got message can exit"); - }; - - dbProvider = DbProvider(); - await dbProvider.ready; apiProvider = ApiProvider(); await apiProvider.connect(); final stopwatch = Stopwatch()..start(); - while (!gotMessage) { + while (true) { if (stopwatch.elapsed >= Duration(seconds: 20)) { - Logger("firebase-background").shout('Timeout reached. Exiting the loop.'); - break; // Exit the loop if the timeout is reached. + Logger("firebase-background").shout('Exiting background handler'); + break; } await Future.delayed(Duration(milliseconds: 10)); } diff --git a/lib/src/services/notification_service.dart b/lib/src/services/notification_service.dart index a783d3d..cfcb7c9 100644 --- a/lib/src/services/notification_service.dart +++ b/lib/src/services/notification_service.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'dart:io'; - import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:logging/logging.dart'; -import 'package:twonly/src/model/contacts_model.dart'; +import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/model/json/message.dart' as my; /// Streams are created so that app can respond to notification-related events @@ -143,9 +142,9 @@ String getPushNotificationText(String key, String userName) { if (systemLanguage.contains("de")) { pushNotificationText = { "newTextMessage": "%userName% hat dir eine Nachricht gesendet.", - "newTwonly": "%userName% hat dir einen twonly gesendet.", - "newVideo": "%userName% hat dir eine Video gesendet.", - "newImage": "%userName% hat dir eine Bild gesendet.", + "newTwonly": "%userName% hat dir ein twonly gesendet.", + "newVideo": "%userName% hat dir ein Video gesendet.", + "newImage": "%userName% hat dir ein Bild gesendet.", "contactRequest": "%userName% möchte sich mir dir vernetzen.", "acceptRequest": "%userName% ist jetzt mit dir vernetzt.", }; @@ -166,7 +165,10 @@ String getPushNotificationText(String key, String userName) { Future localPushNotificationNewMessage( int fromUserId, my.Message message, int messageId) async { - Contact? user = await DbContacts.getUserById(fromUserId); + Contact? user = await TwonlyDatabase.provider + .getContactByUserId(fromUserId) + .getSingleOrNull(); + if (user == null) return; String msg = ""; diff --git a/lib/src/tasks/websocket_foreground_task.dart b/lib/src/tasks/websocket_foreground_task.dart deleted file mode 100644 index c1a94fb..0000000 --- a/lib/src/tasks/websocket_foreground_task.dart +++ /dev/null @@ -1,65 +0,0 @@ -// The callback function should always be a top-level or static function. -import 'package:flutter_foreground_task/flutter_foreground_task.dart'; -import 'package:twonly/globals.dart'; -import 'package:twonly/src/providers/api_provider.dart'; -import 'package:twonly/src/providers/db_provider.dart'; - -@pragma('vm:entry-point') -void startCallback() { - FlutterForegroundTask.setTaskHandler(WebsocketForegroundTask()); -} - -class WebsocketForegroundTask extends TaskHandler { - // Called when the task is started. - @override - Future onStart(DateTime timestamp, TaskStarter starter) async { - print('onStart(starter: ${starter.name})'); - - dbProvider = DbProvider(); - await dbProvider.ready; - apiProvider = ApiProvider(); - apiProvider.connect(); - } - - // Called based on the eventAction set in ForegroundTaskOptions. - @override - void onRepeatEvent(DateTime timestamp) { - // Send data to main isolate. - final Map data = { - "timestampMillis": timestamp.millisecondsSinceEpoch, - }; - FlutterForegroundTask.sendDataToMain(data); - } - - // Called when the task is destroyed. - @override - Future onDestroy(DateTime timestamp) async { - await apiProvider.close(() {}); - print('onDestroy'); - } - - // Called when data is sent using `FlutterForegroundTask.sendDataToTask`. - @override - void onReceiveData(Object data) { - apiProvider.close(() {}); - print('onReceiveData: $data'); - } - - // Called when the notification button is pressed. - @override - void onNotificationButtonPressed(String id) { - print('onNotificationButtonPressed: $id'); - } - - // Called when the notification itself is pressed. - @override - void onNotificationPressed() { - apiProvider.close(() {}); - } - - // Called when the notification itself is dismissed. - @override - void onNotificationDismissed() { - print('onNotificationDismissed'); - } -} diff --git a/lib/src/utils/json.dart b/lib/src/utils/json.dart index 70d0e8e..9296d57 100644 --- a/lib/src/utils/json.dart +++ b/lib/src/utils/json.dart @@ -1,22 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:fixnum/fixnum.dart'; import 'dart:convert'; import 'dart:typed_data'; -class Int64Converter implements JsonConverter { - const Int64Converter(); - - @override - Int64 fromJson(String json) { - return Int64.parseInt(json); - } - - @override - String toJson(Int64 object) { - return object.toString(); - } -} - class Uint8ListConverter implements JsonConverter { const Uint8ListConverter(); @override diff --git a/lib/src/utils/misc.dart b/lib/src/utils/misc.dart index 794c61c..e198acd 100644 --- a/lib/src/utils/misc.dart +++ b/lib/src/utils/misc.dart @@ -9,13 +9,15 @@ import 'package:local_auth/local_auth.dart'; 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/model/messages_model.dart'; +import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -extension LocalizationExtension on BuildContext { +extension ShortCutsExtension on BuildContext { AppLocalizations get lang => AppLocalizations.of(this)!; + TwonlyDatabase get db => Provider.of(this); } // Function to check if a column exists @@ -148,50 +150,48 @@ Future getCompressedImage(Uint8List imageBytes) async { return result; } -int getFlameCounter(List dates) { - if (dates.isEmpty) return 0; +// int getFlameCounter(List dates) { +// if (dates.isEmpty) return 0; - int flamesCount = 0; - DateTime lastFlameCount = DateTime.now(); +// int flamesCount = 0; +// DateTime lastFlameCount = DateTime.now(); - if (calculateTimeDifference(dates[0], lastFlameCount).inDays == 0) { - flamesCount = 1; - lastFlameCount = dates[0]; - } +// if (calculateTimeDifference(dates[0], lastFlameCount).inDays == 0) { +// flamesCount = 1; +// lastFlameCount = dates[0]; +// } - // print(dates[0]); - for (int i = 1; i < dates.length; i++) { - // print( - // "${dates[i]} ${dates[i].difference(dates[i - 1]).inDays} ${dates[i].difference(lastFlameCount).inDays}"); - if (calculateTimeDifference(dates[i], dates[i - 1]).inDays == 0) { - if (lastFlameCount.difference(dates[i]).inDays == 1) { - flamesCount++; - lastFlameCount = dates[i]; - } - } else { - break; // Stop counting if there's a break in the sequence - } - } - return flamesCount; -} +// // print(dates[0]); +// for (int i = 1; i < dates.length; i++) { +// // print( +// // "${dates[i]} ${dates[i].difference(dates[i - 1]).inDays} ${dates[i].difference(lastFlameCount).inDays}"); +// if (calculateTimeDifference(dates[i], dates[i - 1]).inDays == 0) { +// if (lastFlameCount.difference(dates[i]).inDays == 1) { +// flamesCount++; +// lastFlameCount = dates[i]; +// } +// } else { +// break; // Stop counting if there's a break in the sequence +// } +// } +// return flamesCount; +// } -Future getFlamesForOtherUser(int otherUserId) async { - List<(DateTime, int?)> dates = await DbMessages.getMessageDates(otherUserId); - // print("Dates ${dates.length}"); - if (dates.isEmpty) return 0; +// Future getFlamesForOtherUser(int otherUserId) async { +// List<(DateTime, int?)> dates = await DbMessages.getMessageDates(otherUserId); +// // print("Dates ${dates.length}"); +// if (dates.isEmpty) return 0; - List received = - dates.where((x) => x.$2 != null).map((x) => x.$1).toList(); - List send = - dates.where((x) => x.$2 == null).map((x) => x.$1).toList(); +// List received = +// dates.where((x) => x.$2 != null).map((x) => x.$1).toList(); +// List send = +// dates.where((x) => x.$2 == null).map((x) => x.$1).toList(); - // print("Received ${received.length} and send ${send.length}"); - - int a = getFlameCounter(received); - int b = getFlameCounter(send); - // print("Received $a and send $b"); - return min(a, b); -} +// int a = getFlameCounter(received); +// int b = getFlameCounter(send); +// // print("Received $a and send $b"); +// return min(a, b); +// } Duration calculateTimeDifference(DateTime now, DateTime startTime) { // Get the timezone offsets diff --git a/lib/src/utils/signal.dart b/lib/src/utils/signal.dart index e2452c0..0a13cbf 100644 --- a/lib/src/utils/signal.dart +++ b/lib/src/utils/signal.dart @@ -27,7 +27,7 @@ Future getPrivateKey() async { } Future addNewContact(Response_UserData userData) async { - final Int64 userId = userData.userId; + final int userId = userData.userId.toInt(); SignalProtocolAddress targetAddress = SignalProtocolAddress(userId.toString(), defaultDeviceId); @@ -140,14 +140,15 @@ Future createIfNotExistsSignalIdentity() async { .storeSignedPreKey(signedPreKey.id, signedPreKey); final storedSignalIdentity = SignalIdentity( - identityKeyPairU8List: identityKeyPair.serialize(), - registrationId: Int64(registrationId)); + identityKeyPairU8List: identityKeyPair.serialize(), + registrationId: registrationId, + ); await storage.write( key: "signal_identity", value: jsonEncode(storedSignalIdentity)); } -Future generateSessionFingerPrint(Int64 target) async { +Future generateSessionFingerPrint(int target) async { ConnectSignalProtocolStore? signalStore = await getSignalStore(); UserData? user = await getUser(); if (signalStore == null || user == null) return null; @@ -215,7 +216,7 @@ Future encryptBytes(Uint8List bytes, Int64 target) async { } } -Future decryptBytes(Uint8List bytes, Int64 target) async { +Future decryptBytes(Uint8List bytes, int target) async { try { ConnectSignalProtocolStore signalStore = (await getSignalStore())!; @@ -247,7 +248,7 @@ Future decryptBytes(Uint8List bytes, Int64 target) async { } } -Future encryptMessage(Message msg, Int64 target) async { +Future encryptMessage(Message msg, int target) async { try { ConnectSignalProtocolStore signalStore = (await getSignalStore())!; diff --git a/lib/src/views/camera_to_share/share_image_editor_view.dart b/lib/src/views/camera_to_share/share_image_editor_view.dart index 9d61d1e..f7df2a1 100644 --- a/lib/src/views/camera_to_share/share_image_editor_view.dart +++ b/lib/src/views/camera_to_share/share_image_editor_view.dart @@ -375,7 +375,7 @@ class _ShareImageEditorView extends State { if (sendNextMediaToUserId != null) { Uint8List? imageBytes = await getMergedImage(); sendImage( - [Int64(sendNextMediaToUserId)], + [sendNextMediaToUserId], imageBytes!, _isRealTwonly, _maxShowTime, diff --git a/lib/src/views/camera_to_share/share_image_view.dart b/lib/src/views/camera_to_share/share_image_view.dart index 88a72b0..a5bd786 100644 --- a/lib/src/views/camera_to_share/share_image_view.dart +++ b/lib/src/views/camera_to_share/share_image_view.dart @@ -9,7 +9,7 @@ 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/model/contacts_model.dart'; +import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/providers/send_next_media_to.dart'; diff --git a/lib/src/views/chats/chat_item_details_view.dart b/lib/src/views/chats/chat_item_details_view.dart index c0ba394..f2c7133 100644 --- a/lib/src/views/chats/chat_item_details_view.dart +++ b/lib/src/views/chats/chat_item_details_view.dart @@ -6,9 +6,9 @@ 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/model/contacts_model.dart'; +import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/model/json/message.dart'; -import 'package:twonly/src/model/messages_model.dart'; +import '../../../../.blocked/archives/messages_model.dart'; import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/contacts_change_provider.dart'; import 'package:twonly/src/providers/download_change_provider.dart'; diff --git a/lib/src/views/chats/chat_list_view.dart b/lib/src/views/chats/chat_list_view.dart index dd9fe67..dc58642 100644 --- a/lib/src/views/chats/chat_list_view.dart +++ b/lib/src/views/chats/chat_list_view.dart @@ -7,13 +7,11 @@ 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/model/contacts_model.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/model/messages_model.dart'; import 'package:twonly/src/providers/api/api.dart'; -import 'package:twonly/src/providers/contacts_change_provider.dart'; -import 'package:twonly/src/providers/download_change_provider.dart'; -import 'package:twonly/src/providers/messages_change_provider.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'; @@ -34,64 +32,12 @@ class ChatListView extends StatefulWidget { class _ChatListViewState extends State { @override Widget build(BuildContext context) { - Map lastMessages = - context.watch().lastMessage; - - List allUsers = context - .watch() - .allContacts - .where((c) => c.accepted) - .toList(); - - allUsers.sort((b, a) { - DbMessage? msgA = lastMessages[a.userId.toInt()]; - DbMessage? msgB = lastMessages[b.userId.toInt()]; - if (msgA == null) return 1; - if (msgB == null) return -1; - return msgA.sendAt.compareTo(msgB.sendAt); - }); - - int maxTotalMediaCounter = 0; - if (allUsers.isNotEmpty) { - maxTotalMediaCounter = allUsers - .map((x) => x.totalMediaCounter) - .reduce((a, b) => a > b ? a : b); - } + Stream> contacts = context.db.watchContactsForChatList(); return Scaffold( - appBar: AppBar( - title: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ProfileView(), - ), - ); - }, - child: Text("twonly"), - ), - // title: - actions: [ - NotificationBadge( - count: context - .watch() - .newContactRequests - .toString(), - child: IconButton( - icon: FaIcon(FontAwesomeIcons.userPlus, size: 18), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SearchUsernameView(), - ), - ); - }, - ), - ), - IconButton( - onPressed: () { + appBar: AppBar( + title: GestureDetector( + onTap: () { Navigator.push( context, MaterialPageRoute( @@ -99,16 +45,21 @@ class _ChatListViewState extends State { ), ); }, - icon: FaIcon(FontAwesomeIcons.gear, size: 19), - ) - ], - ), - body: (allUsers.isEmpty) - ? Center( - child: Padding( - padding: const EdgeInsets.all(10), - child: OutlinedButton.icon( - icon: Icon(Icons.person_add), + child: Text("twonly"), + ), + // title: + actions: [ + StreamBuilder( + stream: context.db.watchContactsRequested(), + builder: (context, snapshot) { + var count = 0; + if (snapshot.hasData && snapshot.data != null) { + count = snapshot.data!; + } + return NotificationBadge( + count: count.toString(), + child: IconButton( + icon: FaIcon(FontAwesomeIcons.userPlus, size: 18), onPressed: () { Navigator.push( context, @@ -117,35 +68,77 @@ class _ChatListViewState extends State { ), ); }, - label: Text(context.lang.chatListViewSearchUserNameBtn)), - ), - ) - : ListView.builder( - restorationId: 'chat_list_view', - itemCount: allUsers.length, - itemBuilder: (BuildContext context, int index) { - final user = allUsers[index]; - return UserListItem( - user: user, - maxTotalMediaCounter: maxTotalMediaCounter, - lastMessage: lastMessages[user.userId.toInt()], + ), ); }, ), - ); + IconButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProfileView(), + ), + ); + }, + icon: FaIcon(FontAwesomeIcons.gear, size: 19), + ) + ], + ), + body: StreamBuilder( + stream: contacts, + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data == null) { + return Container(); + } + + final contacts = snapshot.data!; + if (contacts.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(10), + child: OutlinedButton.icon( + icon: Icon(Icons.person_add), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SearchUsernameView())); + }, + label: Text(context.lang.chatListViewSearchUserNameBtn)), + ), + ); + } + + int maxTotalMediaCounter = 0; + if (contacts.isNotEmpty) { + maxTotalMediaCounter = contacts + .map((x) => x.totalMediaCounter) + .reduce((a, b) => a > b ? a : b); + } + + return ListView.builder( + restorationId: 'chat_list_view', + itemCount: contacts.length, + itemBuilder: (BuildContext context, int index) { + final user = contacts[index]; + return UserListItem( + user: user, + maxTotalMediaCounter: maxTotalMediaCounter, + ); + }, + ); + }, + )); } } class UserListItem extends StatefulWidget { final Contact user; - final DbMessage? lastMessage; final int maxTotalMediaCounter; const UserListItem( - {super.key, - required this.user, - required this.lastMessage, - required this.maxTotalMediaCounter}); + {super.key, required this.user, required this.maxTotalMediaCounter}); @override State createState() => _UserListItem(); @@ -154,38 +147,38 @@ class UserListItem extends StatefulWidget { class _UserListItem extends State { int lastMessageInSeconds = 0; MessageSendState state = MessageSendState.send; - bool isDownloading = false; List token = []; + Message? currentMessage; Timer? updateTime; @override void initState() { super.initState(); - initAsync(); + // initAsync(); lastUpdateTime(); } - Future initAsync() async { - if (widget.lastMessage != null) { - if (!widget.lastMessage!.isDownloaded) { - final content = widget.lastMessage!.messageContent; - if (content is MediaMessageContent) { - tryDownloadMedia(widget.lastMessage!.messageId, - widget.lastMessage!.otherUserId, content.downloadToken); - } - } - } - } + // Future initAsync() async { + // if (currentMessage != null) { + // if (currentMessage!.downloadState != DownloadState.downloading) { + // final content = widget.lastMessage!.messageContent; + // if (content is MediaMessageContent) { + // tryDownloadMedia(widget.lastMessage!.messageId, + // widget.lastMessage!.otherUserId, content.downloadToken); + // } + // } + // } + // } void lastUpdateTime() { // Change the color every 200 milliseconds updateTime = Timer.periodic(Duration(milliseconds: 200), (timer) { setState(() { - if (widget.lastMessage != null) { - lastMessageInSeconds = calculateTimeDifference( - DateTime.now(), widget.lastMessage!.sendAt) - .inSeconds; + if (currentMessage != null) { + lastMessageInSeconds = + calculateTimeDifference(DateTime.now(), currentMessage!.sendAt) + .inSeconds; } }); }); @@ -199,72 +192,98 @@ class _UserListItem extends State { @override Widget build(BuildContext context) { - if (widget.lastMessage != null) { - state = widget.lastMessage!.getSendState(); + final notOpenedMessages = + context.db.watchMessageNotOpened(widget.user.userId); + final lastMessage = context.db.watchLastMessage(widget.user.userId); - final content = widget.lastMessage!.messageContent; + // if (widget.lastMessage != null) { + // state = widget.lastMessage!.getSendState(); - if (widget.lastMessage!.messageReceived && - content is MediaMessageContent) { - token = content.downloadToken; - isDownloading = context - .watch() - .currentlyDownloading - .contains(token.toString()); - } - } + // final content = widget.lastMessage!.messageContent; - int flameCounter = context - .watch() - .flamesCounter[widget.user.userId.toInt()] ?? - 0; + // if (widget.lastMessage!.messageReceived && + // content is MediaMessageContent) { + // token = content.downloadToken; + // isDownloading = context + // .watch() + // .currentlyDownloading + // .contains(token.toString()); + // } + // } + + int flameCounter = getFlameCounterFromContact(widget.user); return UserContextMenu( - user: widget.user, + contact: widget.user, child: ListTile( - title: Text(widget.user.displayName), - subtitle: (widget.lastMessage == null) - ? Text(context.lang.chatsTapToSend) - : Row( - children: [ - MessageSendStateIcon(widget.lastMessage!), - Text("•"), - const SizedBox(width: 5), - Text( - formatDuration(lastMessageInSeconds), - style: TextStyle(fontSize: 12), - ), - if (flameCounter > 0) - FlameCounterWidget( - widget.user, - flameCounter, - widget.maxTotalMediaCounter, - prefix: true, + title: Text(getContactDisplayName(widget.user)), + subtitle: StreamBuilder( + stream: lastMessage, + builder: (context, lastMessageSnapshot) { + if (!lastMessageSnapshot.hasData) { + return Container(); + } + if (lastMessageSnapshot.data == null) { + return Text(context.lang.chatsTapToSend); + } + final lastMessage = lastMessageSnapshot.data!; + return StreamBuilder( + stream: notOpenedMessages, + builder: (context, notOpenedMessagesSnapshot) { + if (!lastMessageSnapshot.hasData) { + return Container(); + } + + var lastMessages = [lastMessage]; + if (notOpenedMessagesSnapshot.data != null) { + lastMessages = notOpenedMessagesSnapshot.data!; + } + + return Row( + children: [ + MessageSendStateIcon(lastMessages), + Text("•"), + const SizedBox(width: 5), + Text( + formatDuration(lastMessageInSeconds), + style: TextStyle(fontSize: 12), ), - ], - ), - leading: InitialsAvatar(displayName: widget.user.displayName), + if (flameCounter > 0) + FlameCounterWidget( + widget.user, + flameCounter, + widget.maxTotalMediaCounter, + prefix: true, + ), + ], + ); + }, + ); + }, + ), + leading: InitialsAvatar(getContactDisplayName(widget.user)), onTap: () { - if (widget.lastMessage == null) { + if (currentMessage == null) { context .read() .updateSendNextMediaTo(widget.user.userId.toInt()); globalUpdateOfHomeViewPageIndex(0); return; } - if (isDownloading) return; - if (!widget.lastMessage!.isDownloaded) { - tryDownloadMedia(widget.lastMessage!.messageId, - widget.lastMessage!.otherUserId, token, - force: true); + Message msg = currentMessage!; + if (msg.downloadState == DownloadState.downloading) { + return; + } + if (msg.downloadState == DownloadState.pending) { + tryDownloadMedia(msg.messageId, msg.contactId, token, force: true); return; } if (state == MessageSendState.received && - widget.lastMessage!.containsOtherMedia()) { + msg.kind == MessageKind.media) { Navigator.push( context, MaterialPageRoute(builder: (context) { - return MediaViewerView(widget.user, widget.lastMessage!); + return MediaViewerView(widget.user, msg); }), ); return; diff --git a/lib/src/views/chats/media_viewer_view.dart b/lib/src/views/chats/media_viewer_view.dart index 9dff168..37c04a6 100644 --- a/lib/src/views/chats/media_viewer_view.dart +++ b/lib/src/views/chats/media_viewer_view.dart @@ -7,9 +7,9 @@ import 'package:no_screenshot/no_screenshot.dart'; import 'package:provider/provider.dart'; import 'package:twonly/src/components/animate_icon.dart'; import 'package:twonly/src/components/media_view_sizing.dart'; -import 'package:twonly/src/model/contacts_model.dart'; +import '../../../../.blocked/archives/contacts_model.dart'; import 'package:twonly/src/model/json/message.dart'; -import 'package:twonly/src/model/messages_model.dart'; +import '../../../../.blocked/archives/messages_model.dart'; import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/providers/send_next_media_to.dart'; diff --git a/lib/src/views/chats/search_username_view.dart b/lib/src/views/chats/search_username_view.dart index 9898bce..b48bc07 100644 --- a/lib/src/views/chats/search_username_view.dart +++ b/lib/src/views/chats/search_username_view.dart @@ -1,14 +1,14 @@ 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/utils/misc.dart'; -import 'package:provider/provider.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/contacts_model.dart'; import 'package:twonly/src/model/json/message.dart'; -import 'package:twonly/src/providers/contacts_change_provider.dart'; import 'package:twonly/src/providers/api/api.dart'; // ignore: library_prefixes import 'package:twonly/src/utils/signal.dart' as SignalHelper; @@ -31,18 +31,28 @@ class _SearchUsernameView extends State { final res = await apiProvider.getUserData(searchUserName.text); - if (res.isSuccess) { - bool added = await DbContacts.insertNewContact( - searchUserName.text, - res.value.userdata.userId.toInt(), - false, - ); + if (!context.mounted) { + return; + } - if (added) { + if (res.isSuccess) { + final addUser = await showAlertDialog( + context, "User found", "Do you want to create a follow request?"); + if (!addUser || !context.mounted) { + return; + } + + int added = await context.db.insertContact(ContactsCompanion( + username: Value(searchUserName.text), + userId: Value(res.value.userdata.userId), + requested: Value(false), + )); + + if (added > 0) { if (await SignalHelper.addNewContact(res.value.userdata)) { encryptAndSendMessage( res.value.userdata.userId, - Message( + MessageJson( kind: MessageKind.contactRequest, timestamp: DateTime.now(), content: MessageContent(), @@ -50,7 +60,7 @@ class _SearchUsernameView extends State { ); } } - } else if (context.mounted) { + } else { showAlertDialog(context, context.lang.searchUsernameNotFound, context.lang.searchUsernameNotFoundBody(searchUserName.text)); } @@ -79,6 +89,8 @@ class _SearchUsernameView extends State { ); } + Stream> contacts = context.db.watchNotAcceptedContacts(); + return Scaffold( appBar: AppBar( title: Text(context.lang.searchUsernameTitle), @@ -108,14 +120,24 @@ class _SearchUsernameView extends State { label: Text(context.lang.searchUsernameQrCodeBtn), ), SizedBox(height: 30), - if (context - .watch() - .allContacts - .where((contact) => !contact.accepted) - .isNotEmpty) - HeadLineComponent(context.lang.searchUsernameNewFollowerTitle), - Expanded( - child: ContactsListView(), + StreamBuilder( + stream: contacts, + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data != null) { + return Container(); + } + final contacts = snapshot.data!; + if (contacts.isEmpty) { + return Container(); + } + return Row(children: [ + HeadLineComponent( + context.lang.searchUsernameNewFollowerTitle), + Expanded( + child: ContactsListView(contacts), + ) + ]); + }, ) ], ), @@ -136,7 +158,9 @@ class _SearchUsernameView extends State { } class ContactsListView extends StatefulWidget { - const ContactsListView({super.key}); + const ContactsListView(this.contacts, {super.key}); + + final List contacts; @override State createState() => _ContactsListViewState(); @@ -145,18 +169,14 @@ class ContactsListView extends StatefulWidget { class _ContactsListViewState extends State { @override Widget build(BuildContext context) { - List contacts = context - .read() - .allContacts - .where((contact) => !contact.accepted) - .toList(); return ListView.builder( - itemCount: contacts.length, + itemCount: widget.contacts.length, itemBuilder: (context, index) { - final contact = contacts[index]; + final contact = widget.contacts[index]; + final displayName = getContactDisplayName(contact); return ListTile( - title: Text(contact.displayName), - leading: InitialsAvatar(displayName: contact.displayName), + title: Text(displayName), + leading: InitialsAvatar(displayName), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -168,7 +188,8 @@ class _ContactsListViewState extends State { icon: Icon(Icons.person_off_rounded, color: const Color.fromARGB(164, 244, 67, 54)), onPressed: () async { - await DbContacts.blockUser(contact.userId.toInt()); + final update = ContactsCompanion(blocked: Value(true)); + await context.db.updateContact(contact.userId, update); }, ), ), @@ -177,10 +198,10 @@ class _ContactsListViewState extends State { child: IconButton( icon: Icon(Icons.close, color: Colors.red), onPressed: () async { - await DbContacts.deleteUser(contact.userId.toInt()); + await context.db.deleteContactByUserId(contact.userId); encryptAndSendMessage( contact.userId, - Message( + MessageJson( kind: MessageKind.rejectRequest, timestamp: DateTime.now(), content: MessageContent(), @@ -192,10 +213,11 @@ class _ContactsListViewState extends State { IconButton( icon: Icon(Icons.check, color: Colors.green), onPressed: () async { - await DbContacts.acceptUser(contact.userId.toInt()); + final update = ContactsCompanion(accepted: Value(true)); + await context.db.updateContact(contact.userId, update); encryptAndSendMessage( contact.userId, - Message( + MessageJson( kind: MessageKind.acceptRequest, timestamp: DateTime.now(), content: MessageContent(), diff --git a/lib/src/views/contact/contact_verify_view.dart b/lib/src/views/contact/contact_verify_view.dart index e6d3c20..582fa7e 100644 --- a/lib/src/views/contact/contact_verify_view.dart +++ b/lib/src/views/contact/contact_verify_view.dart @@ -1,12 +1,12 @@ import 'dart:convert'; +import 'package:drift/drift.dart' hide Column; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; -import 'package:provider/provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:twonly/src/components/format_long_string.dart'; -import 'package:twonly/src/model/contacts_model.dart'; import 'package:flutter/material.dart'; -import 'package:twonly/src/providers/contacts_change_provider.dart'; +import 'package:twonly/src/database/contacts_db.dart'; +import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/signal.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -36,10 +36,9 @@ class _ContactVerifyViewState extends State { @override Widget build(BuildContext context) { - Contact contact = context - .watch() - .allContacts - .firstWhere((c) => c.userId == widget.contact.userId); + Stream contact = context.db + .getContactByUserId(widget.contact.userId) + .watchSingleOrNull(); return Scaffold( appBar: AppBar( @@ -92,13 +91,21 @@ class _ContactVerifyViewState extends State { ), ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text( - context.lang - .contactVerifyNumberLongDesc(contact.displayName), - textAlign: TextAlign.center, - ), + StreamBuilder( + stream: contact, + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data == null) { + return Container(); + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Text( + context.lang.contactVerifyNumberLongDesc( + getContactDisplayName(snapshot.data!)), + textAlign: TextAlign.center, + ), + ); + }, ), Padding( padding: @@ -125,24 +132,34 @@ class _ContactVerifyViewState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - contact.verified - ? OutlinedButton.icon( + StreamBuilder( + stream: contact, + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data == null) { + return Container(); + } + final contact = snapshot.data!; + if (contact.verified) { + return OutlinedButton.icon( onPressed: () { - DbContacts.updateVerificationStatus( - contact.userId.toInt(), false); + final update = + ContactsCompanion(verified: Value(false)); + context.db.updateContact(contact.userId, update); }, label: Text( context.lang.contactVerifyNumberClearVerification), - ) - : FilledButton.icon( - icon: FaIcon(FontAwesomeIcons.shieldHeart), - onPressed: () { - DbContacts.updateVerificationStatus( - contact.userId.toInt(), true); - }, - label: - Text(context.lang.contactVerifyNumberMarkAsVerified), - ), + ); + } + return FilledButton.icon( + icon: FaIcon(FontAwesomeIcons.shieldHeart), + onPressed: () { + final update = ContactsCompanion(verified: Value(true)); + context.db.updateContact(contact.userId, update); + }, + label: Text(context.lang.contactVerifyNumberMarkAsVerified), + ); + }, + ), ], ), ), diff --git a/lib/src/views/contact/contact_view.dart b/lib/src/views/contact/contact_view.dart index 16d3f17..187e0aa 100644 --- a/lib/src/views/contact/contact_view.dart +++ b/lib/src/views/contact/contact_view.dart @@ -1,14 +1,13 @@ +import 'package:drift/drift.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:provider/provider.dart'; import 'package:twonly/src/components/alert_dialog.dart'; import 'package:twonly/src/components/better_list_title.dart'; import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/verified_shield.dart'; -import 'package:twonly/src/model/contacts_model.dart'; import 'package:flutter/material.dart'; -import 'package:twonly/src/providers/contacts_change_provider.dart'; -import 'package:twonly/src/providers/messages_change_provider.dart'; +import 'package:twonly/src/database/contacts_db.dart'; +import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/contact/contact_verify_view.dart'; @@ -24,93 +23,100 @@ class ContactView extends StatefulWidget { class _ContactViewState extends State { @override Widget build(BuildContext context) { - Contact contact = context - .watch() - .allContacts - .firstWhere((c) => c.userId == widget.userId); - - int flameCounter = context - .watch() - .flamesCounter[contact.userId.toInt()] ?? - 0; + Stream contact = + context.db.getContactByUserId(widget.userId).watchSingleOrNull(); return Scaffold( appBar: AppBar( title: Text(""), ), - body: ListView( - children: [ - Padding( - padding: const EdgeInsets.all(10), - child: InitialsAvatar( - displayName: contact.displayName, - fontSize: 30, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, + body: StreamBuilder( + stream: contact, + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data == null) { + return Container(); + } + final contact = snapshot.data!; + int flameCounter = getFlameCounterFromContact(contact); + return ListView( children: [ Padding( - padding: EdgeInsets.only(right: 10), - child: VerifiedShield(contact)), - Text( - contact.displayName, - style: TextStyle(fontSize: 20), - ), - if (flameCounter > 0) - FlameCounterWidget( - contact, - flameCounter, - 110000000, - prefix: true, + padding: const EdgeInsets.all(10), + child: InitialsAvatar( + getContactDisplayName(contact), + fontSize: 30, ), - ], - ), - SizedBox(height: 50), - BetterListTile( - icon: FontAwesomeIcons.pencil, - text: context.lang.contactNickname, - onTap: () async { - String? newUsername = - await showNicknameChangeDialog(context, contact); - if (newUsername != null && newUsername != "") { - await DbContacts.changeDisplayName( - contact.userId.toInt(), newUsername); - } - }, - ), - const Divider(), - BetterListTile( - icon: FontAwesomeIcons.shieldHeart, - text: context.lang.contactVerifyNumberTitle, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return ContactVerifyView(contact); + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(right: 10), + child: VerifiedShield(contact)), + Text( + getContactDisplayName(contact), + style: TextStyle(fontSize: 20), + ), + if (flameCounter > 0) + FlameCounterWidget( + contact, + flameCounter, + 110000000, + prefix: true, + ), + ], + ), + SizedBox(height: 50), + BetterListTile( + icon: FontAwesomeIcons.pencil, + text: context.lang.contactNickname, + onTap: () async { + String? nickName = + await showNicknameChangeDialog(context, contact); + + if (context.mounted && nickName != null && nickName != "") { + final update = ContactsCompanion(nickName: Value(nickName)); + context.db.updateContact(contact.userId, update); + } }, - )); - }, - ), - BetterListTile( - icon: FontAwesomeIcons.ban, - color: Colors.red, - text: context.lang.contactBlock, - onTap: () async { - bool block = await showAlertDialog( - context, - context.lang.contactBlockTitle(contact.displayName), - context.lang.contactBlockBody, - ); - if (block) { - await DbContacts.blockUser(contact.userId.toInt()); - // go back to the first - if (context.mounted) { - Navigator.popUntil(context, (route) => route.isFirst); - } - } - }, - ), - ], + ), + const Divider(), + BetterListTile( + icon: FontAwesomeIcons.shieldHeart, + text: context.lang.contactVerifyNumberTitle, + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (context) { + return ContactVerifyView(contact); + }, + )); + }, + ), + BetterListTile( + icon: FontAwesomeIcons.ban, + color: Colors.red, + text: context.lang.contactBlock, + onTap: () async { + bool block = await showAlertDialog( + context, + context.lang + .contactBlockTitle(getContactDisplayName(contact)), + context.lang.contactBlockBody, + ); + if (block) { + final update = ContactsCompanion(blocked: Value(true)); + if (context.mounted) { + await context.db.updateContact(contact.userId, update); + } + if (context.mounted) { + Navigator.popUntil(context, (route) => route.isFirst); + } + } + }, + ), + ], + ); + }, ), ); } diff --git a/lib/src/views/settings/privacy_view.dart b/lib/src/views/settings/privacy_view.dart index 2689575..55b0cc9 100644 --- a/lib/src/views/settings/privacy_view.dart +++ b/lib/src/views/settings/privacy_view.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/settings/privacy_view_block_users.dart'; @@ -11,17 +10,9 @@ class PrivacyView extends StatefulWidget { } class _PrivacyViewState extends State { - List blockedUsers = []; - @override void initState() { super.initState(); - updateBlockedUsers(); - } - - Future updateBlockedUsers() async { - blockedUsers = await DbContacts.getBlockedUsers(); - setState(() {}); } @override @@ -34,15 +25,25 @@ class _PrivacyViewState extends State { children: [ ListTile( title: Text(context.lang.settingsPrivacyBlockUsers), - subtitle: Text( - context.lang.settingsPrivacyBlockUsersCount(blockedUsers.length), + subtitle: StreamBuilder( + stream: context.db.watchContactsBlocked(), + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data != null) { + return Text( + context.lang.settingsPrivacyBlockUsersCount(snapshot.data!), + ); + } else { + return Text( + context.lang.settingsPrivacyBlockUsersCount(0), + ); + } + }, ), onTap: () async { await Navigator.push(context, MaterialPageRoute(builder: (context) { return PrivacyViewBlockUsers(); })); - updateBlockedUsers(); }, ), ], diff --git a/lib/src/views/settings/privacy_view_block_users.dart b/lib/src/views/settings/privacy_view_block_users.dart index eef8adf..0ae9818 100644 --- a/lib/src/views/settings/privacy_view_block_users.dart +++ b/lib/src/views/settings/privacy_view_block_users.dart @@ -1,6 +1,8 @@ +import 'package:drift/drift.dart' hide Column; import 'package:flutter/material.dart'; import 'package:twonly/src/components/initialsavatar.dart'; -import 'package:twonly/src/model/contacts_model.dart'; +import 'package:twonly/src/database/contacts_db.dart'; +import 'package:twonly/src/database/database.dart'; import 'package:twonly/src/utils/misc.dart'; class PrivacyViewBlockUsers extends StatefulWidget { @@ -11,35 +13,34 @@ class PrivacyViewBlockUsers extends StatefulWidget { } class _PrivacyViewBlockUsers extends State { - List allUsers = []; + late Stream> allUsers; List filteredUsers = []; - String lastQuery = ""; + String filter = ""; @override void initState() { super.initState(); + allUsers = context.db.watchAllContacts(); loadAsync(); } Future loadAsync() async { - allUsers = await DbContacts.getAllUsers(); - _filterUsers(lastQuery); setState(() {}); } - Future _filterUsers(String query) async { - lastQuery = query; - if (query.isEmpty) { - filteredUsers = allUsers; - setState(() {}); - return; - } - filteredUsers = allUsers - .where((user) => - user.displayName.toLowerCase().contains(query.toLowerCase())) - .toList(); - setState(() {}); - } + // Future _filterUsers(String query) async { + // lastQuery = query; + // if (query.isEmpty) { + // filteredUsers = allUsers; + // setState(() {}); + // return; + // } + // filteredUsers = allUsers + // .where((user) => + // user.displayName.toLowerCase().contains(query.toLowerCase())) + // .toList(); + // setState(() {}); + // } @override Widget build(BuildContext context) { @@ -54,7 +55,9 @@ class _PrivacyViewBlockUsers extends State { Padding( padding: EdgeInsets.symmetric(horizontal: 10), child: TextField( - onChanged: _filterUsers, + onChanged: (value) => setState(() { + filter = value; + }), decoration: getInputDecoration( context, context.lang.searchUsernameInput, @@ -68,10 +71,22 @@ class _PrivacyViewBlockUsers extends State { ), const SizedBox(height: 30), Expanded( - child: UserList( - List.from(filteredUsers), - updateStatus: () { - loadAsync(); + child: StreamBuilder( + stream: allUsers, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Container(); + } + + final filteredContacts = snapshot.data!.where((contact) { + return getContactDisplayName(contact) + .toLowerCase() + .contains(filter.toLowerCase()); + }).toList(); + + return UserList( + List.from(filteredContacts), + ); }, ), ) @@ -83,20 +98,21 @@ class _PrivacyViewBlockUsers extends State { } class UserList extends StatelessWidget { - const UserList(this.users, {super.key, required this.updateStatus}); + const UserList(this.users, {super.key}); final List users; - final Function updateStatus; - Future block(bool? value, int userId) async { - if (value == null) return; - await DbContacts.blockUser(userId, unblock: !value); - updateStatus(); + Future block(BuildContext context, int userId, bool? value) async { + if (value != null) { + final update = ContactsCompanion(blocked: Value(!value)); + await context.db.updateContact(userId, update); + } } @override Widget build(BuildContext context) { // Step 1: Sort the users alphabetically - users.sort((a, b) => a.displayName.compareTo(b.displayName)); + users.sort( + (a, b) => getContactDisplayName(a).compareTo(getContactDisplayName(b))); return ListView.builder( restorationId: 'new_message_users_list', @@ -105,20 +121,20 @@ class UserList extends StatelessWidget { Contact user = users[i]; return ListTile( title: Row(children: [ - Text(user.displayName), + Text(getContactDisplayName(user)), ]), leading: InitialsAvatar( - displayName: user.displayName, + getContactDisplayName(user), fontSize: 15, ), trailing: Checkbox( value: user.blocked, onChanged: (bool? value) { - block(value, user.userId.toInt()); + block(context, user.userId, value); }, ), onTap: () { - block(!user.blocked, user.userId.toInt()); + block(context, user.userId, !user.blocked); }, ); }, diff --git a/lib/src/views/settings/settings_main_view.dart b/lib/src/views/settings/settings_main_view.dart index 940052a..1a1d492 100644 --- a/lib/src/views/settings/settings_main_view.dart +++ b/lib/src/views/settings/settings_main_view.dart @@ -46,7 +46,7 @@ class _ProfileViewState extends State { child: Row( children: [ InitialsAvatar( - displayName: userData!.username, + userData!.username, fontSize: 30, ), SizedBox(width: 20), diff --git a/pubspec.lock b/pubspec.lock index 0518c02..0560602 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -93,18 +93,18 @@ packages: dependency: transitive description: name: build_resolvers - sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e" + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" + sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" url: "https://pub.dev" source: hosted - version: "2.4.14" + version: "2.4.15" build_runner_core: dependency: transitive description: @@ -153,6 +153,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + url: "https://pub.dev" + source: hosted + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -273,6 +281,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + drift: + dependency: "direct main" + description: + name: drift + sha256: "97d5832657d49f26e7a8e07de397ddc63790b039372878d5117af816d0fdb5cb" + url: "https://pub.dev" + source: hosted + version: "2.25.1" + drift_dev: + dependency: "direct dev" + description: + name: drift_dev + sha256: f1db88482dbb016b9bbddddf746d5d0a6938b156ff20e07320052981f97388cc + url: "https://pub.dev" + source: hosted + version: "2.25.2" + drift_flutter: + dependency: "direct main" + description: + name: drift_flutter + sha256: "0cadbf3b8733409a6cf61d18ba2e94e149df81df7de26f48ae0695b48fd71922" + url: "https://pub.dev" + source: hosted + version: "0.2.4" ed25519_edwards: dependency: transitive description: @@ -1133,6 +1165,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" reorderables: dependency: "direct main" description: @@ -1290,6 +1330,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0+1" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: "32b632dda27d664f85520093ed6f735ae5c49b5b75345afb8b19411bc59bb53d" + url: "https://pub.dev" + source: hosted + version: "2.7.4" + sqlite3_flutter_libs: + dependency: transitive + description: + name: sqlite3_flutter_libs + sha256: "7adb4cc96dc08648a5eb1d80a7619070796ca6db03901ff2b6dcb15ee30468f3" + url: "https://pub.dev" + source: hosted + version: "0.5.31" + sqlparser: + dependency: transitive + description: + name: sqlparser + sha256: "27dd0a9f0c02e22ac0eb42a23df9ea079ce69b52bb4a3b478d64e0ef34a263ee" + url: "https://pub.dev" + source: hosted + version: "0.41.0" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b28914c..bffdc92 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,8 @@ dependencies: 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 @@ -60,10 +62,11 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - build_runner: ^2.3.3 + build_runner: ^2.4.15 json_serializable: ^6.8.0 flutter_lints: ^5.0.0 flutter_launcher_icons: ^0.14.1 + drift_dev: ^2.25.2 flutter_launcher_icons: android: true