diff --git a/.github/workflows/dev_github.yml b/.github/workflows/dev_github.yml index c4a5e39..6284a37 100644 --- a/.github/workflows/dev_github.yml +++ b/.github/workflows/dev_github.yml @@ -1,4 +1,4 @@ -name: Publish on Github +name: Flutter analyze & test on: workflow_dispatch: {} @@ -9,7 +9,7 @@ on: - lib/**/*.dart jobs: - build_and_publish: + flutter_analyze_and_test: runs-on: ubuntu-latest permissions: contents: write diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe3681..5121631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ # Changelog +## 0.0.61 + +- Improving image editor when changing colors +- Fixing message decryption error +- Fixing issue with user deletion +- Fixing issue with flame counter sync +- Dependency and Flutter upgrade ## 0.0.60 - Improved logging to debug the 'Tap to load' issue. - -==> If you encounter any issues, please send your debug log via the feedback button along with a short description of the error so that we can resolve them. :) - - Display your own avatar in the title bar of the chat list. - Created a default avatar image in case none was set. - Improved UI handling when requesting microphone access for the first time. diff --git a/analysis_options.yaml b/analysis_options.yaml index 1a25be6..2552c6d 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -10,6 +10,7 @@ analyzer: inference_failure_on_instance_creation: ignore avoid_positional_boolean_parameters: ignore inference_failure_on_collection_literal: ignore + matching_super_parameters: ignore exclude: - "lib/src/model/protobuf/**" - "lib/src/model/protobuf/api/websocket/**" @@ -23,3 +24,4 @@ linter: public_member_api_docs: false avoid_catches_without_on_clauses: false document_ignores: false + discarded_futures: false diff --git a/android/app/build.gradle b/android/app/build.gradle index 6b71187..a500868 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -17,7 +17,7 @@ if (keystorePropertiesFile.exists()) { android { namespace = "eu.twonly" // compileSdk = flutter.compileSdkVersion - compileSdk 35 + compileSdk 36 //ndkVersion = flutter.ndkVersion ndkVersion = "27.0.12077973" @@ -37,7 +37,7 @@ android { multiDexEnabled true // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. - minSdk = 23 + minSdkVersion = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName @@ -67,4 +67,4 @@ flutter { dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4' -} \ No newline at end of file +} diff --git a/android/settings.gradle b/android/settings.gradle index 199cd49..cc0a7b6 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -22,7 +22,7 @@ plugins { // START: FlutterFire Configuration id "com.google.gms.google-services" version "4.3.15" apply false // END: FlutterFire Configuration - id "org.jetbrains.kotlin.android" version "2.0.20" apply false + id "org.jetbrains.kotlin.android" version "2.1.0" apply false } include ":app" diff --git a/drift_schemas/twonly_database/drift_schema_v17.json b/drift_schemas/twonly_database/drift_schema_v17.json new file mode 100644 index 0000000..9ed61a7 --- /dev/null +++ b/drift_schemas/twonly_database/drift_schema_v17.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"contacts","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"username","getter_name":"username","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"UNIQUE","dialectAwareDefaultConstraints":{"sqlite":"UNIQUE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unique"]},{"name":"display_name","getter_name":"displayName","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"nick_name","getter_name":"nickName","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"avatar_svg","getter_name":"avatarSvg","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"my_avatar_counter","getter_name":"myAvatarCounter","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"accepted","getter_name":"accepted","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"accepted\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"accepted\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"requested","getter_name":"requested","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"requested\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"requested\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"blocked","getter_name":"blocked","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"blocked\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"blocked\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"verified","getter_name":"verified","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"verified\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"verified\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"archived","getter_name":"archived","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"archived\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"archived\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"pinned","getter_name":"pinned","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"pinned\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"pinned\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"deleted","getter_name":"deleted","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"deleted\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"deleted\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"also_best_friend","getter_name":"alsoBestFriend","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"also_best_friend\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"also_best_friend\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"delete_messages_after_x_minutes","getter_name":"deleteMessagesAfterXMinutes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('1440')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]},{"name":"total_media_counter","getter_name":"totalMediaCounter","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"last_message_send","getter_name":"lastMessageSend","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"last_message_received","getter_name":"lastMessageReceived","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"last_flame_counter_change","getter_name":"lastFlameCounterChange","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"last_flame_sync","getter_name":"lastFlameSync","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"last_message_exchange","getter_name":"lastMessageExchange","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]},{"name":"flame_counter","getter_name":"flameCounter","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["user_id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"messages","was_declared_in_moor":false,"columns":[{"name":"contact_id","getter_name":"contactId","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES contacts (user_id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES contacts (user_id)"},"default_dart":null,"default_client_dart":null,"dsl_features":[{"foreign_key":{"to":{"table":"contacts","column":"user_id"},"initially_deferred":false,"on_update":null,"on_delete":null}}]},{"name":"message_id","getter_name":"messageId","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"message_other_id","getter_name":"messageOtherId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"media_upload_id","getter_name":"mediaUploadId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"media_download_id","getter_name":"mediaDownloadId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"response_to_message_id","getter_name":"responseToMessageId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"response_to_other_message_id","getter_name":"responseToOtherMessageId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"acknowledge_by_user","getter_name":"acknowledgeByUser","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"acknowledge_by_user\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"acknowledge_by_user\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"media_stored","getter_name":"mediaStored","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"media_stored\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"media_stored\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"download_state","getter_name":"downloadState","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('2')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(DownloadState.values)","dart_type_name":"DownloadState"}},{"name":"acknowledge_by_server","getter_name":"acknowledgeByServer","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"acknowledge_by_server\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"acknowledge_by_server\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"error_while_sending","getter_name":"errorWhileSending","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"error_while_sending\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"error_while_sending\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"media_retransmission_state","getter_name":"mediaRetransmissionState","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'none\\'')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(MediaRetransmitting.values)","dart_type_name":"MediaRetransmitting"}},{"name":"kind","getter_name":"kind","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(MessageKind.values)","dart_type_name":"MessageKind"}},{"name":"content_json","getter_name":"contentJson","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"opened_at","getter_name":"openedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"send_at","getter_name":"sendAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":2,"references":[],"type":"table","data":{"name":"media_uploads","was_declared_in_moor":false,"columns":[{"name":"media_upload_id","getter_name":"mediaUploadId","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"state","getter_name":"state","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'pending\\'')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(UploadState.values)","dart_type_name":"UploadState"}},{"name":"metadata","getter_name":"metadata","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MediaUploadMetadataConverter()","dart_type_name":"MediaUploadMetadata"}},{"name":"message_ids","getter_name":"messageIds","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"IntListTypeConverter()","dart_type_name":"List"}},{"name":"encryption_data","getter_name":"encryptionData","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MediaEncryptionDataConverter()","dart_type_name":"MediaEncryptionData"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":3,"references":[],"type":"table","data":{"name":"signal_identity_key_stores","was_declared_in_moor":false,"columns":[{"name":"device_id","getter_name":"deviceId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"identity_key","getter_name":"identityKey","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["device_id","name"]}},{"id":4,"references":[],"type":"table","data":{"name":"signal_pre_key_stores","was_declared_in_moor":false,"columns":[{"name":"pre_key_id","getter_name":"preKeyId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pre_key","getter_name":"preKey","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["pre_key_id"]}},{"id":5,"references":[],"type":"table","data":{"name":"signal_sender_key_stores","was_declared_in_moor":false,"columns":[{"name":"sender_key_name","getter_name":"senderKeyName","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"sender_key","getter_name":"senderKey","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["sender_key_name"]}},{"id":6,"references":[],"type":"table","data":{"name":"signal_session_stores","was_declared_in_moor":false,"columns":[{"name":"device_id","getter_name":"deviceId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"session_record","getter_name":"sessionRecord","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["device_id","name"]}},{"id":7,"references":[],"type":"table","data":{"name":"signal_contact_pre_keys","was_declared_in_moor":false,"columns":[{"name":"contact_id","getter_name":"contactId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pre_key_id","getter_name":"preKeyId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pre_key","getter_name":"preKey","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["contact_id","pre_key_id"]}},{"id":8,"references":[],"type":"table","data":{"name":"signal_contact_signed_pre_keys","was_declared_in_moor":false,"columns":[{"name":"contact_id","getter_name":"contactId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"signed_pre_key_id","getter_name":"signedPreKeyId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"signed_pre_key","getter_name":"signedPreKey","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"signed_pre_key_signature","getter_name":"signedPreKeySignature","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["contact_id"]}},{"id":9,"references":[0,1],"type":"table","data":{"name":"message_retransmissions","was_declared_in_moor":false,"columns":[{"name":"retransmission_id","getter_name":"retransmissionId","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"contact_id","getter_name":"contactId","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES contacts (user_id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES contacts (user_id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":[{"foreign_key":{"to":{"table":"contacts","column":"user_id"},"initially_deferred":false,"on_update":null,"on_delete":"cascade"}}]},{"name":"message_id","getter_name":"messageId","moor_type":"int","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES messages (message_id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES messages (message_id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":[{"foreign_key":{"to":{"table":"messages","column":"message_id"},"initially_deferred":false,"on_update":null,"on_delete":"cascade"}}]},{"name":"plaintext_content","getter_name":"plaintextContent","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"push_data","getter_name":"pushData","moor_type":"blob","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"encrypted_hash","getter_name":"encryptedHash","moor_type":"blob","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"retry_count","getter_name":"retryCount","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"last_retry","getter_name":"lastRetry","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"acknowledge_by_server_at","getter_name":"acknowledgeByServerAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]} \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index 88b581a..856e804 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '14.0' +platform :ios, '15.0' use_frameworks! # CocoaPods analytics sends network stats synchronously affecting flutter build latency. diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 87dd41b..42f70db 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,56 +9,56 @@ PODS: - Flutter - device_info_plus (0.0.1): - Flutter - - Firebase (11.15.0): - - Firebase/Core (= 11.15.0) - - Firebase/Core (11.15.0): + - Firebase (12.4.0): + - Firebase/Core (= 12.4.0) + - Firebase/Core (12.4.0): - Firebase/CoreOnly - - FirebaseAnalytics (~> 11.15.0) - - Firebase/CoreOnly (11.15.0): - - FirebaseCore (~> 11.15.0) - - Firebase/Messaging (11.15.0): + - FirebaseAnalytics (~> 12.4.0) + - Firebase/CoreOnly (12.4.0): + - FirebaseCore (~> 12.4.0) + - Firebase/Messaging (12.4.0): - Firebase/CoreOnly - - FirebaseMessaging (~> 11.15.0) - - firebase_core (3.15.2): - - Firebase/CoreOnly (= 11.15.0) + - FirebaseMessaging (~> 12.4.0) + - firebase_core (4.2.0): + - Firebase/CoreOnly (= 12.4.0) - Flutter - - firebase_messaging (15.2.10): - - Firebase/Messaging (= 11.15.0) + - firebase_messaging (16.0.3): + - Firebase/Messaging (= 12.4.0) - firebase_core - Flutter - - FirebaseAnalytics (11.15.0): - - FirebaseAnalytics/Default (= 11.15.0) - - FirebaseCore (~> 11.15.0) - - FirebaseInstallations (~> 11.0) + - FirebaseAnalytics (12.4.0): + - FirebaseAnalytics/Default (= 12.4.0) + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - FirebaseAnalytics/Default (11.15.0): - - FirebaseCore (~> 11.15.0) - - FirebaseInstallations (~> 11.0) - - GoogleAppMeasurement/Default (= 11.15.0) + - FirebaseAnalytics/Default (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - GoogleAppMeasurement/Default (= 12.4.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - FirebaseCore (11.15.0): - - FirebaseCoreInternal (~> 11.15.0) + - FirebaseCore (12.4.0): + - FirebaseCoreInternal (~> 12.4.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Logger (~> 8.1) - - FirebaseCoreInternal (11.15.0): + - FirebaseCoreInternal (12.4.0): - "GoogleUtilities/NSData+zlib (~> 8.1)" - - FirebaseInstallations (11.15.0): - - FirebaseCore (~> 11.15.0) + - FirebaseInstallations (12.4.0): + - FirebaseCore (~> 12.4.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) - PromisesObjC (~> 2.4) - - FirebaseMessaging (11.15.0): - - FirebaseCore (~> 11.15.0) - - FirebaseInstallations (~> 11.0) - - GoogleDataTransport (~> 10.0) + - FirebaseMessaging (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - GoogleDataTransport (~> 10.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Reachability (~> 8.1) @@ -70,7 +70,7 @@ PODS: - Mantle - SDWebImage - SDWebImageWebPCoder - - flutter_keyboard_visibility (0.0.1): + - flutter_keyboard_visibility_temp_fork (0.0.1): - Flutter - flutter_local_notifications (0.0.1): - Flutter @@ -82,27 +82,28 @@ PODS: - gal (1.0.0): - Flutter - FlutterMacOS - - GoogleAdsOnDeviceConversion (2.1.0): + - GoogleAdsOnDeviceConversion (3.1.0): + - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Network (~> 8.1) - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/Core (11.15.0): + - GoogleAppMeasurement/Core (12.4.0): - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/Default (11.15.0): - - GoogleAdsOnDeviceConversion (= 2.1.0) - - GoogleAppMeasurement/Core (= 11.15.0) - - GoogleAppMeasurement/IdentitySupport (= 11.15.0) + - GoogleAppMeasurement/Default (12.4.0): + - GoogleAdsOnDeviceConversion (~> 3.1.0) + - GoogleAppMeasurement/Core (= 12.4.0) + - GoogleAppMeasurement/IdentitySupport (= 12.4.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/IdentitySupport (11.15.0): - - GoogleAppMeasurement/Core (= 11.15.0) + - GoogleAppMeasurement/IdentitySupport (12.4.0): + - GoogleAppMeasurement/Core (= 12.4.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) @@ -190,9 +191,9 @@ PODS: - restart_app (0.0.1): - Flutter - ScreenProtectorKit (1.3.1) - - SDWebImage (5.21.1): - - SDWebImage/Core (= 5.21.1) - - SDWebImage/Core (5.21.1) + - SDWebImage (5.21.3): + - SDWebImage/Core (= 5.21.3) + - SDWebImage/Core (5.21.3) - SDWebImageWebPCoder (0.14.6): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.17) @@ -204,32 +205,32 @@ PODS: - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS - - sqlite3 (3.50.3): - - sqlite3/common (= 3.50.3) - - sqlite3/common (3.50.3) - - sqlite3/dbstatvtab (3.50.3): + - sqlite3 (3.50.4): + - sqlite3/common (= 3.50.4) + - sqlite3/common (3.50.4) + - sqlite3/dbstatvtab (3.50.4): - sqlite3/common - - sqlite3/fts5 (3.50.3): + - sqlite3/fts5 (3.50.4): - sqlite3/common - - sqlite3/math (3.50.3): + - sqlite3/math (3.50.4): - sqlite3/common - - sqlite3/perf-threadsafe (3.50.3): + - sqlite3/perf-threadsafe (3.50.4): - sqlite3/common - - sqlite3/rtree (3.50.3): + - sqlite3/rtree (3.50.4): - sqlite3/common - - sqlite3/session (3.50.3): + - sqlite3/session (3.50.4): - sqlite3/common - sqlite3_flutter_libs (0.0.1): - Flutter - FlutterMacOS - - sqlite3 (~> 3.50.3) + - sqlite3 (~> 3.50.4) - sqlite3/dbstatvtab - sqlite3/fts5 - sqlite3/math - sqlite3/perf-threadsafe - sqlite3/rtree - sqlite3/session - - SwiftProtobuf (1.30.0) + - SwiftProtobuf (1.32.0) - url_launcher_ios (0.0.1): - Flutter - video_compress (0.3.0): @@ -255,7 +256,7 @@ DEPENDENCIES: - FirebaseMessaging - Flutter (from `Flutter`) - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) - - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) + - flutter_keyboard_visibility_temp_fork (from `.symlinks/plugins/flutter_keyboard_visibility_temp_fork/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`) - flutter_zxing (from `.symlinks/plugins/flutter_zxing/ios`) @@ -319,8 +320,8 @@ EXTERNAL SOURCES: :path: Flutter flutter_image_compress_common: :path: ".symlinks/plugins/flutter_image_compress_common/ios" - flutter_keyboard_visibility: - :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" + flutter_keyboard_visibility_temp_fork: + :path: ".symlinks/plugins/flutter_keyboard_visibility_temp_fork/ios" flutter_local_notifications: :path: ".symlinks/plugins/flutter_local_notifications/ios" flutter_secure_storage_darwin: @@ -362,32 +363,32 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad - camera_avfoundation: be3be85408cd4126f250386828e9b1dfa40ab436 + camera_avfoundation: 5675ca25298b6f81fa0a325188e7df62cc217741 connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd cryptography_flutter_plus: 44f4e9e4079395fcbb3e7809c0ac2c6ae2d9576f device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe - Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e - firebase_core: 995454a784ff288be5689b796deb9e9fa3601818 - firebase_messaging: f4a41dd102ac18b840eba3f39d67e77922d3f707 - FirebaseAnalytics: 6433dfd311ba78084fc93bdfc145e8cb75740eae - FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e - FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4 - FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843 - FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09 - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e + firebase_core: 744984dbbed8b3036abf34f0b98d80f130a7e464 + firebase_messaging: 82c70650c426a0a14873e1acdb9ec2b443c4e8b4 + FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f + FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3 + FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6 + FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2 + FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1 - flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619 + flutter_keyboard_visibility_temp_fork: 95b2d534bacf6ac62e7fcbe5c2a9e2c2a17ce06f flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb flutter_secure_storage_darwin: ce237a8775b39723566dc72571190a3769d70468 flutter_zxing: e8bcc43bd3056c70c271b732ed94e7a16fd62f93 gal: baecd024ebfd13c441269ca7404792a7152fde89 - GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64 - GoogleAppMeasurement: 700dce7541804bec33db590a5c496b663fbe2539 + GoogleAdsOnDeviceConversion: e03a386840803ea7eef3fd22a061930142c039c1 + GoogleAppMeasurement: 1e718274b7e015cefd846ac1fcf7820c70dc017d GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 - local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19 + local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 no_screenshot: 6d183496405a3ab709a67a54e5cd0f639e94729e @@ -397,19 +398,19 @@ SPEC CHECKSUMS: PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 restart_app: 9cda5378aacc5000e3f66ee76a9201534e7d3ecf ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4 - SDWebImage: f29024626962457f3470184232766516dee8dfea + SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - sqlite3: 83105acd294c9137c026e2da1931c30b4588ab81 - sqlite3_flutter_libs: 616267f2fca40e9c6af8c5d82324e05667040b6e - SwiftProtobuf: 3697407f0d5b23bedeba9c2eaaf3ec6fdff69349 + sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b + sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1 + SwiftProtobuf: 81e341191afbddd64aa031bd12862dccfab2f639 url_launcher_ios: 694010445543906933d732453a59da0a173ae33d video_compress: f2133a07762889d67f0711ac831faa26f956980e video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140 -PODFILE CHECKSUM: a01f0821a361ca6708e29b1299e8becf492a8a71 +PODFILE CHECKSUM: 47470fbd5b59affa461eaf943ac57acce81e0ee8 COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ed10d98..3b02435 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -609,7 +609,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -752,7 +752,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -805,7 +805,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/l10n.yaml b/l10n.yaml index 7dafe6f..438687a 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -1,4 +1,3 @@ -synthetic-package: false arb-dir: lib/src/localization template-arb-file: app_en.arb output-localization-file: app_localizations.dart diff --git a/lib/app.dart b/lib/app.dart index 0de5d7d..36c9c2d 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -28,16 +28,18 @@ class _AppState extends State with WidgetsBindingObserver { globalIsAppInBackground = false; WidgetsBinding.instance.addObserver(this); - globalCallbackConnectionState = ({required bool isConnected}) { - context.read().updateConnectionState(isConnected); - setUserPlan(); + globalCallbackConnectionState = ({required bool isConnected}) async { + await context + .read() + .updateConnectionState(isConnected); + await setUserPlan(); }; - globalCallbackUpdatePlan = (String planId) { - context.read().updatePlan(planId); + globalCallbackUpdatePlan = (String planId) async { + await context.read().updatePlan(planId); }; - initAsync(); + unawaited(initAsync()); } Future setUserPlan() async { @@ -77,12 +79,12 @@ class _AppState extends State with WidgetsBindingObserver { if (wasPaused) { globalIsAppInBackground = false; twonlyDB.markUpdated(); - apiService.connect(force: true); + unawaited(apiService.connect(force: true)); } } else if (state == AppLifecycleState.paused) { wasPaused = true; globalIsAppInBackground = true; - handleUploadWhenAppGoesBackground(); + unawaited(handleUploadWhenAppGoesBackground()); } } @@ -138,7 +140,7 @@ class _AppState extends State with WidgetsBindingObserver { initialRoute: '/', routes: { '/': (context) => const AppMainWidget(initialPage: 1), - '/chats': (context) => const AppMainWidget(initialPage: 0) + '/chats': (context) => const AppMainWidget(initialPage: 0), }, ); }, diff --git a/lib/src/database/daos/contacts_dao.dart b/lib/src/database/daos/contacts_dao.dart index f889628..f97918f 100644 --- a/lib/src/database/daos/contacts_dao.dart +++ b/lib/src/database/daos/contacts_dao.dart @@ -10,6 +10,7 @@ class ContactsDao extends DatabaseAccessor with _$ContactsDaoMixin { // this constructor is required so that the main database can create an instance // of this object. + // ignore: matching_super_parameters ContactsDao(super.db); Future insertContact(ContactsCompanion contact) async { @@ -102,7 +103,9 @@ class ContactsDao extends DatabaseAccessor } Future updateContact( - int userId, ContactsCompanion updatedValues) async { + int userId, + ContactsCompanion updatedValues, + ) async { await (update(contacts)..where((c) => c.userId.equals(userId))) .write(updatedValues); if (updatedValues.blocked.present || @@ -117,10 +120,12 @@ class ContactsDao extends DatabaseAccessor Stream> watchNotAcceptedContacts() { return (select(contacts) - ..where((t) => - t.accepted.equals(false) & - t.archived.equals(false) & - t.blocked.equals(false))) + ..where( + (t) => + t.accepted.equals(false) & + t.archived.equals(false) & + t.blocked.equals(false), + )) .watch(); // return (select(contacts)).watch(); } @@ -132,10 +137,12 @@ class ContactsDao extends DatabaseAccessor Stream> watchContactsForShareView() { return (select(contacts) - ..where((t) => - t.accepted.equals(true) & - t.blocked.equals(false) & - t.deleted.equals(false)) + ..where( + (t) => + t.accepted.equals(true) & + t.blocked.equals(false) & + t.deleted.equals(false), + ) ..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)])) .watch(); } @@ -177,8 +184,9 @@ class ContactsDao extends DatabaseAccessor Stream watchContactsRequested() { final count = contacts.requested.count(distinct: true); final query = selectOnly(contacts) - ..where(contacts.requested.equals(true) & - contacts.accepted.equals(true).not()) + ..where( + contacts.requested.equals(true) & contacts.accepted.equals(true).not(), + ) ..addColumns([count]); return query.map((row) => row.read(count)).watchSingle(); } @@ -187,6 +195,16 @@ class ContactsDao extends DatabaseAccessor return select(contacts).watch(); } + Future modifyFlameCounterForTesting() async { + await update(contacts).write( + ContactsCompanion( + lastFlameCounterChange: Value(DateTime.now()), + flameCounter: const Value(1337), + lastFlameSync: const Value(null), + ), + ); + } + Stream watchFlameCounter(int userId) { return (select(contacts) ..where( diff --git a/lib/src/database/daos/media_uploads_dao.dart b/lib/src/database/daos/media_uploads_dao.dart index 4ac6961..97135b3 100644 --- a/lib/src/database/daos/media_uploads_dao.dart +++ b/lib/src/database/daos/media_uploads_dao.dart @@ -8,17 +8,21 @@ part 'media_uploads_dao.g.dart'; @DriftAccessor(tables: [MediaUploads]) class MediaUploadsDao extends DatabaseAccessor with _$MediaUploadsDaoMixin { + // ignore: matching_super_parameters MediaUploadsDao(super.db); Future> getMediaUploadsForRetry() { return (select(mediaUploads) ..where( - (t) => t.state.equals(UploadState.receiverNotified.name).not())) + (t) => t.state.equals(UploadState.receiverNotified.name).not(), + )) .get(); } Future updateMediaUpload( - int mediaUploadId, MediaUploadsCompanion updatedValues) { + int mediaUploadId, + MediaUploadsCompanion updatedValues, + ) { return (update(mediaUploads) ..where((c) => c.mediaUploadId.equals(mediaUploadId))) .write(updatedValues); diff --git a/lib/src/database/daos/message_retransmissions.dao.dart b/lib/src/database/daos/message_retransmissions.dao.dart index 49cb0a9..b4a7934 100644 --- a/lib/src/database/daos/message_retransmissions.dao.dart +++ b/lib/src/database/daos/message_retransmissions.dao.dart @@ -10,10 +10,12 @@ class MessageRetransmissionDao extends DatabaseAccessor with _$MessageRetransmissionDaoMixin { // this constructor is required so that the main database can create an instance // of this object. + // ignore: matching_super_parameters MessageRetransmissionDao(super.db); Future insertRetransmission( - MessageRetransmissionsCompanion message) async { + MessageRetransmissionsCompanion message, + ) async { try { return await into(messageRetransmissions).insert(message); } catch (e) { @@ -25,18 +27,22 @@ class MessageRetransmissionDao extends DatabaseAccessor Future purgeOldRetransmissions() async { // delete entries older than two weeks await (delete(messageRetransmissions) - ..where((t) => (t.acknowledgeByServerAt.isSmallerThanValue( - DateTime.now().subtract( - const Duration(days: 25), - ), - )))) + ..where( + (t) => (t.acknowledgeByServerAt.isSmallerThanValue( + DateTime.now().subtract( + const Duration(days: 25), + ), + )), + )) .go(); } Future> getRetransmitAbleMessages() async { final countDeleted = await (delete(messageRetransmissions) - ..where((t) => - t.encryptedHash.isNull() & t.acknowledgeByServerAt.isNotNull())) + ..where( + (t) => + t.encryptedHash.isNull() & t.acknowledgeByServerAt.isNotNull(), + )) .go(); if (countDeleted > 0) { @@ -51,11 +57,18 @@ class MessageRetransmissionDao extends DatabaseAccessor } SingleOrNullSelectable getRetransmissionById( - int retransmissionId) { + int retransmissionId, + ) { return select(messageRetransmissions) ..where((t) => t.retransmissionId.equals(retransmissionId)); } + Stream> watchAllMessages() { + return (select(messageRetransmissions) + ..orderBy([(t) => OrderingTerm.asc(t.retransmissionId)])) + .watch(); + } + Future updateRetransmission( int retransmissionId, MessageRetransmissionsCompanion updatedValues, @@ -67,9 +80,11 @@ class MessageRetransmissionDao extends DatabaseAccessor Future resetAckStatusFor(int fromUserId, Uint8List encryptedHash) async { return ((update(messageRetransmissions)) - ..where((m) => - m.contactId.equals(fromUserId) & - m.encryptedHash.equals(encryptedHash))) + ..where( + (m) => + m.contactId.equals(fromUserId) & + m.encryptedHash.equals(encryptedHash), + )) .write( const MessageRetransmissionsCompanion( acknowledgeByServerAt: Value(null), @@ -78,11 +93,15 @@ class MessageRetransmissionDao extends DatabaseAccessor } Future getRetransmissionFromHash( - int fromUserId, Uint8List encryptedHash) async { + int fromUserId, + Uint8List encryptedHash, + ) async { return ((select(messageRetransmissions)) - ..where((m) => - m.contactId.equals(fromUserId) & - m.encryptedHash.equals(encryptedHash))) + ..where( + (m) => + m.contactId.equals(fromUserId) & + m.encryptedHash.equals(encryptedHash), + )) .getSingleOrNull(); } diff --git a/lib/src/database/daos/messages_dao.dart b/lib/src/database/daos/messages_dao.dart index 95891f4..7bb05e3 100644 --- a/lib/src/database/daos/messages_dao.dart +++ b/lib/src/database/daos/messages_dao.dart @@ -11,26 +11,31 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { // this constructor is required so that the main database can create an instance // of this object. + // ignore: matching_super_parameters MessagesDao(super.db); Stream> watchMessageNotOpened(int contactId) { return (select(messages) - ..where((t) => - t.openedAt.isNull() & - t.contactId.equals(contactId) & - t.errorWhileSending.equals(false)) + ..where( + (t) => + t.openedAt.isNull() & + t.contactId.equals(contactId) & + t.errorWhileSending.equals(false), + ) ..orderBy([(t) => OrderingTerm.desc(t.sendAt)])) .watch(); } Stream> watchMediaMessageNotOpened(int contactId) { return (select(messages) - ..where((t) => - t.openedAt.isNull() & - t.contactId.equals(contactId) & - t.errorWhileSending.equals(false) & - t.messageOtherId.isNotNull() & - t.kind.equals(MessageKind.media.name)) + ..where( + (t) => + t.openedAt.isNull() & + t.contactId.equals(contactId) & + t.errorWhileSending.equals(false) & + t.messageOtherId.isNotNull() & + t.kind.equals(MessageKind.media.name), + ) ..orderBy([(t) => OrderingTerm.asc(t.sendAt)])) .watch(); } @@ -45,27 +50,33 @@ class MessagesDao extends DatabaseAccessor Stream> watchAllMessagesFrom(int contactId) { return (select(messages) - ..where((t) => - t.contactId.equals(contactId) & - t.contentJson.isNotNull() & - (t.openedAt.isNull() | - t.mediaStored.equals(true) | - t.openedAt.isBiggerThanValue( - DateTime.now().subtract(const Duration(days: 1))))) + ..where( + (t) => + t.contactId.equals(contactId) & + t.contentJson.isNotNull() & + (t.openedAt.isNull() | + t.mediaStored.equals(true) | + t.openedAt.isBiggerThanValue( + DateTime.now().subtract(const Duration(days: 1)), + )), + ) ..orderBy([(t) => OrderingTerm.asc(t.sendAt)])) .watch(); } Future removeOldMessages() { return (update(messages) - ..where((t) => - (t.openedAt.isSmallerThanValue( - DateTime.now().subtract(const Duration(days: 1)), - ) | - (t.sendAt.isSmallerThanValue( - DateTime.now().subtract(const Duration(days: 1))) & - t.errorWhileSending.equals(true))) & - t.kind.equals(MessageKind.textMessage.name))) + ..where( + (t) => + (t.openedAt.isSmallerThanValue( + DateTime.now().subtract(const Duration(days: 1)), + ) | + (t.sendAt.isSmallerThanValue( + DateTime.now().subtract(const Duration(days: 3)), + ) & + t.errorWhileSending.equals(true))) & + t.kind.equals(MessageKind.textMessage.name), + )) .write(const MessagesCompanion(contentJson: Value(null))); } @@ -99,13 +110,15 @@ class MessagesDao extends DatabaseAccessor Future> getAllNonACKMessagesFromUser() { return (select(messages) - ..where((t) => - t.acknowledgeByUser.equals(false) & - t.messageOtherId.isNull() & - t.errorWhileSending.equals(false) & - t.sendAt.isBiggerThanValue( - DateTime.now().subtract(const Duration(minutes: 10)), - ))) + ..where( + (t) => + t.acknowledgeByUser.equals(false) & + t.messageOtherId.isNull() & + t.errorWhileSending.equals(false) & + t.sendAt.isBiggerThanValue( + DateTime.now().subtract(const Duration(minutes: 10)), + ), + )) .get(); } @@ -133,11 +146,13 @@ class MessagesDao extends DatabaseAccessor Future openedAllNonMediaMessages(int contactId) { final updates = MessagesCompanion(openedAt: Value(DateTime.now())); return (update(messages) - ..where((t) => - t.contactId.equals(contactId) & - t.messageOtherId.isNotNull() & - t.openedAt.isNull() & - t.kind.equals(MessageKind.media.name).not())) + ..where( + (t) => + t.contactId.equals(contactId) & + t.messageOtherId.isNotNull() & + t.openedAt.isNull() & + t.kind.equals(MessageKind.media.name).not(), + )) .write(updates); } @@ -148,44 +163,59 @@ class MessagesDao extends DatabaseAccessor const updates = MessagesCompanion(downloadState: Value(DownloadState.pending)); return (update(messages) - ..where((t) => - t.messageOtherId.isNotNull() & - t.downloadState.equals(DownloadState.downloading.index) & - t.kind.equals(MessageKind.media.name))) + ..where( + (t) => + t.messageOtherId.isNotNull() & + t.downloadState.equals(DownloadState.downloading.index) & + t.kind.equals(MessageKind.media.name), + )) .write(updates); } Future openedAllNonMediaMessagesFromOtherUser(int contactId) { final updates = MessagesCompanion(openedAt: Value(DateTime.now())); return (update(messages) - ..where((t) => - t.contactId.equals(contactId) & - t.messageOtherId - .isNull() & // only mark messages open that where send - t.openedAt.isNull() & - t.kind.equals(MessageKind.media.name).not())) + ..where( + (t) => + t.contactId.equals(contactId) & + t.messageOtherId + .isNull() & // only mark messages open that where send + t.openedAt.isNull() & + t.kind.equals(MessageKind.media.name).not(), + )) .write(updates); } Future updateMessageByOtherUser( - int userId, int messageId, MessagesCompanion updatedValues) { + int userId, + int messageId, + MessagesCompanion updatedValues, + ) { return (update(messages) - ..where((c) => - c.contactId.equals(userId) & c.messageId.equals(messageId))) + ..where( + (c) => c.contactId.equals(userId) & c.messageId.equals(messageId), + )) .write(updatedValues); } Future updateMessageByOtherMessageId( - int userId, int messageOtherId, MessagesCompanion updatedValues) { + int userId, + int messageOtherId, + MessagesCompanion updatedValues, + ) { return (update(messages) - ..where((c) => - c.contactId.equals(userId) & - c.messageOtherId.equals(messageOtherId))) + ..where( + (c) => + c.contactId.equals(userId) & + c.messageOtherId.equals(messageOtherId), + )) .write(updatedValues); } Future updateMessageByMessageId( - int messageId, MessagesCompanion updatedValues) { + int messageId, + MessagesCompanion updatedValues, + ) { return (update(messages)..where((c) => c.messageId.equals(messageId))) .write(updatedValues); } @@ -207,17 +237,22 @@ class MessagesDao extends DatabaseAccessor Future deleteMessagesByContactId(int contactId) { return (delete(messages) - ..where((t) => - t.contactId.equals(contactId) & t.mediaStored.equals(false))) + ..where( + (t) => t.contactId.equals(contactId) & t.mediaStored.equals(false), + )) .go(); } Future deleteMessagesByContactIdAndOtherMessageId( - int contactId, int messageOtherId) { + int contactId, + int messageOtherId, + ) { return (delete(messages) - ..where((t) => - t.contactId.equals(contactId) & - t.messageOtherId.equals(messageOtherId))) + ..where( + (t) => + t.contactId.equals(contactId) & + t.messageOtherId.equals(messageOtherId), + )) .go(); } @@ -234,9 +269,11 @@ class MessagesDao extends DatabaseAccessor int messageOtherId, ) async { final query = select(messages) - ..where((t) => - t.messageOtherId.equals(messageOtherId) & - t.contactId.equals(fromUserId)); + ..where( + (t) => + t.messageOtherId.equals(messageOtherId) & + t.contactId.equals(fromUserId), + ); final entry = await query.get(); return entry.isNotEmpty; } @@ -252,16 +289,23 @@ class MessagesDao extends DatabaseAccessor } SingleOrNullSelectable getMessageByOtherMessageId( - int fromUserId, int messageId) { + int fromUserId, + int messageId, + ) { return select(messages) - ..where((t) => - t.messageOtherId.equals(messageId) & t.contactId.equals(fromUserId)); + ..where( + (t) => + t.messageOtherId.equals(messageId) & t.contactId.equals(fromUserId), + ); } SingleOrNullSelectable getMessageByIdAndContactId( - int fromUserId, int messageId) { + int fromUserId, + int messageId, + ) { return select(messages) - ..where((t) => - t.messageId.equals(messageId) & t.contactId.equals(fromUserId)); + ..where( + (t) => t.messageId.equals(messageId) & t.contactId.equals(fromUserId), + ); } } diff --git a/lib/src/database/daos/signal_dao.dart b/lib/src/database/daos/signal_dao.dart index 3ce89cc..e8f4732 100644 --- a/lib/src/database/daos/signal_dao.dart +++ b/lib/src/database/daos/signal_dao.dart @@ -7,13 +7,16 @@ import 'package:twonly/src/utils/log.dart'; part 'signal_dao.g.dart'; -@DriftAccessor(tables: [ - SignalContactPreKeys, - SignalContactSignedPreKeys, -]) +@DriftAccessor( + tables: [ + SignalContactPreKeys, + SignalContactSignedPreKeys, + ], +) class SignalDao extends DatabaseAccessor with _$SignalDaoMixin { // this constructor is required so that the main database can create an instance // of this object. + // ignore: matching_super_parameters SignalDao(super.db); Future deleteAllByContactId(int contactId) async { await (delete(signalContactPreKeys) @@ -48,10 +51,13 @@ class SignalDao extends DatabaseAccessor with _$SignalDaoMixin { if (preKey != null) { // remove the pre key... await (delete(signalContactPreKeys) - ..where((tbl) => - tbl.contactId.equals(contactId) & - tbl.preKeyId.equals(preKey.preKeyId))) + ..where( + (tbl) => + tbl.contactId.equals(contactId) & + tbl.preKeyId.equals(preKey.preKeyId), + )) .go(); + Log.info('Using prekey ${preKey.preKeyId} for $contactId'); return preKey; } return null; @@ -59,7 +65,8 @@ class SignalDao extends DatabaseAccessor with _$SignalDaoMixin { // 3: Insert multiple pre-keys Future insertPreKeys( - List preKeys) async { + List preKeys, + ) async { for (final preKey in preKeys) { try { await into(signalContactPreKeys).insert(preKey); @@ -78,7 +85,8 @@ class SignalDao extends DatabaseAccessor with _$SignalDaoMixin { // 5: Insert or update signed pre-key by contact ID Future insertOrUpdateSignedPreKeyByContactId( - SignalContactSignedPreKeysCompanion signedPreKey) async { + SignalContactSignedPreKeysCompanion signedPreKey, + ) async { await (delete(signalContactSignedPreKeys) ..where((t) => t.contactId.equals(signedPreKey.contactId.value))) .go(); @@ -86,21 +94,35 @@ class SignalDao extends DatabaseAccessor with _$SignalDaoMixin { } Future purgeOutDatedPreKeys() async { - // other pre keys are valid 25 days - await (delete(signalContactSignedPreKeys) - ..where((t) => (t.createdAt.isSmallerThanValue( - DateTime.now().subtract( - const Duration(days: 25), - ), - )))) + // Deletion is a workaround for the issue, that own pre keys where deleted after 40 days, while they could be 30days + // on the server + 25 days on the others device old, resulting in the issue that the receiver could not decrypt the + // messages... + await (delete(signalContactPreKeys) + ..where( + (t) => (t.createdAt.isSmallerThanValue( + DateTime(2025, 10, 10), + )), + )) .go(); - // own pre keys are valid for 40 days + // other pre keys are valid 100 days + await (delete(signalContactPreKeys) + ..where( + (t) => (t.createdAt.isSmallerThanValue( + DateTime.now().subtract( + const Duration(days: 100), + ), + )), + )) + .go(); + // own pre keys are valid for 180 days await (delete(twonlyDB.signalPreKeyStores) - ..where((t) => (t.createdAt.isSmallerThanValue( - DateTime.now().subtract( - const Duration(days: 40), - ), - )))) + ..where( + (t) => (t.createdAt.isSmallerThanValue( + DateTime.now().subtract( + const Duration(days: 365), + ), + )), + )) .go(); } } diff --git a/lib/src/database/signal/connect_identity_key_store.dart b/lib/src/database/signal/connect_identity_key_store.dart index aa2fe55..47d4d88 100644 --- a/lib/src/database/signal/connect_identity_key_store.dart +++ b/lib/src/database/signal/connect_identity_key_store.dart @@ -13,9 +13,11 @@ class ConnectIdentityKeyStore extends IdentityKeyStore { @override Future getIdentity(SignalProtocolAddress address) async { final identity = await (twonlyDB.select(twonlyDB.signalIdentityKeyStores) - ..where((t) => - t.deviceId.equals(address.getDeviceId()) & - t.name.equals(address.getName()))) + ..where( + (t) => + t.deviceId.equals(address.getDeviceId()) & + t.name.equals(address.getName()), + )) .getSingleOrNull(); if (identity == null) return null; return IdentityKey.fromBytes(identity.identityKey, 0); @@ -28,8 +30,11 @@ class ConnectIdentityKeyStore extends IdentityKeyStore { Future getLocalRegistrationId() async => localRegistrationId; @override - Future isTrustedIdentity(SignalProtocolAddress address, - IdentityKey? identityKey, Direction? direction) async { + Future isTrustedIdentity( + SignalProtocolAddress address, + IdentityKey? identityKey, + Direction? direction, + ) async { final trusted = await getIdentity(address); if (identityKey == null) { return false; @@ -41,7 +46,9 @@ class ConnectIdentityKeyStore extends IdentityKeyStore { @override Future saveIdentity( - SignalProtocolAddress address, IdentityKey? identityKey) async { + SignalProtocolAddress address, + IdentityKey? identityKey, + ) async { if (identityKey == null) { return false; } @@ -55,9 +62,11 @@ class ConnectIdentityKeyStore extends IdentityKeyStore { ); } else { await (twonlyDB.update(twonlyDB.signalIdentityKeyStores) - ..where((t) => - t.deviceId.equals(address.getDeviceId()) & - t.name.equals(address.getName()))) + ..where( + (t) => + t.deviceId.equals(address.getDeviceId()) & + t.name.equals(address.getName()), + )) .write( SignalIdentityKeyStoresCompanion( identityKey: Value(identityKey.serialize()), diff --git a/lib/src/database/signal/connect_pre_key_store.dart b/lib/src/database/signal/connect_pre_key_store.dart index 934b4f2..6fb193b 100644 --- a/lib/src/database/signal/connect_pre_key_store.dart +++ b/lib/src/database/signal/connect_pre_key_store.dart @@ -21,6 +21,7 @@ class ConnectPreKeyStore extends PreKeyStore { if (preKeyRecord.isEmpty) { throw InvalidKeyIdException('No such preKey record! - $preKeyId'); } + Log.info('Contact used preKey $preKeyId'); final preKey = preKeyRecord.first.preKey; return PreKeyRecord.fromBuffer(preKey); } diff --git a/lib/src/database/signal/connect_sender_key_store.dart b/lib/src/database/signal/connect_sender_key_store.dart index 3639e73..e9b0ee2 100644 --- a/lib/src/database/signal/connect_sender_key_store.dart +++ b/lib/src/database/signal/connect_sender_key_store.dart @@ -11,14 +11,17 @@ class ConnectSenderKeyStore extends SenderKeyStore { .getSingleOrNull(); if (identity == null) { throw InvalidKeyIdException( - 'No such sender key record! - $senderKeyName'); + 'No such sender key record! - $senderKeyName', + ); } return SenderKeyRecord.fromSerialized(identity.senderKey); } @override Future storeSenderKey( - SenderKeyName senderKeyName, SenderKeyRecord record) async { + SenderKeyName senderKeyName, + SenderKeyRecord record, + ) async { await twonlyDB.into(twonlyDB.signalSenderKeyStores).insert( SignalSenderKeyStoresCompanion( senderKey: Value(record.serialize()), diff --git a/lib/src/database/signal/connect_session_store.dart b/lib/src/database/signal/connect_session_store.dart index 889b749..d851600 100644 --- a/lib/src/database/signal/connect_session_store.dart +++ b/lib/src/database/signal/connect_session_store.dart @@ -7,9 +7,11 @@ class ConnectSessionStore extends SessionStore { @override Future containsSession(SignalProtocolAddress address) async { final sessions = await (twonlyDB.select(twonlyDB.signalSessionStores) - ..where((tbl) => - tbl.deviceId.equals(address.getDeviceId()) & - tbl.name.equals(address.getName()))) + ..where( + (tbl) => + tbl.deviceId.equals(address.getDeviceId()) & + tbl.name.equals(address.getName()), + )) .get(); return sessions.isNotEmpty; } @@ -24,9 +26,11 @@ class ConnectSessionStore extends SessionStore { @override Future deleteSession(SignalProtocolAddress address) async { await (twonlyDB.delete(twonlyDB.signalSessionStores) - ..where((tbl) => - tbl.deviceId.equals(address.getDeviceId()) & - tbl.name.equals(address.getName()))) + ..where( + (tbl) => + tbl.deviceId.equals(address.getDeviceId()) & + tbl.name.equals(address.getName()), + )) .go(); } @@ -34,7 +38,8 @@ class ConnectSessionStore extends SessionStore { Future> getSubDeviceSessions(String name) async { final deviceIds = await (twonlyDB.select(twonlyDB.signalSessionStores) ..where( - (tbl) => tbl.deviceId.equals(1).not() & tbl.name.equals(name))) + (tbl) => tbl.deviceId.equals(1).not() & tbl.name.equals(name), + )) .get(); return deviceIds.map((row) => row.deviceId).toList(); } @@ -42,9 +47,11 @@ class ConnectSessionStore extends SessionStore { @override Future loadSession(SignalProtocolAddress address) async { final dbSession = await (twonlyDB.select(twonlyDB.signalSessionStores) - ..where((tbl) => - tbl.deviceId.equals(address.getDeviceId()) & - tbl.name.equals(address.getName()))) + ..where( + (tbl) => + tbl.deviceId.equals(address.getDeviceId()) & + tbl.name.equals(address.getName()), + )) .get(); if (dbSession.isEmpty) { @@ -56,7 +63,9 @@ class ConnectSessionStore extends SessionStore { @override Future storeSession( - SignalProtocolAddress address, SessionRecord record) async { + SignalProtocolAddress address, + SessionRecord record, + ) async { final sessionCompanion = SignalSessionStoresCompanion( deviceId: Value(address.getDeviceId()), name: Value(address.getName()), @@ -69,9 +78,11 @@ class ConnectSessionStore extends SessionStore { .insert(sessionCompanion); } else { await (twonlyDB.update(twonlyDB.signalSessionStores) - ..where((tbl) => - tbl.deviceId.equals(address.getDeviceId()) & - tbl.name.equals(address.getName()))) + ..where( + (tbl) => + tbl.deviceId.equals(address.getDeviceId()) & + tbl.name.equals(address.getName()), + )) .write(sessionCompanion); } } diff --git a/lib/src/database/signal/connect_signal_protocol_store.dart b/lib/src/database/signal/connect_signal_protocol_store.dart index 3a6913a..f84774d 100644 --- a/lib/src/database/signal/connect_signal_protocol_store.dart +++ b/lib/src/database/signal/connect_signal_protocol_store.dart @@ -6,7 +6,9 @@ import 'package:twonly/src/database/signal/connect_signed_pre_key_store.dart'; class ConnectSignalProtocolStore implements SignalProtocolStore { ConnectSignalProtocolStore( - IdentityKeyPair identityKeyPair, int registrationId) { + IdentityKeyPair identityKeyPair, + int registrationId, + ) { _identityKeyStore = ConnectIdentityKeyStore(identityKeyPair, registrationId); } @@ -27,12 +29,17 @@ class ConnectSignalProtocolStore implements SignalProtocolStore { @override Future saveIdentity( - SignalProtocolAddress address, IdentityKey? identityKey) async => + SignalProtocolAddress address, + IdentityKey? identityKey, + ) async => _identityKeyStore.saveIdentity(address, identityKey); @override - Future isTrustedIdentity(SignalProtocolAddress address, - IdentityKey? identityKey, Direction direction) async => + Future isTrustedIdentity( + SignalProtocolAddress address, + IdentityKey? identityKey, + Direction direction, + ) async => _identityKeyStore.isTrustedIdentity(address, identityKey, direction); @override @@ -67,7 +74,9 @@ class ConnectSignalProtocolStore implements SignalProtocolStore { @override Future storeSession( - SignalProtocolAddress address, SessionRecord record) async { + SignalProtocolAddress address, + SessionRecord record, + ) async { await sessionStore.storeSession(address, record); } @@ -95,7 +104,9 @@ class ConnectSignalProtocolStore implements SignalProtocolStore { @override Future storeSignedPreKey( - int signedPreKeyId, SignedPreKeyRecord record) async { + int signedPreKeyId, + SignedPreKeyRecord record, + ) async { await signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record); } diff --git a/lib/src/database/tables/message_retransmissions.dart b/lib/src/database/tables/message_retransmissions.dart index 55da58b..a746f98 100644 --- a/lib/src/database/tables/message_retransmissions.dart +++ b/lib/src/database/tables/message_retransmissions.dart @@ -16,5 +16,8 @@ class MessageRetransmissions extends Table { BlobColumn get pushData => blob().nullable()(); BlobColumn get encryptedHash => blob().nullable()(); + IntColumn get retryCount => integer().withDefault(const Constant(0))(); + DateTimeColumn get lastRetry => dateTime().nullable()(); + DateTimeColumn get acknowledgeByServerAt => dateTime().nullable()(); } diff --git a/lib/src/database/twonly_database.dart b/lib/src/database/twonly_database.dart index 16f6070..65a88ec 100644 --- a/lib/src/database/twonly_database.dart +++ b/lib/src/database/twonly_database.dart @@ -23,34 +23,38 @@ import 'package:twonly/src/utils/log.dart'; part 'twonly_database.g.dart'; // You can then create a database class that includes this table -@DriftDatabase(tables: [ - Contacts, - Messages, - MediaUploads, - SignalIdentityKeyStores, - SignalPreKeyStores, - SignalSenderKeyStores, - SignalSessionStores, - SignalContactPreKeys, - SignalContactSignedPreKeys, - MessageRetransmissions -], daos: [ - MessagesDao, - ContactsDao, - MediaUploadsDao, - SignalDao, - MessageRetransmissionDao -]) +@DriftDatabase( + tables: [ + Contacts, + Messages, + MediaUploads, + SignalIdentityKeyStores, + SignalPreKeyStores, + SignalSenderKeyStores, + SignalSessionStores, + SignalContactPreKeys, + SignalContactSignedPreKeys, + MessageRetransmissions, + ], + daos: [ + MessagesDao, + ContactsDao, + MediaUploadsDao, + SignalDao, + MessageRetransmissionDao, + ], +) class TwonlyDatabase extends _$TwonlyDatabase { TwonlyDatabase([QueryExecutor? e]) : super( e ?? _openConnection(), ); + // ignore: matching_super_parameters TwonlyDatabase.forTesting(DatabaseConnection super.connection); @override - int get schemaVersion => 16; + int get schemaVersion => 17; static QueryExecutor _openConnection() { return driftDatabase( @@ -74,17 +78,21 @@ class TwonlyDatabase extends _$TwonlyDatabase { from2To3: (m, schema) async { await m.addColumn(schema.contacts, schema.contacts.archived); await m.addColumn( - schema.contacts, schema.contacts.deleteMessagesAfterXMinutes); + schema.contacts, + schema.contacts.deleteMessagesAfterXMinutes, + ); }, from3To4: (m, schema) async { await m.createTable(schema.mediaUploads); - await m.alterTable(TableMigration( - schema.mediaUploads, - columnTransformer: { - schema.mediaUploads.metadata: - schema.mediaUploads.metadata.cast(), - }, - )); + await m.alterTable( + TableMigration( + schema.mediaUploads, + columnTransformer: { + schema.mediaUploads.metadata: + schema.mediaUploads.metadata.cast(), + }, + ), + ); }, from4To5: (m, schema) async { await m.createTable(schema.mediaDownloads); @@ -102,13 +110,15 @@ class TwonlyDatabase extends _$TwonlyDatabase { await m.addColumn(schema.contacts, schema.contacts.lastFlameSync); }, from8To9: (m, schema) async { - await m.alterTable(TableMigration( - schema.mediaUploads, - columnTransformer: { - schema.mediaUploads.metadata: - schema.mediaUploads.metadata.cast(), - }, - )); + await m.alterTable( + TableMigration( + schema.mediaUploads, + columnTransformer: { + schema.mediaUploads.metadata: + schema.mediaUploads.metadata.cast(), + }, + ), + ); }, from9To10: (m, schema) async { await m.createTable(schema.signalContactPreKeys); @@ -119,26 +129,44 @@ class TwonlyDatabase extends _$TwonlyDatabase { await m.createTable(schema.messageRetransmissions); }, from11To12: (m, schema) async { - await m.addColumn(schema.messageRetransmissions, - schema.messageRetransmissions.willNotGetACKByUser); + await m.addColumn( + schema.messageRetransmissions, + schema.messageRetransmissions.willNotGetACKByUser, + ); }, from12To13: (m, schema) async { await m.dropColumn( - schema.messageRetransmissions, 'will_not_get_a_c_k_by_user'); + schema.messageRetransmissions, + 'will_not_get_a_c_k_by_user', + ); }, from13To14: (m, schema) async { - await m.addColumn(schema.messageRetransmissions, - schema.messageRetransmissions.encryptedHash); + await m.addColumn( + schema.messageRetransmissions, + schema.messageRetransmissions.encryptedHash, + ); }, from14To15: (m, schema) async { await m.dropColumn(schema.mediaUploads, 'upload_tokens'); await m.dropColumn(schema.mediaUploads, 'already_notified'); await m.addColumn( - schema.messages, schema.messages.mediaRetransmissionState); + schema.messages, + schema.messages.mediaRetransmissionState, + ); }, from15To16: (m, schema) async { await m.deleteTable('media_downloads'); }, + from16To17: (m, schema) async { + await m.addColumn( + schema.messageRetransmissions, + schema.messageRetransmissions.lastRetry, + ); + await m.addColumn( + schema.messageRetransmissions, + schema.messageRetransmissions.retryCount, + ); + }, ), ); } @@ -150,8 +178,8 @@ class TwonlyDatabase extends _$TwonlyDatabase { Future printTableSizes() async { final result = await customSelect( - 'SELECT name, SUM(pgsize) as size FROM dbstat GROUP BY name') - .get(); + 'SELECT name, SUM(pgsize) as size FROM dbstat GROUP BY name', + ).get(); for (final row in result) { final tableName = row.read('name'); @@ -173,11 +201,13 @@ class TwonlyDatabase extends _$TwonlyDatabase { await delete(signalContactPreKeys).go(); await delete(signalContactSignedPreKeys).go(); await (delete(signalPreKeyStores) - ..where((t) => (t.createdAt.isSmallerThanValue( - DateTime.now().subtract( - const Duration(days: 25), - ), - )))) + ..where( + (t) => (t.createdAt.isSmallerThanValue( + DateTime.now().subtract( + const Duration(days: 25), + ), + )), + )) .go(); } } diff --git a/lib/src/database/twonly_database.g.dart b/lib/src/database/twonly_database.g.dart index f1f9cbd..bef787d 100644 --- a/lib/src/database/twonly_database.g.dart +++ b/lib/src/database/twonly_database.g.dart @@ -3983,6 +3983,20 @@ class $MessageRetransmissionsTable extends MessageRetransmissions late final GeneratedColumn encryptedHash = GeneratedColumn('encrypted_hash', aliasedName, true, type: DriftSqlType.blob, requiredDuringInsert: false); + static const VerificationMeta _retryCountMeta = + const VerificationMeta('retryCount'); + @override + late final GeneratedColumn retryCount = GeneratedColumn( + 'retry_count', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(0)); + static const VerificationMeta _lastRetryMeta = + const VerificationMeta('lastRetry'); + @override + late final GeneratedColumn lastRetry = GeneratedColumn( + 'last_retry', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); static const VerificationMeta _acknowledgeByServerAtMeta = const VerificationMeta('acknowledgeByServerAt'); @override @@ -3997,6 +4011,8 @@ class $MessageRetransmissionsTable extends MessageRetransmissions plaintextContent, pushData, encryptedHash, + retryCount, + lastRetry, acknowledgeByServerAt ]; @override @@ -4044,6 +4060,16 @@ class $MessageRetransmissionsTable extends MessageRetransmissions encryptedHash.isAcceptableOrUnknown( data['encrypted_hash']!, _encryptedHashMeta)); } + if (data.containsKey('retry_count')) { + context.handle( + _retryCountMeta, + retryCount.isAcceptableOrUnknown( + data['retry_count']!, _retryCountMeta)); + } + if (data.containsKey('last_retry')) { + context.handle(_lastRetryMeta, + lastRetry.isAcceptableOrUnknown(data['last_retry']!, _lastRetryMeta)); + } if (data.containsKey('acknowledge_by_server_at')) { context.handle( _acknowledgeByServerAtMeta, @@ -4071,6 +4097,10 @@ class $MessageRetransmissionsTable extends MessageRetransmissions .read(DriftSqlType.blob, data['${effectivePrefix}push_data']), encryptedHash: attachedDatabase.typeMapping .read(DriftSqlType.blob, data['${effectivePrefix}encrypted_hash']), + retryCount: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}retry_count'])!, + lastRetry: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}last_retry']), acknowledgeByServerAt: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}acknowledge_by_server_at']), @@ -4091,6 +4121,8 @@ class MessageRetransmission extends DataClass final Uint8List plaintextContent; final Uint8List? pushData; final Uint8List? encryptedHash; + final int retryCount; + final DateTime? lastRetry; final DateTime? acknowledgeByServerAt; const MessageRetransmission( {required this.retransmissionId, @@ -4099,6 +4131,8 @@ class MessageRetransmission extends DataClass required this.plaintextContent, this.pushData, this.encryptedHash, + required this.retryCount, + this.lastRetry, this.acknowledgeByServerAt}); @override Map toColumns(bool nullToAbsent) { @@ -4115,6 +4149,10 @@ class MessageRetransmission extends DataClass if (!nullToAbsent || encryptedHash != null) { map['encrypted_hash'] = Variable(encryptedHash); } + map['retry_count'] = Variable(retryCount); + if (!nullToAbsent || lastRetry != null) { + map['last_retry'] = Variable(lastRetry); + } if (!nullToAbsent || acknowledgeByServerAt != null) { map['acknowledge_by_server_at'] = Variable(acknowledgeByServerAt); @@ -4136,6 +4174,10 @@ class MessageRetransmission extends DataClass encryptedHash: encryptedHash == null && nullToAbsent ? const Value.absent() : Value(encryptedHash), + retryCount: Value(retryCount), + lastRetry: lastRetry == null && nullToAbsent + ? const Value.absent() + : Value(lastRetry), acknowledgeByServerAt: acknowledgeByServerAt == null && nullToAbsent ? const Value.absent() : Value(acknowledgeByServerAt), @@ -4153,6 +4195,8 @@ class MessageRetransmission extends DataClass serializer.fromJson(json['plaintextContent']), pushData: serializer.fromJson(json['pushData']), encryptedHash: serializer.fromJson(json['encryptedHash']), + retryCount: serializer.fromJson(json['retryCount']), + lastRetry: serializer.fromJson(json['lastRetry']), acknowledgeByServerAt: serializer.fromJson(json['acknowledgeByServerAt']), ); @@ -4167,6 +4211,8 @@ class MessageRetransmission extends DataClass 'plaintextContent': serializer.toJson(plaintextContent), 'pushData': serializer.toJson(pushData), 'encryptedHash': serializer.toJson(encryptedHash), + 'retryCount': serializer.toJson(retryCount), + 'lastRetry': serializer.toJson(lastRetry), 'acknowledgeByServerAt': serializer.toJson(acknowledgeByServerAt), }; @@ -4179,6 +4225,8 @@ class MessageRetransmission extends DataClass Uint8List? plaintextContent, Value pushData = const Value.absent(), Value encryptedHash = const Value.absent(), + int? retryCount, + Value lastRetry = const Value.absent(), Value acknowledgeByServerAt = const Value.absent()}) => MessageRetransmission( retransmissionId: retransmissionId ?? this.retransmissionId, @@ -4188,6 +4236,8 @@ class MessageRetransmission extends DataClass pushData: pushData.present ? pushData.value : this.pushData, encryptedHash: encryptedHash.present ? encryptedHash.value : this.encryptedHash, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry.present ? lastRetry.value : this.lastRetry, acknowledgeByServerAt: acknowledgeByServerAt.present ? acknowledgeByServerAt.value : this.acknowledgeByServerAt, @@ -4207,6 +4257,9 @@ class MessageRetransmission extends DataClass encryptedHash: data.encryptedHash.present ? data.encryptedHash.value : this.encryptedHash, + retryCount: + data.retryCount.present ? data.retryCount.value : this.retryCount, + lastRetry: data.lastRetry.present ? data.lastRetry.value : this.lastRetry, acknowledgeByServerAt: data.acknowledgeByServerAt.present ? data.acknowledgeByServerAt.value : this.acknowledgeByServerAt, @@ -4222,6 +4275,8 @@ class MessageRetransmission extends DataClass ..write('plaintextContent: $plaintextContent, ') ..write('pushData: $pushData, ') ..write('encryptedHash: $encryptedHash, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') ..write('acknowledgeByServerAt: $acknowledgeByServerAt') ..write(')')) .toString(); @@ -4235,6 +4290,8 @@ class MessageRetransmission extends DataClass $driftBlobEquality.hash(plaintextContent), $driftBlobEquality.hash(pushData), $driftBlobEquality.hash(encryptedHash), + retryCount, + lastRetry, acknowledgeByServerAt); @override bool operator ==(Object other) => @@ -4247,6 +4304,8 @@ class MessageRetransmission extends DataClass other.plaintextContent, this.plaintextContent) && $driftBlobEquality.equals(other.pushData, this.pushData) && $driftBlobEquality.equals(other.encryptedHash, this.encryptedHash) && + other.retryCount == this.retryCount && + other.lastRetry == this.lastRetry && other.acknowledgeByServerAt == this.acknowledgeByServerAt); } @@ -4258,6 +4317,8 @@ class MessageRetransmissionsCompanion final Value plaintextContent; final Value pushData; final Value encryptedHash; + final Value retryCount; + final Value lastRetry; final Value acknowledgeByServerAt; const MessageRetransmissionsCompanion({ this.retransmissionId = const Value.absent(), @@ -4266,6 +4327,8 @@ class MessageRetransmissionsCompanion this.plaintextContent = const Value.absent(), this.pushData = const Value.absent(), this.encryptedHash = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), this.acknowledgeByServerAt = const Value.absent(), }); MessageRetransmissionsCompanion.insert({ @@ -4275,6 +4338,8 @@ class MessageRetransmissionsCompanion required Uint8List plaintextContent, this.pushData = const Value.absent(), this.encryptedHash = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), this.acknowledgeByServerAt = const Value.absent(), }) : contactId = Value(contactId), plaintextContent = Value(plaintextContent); @@ -4285,6 +4350,8 @@ class MessageRetransmissionsCompanion Expression? plaintextContent, Expression? pushData, Expression? encryptedHash, + Expression? retryCount, + Expression? lastRetry, Expression? acknowledgeByServerAt, }) { return RawValuesInsertable({ @@ -4294,6 +4361,8 @@ class MessageRetransmissionsCompanion if (plaintextContent != null) 'plaintext_content': plaintextContent, if (pushData != null) 'push_data': pushData, if (encryptedHash != null) 'encrypted_hash': encryptedHash, + if (retryCount != null) 'retry_count': retryCount, + if (lastRetry != null) 'last_retry': lastRetry, if (acknowledgeByServerAt != null) 'acknowledge_by_server_at': acknowledgeByServerAt, }); @@ -4306,6 +4375,8 @@ class MessageRetransmissionsCompanion Value? plaintextContent, Value? pushData, Value? encryptedHash, + Value? retryCount, + Value? lastRetry, Value? acknowledgeByServerAt}) { return MessageRetransmissionsCompanion( retransmissionId: retransmissionId ?? this.retransmissionId, @@ -4314,6 +4385,8 @@ class MessageRetransmissionsCompanion plaintextContent: plaintextContent ?? this.plaintextContent, pushData: pushData ?? this.pushData, encryptedHash: encryptedHash ?? this.encryptedHash, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry ?? this.lastRetry, acknowledgeByServerAt: acknowledgeByServerAt ?? this.acknowledgeByServerAt, ); @@ -4340,6 +4413,12 @@ class MessageRetransmissionsCompanion if (encryptedHash.present) { map['encrypted_hash'] = Variable(encryptedHash.value); } + if (retryCount.present) { + map['retry_count'] = Variable(retryCount.value); + } + if (lastRetry.present) { + map['last_retry'] = Variable(lastRetry.value); + } if (acknowledgeByServerAt.present) { map['acknowledge_by_server_at'] = Variable(acknowledgeByServerAt.value); @@ -4356,6 +4435,8 @@ class MessageRetransmissionsCompanion ..write('plaintextContent: $plaintextContent, ') ..write('pushData: $pushData, ') ..write('encryptedHash: $encryptedHash, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') ..write('acknowledgeByServerAt: $acknowledgeByServerAt') ..write(')')) .toString(); @@ -6752,6 +6833,8 @@ typedef $$MessageRetransmissionsTableCreateCompanionBuilder required Uint8List plaintextContent, Value pushData, Value encryptedHash, + Value retryCount, + Value lastRetry, Value acknowledgeByServerAt, }); typedef $$MessageRetransmissionsTableUpdateCompanionBuilder @@ -6762,6 +6845,8 @@ typedef $$MessageRetransmissionsTableUpdateCompanionBuilder Value plaintextContent, Value pushData, Value encryptedHash, + Value retryCount, + Value lastRetry, Value acknowledgeByServerAt, }); @@ -6824,6 +6909,12 @@ class $$MessageRetransmissionsTableFilterComposer ColumnFilters get encryptedHash => $composableBuilder( column: $table.encryptedHash, builder: (column) => ColumnFilters(column)); + ColumnFilters get retryCount => $composableBuilder( + column: $table.retryCount, builder: (column) => ColumnFilters(column)); + + ColumnFilters get lastRetry => $composableBuilder( + column: $table.lastRetry, builder: (column) => ColumnFilters(column)); + ColumnFilters get acknowledgeByServerAt => $composableBuilder( column: $table.acknowledgeByServerAt, builder: (column) => ColumnFilters(column)); @@ -6893,6 +6984,12 @@ class $$MessageRetransmissionsTableOrderingComposer column: $table.encryptedHash, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get retryCount => $composableBuilder( + column: $table.retryCount, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get lastRetry => $composableBuilder( + column: $table.lastRetry, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get acknowledgeByServerAt => $composableBuilder( column: $table.acknowledgeByServerAt, builder: (column) => ColumnOrderings(column)); @@ -6959,6 +7056,12 @@ class $$MessageRetransmissionsTableAnnotationComposer GeneratedColumn get encryptedHash => $composableBuilder( column: $table.encryptedHash, builder: (column) => column); + GeneratedColumn get retryCount => $composableBuilder( + column: $table.retryCount, builder: (column) => column); + + GeneratedColumn get lastRetry => + $composableBuilder(column: $table.lastRetry, builder: (column) => column); + GeneratedColumn get acknowledgeByServerAt => $composableBuilder( column: $table.acknowledgeByServerAt, builder: (column) => column); @@ -7036,6 +7139,8 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager< Value plaintextContent = const Value.absent(), Value pushData = const Value.absent(), Value encryptedHash = const Value.absent(), + Value retryCount = const Value.absent(), + Value lastRetry = const Value.absent(), Value acknowledgeByServerAt = const Value.absent(), }) => MessageRetransmissionsCompanion( @@ -7045,6 +7150,8 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager< plaintextContent: plaintextContent, pushData: pushData, encryptedHash: encryptedHash, + retryCount: retryCount, + lastRetry: lastRetry, acknowledgeByServerAt: acknowledgeByServerAt, ), createCompanionCallback: ({ @@ -7054,6 +7161,8 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager< required Uint8List plaintextContent, Value pushData = const Value.absent(), Value encryptedHash = const Value.absent(), + Value retryCount = const Value.absent(), + Value lastRetry = const Value.absent(), Value acknowledgeByServerAt = const Value.absent(), }) => MessageRetransmissionsCompanion.insert( @@ -7063,6 +7172,8 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager< plaintextContent: plaintextContent, pushData: pushData, encryptedHash: encryptedHash, + retryCount: retryCount, + lastRetry: lastRetry, acknowledgeByServerAt: acknowledgeByServerAt, ), withReferenceMapper: (p0) => p0 diff --git a/lib/src/database/twonly_database.steps.dart b/lib/src/database/twonly_database.steps.dart index a485b29..389937e 100644 --- a/lib/src/database/twonly_database.steps.dart +++ b/lib/src/database/twonly_database.steps.dart @@ -3713,6 +3713,253 @@ final class Schema16 extends i0.VersionedSchema { alias: null); } +final class Schema17 extends i0.VersionedSchema { + Schema17({required super.database}) : super(version: 17); + @override + late final List entities = [ + contacts, + messages, + mediaUploads, + signalIdentityKeyStores, + signalPreKeyStores, + signalSenderKeyStores, + signalSessionStores, + signalContactPreKeys, + signalContactSignedPreKeys, + messageRetransmissions, + ]; + late final Shape13 contacts = Shape13( + source: i0.VersionedTable( + entityName: 'contacts', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'PRIMARY KEY(user_id)', + ], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + _column_8, + _column_9, + _column_39, + _column_53, + _column_57, + _column_54, + _column_40, + _column_10, + _column_11, + _column_12, + _column_13, + _column_14, + _column_55, + _column_15, + _column_16, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape19 messages = Shape19( + source: i0.VersionedTable( + entityName: 'messages', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_17, + _column_18, + _column_19, + _column_48, + _column_49, + _column_20, + _column_21, + _column_22, + _column_52, + _column_23, + _column_24, + _column_25, + _column_70, + _column_26, + _column_27, + _column_28, + _column_29, + _column_30, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape20 mediaUploads = Shape20( + source: i0.VersionedTable( + entityName: 'media_uploads', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_41, + _column_42, + _column_56, + _column_44, + _column_45, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape2 signalIdentityKeyStores = Shape2( + source: i0.VersionedTable( + entityName: 'signal_identity_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'PRIMARY KEY(device_id, name)', + ], + columns: [ + _column_31, + _column_32, + _column_33, + _column_10, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape3 signalPreKeyStores = Shape3( + source: i0.VersionedTable( + entityName: 'signal_pre_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'PRIMARY KEY(pre_key_id)', + ], + columns: [ + _column_34, + _column_35, + _column_10, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape4 signalSenderKeyStores = Shape4( + source: i0.VersionedTable( + entityName: 'signal_sender_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'PRIMARY KEY(sender_key_name)', + ], + columns: [ + _column_36, + _column_37, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape5 signalSessionStores = Shape5( + source: i0.VersionedTable( + entityName: 'signal_session_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'PRIMARY KEY(device_id, name)', + ], + columns: [ + _column_31, + _column_32, + _column_38, + _column_10, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape14 signalContactPreKeys = Shape14( + source: i0.VersionedTable( + entityName: 'signal_contact_pre_keys', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'PRIMARY KEY(contact_id, pre_key_id)', + ], + columns: [ + _column_58, + _column_34, + _column_35, + _column_10, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape15 signalContactSignedPreKeys = Shape15( + source: i0.VersionedTable( + entityName: 'signal_contact_signed_pre_keys', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'PRIMARY KEY(contact_id)', + ], + columns: [ + _column_58, + _column_59, + _column_60, + _column_61, + _column_10, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape21 messageRetransmissions = Shape21( + source: i0.VersionedTable( + entityName: 'message_retransmissions', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_69, + _column_71, + _column_72, + _column_67, + ], + attachedDatabase: database, + ), + alias: null); +} + +class Shape21 extends i0.VersionedTable { + Shape21({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get retransmissionId => + columnsByName['retransmission_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get contactId => + columnsByName['contact_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get messageId => + columnsByName['message_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get plaintextContent => + columnsByName['plaintext_content']! as i1.GeneratedColumn; + i1.GeneratedColumn get pushData => + columnsByName['push_data']! as i1.GeneratedColumn; + i1.GeneratedColumn get encryptedHash => + columnsByName['encrypted_hash']! as i1.GeneratedColumn; + i1.GeneratedColumn get retryCount => + columnsByName['retry_count']! as i1.GeneratedColumn; + i1.GeneratedColumn get lastRetry => + columnsByName['last_retry']! as i1.GeneratedColumn; + i1.GeneratedColumn get acknowledgeByServerAt => + columnsByName['acknowledge_by_server_at']! + as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_71(String aliasedName) => + i1.GeneratedColumn('retry_count', aliasedName, false, + type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); +i1.GeneratedColumn _column_72(String aliasedName) => + i1.GeneratedColumn('last_retry', aliasedName, true, + type: i1.DriftSqlType.dateTime); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, @@ -3729,6 +3976,7 @@ i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema14 schema) from13To14, required Future Function(i1.Migrator m, Schema15 schema) from14To15, required Future Function(i1.Migrator m, Schema16 schema) from15To16, + required Future Function(i1.Migrator m, Schema17 schema) from16To17, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -3807,6 +4055,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from15To16(migrator, schema); return 16; + case 16: + final schema = Schema17(database: database); + final migrator = i1.Migrator(database, schema); + await from16To17(migrator, schema); + return 17; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -3829,6 +4082,7 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema14 schema) from13To14, required Future Function(i1.Migrator m, Schema15 schema) from14To15, required Future Function(i1.Migrator m, Schema16 schema) from15To16, + required Future Function(i1.Migrator m, Schema17 schema) from16To17, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( @@ -3847,4 +4101,5 @@ i1.OnUpgrade stepByStep({ from13To14: from13To14, from14To15: from14To15, from15To16: from15To16, + from16To17: from16To17, )); diff --git a/lib/src/model/json/message.dart b/lib/src/model/json/message.dart index be48336..7a115fb 100644 --- a/lib/src/model/json/message.dart +++ b/lib/src/model/json/message.dart @@ -65,7 +65,9 @@ class MessageJson { messageSenderId: (json['messageSenderId'] as num?)?.toInt(), retransId: (json['retransId'] as num?)?.toInt(), content: MessageContent.fromJson( - kind, json['content'] as Map), + kind, + json['content'] as Map, + ), timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int), ); } @@ -185,13 +187,14 @@ class TextMessageContent extends MessageContent { static TextMessageContent fromJson(Map json) { return TextMessageContent( - text: json['text'] as String, - responseToOtherMessageId: json.containsKey('responseToOtherMessageId') - ? json['responseToOtherMessageId'] as int? - : null, - responseToMessageId: json.containsKey('responseToMessageId') - ? json['responseToMessageId'] as int? - : null); + text: json['text'] as String, + responseToOtherMessageId: json.containsKey('responseToOtherMessageId') + ? json['responseToOtherMessageId'] as int? + : null, + responseToMessageId: json.containsKey('responseToMessageId') + ? json['responseToMessageId'] as int? + : null, + ); } @override @@ -297,10 +300,11 @@ class PushKeyContent extends MessageContent { } class FlameSyncContent extends MessageContent { - FlameSyncContent( - {required this.flameCounter, - required this.bestFriend, - required this.lastFlameCounterChange}); + FlameSyncContent({ + required this.flameCounter, + required this.bestFriend, + required this.lastFlameCounterChange, + }); int flameCounter; DateTime lastFlameCounterChange; bool bestFriend; @@ -310,7 +314,8 @@ class FlameSyncContent extends MessageContent { flameCounter: json['flameCounter'] as int, bestFriend: json['bestFriend'] as bool, lastFlameCounterChange: DateTime.fromMillisecondsSinceEpoch( - json['lastFlameCounterChange'] as int), + json['lastFlameCounterChange'] as int, + ), ); } diff --git a/lib/src/model/json/userdata.dart b/lib/src/model/json/userdata.dart index d28cb58..987ff9f 100644 --- a/lib/src/model/json/userdata.dart +++ b/lib/src/model/json/userdata.dart @@ -29,6 +29,9 @@ class UserData { @JsonKey(defaultValue: 0) int avatarCounter = 0; + @JsonKey(defaultValue: false) + bool isDeveloper = false; + @JsonKey(defaultValue: 0) int deviceId = 0; diff --git a/lib/src/model/json/userdata.g.dart b/lib/src/model/json/userdata.g.dart index 1e4004d..a73b3ec 100644 --- a/lib/src/model/json/userdata.g.dart +++ b/lib/src/model/json/userdata.g.dart @@ -16,6 +16,7 @@ UserData _$UserDataFromJson(Map json) => UserData( ..avatarSvg = json['avatarSvg'] as String? ..avatarJson = json['avatarJson'] as String? ..avatarCounter = (json['avatarCounter'] as num?)?.toInt() ?? 0 + ..isDeveloper = json['isDeveloper'] as bool? ?? false ..deviceId = (json['deviceId'] as num?)?.toInt() ?? 0 ..lastImageSend = json['lastImageSend'] == null ? null @@ -79,6 +80,7 @@ Map _$UserDataToJson(UserData instance) => { 'avatarSvg': instance.avatarSvg, 'avatarJson': instance.avatarJson, 'avatarCounter': instance.avatarCounter, + 'isDeveloper': instance.isDeveloper, 'deviceId': instance.deviceId, 'subscriptionPlan': instance.subscriptionPlan, 'lastImageSend': instance.lastImageSend?.toIso8601String(), diff --git a/lib/src/model/memory_item.model.dart b/lib/src/model/memory_item.model.dart index b79487e..8e57d55 100644 --- a/lib/src/model/memory_item.model.dart +++ b/lib/src/model/memory_item.model.dart @@ -67,21 +67,24 @@ class MemoryItem { var mirrorVideo = false; if (videoPath != null) { final content = MediaMessageContent.fromJson( - jsonDecode(message.contentJson!) as Map); + jsonDecode(message.contentJson!) as Map, + ); mirrorVideo = content.mirrorVideo; } items .putIfAbsent( - id, - () => MemoryItem( - id: id, - messages: [], - date: message.sendAt, - mirrorVideo: mirrorVideo, - thumbnailPath: thumbnailFile, - imagePath: imagePath, - videoPath: videoPath)) + id, + () => MemoryItem( + id: id, + messages: [], + date: message.sendAt, + mirrorVideo: mirrorVideo, + thumbnailPath: thumbnailFile, + imagePath: imagePath, + videoPath: videoPath, + ), + ) .messages .add(message); } diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index fc23916..461d180 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -119,9 +119,10 @@ class ApiService { Future startReconnectionTimer() async { reconnectionTimer?.cancel(); - reconnectionTimer ??= Timer(Duration(seconds: _reconnectionDelay), () { + reconnectionTimer ??= + Timer(Duration(seconds: _reconnectionDelay), () async { reconnectionTimer = null; - connect(force: true); + await connect(force: true); }); _reconnectionDelay += 5; } @@ -143,9 +144,9 @@ class ApiService { } connectivitySubscription = Connectivity() .onConnectivityChanged - .listen((List result) { + .listen((List result) async { if (!result.contains(ConnectivityResult.none)) { - connect(force: true); + await connect(force: true); } // Received changes in available connectivity types! }); @@ -186,14 +187,14 @@ class ApiService { bool get isConnected => _channel != null && _channel!.closeCode != null; - void _onDone() { + Future _onDone() async { Log.info('websocket closed without error'); - onClosed(); + await onClosed(); } - void _onError(dynamic e) { + Future _onError(dynamic e) async { Log.error('websocket error: $e'); - onClosed(); + await onClosed(); } Future _onData(dynamic msgBuffer) async { @@ -338,12 +339,18 @@ class ApiService { } if (res.error == ErrorCode.UserIdNotFound && contactId != null) { Log.error('Contact deleted their account $contactId.'); - await twonlyDB.contactsDao.updateContact( - contactId, - const ContactsCompanion( - deleted: Value(true), - ), - ); + final contact = await twonlyDB.contactsDao + .getContactByUserId(contactId) + .getSingleOrNull(); + if (contact != null) { + await twonlyDB.contactsDao.updateContact( + contactId, + ContactsCompanion( + deleted: const Value(true), + username: Value('${contact.username} (${contact.userId})'), + ), + ); + } } } return res; @@ -441,7 +448,9 @@ class ApiService { const storage = FlutterSecureStorage(); await storage.write( - key: SecureStorageKeys.apiAuthToken, value: apiAuthTokenB64); + key: SecureStorageKeys.apiAuthToken, + value: apiAuthTokenB64, + ); await tryAuthenticateWithToken(userData.userId); } @@ -575,7 +584,10 @@ class ApiService { } Future switchToPayedPlan( - String planId, bool payMonthly, bool autoRenewal) async { + String planId, + bool payMonthly, + bool autoRenewal, + ) async { final get = ApplicationData_SwitchToPayedPlan() ..planId = planId ..payMonthly = payMonthly @@ -676,7 +688,10 @@ class ApiService { } Future sendTextMessage( - int target, Uint8List msg, List? pushData) async { + int target, + Uint8List msg, + List? pushData, + ) async { final testMessage = ApplicationData_TextMessage() ..userId = Int64(target) ..body = msg; diff --git a/lib/src/services/api/media_download.dart b/lib/src/services/api/media_download.dart index 3d89180..4148d9d 100644 --- a/lib/src/services/api/media_download.dart +++ b/lib/src/services/api/media_download.dart @@ -40,8 +40,8 @@ Map> defaultAutoDownloadOptions = { ConnectivityResult.mobile.name: [], ConnectivityResult.wifi.name: [ DownloadMediaTypes.video.name, - DownloadMediaTypes.image.name - ] + DownloadMediaTypes.image.name, + ], }; Future isAllowedToDownload(bool isVideo) async { @@ -121,7 +121,8 @@ Mutex protectDownload = Mutex(); Future startDownloadMedia(Message message, bool force) async { Log.info( - 'Download blocked for ${message.messageId} because of network state.'); + 'Download blocked for ${message.messageId} because of network state.', + ); if (message.contentJson == null) { Log.error('Content of ${message.messageId} not found.'); await handleMediaError(message); @@ -147,7 +148,8 @@ Future startDownloadMedia(Message message, bool force) async { if (!force && !await isAllowedToDownload(content.isVideo)) { Log.warn( - 'Download blocked for ${message.messageId} because of network state.'); + 'Download blocked for ${message.messageId} because of network state.', + ); return; } @@ -160,7 +162,8 @@ Future startDownloadMedia(Message message, bool force) async { if (msg.downloadState != DownloadState.pending) { Log.error( - '${message.messageId} is already downloaded or is downloading.'); + '${message.messageId} is already downloaded or is downloading.', + ); return true; } @@ -317,7 +320,8 @@ Future handleEncryptedFile(int messageId) async { await writeMediaFile(msg.messageId, 'png', imageBytes); } catch (e) { Log.error( - 'could not decrypt the media file in the second try. reporting error to user: $e'); + 'could not decrypt the media file in the second try. reporting error to user: $e', + ); await handleMediaError(msg); return; } @@ -461,7 +465,7 @@ Future purgeMediaFiles(Directory directory) async { if ((message == null) || (message.openedAt != null && !message.mediaStored && - message.acknowledgeByServer == true) || + message.acknowledgeByServer) || message.errorWhileSending) { file.deleteSync(); } diff --git a/lib/src/services/api/media_upload.dart b/lib/src/services/api/media_upload.dart index 815032c..96c35a4 100644 --- a/lib/src/services/api/media_upload.dart +++ b/lib/src/services/api/media_upload.dart @@ -75,16 +75,19 @@ Future initFileDownloader() async { } case TaskProgressUpdate(): Log.info( - 'Progress update for ${update.task} with progress ${update.progress}'); + 'Progress update for ${update.task} with progress ${update.progress}', + ); } }); await FileDownloader().start(); try { - await FileDownloader().configure(androidConfig: [ - (Config.bypassTLSCertificateValidation, kDebugMode), - ]); + var androidConfig = []; + if (kDebugMode) { + androidConfig = [(Config.bypassTLSCertificateValidation, kDebugMode)]; + } + await FileDownloader().configure(androidConfig: androidConfig); } catch (e) { Log.error(e); } @@ -201,7 +204,9 @@ Future addVideoToUpload(int mediaUploadId, File videoFilePath) async { } Future addOrModifyImageToUpload( - int mediaUploadId, Uint8List imageBytes) async { + int mediaUploadId, + Uint8List imageBytes, +) async { Uint8List imageBytesCompressed; final stopwatch = Stopwatch()..start(); @@ -236,7 +241,8 @@ Future addOrModifyImageToUpload( stopwatch.stop(); Log.info( - 'Compression the image took: ${stopwatch.elapsedMilliseconds} milliseconds'); + 'Compression the image took: ${stopwatch.elapsedMilliseconds} milliseconds', + ); Log.info('Raw images size in bytes: ${imageBytesCompressed.length}'); // stopwatch.reset(); @@ -285,7 +291,6 @@ Future encryptMediaFiles( Future? videoHandler, ) async { Log.info('$mediaUploadId encrypting files'); - // ignore: cast_nullable_to_non_nullable var dataToEncrypt = await imageHandler; /// if there is a video wait until it is finished with compression @@ -332,8 +337,14 @@ Future encryptMediaFiles( unawaited(handleNextMediaUploadSteps(mediaUploadId)); } -Future finalizeUpload(int mediaUploadId, List contactIds, - bool isRealTwonly, bool isVideo, bool mirrorVideo, int maxShowTime) async { +Future finalizeUpload( + int mediaUploadId, + List contactIds, + bool isRealTwonly, + bool isVideo, + bool mirrorVideo, + int maxShowTime, +) async { final metadata = MediaUploadMetadata() ..contactIds = contactIds ..isRealTwonly = isRealTwonly @@ -474,7 +485,8 @@ Future handleUploadStatusUpdate(TaskStatusUpdate update) async { } } Log.info( - 'Status update for ${update.task.taskId} with status ${update.status}'); + 'Status update for ${update.task.taskId} with status ${update.status}', + ); } Future handleUploadSuccess(MediaUpload media) async { @@ -565,7 +577,8 @@ Future handleMediaUpload(MediaUpload media) async { if (contact == null || contact.deleted) { Log.warn( - 'Contact deleted ${message.contactId} or not found in database.'); + 'Contact deleted ${message.contactId} or not found in database.', + ); await twonlyDB.messagesDao.updateMessageByMessageId( message.messageId, const MessagesCompanion(errorWhileSending: Value(true)), @@ -708,11 +721,13 @@ Future uploadFileFast( ); requestMultipart.headers['x-twonly-auth-token'] = apiAuthToken; - requestMultipart.files.add(http.MultipartFile.fromBytes( - 'file', - uploadRequestFile, - filename: 'upload', - )); + requestMultipart.files.add( + http.MultipartFile.fromBytes( + 'file', + uploadRequestFile, + filename: 'upload', + ), + ); final response = await requestMultipart.send(); if (response.statusCode == 200) { @@ -790,7 +805,10 @@ Future readSendMediaFile(int mediaUploadId, String type) async { } Future writeSendMediaFile( - int mediaUploadId, String type, Uint8List data) async { + int mediaUploadId, + String type, + Uint8List data, +) async { final basePath = await getMediaFilePath(mediaUploadId, 'send'); final file = File('$basePath.$type'); await file.writeAsBytes(data); @@ -838,8 +856,11 @@ List extractUint8Lists(Uint8List combinedList) { final byteData = ByteData.sublistView(combinedList); final sizeOfList1 = byteData.getInt32(0); final list1 = Uint8List.view(combinedList.buffer, 4, sizeOfList1); - final list2 = Uint8List.view(combinedList.buffer, 4 + sizeOfList1, - combinedList.lengthInBytes - 4 - sizeOfList1); + final list2 = Uint8List.view( + combinedList.buffer, + 4 + sizeOfList1, + combinedList.lengthInBytes - 4 - sizeOfList1, + ); return [list1, list2]; } @@ -853,9 +874,12 @@ String uint8ListToHex(List bytes) { return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); } -Uint8List hexToUint8List(String hex) => Uint8List.fromList(List.generate( - hex.length ~/ 2, - (i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16))); +Uint8List hexToUint8List(String hex) => Uint8List.fromList( + List.generate( + hex.length ~/ 2, + (i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16), + ), + ); Uint8List createDownloadToken() { final random = Random(); diff --git a/lib/src/services/api/messages.dart b/lib/src/services/api/messages.dart index 969827a..c939546 100644 --- a/lib/src/services/api/messages.dart +++ b/lib/src/services/api/messages.dart @@ -52,11 +52,13 @@ Future sendRetransmitMessage(int retransId) async { return; } - final json = MessageJson.fromJson(jsonDecode( - utf8.decode( - gzip.decode(retrans.plaintextContent), - ), - ) as Map); + final json = MessageJson.fromJson( + jsonDecode( + utf8.decode( + gzip.decode(retrans.plaintextContent), + ), + ) as Map, + ); Log.info('Retransmitting $retransId: ${json.kind} to ${retrans.contactId}'); @@ -65,6 +67,8 @@ Future sendRetransmitMessage(int retransId) async { .getSingleOrNull(); if (contact == null || contact.deleted) { Log.warn('Contact deleted $retransId or not found in database.'); + await twonlyDB.messageRetransmissionDao + .deleteRetransmissionById(retransId); if (retrans.messageId != null) { await twonlyDB.messagesDao.updateMessageByMessageId( retrans.messageId!, @@ -140,6 +144,8 @@ Future sendRetransmitMessage(int retransId) async { retransId, MessageRetransmissionsCompanion( acknowledgeByServerAt: Value(DateTime.now()), + retryCount: Value(retrans.retryCount + 1), + lastRetry: Value(DateTime.now()), ), ); } @@ -183,9 +189,11 @@ Future encryptAndSendMessageAsync( Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson())))); await twonlyDB.messageRetransmissionDao.updateRetransmission( - retransId, - MessageRetransmissionsCompanion( - plaintextContent: Value(plaintextContent))); + retransId, + MessageRetransmissionsCompanion( + plaintextContent: Value(plaintextContent), + ), + ); // this can now be done in the background... unawaited(sendRetransmitMessage(retransId)); diff --git a/lib/src/services/api/server_messages.dart b/lib/src/services/api/server_messages.dart index c969e5d..dbe4f7b 100644 --- a/lib/src/services/api/server_messages.dart +++ b/lib/src/services/api/server_messages.dart @@ -46,7 +46,7 @@ Future handleServerMessage(server.ServerToClient msg) async { final fromUserId = msg.v0.newMessage.fromUserId.toInt(); response = await handleNewMessage(fromUserId, body); } else { - Log.error('Got a new message from the server: $msg'); + Log.error('Got a unknown message from the server: $msg'); response = client.Response()..error = ErrorCode.InternalError; } } catch (e) { @@ -90,26 +90,10 @@ Future handleNewMessage(int fromUserId, Uint8List body) async { return client.Response()..ok = ok; } + client.Response? result; + Log.info('Got: ${message.kind} from $fromUserId'); - if (messageGetsAck(message.kind) && message.retransId != null) { - Log.info('Sending ACK for ${message.kind}'); - - /// ACK every message - await encryptAndSendMessageAsync( - null, - fromUserId, - MessageJson( - kind: MessageKind.ack, - content: AckContent( - messageIdToAck: message.messageSenderId, - retransIdToAck: message.retransId!, - ), - timestamp: DateTime.now(), - ), - ); - } - switch (message.kind) { case MessageKind.ack: final content = message.content; @@ -125,13 +109,13 @@ Future handleNewMessage(int fromUserId, Uint8List body) async { update, ); } - await twonlyDB.messageRetransmissionDao .deleteRetransmissionById(content.retransIdToAck); } case MessageKind.signalDecryptError: Log.error( - 'Got signal decrypt error from other user! Sending all non ACK messages again.'); + 'Got signal decrypt error from other user! Sending it again.', + ); final content = message.content; if (content is SignalDecryptErrorContent) { @@ -148,7 +132,7 @@ Future handleNewMessage(int fromUserId, Uint8List body) async { } case MessageKind.contactRequest: - return handleContactRequest(fromUserId, message); + await handleContactRequest(fromUserId, message); case MessageKind.flameSync: final contact = await twonlyDB.contactsDao @@ -164,7 +148,6 @@ Future handleNewMessage(int fromUserId, Uint8List body) async { isToday(content.lastFlameCounterChange)) { if (content.flameCounter > contact.flameCounter) { updates = ContactsCompanion( - alsoBestFriend: Value(content.bestFriend), flameCounter: Value(content.flameCounter), ); } @@ -282,175 +265,214 @@ Future handleNewMessage(int fromUserId, Uint8List body) async { // ignore: no_default_cases default: - if (message.kind != MessageKind.textMessage && - message.kind != MessageKind.media && - message.kind != MessageKind.storedMediaFile && - message.kind != MessageKind.reopenedMedia) { - Log.error('Got unknown MessageKind $message'); - } else if (message.messageSenderId == null) { + if (message.messageSenderId == null) { Log.error('Messageid not defined $message'); + } else if ([ + MessageKind.textMessage, + MessageKind.media, + MessageKind.storedMediaFile, + MessageKind.reopenedMedia, + ].contains(message.kind)) { + result = await handleMediaOrTextMessage(fromUserId, message); } else { - if (message.kind == MessageKind.storedMediaFile) { - if (message.messageReceiverId != null) { - /// stored media file just updates the message - await twonlyDB.messagesDao.updateMessageByOtherUser( - fromUserId, - message.messageReceiverId!, - const MessagesCompanion( - mediaStored: Value(true), - errorWhileSending: Value(false), - ), - ); - final msg = await twonlyDB.messagesDao - .getMessageByIdAndContactId( - fromUserId, message.messageReceiverId!) - .getSingleOrNull(); - if (msg != null && msg.mediaUploadId != null) { - final filePath = - await getMediaFilePath(msg.mediaUploadId, 'send'); - if (filePath.contains('mp4')) { - unawaited(createThumbnailsForVideo(File(filePath))); - } else { - unawaited(createThumbnailsForImage(File(filePath))); - } - } - } - } else if (message.content != null) { - final content = message.content!; - // when a message is received doubled ignore it... - - final openedMessage = await twonlyDB.messagesDao - .getMessageByOtherMessageId(fromUserId, message.messageSenderId!) - .getSingleOrNull(); - - if (openedMessage != null) { - if (openedMessage.errorWhileSending) { - await twonlyDB.messagesDao - .deleteMessagesByMessageId(openedMessage.messageId); - } else { - Log.error( - 'Got a duplicated message from other user: ${message.messageSenderId!}'); - final ok = client.Response_Ok()..none = true; - return client.Response()..ok = ok; - } - } - - int? responseToMessageId; - int? responseToOtherMessageId; - int? messageId; - - var acknowledgeByUser = false; - DateTime? openedAt; - - if (message.kind == MessageKind.reopenedMedia) { - acknowledgeByUser = true; - openedAt = DateTime.now(); - } - - if (content is TextMessageContent) { - responseToMessageId = content.responseToMessageId; - responseToOtherMessageId = content.responseToOtherMessageId; - - if (responseToMessageId != null || - responseToOtherMessageId != null) { - // reactions are shown in the notification directly... - if (isEmoji(content.text)) { - openedAt = DateTime.now(); - } - } - } - if (content is ReopenedMediaFileContent) { - responseToMessageId = content.messageId; - } - - if (responseToMessageId != null) { - await twonlyDB.messagesDao.updateMessageByOtherUser( - fromUserId, - responseToMessageId, - MessagesCompanion( - errorWhileSending: const Value(false), - openedAt: Value( - DateTime.now(), - ), // when a user reacted to the media file, it should be marked as opened - ), - ); - } - - final contentJson = jsonEncode(content.toJson()); - final update = MessagesCompanion( - contactId: Value(fromUserId), - kind: Value(message.kind), - messageOtherId: Value(message.messageSenderId), - contentJson: Value(contentJson), - acknowledgeByServer: const Value(true), - acknowledgeByUser: Value(acknowledgeByUser), - responseToMessageId: Value(responseToMessageId), - responseToOtherMessageId: Value(responseToOtherMessageId), - openedAt: Value(openedAt), - downloadState: Value(message.kind == MessageKind.media - ? DownloadState.pending - : DownloadState.downloaded), - sendAt: Value(message.timestamp), - ); - - messageId = await twonlyDB.messagesDao.insertMessage( - update, - ); - - if (messageId == null) { - Log.error('could not insert message into db'); - return client.Response()..error = ErrorCode.InternalError; - } - - Log.info('Inserted a new message with id: $messageId'); - - if (message.kind == MessageKind.media) { - await twonlyDB.contactsDao.incFlameCounter( - fromUserId, - true, - message.timestamp, - ); - - final msg = await twonlyDB.messagesDao - .getMessageByMessageId(messageId) - .getSingleOrNull(); - if (msg != null) { - unawaited(startDownloadMedia(msg, false)); - } - } - } else { - Log.error('Content is not defined $message'); - } - - // unarchive contact when receiving a new message - await twonlyDB.contactsDao.updateContact( - fromUserId, - const ContactsCompanion( - archived: Value(false), - ), - ); + Log.error('Got unknown MessageKind $message'); } } + + if (messageGetsAck(message.kind) && message.retransId != null) { + Log.info('Sending ACK for ${message.kind}'); + + /// ACK every message + await encryptAndSendMessageAsync( + null, + fromUserId, + MessageJson( + kind: MessageKind.ack, + content: AckContent( + messageIdToAck: message.messageSenderId, + retransIdToAck: message.retransId!, + ), + timestamp: DateTime.now(), + ), + ); + } + + if (result != null) { + return result; + } final ok = client.Response_Ok()..none = true; return client.Response()..ok = ok; } +Future handleMediaOrTextMessage( + int fromUserId, + MessageJson message, +) async { + if (message.kind == MessageKind.storedMediaFile) { + if (message.messageReceiverId != null) { + /// stored media file just updates the message + await twonlyDB.messagesDao.updateMessageByOtherUser( + fromUserId, + message.messageReceiverId!, + const MessagesCompanion( + mediaStored: Value(true), + errorWhileSending: Value(false), + ), + ); + final msg = await twonlyDB.messagesDao + .getMessageByIdAndContactId( + fromUserId, + message.messageReceiverId!, + ) + .getSingleOrNull(); + if (msg != null && msg.mediaUploadId != null) { + final filePath = await getMediaFilePath(msg.mediaUploadId, 'send'); + if (filePath.contains('mp4')) { + unawaited(createThumbnailsForVideo(File(filePath))); + } else { + unawaited(createThumbnailsForImage(File(filePath))); + } + } + } + } else if (message.content != null) { + final content = message.content!; + // when a message is received doubled ignore it... + + final openedMessage = await twonlyDB.messagesDao + .getMessageByOtherMessageId(fromUserId, message.messageSenderId!) + .getSingleOrNull(); + + if (openedMessage != null) { + if (openedMessage.errorWhileSending) { + await twonlyDB.messagesDao + .deleteMessagesByMessageId(openedMessage.messageId); + } else { + Log.error( + 'Got a duplicated message from other user: ${message.messageSenderId!}', + ); + final ok = client.Response_Ok()..none = true; + return client.Response()..ok = ok; + } + } + + int? responseToMessageId; + int? responseToOtherMessageId; + int? messageId; + + var acknowledgeByUser = false; + DateTime? openedAt; + + if (message.kind == MessageKind.reopenedMedia) { + acknowledgeByUser = true; + openedAt = DateTime.now(); + } + + if (content is TextMessageContent) { + responseToMessageId = content.responseToMessageId; + responseToOtherMessageId = content.responseToOtherMessageId; + + if (responseToMessageId != null || responseToOtherMessageId != null) { + // reactions are shown in the notification directly... + if (isEmoji(content.text)) { + openedAt = DateTime.now(); + } + } + } + if (content is ReopenedMediaFileContent) { + responseToMessageId = content.messageId; + } + + if (responseToMessageId != null) { + await twonlyDB.messagesDao.updateMessageByOtherUser( + fromUserId, + responseToMessageId, + MessagesCompanion( + errorWhileSending: const Value(false), + openedAt: Value( + DateTime.now(), + ), // when a user reacted to the media file, it should be marked as opened + ), + ); + } + + final contentJson = jsonEncode(content.toJson()); + final update = MessagesCompanion( + contactId: Value(fromUserId), + kind: Value(message.kind), + messageOtherId: Value(message.messageSenderId), + contentJson: Value(contentJson), + acknowledgeByServer: const Value(true), + acknowledgeByUser: Value(acknowledgeByUser), + responseToMessageId: Value(responseToMessageId), + responseToOtherMessageId: Value(responseToOtherMessageId), + openedAt: Value(openedAt), + downloadState: Value( + message.kind == MessageKind.media + ? DownloadState.pending + : DownloadState.downloaded, + ), + sendAt: Value(message.timestamp), + ); + + messageId = await twonlyDB.messagesDao.insertMessage( + update, + ); + + if (messageId == null) { + Log.error('could not insert message into db'); + return client.Response()..error = ErrorCode.InternalError; + } + + Log.info('Inserted a new message with id: $messageId'); + + if (message.kind == MessageKind.media) { + await twonlyDB.contactsDao.incFlameCounter( + fromUserId, + true, + message.timestamp, + ); + + final msg = await twonlyDB.messagesDao + .getMessageByMessageId(messageId) + .getSingleOrNull(); + if (msg != null) { + unawaited(startDownloadMedia(msg, false)); + } + } + } else { + Log.error('Content is not defined $message'); + } + + // unarchive contact when receiving a new message + await twonlyDB.contactsDao.updateContact( + fromUserId, + const ContactsCompanion( + archived: Value(false), + ), + ); + return null; +} + Future handleRequestNewPreKey() async { final localPreKeys = await signalGetPreKeys(); final prekeysList = []; for (var i = 0; i < localPreKeys.length; i++) { - prekeysList.add(client.Response_PreKey() - ..id = Int64(localPreKeys[i].id) - ..prekey = localPreKeys[i].getKeyPair().publicKey.serialize()); + prekeysList.add( + client.Response_PreKey() + ..id = Int64(localPreKeys[i].id) + ..prekey = localPreKeys[i].getKeyPair().publicKey.serialize(), + ); } final prekeys = client.Response_Prekeys(prekeys: prekeysList); final ok = client.Response_Ok()..prekeys = prekeys; return client.Response()..ok = ok; } -Future handleContactRequest( - int fromUserId, MessageJson message) async { +Future handleContactRequest( + int fromUserId, + MessageJson message, +) async { // request the username by the server so an attacker can not // forge the displayed username in the contact request final username = await apiService.getUsername(fromUserId); @@ -465,6 +487,4 @@ Future handleContactRequest( ); } await setupNotificationWithUsers(); - final ok = client.Response_Ok()..none = true; - return client.Response()..ok = ok; } diff --git a/lib/src/services/api/utils.dart b/lib/src/services/api/utils.dart index e67254d..3c42df9 100644 --- a/lib/src/services/api/utils.dart +++ b/lib/src/services/api/utils.dart @@ -44,7 +44,8 @@ ClientToServer createClientToServerFromHandshake(Handshake handshake) { } ClientToServer createClientToServerFromApplicationData( - ApplicationData applicationData) { + ApplicationData applicationData, +) { final v0 = client.V0() ..seq = Int64() ..applicationdata = applicationData; diff --git a/lib/src/services/fcm.service.dart b/lib/src/services/fcm.service.dart index 303fc68..899936f 100644 --- a/lib/src/services/fcm.service.dart +++ b/lib/src/services/fcm.service.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unreachable_from_main + import 'dart:io' show Platform; import 'package:firebase_core/firebase_core.dart'; diff --git a/lib/src/services/flame.service.dart b/lib/src/services/flame.service.dart index 1b75eea..01edd40 100644 --- a/lib/src/services/flame.service.dart +++ b/lib/src/services/flame.service.dart @@ -27,7 +27,8 @@ Future syncFlameCounters() async { } for (final contact in contacts) { - if (contact.lastFlameCounterChange == null) continue; + if (contact.lastFlameCounterChange == null || contact.deleted) continue; + if (!isToday(contact.lastFlameCounterChange!)) continue; if (contact.lastFlameSync != null) { if (isToday(contact.lastFlameSync!)) continue; } diff --git a/lib/src/services/notifications/background.notifications.dart b/lib/src/services/notifications/background.notifications.dart index fc6c455..294fd03 100644 --- a/lib/src/services/notifications/background.notifications.dart +++ b/lib/src/services/notifications/background.notifications.dart @@ -98,7 +98,9 @@ Future handlePushData(String pushDataB64) async { } Future tryDecryptMessage( - List key, EncryptedPushNotification push) async { + List key, + EncryptedPushNotification push, +) async { try { final chacha20 = FlutterChacha20.poly1305Aead(); final secretKeyData = SecretKeyData(key); @@ -190,7 +192,9 @@ Future showLocalPushNotificationWithoutUserId( const darwinNotificationDetails = DarwinNotificationDetails(); const notificationDetails = NotificationDetails( - android: androidNotificationDetails, iOS: darwinNotificationDetails); + android: androidNotificationDetails, + iOS: darwinNotificationDetails, + ); await flutterLocalNotificationsPlugin.show( 2, @@ -307,7 +311,9 @@ String getPushNotificationText(PushNotification pushNotification) { var contentText = pushNotificationText[pushNotification.kind.name] ?? ''; if (pushNotification.hasReactionContent()) { contentText = contentText.replaceAll( - '{{reaction}}', pushNotification.reactionContent); + '{{reaction}}', + pushNotification.reactionContent, + ); } return contentText; } diff --git a/lib/src/services/notifications/pushkeys.notifications.dart b/lib/src/services/notifications/pushkeys.notifications.dart index 258297a..5488cc1 100644 --- a/lib/src/services/notifications/pushkeys.notifications.dart +++ b/lib/src/services/notifications/pushkeys.notifications.dart @@ -18,8 +18,10 @@ import 'package:twonly/src/services/api/messages.dart'; import 'package:twonly/src/utils/log.dart'; /// This function must be called after the database is setup -Future setupNotificationWithUsers( - {bool force = false, int? forceContact}) async { +Future setupNotificationWithUsers({ + bool force = false, + int? forceContact, +}) async { var pushUsers = await getPushKeys(SecureStorageKeys.receivingPushKeys); // HotFIX: Search for user with id 0 if not there remove all @@ -29,11 +31,13 @@ Future setupNotificationWithUsers( Log.info('Clearing push keys'); await setPushKeys(SecureStorageKeys.receivingPushKeys, []); pushUsers = await getPushKeys(SecureStorageKeys.receivingPushKeys) - ..add(PushUser( - userId: Int64(), - displayName: 'NoUser', - pushKeys: [], - )); + ..add( + PushUser( + userId: Int64(), + displayName: 'NoUser', + pushKeys: [], + ), + ); } var wasChanged = false; @@ -51,7 +55,8 @@ Future setupNotificationWithUsers( DateTime.now().subtract(Duration(days: 5 + random.nextInt(5))); final lastKey = pushUser.pushKeys.last; final createdAt = DateTime.fromMillisecondsSinceEpoch( - lastKey.createdAtUnixTimestamp.toInt()); + lastKey.createdAtUnixTimestamp.toInt(), + ); if (force || (forceContact == contact.userId) || @@ -82,12 +87,14 @@ Future setupNotificationWithUsers( createdAtUnixTimestamp: Int64(DateTime.now().millisecondsSinceEpoch), ); await sendNewPushKey(contact.userId, pushKey); - pushUsers.add(PushUser( - userId: Int64(contact.userId), - displayName: getContactDisplayName(contact), - blocked: contact.blocked, - pushKeys: [pushKey], - )); + pushUsers.add( + PushUser( + userId: Int64(contact.userId), + displayName: getContactDisplayName(contact), + blocked: contact.blocked, + pushKeys: [pushKey], + ), + ); } } @@ -119,13 +126,15 @@ Future updatePushUser(Contact contact) async { final pushUser = pushKeys.firstWhereOrNull((x) => x.userId == contact.userId); if (pushUser == null) { - pushKeys.add(PushUser( - userId: Int64(contact.userId), - displayName: getContactDisplayName(contact), - pushKeys: [], - blocked: contact.blocked, - lastMessageId: Int64(), - )); + pushKeys.add( + PushUser( + userId: Int64(contact.userId), + displayName: getContactDisplayName(contact), + pushKeys: [], + blocked: contact.blocked, + lastMessageId: Int64(), + ), + ); } else { pushUser ..displayName = getContactDisplayName(contact) @@ -145,13 +154,15 @@ Future handleNewPushKey(int fromUserId, my.PushKeyContent pushKey) async { .getContactByUserId(fromUserId) .getSingleOrNull(); if (contact == null) return; - pushKeys.add(PushUser( - userId: Int64(fromUserId), - displayName: getContactDisplayName(contact), - pushKeys: [], - blocked: contact.blocked, - lastMessageId: Int64(), - )); + pushKeys.add( + PushUser( + userId: Int64(fromUserId), + displayName: getContactDisplayName(contact), + pushKeys: [], + blocked: contact.blocked, + lastMessageId: Int64(), + ), + ); pushUser = pushKeys.firstWhereOrNull((x) => x.userId == fromUserId); } diff --git a/lib/src/services/notifications/setup.notifications.dart b/lib/src/services/notifications/setup.notifications.dart index 4631320..03ff634 100644 --- a/lib/src/services/notifications/setup.notifications.dart +++ b/lib/src/services/notifications/setup.notifications.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unreachable_from_main + import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; @@ -18,7 +20,8 @@ void notificationTapBackground(NotificationResponse notificationResponse) { if (notificationResponse.input?.isNotEmpty ?? false) { // ignore: avoid_print print( - 'notification action tapped with input: ${notificationResponse.input}'); + 'notification action tapped with input: ${notificationResponse.input}', + ); } } diff --git a/lib/src/services/signal/encryption.signal.dart b/lib/src/services/signal/encryption.signal.dart index 9307db5..8087fac 100644 --- a/lib/src/services/signal/encryption.signal.dart +++ b/lib/src/services/signal/encryption.signal.dart @@ -15,7 +15,9 @@ import 'package:twonly/src/utils/misc.dart'; final lockingSignalEncryption = Mutex(); Future signalEncryptMessage( - int target, Uint8List plaintextContent) async { + int target, + Uint8List plaintextContent, +) async { return lockingSignalEncryption.protect(() async { try { final signalStore = (await getSignalStore())!; @@ -95,7 +97,9 @@ Future signalDecryptMessage(int source, Uint8List msg) async { final signalStore = (await getSignalStore())!; final session = SessionCipher.fromStore( - signalStore, SignalProtocolAddress(source.toString(), defaultDeviceId)); + signalStore, + SignalProtocolAddress(source.toString(), defaultDeviceId), + ); final msgs = removeLastXBytes(msg, 4); if (msgs == null) { @@ -115,11 +119,13 @@ Future signalDecryptMessage(int source, Uint8List msg) async { Log.error('Type not known: $type'); return null; } - return MessageJson.fromJson(jsonDecode( - utf8.decode( - gzip.decode(plaintext), - ), - ) as Map); + return MessageJson.fromJson( + jsonDecode( + utf8.decode( + gzip.decode(plaintext), + ), + ) as Map, + ); } catch (e) { Log.error(e.toString()); return null; diff --git a/lib/src/services/signal/prekeys.signal.dart b/lib/src/services/signal/prekeys.signal.dart index 4f50493..9ce59be 100644 --- a/lib/src/services/signal/prekeys.signal.dart +++ b/lib/src/services/signal/prekeys.signal.dart @@ -37,7 +37,8 @@ Future requestNewPrekeysForContact(int contactId) async { final otherKeys = await apiService.getPreKeysByUserId(contactId); if (otherKeys != null) { Log.info( - 'got fresh ${otherKeys.preKeys.length} pre keys from other $contactId!'); + 'got fresh ${otherKeys.preKeys.length} pre keys from other $contactId!', + ); final preKeys = otherKeys.preKeys .map( (preKey) => SignalContactPreKeysCompanion( @@ -74,13 +75,14 @@ Future requestNewSignedPreKeyForContact(int contactId) async { if (signedPreKey != null) { Log.info('got fresh signed pre keys from other $contactId!'); await twonlyDB.signalDao.insertOrUpdateSignedPreKeyByContactId( - SignalContactSignedPreKeysCompanion( - contactId: Value(contactId), - signedPreKey: Value(Uint8List.fromList(signedPreKey.signedPrekey)), - signedPreKeySignature: - Value(Uint8List.fromList(signedPreKey.signedPrekeySignature)), - signedPreKeyId: Value(signedPreKey.signedPrekeyId.toInt()), - )); + SignalContactSignedPreKeysCompanion( + contactId: Value(contactId), + signedPreKey: Value(Uint8List.fromList(signedPreKey.signedPrekey)), + signedPreKeySignature: + Value(Uint8List.fromList(signedPreKey.signedPrekeySignature)), + signedPreKeyId: Value(signedPreKey.signedPrekeyId.toInt()), + ), + ); } else { Log.error('could not load new signed pre key for user $contactId'); } diff --git a/lib/src/services/signal/utils.signal.dart b/lib/src/services/signal/utils.signal.dart index e883132..ef4a1cd 100644 --- a/lib/src/services/signal/utils.signal.dart +++ b/lib/src/services/signal/utils.signal.dart @@ -8,7 +8,8 @@ Future getSignalStore() async { } Future getSignalStoreFromIdentity( - SignalIdentity signalIdentity) async { + SignalIdentity signalIdentity, +) async { final identityKeyPair = IdentityKeyPair.fromSerialized(signalIdentity.identityKeyPairU8List); diff --git a/lib/src/services/twonly_safe/create_backup.twonly_safe.dart b/lib/src/services/twonly_safe/create_backup.twonly_safe.dart index e954c9b..5d18482 100644 --- a/lib/src/services/twonly_safe/create_backup.twonly_safe.dart +++ b/lib/src/services/twonly_safe/create_backup.twonly_safe.dart @@ -34,7 +34,7 @@ Future performTwonlySafeBackup({bool force = false}) async { } final lastUpdateTime = user.twonlySafeBackup!.lastBackupDone; - if (force != true && lastUpdateTime != null) { + if (!force && lastUpdateTime != null) { if (lastUpdateTime .isAfter(DateTime.now().subtract(const Duration(days: 1)))) { return; @@ -163,7 +163,8 @@ Future performTwonlySafeBackup({bool force = false}) async { await encryptedBackupBytesFile.writeAsBytes(encryptedBackupBytes); Log.info( - 'Create twonly Safe backup with a size of ${encryptedBackupBytes.length} bytes.'); + 'Create twonly Safe backup with a size of ${encryptedBackupBytes.length} bytes.', + ); if (user.backupServer != null) { if (encryptedBackupBytes.length > user.backupServer!.maxBackupBytes) { @@ -205,7 +206,8 @@ Future handleBackupStatusUpdate(TaskStatusUpdate update) async { if (update.status == TaskStatus.failed || update.status == TaskStatus.canceled) { Log.error( - 'twonly Safe upload failed. ${update.responseStatusCode} ${update.responseBody} ${update.responseHeaders} ${update.exception}'); + 'twonly Safe upload failed. ${update.responseStatusCode} ${update.responseBody} ${update.responseHeaders} ${update.exception}', + ); await updateUserdata((user) { if (user.twonlySafeBackup != null) { user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.failed; @@ -214,7 +216,8 @@ Future handleBackupStatusUpdate(TaskStatusUpdate update) async { }); } else if (update.status == TaskStatus.complete) { Log.error( - 'twonly Safe uploaded with status code ${update.responseStatusCode}'); + 'twonly Safe uploaded with status code ${update.responseStatusCode}', + ); await updateUserdata((user) { if (user.twonlySafeBackup != null) { user.twonlySafeBackup!.backupUploadState = diff --git a/lib/src/services/twonly_safe/restore.twonly_safe.dart b/lib/src/services/twonly_safe/restore.twonly_safe.dart index c925cb7..471bef2 100644 --- a/lib/src/services/twonly_safe/restore.twonly_safe.dart +++ b/lib/src/services/twonly_safe/restore.twonly_safe.dart @@ -37,9 +37,12 @@ Future recoverTwonlySafe( late http.Response response; try { - response = await http.get(Uri.parse(backupServerUrl), headers: { - HttpHeaders.acceptHeader: 'application/octet-stream', - }); + response = await http.get( + Uri.parse(backupServerUrl), + headers: { + HttpHeaders.acceptHeader: 'application/octet-stream', + }, + ); } catch (e) { Log.error('Error fetching backup: $e'); throw Exception('Backup server could not be reached. ($e)'); @@ -110,7 +113,8 @@ Future handleBackupData( // for each day add 400 message ids final dummyMessagesCounter = (lastMessageSend + 1) * 400; Log.info( - 'Creating $dummyMessagesCounter dummy messages to increase message counter as last message was $lastMessageSend days ago.'); + 'Creating $dummyMessagesCounter dummy messages to increase message counter as last message was $lastMessageSend days ago.', + ); for (var i = 0; i < dummyMessagesCounter; i++) { await database.messagesDao.insertMessage( MessagesCompanion( @@ -129,14 +133,17 @@ Future handleBackupData( final secureStorage = jsonDecode(backupContent.secureStorageJson); await storage.write( - key: SecureStorageKeys.signalIdentity, - value: secureStorage[SecureStorageKeys.signalIdentity] as String); + key: SecureStorageKeys.signalIdentity, + value: secureStorage[SecureStorageKeys.signalIdentity] as String, + ); await storage.write( - key: SecureStorageKeys.signalSignedPreKey, - value: secureStorage[SecureStorageKeys.signalSignedPreKey] as String); + key: SecureStorageKeys.signalSignedPreKey, + value: secureStorage[SecureStorageKeys.signalSignedPreKey] as String, + ); await storage.write( - key: SecureStorageKeys.userData, - value: secureStorage[SecureStorageKeys.userData] as String); + key: SecureStorageKeys.userData, + value: secureStorage[SecureStorageKeys.userData] as String, + ); await updateUserdata((u) { u.deviceId += 1; return u; diff --git a/lib/src/utils/log.dart b/lib/src/utils/log.dart index 818c1b6..8917f23 100644 --- a/lib/src/utils/log.dart +++ b/lib/src/utils/log.dart @@ -11,7 +11,8 @@ void initLogger() { await _writeLogToFile(record); if (kDebugMode) { print( - '${record.level.name} [twonly] ${record.loggerName} > ${record.message}'); + '${record.level.name} [twonly] ${record.loggerName} > ${record.message}', + ); } }); } diff --git a/lib/src/utils/misc.dart b/lib/src/utils/misc.dart index 41f2bff..41b1847 100644 --- a/lib/src/utils/misc.dart +++ b/lib/src/utils/misc.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:math'; -import 'package:drift/drift.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -10,8 +9,6 @@ import 'package:gal/gal.dart'; import 'package:intl/intl.dart'; import 'package:local_auth/local_auth.dart'; import 'package:provider/provider.dart'; -import 'package:twonly/globals.dart'; -import 'package:twonly/src/database/tables/messages_table.dart'; import 'package:twonly/src/database/twonly_database.dart'; import 'package:twonly/src/localization/generated/app_localizations.dart'; import 'package:twonly/src/model/json/message.dart'; @@ -133,18 +130,20 @@ Future getCompressedImage(Uint8List imageBytes) async { return result; } -Future authenticateUser(String localizedReason, - {bool force = true}) async { +Future authenticateUser( + String localizedReason, { + bool force = true, +}) async { try { final auth = LocalAuthentication(); final didAuthenticate = await auth.authenticate( - localizedReason: localizedReason, - options: const AuthenticationOptions(useErrorDialogs: false)); + localizedReason: localizedReason, + ); if (didAuthenticate) { return true; } - } on PlatformException catch (e) { - debugPrint(e.toString()); + } on LocalAuthException catch (e) { + Log.error(e.toString()); if (!force) { return true; } @@ -218,171 +217,6 @@ String truncateString(String input, {int maxLength = 20}) { return input; } -Future insertDemoContacts() async { - final commonUsernames = [ - 'James', - 'Mary', - 'John', - 'Patricia', - 'Robert', - 'Jennifer', - 'Michael', - 'Linda', - 'William', - 'Elizabeth', - 'David', - 'Barbara', - 'Richard', - 'Susan', - 'Joseph', - 'Jessica', - 'Charles', - 'Sarah', - 'Thomas', - 'Karen', - ]; - final contactConfigs = >[ - {'count': 3, 'requested': true}, - {'count': 4, 'requested': false, 'accepted': true}, - {'count': 1, 'accepted': true, 'blocked': true}, - {'count': 1, 'accepted': true, 'archived': true}, - {'count': 2, 'accepted': true, 'pinned': true}, - {'count': 1, 'requested': false}, - ]; - - var counter = 0; - - for (final config in contactConfigs) { - for (var i = 0; i < (config['count'] as int); i++) { - if (counter >= commonUsernames.length) { - break; - } - final username = commonUsernames[counter]; - final userId = Random().nextInt(1000000); - await twonlyDB.contactsDao.insertContact( - ContactsCompanion( - username: Value(username), - userId: Value(userId), - requested: Value(config['requested'] as bool? ?? false), - accepted: Value(config['accepted'] as bool? ?? false), - blocked: Value(config['blocked'] as bool? ?? false), - archived: Value(config['archived'] as bool? ?? false), - pinned: Value(config['pinned'] as bool? ?? false), - ), - ); - if (config['accepted'] as bool? ?? false) { - for (var i = 0; i < 20; i++) { - final chatId = Random().nextInt(chatMessages.length); - await twonlyDB.messagesDao.insertMessage( - MessagesCompanion( - contactId: Value(userId), - kind: const Value(MessageKind.textMessage), - sendAt: Value(chatMessages[chatId][1] as DateTime), - acknowledgeByServer: const Value(true), - acknowledgeByUser: const Value(true), - messageOtherId: - Value(Random().nextBool() ? Random().nextInt(10000) : null), - // responseToOtherMessageId: Value(content.responseToMessageId), - // responseToMessageId: Value(content.responseToOtherMessageId), - downloadState: const Value(DownloadState.downloaded), - contentJson: Value( - jsonEncode(TextMessageContent( - text: chatMessages[chatId][0] as String)), - ), - ), - ); - } - } - counter++; - } - } -} - -Future createFakeDemoData() async { - await insertDemoContacts(); -} - -List> chatMessages = [ - [ - 'Lorem ipsum dolor sit amet.', - DateTime.now().subtract(const Duration(minutes: 20)) - ], - [ - 'Consectetur adipiscing elit.', - DateTime.now().subtract(const Duration(minutes: 19)) - ], - [ - 'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', - DateTime.now().subtract(const Duration(minutes: 18)) - ], - [ - 'Ut enim ad minim veniam.', - DateTime.now().subtract(const Duration(minutes: 17)) - ], - [ - 'Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.', - DateTime.now().subtract(const Duration(minutes: 16)) - ], - [ - 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.', - DateTime.now().subtract(const Duration(minutes: 15)) - ], - [ - 'Excepteur sint occaecat cupidatat non proident.', - DateTime.now().subtract(const Duration(minutes: 14)) - ], - [ - 'Sunt in culpa qui officia deserunt mollit anim id est laborum.', - DateTime.now().subtract(const Duration(minutes: 13)) - ], - [ - 'Curabitur pretium tincidunt lacus.', - DateTime.now().subtract(const Duration(minutes: 12)) - ], - ['Nulla facilisi.', DateTime.now().subtract(const Duration(minutes: 11))], - [ - 'Aenean lacinia bibendum nulla sed consectetur.', - DateTime.now().subtract(const Duration(minutes: 10)) - ], - [ - 'Sed posuere consectetur est at lobortis.', - DateTime.now().subtract(const Duration(minutes: 9)) - ], - [ - 'Vestibulum id ligula porta felis euismod semper.', - DateTime.now().subtract(const Duration(minutes: 8)) - ], - [ - 'Cras justo odio, dapibus ac facilisis in, egestas eget quam.', - DateTime.now().subtract(const Duration(minutes: 7)) - ], - [ - 'Morbi leo risus, porta ac consectetur ac, vestibulum at eros.', - DateTime.now().subtract(const Duration(minutes: 6)) - ], - [ - 'Praesent commodo cursus magna, vel scelerisque nisl consectetur et.', - DateTime.now().subtract(const Duration(minutes: 5)) - ], - [ - 'Donec ullamcorper nulla non metus auctor fringilla.', - DateTime.now().subtract(const Duration(minutes: 4)) - ], - [ - 'Etiam porta sem malesuada magna mollis euismod.', - DateTime.now().subtract(const Duration(minutes: 3)) - ], - [ - 'Aenean lacinia bibendum nulla sed consectetur.', - DateTime.now().subtract(const Duration(minutes: 2)) - ], - [ - 'Nullam quis risus eget urna mollis ornare vel eu leo.', - DateTime.now().subtract(const Duration(minutes: 1)) - ], - ['Curabitur blandit tempus porttitor.', DateTime.now()], -]; - String formatDateTime(BuildContext context, DateTime? dateTime) { if (dateTime == null) { return 'Never'; @@ -426,7 +260,8 @@ MediaMessageContent? getMediaContent(Message message) { try { if (message.contentJson == null) return null; return MediaMessageContent.fromJson( - jsonDecode(message.contentJson!) as Map); + jsonDecode(message.contentJson!) as Map, + ); } catch (e) { Log.error(e); return null; diff --git a/lib/src/views/camera/camera_preview_components/permissions_view.dart b/lib/src/views/camera/camera_preview_components/permissions_view.dart index fead6f4..411fb54 100644 --- a/lib/src/views/camera/camera_preview_components/permissions_view.dart +++ b/lib/src/views/camera/camera_preview_components/permissions_view.dart @@ -30,7 +30,7 @@ class PermissionHandlerViewState extends State { final statuses = await [ Permission.camera, // Permission.microphone, - Permission.notification + Permission.notification, ].request(); // } catch (e) {} // You can request multiple permissions at once. diff --git a/lib/src/views/camera/camera_preview_components/save_to_gallery.dart b/lib/src/views/camera/camera_preview_components/save_to_gallery.dart index 2f67d3f..b522422 100644 --- a/lib/src/views/camera/camera_preview_components/save_to_gallery.dart +++ b/lib/src/views/camera/camera_preview_components/save_to_gallery.dart @@ -61,7 +61,8 @@ class SaveToGalleryButtonState extends State { } else { final random = Random(); final token = uint8ListToHex( - List.generate(32, (i) => random.nextInt(256))); + List.generate(32, (i) => random.nextInt(256)), + ); memoryPath = join(memoryPath, token); } final user = await getUser(); @@ -111,18 +112,21 @@ class SaveToGalleryButtonState extends State { children: [ if (_imageSaving || widget.isLoading) const SizedBox( - width: 12, - height: 12, - child: CircularProgressIndicator(strokeWidth: 1)) + width: 12, + height: 12, + child: CircularProgressIndicator(strokeWidth: 1), + ) else _imageSaved ? const Icon(Icons.check) : const FaIcon(FontAwesomeIcons.floppyDisk), if (widget.displayButtonLabel) const SizedBox(width: 10), if (widget.displayButtonLabel) - Text(_imageSaved - ? context.lang.shareImagedEditorSavedImage - : context.lang.shareImagedEditorSaveImage) + Text( + _imageSaved + ? context.lang.shareImagedEditorSavedImage + : context.lang.shareImagedEditorSaveImage, + ), ], ), ); diff --git a/lib/src/views/camera/camera_preview_components/video_recording_time.dart b/lib/src/views/camera/camera_preview_components/video_recording_time.dart index a2478cb..bcbc11c 100644 --- a/lib/src/views/camera/camera_preview_components/video_recording_time.dart +++ b/lib/src/views/camera/camera_preview_components/video_recording_time.dart @@ -47,11 +47,11 @@ class VideoRecordingTimer extends StatelessWidget { Shadow( color: Color.fromARGB(122, 0, 0, 0), blurRadius: 5, - ) + ), ], ), ), - ) + ), ], ), ), diff --git a/lib/src/views/camera/camera_preview_components/zoom_selector.dart b/lib/src/views/camera/camera_preview_components/zoom_selector.dart index a6d4400..499a335 100644 --- a/lib/src/views/camera/camera_preview_components/zoom_selector.dart +++ b/lib/src/views/camera/camera_preview_components/zoom_selector.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_dynamic_calls +import 'dart:async'; import 'dart:io'; import 'dart:math'; @@ -45,7 +46,7 @@ class _CameraZoomButtonsState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -121,25 +122,26 @@ class _CameraZoomButtonsState extends State { ), ), TextButton( - style: zoomButtonStyle.copyWith( - foregroundColor: WidgetStateProperty.all( - isMiddleFocused ? Colors.yellow : Colors.white, - ), + style: zoomButtonStyle.copyWith( + foregroundColor: WidgetStateProperty.all( + isMiddleFocused ? Colors.yellow : Colors.white, ), - onPressed: () async { - if (showWideAngleZoomIOS && - widget.selectedCameraDetails.cameraId == 2) { - await widget.selectCamera(0, true, false); - } else { - widget.updateScaleFactor(1.0); - } - }, - child: Text( - isMiddleFocused - ? '${beautifulZoomScale(widget.scaleFactor)}x' - : '1.0x', - style: zoomTextStyle, - )), + ), + onPressed: () async { + if (showWideAngleZoomIOS && + widget.selectedCameraDetails.cameraId == 2) { + await widget.selectCamera(0, true, false); + } else { + widget.updateScaleFactor(1.0); + } + }, + child: Text( + isMiddleFocused + ? '${beautifulZoomScale(widget.scaleFactor)}x' + : '1.0x', + style: zoomTextStyle, + ), + ), TextButton( style: zoomButtonStyle.copyWith( foregroundColor: WidgetStateProperty.all( @@ -152,9 +154,11 @@ class _CameraZoomButtonsState extends State { .toDouble(); widget.updateScaleFactor(level); }, - child: Text('${beautifulZoomScale(maxLevel.toDouble())}x', - style: zoomTextStyle), - ) + child: Text( + '${beautifulZoomScale(maxLevel.toDouble())}x', + style: zoomTextStyle, + ), + ), ], ), ), diff --git a/lib/src/views/camera/camera_preview_controller_view.dart b/lib/src/views/camera/camera_preview_controller_view.dart index 43eeb01..40ded4d 100644 --- a/lib/src/views/camera/camera_preview_controller_view.dart +++ b/lib/src/views/camera/camera_preview_controller_view.dart @@ -94,7 +94,10 @@ class CameraPreviewControllerView extends StatelessWidget { }); final Contact? sendTo; final Future Function( - int sCameraId, bool init, bool enableAudio) selectCamera; + int sCameraId, + bool init, + bool enableAudio, + ) selectCamera; final CameraController? cameraController; final SelectedCameraDetails selectedCameraDetails; final ScreenshotController screenshotController; @@ -114,10 +117,12 @@ class CameraPreviewControllerView extends StatelessWidget { screenshotController: screenshotController, ); } else { - return PermissionHandlerView(onSuccess: () { - // setState(() {}); - selectCamera(0, true, false); - }); + return PermissionHandlerView( + onSuccess: () { + // setState(() {}); + selectCamera(0, true, false); + }, + ); } } else { return Container(); @@ -138,7 +143,10 @@ class CameraPreviewView extends StatefulWidget { }); final Contact? sendTo; final Future Function( - int sCameraId, bool init, bool enableAudio) selectCamera; + int sCameraId, + bool init, + bool enableAudio, + ) selectCamera; final CameraController? cameraController; final SelectedCameraDetails selectedCameraDetails; final ScreenshotController screenshotController; @@ -212,9 +220,12 @@ class _CameraPreviewViewState extends State { widget.cameraController == null) { return; } - await widget.cameraController?.setZoomLevel(newScale.clamp( + await widget.cameraController?.setZoomLevel( + newScale.clamp( widget.selectedCameraDetails.minAvailableZoom, - widget.selectedCameraDetails.maxAvailableZoom)); + widget.selectedCameraDetails.maxAvailableZoom, + ), + ); setState(() { widget.selectedCameraDetails.scaleFactor = newScale; }); @@ -284,8 +295,10 @@ class _CameraPreviewViewState extends State { } Future pushMediaEditor( - Future? imageBytes, File? videoFilePath, - {bool sharedFromGallery = false}) async { + Future? imageBytes, + File? videoFilePath, { + bool sharedFromGallery = false, + }) async { final shouldReturn = await Navigator.push( context, PageRouteBuilder( @@ -314,7 +327,6 @@ class _CameraPreviewViewState extends State { if (!mounted) return true; // shouldReturn is null when the user used the back button if (shouldReturn != null && shouldReturn) { - // ignore: use_build_context_synchronously if (widget.sendTo == null) { globalUpdateOfHomeViewPageIndex(0); } else { @@ -323,7 +335,10 @@ class _CameraPreviewViewState extends State { return true; } await widget.selectCamera( - widget.selectedCameraDetails.cameraId, false, false); + widget.selectedCameraDetails.cameraId, + false, + false, + ); return false; } @@ -466,7 +481,6 @@ class _CameraPreviewViewState extends State { Log.error('$e'); try { if (context.mounted) { - // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Error: $e'), @@ -553,9 +567,10 @@ class _CameraPreviewViewState extends State { tooltipText: context.lang.switchFrontAndBackCamera, onPressed: () async { await widget.selectCamera( - (widget.selectedCameraDetails.cameraId + 1) % 2, - false, - false); + (widget.selectedCameraDetails.cameraId + 1) % 2, + false, + false, + ); }, ), ActionButton( @@ -676,7 +691,7 @@ class _CameraPreviewViewState extends State { ), ), ), - if (!isVideoRecording) const SizedBox(width: 80) + if (!isVideoRecording) const SizedBox(width: 80), ], ), ], diff --git a/lib/src/views/camera/camera_send_to_view.dart b/lib/src/views/camera/camera_send_to_view.dart index 37660e6..b29c109 100644 --- a/lib/src/views/camera/camera_send_to_view.dart +++ b/lib/src/views/camera/camera_send_to_view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:screenshot/screenshot.dart'; @@ -20,7 +22,7 @@ class CameraSendToViewState extends State { @override void initState() { super.initState(); - selectCamera(0, true, false); + unawaited(selectCamera(0, true, false)); } @override @@ -32,9 +34,16 @@ class CameraSendToViewState extends State { } Future selectCamera( - int sCameraId, bool init, bool enableAudio) async { + int sCameraId, + bool init, + bool enableAudio, + ) async { final opts = await initializeCameraController( - selectedCameraDetails, sCameraId, init, enableAudio); + selectedCameraDetails, + sCameraId, + init, + enableAudio, + ); if (opts != null) { selectedCameraDetails = opts.$1; cameraController = opts.$2; @@ -47,7 +56,7 @@ class CameraSendToViewState extends State { Future toggleSelectedCamera() async { if (cameraController == null) return; // do not allow switching camera when recording - if (cameraController!.value.isRecordingVideo == true) { + if (cameraController!.value.isRecordingVideo) { return; } await cameraController!.dispose(); diff --git a/lib/src/views/camera/image_editor/action_button.dart b/lib/src/views/camera/image_editor/action_button.dart index b5d84e9..d5307e6 100644 --- a/lib/src/views/camera/image_editor/action_button.dart +++ b/lib/src/views/camera/image_editor/action_button.dart @@ -31,7 +31,7 @@ class ActionButton extends StatelessWidget { Shadow( color: Color.fromARGB(122, 0, 0, 0), blurRadius: 5, - ) + ), ], ), onPressed: () { diff --git a/lib/src/views/camera/image_editor/data/image_item.dart b/lib/src/views/camera/image_editor/data/image_item.dart index 5b3ddb4..6468ca7 100755 --- a/lib/src/views/camera/image_editor/data/image_item.dart +++ b/lib/src/views/camera/image_editor/data/image_item.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; class ImageItem { ImageItem([dynamic image]) { - if (image != null) load(image); + if (image != null) unawaited(load(image)); } int width = 1; int height = 1; diff --git a/lib/src/views/camera/image_editor/data/layer.dart b/lib/src/views/camera/image_editor/data/layer.dart index c2a8e42..707e348 100755 --- a/lib/src/views/camera/image_editor/data/layer.dart +++ b/lib/src/views/camera/image_editor/data/layer.dart @@ -77,5 +77,12 @@ class DrawLayerData extends Layer { super.hasCustomActionButtons = true, super.isEditing = true, }); - final control = HandSignatureControl(); + final control = HandSignatureControl( + // ignore: prefer_const_constructors + setup: () => SignaturePathSetup( + args: { + 'color': null, + }, + ), + ); } diff --git a/lib/src/views/camera/image_editor/layers/draw_layer.dart b/lib/src/views/camera/image_editor/layers/draw_layer.dart index 22facf4..a04f753 100644 --- a/lib/src/views/camera/image_editor/layers/draw_layer.dart +++ b/lib/src/views/camera/image_editor/layers/draw_layer.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:hand_signature/signature.dart'; +// ignore: implementation_imports +import 'package:hand_signature/src/utils.dart'; import 'package:screenshot/screenshot.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/camera/image_editor/action_button.dart'; @@ -91,7 +93,7 @@ class _DrawLayerState extends State { controller: screenshotController, child: HandSignature( control: widget.layerData.control, - drawer: LineSignatureDrawer(color: currentColor, width: 7), + drawer: CustomSignatureDrawer(color: currentColor, width: 7), ), ), ), @@ -167,8 +169,10 @@ class _DrawLayerState extends State { begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: colors, - stops: List.generate(colors.length, - (index) => index / (colors.length - 1)), + stops: List.generate( + colors.length, + (index) => index / (colors.length - 1), + ), ), borderRadius: BorderRadius.circular(10), ), @@ -187,12 +191,12 @@ class _DrawLayerState extends State { onChangeStart: (value) => { setState(() { showMagnifyingGlass = true; - }) + }), }, onChangeEnd: (value) => { setState(() { showMagnifyingGlass = false; - }) + }), }, divisions: 100, ), @@ -209,9 +213,10 @@ class _DrawLayerState extends State { ), if (!widget.layerData.isEditing) Positioned.fill( - child: Container( - color: Colors.transparent, - )) + child: Container( + color: Colors.transparent, + ), + ), ], ); } @@ -239,3 +244,33 @@ class MagnifyingGlass extends StatelessWidget { ); } } + +class CustomSignatureDrawer extends HandSignatureDrawer { + const CustomSignatureDrawer({ + this.width = 1.0, + this.color = Colors.black, + }); + final Color color; + final double width; + + @override + void paint(Canvas canvas, Size size, List paths) { + for (final path in paths) { + var lineColor = color; + if (path.setup.args!['color'] != null) { + lineColor = path.setup.args!['color'] as Color; + } else { + path.setup.args!['color'] = color; + } + final paint = Paint() + ..color = lineColor + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round + ..strokeWidth = width; + if (path.isFilled) { + canvas.drawPath(PathUtil.toLinePath(path.lines), paint); + } + } + } +} diff --git a/lib/src/views/camera/image_editor/layers/emoji_layer.dart b/lib/src/views/camera/image_editor/layers/emoji_layer.dart index 30c795b..0e5b7d7 100755 --- a/lib/src/views/camera/image_editor/layers/emoji_layer.dart +++ b/lib/src/views/camera/image_editor/layers/emoji_layer.dart @@ -39,8 +39,9 @@ class _EmojiLayerState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { widget.layerData.offset = Offset( - MediaQuery.of(context).size.width / 2 - (153 / 2), - MediaQuery.of(context).size.height / 2 - (153 / 2) - 100); + MediaQuery.of(context).size.width / 2 - (153 / 2), + MediaQuery.of(context).size.height / 2 - (153 / 2) - 100, + ); }); display = true; }); @@ -87,8 +88,8 @@ class _EmojiLayerState extends State { setState(() {}); }, - onScaleUpdate: (details) { - if (twoPointerWhereDown == true && details.pointerCount != 2) { + onScaleUpdate: (details) async { + if (twoPointerWhereDown && details.pointerCount != 2) { return; } final outlineBox = @@ -109,7 +110,7 @@ class _EmojiLayerState extends State { if (isAtTheBottom && isInTheCenter) { if (!deleteLayer) { - HapticFeedback.heavyImpact(); + await HapticFeedback.heavyImpact(); } deleteLayer = true; } else { diff --git a/lib/src/views/camera/image_editor/layers/filter_layer.dart b/lib/src/views/camera/image_editor/layers/filter_layer.dart index b5318f6..c0179d0 100644 --- a/lib/src/views/camera/image_editor/layers/filter_layer.dart +++ b/lib/src/views/camera/image_editor/layers/filter_layer.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:twonly/src/views/camera/image_editor/data/layer.dart'; @@ -61,7 +63,7 @@ class FilterText extends StatelessWidget { Shadow( color: Color.fromARGB(122, 0, 0, 0), blurRadius: 5, - ) + ), ], ), ); @@ -83,7 +85,7 @@ class _FilterLayerState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { pageController.jumpToPage(1); }); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { diff --git a/lib/src/views/camera/image_editor/layers/filters/location_filter.dart b/lib/src/views/camera/image_editor/layers/filters/location_filter.dart index c51b7ea..5b3ae64 100644 --- a/lib/src/views/camera/image_editor/layers/filters/location_filter.dart +++ b/lib/src/views/camera/image_editor/layers/filters/location_filter.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -26,7 +27,7 @@ class _LocationFilterState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { diff --git a/lib/src/views/camera/image_editor/layers/text_layer.dart b/lib/src/views/camera/image_editor/layers/text_layer.dart index a1ce9a9..5cff596 100755 --- a/lib/src/views/camera/image_editor/layers/text_layer.dart +++ b/lib/src/views/camera/image_editor/layers/text_layer.dart @@ -40,10 +40,11 @@ class _TextViewState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { widget.layerData.offset = Offset( - 0, - MediaQuery.of(context).size.height / 2 - - 150 + - (widget.layerData.textLayersBefore * 40)); + 0, + MediaQuery.of(context).size.height / 2 - + 150 + + (widget.layerData.textLayersBefore * 40), + ); textController.text = widget.layerData.text; }); }); @@ -68,38 +69,37 @@ class _TextViewState extends State { autofocus: true, maxLines: null, minLines: 1, - onEditingComplete: () { + onEditingComplete: () async { setState(() { widget.layerData.isDeleted = textController.text == ''; widget.layerData.isEditing = false; widget.layerData.text = textController.text; }); - context + await context .read() .updateSomeTextViewIsAlreadyEditing(false); if (widget.onUpdate != null) { widget.onUpdate!(); } }, - onTapOutside: (a) { + onTapOutside: (a) async { widget.layerData.text = textController.text; - Future.delayed(const Duration(milliseconds: 100), () { + Future.delayed(const Duration(milliseconds: 100), () async { if (context.mounted) { - setState(() { - widget.layerData.isDeleted = textController.text == ''; - widget.layerData.isEditing = false; - context - .read() - .updateSomeTextViewIsAlreadyEditing(false); - if (widget.onUpdate != null) { - widget.onUpdate!(); - } - }); + widget.layerData.isDeleted = textController.text == ''; + widget.layerData.isEditing = false; + await context + .read() + .updateSomeTextViewIsAlreadyEditing(false); + if (widget.onUpdate != null) { + widget.onUpdate!(); + } + if (mounted) setState(() {}); } }); - context + await context .read() .updateSomeTextViewIsAlreadyEditing(false); }, @@ -149,25 +149,26 @@ class _TextViewState extends State { .watch() .someTextViewIsAlreadyEditing) ? null - : () { - setState(() { - context - .read() - .updateSomeTextViewIsAlreadyEditing(true); - widget.layerData.isEditing = true; - }); + : () async { + await context + .read() + .updateSomeTextViewIsAlreadyEditing(true); + widget.layerData.isEditing = true; + if (mounted) setState(() {}); }, - onScaleUpdate: (detail) { + onScaleUpdate: (detail) async { if (detail.pointerCount == 1) { widget.layerData.offset = Offset( - 0, widget.layerData.offset.dy + detail.focalPointDelta.dy); + 0, + widget.layerData.offset.dy + detail.focalPointDelta.dy, + ); } final renderBox = _widgetKey.currentContext!.findRenderObject()! as RenderBox; if (widget.layerData.offset.dy > renderBox.size.height - 80) { if (!deleteLayer) { - HapticFeedback.heavyImpact(); + await HapticFeedback.heavyImpact(); } deleteLayer = true; } else { diff --git a/lib/src/views/camera/image_editor/layers_viewer.dart b/lib/src/views/camera/image_editor/layers_viewer.dart index f6155f3..0fcf148 100644 --- a/lib/src/views/camera/image_editor/layers_viewer.dart +++ b/lib/src/views/camera/image_editor/layers_viewer.dart @@ -33,8 +33,10 @@ class LayersViewer extends StatelessWidget { ); }), ...layers - .where((layerItem) => - layerItem is EmojiLayerData || layerItem is DrawLayerData) + .where( + (layerItem) => + layerItem is EmojiLayerData || layerItem is DrawLayerData, + ) .map((layerItem) { if (layerItem is EmojiLayerData) { return EmojiLayer( diff --git a/lib/src/views/camera/image_editor/modules/all_emojis.dart b/lib/src/views/camera/image_editor/modules/all_emojis.dart index c995b4e..18775ea 100755 --- a/lib/src/views/camera/image_editor/modules/all_emojis.dart +++ b/lib/src/views/camera/image_editor/modules/all_emojis.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/camera/image_editor/data/data.dart'; @@ -16,7 +18,7 @@ class _EmojisState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -86,22 +88,23 @@ class _EmojisState extends State { ), children: lastUsed.map((String emoji) { return GridTile( - child: GestureDetector( - onTap: () { - selectEmojis(emoji); - }, - child: Container( - padding: EdgeInsets.zero, - alignment: Alignment.center, - child: Text( - emoji, - style: const TextStyle(fontSize: 35), + child: GestureDetector( + onTap: () async { + await selectEmojis(emoji); + }, + child: Container( + padding: EdgeInsets.zero, + alignment: Alignment.center, + child: Text( + emoji, + style: const TextStyle(fontSize: 35), + ), ), ), - )); + ); }).toList(), ), - ) + ), ], ), ), diff --git a/lib/src/views/camera/share_image_components/best_friends_selector.dart b/lib/src/views/camera/share_image_components/best_friends_selector.dart index 1562716..86ba2c9 100644 --- a/lib/src/views/camera/share_image_components/best_friends_selector.dart +++ b/lib/src/views/camera/share_image_components/best_friends_selector.dart @@ -59,8 +59,10 @@ class BestFriendsSelector extends StatelessWidget { ], borderRadius: BorderRadius.circular(8), ), - child: Text(context.lang.shareImagedSelectAll, - style: const TextStyle(fontSize: 10)), + child: Text( + context.lang.shareImagedSelectAll, + style: const TextStyle(fontSize: 10), + ), ), ), ], @@ -127,7 +129,8 @@ class UserCheckbox extends StatelessWidget { return Container( padding: const EdgeInsets.symmetric( - horizontal: 3), // Padding inside the container + horizontal: 3, + ), // Padding inside the container child: GestureDetector( onTap: () { onChanged(user.userId, !isChecked); @@ -172,7 +175,7 @@ class UserCheckbox extends StatelessWidget { } return FlameCounterWidget(user, snapshot.data!); }, - ) + ), ], ), Expanded(child: Container()), @@ -184,7 +187,8 @@ class UserCheckbox extends StatelessWidget { return const BorderSide(width: 0); } return BorderSide( - color: Theme.of(context).colorScheme.outline); + color: Theme.of(context).colorScheme.outline, + ); }, ), onChanged: (bool? value) { diff --git a/lib/src/views/camera/share_image_editor_view.dart b/lib/src/views/camera/share_image_editor_view.dart index 3e5565b..6dc0239 100644 --- a/lib/src/views/camera/share_image_editor_view.dart +++ b/lib/src/views/camera/share_image_editor_view.dart @@ -74,14 +74,14 @@ class _ShareImageEditorView extends State { @override void initState() { super.initState(); - initAsync(); - initMediaFileUpload(); + unawaited(initAsync()); + unawaited(initMediaFileUpload()); layers.add(FilterLayerData()); if (widget.sendTo != null) { selectedUserIds.add(widget.sendTo!.userId); } if (widget.imageBytes != null) { - loadImage(widget.imageBytes!); + unawaited(loadImage(widget.imageBytes!)); } else if (widget.videoFilePath != null) { setState(() { sendingOrLoadingImage = false; @@ -89,8 +89,8 @@ class _ShareImageEditorView extends State { }); videoController = VideoPlayerController.file(widget.videoFilePath!); videoController?.setLooping(true); - videoController?.initialize().then((_) { - videoController!.play(); + videoController?.initialize().then((_) async { + await videoController!.play(); setState(() {}); // ignore: invalid_return_type_for_catch_error, argument_type_not_assignable_to_error_handler }).catchError(Log.error); @@ -155,9 +155,11 @@ class _ShareImageEditorView extends State { if (layers.any((x) => x.isEditing)) return; undoLayers.clear(); removedLayers.clear(); - layers.add(TextLayerData( - textLayersBefore: layers.whereType().length, - )); + layers.add( + TextLayerData( + textLayersBefore: layers.whereType().length, + ), + ); setState(() {}); }, ), @@ -301,7 +303,7 @@ class _ShareImageEditorView extends State { setState(() {}); }, ), - const SizedBox(width: 70) + const SizedBox(width: 70), ]; } @@ -329,7 +331,6 @@ class _ShareImageEditorView extends State { ), ) as bool?; if (wasSend != null && wasSend && mounted) { - // ignore: use_build_context_synchronously Navigator.pop(context, true); } else { await videoController?.play(); @@ -345,7 +346,8 @@ class _ShareImageEditorView extends State { } setState(() {}); image = await screenshotController.capture( - pixelRatio: (widget.useHighQuality) ? pixelRatio : 1); + pixelRatio: (widget.useHighQuality) ? pixelRatio : 1, + ); for (final x in layers) { x.showCustomButtons = true; } @@ -397,11 +399,16 @@ class _ShareImageEditorView extends State { sendingOrLoadingImage = false; }); if (mounted) { - await Navigator.push(context, MaterialPageRoute(builder: (context) { - return SubscriptionView( - redirectError: err, - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return SubscriptionView( + redirectError: err, + ); + }, + ), + ); } } else { final imageHandler = addOrModifyImageToUpload(mediaUploadId!, imageBytes); @@ -456,10 +463,12 @@ class _ShareImageEditorView extends State { layers = layers.where((x) => !x.isDeleted).toList(); undoLayers.clear(); removedLayers.clear(); - layers.add(TextLayerData( - offset: Offset(0, tabDownPosition), - textLayersBefore: layers.whereType().length, - )); + layers.add( + TextLayerData( + offset: Offset(0, tabDownPosition), + textLayersBefore: layers.whereType().length, + ), + ); setState(() {}); }, child: MediaViewSizing( @@ -508,7 +517,9 @@ class _ShareImageEditorView extends State { style: ButtonStyle( padding: WidgetStateProperty.all( const EdgeInsets.symmetric( - vertical: 10, horizontal: 30), + vertical: 10, + horizontal: 30, + ), ), ), label: Text( diff --git a/lib/src/views/camera/share_image_view.dart b/lib/src/views/camera/share_image_view.dart index 9d9fd65..f957ba6 100644 --- a/lib/src/views/camera/share_image_view.dart +++ b/lib/src/views/camera/share_image_view.dart @@ -63,14 +63,14 @@ class _ShareImageView extends State { final allContacts = twonlyDB.contactsDao.watchContactsForShareView(); - contactSub = allContacts.listen((allContacts) { + contactSub = allContacts.listen((allContacts) async { setState(() { contacts = allContacts; }); - updateUsers(allContacts.where((x) => !x.archived).toList()); + await updateUsers(allContacts.where((x) => !x.archived).toList()); }); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -80,7 +80,10 @@ class _ShareImageView extends State { addOrModifyImageToUpload(widget.mediaUploadId, imageBytes!); // start with the pre upload of the media file... await encryptMediaFiles( - widget.mediaUploadId, imageHandler, widget.videoUploadHandler); + widget.mediaUploadId, + imageHandler, + widget.videoUploadHandler, + ); } if (!mounted) return; setState(() {}); @@ -88,8 +91,8 @@ class _ShareImageView extends State { @override void dispose() { + unawaited(contactSub.cancel()); super.dispose(); - contactSub.cancel(); } Future updateUsers(List users) async { @@ -103,7 +106,8 @@ class _ShareImageView extends State { } // If flameCounter is the same, compare by totalMediaCounter return b.totalMediaCounter.compareTo( - a.totalMediaCounter); // Sort by totalMediaCounter in descending order + a.totalMediaCounter, + ); // Sort by totalMediaCounter in descending order }); // Separate best friends and other users @@ -132,18 +136,24 @@ class _ShareImageView extends State { Future _filterUsers(String query) async { lastQuery = query; if (query.isEmpty) { - await updateUsers(contacts - .where((x) => - !x.archived || - !hideArchivedUsers || - widget.selectedUserIds.contains(x.userId)) - .toList()); + await updateUsers( + contacts + .where( + (x) => + !x.archived || + !hideArchivedUsers || + widget.selectedUserIds.contains(x.userId), + ) + .toList(), + ); return; } final usersFiltered = contacts - .where((user) => getContactDisplayName(user) - .toLowerCase() - .contains(query.toLowerCase())) + .where( + (user) => getContactDisplayName(user) + .toLowerCase() + .contains(query.toLowerCase()), + ) .toList(); await updateUsers(usersFiltered); } @@ -214,21 +224,20 @@ class _ShareImageView extends State { return const BorderSide(width: 0); } return BorderSide( - color: Theme.of(context) - .colorScheme - .outline); + color: + Theme.of(context).colorScheme.outline, + ); }, ), - onChanged: (a) { - setState(() { - hideArchivedUsers = !hideArchivedUsers; - _filterUsers(lastQuery); - }); + onChanged: (a) async { + hideArchivedUsers = !hideArchivedUsers; + await _filterUsers(lastQuery); + if (mounted) setState(() {}); }, ), - ) + ), ], - ) + ), ], ), Expanded( @@ -238,7 +247,7 @@ class _ShareImageView extends State { isRealTwonly: widget.isRealTwonly, updateStatus: updateStatus, ), - ) + ), ], ), ), @@ -270,12 +279,16 @@ class _ShareImageView extends State { if (!context.mounted) return; if (err != null) { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return SubscriptionView( - redirectError: err, - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return SubscriptionView( + redirectError: err, + ); + }, + ), + ); } else { setState(() { sendingImage = true; @@ -305,14 +318,15 @@ class _ShareImageView extends State { } }, style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric(vertical: 10, horizontal: 30), - ), - backgroundColor: WidgetStateProperty.all( - imageBytes == null || widget.selectedUserIds.isEmpty - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).colorScheme.primary, - )), + padding: WidgetStateProperty.all( + const EdgeInsets.symmetric(vertical: 10, horizontal: 30), + ), + backgroundColor: WidgetStateProperty.all( + imageBytes == null || widget.selectedUserIds.isEmpty + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.primary, + ), + ), label: Text( context.lang.shareImagedEditorSendImage, style: const TextStyle(fontSize: 17), diff --git a/lib/src/views/chats/add_new_user.view.dart b/lib/src/views/chats/add_new_user.view.dart index 2bec5f7..d6398b4 100644 --- a/lib/src/views/chats/add_new_user.view.dart +++ b/lib/src/views/chats/add_new_user.view.dart @@ -38,16 +38,16 @@ class _SearchUsernameView extends State { @override void initState() { super.initState(); - contactsStream = twonlyDB.contactsDao - .watchNotAcceptedContacts() - .listen((update) => setState(() { - contacts = update; - })); + contactsStream = twonlyDB.contactsDao.watchNotAcceptedContacts().listen( + (update) => setState(() { + contacts = update; + }), + ); } @override void dispose() { - contactsStream.cancel(); + unawaited(contactsStream.cancel()); super.dispose(); } @@ -69,8 +69,11 @@ class _SearchUsernameView extends State { }); if (userdata == null) { - await showAlertDialog(context, context.lang.searchUsernameNotFound, - context.lang.searchUsernameNotFoundBody(searchUserName.text)); + await showAlertDialog( + context, + context.lang.searchUsernameNotFound, + context.lang.searchUsernameNotFoundBody(searchUserName.text), + ); return; } @@ -141,8 +144,8 @@ class _SearchUsernameView extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: TextField( - onSubmitted: (_) { - _addNewUser(context); + onSubmitted: (_) async { + await _addNewUser(context); }, onChanged: (value) { searchUserName.text = value.toLowerCase(); @@ -166,7 +169,7 @@ class _SearchUsernameView extends State { ), Expanded( child: ContactsListView(contacts), - ) + ), ], ), ), @@ -209,8 +212,10 @@ class ContactsListView extends StatelessWidget { Tooltip( message: context.lang.searchUserNameBlockUserTooltip, child: IconButton( - icon: const Icon(Icons.person_off_rounded, - color: Color.fromARGB(164, 244, 67, 54)), + icon: const Icon( + Icons.person_off_rounded, + color: Color.fromARGB(164, 244, 67, 54), + ), onPressed: () async { const update = ContactsCompanion(blocked: Value(true)); await twonlyDB.contactsDao.updateContact(contact.userId, update); diff --git a/lib/src/views/chats/chat_list.view.dart b/lib/src/views/chats/chat_list.view.dart index 98f6994..fb2157d 100644 --- a/lib/src/views/chats/chat_list.view.dart +++ b/lib/src/views/chats/chat_list.view.dart @@ -95,11 +95,16 @@ class _ChatListViewState extends State { // only show changelog to people who already have contacts // this prevents that this is shown directly after the user registered if (_contacts.isNotEmpty) { - await Navigator.push(context, MaterialPageRoute(builder: (context) { - return ChangeLogView( - changeLog: changeLog, - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ChangeLogView( + changeLog: changeLog, + ); + }, + ), + ); } } } @@ -117,49 +122,61 @@ class _ChatListViewState extends State { final planId = context.watch().plan; return Scaffold( appBar: AppBar( - title: Row(children: [ - GestureDetector( - onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const ProfileView(); - })); - _user = await getUser(); - if (!mounted) return; - setState(() {}); - }, - child: ContactAvatar( - userData: _user, - fontSize: 14, - color: context.color.onSurface.withAlpha(20), - ), - ), - const SizedBox(width: 10), - const Text('twonly '), - if (planId != 'Free') + title: Row( + children: [ GestureDetector( - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return const SubscriptionView(); - })); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const ProfileView(); + }, + ), + ); + _user = await getUser(); + if (!mounted) return; + setState(() {}); }, - child: Container( - decoration: BoxDecoration( - color: context.color.primary, - borderRadius: BorderRadius.circular(15), - ), - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 3), - child: Text( - planId, - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - color: isDarkMode(context) ? Colors.black : Colors.white, + child: ContactAvatar( + userData: _user, + fontSize: 14, + color: context.color.onSurface.withAlpha(20), + ), + ), + const SizedBox(width: 10), + const Text('twonly '), + if (planId != 'Free') + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const SubscriptionView(); + }, + ), + ); + }, + child: Container( + decoration: BoxDecoration( + color: context.color.primary, + borderRadius: BorderRadius.circular(15), + ), + padding: + const EdgeInsets.symmetric(horizontal: 5, vertical: 3), + child: Text( + planId, + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: isDarkMode(context) ? Colors.black : Colors.white, + ), ), ), ), - ), - ]), + ], + ), actions: [ const FeedbackIconButton(), StreamBuilder( @@ -199,7 +216,7 @@ class _ChatListViewState extends State { setState(() {}); }, icon: const FaIcon(FontAwesomeIcons.gear, size: 19), - ) + ), ], ), body: Stack( @@ -216,17 +233,17 @@ class _ChatListViewState extends State { child: Padding( padding: const EdgeInsets.all(10), child: OutlinedButton.icon( - icon: const Icon(Icons.person_add), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const AddNewUserView(), - ), - ); - }, - label: - Text(context.lang.chatListViewSearchUserNameBtn)), + icon: const Icon(Icons.person_add), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AddNewUserView(), + ), + ); + }, + label: Text(context.lang.chatListViewSearchUserNameBtn), + ), ), ) : RefreshIndicator( @@ -288,9 +305,11 @@ class _ChatListViewState extends State { onPressed: () { Navigator.push( context, - MaterialPageRoute(builder: (context) { - return const StartNewChatView(); - }), + MaterialPageRoute( + builder: (context) { + return const StartNewChatView(); + }, + ), ); }, child: const FaIcon(FontAwesomeIcons.penToSquare), @@ -399,11 +418,14 @@ class _UserListItem extends State { Future onTap() async { if (currentMessage == null) { - await Navigator.push(context, MaterialPageRoute( - builder: (context) { - return CameraSendToView(widget.user); - }, - )); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return CameraSendToView(widget.user); + }, + ), + ); return; } @@ -417,9 +439,11 @@ class _UserListItem extends State { case DownloadState.downloaded: await Navigator.push( context, - MaterialPageRoute(builder: (context) { - return MediaViewerView(widget.user); - }), + MaterialPageRoute( + builder: (context) { + return MediaViewerView(widget.user); + }, + ), ); return; case DownloadState.downloading: @@ -429,9 +453,11 @@ class _UserListItem extends State { if (!mounted) return; await Navigator.push( context, - MaterialPageRoute(builder: (context) { - return ChatMessagesView(widget.user); - }), + MaterialPageRoute( + builder: (context) { + return ChatMessagesView(widget.user); + }, + ), ); } @@ -481,15 +507,18 @@ class _UserListItem extends State { ? null : IconButton( onPressed: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - if (hasNonOpenedMediaFile) { - return ChatMessagesView(widget.user); - } else { - return CameraSendToView(widget.user); - } - }, - )); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + if (hasNonOpenedMediaFile) { + return ChatMessagesView(widget.user); + } else { + return CameraSendToView(widget.user); + } + }, + ), + ); }, icon: FaIcon( hasNonOpenedMediaFile @@ -500,7 +529,7 @@ class _UserListItem extends State { ), onTap: onTap, ), - ) + ), ], ); } diff --git a/lib/src/views/chats/chat_list_components/backup_notice.card.dart b/lib/src/views/chats/chat_list_components/backup_notice.card.dart index bb717ce..d5b9a36 100644 --- a/lib/src/views/chats/chat_list_components/backup_notice.card.dart +++ b/lib/src/views/chats/chat_list_components/backup_notice.card.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; @@ -15,8 +17,8 @@ class _BackupNoticeCardState extends State { @override void initState() { - initAsync(); super.initState(); + unawaited(initAsync()); } Future initAsync() async { @@ -74,8 +76,8 @@ class _BackupNoticeCardState extends State { ), const SizedBox(width: 10), FilledButton( - onPressed: () { - Navigator.push( + onPressed: () async { + await Navigator.push( context, MaterialPageRoute( builder: (context) => const BackupView(), diff --git a/lib/src/views/chats/chat_list_components/connection_info.comp.dart b/lib/src/views/chats/chat_list_components/connection_info.comp.dart index 524a273..aa19500 100644 --- a/lib/src/views/chats/chat_list_components/connection_info.comp.dart +++ b/lib/src/views/chats/chat_list_components/connection_info.comp.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -34,17 +36,19 @@ class _ConnectionInfoWidgetState extends State _widthAnim = TweenSequence([ TweenSequenceItem( - tween: Tween(begin: minBarWidth, end: maxBarWidth), - weight: 50), + tween: Tween(begin: minBarWidth, end: maxBarWidth), + weight: 50, + ), TweenSequenceItem( - tween: Tween(begin: maxBarWidth, end: minBarWidth), - weight: 50), + tween: Tween(begin: maxBarWidth, end: minBarWidth), + weight: 50, + ), ]).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); // Delay start by 2 seconds Future.delayed(const Duration(seconds: 2), () { if (mounted) { - _controller.repeat(reverse: true); + unawaited(_controller.repeat(reverse: true)); setState(() { showAnimation = true; }); diff --git a/lib/src/views/chats/chat_list_components/demo_user.card.dart b/lib/src/views/chats/chat_list_components/demo_user.card.dart index eb09495..abfab5e 100644 --- a/lib/src/views/chats/chat_list_components/demo_user.card.dart +++ b/lib/src/views/chats/chat_list_components/demo_user.card.dart @@ -30,7 +30,7 @@ class DemoUserCard extends StatelessWidget { ); }, child: const Text('Register'), - ) + ), ], ), ); diff --git a/lib/src/views/chats/chat_list_components/feedback_btn.dart b/lib/src/views/chats/chat_list_components/feedback_btn.dart index 108d3b8..dd25ecf 100644 --- a/lib/src/views/chats/chat_list_components/feedback_btn.dart +++ b/lib/src/views/chats/chat_list_components/feedback_btn.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -17,7 +19,7 @@ class _FeedbackIconButtonState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -35,8 +37,8 @@ class _FeedbackIconButtonState extends State { } return IconButton( - onPressed: () { - Navigator.push( + onPressed: () async { + await Navigator.push( context, MaterialPageRoute( builder: (context) => const ContactUsView(), diff --git a/lib/src/views/chats/chat_messages.view.dart b/lib/src/views/chats/chat_messages.view.dart index 208a97f..c00c514 100644 --- a/lib/src/views/chats/chat_messages.view.dart +++ b/lib/src/views/chats/chat_messages.view.dart @@ -102,11 +102,11 @@ class _ChatMessagesViewState extends State { @override void dispose() { - super.dispose(); userSub.cancel(); messageSub.cancel(); tutorial?.cancel(); textFieldFocus.dispose(); + super.dispose(); } Future initStreams() async { @@ -196,10 +196,14 @@ class _ChatMessagesViewState extends State { chatItems.add(ChatItem.time(msg.sendAt)); lastDate = msg.sendAt; } - chatItems.add(ChatItem.message(ChatMessage( - message: msg, - responseTo: responseTo, - ))); + chatItems.add( + ChatItem.message( + ChatMessage( + message: msg, + responseTo: responseTo, + ), + ), + ); } } @@ -253,7 +257,8 @@ class _ChatMessagesViewState extends State { Future scrollToMessage(int messageId) async { final index = messages.indexWhere( - (x) => x.isMessage && x.message!.message.messageId == messageId); + (x) => x.isMessage && x.message!.message.messageId == messageId, + ); if (index == -1) return; setState(() { focusedScrollItem = index; @@ -279,9 +284,14 @@ class _ChatMessagesViewState extends State { appBar: AppBar( title: GestureDetector( onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return ContactView(widget.contact.userId); - })); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ContactView(widget.contact.userId); + }, + ), + ); }, child: Row( children: [ @@ -298,7 +308,7 @@ class _ChatMessagesViewState extends State { Text(getContactDisplayName(user)), const SizedBox(width: 10), if (user.verified) - VerifiedShield(key: verifyShieldKey, user) + VerifiedShield(key: verifyShieldKey, user), ], ), ), @@ -331,12 +341,13 @@ class _ChatMessagesViewState extends State { final chatMessage = messages[i].message!; return Transform.translate( offset: Offset( - (focusedScrollItem == i) - ? (chatMessage.message.messageOtherId == null) - ? -8 - : 8 - : 0, - 0), + (focusedScrollItem == i) + ? (chatMessage.message.messageOtherId == null) + ? -8 + : 8 + : 0, + 0, + ), child: Transform.scale( scale: (focusedScrollItem == i) ? 1.05 : 1, child: ChatListEntry( @@ -389,7 +400,7 @@ class _ChatMessagesViewState extends State { FontAwesomeIcons.xmark, size: 16, ), - ) + ), ], ), ), @@ -425,7 +436,8 @@ class _ChatMessagesViewState extends State { IconButton( padding: const EdgeInsets.all(15), icon: const FaIcon( - FontAwesomeIcons.solidPaperPlane), + FontAwesomeIcons.solidPaperPlane, + ), onPressed: _sendMessage, ) else @@ -442,7 +454,7 @@ class _ChatMessagesViewState extends State { ), ); }, - ) + ), ], ), ), diff --git a/lib/src/views/chats/chat_messages_components/chat_list_entry.dart b/lib/src/views/chats/chat_messages_components/chat_list_entry.dart index 3b1d831..4ba5c6b 100644 --- a/lib/src/views/chats/chat_messages_components/chat_list_entry.dart +++ b/lib/src/views/chats/chat_messages_components/chat_list_entry.dart @@ -42,8 +42,10 @@ class _ChatListEntryState extends State { @override void initState() { super.initState(); - final msgContent = MessageContent.fromJson(widget.msg.message.kind, - jsonDecode(widget.msg.message.contentJson!) as Map); + final msgContent = MessageContent.fromJson( + widget.msg.message.kind, + jsonDecode(widget.msg.message.contentJson!) as Map, + ); if (msgContent is TextMessageContent) { textMessage = msgContent.text; } diff --git a/lib/src/views/chats/chat_messages_components/chat_media_entry.dart b/lib/src/views/chats/chat_messages_components/chat_media_entry.dart index 09b477c..65208f0 100644 --- a/lib/src/views/chats/chat_messages_components/chat_media_entry.dart +++ b/lib/src/views/chats/chat_messages_components/chat_media_entry.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:drift/drift.dart' show Value; import 'package:flutter/material.dart'; import 'package:twonly/globals.dart'; @@ -39,7 +41,7 @@ class _ChatMediaEntryState extends State { @override void initState() { super.initState(); - checkIfTutorialCanBeShown(); + unawaited(checkIfTutorialCanBeShown()); } Future checkIfTutorialCanBeShown() async { @@ -60,9 +62,9 @@ class _ChatMediaEntryState extends State { canBeReopened = true; }); } - Future.delayed(const Duration(seconds: 1), () { + Future.delayed(const Duration(seconds: 1), () async { if (!mounted) return; - showReopenMediaFilesTutorial(context, reopenMediaFile); + await showReopenMediaFilesTutorial(context, reopenMediaFile); }); } } @@ -101,10 +103,14 @@ class _ChatMediaEntryState extends State { widget.message.openedAt == null) { await Navigator.push( context, - MaterialPageRoute(builder: (context) { - return MediaViewerView(widget.contact, - initialMessage: widget.message); - }), + MaterialPageRoute( + builder: (context) { + return MediaViewerView( + widget.contact, + initialMessage: widget.message, + ); + }, + ), ); await checkIfTutorialCanBeShown(); } else if (widget.message.downloadState == DownloadState.pending) { diff --git a/lib/src/views/chats/chat_messages_components/chat_reaction_row.dart b/lib/src/views/chats/chat_messages_components/chat_reaction_row.dart index 8f57e45..113400d 100644 --- a/lib/src/views/chats/chat_messages_components/chat_reaction_row.dart +++ b/lib/src/views/chats/chat_messages_components/chat_reaction_row.dart @@ -29,7 +29,9 @@ class _ReactionRowState extends State { var hasOneReopened = false; for (final reaction in widget.otherReactions.reversed) { final content = MessageContent.fromJson( - reaction.kind, jsonDecode(reaction.contentJson!) as Map); + reaction.kind, + jsonDecode(reaction.contentJson!) as Map, + ); if (content is ReopenedMediaFileContent) { if (hasOneReopened) continue; diff --git a/lib/src/views/chats/chat_messages_components/chat_text_entry.dart b/lib/src/views/chats/chat_messages_components/chat_text_entry.dart index c249ae9..f161c52 100644 --- a/lib/src/views/chats/chat_messages_components/chat_text_entry.dart +++ b/lib/src/views/chats/chat_messages_components/chat_text_entry.dart @@ -34,7 +34,11 @@ class ChatTextEntry extends StatelessWidget { maxWidth: MediaQuery.of(context).size.width * 0.8, ), padding: EdgeInsets.only( - left: 10, top: 4, bottom: 4, right: hasReaction ? 30 : 10), + left: 10, + top: 4, + bottom: 4, + right: hasReaction ? 30 : 10, + ), decoration: BoxDecoration( color: message.responseTo == null ? getMessageColor(message.message) diff --git a/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart b/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart index 1a130c2..26774e0 100644 --- a/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart +++ b/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart @@ -37,8 +37,8 @@ class _InChatMediaViewerState extends State { @override void initState() { super.initState(); - loadIndexAsync(); - initStream(); + unawaited(loadIndexAsync()); + unawaited(initStream()); } Future loadIndexAsync() async { @@ -55,8 +55,10 @@ class _InChatMediaViewerState extends State { bool loadIndex() { if (widget.message.mediaStored) { - final index = widget.galleryItems.indexWhere((x) => - x.id == (widget.message.mediaUploadId ?? widget.message.messageId)); + final index = widget.galleryItems.indexWhere( + (x) => + x.id == (widget.message.mediaUploadId ?? widget.message.messageId), + ); if (index != -1) { galleryItemIndex = index; return true; @@ -122,7 +124,9 @@ class _InChatMediaViewerState extends State { ), child: Padding( padding: EdgeInsets.symmetric( - vertical: (widget.canBeReopened) ? 5 : 10.0, horizontal: 4), + vertical: (widget.canBeReopened) ? 5 : 10.0, + horizontal: 4, + ), child: MessageSendStateIcon( [widget.message], mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/src/views/chats/chat_messages_components/message_actions.dart b/lib/src/views/chats/chat_messages_components/message_actions.dart index 2e289e4..7ee69ea 100644 --- a/lib/src/views/chats/chat_messages_components/message_actions.dart +++ b/lib/src/views/chats/chat_messages_components/message_actions.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid_dynamic_calls, inference_failure_on_function_invocation +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -30,7 +30,7 @@ class _SlidingResponseWidgetState extends State { if (_offsetX > 40) { _offsetX = 40; if (!gotFeedback) { - HapticFeedback.heavyImpact(); + unawaited(HapticFeedback.heavyImpact()); gotFeedback = true; } } diff --git a/lib/src/views/chats/chat_messages_components/message_context_menu.dart b/lib/src/views/chats/chat_messages_components/message_context_menu.dart index 91c0834..26fe88e 100644 --- a/lib/src/views/chats/chat_messages_components/message_context_menu.dart +++ b/lib/src/views/chats/chat_messages_components/message_context_menu.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid_dynamic_calls, inference_failure_on_function_invocation +// ignore_for_file: inference_failure_on_function_invocation import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -30,9 +30,9 @@ class MessageContextMenu extends StatelessWidget { Widget build(BuildContext context) { return PieMenu( onPressed: () => (), - onToggle: (menuOpen) { + onToggle: (menuOpen) async { if (menuOpen) { - HapticFeedback.heavyImpact(); + await HapticFeedback.heavyImpact(); } }, actions: [ @@ -51,11 +51,11 @@ class MessageContextMenu extends StatelessWidget { await sendTextMessage( message.contactId, TextMessageContent( - text: layer.text, - responseToMessageId: message.messageOtherId, - responseToOtherMessageId: (message.messageOtherId == null) - ? message.messageId - : null), + text: layer.text, + responseToMessageId: message.messageOtherId, + responseToOtherMessageId: + (message.messageOtherId == null) ? message.messageId : null, + ), (message.messageOtherId != null) ? PushNotification( kind: (message.kind == MessageKind.textMessage) @@ -77,10 +77,10 @@ class MessageContextMenu extends StatelessWidget { ), PieAction( tooltip: Text(context.lang.copy), - onSelect: () { + onSelect: () async { final text = getMessageText(message); - Clipboard.setData(ClipboardData(text: text)); - HapticFeedback.heavyImpact(); + await Clipboard.setData(ClipboardData(text: text)); + await HapticFeedback.heavyImpact(); }, child: const FaIcon(FontAwesomeIcons.solidCopy), ), diff --git a/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart b/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart index 9d0968e..f6c2cda 100644 --- a/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart +++ b/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart @@ -198,8 +198,8 @@ class _MessageSendStateIconState extends State { Transform( transform: Matrix4.identity() - ..scale(0.7) // Scale to half - ..translate(3.0, 5), + ..scaleByDouble(0.7, 0.7, 0.7, 0.7) // Scale to half + ..translateByDouble(3, 5, 0, 1), // Move down by 10 pixels (adjust as needed) alignment: Alignment.center, child: icons[1], diff --git a/lib/src/views/chats/chat_messages_components/response_container.dart b/lib/src/views/chats/chat_messages_components/response_container.dart index 322fde3..14bdf7a 100644 --- a/lib/src/views/chats/chat_messages_components/response_container.dart +++ b/lib/src/views/chats/chat_messages_components/response_container.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -126,8 +127,8 @@ class _ResponsePreviewState extends State { @override void initState() { - initAsync(); super.initState(); + unawaited(initAsync()); } Future initAsync() async { @@ -145,8 +146,10 @@ class _ResponsePreviewState extends State { if (widget.message.kind == MessageKind.textMessage) { if (widget.message.contentJson != null) { - final content = MessageContent.fromJson(MessageKind.textMessage, - jsonDecode(widget.message.contentJson!) as Map); + final content = MessageContent.fromJson( + MessageKind.textMessage, + jsonDecode(widget.message.contentJson!) as Map, + ); if (content is TextMessageContent) { subtitle = truncateString(content.text); } @@ -154,7 +157,9 @@ class _ResponsePreviewState extends State { } if (widget.message.kind == MessageKind.media) { final content = MessageContent.fromJson( - MessageKind.media, jsonDecode(widget.message.contentJson!) as Map); + MessageKind.media, + jsonDecode(widget.message.contentJson!) as Map, + ); if (content is MediaMessageContent) { subtitle = content.isVideo ? 'Video' : 'Image'; } @@ -189,7 +194,7 @@ class _ResponsePreviewState extends State { username, style: const TextStyle(fontWeight: FontWeight.bold), ), - if (subtitle != null) Text(subtitle) + if (subtitle != null) Text(subtitle), ], ), ); @@ -216,7 +221,7 @@ class _ResponsePreviewState extends State { username, style: const TextStyle(fontWeight: FontWeight.bold), ), - if (subtitle != null) Text(subtitle) + if (subtitle != null) Text(subtitle), ], ), ), @@ -224,7 +229,7 @@ class _ResponsePreviewState extends State { SizedBox( height: widget.showBorder ? 100 : 210, child: Image.file(thumbnailPath!), - ) + ), ], ), ); diff --git a/lib/src/views/chats/media_viewer.view.dart b/lib/src/views/chats/media_viewer.view.dart index de08f44..f58449c 100644 --- a/lib/src/views/chats/media_viewer.view.dart +++ b/lib/src/views/chats/media_viewer.view.dart @@ -1,4 +1,4 @@ -// ignore_for_file: inference_failure_on_collection_literal, avoid_dynamic_calls +// ignore_for_file: avoid_dynamic_calls import 'dart:async'; import 'dart:convert'; @@ -201,7 +201,9 @@ class _MediaViewerViewState extends State { } Future handleNextDownloadedMedia( - Message current, bool showTwonly) async { + Message current, + bool showTwonly, + ) async { final content = MediaMessageContent.fromJson(jsonDecode(current.contentJson!) as Map); @@ -371,9 +373,10 @@ class _MediaViewerViewState extends State { children: [ if (imageSaving) const SizedBox( - width: 10, - height: 10, - child: CircularProgressIndicator(strokeWidth: 1)) + width: 10, + height: 10, + child: CircularProgressIndicator(strokeWidth: 1), + ) else imageSaved ? const Icon(Icons.check) @@ -443,11 +446,14 @@ class _MediaViewerViewState extends State { progressTimer?.cancel(); await videoController?.pause(); if (!mounted) return; - await Navigator.push(context, MaterialPageRoute( - builder: (context) { - return CameraSendToView(widget.contact); - }, - )); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return CameraSendToView(widget.contact); + }, + ), + ); if (mounted && maxShowTime != gMediaShowInfinite) { await nextMediaOrExit(); } else { @@ -501,8 +507,12 @@ class _MediaViewerViewState extends State { child: Image.memory( imageBytes!, fit: BoxFit.contain, - frameBuilder: (context, child, frame, - wasSynchronouslyLoaded) { + frameBuilder: ( + context, + child, + frame, + wasSynchronouslyLoaded, + ) { if (wasSynchronouslyLoaded) return child; return AnimatedSwitcher( duration: const Duration(milliseconds: 200), @@ -604,7 +614,7 @@ class _MediaViewerViewState extends State { Shadow( color: Color.fromARGB(122, 0, 0, 0), blurRadius: 5, - ) + ), ], ), ), @@ -617,7 +627,11 @@ class _MediaViewerViewState extends State { child: Container( color: context.color.surface, padding: const EdgeInsets.only( - bottom: 10, left: 20, right: 20, top: 10), + bottom: 10, + left: 20, + right: 20, + top: 10, + ), child: Row( children: [ IconButton( @@ -664,7 +678,7 @@ class _MediaViewerViewState extends State { showShortReactions = false; }); }, - ) + ), ], ), ), @@ -765,14 +779,16 @@ class _ReactionButtonsState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.end, children: secondRowEmojis - .map((emoji) => EmojiReactionWidget( - userId: widget.userId, - responseToMessageId: widget.responseToMessageId, - hide: widget.hide, - show: widget.show, - isVideo: widget.isVideo, - emoji: emoji as String, - )) + .map( + (emoji) => EmojiReactionWidget( + userId: widget.userId, + responseToMessageId: widget.responseToMessageId, + hide: widget.hide, + show: widget.show, + isVideo: widget.isVideo, + emoji: emoji as String, + ), + ) .toList(), ), if (secondRowEmojis.isNotEmpty) const SizedBox(height: 15), @@ -848,10 +864,12 @@ class _EmojiReactionWidgetState extends State { selectedShortReaction = 0; // Assuming index is 0 for this example }); Future.delayed(const Duration(milliseconds: 300), () { - setState(() { - widget.hide(); - selectedShortReaction = -1; - }); + if (mounted) { + setState(() { + widget.hide(); + selectedShortReaction = -1; + }); + } }); }, child: (selectedShortReaction == diff --git a/lib/src/views/chats/start_new_chat.view.dart b/lib/src/views/chats/start_new_chat.view.dart index a68b823..85c0773 100644 --- a/lib/src/views/chats/start_new_chat.view.dart +++ b/lib/src/views/chats/start_new_chat.view.dart @@ -32,20 +32,21 @@ class _StartNewChatView extends State { final stream = twonlyDB.contactsDao.watchContactsForStartNewChat(); - contactSub = stream.listen((update) { - update.sort((a, b) => - getContactDisplayName(a).compareTo(getContactDisplayName(b))); + contactSub = stream.listen((update) async { + update.sort( + (a, b) => getContactDisplayName(a).compareTo(getContactDisplayName(b)), + ); setState(() { allContacts = update; }); - filterUsers(); + await filterUsers(); }); } @override void dispose() { + unawaited(contactSub.cancel()); super.dispose(); - contactSub.cancel(); } Future filterUsers() async { @@ -56,9 +57,11 @@ class _StartNewChatView extends State { return; } final usersFiltered = allContacts - .where((user) => getContactDisplayName(user) - .toLowerCase() - .contains(searchUserName.value.text.toLowerCase())) + .where( + (user) => getContactDisplayName(user) + .toLowerCase() + .contains(searchUserName.value.text.toLowerCase()), + ) .toList(); setState(() { contacts = usersFiltered; @@ -82,8 +85,8 @@ class _StartNewChatView extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: TextField( - onChanged: (_) { - filterUsers(); + onChanged: (_) async { + await filterUsers(); }, controller: searchUserName, decoration: getInputDecoration( @@ -97,7 +100,7 @@ class _StartNewChatView extends State { child: UserList( contacts, ), - ) + ), ], ), ), @@ -130,8 +133,8 @@ class UserList extends StatelessWidget { size: 13, ), ), - onTap: () { - Navigator.push( + onTap: () async { + await Navigator.push( context, MaterialPageRoute( builder: (context) => const AddNewUserView(), @@ -160,29 +163,34 @@ class UserList extends StatelessWidget { ), const Spacer(), IconButton( - icon: FaIcon(FontAwesomeIcons.boxOpen, - size: 13, - color: user.archived ? null : Colors.transparent), - onPressed: user.archived - ? () async { - const update = - ContactsCompanion(archived: Value(false)); - await twonlyDB.contactsDao - .updateContact(user.userId, update); - } - : null) + icon: FaIcon( + FontAwesomeIcons.boxOpen, + size: 13, + color: user.archived ? null : Colors.transparent, + ), + onPressed: user.archived + ? () async { + const update = + ContactsCompanion(archived: Value(false)); + await twonlyDB.contactsDao + .updateContact(user.userId, update); + } + : null, + ), ], ), leading: ContactAvatar( contact: user, fontSize: 13, ), - onTap: () { - Navigator.pushReplacement( + onTap: () async { + await Navigator.pushReplacement( context, - MaterialPageRoute(builder: (context) { - return ChatMessagesView(user); - }), + MaterialPageRoute( + builder: (context) { + return ChatMessagesView(user); + }, + ), ); }, ), diff --git a/lib/src/views/components/animate_icon.dart b/lib/src/views/components/animate_icon.dart index 78ba086..267a400 100644 --- a/lib/src/views/components/animate_icon.dart +++ b/lib/src/views/components/animate_icon.dart @@ -6,7 +6,8 @@ import 'package:twonly/src/views/camera/image_editor/data/data.dart'; // from https://github.com/eitanliu/emoji_regex/tree/master RegExp emojiRegex() => RegExp( - r'[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26F9(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC3\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC08\uDC26](?:\u200D\u2B1B)?|[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC2\uDECE-\uDEDB\uDEE0-\uDEE8]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)'); + r'[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26F9(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC3\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC08\uDC26](?:\u200D\u2B1B)?|[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC2\uDECE-\uDEDB\uDEE0-\uDEE8]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)', + ); bool isEmoji(String character) { final matches = emojiRegex().allMatches(character); @@ -233,7 +234,9 @@ class EmojiAnimationFlying extends StatelessWidget { Widget build(BuildContext context) { return TweenAnimationBuilder( tween: Tween( - begin: startPosition, end: 1), // Adjust end value as needed + begin: startPosition, + end: 1, + ), // Adjust end value as needed duration: duration, curve: Curves.linearToEaseOut, builder: (context, value, child) { diff --git a/lib/src/views/components/app_outdated.dart b/lib/src/views/components/app_outdated.dart index 2dfca67..952cf98 100644 --- a/lib/src/views/components/app_outdated.dart +++ b/lib/src/views/components/app_outdated.dart @@ -100,9 +100,12 @@ class _AppOutdatedState extends State { if (Platform.isAndroid) const SizedBox(height: 5), if (Platform.isAndroid) ElevatedButton( - onPressed: () { - launchUrl(Uri.parse( - 'https://play.google.com/store/apps/details?id=eu.twonly')); + onPressed: () async { + await launchUrl( + Uri.parse( + 'https://play.google.com/store/apps/details?id=eu.twonly', + ), + ); }, style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( diff --git a/lib/src/views/components/better_list_title.dart b/lib/src/views/components/better_list_title.dart index b120c9e..b7ad83c 100644 --- a/lib/src/views/components/better_list_title.dart +++ b/lib/src/views/components/better_list_title.dart @@ -2,14 +2,15 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class BetterListTile extends StatelessWidget { - const BetterListTile( - {required this.icon, - required this.text, - required this.onTap, - super.key, - this.color, - this.subtitle, - this.iconSize = 20}); + const BetterListTile({ + required this.icon, + required this.text, + required this.onTap, + super.key, + this.color, + this.subtitle, + this.iconSize = 20, + }); final IconData icon; final String text; final Widget? subtitle; diff --git a/lib/src/views/components/better_text.dart b/lib/src/views/components/better_text.dart index 9841e2d..2373f9f 100644 --- a/lib/src/views/components/better_text.dart +++ b/lib/src/views/components/better_text.dart @@ -27,23 +27,25 @@ class BetterText extends StatelessWidget { } final url = match.group(0); - spans.add(TextSpan( - text: url, - style: const TextStyle( - decoration: TextDecoration.underline, - decorationColor: Colors.white, + spans.add( + TextSpan( + text: url, + style: const TextStyle( + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + recognizer: TapGestureRecognizer() + ..onTap = () async { + final lUrl = + Uri.parse(url!.startsWith('http') ? url : 'http://$url'); + try { + await launchUrl(lUrl); + } catch (e) { + Log.error('Could not launch $e'); + } + }, ), - recognizer: TapGestureRecognizer() - ..onTap = () async { - final lUrl = - Uri.parse(url!.startsWith('http') ? url : 'http://$url'); - try { - await launchUrl(lUrl); - } catch (e) { - Log.error('Could not launch $e'); - } - }, - )); + ); lastMatchEnd = match.end; } diff --git a/lib/src/views/components/flame.dart b/lib/src/views/components/flame.dart index 2fc4ccc..2f3c80a 100644 --- a/lib/src/views/components/flame.dart +++ b/lib/src/views/components/flame.dart @@ -28,7 +28,8 @@ class FlameCounterWidget extends StatelessWidget { SizedBox( height: 15, child: EmojiAnimation( - emoji: (globalBestFriendUserId == user.userId) ? '❤️‍🔥' : '🔥'), + emoji: (globalBestFriendUserId == user.userId) ? '❤️‍🔥' : '🔥', + ), ), ], ); diff --git a/lib/src/views/components/notification_badge.dart b/lib/src/views/components/notification_badge.dart index 47c2645..dd4e0ac 100644 --- a/lib/src/views/components/notification_badge.dart +++ b/lib/src/views/components/notification_badge.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; class NotificationBadge extends StatelessWidget { - const NotificationBadge( - {required this.count, required this.child, super.key}); + const NotificationBadge({ + required this.count, + required this.child, + super.key, + }); final String count; final Widget child; @@ -35,7 +38,7 @@ class NotificationBadge extends StatelessWidget { ), ), ), - ) + ), ], ); } diff --git a/lib/src/views/components/radio_button.dart b/lib/src/views/components/radio_button.dart index 3d71638..4fae1c6 100644 --- a/lib/src/views/components/radio_button.dart +++ b/lib/src/views/components/radio_button.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use + import 'package:flutter/material.dart'; class RadioButton extends StatelessWidget { diff --git a/lib/src/views/components/user_context_menu.dart b/lib/src/views/components/user_context_menu.dart index 8b7666a..2b30fce 100644 --- a/lib/src/views/components/user_context_menu.dart +++ b/lib/src/views/components/user_context_menu.dart @@ -27,9 +27,9 @@ class _UserContextMenuState extends State { Widget build(BuildContext context) { return PieMenu( onPressed: () => (), - onToggle: (menuOpen) { + onToggle: (menuOpen) async { if (menuOpen) { - HapticFeedback.heavyImpact(); + await HapticFeedback.heavyImpact(); } }, actions: [ @@ -59,12 +59,15 @@ class _UserContextMenuState extends State { ), PieAction( tooltip: Text(context.lang.contextMenuOpenChat), - onSelect: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return ChatMessagesView(widget.contact); - }, - )); + onSelect: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ChatMessagesView(widget.contact); + }, + ), + ); }, child: const FaIcon(FontAwesomeIcons.solidComments), ), @@ -82,9 +85,11 @@ class _UserContextMenuState extends State { .updateContact(widget.contact.userId, update); } }, - child: FaIcon(widget.contact.pinned - ? FontAwesomeIcons.thumbtackSlash - : FontAwesomeIcons.thumbtack), + child: FaIcon( + widget.contact.pinned + ? FontAwesomeIcons.thumbtackSlash + : FontAwesomeIcons.thumbtack, + ), ), ], child: widget.child, @@ -137,12 +142,15 @@ class _UserContextMenuBlocked extends State { ), PieAction( tooltip: Text(context.lang.contextMenuUserProfile), - onSelect: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return ContactView(widget.contact.userId); - }, - )); + onSelect: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ContactView(widget.contact.userId); + }, + ), + ); }, child: const FaIcon(FontAwesomeIcons.user), ), diff --git a/lib/src/views/components/verified_shield.dart b/lib/src/views/components/verified_shield.dart index 101a20c..27c1c45 100644 --- a/lib/src/views/components/verified_shield.dart +++ b/lib/src/views/components/verified_shield.dart @@ -11,12 +11,15 @@ class VerifiedShield extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return ContactVerifyView(contact); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ContactVerifyView(contact); + }, + ), + ); }, child: Tooltip( message: contact.verified diff --git a/lib/src/views/components/video_player_wrapper.dart b/lib/src/views/components/video_player_wrapper.dart index 2ed4be2..b35edc8 100644 --- a/lib/src/views/components/video_player_wrapper.dart +++ b/lib/src/views/components/video_player_wrapper.dart @@ -1,11 +1,15 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; class VideoPlayerWrapper extends StatefulWidget { - const VideoPlayerWrapper( - {required this.videoPath, required this.mirrorVideo, super.key}); + const VideoPlayerWrapper({ + required this.videoPath, + required this.mirrorVideo, + super.key, + }); final File videoPath; final bool mirrorVideo; @@ -19,21 +23,22 @@ class _VideoPlayerWrapperState extends State { @override void initState() { super.initState(); - _controller = VideoPlayerController.file(widget.videoPath) - ..initialize().then((_) { + _controller = VideoPlayerController.file(widget.videoPath); + + unawaited( + _controller.initialize().then((_) async { if (context.mounted) { - setState(() { - _controller - ..setLooping(true) - ..play(); - }); + await _controller.setLooping(true); + await _controller.play(); + setState(() {}); } - }); + }), + ); } @override void dispose() { - _controller.dispose(); + unawaited(_controller.dispose()); super.dispose(); } diff --git a/lib/src/views/contact/contact.view.dart b/lib/src/views/contact/contact.view.dart index 2a772fb..31fcfde 100644 --- a/lib/src/views/contact/contact.view.dart +++ b/lib/src/views/contact/contact.view.dart @@ -72,7 +72,8 @@ class _ContactViewState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( - 'Es ist ein Fehler aufgetreten. Bitte versuche es später erneut.'), + 'Es ist ein Fehler aufgetreten. Bitte versuche es später erneut.', + ), duration: Duration(seconds: 3), ), ); @@ -116,8 +117,9 @@ class _ContactViewState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( - padding: const EdgeInsets.only(right: 10), - child: VerifiedShield(contact)), + padding: const EdgeInsets.only(right: 10), + child: VerifiedShield(contact), + ), Text( getContactDisplayName(contact), style: const TextStyle(fontSize: 20), @@ -151,12 +153,15 @@ class _ContactViewState extends State { BetterListTile( icon: FontAwesomeIcons.shieldHeart, text: context.lang.contactVerifyNumberTitle, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return ContactVerifyView(contact); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ContactVerifyView(contact); + }, + ), + ); }, ), BetterListTile( @@ -168,7 +173,8 @@ class _ContactViewState extends State { context, context.lang.deleteAllContactMessages, context.lang.deleteAllContactMessagesBody( - getContactDisplayName(contact)), + getContactDisplayName(contact), + ), ); if (block) { if (context.mounted) { @@ -204,7 +210,9 @@ class _ContactViewState extends State { } Future showNicknameChangeDialog( - BuildContext context, Contact contact) { + BuildContext context, + Contact contact, +) { final controller = TextEditingController(text: getContactDisplayName(contact)); diff --git a/lib/src/views/contact/contact_verify.view.dart b/lib/src/views/contact/contact_verify.view.dart index cfe9c65..05b05c3 100644 --- a/lib/src/views/contact/contact_verify.view.dart +++ b/lib/src/views/contact/contact_verify.view.dart @@ -39,12 +39,12 @@ class _ContactVerifyViewState extends State { void initState() { super.initState(); _contact = widget.contact; - loadAsync(); + unawaited(loadAsync()); } @override void dispose() { - _contactSub.cancel(); + unawaited(_contactSub.cancel()); super.dispose(); } @@ -86,14 +86,17 @@ class _ContactVerifyViewState extends State { Future openQrScanner() async { if (_fingerprint == null) return; - final isValid = await Navigator.push(context, MaterialPageRoute( - builder: (context) { - return ContactVerifyQrScanView( - widget.contact, - fingerprint: _fingerprint!, - ); - }, - )) as bool?; + final isValid = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ContactVerifyQrScanView( + widget.contact, + fingerprint: _fingerprint!, + ); + }, + ), + ) as bool?; if (isValid == null) { return; // user just returned... } @@ -205,7 +208,8 @@ class _ContactVerifyViewState extends State { padding: const EdgeInsets.symmetric(horizontal: 30), child: Text( context.lang.contactVerifyNumberLongDesc( - getContactDisplayName(_contact)), + getContactDisplayName(_contact), + ), textAlign: TextAlign.center, ), ), @@ -213,9 +217,12 @@ class _ContactVerifyViewState extends State { padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), child: GestureDetector( - onTap: () { - launchUrl(Uri.parse( - 'https://twonly.eu/en/faq/security/verify-security-number.html')); + onTap: () async { + await launchUrl( + Uri.parse( + 'https://twonly.eu/en/faq/security/verify-security-number.html', + ), + ); }, child: Text( 'Read more.', @@ -226,7 +233,7 @@ class _ContactVerifyViewState extends State { ), ), ), - ) + ), ], ), bottomNavigationBar: SafeArea( @@ -248,7 +255,7 @@ class _ContactVerifyViewState extends State { label: Text( context.lang.contactVerifyNumberMarkAsVerified, ), - ) + ), ], ), ), diff --git a/lib/src/views/contact/contact_verify_qr_scan.view.dart b/lib/src/views/contact/contact_verify_qr_scan.view.dart index 097e594..566a833 100644 --- a/lib/src/views/contact/contact_verify_qr_scan.view.dart +++ b/lib/src/views/contact/contact_verify_qr_scan.view.dart @@ -7,8 +7,11 @@ import 'package:twonly/src/database/twonly_database.dart'; import 'package:twonly/src/utils/log.dart'; class ContactVerifyQrScanView extends StatefulWidget { - const ContactVerifyQrScanView(this.contact, - {required this.fingerprint, super.key}); + const ContactVerifyQrScanView( + this.contact, { + required this.fingerprint, + super.key, + }); final Fingerprint fingerprint; final Contact contact; diff --git a/lib/src/views/home.view.dart b/lib/src/views/home.view.dart index f796dc0..3463ea0 100644 --- a/lib/src/views/home.view.dart +++ b/lib/src/views/home.view.dart @@ -72,11 +72,11 @@ class HomeViewState extends State { } if (cameraController == null && !initCameraStarted && offsetRatio < 1) { initCameraStarted = true; - selectCamera(selectedCameraDetails.cameraId, false, false); + unawaited(selectCamera(selectedCameraDetails.cameraId, false, false)); } if (offsetRatio == 1) { - disableCameraTimer = Timer(const Duration(milliseconds: 500), () { - cameraController?.dispose(); + disableCameraTimer = Timer(const Duration(milliseconds: 500), () async { + await cameraController?.dispose(); cameraController = null; selectedCameraDetails = SelectedCameraDetails(); disableCameraTimer = null; @@ -99,13 +99,13 @@ class HomeViewState extends State { .listen((NotificationResponse? response) async { globalUpdateOfHomeViewPageIndex(0); }); - selectCamera(0, true, false); - initAsync(); + unawaited(selectCamera(0, true, false)); + unawaited(initAsync()); } @override void dispose() { - selectNotificationStream.close(); + unawaited(selectNotificationStream.close()); disableCameraTimer?.cancel(); cameraController?.dispose(); super.dispose(); @@ -117,7 +117,11 @@ class HomeViewState extends State { bool enableAudio, ) async { final opts = await initializeCameraController( - selectedCameraDetails, sCameraId, init, enableAudio); + selectedCameraDetails, + sCameraId, + init, + enableAudio, + ); if (opts != null) { selectedCameraDetails = opts.$1; cameraController = opts.$2; @@ -131,7 +135,7 @@ class HomeViewState extends State { Future toggleSelectedCamera() async { if (cameraController == null) return; // do not allow switching camera when recording - if (cameraController!.value.isRecordingVideo == true) { + if (cameraController!.value.isRecordingVideo) { return; } await cameraController!.dispose(); @@ -185,21 +189,22 @@ class HomeViewState extends State { ), ), Positioned( - left: 0, - top: 0, - right: 0, - bottom: (offsetRatio > 0.25) - ? MediaQuery.sizeOf(context).height * 2 - : 0, - child: Opacity( - opacity: 1 - (offsetRatio * 4) % 1, - child: CameraPreviewControllerView( - cameraController: cameraController, - screenshotController: screenshotController, - selectedCameraDetails: selectedCameraDetails, - selectCamera: selectCamera, - ), - )), + left: 0, + top: 0, + right: 0, + bottom: (offsetRatio > 0.25) + ? MediaQuery.sizeOf(context).height * 2 + : 0, + child: Opacity( + opacity: 1 - (offsetRatio * 4) % 1, + child: CameraPreviewControllerView( + cameraController: cameraController, + screenshotController: screenshotController, + selectedCameraDetails: selectedCameraDetails, + selectCamera: selectCamera, + ), + ), + ), ], ), ), @@ -207,10 +212,11 @@ class HomeViewState extends State { showSelectedLabels: false, showUnselectedLabels: false, unselectedIconTheme: IconThemeData( - color: - Theme.of(context).colorScheme.inverseSurface.withAlpha(150)), + color: Theme.of(context).colorScheme.inverseSurface.withAlpha(150), + ), selectedIconTheme: IconThemeData( - color: Theme.of(context).colorScheme.inverseSurface), + color: Theme.of(context).colorScheme.inverseSurface, + ), items: const [ BottomNavigationBarItem( icon: FaIcon(FontAwesomeIcons.solidComments), @@ -225,15 +231,14 @@ class HomeViewState extends State { label: '', ), ], - onTap: (int index) { + onTap: (int index) async { activePageIdx = index; - setState(() { - homeViewPageController.animateToPage( - index, - duration: const Duration(milliseconds: 100), - curve: Curves.bounceIn, - ); - }); + await homeViewPageController.animateToPage( + index, + duration: const Duration(milliseconds: 100), + curve: Curves.bounceIn, + ); + if (mounted) setState(() {}); }, currentIndex: activePageIdx, ), diff --git a/lib/src/views/memories/memories.view.dart b/lib/src/views/memories/memories.view.dart index 2d16c24..91702c6 100644 --- a/lib/src/views/memories/memories.view.dart +++ b/lib/src/views/memories/memories.view.dart @@ -29,7 +29,7 @@ class MemoriesViewState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } @override @@ -71,15 +71,17 @@ class MemoriesViewState extends State { break; } final creationDate = file.lastModifiedSync(); - items.add(MemoryItem( - id: int.parse(fileName.split('.')[0]), - messages: [], - date: creationDate, - mirrorVideo: false, - thumbnailPath: thumbnailFile, - imagePath: imagePath, - videoPath: videoPath, - )); + items.add( + MemoryItem( + id: int.parse(fileName.split('.')[0]), + messages: [], + date: creationDate, + mirrorVideo: false, + thumbnailPath: thumbnailFile, + imagePath: imagePath, + videoPath: videoPath, + ), + ); } } } @@ -98,8 +100,9 @@ class MemoriesViewState extends State { var lastMonth = ''; galleryItems = await loadMemoriesDirectory(); for (final item in galleryItems) { - items.remove(item - .id); // prefer the stored one and not the saved on in the chat.... + items.remove( + item.id, + ); // prefer the stored one and not the saved on in the chat.... } galleryItems += items.values.toList(); galleryItems.sort((a, b) => b.date.compareTo(a.date)); @@ -125,19 +128,20 @@ class MemoriesViewState extends State { child: (galleryItems.isEmpty) ? Center( child: Text( - context.lang.memoriesEmpty, - textAlign: TextAlign.center, - )) + context.lang.memoriesEmpty, + textAlign: TextAlign.center, + ), + ) : ListView.builder( itemCount: months.length * 2, itemBuilder: (context, mIndex) { if (mIndex.isEven) { return Padding( padding: const EdgeInsets.all(8), - child: Text(months[(mIndex / 2).toInt()]), + child: Text(months[(mIndex ~/ 2)]), ); } - final index = ((mIndex - 1) / 2).toInt(); + final index = (mIndex - 1) ~/ 2; return GridView.builder( shrinkWrap: true, physics: const ClampingScrollPhysics(), @@ -151,8 +155,8 @@ class MemoriesViewState extends State { final gaIndex = orderedByMonth[months[index]]![gIndex]; return MemoriesItemThumbnail( galleryItem: galleryItems[gaIndex], - onTap: () { - open(context, gaIndex); + onTap: () async { + await open(context, gaIndex); }, ); }, diff --git a/lib/src/views/memories/memories_photo_slider.view.dart b/lib/src/views/memories/memories_photo_slider.view.dart index 8ac1d3a..a1c0103 100644 --- a/lib/src/views/memories/memories_photo_slider.view.dart +++ b/lib/src/views/memories/memories_photo_slider.view.dart @@ -126,8 +126,8 @@ class _MemoriesPhotoSliderViewState extends State { children: [ FilledButton.icon( icon: const FaIcon(FontAwesomeIcons.solidPaperPlane), - onPressed: () { - Navigator.push( + onPressed: () async { + await Navigator.push( context, MaterialPageRoute( builder: (context) => ShareImageEditorView( @@ -144,13 +144,16 @@ class _MemoriesPhotoSliderViewState extends State { ); }, style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric( - vertical: 10, horizontal: 30), + padding: WidgetStateProperty.all( + const EdgeInsets.symmetric( + vertical: 10, + horizontal: 30, ), - backgroundColor: WidgetStateProperty.all( - Theme.of(context).colorScheme.primary, - )), + ), + backgroundColor: WidgetStateProperty.all( + Theme.of(context).colorScheme.primary, + ), + ), label: Text( context.lang.shareImagedEditorSendImage, style: const TextStyle(fontSize: 17), @@ -173,12 +176,12 @@ class _MemoriesPhotoSliderViewState extends State { Positioned( right: 5, child: PopupMenuButton( - onSelected: (String result) { + onSelected: (String result) async { if (result == 'delete') { - deleteFile(); + await deleteFile(); } if (result == 'export') { - exportFile(); + await exportFile(); } }, itemBuilder: (BuildContext context) => @@ -197,7 +200,7 @@ class _MemoriesPhotoSliderViewState extends State { // ), ], ), - ) + ), ], ), ), diff --git a/lib/src/views/onboarding/recover.view.dart b/lib/src/views/onboarding/recover.view.dart index 67e312c..4590aef 100644 --- a/lib/src/views/onboarding/recover.view.dart +++ b/lib/src/views/onboarding/recover.view.dart @@ -63,8 +63,8 @@ class _BackupRecoveryViewState extends State { title: Text('twonly Safe ${context.lang.twonlySafeRecoverTitle}'), actions: [ IconButton( - onPressed: () { - showAlertDialog( + onPressed: () async { + await showAlertDialog( context, 'twonly Safe', context.lang.backupTwonlySafeLongDesc, @@ -72,7 +72,7 @@ class _BackupRecoveryViewState extends State { }, icon: const FaIcon(FontAwesomeIcons.circleInfo), iconSize: 18, - ) + ), ], ), body: Padding( @@ -128,17 +128,21 @@ class _BackupRecoveryViewState extends State { size: 16, ), ), - ) + ), ], ), const SizedBox(height: 30), Center( child: OutlinedButton( onPressed: () async { - backupServer = await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const TwonlySafeServerView(); - })); + backupServer = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const TwonlySafeServerView(); + }, + ), + ); setState(() {}); }, child: Text(context.lang.backupExpertSettings), @@ -146,17 +150,18 @@ class _BackupRecoveryViewState extends State { ), const SizedBox(height: 10), Center( - child: FilledButton.icon( - onPressed: (!isLoading) ? _recoverTwonlySafe : null, - icon: isLoading - ? const SizedBox( - height: 12, - width: 12, - child: CircularProgressIndicator(strokeWidth: 1), - ) - : const Icon(Icons.lock_clock_rounded), - label: Text(context.lang.twonlySafeRecoverBtn), - )) + child: FilledButton.icon( + onPressed: (!isLoading) ? _recoverTwonlySafe : null, + icon: isLoading + ? const SizedBox( + height: 12, + width: 12, + child: CircularProgressIndicator(strokeWidth: 1), + ) + : const Icon(Icons.lock_clock_rounded), + label: Text(context.lang.twonlySafeRecoverBtn), + ), + ), ], ), ), diff --git a/lib/src/views/onboarding/register.view.dart b/lib/src/views/onboarding/register.view.dart index 748e247..84edc97 100644 --- a/lib/src/views/onboarding/register.view.dart +++ b/lib/src/views/onboarding/register.view.dart @@ -105,92 +105,93 @@ class _RegisterViewState extends State { title: const Text(''), ), body: Padding( - padding: const EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.only(left: 10, right: 10), - child: ListView( - children: [ - const SizedBox(height: 50), - Text( - context.lang.registerTitle, + padding: const EdgeInsets.all(10), + child: Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: ListView( + children: [ + const SizedBox(height: 50), + Text( + context.lang.registerTitle, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 30), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Text( + context.lang.registerSlogan, textAlign: TextAlign.center, - style: const TextStyle(fontSize: 30), + style: const TextStyle(fontSize: 12), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), + ), + const SizedBox(height: 60), + Center( + child: Padding( + padding: const EdgeInsets.only(left: 10, right: 10), child: Text( - context.lang.registerSlogan, + context.lang.registerUsernameSlogan, textAlign: TextAlign.center, - style: const TextStyle(fontSize: 12), + style: const TextStyle(fontSize: 15), ), ), - const SizedBox(height: 60), - Center( - child: Padding( - padding: const EdgeInsets.only(left: 10, right: 10), - child: Text( - context.lang.registerUsernameSlogan, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 15), - ), - ), + ), + const SizedBox(height: 15), + TextField( + controller: usernameController, + onChanged: (value) { + usernameController.text = value.toLowerCase(); + usernameController.selection = TextSelection.fromPosition( + TextPosition(offset: usernameController.text.length), + ); + setState(() { + _isValidUserName = usernameController.text.length >= 3; + }); + }, + inputFormatters: [ + LengthLimitingTextInputFormatter(12), + FilteringTextInputFormatter.allow(RegExp('[a-z0-9A-Z]')), + ], + style: const TextStyle(fontSize: 17), + decoration: getInputDecoration( + context.lang.registerUsernameDecoration, ), - const SizedBox(height: 15), - TextField( - controller: usernameController, - onChanged: (value) { - usernameController.text = value.toLowerCase(); - usernameController.selection = TextSelection.fromPosition( - TextPosition(offset: usernameController.text.length), - ); - setState(() { - _isValidUserName = usernameController.text.length >= 3; - }); - }, - inputFormatters: [ - LengthLimitingTextInputFormatter(12), - FilteringTextInputFormatter.allow(RegExp('[a-z0-9A-Z]')), - ], - style: const TextStyle(fontSize: 17), - decoration: getInputDecoration( - context.lang.registerUsernameDecoration, - ), + ), + const SizedBox(height: 10), + Text( + context.lang.registerUsernameLimits, + style: TextStyle( + color: _showUserNameError ? Colors.red : Colors.transparent, + fontSize: 12, ), - const SizedBox(height: 10), - Text( - context.lang.registerUsernameLimits, - style: TextStyle( - color: _showUserNameError ? Colors.red : Colors.transparent, - fontSize: 12, - ), - textAlign: TextAlign.center, - ), - // const SizedBox(height: 5), - // Center( - // child: Padding( - // padding: EdgeInsets.only(left: 10, right: 10), - // child: Text( - // context.lang.registerUsernameLimits, - // textAlign: TextAlign.center, - // style: const TextStyle(fontSize: 9), - // ), - // ), - // ), - // const SizedBox(height: 30), - // Center( - // child: Text( - // context.lang.registerTwonlyCodeText, - // textAlign: TextAlign.center, - // ), - // ), - // const SizedBox(height: 10), - // TextField( - // controller: inviteCodeController, - // decoration: - // getInputDecoration(context.lang.registerTwonlyCodeLabel), - // ), - const SizedBox(height: 30), - Column(children: [ + textAlign: TextAlign.center, + ), + // const SizedBox(height: 5), + // Center( + // child: Padding( + // padding: EdgeInsets.only(left: 10, right: 10), + // child: Text( + // context.lang.registerUsernameLimits, + // textAlign: TextAlign.center, + // style: const TextStyle(fontSize: 9), + // ), + // ), + // ), + // const SizedBox(height: 30), + // Center( + // child: Text( + // context.lang.registerTwonlyCodeText, + // textAlign: TextAlign.center, + // ), + // ), + // const SizedBox(height: 10), + // TextField( + // controller: inviteCodeController, + // decoration: + // getInputDecoration(context.lang.registerTwonlyCodeLabel), + // ), + const SizedBox(height: 30), + Column( + children: [ FilledButton.icon( icon: _isTryingToRegister ? const SizedBox( @@ -204,14 +205,18 @@ class _RegisterViewState extends State { : const Icon(Icons.group), onPressed: createNewUser, style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric( - vertical: 10, horizontal: 30), + padding: WidgetStateProperty.all( + const EdgeInsets.symmetric( + vertical: 10, + horizontal: 30, ), - backgroundColor: _isTryingToRegister - ? WidgetStateProperty.all( - Colors.grey) - : null), + ), + backgroundColor: _isTryingToRegister + ? WidgetStateProperty.all( + Colors.grey, + ) + : null, + ), label: Text( context.lang.registerSubmitButton, style: const TextStyle(fontSize: 17), @@ -222,22 +227,27 @@ class _RegisterViewState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ OutlinedButton.icon( - onPressed: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const BackupRecoveryView(); - }, - )); + onPressed: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const BackupRecoveryView(); + }, + ), + ); }, label: Text(context.lang.twonlySafeRecoverBtn), ), ], ), - ]), - // ), - ], - ), - )), + ], + ), + // ), + ], + ), + ), + ), ); } } diff --git a/lib/src/views/settings/account.view.dart b/lib/src/views/settings/account.view.dart index 085be3d..37af702 100644 --- a/lib/src/views/settings/account.view.dart +++ b/lib/src/views/settings/account.view.dart @@ -26,17 +26,19 @@ class _AccountViewState extends State { @override void initState() { - initAsync(); super.initState(); + unawaited(initAsync()); } Future initAsync() async { final ballance = await loadPlanBalance(useCache: false); if (ballance == null || !mounted) return; var ballanceInCents = ballance.transactions - .where((x) => - x.transactionType != Response_TransactionTypes.ThanksForTesting || - kDebugMode) + .where( + (x) => + x.transactionType != Response_TransactionTypes.ThanksForTesting || + kDebugMode, + ) .map((a) => a.depositCents.toInt()) .sum; if (ballanceInCents < 0) { @@ -93,9 +95,11 @@ class _AccountViewState extends State { subtitle: (formattedBallance == null) ? Text(context.lang.settingsAccountDeleteAccountNoInternet) : hasRemainingBallance - ? Text(context.lang - .settingsAccountDeleteAccountWithBallance( - formattedBallance!)) + ? Text( + context.lang.settingsAccountDeleteAccountWithBallance( + formattedBallance!, + ), + ) : Text(context.lang.settingsAccountDeleteAccountNoBallance), onLongPress: kDebugMode ? () async { @@ -110,12 +114,16 @@ class _AccountViewState extends State { ? null : () async { if (hasRemainingBallance) { - final canGoNext = await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return RefundCreditsView( - formattedBalance: formattedBallance!, - ); - })) as bool?; + final canGoNext = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return RefundCreditsView( + formattedBalance: formattedBallance!, + ); + }, + ), + ) as bool?; unawaited(initAsync()); if (canGoNext == null || !canGoNext) return; } diff --git a/lib/src/views/settings/account/refund_credits.view.dart b/lib/src/views/settings/account/refund_credits.view.dart index 2f6ea2e..1e997bf 100644 --- a/lib/src/views/settings/account/refund_credits.view.dart +++ b/lib/src/views/settings/account/refund_credits.view.dart @@ -34,10 +34,14 @@ class _RefundCreditsViewState extends State { ListTile( title: const Text('Create a Voucher'), onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const VoucherView(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const VoucherView(); + }, + ), + ); if (context.mounted) { Navigator.pop(context, false); } diff --git a/lib/src/views/settings/appearance.view.dart b/lib/src/views/settings/appearance.view.dart index 00685a4..34e0688 100644 --- a/lib/src/views/settings/appearance.view.dart +++ b/lib/src/views/settings/appearance.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:twonly/src/providers/settings.provider.dart'; @@ -18,7 +20,7 @@ class _AppearanceViewState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -99,10 +101,12 @@ class _AppearanceViewState extends State { children: [ ListTile( title: Text(context.lang.settingsAppearanceTheme), - subtitle: Text(selectedTheme.name, - style: const TextStyle(color: Colors.grey)), - onTap: () { - _showSelectThemeMode(context); + subtitle: Text( + selectedTheme.name, + style: const TextStyle(color: Colors.grey), + ), + onTap: () async { + await _showSelectThemeMode(context); }, ), ListTile( diff --git a/lib/src/views/settings/backup/backup.view.dart b/lib/src/views/settings/backup/backup.view.dart index f3d2f5e..87daaab 100644 --- a/lib/src/views/settings/backup/backup.view.dart +++ b/lib/src/views/settings/backup/backup.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/model/json/userdata.dart'; @@ -34,8 +36,8 @@ class _BackupViewState extends State { @override void initState() { - initAsync(); super.initState(); + unawaited(initAsync()); gUpdateBackupView = initAsync; } @@ -113,7 +115,9 @@ class _BackupViewState extends State { ( context.lang.backupLastBackupDate, formatDateTime( - context, twonlySafeBackup!.lastBackupDone) + context, + twonlySafeBackup!.lastBackupDone, + ) ), ( context.lang.backupLastBackupSize, @@ -122,7 +126,7 @@ class _BackupViewState extends State { ( context.lang.backupLastBackupResult, backupStatus(twonlySafeBackup!.backupUploadState) - ) + ), ].map((pair) { return TableRow( children: [ @@ -159,15 +163,16 @@ class _BackupViewState extends State { }); }, child: Text(context.lang.backupTwonlySaveNow), - ) + ), ], ), onTap: () async { if (twonlySafeBackup != null) { final disable = await showAlertDialog( - context, - context.lang.deleteBackupTitle, - context.lang.deleteBackupBody); + context, + context.lang.deleteBackupTitle, + context.lang.deleteBackupBody, + ); if (disable) { await disableTwonlySafe(); } @@ -175,10 +180,14 @@ class _BackupViewState extends State { setState(() { isLoading = true; }); - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const TwonlyIdentityBackupView(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const TwonlyIdentityBackupView(); + }, + ), + ); } await initAsync(); }, @@ -195,7 +204,8 @@ class _BackupViewState extends State { showSelectedLabels: true, showUnselectedLabels: true, unselectedIconTheme: IconThemeData( - color: Theme.of(context).colorScheme.inverseSurface.withAlpha(150)), + color: Theme.of(context).colorScheme.inverseSurface.withAlpha(150), + ), selectedIconTheme: IconThemeData(color: Theme.of(context).colorScheme.inverseSurface), items: [ @@ -208,15 +218,14 @@ class _BackupViewState extends State { label: context.lang.backupData, ), ], - onTap: (int index) { + onTap: (int index) async { activePageIdx = index; - setState(() { - pageController.animateToPage( - index, - duration: const Duration(milliseconds: 100), - curve: Curves.bounceIn, - ); - }); + await pageController.animateToPage( + index, + duration: const Duration(milliseconds: 100), + curve: Curves.bounceIn, + ); + if (mounted) setState(() {}); }, currentIndex: activePageIdx, // ), @@ -271,7 +280,7 @@ class BackupOption extends StatelessWidget { onPressed: onTap, child: Text(context.lang.enable), ), - ) + ), ], ), ), diff --git a/lib/src/views/settings/backup/twonly_safe_backup.view.dart b/lib/src/views/settings/backup/twonly_safe_backup.view.dart index 795d661..7d7915f 100644 --- a/lib/src/views/settings/backup/twonly_safe_backup.view.dart +++ b/lib/src/views/settings/backup/twonly_safe_backup.view.dart @@ -66,8 +66,8 @@ class _TwonlyIdentityBackupViewState extends State { title: const Text('twonly Safe'), actions: [ IconButton( - onPressed: () { - showAlertDialog( + onPressed: () async { + await showAlertDialog( context, 'twonly Safe', context.lang.backupTwonlySafeLongDesc, @@ -75,7 +75,7 @@ class _TwonlyIdentityBackupViewState extends State { }, icon: const FaIcon(FontAwesomeIcons.circleInfo), iconSize: 18, - ) + ), ], ), body: Padding( @@ -119,7 +119,7 @@ class _TwonlyIdentityBackupViewState extends State { size: 16, ), ), - ) + ), ], ), Padding( @@ -127,11 +127,12 @@ class _TwonlyIdentityBackupViewState extends State { child: Text( context.lang.backupPasswordRequirement, style: TextStyle( - fontSize: 13, - color: (passwordCtrl.text.length < 8 && - passwordCtrl.text.isNotEmpty) - ? Colors.red - : Colors.transparent), + fontSize: 13, + color: (passwordCtrl.text.length < 8 && + passwordCtrl.text.isNotEmpty) + ? Colors.red + : Colors.transparent, + ), ), ), const SizedBox(height: 5), @@ -152,42 +153,49 @@ class _TwonlyIdentityBackupViewState extends State { child: Text( context.lang.passwordRepeatedNotEqual, style: TextStyle( - fontSize: 13, - color: (passwordCtrl.text != repeatedPasswordCtrl.text && - repeatedPasswordCtrl.text.isNotEmpty) - ? Colors.red - : Colors.transparent), + fontSize: 13, + color: (passwordCtrl.text != repeatedPasswordCtrl.text && + repeatedPasswordCtrl.text.isNotEmpty) + ? Colors.red + : Colors.transparent, + ), ), ), const SizedBox(height: 10), Center( child: OutlinedButton( - onPressed: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return const TwonlySafeServerView(); - })); + onPressed: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const TwonlySafeServerView(); + }, + ), + ); }, child: Text(context.lang.backupExpertSettings), ), ), const SizedBox(height: 10), Center( - child: FilledButton.icon( - onPressed: (!isLoading && - (passwordCtrl.text == repeatedPasswordCtrl.text && - passwordCtrl.text.length >= 8 || - kDebugMode)) - ? onPressedEnableTwonlySafe - : null, - icon: isLoading - ? const SizedBox( - height: 12, - width: 12, - child: CircularProgressIndicator(strokeWidth: 1), - ) - : const Icon(Icons.lock_clock_rounded), - label: Text(context.lang.backupEnableBackup), - )) + child: FilledButton.icon( + onPressed: (!isLoading && + (passwordCtrl.text == repeatedPasswordCtrl.text && + passwordCtrl.text.length >= 8 || + kDebugMode)) + ? onPressedEnableTwonlySafe + : null, + icon: isLoading + ? const SizedBox( + height: 12, + width: 12, + child: CircularProgressIndicator(strokeWidth: 1), + ) + : const Icon(Icons.lock_clock_rounded), + label: Text(context.lang.backupEnableBackup), + ), + ), ], ), ), diff --git a/lib/src/views/settings/backup/twonly_safe_server.view.dart b/lib/src/views/settings/backup/twonly_safe_server.view.dart index e2e3aa2..973f87a 100644 --- a/lib/src/views/settings/backup/twonly_safe_server.view.dart +++ b/lib/src/views/settings/backup/twonly_safe_server.view.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_dynamic_calls +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -25,7 +26,7 @@ class _TwonlySafeServerViewState extends State { void initState() { _urlController.text = 'https://'; super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -86,7 +87,8 @@ class _TwonlySafeServerViewState extends State { } else { // If the server did not return a 200 OK response, throw an exception. throw Exception( - 'Got invalid status code ${response.statusCode} from server.'); + 'Got invalid status code ${response.statusCode} from server.', + ); } } catch (e) { Log.error('$e'); @@ -172,7 +174,7 @@ class _TwonlySafeServerViewState extends State { }, child: Text(context.lang.backupResetServer), ), - ) + ), ], ), ), diff --git a/lib/src/views/settings/chat/chat_reactions.view.dart b/lib/src/views/settings/chat/chat_reactions.view.dart index ce1acf1..086d44a 100644 --- a/lib/src/views/settings/chat/chat_reactions.view.dart +++ b/lib/src/views/settings/chat/chat_reactions.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; @@ -17,7 +19,7 @@ class _ChatReactionSelectionView extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -66,8 +68,8 @@ class _ChatReactionSelectionView extends State { itemBuilder: (context, index) { final emoji = EmojiAnimation.animatedIcons.keys.elementAt(index); return GestureDetector( - onTap: () { - _onEmojiSelected(emoji); + onTap: () async { + await _onEmojiSelected(emoji); }, child: Card( color: selectedEmojis.contains(emoji) diff --git a/lib/src/views/settings/chat/chat_settings.view.dart b/lib/src/views/settings/chat/chat_settings.view.dart index f37b2ed..1a20c27 100644 --- a/lib/src/views/settings/chat/chat_settings.view.dart +++ b/lib/src/views/settings/chat/chat_settings.view.dart @@ -26,10 +26,14 @@ class _ChatSettingsViewState extends State { ListTile( title: Text(context.lang.settingsPreSelectedReactions), onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const ChatReactionSelectionView(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const ChatReactionSelectionView(); + }, + ), + ); }, ), ], diff --git a/lib/src/views/settings/data_and_storage.view.dart b/lib/src/views/settings/data_and_storage.view.dart index 559c50b..4c21c75 100644 --- a/lib/src/views/settings/data_and_storage.view.dart +++ b/lib/src/views/settings/data_and_storage.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:twonly/src/services/api/media_download.dart'; @@ -18,7 +20,6 @@ class _DataAndStorageViewState extends State { @override void initState() { super.initState(); - initAsync(); } Future initAsync() async { @@ -32,7 +33,9 @@ class _DataAndStorageViewState extends State { } Future showAutoDownloadOptions( - BuildContext context, ConnectivityResult connectionMode) async { + BuildContext context, + ConnectivityResult connectionMode, + ) async { // ignore: inference_failure_on_function_invocation await showDialog( context: context, @@ -86,8 +89,8 @@ class _DataAndStorageViewState extends State { autoDownloadOptions[ConnectivityResult.mobile.name]!.join(', '), style: const TextStyle(color: Colors.grey), ), - onTap: () { - showAutoDownloadOptions(context, ConnectivityResult.mobile); + onTap: () async { + await showAutoDownloadOptions(context, ConnectivityResult.mobile); }, ), ListTile( @@ -96,8 +99,8 @@ class _DataAndStorageViewState extends State { autoDownloadOptions[ConnectivityResult.wifi.name]!.join(', '), style: const TextStyle(color: Colors.grey), ), - onTap: () { - showAutoDownloadOptions(context, ConnectivityResult.wifi); + onTap: () async { + await showAutoDownloadOptions(context, ConnectivityResult.wifi); }, ), ], @@ -168,7 +171,9 @@ class _AutoDownloadOptionsDialogState extends State { } Future _updateAutoDownloadSetting( - DownloadMediaTypes type, bool? value) async { + DownloadMediaTypes type, + bool? value, + ) async { if (value == null) return; // Update the autoDownloadOptions based on the checkbox state diff --git a/lib/src/views/settings/developer/developer.view.dart b/lib/src/views/settings/developer/developer.view.dart new file mode 100644 index 0000000..c2cd49c --- /dev/null +++ b/lib/src/views/settings/developer/developer.view.dart @@ -0,0 +1,83 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:twonly/globals.dart'; +import 'package:twonly/src/services/flame.service.dart'; +import 'package:twonly/src/utils/storage.dart'; +import 'package:twonly/src/views/settings/developer/retransmission_data.view.dart'; + +class DeveloperSettingsView extends StatefulWidget { + const DeveloperSettingsView({super.key}); + + @override + State createState() => _DeveloperSettingsViewState(); +} + +class _DeveloperSettingsViewState extends State { + bool isDeveloper = true; + + @override + void initState() { + super.initState(); + unawaited(initAsync()); + } + + Future initAsync() async { + final user = await getUser(); + if (user == null) return; + setState(() { + isDeveloper = user.isDeveloper; + }); + } + + Future toggleDeveloperSettings() async { + await updateUserdata((u) { + u.isDeveloper = !u.isDeveloper; + return u; + }); + await initAsync(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Developer Settings'), + ), + body: ListView( + children: [ + ListTile( + title: const Text('Show Developer Settings'), + onTap: toggleDeveloperSettings, + trailing: Switch( + value: isDeveloper, + onChanged: (a) => toggleDeveloperSettings(), + ), + ), + ListTile( + title: const Text('Show Retransmission Database'), + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const RetransmissionDataView(); + }, + ), + ); + }, + ), + if (kDebugMode) + ListTile( + title: const Text('FlameSync Test'), + onTap: () async { + await twonlyDB.contactsDao.modifyFlameCounterForTesting(); + await syncFlameCounters(); + }, + ), + ], + ), + ); + } +} diff --git a/lib/src/views/settings/developer/retransmission_data.view.dart b/lib/src/views/settings/developer/retransmission_data.view.dart new file mode 100644 index 0000000..34538fc --- /dev/null +++ b/lib/src/views/settings/developer/retransmission_data.view.dart @@ -0,0 +1,187 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:drift/drift.dart' hide Column; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/globals.dart'; +import 'package:twonly/src/database/twonly_database.dart'; +import 'package:twonly/src/model/json/message.dart'; +import 'package:twonly/src/services/api/messages.dart'; + +class RetransmissionDataView extends StatefulWidget { + const RetransmissionDataView({super.key}); + + @override + State createState() => _RetransmissionDataViewState(); +} + +class RetransMsg { + RetransMsg({ + required this.json, + required this.retrans, + required this.contact, + }); + final MessageJson json; + final MessageRetransmission retrans; + final Contact? contact; + + static List fromRaw( + List retrans, + Map contacts, + ) { + final res = []; + + for (final retrans in retrans) { + final json = MessageJson.fromJson( + jsonDecode( + utf8.decode( + gzip.decode(retrans.plaintextContent), + ), + ) as Map, + ); + res.add( + RetransMsg( + json: json, + retrans: retrans, + contact: contacts[retrans.contactId], + ), + ); + } + return res; + } +} + +class _RetransmissionDataViewState extends State { + List retransmissions = []; + Map contacts = {}; + StreamSubscription>? subscriptionRetransmission; + StreamSubscription>? subscriptionContacts; + List messages = []; + + @override + void initState() { + super.initState(); + unawaited(initAsync()); + } + + @override + void dispose() { + subscriptionRetransmission?.cancel(); + subscriptionContacts?.cancel(); + super.dispose(); + } + + Future initAsync() async { + subscriptionContacts = + twonlyDB.contactsDao.watchAllContacts().listen((updated) { + for (final contact in updated) { + contacts[contact.userId] = contact; + } + if (retransmissions.isNotEmpty) { + messages = RetransMsg.fromRaw(retransmissions, contacts); + } + setState(() {}); + }); + subscriptionRetransmission = + twonlyDB.messageRetransmissionDao.watchAllMessages().listen((updated) { + retransmissions = updated; + if (contacts.isNotEmpty) { + messages = RetransMsg.fromRaw(retransmissions, contacts); + } + setState(() {}); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Retransmission Database'), + ), + body: Column( + children: [ + Expanded( + child: ListView( + children: messages + .map( + (retrans) => ListTile( + title: Text( + '${retrans.retrans.retransmissionId}: ${retrans.json.kind}', + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'To ${retrans.contact?.username}', + ), + Text( + 'Server-Ack: ${retrans.retrans.acknowledgeByServerAt}', + ), + Text( + 'Retry: ${retrans.retrans.retryCount} : ${retrans.retrans.lastRetry}', + ), + ], + ), + trailing: SizedBox( + width: 80, + child: Row( + children: [ + SizedBox( + height: 20, + width: 40, + child: Center( + child: GestureDetector( + onDoubleTap: () async { + await twonlyDB.messageRetransmissionDao + .deleteRetransmissionById( + retrans.retrans.retransmissionId, + ); + }, + child: const FaIcon( + FontAwesomeIcons.trash, + size: 15, + ), + ), + ), + ), + SizedBox( + width: 40, + child: OutlinedButton( + style: ButtonStyle( + padding: WidgetStateProperty.all( + EdgeInsets.zero, + ), + ), + onPressed: () async { + await twonlyDB.messageRetransmissionDao + .updateRetransmission( + retrans.retrans.retransmissionId, + const MessageRetransmissionsCompanion( + acknowledgeByServerAt: Value(null), + ), + ); + await sendRetransmitMessage( + retrans.retrans.retransmissionId, + ); + }, + child: const FaIcon( + FontAwesomeIcons.arrowRotateLeft, + size: 15, + ), + ), + ), + ], + ), + ), + ), + ) + .toList(), + ), + ), + ], + ), + ); + } +} diff --git a/lib/src/views/settings/help/changelog.view.dart b/lib/src/views/settings/help/changelog.view.dart index 68f8d5b..bd2402f 100644 --- a/lib/src/views/settings/help/changelog.view.dart +++ b/lib/src/views/settings/help/changelog.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -22,32 +24,36 @@ List parseMarkdown(BuildContext context, String markdown) { // ), // )); } else if (line.startsWith('## ')) { - widgets.add(Padding( - padding: const EdgeInsets.only(top: 20, bottom: 10), - child: Text( - line.substring(3), // Remove the '## ' part - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + widgets.add( + Padding( + padding: const EdgeInsets.only(top: 20, bottom: 10), + child: Text( + line.substring(3), // Remove the '## ' part + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), ), - )); + ); } // Check for bullet points else if (line.startsWith('- ')) { - widgets.add(Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(top: 7), - child: Icon( - Icons.brightness_1, - size: 7, - color: context.color.onSurface, - ), - ), // Bullet point icon - const SizedBox(width: 8), // Space between bullet and text - Expanded(child: Text(line.substring(2))), // Remove the '- ' part - ], - )); + widgets.add( + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 7), + child: Icon( + Icons.brightness_1, + size: 7, + color: context.color.onSurface, + ), + ), // Bullet point icon + const SizedBox(width: 8), // Space between bullet and text + Expanded(child: Text(line.substring(2))), // Remove the '- ' part + ], + ), + ); } else { widgets.add(Text(line)); } @@ -75,7 +81,7 @@ class _ChangeLogViewState extends State { if (widget.changeLog != null) { changeLog = widget.changeLog!; } else { - initAsync(); + unawaited(initAsync()); } } diff --git a/lib/src/views/settings/help/contact_us.view.dart b/lib/src/views/settings/help/contact_us.view.dart index 2ab0474..6d30400 100644 --- a/lib/src/views/settings/help/contact_us.view.dart +++ b/lib/src/views/settings/help/contact_us.view.dart @@ -65,11 +65,13 @@ class _ContactUsState extends State { ); requestMultipart.headers['x-twonly-auth-token'] = apiAuthToken; - requestMultipart.files.add(http.MultipartFile.fromBytes( - 'file', - uploadRequestBytes, - filename: 'upload', - )); + requestMultipart.files.add( + http.MultipartFile.fromBytes( + 'file', + uploadRequestBytes, + filename: 'upload', + ), + ); final response = await requestMultipart.send(); if (response.statusCode == 200) { @@ -228,8 +230,8 @@ $debugLogToken mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( - onTap: () { - launchUrl(Uri.parse('https://twonly.eu/en/faq/')); + onTap: () async { + await launchUrl(Uri.parse('https://twonly.eu/en/faq/')); }, child: Text( context.lang.contactUsFaq, @@ -245,12 +247,16 @@ $debugLogToken final fullMessage = await _getFeedbackText(); if (!context.mounted) return; - final feedbackSend = await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return SubmitMessage( - fullMessage: fullMessage, - ); - })); + final feedbackSend = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return SubmitMessage( + fullMessage: fullMessage, + ); + }, + ), + ); if (feedbackSend == true && context.mounted) { Navigator.pop(context); diff --git a/lib/src/views/settings/help/credits.view.dart b/lib/src/views/settings/help/credits.view.dart index 49c1a1e..ca50aa8 100644 --- a/lib/src/views/settings/help/credits.view.dart +++ b/lib/src/views/settings/help/credits.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -24,8 +26,8 @@ class UrlListTitle extends StatelessWidget { leading: leading, title: (title != null) ? Text(title!) : null, subtitle: subtitle == null ? null : Text(subtitle!), - onTap: () { - launchUrl(Uri.parse(url)); + onTap: () async { + await launchUrl(Uri.parse(url)); }, trailing: const FaIcon(FontAwesomeIcons.arrowUpRightFromSquare, size: 15), ); @@ -45,7 +47,7 @@ class _CreditsViewState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -83,10 +85,11 @@ class _CreditsViewState extends State { const Divider(), const ListTile( title: Center( - child: Text( - 'Animations', - style: TextStyle(fontWeight: FontWeight.bold), - )), + child: Text( + 'Animations', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), ), const UrlListTitle( title: 'selfie fast Animation', @@ -144,10 +147,11 @@ class _CreditsViewState extends State { if (sticker.isNotEmpty) const ListTile( title: Center( - child: Text( - 'Filters', - style: TextStyle(fontWeight: FontWeight.bold), - )), + child: Text( + 'Filters', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), ), ...sticker.map( (x) => UrlListTitle( @@ -155,7 +159,8 @@ class _CreditsViewState extends State { height: 50, width: 50, child: CachedNetworkImage( - imageUrl: 'https://twonly.eu/${x.imageSrc}'), + imageUrl: 'https://twonly.eu/${x.imageSrc}', + ), ), title: '', url: x.source, diff --git a/lib/src/views/settings/help/diagnostics.view.dart b/lib/src/views/settings/help/diagnostics.view.dart index 67c8317..7f27dd8 100644 --- a/lib/src/views/settings/help/diagnostics.view.dart +++ b/lib/src/views/settings/help/diagnostics.view.dart @@ -14,9 +14,9 @@ class DiagnosticsView extends StatefulWidget { class _DiagnosticsViewState extends State { final ScrollController _scrollController = ScrollController(); - void _scrollToBottom() { + Future _scrollToBottom() async { // Assuming the button is at the bottom of the scroll view - _scrollController.animateTo( + await _scrollController.animateTo( _scrollController.position.maxScrollExtent, // Scroll to the bottom duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, @@ -66,11 +66,13 @@ class _DiagnosticsViewState extends State { if (result.status != ShareResultStatus.success) { await Clipboard.setData( - ClipboardData(text: logText)); + ClipboardData(text: logText), + ); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Log copied to clipboard!')), + content: Text('Log copied to clipboard!'), + ), ); } } @@ -87,13 +89,15 @@ class _DiagnosticsViewState extends State { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Log file deleted!')), + content: Text('Log file deleted!'), + ), ); } else { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Log file does not exist.')), + content: Text('Log file does not exist.'), + ), ); } }, diff --git a/lib/src/views/settings/help/faq.view.dart b/lib/src/views/settings/help/faq.view.dart index 299eeca..01b114a 100644 --- a/lib/src/views/settings/help/faq.view.dart +++ b/lib/src/views/settings/help/faq.view.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_dynamic_calls, inference_failure_on_untyped_parameter +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; @@ -24,7 +25,7 @@ class _FaqViewState extends State { void initState() { super.initState(); domain = 'https://twonly.eu'; - _fetchFAQData(); + unawaited(_fetchFAQData()); } Future _fetchFAQData() async { diff --git a/lib/src/views/settings/help/help.view.dart b/lib/src/views/settings/help/help.view.dart index 9c8476e..a0bef20 100644 --- a/lib/src/views/settings/help/help.view.dart +++ b/lib/src/views/settings/help/help.view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:twonly/globals.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; @@ -24,18 +23,28 @@ class HelpView extends StatelessWidget { children: [ ListTile( title: Text(context.lang.settingsHelpFAQ), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return const FaqView(); - })); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const FaqView(); + }, + ), + ); }, ), ListTile( title: Text(context.lang.settingsHelpContactUs), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return const ContactUsView(); - })); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const ContactUsView(); + }, + ), + ); }, ), ListTile( @@ -77,50 +86,65 @@ class HelpView extends StatelessWidget { ), ListTile( title: Text(context.lang.settingsHelpCredits), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return const CreditsView(); - })); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const CreditsView(); + }, + ), + ); }, ), ListTile( title: Text(context.lang.settingsHelpDiagnostics), onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const DiagnosticsView(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const DiagnosticsView(); + }, + ), + ); }, ), ListTile( title: const Text('Changelog'), onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const ChangeLogView(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const ChangeLogView(); + }, + ), + ); }, ), ListTile( title: const Text('Open Source'), - onTap: () { - launchUrl(Uri.parse('https://github.com/twonlyapp/twonly-app')); + onTap: () async { + await launchUrl( + Uri.parse('https://github.com/twonlyapp/twonly-app'), + ); }, trailing: const FaIcon(FontAwesomeIcons.arrowUpRightFromSquare, size: 15), ), ListTile( title: Text(context.lang.settingsHelpImprint), - onTap: () { - launchUrl(Uri.parse('https://twonly.eu/de/legal/')); + onTap: () async { + await launchUrl(Uri.parse('https://twonly.eu/de/legal/')); }, trailing: const FaIcon(FontAwesomeIcons.arrowUpRightFromSquare, size: 15), ), ListTile( title: Text(context.lang.settingsHelpTerms), - onTap: () { - launchUrl(Uri.parse('https://twonly.eu/de/legal/agb.html')); + onTap: () async { + await launchUrl(Uri.parse('https://twonly.eu/de/legal/agb.html')); }, trailing: const FaIcon(FontAwesomeIcons.arrowUpRightFromSquare, size: 15), @@ -129,12 +153,14 @@ class HelpView extends StatelessWidget { onLongPress: () async { final okay = await showAlertDialog( context, - 'Delete Retransmission messages', - 'Only do this if you know what you are doing :)', + 'Developer Settings', + 'Do you want to enable the developer settings?', ); - if (okay == true) { - await twonlyDB.messageRetransmissionDao - .clearRetransmissionTable(); + if (okay) { + await updateUserdata((u) { + u.isDeveloper = true; + return u; + }); } }, title: const Text( diff --git a/lib/src/views/settings/notification.view.dart b/lib/src/views/settings/notification.view.dart index a4518a6..db9be48 100644 --- a/lib/src/views/settings/notification.view.dart +++ b/lib/src/views/settings/notification.view.dart @@ -44,9 +44,10 @@ class NotificationView extends StatelessWidget { ); } else { final run = await showAlertDialog( - context, - context.lang.settingsNotifyTroubleshootingNoProblem, - context.lang.settingsNotifyTroubleshootingNoProblemDesc); + context, + context.lang.settingsNotifyTroubleshootingNoProblem, + context.lang.settingsNotifyTroubleshootingNoProblemDesc, + ); if (run) { final user = await getUser(); diff --git a/lib/src/views/settings/privacy.view.dart b/lib/src/views/settings/privacy.view.dart index 0611bfa..c3c50c3 100644 --- a/lib/src/views/settings/privacy.view.dart +++ b/lib/src/views/settings/privacy.view.dart @@ -41,10 +41,14 @@ class _PrivacyViewState extends State { }, ), onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const PrivacyViewBlockUsers(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const PrivacyViewBlockUsers(); + }, + ), + ); }, ), ], diff --git a/lib/src/views/settings/privacy_view_block.users.dart b/lib/src/views/settings/privacy_view_block.users.dart index b98ffa0..ce1dd86 100644 --- a/lib/src/views/settings/privacy_view_block.users.dart +++ b/lib/src/views/settings/privacy_view_block.users.dart @@ -24,11 +24,6 @@ class _PrivacyViewBlockUsers extends State { void initState() { super.initState(); allUsers = twonlyDB.contactsDao.watchAllContacts(); - loadAsync(); - } - - Future loadAsync() async { - setState(() {}); } @override @@ -81,7 +76,7 @@ class _PrivacyViewBlockUsers extends State { ); }, ), - ) + ), ], ), ), @@ -105,7 +100,8 @@ class UserList extends StatelessWidget { Widget build(BuildContext context) { // Step 1: Sort the users alphabetically users.sort( - (a, b) => getContactDisplayName(a).compareTo(getContactDisplayName(b))); + (a, b) => getContactDisplayName(a).compareTo(getContactDisplayName(b)), + ); return ListView.builder( restorationId: 'new_message_users_list', @@ -115,18 +111,20 @@ class UserList extends StatelessWidget { return UserContextMenuBlocked( contact: user, child: ListTile( - title: Row(children: [ - Text(getContactDisplayName(user)), - ]), + title: Row( + children: [ + Text(getContactDisplayName(user)), + ], + ), leading: ContactAvatar(contact: user, fontSize: 15), trailing: Checkbox( value: user.blocked, - onChanged: (bool? value) { - block(context, user.userId, value); + onChanged: (bool? value) async { + await block(context, user.userId, value); }, ), - onTap: () { - block(context, user.userId, !user.blocked); + onTap: () async { + await block(context, user.userId, !user.blocked); }, ), ); diff --git a/lib/src/views/settings/profile/modify_avatar.view.dart b/lib/src/views/settings/profile/modify_avatar.view.dart index acb08f2..0a489da 100644 --- a/lib/src/views/settings/profile/modify_avatar.view.dart +++ b/lib/src/views/settings/profile/modify_avatar.view.dart @@ -119,9 +119,10 @@ class _ModifyAvatarState extends State { ), IconButton( icon: const Icon(FontAwesomeIcons.rotateLeft), - onLongPress: () { - PersistentAvatarMakerController.clearAvatarMaker(); - _avatarMakerController.restoreState(); + onLongPress: () async { + await PersistentAvatarMakerController + .clearAvatarMaker(); + await _avatarMakerController.restoreState(); }, onPressed: _avatarMakerController.restoreState, ), diff --git a/lib/src/views/settings/profile/profile.view.dart b/lib/src/views/settings/profile/profile.view.dart index 3e83c43..104bde0 100644 --- a/lib/src/views/settings/profile/profile.view.dart +++ b/lib/src/views/settings/profile/profile.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:avatar_maker/avatar_maker.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -23,7 +25,7 @@ class _ProfileViewState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -63,18 +65,19 @@ class _ProfileViewState extends State { child: SizedBox( height: 35, child: ElevatedButton.icon( - icon: const Icon(Icons.edit), - label: Text(context.lang.settingsProfileCustomizeAvatar), - onPressed: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ModifyAvatar(), - ), - ); - await _avatarMakerController.performRestore(); - setState(() {}); - }), + icon: const Icon(Icons.edit), + label: Text(context.lang.settingsProfileCustomizeAvatar), + onPressed: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ModifyAvatar(), + ), + ); + await _avatarMakerController.performRestore(); + setState(() {}); + }, + ), ), ), const SizedBox(height: 20), @@ -98,7 +101,9 @@ class _ProfileViewState extends State { } Future showDisplayNameChangeDialog( - BuildContext context, String currentName) { + BuildContext context, + String currentName, +) { final controller = TextEditingController(text: currentName); return showDialog( @@ -110,7 +115,8 @@ Future showDisplayNameChangeDialog( controller: controller, autofocus: true, decoration: InputDecoration( - hintText: context.lang.settingsProfileEditDisplayNameNew), + hintText: context.lang.settingsProfileEditDisplayNameNew, + ), ), actions: [ TextButton( diff --git a/lib/src/views/settings/settings_main.view.dart b/lib/src/views/settings/settings_main.view.dart index 0d22132..b1ffe1c 100644 --- a/lib/src/views/settings/settings_main.view.dart +++ b/lib/src/views/settings/settings_main.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/model/json/userdata.dart'; @@ -10,6 +12,7 @@ import 'package:twonly/src/views/settings/appearance.view.dart'; import 'package:twonly/src/views/settings/backup/backup.view.dart'; import 'package:twonly/src/views/settings/chat/chat_settings.view.dart'; import 'package:twonly/src/views/settings/data_and_storage.view.dart'; +import 'package:twonly/src/views/settings/developer/developer.view.dart'; import 'package:twonly/src/views/settings/help/help.view.dart'; import 'package:twonly/src/views/settings/notification.view.dart'; import 'package:twonly/src/views/settings/privacy.view.dart'; @@ -30,7 +33,7 @@ class _SettingsMainViewState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -55,11 +58,14 @@ class _SettingsMainViewState extends State { Expanded( child: GestureDetector( onTap: () async { - await Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const ProfileView(); - }, - )); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const ProfileView(); + }, + ), + ); await initAsync(); }, child: ColoredBox( @@ -106,114 +112,159 @@ class _SettingsMainViewState extends State { BetterListTile( icon: FontAwesomeIcons.user, text: context.lang.settingsAccount, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const AccountView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const AccountView(); + }, + ), + ); }, ), BetterListTile( icon: FontAwesomeIcons.shieldHeart, text: context.lang.settingsSubscription, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const SubscriptionView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const SubscriptionView(); + }, + ), + ); }, ), BetterListTile( icon: Icons.lock_clock_rounded, text: context.lang.settingsBackup, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const BackupView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const BackupView(); + }, + ), + ); }, ), const Divider(), BetterListTile( icon: FontAwesomeIcons.sun, text: context.lang.settingsAppearance, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const AppearanceView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const AppearanceView(); + }, + ), + ); }, ), BetterListTile( icon: FontAwesomeIcons.comment, text: context.lang.settingsChats, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const ChatSettingsView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const ChatSettingsView(); + }, + ), + ); }, ), BetterListTile( icon: FontAwesomeIcons.lock, text: context.lang.settingsPrivacy, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const PrivacyView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const PrivacyView(); + }, + ), + ); }, ), BetterListTile( icon: FontAwesomeIcons.bell, text: context.lang.settingsNotification, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const NotificationView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const NotificationView(); + }, + ), + ); }, ), BetterListTile( icon: FontAwesomeIcons.chartPie, iconSize: 15, text: context.lang.settingsStorageData, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const DataAndStorageView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const DataAndStorageView(); + }, + ), + ); }, ), const Divider(), BetterListTile( icon: FontAwesomeIcons.circleQuestion, text: context.lang.settingsHelp, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const HelpView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const HelpView(); + }, + ), + ); }, ), + if (userData != null && userData!.isDeveloper) + BetterListTile( + icon: FontAwesomeIcons.code, + text: 'Developer Settings', + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const DeveloperSettingsView(); + }, + ), + ); + }, + ), BetterListTile( icon: FontAwesomeIcons.shareFromSquare, text: context.lang.inviteFriends, - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return const ShareWithFriendsView(); - }, - )); + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const ShareWithFriendsView(); + }, + ), + ); }, ), ], diff --git a/lib/src/views/settings/subscription/additional_users.view.dart b/lib/src/views/settings/subscription/additional_users.view.dart index b382dd4..1afc62f 100644 --- a/lib/src/views/settings/subscription/additional_users.view.dart +++ b/lib/src/views/settings/subscription/additional_users.view.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -51,7 +52,7 @@ class _AdditionalUsersViewState extends State { void initState() { super.initState(); ballance = widget.ballance; - initAsync(force: false); + unawaited(initAsync(force: false)); } Future initAsync({required bool force}) async { @@ -86,12 +87,14 @@ class _AdditionalUsersViewState extends State { ), ), if (ballance != null) - ...ballance!.additionalAccounts.map((e) => AdditionalAccount( - account: e, - refresh: () { - initAsync(force: true); - }, - )), + ...ballance!.additionalAccounts.map( + (e) => AdditionalAccount( + account: e, + refresh: () async { + await initAsync(force: true); + }, + ), + ), if (plusInvites.isNotEmpty) ListTile( title: Text( @@ -152,7 +155,7 @@ class _AdditionalAccountState extends State { void initState() { super.initState(); username = widget.account.userId.toString(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -181,7 +184,9 @@ class _AdditionalAccountState extends State { Text( username, style: const TextStyle( - fontSize: 20, fontWeight: FontWeight.bold), + fontSize: 20, + fontWeight: FontWeight.bold, + ), ), const SizedBox(height: 4), Text( @@ -194,9 +199,10 @@ class _AdditionalAccountState extends State { icon: const FaIcon(FontAwesomeIcons.userXmark, size: 16), onPressed: () async { final remove = await showAlertDialog( - context, - 'Remove this additional user', - 'The additional user will automatically be downgraded to the preview plan after removal and you will receive a new invitation code to give to another person.'); + context, + 'Remove this additional user', + 'The additional user will automatically be downgraded to the preview plan after removal and you will receive a new invitation code to give to another person.', + ); if (remove) { final res = await apiService .removeAdditionalUser(widget.account.userId); @@ -206,10 +212,13 @@ class _AdditionalAccountState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(errorCodeToText( - context, - res.error as ErrorCode, - ))), + content: Text( + errorCodeToText( + context, + res.error as ErrorCode, + ), + ), + ), ); } } @@ -230,9 +239,10 @@ class AdditionalUserInvite extends StatefulWidget { } class _AdditionalUserInviteState extends State { - void _copyVoucherId() { - Clipboard.setData(ClipboardData(text: widget.invite.inviteCode)); - HapticFeedback.heavyImpact(); + Future _copyVoucherId() async { + await Clipboard.setData(ClipboardData(text: widget.invite.inviteCode)); + await HapticFeedback.heavyImpact(); + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('${widget.invite.inviteCode} copied.')), ); diff --git a/lib/src/views/settings/subscription/checkout.view.dart b/lib/src/views/settings/subscription/checkout.view.dart index ea70f93..174c2d2 100644 --- a/lib/src/views/settings/subscription/checkout.view.dart +++ b/lib/src/views/settings/subscription/checkout.view.dart @@ -123,22 +123,27 @@ class _CheckoutViewState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: FilledButton( - onPressed: () async { - final success = await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return SelectPaymentView( - planId: widget.planId, - payMonthly: paidMonthly, - refund: widget.refund, - ); - })) as bool?; - if (success != null && success && context.mounted) { - Navigator.pop(context); - } - }, - child: Text(context.lang.selectPaymentMethod)), + onPressed: () async { + final success = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return SelectPaymentView( + planId: widget.planId, + payMonthly: paidMonthly, + refund: widget.refund, + ); + }, + ), + ) as bool?; + if (success != null && success && context.mounted) { + Navigator.pop(context); + } + }, + child: Text(context.lang.selectPaymentMethod), + ), ), - const SizedBox(height: 20) + const SizedBox(height: 20), ], ), ), diff --git a/lib/src/views/settings/subscription/manage_subscription.view.dart b/lib/src/views/settings/subscription/manage_subscription.view.dart index 3e2a0db..7fb96ab 100644 --- a/lib/src/views/settings/subscription/manage_subscription.view.dart +++ b/lib/src/views/settings/subscription/manage_subscription.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; @@ -33,7 +35,7 @@ class _ManageSubscriptionViewState extends State { if (ballance != null) { autoRenewal = ballance!.autoRenewal; } - initAsync(true); + unawaited(initAsync(true)); } Future initAsync(bool force) async { @@ -52,7 +54,8 @@ class _ManageSubscriptionViewState extends State { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(errorCodeToText(context, res.error as ErrorCode))), + content: Text(errorCodeToText(context, res.error as ErrorCode)), + ), ); } } @@ -89,8 +92,8 @@ class _ManageSubscriptionViewState extends State { onTap: toggleRenewalOption, trailing: Switch( value: autoRenewal!, - onChanged: (a) { - toggleRenewalOption(); + onChanged: (a) async { + await toggleRenewalOption(); }, ), ), diff --git a/lib/src/views/settings/subscription/select_payment.view.dart b/lib/src/views/settings/subscription/select_payment.view.dart index 834447a..c92f115 100644 --- a/lib/src/views/settings/subscription/select_payment.view.dart +++ b/lib/src/views/settings/subscription/select_payment.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:twonly/globals.dart'; @@ -43,7 +45,7 @@ class _SelectPaymentViewState extends State { void initState() { super.initState(); setCheckout(true); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -121,7 +123,7 @@ class _SelectPaymentViewState extends State { Text( '${context.lang.currentBalance}: ${localePrizing(context, balanceInCents!)}', style: const TextStyle(fontSize: 10), - ) + ), ], ), Checkbox( @@ -154,10 +156,14 @@ class _SelectPaymentViewState extends State { padding: const EdgeInsets.symmetric(horizontal: 16), child: FilledButton( onPressed: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const VoucherView(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const VoucherView(); + }, + ), + ); await initAsync(); }, child: Text(context.lang.chargeCredit), @@ -233,7 +239,10 @@ class _SelectPaymentViewState extends State { onPressed: canPay ? () async { final res = await apiService.switchToPayedPlan( - widget.planId!, widget.payMonthly!, tryAutoRenewal); + widget.planId!, + widget.payMonthly!, + tryAutoRenewal, + ); if (!context.mounted) return; if (res.isSuccess) { await updateUsersPlan(context, widget.planId!); @@ -247,9 +256,13 @@ class _SelectPaymentViewState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - errorCodeToText(context, res.error as ErrorCode), - )), + content: Text( + errorCodeToText( + context, + res.error as ErrorCode, + ), + ), + ), ); } } @@ -261,8 +274,11 @@ class _SelectPaymentViewState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - onPressed: () => launchUrl(Uri.parse( - 'https://twonly.eu/de/legal/#revocation-policy')), + onPressed: () => launchUrl( + Uri.parse( + 'https://twonly.eu/de/legal/#revocation-policy', + ), + ), child: const Text( 'Widerrufsbelehrung', style: TextStyle(color: Colors.blue), @@ -270,15 +286,16 @@ class _SelectPaymentViewState extends State { ), TextButton( onPressed: () => launchUrl( - Uri.parse('https://twonly.eu/de/legal/agb.html')), + Uri.parse('https://twonly.eu/de/legal/agb.html'), + ), child: const Text( 'ABG', style: TextStyle(color: Colors.blue), ), - ) + ), ], ), - const SizedBox(height: 20) + const SizedBox(height: 20), ], ), ), diff --git a/lib/src/views/settings/subscription/subscription.view.dart b/lib/src/views/settings/subscription/subscription.view.dart index 25f967c..bdaeccd 100644 --- a/lib/src/views/settings/subscription/subscription.view.dart +++ b/lib/src/views/settings/subscription/subscription.view.dart @@ -1,4 +1,6 @@ // ignore_for_file: inference_failure_on_instance_creation +import 'dart:async'; + import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -66,8 +68,11 @@ int calculateRefund(Response_PlanBallance current) { if (current.paymentPeriodDays == YEARLY_PAYMENT_DAYS) { final elapsedDays = DateTime.now() - .difference(DateTime.fromMillisecondsSinceEpoch( - current.lastPaymentDoneUnixTimestamp.toInt() * 1000)) + .difference( + DateTime.fromMillisecondsSinceEpoch( + current.lastPaymentDoneUnixTimestamp.toInt() * 1000, + ), + ) .inDays; if (elapsedDays < current.paymentPeriodDays.toInt()) { // User has yearly plan with 10€ @@ -83,8 +88,11 @@ int calculateRefund(Response_PlanBallance current) { } } else { final elapsedDays = DateTime.now() - .difference(DateTime.fromMillisecondsSinceEpoch( - current.lastPaymentDoneUnixTimestamp.toInt() * 1000)) + .difference( + DateTime.fromMillisecondsSinceEpoch( + current.lastPaymentDoneUnixTimestamp.toInt() * 1000, + ), + ) .inDays; if (elapsedDays > 14) { refund = 0; @@ -111,7 +119,7 @@ class _SubscriptionViewState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -140,7 +148,8 @@ class _SubscriptionViewState extends State { if (ballance != null) { final lastPaymentDateTime = DateTime.fromMillisecondsSinceEpoch( - ballance!.lastPaymentDoneUnixTimestamp.toInt() * 1000); + ballance!.lastPaymentDoneUnixTimestamp.toInt() * 1000, + ); if (isPayingUser) { nextPayment = lastPaymentDateTime .add(Duration(days: ballance!.paymentPeriodDays.toInt())); @@ -226,12 +235,16 @@ class _SubscriptionViewState extends State { PlanCard( planId: 'Pro', onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const CheckoutView( - planId: 'Pro', - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const CheckoutView( + planId: 'Pro', + ); + }, + ), + ); await initAsync(); }, ), @@ -240,16 +253,20 @@ class _SubscriptionViewState extends State { planId: 'Family', refund: refund, onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return CheckoutView( - planId: 'Family', - refund: (refund > 0) ? refund : null, - disableMonthlyOption: currentPlan == 'Pro' && - ballance!.paymentPeriodDays.toInt() == - YEARLY_PAYMENT_DAYS, - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return CheckoutView( + planId: 'Family', + refund: (refund > 0) ? refund : null, + disableMonthlyOption: currentPlan == 'Pro' && + ballance!.paymentPeriodDays.toInt() == + YEARLY_PAYMENT_DAYS, + ); + }, + ), + ); await initAsync(); }, ), @@ -281,16 +298,21 @@ class _SubscriptionViewState extends State { text: context.lang.manageSubscription, subtitle: (nextPayment != null) ? Text( - '${context.lang.nextPayment}: ${DateFormat.yMMMMd(myLocale.toString()).format(nextPayment)}') + '${context.lang.nextPayment}: ${DateFormat.yMMMMd(myLocale.toString()).format(nextPayment)}', + ) : null, onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return ManageSubscriptionView( - ballance: ballance, - nextPayment: nextPayment, - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return ManageSubscriptionView( + ballance: ballance, + nextPayment: nextPayment, + ); + }, + ), + ); await initAsync(); }, ), @@ -300,14 +322,19 @@ class _SubscriptionViewState extends State { subtitle: (formattedBalance != null) ? Text('${context.lang.currentBalance}: $formattedBalance') : null, - onTap: () { + onTap: () async { if (formattedBalance == null) return; - Navigator.push(context, MaterialPageRoute(builder: (context) { - return TransactionView( - transactions: ballance?.transactions, - formattedBalance: formattedBalance!, - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return TransactionView( + transactions: ballance?.transactions, + formattedBalance: formattedBalance!, + ); + }, + ), + ); }, ), if (isPayingUser || currentPlan == 'Tester') @@ -316,12 +343,16 @@ class _SubscriptionViewState extends State { text: context.lang.manageAdditionalUsers, subtitle: loaded ? Text('${context.lang.open}: 3') : null, onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return AdditionalUsersView( - ballance: ballance, - ); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return AdditionalUsersView( + ballance: ballance, + ); + }, + ), + ); await initAsync(); }, ), @@ -329,14 +360,18 @@ class _SubscriptionViewState extends State { icon: FontAwesomeIcons.ticket, text: context.lang.createOrRedeemVoucher, onTap: () async { - await Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const VoucherView(); - })); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const VoucherView(); + }, + ), + ); await initAsync(); }, ), - const SizedBox(height: 30) + const SizedBox(height: 30), ], ), ); @@ -488,7 +523,7 @@ class PlanCard extends StatelessWidget { ? Text(context.lang.redeemUserInviteCodeTitle) : Text(context.lang.upgradeToPaidPlanButton(planId)), ), - ) + ), ], ), ), @@ -553,8 +588,10 @@ Future redeemUserInviteCode(BuildContext context, String newPlan) async { } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - errorCodeToText(context, res.error as ErrorCode))), + content: Text( + errorCodeToText(context, res.error as ErrorCode), + ), + ), ); } if (!context.mounted) return; diff --git a/lib/src/views/settings/subscription/transaction.view.dart b/lib/src/views/settings/subscription/transaction.view.dart index 87de74c..70a597b 100644 --- a/lib/src/views/settings/subscription/transaction.view.dart +++ b/lib/src/views/settings/subscription/transaction.view.dart @@ -46,7 +46,7 @@ class _TransactionViewState extends State { ), ), if (widget.transactions != null) - ...widget.transactions!.map((x) => TransactionCard(transaction: x)) + ...widget.transactions!.map((x) => TransactionCard(transaction: x)), ], ), ); @@ -94,7 +94,8 @@ class _TransactionCardState extends State { ).format(widget.transaction.depositCents.toInt() / 100); final timestamp = DateTime.fromMillisecondsSinceEpoch( - widget.transaction.createdAtUnixTimestamp.toInt() * 1000); + widget.transaction.createdAtUnixTimestamp.toInt() * 1000, + ); return Card( margin: const EdgeInsets.all(10), diff --git a/lib/src/views/settings/subscription/voucher.view.dart b/lib/src/views/settings/subscription/voucher.view.dart index 23cfbd1..f2c48dd 100644 --- a/lib/src/views/settings/subscription/voucher.view.dart +++ b/lib/src/views/settings/subscription/voucher.view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; @@ -19,7 +21,7 @@ class _VoucherViewState extends State { @override void initState() { super.initState(); - initAsync(); + unawaited(initAsync()); } Future initAsync() async { @@ -85,10 +87,11 @@ class VoucherCard extends StatefulWidget { } class _VoucherCardState extends State { - void _copyVoucherId() { + Future _copyVoucherId() async { if (!widget.voucher.redeemed) { - Clipboard.setData(ClipboardData(text: widget.voucher.voucherId)); - HapticFeedback.heavyImpact(); + await Clipboard.setData(ClipboardData(text: widget.voucher.voucherId)); + await HapticFeedback.heavyImpact(); + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('${widget.voucher.voucherId} copied.')), ); diff --git a/lib/src/views/tutorial/show_tutorial.dart b/lib/src/views/tutorial/show_tutorial.dart index e727aa9..61f548e 100644 --- a/lib/src/views/tutorial/show_tutorial.dart +++ b/lib/src/views/tutorial/show_tutorial.dart @@ -49,7 +49,11 @@ Future checkIfTutorialAlreadyShown(String tutorialId) async { } TargetFocus getTargetFocus( - BuildContext context, GlobalKey key, String title, String body) { + BuildContext context, + GlobalKey key, + String title, + String body, +) { final renderBox = key.currentContext!.findRenderObject()! as RenderBox; final position = renderBox.localToGlobal(Offset.zero); final screenHeight = MediaQuery.of(context).size.height; @@ -73,7 +77,11 @@ TargetFocus getTargetFocus( TargetContent( align: ContentAlign.custom, customPosition: CustomTargetContentPosition( - left: 0, right: 0, top: top, bottom: bottom), + left: 0, + right: 0, + top: top, + bottom: bottom, + ), child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, @@ -97,10 +105,10 @@ TargetFocus getTargetFocus( fontSize: 16, ), ), - ) + ), ], ), - ) + ), ], ); } diff --git a/lib/src/views/tutorial/tutorials.dart b/lib/src/views/tutorial/tutorials.dart index 31f98fe..77af385 100644 --- a/lib/src/views/tutorial/tutorials.dart +++ b/lib/src/views/tutorial/tutorials.dart @@ -21,7 +21,7 @@ Future showChatListTutorialSearchOtherUsers( searchForOtherUsers, context.lang.tutorialChatListSearchUsersTitle, context.lang.tutorialChatListSearchUsersDesc, - ) + ), ]; await showTutorial(context, targets); }); @@ -42,7 +42,7 @@ Future showChatListTutorialContextMenu( firstUserListItemKey, context.lang.tutorialChatListContextMenuTitle, context.lang.tutorialChatListContextMenuDesc, - ) + ), ]; await showTutorial(context, targets); }); @@ -63,7 +63,7 @@ Future showVerifyShieldTutorial( firstUserListItemKey, context.lang.tutorialChatMessagesVerifyShieldTitle, context.lang.tutorialChatMessagesVerifyShieldDesc, - ) + ), ]; await showTutorial(context, targets); }); @@ -84,7 +84,7 @@ Future showReopenMediaFilesTutorial( firstUserListItemKey, context.lang.tutorialChatMessagesReopenMessageTitle, context.lang.tutorialChatMessagesReopenMessageDesc, - ) + ), ]; await showTutorial(context, targets); }); diff --git a/pubspec.lock b/pubspec.lock index 3a1c756..8b26fd6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "85.0.0" + version: "91.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 + sha256: f871a7d1b686bea1f13722aa51ab31554d05c81f47054d6de48cc8c45153508b url: "https://pub.dev" source: hosted - version: "1.3.59" + version: "1.3.63" adaptive_number: dependency: transitive description: @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" + sha256: a40a0cee526a7e1f387c6847bd8a5ccbf510a75952ef8a28338e989558072cb0 url: "https://pub.dev" source: hosted - version: "7.7.1" + version: "8.4.0" archive: dependency: transitive description: @@ -69,10 +69,10 @@ packages: dependency: "direct main" description: name: background_downloader - sha256: c59bff0b66a6704bed8bfb09c67571df88167906e0f5543a722373b3d180a743 + sha256: a22acfa37aa06ba5cfe6eb7b1aa700c78af64770ff450c73dd3d279d7c37d4ac url: "https://pub.dev" source: hosted - version: "9.2.3" + version: "9.2.6" boolean_selector: dependency: transitive description: @@ -85,18 +85,18 @@ packages: dependency: transitive description: name: build - sha256: "7d95cbbb1526ab5ae977df9b4cc660963b9b27f6d1075c0b34653868911385e4" + sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.2" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" build_daemon: dependency: transitive description: @@ -105,30 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.4" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "38c9c339333a09b090a638849a4c56e70a404c6bdd3b511493addfbc113b60c2" - url: "https://pub.dev" - source: hosted - version: "3.0.0" build_runner: dependency: "direct dev" description: name: build_runner - sha256: b971d4a1c789eba7be3e6fe6ce5e5b50fd3719e3cb485b3fad6d04358304351d + sha256: "4e54dbeefdc70691ba80b3bce3976af63b5425c8c07dface348dfee664a0edc1" url: "https://pub.dev" source: hosted - version: "2.6.0" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: c04e612ca801cd0928ccdb891c263a2b1391cb27940a5ea5afcf9ba894de5d62 - url: "https://pub.dev" - source: hosted - version: "9.2.0" + version: "2.9.0" built_collection: dependency: transitive description: @@ -141,10 +125,10 @@ packages: dependency: transitive description: name: built_value - sha256: "0b1b12a0a549605e5f04476031cd0bc91ead1d7c8e830773a18ee54179b3cb62" + sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d url: "https://pub.dev" source: hosted - version: "8.11.0" + version: "8.12.0" cached_network_image: dependency: "direct main" description: @@ -181,26 +165,26 @@ packages: dependency: transitive description: name: camera_android_camerax - sha256: "58b8fe843a3c83fd1273c00cb35f5a8ae507f6cc9b2029bcf7e2abba499e28d8" + sha256: "92dcc36e8ff2fa1ea3acdbb609ca2976cded55dceb719b4869c124c6d011f110" url: "https://pub.dev" source: hosted - version: "0.6.19+1" + version: "0.6.23+2" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: "04e1f052ef268085a4f0550389211cc46005a9af015e444c7b1ee7aa19332e5d" + sha256: "397f44f8a63c8c0a474668d500f9739d4f2bc45ac2b21801194b7d29260f03ee" url: "https://pub.dev" source: hosted - version: "0.9.20+6" + version: "0.9.22+1" camera_platform_interface: dependency: transitive description: name: camera_platform_interface - sha256: "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2" + sha256: ea1ef6ba79cdbed93df2d3eeef11542a90dec24dbcd9cde574926b86d7a09a10 url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" camera_web: dependency: transitive description: @@ -253,10 +237,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.11.0" collection: dependency: "direct main" description: @@ -269,10 +253,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99" + sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" connectivity_plus_platform_interface: dependency: transitive description: @@ -333,10 +317,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" dbus: dependency: transitive description: @@ -349,10 +333,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a" + sha256: "49413c8ca514dea7633e8def233b25efdf83ec8522955cc2c0e3ad802927e7c6" url: "https://pub.dev" source: hosted - version: "11.5.0" + version: "12.1.0" device_info_plus_platform_interface: dependency: transitive description: @@ -373,26 +357,26 @@ packages: dependency: "direct main" description: name: drift - sha256: "6aaea757f53bb035e8a3baedf3d1d53a79d6549a6c13d84f7546509da9372c7c" + sha256: "540cf382a3bfa99b76e51514db5b0ebcd81ce3679b7c1c9cb9478ff3735e47a1" url: "https://pub.dev" source: hosted - version: "2.28.1" + version: "2.28.2" drift_dev: dependency: "direct dev" description: name: drift_dev - sha256: "2fc05ad458a7c562755bf0cae11178dfc58387a416829b78d4da5155a61465fd" + sha256: "4db0eeedc7e8bed117a9f22d867ab7a3a294300fed5c269aac90d0b3545967ca" url: "https://pub.dev" source: hosted - version: "2.28.1" + version: "2.28.3" drift_flutter: dependency: "direct main" description: name: drift_flutter - sha256: ccfb42bc942e59f81500b16228df59cf8eb40d2fbd96637ff677df923350af7b + sha256: b7534bf320aac5213259aac120670ba67b63a1fd010505babc436ff86083818f url: "https://pub.dev" source: hosted - version: "0.2.5" + version: "0.2.7" ed25519_edwards: dependency: transitive description: @@ -437,10 +421,10 @@ packages: dependency: transitive description: name: file_selector_macos - sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711" + sha256: "19124ff4a3d8864fdc62072b6a2ef6c222d55a3404fe14893a3c02744907b60c" url: "https://pub.dev" source: hosted - version: "0.9.4+3" + version: "0.9.4+4" file_selector_platform_interface: dependency: transitive description: @@ -461,50 +445,50 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" + sha256: "132e1c311bc41e7d387b575df0aacdf24efbf4930365eb61042be5bde3978f03" url: "https://pub.dev" source: hosted - version: "3.15.2" + version: "4.2.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: "5dbc900677dcbe5873d22ad7fbd64b047750124f1f9b7ebe2a33b9ddccc838eb" + sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.2" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" + sha256: ecde2def458292404a4fcd3731ee4992fd631a0ec359d2d67c33baa8da5ec8ae url: "https://pub.dev" source: hosted - version: "2.24.1" + version: "3.2.0" firebase_messaging: dependency: "direct main" description: name: firebase_messaging - sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc" + sha256: "5021279acd1cb5ccaceaa388e616e82cc4a2e4d862f02637df0e8ab766e6900a" url: "https://pub.dev" source: hosted - version: "15.2.10" + version: "16.0.3" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface - sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754" + sha256: f3a16c51f02055ace2a7c16ccb341c1f1b36b67c13270a48bcef68c1d970bbe8 url: "https://pub.dev" source: hosted - version: "4.6.10" + version: "4.7.3" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web - sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390" + sha256: "3eb9a1382caeb95b370f21e36d4a460496af777c9c2ef5df9b90d4803982c069" url: "https://pub.dev" source: hosted - version: "3.10.10" + version: "4.0.3" fixnum: dependency: "direct main" description: @@ -574,14 +558,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.5" - flutter_keyboard_visibility: - dependency: transitive - description: - name: flutter_keyboard_visibility - sha256: "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8" - url: "https://pub.dev" - source: hosted - version: "6.0.0" flutter_keyboard_visibility_linux: dependency: transitive description: @@ -606,14 +582,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - flutter_keyboard_visibility_web: + flutter_keyboard_visibility_temp_fork: dependency: transitive description: - name: flutter_keyboard_visibility_web - sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1 + name: flutter_keyboard_visibility_temp_fork + sha256: e3d02900640fbc1129245540db16944a0898b8be81694f4bf04b6c985bed9048 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "0.1.5" flutter_keyboard_visibility_windows: dependency: transitive description: @@ -642,10 +618,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: "20ca0a9c82ce0c855ac62a2e580ab867f3fbea82680a90647f7953832d0850ae" + sha256: "19ffb0a8bb7407875555e5e98d7343a633bb73707bae6c6a5f37c90014077875" url: "https://pub.dev" source: hosted - version: "19.4.0" + version: "19.5.0" flutter_local_notifications_linux: dependency: transitive description: @@ -666,10 +642,10 @@ packages: dependency: transitive description: name: flutter_local_notifications_windows - sha256: ed46d7ae4ec9d19e4c8fa2badac5fe27ba87a3fe387343ce726f927af074ec98 + sha256: "8d658f0d367c48bd420e7cf2d26655e2d1130147bca1eea917e576ca76668aaf" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.3" flutter_localizations: dependency: "direct main" description: flutter @@ -679,10 +655,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + sha256: "306f0596590e077338312f38837f595c04f28d6cdeeac392d3d74df2f0003687" url: "https://pub.dev" source: hosted - version: "2.0.28" + version: "2.0.32" flutter_secure_storage: dependency: "direct main" description: @@ -734,10 +710,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: cd57f7969b4679317c17af6fd16ee233c1e60a82ed209d8a475c54fd6fd6f845 + sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" flutter_test: dependency: "direct dev" description: flutter @@ -759,18 +735,10 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: f50ce90dbe26d977415b9540400d6778bef00894aced6358ae578abd92b14b10 + sha256: "27af5982e6c510dec1ba038eff634fa284676ee84e3fd807225c80c4ad869177" url: "https://pub.dev" source: hosted - version: "10.9.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" + version: "10.10.0" gal: dependency: "direct main" description: @@ -815,10 +783,10 @@ packages: dependency: "direct main" description: name: hashlib - sha256: "145889c76c9530481e90b4b97c02ad817b637f25dadcb5795988f5aa0800f173" + sha256: "408af9bfb16289d433822635f0b6890e4440b74fe7acd40014983abeef6d33f0" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" hashlib_codecs: dependency: transitive description: @@ -839,10 +807,10 @@ packages: dependency: "direct main" description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" http_multi_server: dependency: transitive description: @@ -871,66 +839,66 @@ packages: dependency: "direct main" description: name: image_picker - sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + sha256: "736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: "6fae381e6af2bbe0365a5e4ce1db3959462fa0c4d234facf070746024bb80c8d" + sha256: "58a85e6f09fe9c4484d53d18a0bd6271b72c53fce1d05e6f745ae36d8c18efca" url: "https://pub.dev" source: hosted - version: "0.8.12+24" + version: "0.8.13+5" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" + sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.0" image_picker_ios: dependency: transitive description: name: image_picker_ios - sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100" + sha256: eb06fe30bab4c4497bad449b66448f50edcc695f1c59408e78aa3a8059eb8f0e url: "https://pub.dev" source: hosted - version: "0.8.12+2" + version: "0.8.13" image_picker_linux: dependency: transitive description: name: image_picker_linux - sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9" + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" url: "https://pub.dev" source: hosted - version: "0.2.1+2" + version: "0.2.2" image_picker_macos: dependency: transitive description: name: image_picker_macos - sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1" + sha256: d58cd9d67793d52beefd6585b12050af0a7663c0c2a6ece0fb110a35d6955e04 url: "https://pub.dev" source: hosted - version: "0.2.1+2" + version: "0.2.2" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" + sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665" url: "https://pub.dev" source: hosted - version: "2.10.1" + version: "2.11.0" image_picker_windows: dependency: transitive description: name: image_picker_windows - sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae url: "https://pub.dev" source: hosted - version: "0.2.1+1" + version: "0.2.2" intl: dependency: "direct main" description: @@ -943,10 +911,10 @@ packages: dependency: "direct main" description: name: introduction_screen - sha256: "02c123e074ee85b0ad0a8d3cd990f1eae78d5cfb2ac4588ad3703ac2a07fb5b0" + sha256: "47ad51281f86c3ed47e0c1a0008899ad253ca71e8b626fd862e55993825a271b" url: "https://pub.dev" source: hosted - version: "3.1.17" + version: "4.0.0" io: dependency: transitive description: @@ -975,34 +943,34 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: ce2cf974ccdee13be2a510832d7fba0b94b364e0b0395dee42abaa51b855be27 + sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" url: "https://pub.dev" source: hosted - version: "6.10.0" + version: "6.11.1" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" libsignal_protocol_dart: dependency: "direct main" description: @@ -1023,42 +991,42 @@ packages: dependency: "direct main" description: name: local_auth - sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + sha256: a4f1bf57f0236a4aeb5e8f0ec180e197f4b112a3456baa6c1e73b546630b0422 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "3.0.0" local_auth_android: dependency: transitive description: name: local_auth_android - sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" + sha256: d836715ed95b16b2de3a8c47a88ba5e607976bb1e27c9446d193152ea1429fae url: "https://pub.dev" source: hosted - version: "1.0.50" + version: "2.0.0" local_auth_darwin: dependency: transitive description: name: local_auth_darwin - sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" + sha256: "15d9db4ad4d58a11d7269e55d46ff8d49ed5e856226c8a5a91280f0d7c37b3a6" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "2.0.0" local_auth_platform_interface: dependency: transitive description: name: local_auth_platform_interface - sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + sha256: f98b8e388588583d3f781f6806e4f4c9f9e189d898d27f0c249b93a1973dd122 url: "https://pub.dev" source: hosted - version: "1.0.10" + version: "1.1.0" local_auth_windows: dependency: transitive description: name: local_auth_windows - sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + sha256: d95535a73eddf57ce5930d5e78a0fa4f294c31981fdeeee83325b797302be454 url: "https://pub.dev" source: hosted - version: "1.0.11" + version: "2.0.0" logging: dependency: "direct main" description: @@ -1071,10 +1039,10 @@ packages: dependency: "direct main" description: name: lottie - sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950 + sha256: "8ae0be46dbd9e19641791dc12ee480d34e1fd3f84c749adc05f3ad9342b71b95" url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.3.2" matcher: dependency: transitive description: @@ -1167,18 +1135,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "9.0.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" path: dependency: "direct main" description: @@ -1207,18 +1175,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16 url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.20" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" path_provider_linux: dependency: transitive description: @@ -1295,10 +1263,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "7.0.1" photo_view: dependency: "direct main" description: @@ -1342,10 +1310,10 @@ packages: dependency: transitive description: name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "1.5.2" posix: dependency: transitive description: @@ -1358,18 +1326,18 @@ packages: dependency: "direct main" description: name: protobuf - sha256: "6153efcc92a06910918f3db8231fd2cf828ac81e50ebd87adc8f8a8cb3caff0e" + sha256: de9c9eb2c33f8e933a42932fe1dc504800ca45ebc3d673e6ed7f39754ee4053e url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.2.0" provider: dependency: "direct main" description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.1.5+1" pub_semver: dependency: transitive description: @@ -1430,18 +1398,18 @@ packages: dependency: "direct main" description: name: share_plus - sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + sha256: "3424e9d5c22fd7f7590254ba09465febd6f8827c8b19a44350de4ac31d92d3a6" url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "12.0.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.0" shared_preferences: dependency: transitive description: @@ -1454,10 +1422,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.15" shared_preferences_foundation: dependency: transitive description: @@ -1523,18 +1491,18 @@ packages: dependency: transitive description: name: source_gen - sha256: fc787b1f89ceac9580c3616f899c9a447413cbdac1df071302127764c023a134 + sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.2" source_helper: dependency: transitive description: name: source_helper - sha256: "4f81479fe5194a622cdd1713fe1ecb683a6e6c85cd8cec8e2e35ee5ab3fdf2a1" + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" url: "https://pub.dev" source: hosted - version: "1.3.6" + version: "1.3.8" source_span: dependency: transitive description: @@ -1563,10 +1531,10 @@ packages: dependency: transitive description: name: sqflite_android - sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2+2" sqflite_common: dependency: transitive description: @@ -1595,26 +1563,26 @@ packages: dependency: transitive description: name: sqlite3 - sha256: dd806fff004a0aeb01e208b858dbc649bc72104670d425a81a6dd17698535f6e + sha256: f18fd9a72d7a1ad2920db61368f2a69368f1cc9b56b8233e9d83b47b0a8435aa url: "https://pub.dev" source: hosted - version: "2.8.0" + version: "2.9.3" sqlite3_flutter_libs: dependency: transitive description: name: sqlite3_flutter_libs - sha256: fd996da5515a73aacd0a04ae7063db5fe8df42670d974df4c3ee538c652eef2e + sha256: "69c80d812ef2500202ebd22002cbfc1b6565e9ff56b2f971e757fac5d42294df" url: "https://pub.dev" source: hosted - version: "0.5.38" + version: "0.5.40" sqlparser: dependency: transitive description: name: sqlparser - sha256: "7c859c803cf7e9a84d6db918bac824545045692bbe94a6386bd3a45132235d09" + sha256: "57090342af1ce32bb499aa641f4ecdd2d6231b9403cea537ac059e803cc20d67" url: "https://pub.dev" source: hosted - version: "0.41.1" + version: "0.41.2" stack_trace: dependency: transitive description: @@ -1667,10 +1635,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" timezone: dependency: transitive description: @@ -1679,22 +1647,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.10.1" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" - url: "https://pub.dev" - source: hosted - version: "1.0.2" tutorial_coach_mark: dependency: "direct main" description: name: tutorial_coach_mark - sha256: ccc4a2026d361d8a71011d0f131a2278add1a154ef43e33dfd165babbb551c17 + sha256: "5a325d53bcf16ce7a969e2ab8d4dc9610f39ee3eab2b3cc57d5c98936129b891" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" typed_data: dependency: transitive description: @@ -1715,18 +1675,18 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9" url: "https://pub.dev" source: hosted - version: "6.3.16" + version: "6.3.24" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7 url: "https://pub.dev" source: hosted - version: "6.3.3" + version: "6.3.4" url_launcher_linux: dependency: transitive description: @@ -1739,10 +1699,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.3" url_launcher_platform_interface: dependency: transitive description: @@ -1795,26 +1755,26 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc url: "https://pub.dev" source: hosted - version: "1.1.17" + version: "1.1.19" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" very_good_analysis: dependency: "direct dev" description: name: very_good_analysis - sha256: e479fbc0941009262343db308133e121bf8660c2c81d48dd8e952df7b7e1e382 + sha256: "96245839dbcc45dfab1af5fa551603b5c7a282028a64746c19c547d21a7f1e3a" url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "10.0.0" video_compress: dependency: "direct main" description: @@ -1835,26 +1795,26 @@ packages: dependency: transitive description: name: video_player_android - sha256: "0fabf59eea728a6a887f29f2818eafbefb4b37c727dbb62dccef56c9287a692f" + sha256: cf768d02924b91e333e2bc1ff928528f57d686445874f383bafab12d0bdfc340 url: "https://pub.dev" source: hosted - version: "2.8.10" + version: "2.8.17" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - sha256: "509ef9cfe7a3379783ccf306d45f5b5fc9db747401f956ce31c963417019e48e" + sha256: f9a780aac57802b2892f93787e5ea53b5f43cc57dc107bee9436458365be71cd url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.8.4" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface - sha256: cf2a1d29a284db648fd66cbd18aacc157f9862d77d2cc790f6f9678a46c1db5a + sha256: "9e372520573311055cb353b9a0da1c9d72b094b7ba01b8ecc66f28473553793b" url: "https://pub.dev" source: hosted - version: "6.4.0" + version: "6.5.0" video_player_web: dependency: transitive description: @@ -1875,18 +1835,18 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.2" watcher: dependency: transitive description: name: watcher - sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a" + sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.4" web: dependency: transitive description: @@ -1915,10 +1875,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.15.0" win32_registry: dependency: transitive description: @@ -1947,10 +1907,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" yaml: dependency: transitive description: @@ -1960,5 +1920,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index bf34393..a12ab31 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec publish_to: 'none' -version: 0.0.60+60 +version: 0.0.61+61 environment: sdk: ^3.6.0 @@ -14,14 +14,14 @@ dependencies: cached_network_image: ^3.4.1 camera: ^0.11.1 collection: ^1.18.0 - connectivity_plus: ^6.1.2 + connectivity_plus: ^7.0.0 cryptography_flutter_plus: ^2.3.4 cryptography_plus: ^2.7.0 - device_info_plus: ^11.5.0 + device_info_plus: ^12.1.0 drift: ^2.25.1 drift_flutter: ^0.2.4 - firebase_core: ^3.11.0 - firebase_messaging: ^15.2.2 + firebase_core: ^4.2.0 + firebase_messaging: ^16.0.3 fixnum: ^1.1.1 flutter: sdk: flutter @@ -35,7 +35,7 @@ dependencies: flutter_svg: ^2.0.17 flutter_zxing: path: ./dependencies/flutter_zxing - font_awesome_flutter: ^10.8.0 + font_awesome_flutter: ^10.10.0 gal: ^2.3.1 get: ^4.7.2 hand_signature: ^3.0.3 @@ -44,15 +44,15 @@ dependencies: image: ^4.3.0 image_picker: ^1.1.2 intl: ^0.20.2 - introduction_screen: ^3.1.14 + introduction_screen: ^4.0.0 json_annotation: ^4.9.0 - libsignal_protocol_dart: ^0.7.1 - local_auth: ^2.3.0 + libsignal_protocol_dart: ^0.7.4 + local_auth: ^3.0.0 logging: ^1.3.0 lottie: ^3.3.1 mutex: ^3.1.0 no_screenshot: ^0.3.1 - package_info_plus: ^8.2.1 + package_info_plus: ^9.0.0 path: ^1.9.0 path_provider: ^2.1.5 permission_handler: ^12.0.0+1 @@ -64,7 +64,7 @@ dependencies: restart_app: ^1.3.2 screenshot: ^3.0.0 scrollable_positioned_list: ^0.3.8 - share_plus: ^11.0.0 + share_plus: ^12.0.0 tutorial_coach_mark: ^1.3.0 url_launcher: ^6.3.1 video_compress: ^3.1.4 @@ -80,7 +80,7 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.8.0 - very_good_analysis: ^9.0.0 + very_good_analysis: ^10.0.0 flutter_launcher_icons: android: true diff --git a/test/drift/twonly_database/generated/schema.dart b/test/drift/twonly_database/generated/schema.dart index 2774ce2..2891647 100644 --- a/test/drift/twonly_database/generated/schema.dart +++ b/test/drift/twonly_database/generated/schema.dart @@ -19,6 +19,7 @@ import 'schema_v13.dart' as v13; import 'schema_v14.dart' as v14; import 'schema_v15.dart' as v15; import 'schema_v16.dart' as v16; +import 'schema_v17.dart' as v17; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -56,6 +57,8 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v15.DatabaseAtV15(db); case 16: return v16.DatabaseAtV16(db); + case 17: + return v17.DatabaseAtV17(db); default: throw MissingSchemaException(version, versions); } @@ -77,6 +80,7 @@ class GeneratedHelper implements SchemaInstantiationHelper { 13, 14, 15, - 16 + 16, + 17 ]; } diff --git a/test/drift/twonly_database/generated/schema_v17.dart b/test/drift/twonly_database/generated/schema_v17.dart new file mode 100644 index 0000000..5d35e87 --- /dev/null +++ b/test/drift/twonly_database/generated/schema_v17.dart @@ -0,0 +1,3742 @@ +// dart format width=80 +import 'dart:typed_data' as i2; +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class Contacts extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Contacts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn username = GeneratedColumn( + 'username', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE')); + late final GeneratedColumn displayName = GeneratedColumn( + 'display_name', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn nickName = GeneratedColumn( + 'nick_name', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn avatarSvg = GeneratedColumn( + 'avatar_svg', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn myAvatarCounter = GeneratedColumn( + 'my_avatar_counter', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + late final GeneratedColumn accepted = GeneratedColumn( + 'accepted', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("accepted" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn requested = GeneratedColumn( + 'requested', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("requested" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn blocked = GeneratedColumn( + 'blocked', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("blocked" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn verified = GeneratedColumn( + 'verified', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("verified" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn archived = GeneratedColumn( + 'archived', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("archived" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn pinned = GeneratedColumn( + 'pinned', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn deleted = GeneratedColumn( + 'deleted', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("deleted" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn alsoBestFriend = GeneratedColumn( + 'also_best_friend', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("also_best_friend" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn deleteMessagesAfterXMinutes = + GeneratedColumn( + 'delete_messages_after_x_minutes', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('1440')); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + late final GeneratedColumn totalMediaCounter = GeneratedColumn( + 'total_media_counter', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + late final GeneratedColumn lastMessageSend = + GeneratedColumn('last_message_send', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn lastMessageReceived = + GeneratedColumn('last_message_received', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn lastFlameCounterChange = + GeneratedColumn('last_flame_counter_change', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn lastFlameSync = + GeneratedColumn('last_flame_sync', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn lastMessageExchange = + GeneratedColumn('last_message_exchange', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + late final GeneratedColumn flameCounter = GeneratedColumn( + 'flame_counter', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + @override + List get $columns => [ + userId, + username, + displayName, + nickName, + avatarSvg, + myAvatarCounter, + accepted, + requested, + blocked, + verified, + archived, + pinned, + deleted, + alsoBestFriend, + deleteMessagesAfterXMinutes, + createdAt, + totalMediaCounter, + lastMessageSend, + lastMessageReceived, + lastFlameCounterChange, + lastFlameSync, + lastMessageExchange, + flameCounter + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'contacts'; + @override + Set get $primaryKey => {userId}; + @override + ContactsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ContactsData( + 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']), + avatarSvg: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}avatar_svg']), + myAvatarCounter: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}my_avatar_counter'])!, + 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'])!, + archived: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}archived'])!, + pinned: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}pinned'])!, + deleted: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}deleted'])!, + alsoBestFriend: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}also_best_friend'])!, + deleteMessagesAfterXMinutes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}delete_messages_after_x_minutes'])!, + 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']), + lastFlameCounterChange: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}last_flame_counter_change']), + lastFlameSync: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}last_flame_sync']), + lastMessageExchange: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}last_message_exchange'])!, + flameCounter: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}flame_counter'])!, + ); + } + + @override + Contacts createAlias(String alias) { + return Contacts(attachedDatabase, alias); + } +} + +class ContactsData extends DataClass implements Insertable { + final int userId; + final String username; + final String? displayName; + final String? nickName; + final String? avatarSvg; + final int myAvatarCounter; + final bool accepted; + final bool requested; + final bool blocked; + final bool verified; + final bool archived; + final bool pinned; + final bool deleted; + final bool alsoBestFriend; + final int deleteMessagesAfterXMinutes; + final DateTime createdAt; + final int totalMediaCounter; + final DateTime? lastMessageSend; + final DateTime? lastMessageReceived; + final DateTime? lastFlameCounterChange; + final DateTime? lastFlameSync; + final DateTime lastMessageExchange; + final int flameCounter; + const ContactsData( + {required this.userId, + required this.username, + this.displayName, + this.nickName, + this.avatarSvg, + required this.myAvatarCounter, + required this.accepted, + required this.requested, + required this.blocked, + required this.verified, + required this.archived, + required this.pinned, + required this.deleted, + required this.alsoBestFriend, + required this.deleteMessagesAfterXMinutes, + required this.createdAt, + required this.totalMediaCounter, + this.lastMessageSend, + this.lastMessageReceived, + this.lastFlameCounterChange, + this.lastFlameSync, + required this.lastMessageExchange, + 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); + } + if (!nullToAbsent || avatarSvg != null) { + map['avatar_svg'] = Variable(avatarSvg); + } + map['my_avatar_counter'] = Variable(myAvatarCounter); + map['accepted'] = Variable(accepted); + map['requested'] = Variable(requested); + map['blocked'] = Variable(blocked); + map['verified'] = Variable(verified); + map['archived'] = Variable(archived); + map['pinned'] = Variable(pinned); + map['deleted'] = Variable(deleted); + map['also_best_friend'] = Variable(alsoBestFriend); + map['delete_messages_after_x_minutes'] = + Variable(deleteMessagesAfterXMinutes); + 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 || lastFlameCounterChange != null) { + map['last_flame_counter_change'] = + Variable(lastFlameCounterChange); + } + if (!nullToAbsent || lastFlameSync != null) { + map['last_flame_sync'] = Variable(lastFlameSync); + } + map['last_message_exchange'] = Variable(lastMessageExchange); + 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), + avatarSvg: avatarSvg == null && nullToAbsent + ? const Value.absent() + : Value(avatarSvg), + myAvatarCounter: Value(myAvatarCounter), + accepted: Value(accepted), + requested: Value(requested), + blocked: Value(blocked), + verified: Value(verified), + archived: Value(archived), + pinned: Value(pinned), + deleted: Value(deleted), + alsoBestFriend: Value(alsoBestFriend), + deleteMessagesAfterXMinutes: Value(deleteMessagesAfterXMinutes), + createdAt: Value(createdAt), + totalMediaCounter: Value(totalMediaCounter), + lastMessageSend: lastMessageSend == null && nullToAbsent + ? const Value.absent() + : Value(lastMessageSend), + lastMessageReceived: lastMessageReceived == null && nullToAbsent + ? const Value.absent() + : Value(lastMessageReceived), + lastFlameCounterChange: lastFlameCounterChange == null && nullToAbsent + ? const Value.absent() + : Value(lastFlameCounterChange), + lastFlameSync: lastFlameSync == null && nullToAbsent + ? const Value.absent() + : Value(lastFlameSync), + lastMessageExchange: Value(lastMessageExchange), + flameCounter: Value(flameCounter), + ); + } + + factory ContactsData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ContactsData( + userId: serializer.fromJson(json['userId']), + username: serializer.fromJson(json['username']), + displayName: serializer.fromJson(json['displayName']), + nickName: serializer.fromJson(json['nickName']), + avatarSvg: serializer.fromJson(json['avatarSvg']), + myAvatarCounter: serializer.fromJson(json['myAvatarCounter']), + accepted: serializer.fromJson(json['accepted']), + requested: serializer.fromJson(json['requested']), + blocked: serializer.fromJson(json['blocked']), + verified: serializer.fromJson(json['verified']), + archived: serializer.fromJson(json['archived']), + pinned: serializer.fromJson(json['pinned']), + deleted: serializer.fromJson(json['deleted']), + alsoBestFriend: serializer.fromJson(json['alsoBestFriend']), + deleteMessagesAfterXMinutes: + serializer.fromJson(json['deleteMessagesAfterXMinutes']), + createdAt: serializer.fromJson(json['createdAt']), + totalMediaCounter: serializer.fromJson(json['totalMediaCounter']), + lastMessageSend: serializer.fromJson(json['lastMessageSend']), + lastMessageReceived: + serializer.fromJson(json['lastMessageReceived']), + lastFlameCounterChange: + serializer.fromJson(json['lastFlameCounterChange']), + lastFlameSync: serializer.fromJson(json['lastFlameSync']), + lastMessageExchange: + serializer.fromJson(json['lastMessageExchange']), + 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), + 'avatarSvg': serializer.toJson(avatarSvg), + 'myAvatarCounter': serializer.toJson(myAvatarCounter), + 'accepted': serializer.toJson(accepted), + 'requested': serializer.toJson(requested), + 'blocked': serializer.toJson(blocked), + 'verified': serializer.toJson(verified), + 'archived': serializer.toJson(archived), + 'pinned': serializer.toJson(pinned), + 'deleted': serializer.toJson(deleted), + 'alsoBestFriend': serializer.toJson(alsoBestFriend), + 'deleteMessagesAfterXMinutes': + serializer.toJson(deleteMessagesAfterXMinutes), + 'createdAt': serializer.toJson(createdAt), + 'totalMediaCounter': serializer.toJson(totalMediaCounter), + 'lastMessageSend': serializer.toJson(lastMessageSend), + 'lastMessageReceived': serializer.toJson(lastMessageReceived), + 'lastFlameCounterChange': + serializer.toJson(lastFlameCounterChange), + 'lastFlameSync': serializer.toJson(lastFlameSync), + 'lastMessageExchange': serializer.toJson(lastMessageExchange), + 'flameCounter': serializer.toJson(flameCounter), + }; + } + + ContactsData copyWith( + {int? userId, + String? username, + Value displayName = const Value.absent(), + Value nickName = const Value.absent(), + Value avatarSvg = const Value.absent(), + int? myAvatarCounter, + bool? accepted, + bool? requested, + bool? blocked, + bool? verified, + bool? archived, + bool? pinned, + bool? deleted, + bool? alsoBestFriend, + int? deleteMessagesAfterXMinutes, + DateTime? createdAt, + int? totalMediaCounter, + Value lastMessageSend = const Value.absent(), + Value lastMessageReceived = const Value.absent(), + Value lastFlameCounterChange = const Value.absent(), + Value lastFlameSync = const Value.absent(), + DateTime? lastMessageExchange, + int? flameCounter}) => + ContactsData( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName.present ? displayName.value : this.displayName, + nickName: nickName.present ? nickName.value : this.nickName, + avatarSvg: avatarSvg.present ? avatarSvg.value : this.avatarSvg, + myAvatarCounter: myAvatarCounter ?? this.myAvatarCounter, + accepted: accepted ?? this.accepted, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + archived: archived ?? this.archived, + pinned: pinned ?? this.pinned, + deleted: deleted ?? this.deleted, + alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, + deleteMessagesAfterXMinutes: + deleteMessagesAfterXMinutes ?? this.deleteMessagesAfterXMinutes, + createdAt: createdAt ?? this.createdAt, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + lastMessageSend: lastMessageSend.present + ? lastMessageSend.value + : this.lastMessageSend, + lastMessageReceived: lastMessageReceived.present + ? lastMessageReceived.value + : this.lastMessageReceived, + lastFlameCounterChange: lastFlameCounterChange.present + ? lastFlameCounterChange.value + : this.lastFlameCounterChange, + lastFlameSync: + lastFlameSync.present ? lastFlameSync.value : this.lastFlameSync, + lastMessageExchange: lastMessageExchange ?? this.lastMessageExchange, + flameCounter: flameCounter ?? this.flameCounter, + ); + ContactsData copyWithCompanion(ContactsCompanion data) { + return ContactsData( + 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, + avatarSvg: data.avatarSvg.present ? data.avatarSvg.value : this.avatarSvg, + myAvatarCounter: data.myAvatarCounter.present + ? data.myAvatarCounter.value + : this.myAvatarCounter, + 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, + archived: data.archived.present ? data.archived.value : this.archived, + pinned: data.pinned.present ? data.pinned.value : this.pinned, + deleted: data.deleted.present ? data.deleted.value : this.deleted, + alsoBestFriend: data.alsoBestFriend.present + ? data.alsoBestFriend.value + : this.alsoBestFriend, + deleteMessagesAfterXMinutes: data.deleteMessagesAfterXMinutes.present + ? data.deleteMessagesAfterXMinutes.value + : this.deleteMessagesAfterXMinutes, + 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, + lastFlameCounterChange: data.lastFlameCounterChange.present + ? data.lastFlameCounterChange.value + : this.lastFlameCounterChange, + lastFlameSync: data.lastFlameSync.present + ? data.lastFlameSync.value + : this.lastFlameSync, + lastMessageExchange: data.lastMessageExchange.present + ? data.lastMessageExchange.value + : this.lastMessageExchange, + flameCounter: data.flameCounter.present + ? data.flameCounter.value + : this.flameCounter, + ); + } + + @override + String toString() { + return (StringBuffer('ContactsData(') + ..write('userId: $userId, ') + ..write('username: $username, ') + ..write('displayName: $displayName, ') + ..write('nickName: $nickName, ') + ..write('avatarSvg: $avatarSvg, ') + ..write('myAvatarCounter: $myAvatarCounter, ') + ..write('accepted: $accepted, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('archived: $archived, ') + ..write('pinned: $pinned, ') + ..write('deleted: $deleted, ') + ..write('alsoBestFriend: $alsoBestFriend, ') + ..write('deleteMessagesAfterXMinutes: $deleteMessagesAfterXMinutes, ') + ..write('createdAt: $createdAt, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastFlameCounterChange: $lastFlameCounterChange, ') + ..write('lastFlameSync: $lastFlameSync, ') + ..write('lastMessageExchange: $lastMessageExchange, ') + ..write('flameCounter: $flameCounter') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + userId, + username, + displayName, + nickName, + avatarSvg, + myAvatarCounter, + accepted, + requested, + blocked, + verified, + archived, + pinned, + deleted, + alsoBestFriend, + deleteMessagesAfterXMinutes, + createdAt, + totalMediaCounter, + lastMessageSend, + lastMessageReceived, + lastFlameCounterChange, + lastFlameSync, + lastMessageExchange, + flameCounter + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ContactsData && + other.userId == this.userId && + other.username == this.username && + other.displayName == this.displayName && + other.nickName == this.nickName && + other.avatarSvg == this.avatarSvg && + other.myAvatarCounter == this.myAvatarCounter && + other.accepted == this.accepted && + other.requested == this.requested && + other.blocked == this.blocked && + other.verified == this.verified && + other.archived == this.archived && + other.pinned == this.pinned && + other.deleted == this.deleted && + other.alsoBestFriend == this.alsoBestFriend && + other.deleteMessagesAfterXMinutes == + this.deleteMessagesAfterXMinutes && + other.createdAt == this.createdAt && + other.totalMediaCounter == this.totalMediaCounter && + other.lastMessageSend == this.lastMessageSend && + other.lastMessageReceived == this.lastMessageReceived && + other.lastFlameCounterChange == this.lastFlameCounterChange && + other.lastFlameSync == this.lastFlameSync && + other.lastMessageExchange == this.lastMessageExchange && + other.flameCounter == this.flameCounter); +} + +class ContactsCompanion extends UpdateCompanion { + final Value userId; + final Value username; + final Value displayName; + final Value nickName; + final Value avatarSvg; + final Value myAvatarCounter; + final Value accepted; + final Value requested; + final Value blocked; + final Value verified; + final Value archived; + final Value pinned; + final Value deleted; + final Value alsoBestFriend; + final Value deleteMessagesAfterXMinutes; + final Value createdAt; + final Value totalMediaCounter; + final Value lastMessageSend; + final Value lastMessageReceived; + final Value lastFlameCounterChange; + final Value lastFlameSync; + final Value lastMessageExchange; + 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.avatarSvg = const Value.absent(), + this.myAvatarCounter = const Value.absent(), + this.accepted = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.archived = const Value.absent(), + this.pinned = const Value.absent(), + this.deleted = const Value.absent(), + this.alsoBestFriend = const Value.absent(), + this.deleteMessagesAfterXMinutes = const Value.absent(), + this.createdAt = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastFlameCounterChange = const Value.absent(), + this.lastFlameSync = const Value.absent(), + this.lastMessageExchange = 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.avatarSvg = const Value.absent(), + this.myAvatarCounter = const Value.absent(), + this.accepted = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.archived = const Value.absent(), + this.pinned = const Value.absent(), + this.deleted = const Value.absent(), + this.alsoBestFriend = const Value.absent(), + this.deleteMessagesAfterXMinutes = const Value.absent(), + this.createdAt = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastFlameCounterChange = const Value.absent(), + this.lastFlameSync = const Value.absent(), + this.lastMessageExchange = const Value.absent(), + this.flameCounter = const Value.absent(), + }) : username = Value(username); + static Insertable custom({ + Expression? userId, + Expression? username, + Expression? displayName, + Expression? nickName, + Expression? avatarSvg, + Expression? myAvatarCounter, + Expression? accepted, + Expression? requested, + Expression? blocked, + Expression? verified, + Expression? archived, + Expression? pinned, + Expression? deleted, + Expression? alsoBestFriend, + Expression? deleteMessagesAfterXMinutes, + Expression? createdAt, + Expression? totalMediaCounter, + Expression? lastMessageSend, + Expression? lastMessageReceived, + Expression? lastFlameCounterChange, + Expression? lastFlameSync, + Expression? lastMessageExchange, + 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 (avatarSvg != null) 'avatar_svg': avatarSvg, + if (myAvatarCounter != null) 'my_avatar_counter': myAvatarCounter, + if (accepted != null) 'accepted': accepted, + if (requested != null) 'requested': requested, + if (blocked != null) 'blocked': blocked, + if (verified != null) 'verified': verified, + if (archived != null) 'archived': archived, + if (pinned != null) 'pinned': pinned, + if (deleted != null) 'deleted': deleted, + if (alsoBestFriend != null) 'also_best_friend': alsoBestFriend, + if (deleteMessagesAfterXMinutes != null) + 'delete_messages_after_x_minutes': deleteMessagesAfterXMinutes, + 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 (lastFlameCounterChange != null) + 'last_flame_counter_change': lastFlameCounterChange, + if (lastFlameSync != null) 'last_flame_sync': lastFlameSync, + if (lastMessageExchange != null) + 'last_message_exchange': lastMessageExchange, + if (flameCounter != null) 'flame_counter': flameCounter, + }); + } + + ContactsCompanion copyWith( + {Value? userId, + Value? username, + Value? displayName, + Value? nickName, + Value? avatarSvg, + Value? myAvatarCounter, + Value? accepted, + Value? requested, + Value? blocked, + Value? verified, + Value? archived, + Value? pinned, + Value? deleted, + Value? alsoBestFriend, + Value? deleteMessagesAfterXMinutes, + Value? createdAt, + Value? totalMediaCounter, + Value? lastMessageSend, + Value? lastMessageReceived, + Value? lastFlameCounterChange, + Value? lastFlameSync, + Value? lastMessageExchange, + Value? flameCounter}) { + return ContactsCompanion( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName ?? this.displayName, + nickName: nickName ?? this.nickName, + avatarSvg: avatarSvg ?? this.avatarSvg, + myAvatarCounter: myAvatarCounter ?? this.myAvatarCounter, + accepted: accepted ?? this.accepted, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + archived: archived ?? this.archived, + pinned: pinned ?? this.pinned, + deleted: deleted ?? this.deleted, + alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, + deleteMessagesAfterXMinutes: + deleteMessagesAfterXMinutes ?? this.deleteMessagesAfterXMinutes, + createdAt: createdAt ?? this.createdAt, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + lastMessageSend: lastMessageSend ?? this.lastMessageSend, + lastMessageReceived: lastMessageReceived ?? this.lastMessageReceived, + lastFlameCounterChange: + lastFlameCounterChange ?? this.lastFlameCounterChange, + lastFlameSync: lastFlameSync ?? this.lastFlameSync, + lastMessageExchange: lastMessageExchange ?? this.lastMessageExchange, + 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 (avatarSvg.present) { + map['avatar_svg'] = Variable(avatarSvg.value); + } + if (myAvatarCounter.present) { + map['my_avatar_counter'] = Variable(myAvatarCounter.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 (archived.present) { + map['archived'] = Variable(archived.value); + } + if (pinned.present) { + map['pinned'] = Variable(pinned.value); + } + if (deleted.present) { + map['deleted'] = Variable(deleted.value); + } + if (alsoBestFriend.present) { + map['also_best_friend'] = Variable(alsoBestFriend.value); + } + if (deleteMessagesAfterXMinutes.present) { + map['delete_messages_after_x_minutes'] = + Variable(deleteMessagesAfterXMinutes.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 (lastFlameCounterChange.present) { + map['last_flame_counter_change'] = + Variable(lastFlameCounterChange.value); + } + if (lastFlameSync.present) { + map['last_flame_sync'] = Variable(lastFlameSync.value); + } + if (lastMessageExchange.present) { + map['last_message_exchange'] = + Variable(lastMessageExchange.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('avatarSvg: $avatarSvg, ') + ..write('myAvatarCounter: $myAvatarCounter, ') + ..write('accepted: $accepted, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('archived: $archived, ') + ..write('pinned: $pinned, ') + ..write('deleted: $deleted, ') + ..write('alsoBestFriend: $alsoBestFriend, ') + ..write('deleteMessagesAfterXMinutes: $deleteMessagesAfterXMinutes, ') + ..write('createdAt: $createdAt, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastFlameCounterChange: $lastFlameCounterChange, ') + ..write('lastFlameSync: $lastFlameSync, ') + ..write('lastMessageExchange: $lastMessageExchange, ') + ..write('flameCounter: $flameCounter') + ..write(')')) + .toString(); + } +} + +class Messages extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Messages(this.attachedDatabase, [this._alias]); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)')); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn messageOtherId = GeneratedColumn( + 'message_other_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn mediaUploadId = GeneratedColumn( + 'media_upload_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn mediaDownloadId = GeneratedColumn( + 'media_download_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn responseToMessageId = GeneratedColumn( + 'response_to_message_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn responseToOtherMessageId = + GeneratedColumn('response_to_other_message_id', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + 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: const CustomExpression('0')); + late final GeneratedColumn mediaStored = GeneratedColumn( + 'media_stored', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("media_stored" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn downloadState = GeneratedColumn( + 'download_state', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('2')); + 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: const CustomExpression('0')); + late final GeneratedColumn errorWhileSending = GeneratedColumn( + 'error_while_sending', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("error_while_sending" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn mediaRetransmissionState = + GeneratedColumn('media_retransmission_state', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'none\'')); + late final GeneratedColumn kind = GeneratedColumn( + 'kind', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn contentJson = GeneratedColumn( + 'content_json', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn openedAt = GeneratedColumn( + 'opened_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn sendAt = GeneratedColumn( + 'send_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + @override + List get $columns => [ + contactId, + messageId, + messageOtherId, + mediaUploadId, + mediaDownloadId, + responseToMessageId, + responseToOtherMessageId, + acknowledgeByUser, + mediaStored, + downloadState, + acknowledgeByServer, + errorWhileSending, + mediaRetransmissionState, + kind, + contentJson, + openedAt, + sendAt, + updatedAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'messages'; + @override + Set get $primaryKey => {messageId}; + @override + MessagesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MessagesData( + 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']), + mediaUploadId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}media_upload_id']), + mediaDownloadId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}media_download_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'])!, + mediaStored: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}media_stored'])!, + downloadState: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}download_state'])!, + acknowledgeByServer: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}acknowledge_by_server'])!, + errorWhileSending: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}error_while_sending'])!, + mediaRetransmissionState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}media_retransmission_state'])!, + kind: 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 + Messages createAlias(String alias) { + return Messages(attachedDatabase, alias); + } +} + +class MessagesData extends DataClass implements Insertable { + final int contactId; + final int messageId; + final int? messageOtherId; + final int? mediaUploadId; + final int? mediaDownloadId; + final int? responseToMessageId; + final int? responseToOtherMessageId; + final bool acknowledgeByUser; + final bool mediaStored; + final int downloadState; + final bool acknowledgeByServer; + final bool errorWhileSending; + final String mediaRetransmissionState; + final String kind; + final String? contentJson; + final DateTime? openedAt; + final DateTime sendAt; + final DateTime updatedAt; + const MessagesData( + {required this.contactId, + required this.messageId, + this.messageOtherId, + this.mediaUploadId, + this.mediaDownloadId, + this.responseToMessageId, + this.responseToOtherMessageId, + required this.acknowledgeByUser, + required this.mediaStored, + required this.downloadState, + required this.acknowledgeByServer, + required this.errorWhileSending, + required this.mediaRetransmissionState, + 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 || mediaUploadId != null) { + map['media_upload_id'] = Variable(mediaUploadId); + } + if (!nullToAbsent || mediaDownloadId != null) { + map['media_download_id'] = Variable(mediaDownloadId); + } + 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['media_stored'] = Variable(mediaStored); + map['download_state'] = Variable(downloadState); + map['acknowledge_by_server'] = Variable(acknowledgeByServer); + map['error_while_sending'] = Variable(errorWhileSending); + map['media_retransmission_state'] = + Variable(mediaRetransmissionState); + map['kind'] = Variable(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), + mediaUploadId: mediaUploadId == null && nullToAbsent + ? const Value.absent() + : Value(mediaUploadId), + mediaDownloadId: mediaDownloadId == null && nullToAbsent + ? const Value.absent() + : Value(mediaDownloadId), + responseToMessageId: responseToMessageId == null && nullToAbsent + ? const Value.absent() + : Value(responseToMessageId), + responseToOtherMessageId: responseToOtherMessageId == null && nullToAbsent + ? const Value.absent() + : Value(responseToOtherMessageId), + acknowledgeByUser: Value(acknowledgeByUser), + mediaStored: Value(mediaStored), + downloadState: Value(downloadState), + acknowledgeByServer: Value(acknowledgeByServer), + errorWhileSending: Value(errorWhileSending), + mediaRetransmissionState: Value(mediaRetransmissionState), + 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 MessagesData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessagesData( + contactId: serializer.fromJson(json['contactId']), + messageId: serializer.fromJson(json['messageId']), + messageOtherId: serializer.fromJson(json['messageOtherId']), + mediaUploadId: serializer.fromJson(json['mediaUploadId']), + mediaDownloadId: serializer.fromJson(json['mediaDownloadId']), + responseToMessageId: + serializer.fromJson(json['responseToMessageId']), + responseToOtherMessageId: + serializer.fromJson(json['responseToOtherMessageId']), + acknowledgeByUser: serializer.fromJson(json['acknowledgeByUser']), + mediaStored: serializer.fromJson(json['mediaStored']), + downloadState: serializer.fromJson(json['downloadState']), + acknowledgeByServer: + serializer.fromJson(json['acknowledgeByServer']), + errorWhileSending: serializer.fromJson(json['errorWhileSending']), + mediaRetransmissionState: + serializer.fromJson(json['mediaRetransmissionState']), + kind: 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), + 'mediaUploadId': serializer.toJson(mediaUploadId), + 'mediaDownloadId': serializer.toJson(mediaDownloadId), + 'responseToMessageId': serializer.toJson(responseToMessageId), + 'responseToOtherMessageId': + serializer.toJson(responseToOtherMessageId), + 'acknowledgeByUser': serializer.toJson(acknowledgeByUser), + 'mediaStored': serializer.toJson(mediaStored), + 'downloadState': serializer.toJson(downloadState), + 'acknowledgeByServer': serializer.toJson(acknowledgeByServer), + 'errorWhileSending': serializer.toJson(errorWhileSending), + 'mediaRetransmissionState': + serializer.toJson(mediaRetransmissionState), + 'kind': serializer.toJson(kind), + 'contentJson': serializer.toJson(contentJson), + 'openedAt': serializer.toJson(openedAt), + 'sendAt': serializer.toJson(sendAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + MessagesData copyWith( + {int? contactId, + int? messageId, + Value messageOtherId = const Value.absent(), + Value mediaUploadId = const Value.absent(), + Value mediaDownloadId = const Value.absent(), + Value responseToMessageId = const Value.absent(), + Value responseToOtherMessageId = const Value.absent(), + bool? acknowledgeByUser, + bool? mediaStored, + int? downloadState, + bool? acknowledgeByServer, + bool? errorWhileSending, + String? mediaRetransmissionState, + String? kind, + Value contentJson = const Value.absent(), + Value openedAt = const Value.absent(), + DateTime? sendAt, + DateTime? updatedAt}) => + MessagesData( + contactId: contactId ?? this.contactId, + messageId: messageId ?? this.messageId, + messageOtherId: + messageOtherId.present ? messageOtherId.value : this.messageOtherId, + mediaUploadId: + mediaUploadId.present ? mediaUploadId.value : this.mediaUploadId, + mediaDownloadId: mediaDownloadId.present + ? mediaDownloadId.value + : this.mediaDownloadId, + responseToMessageId: responseToMessageId.present + ? responseToMessageId.value + : this.responseToMessageId, + responseToOtherMessageId: responseToOtherMessageId.present + ? responseToOtherMessageId.value + : this.responseToOtherMessageId, + acknowledgeByUser: acknowledgeByUser ?? this.acknowledgeByUser, + mediaStored: mediaStored ?? this.mediaStored, + downloadState: downloadState ?? this.downloadState, + acknowledgeByServer: acknowledgeByServer ?? this.acknowledgeByServer, + errorWhileSending: errorWhileSending ?? this.errorWhileSending, + mediaRetransmissionState: + mediaRetransmissionState ?? this.mediaRetransmissionState, + 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, + ); + MessagesData copyWithCompanion(MessagesCompanion data) { + return MessagesData( + 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, + mediaUploadId: data.mediaUploadId.present + ? data.mediaUploadId.value + : this.mediaUploadId, + mediaDownloadId: data.mediaDownloadId.present + ? data.mediaDownloadId.value + : this.mediaDownloadId, + 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, + mediaStored: + data.mediaStored.present ? data.mediaStored.value : this.mediaStored, + downloadState: data.downloadState.present + ? data.downloadState.value + : this.downloadState, + acknowledgeByServer: data.acknowledgeByServer.present + ? data.acknowledgeByServer.value + : this.acknowledgeByServer, + errorWhileSending: data.errorWhileSending.present + ? data.errorWhileSending.value + : this.errorWhileSending, + mediaRetransmissionState: data.mediaRetransmissionState.present + ? data.mediaRetransmissionState.value + : this.mediaRetransmissionState, + 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('MessagesData(') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('messageOtherId: $messageOtherId, ') + ..write('mediaUploadId: $mediaUploadId, ') + ..write('mediaDownloadId: $mediaDownloadId, ') + ..write('responseToMessageId: $responseToMessageId, ') + ..write('responseToOtherMessageId: $responseToOtherMessageId, ') + ..write('acknowledgeByUser: $acknowledgeByUser, ') + ..write('mediaStored: $mediaStored, ') + ..write('downloadState: $downloadState, ') + ..write('acknowledgeByServer: $acknowledgeByServer, ') + ..write('errorWhileSending: $errorWhileSending, ') + ..write('mediaRetransmissionState: $mediaRetransmissionState, ') + ..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, + mediaUploadId, + mediaDownloadId, + responseToMessageId, + responseToOtherMessageId, + acknowledgeByUser, + mediaStored, + downloadState, + acknowledgeByServer, + errorWhileSending, + mediaRetransmissionState, + kind, + contentJson, + openedAt, + sendAt, + updatedAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessagesData && + other.contactId == this.contactId && + other.messageId == this.messageId && + other.messageOtherId == this.messageOtherId && + other.mediaUploadId == this.mediaUploadId && + other.mediaDownloadId == this.mediaDownloadId && + other.responseToMessageId == this.responseToMessageId && + other.responseToOtherMessageId == this.responseToOtherMessageId && + other.acknowledgeByUser == this.acknowledgeByUser && + other.mediaStored == this.mediaStored && + other.downloadState == this.downloadState && + other.acknowledgeByServer == this.acknowledgeByServer && + other.errorWhileSending == this.errorWhileSending && + other.mediaRetransmissionState == this.mediaRetransmissionState && + 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 mediaUploadId; + final Value mediaDownloadId; + final Value responseToMessageId; + final Value responseToOtherMessageId; + final Value acknowledgeByUser; + final Value mediaStored; + final Value downloadState; + final Value acknowledgeByServer; + final Value errorWhileSending; + final Value mediaRetransmissionState; + 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.mediaUploadId = const Value.absent(), + this.mediaDownloadId = const Value.absent(), + this.responseToMessageId = const Value.absent(), + this.responseToOtherMessageId = const Value.absent(), + this.acknowledgeByUser = const Value.absent(), + this.mediaStored = const Value.absent(), + this.downloadState = const Value.absent(), + this.acknowledgeByServer = const Value.absent(), + this.errorWhileSending = const Value.absent(), + this.mediaRetransmissionState = 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.mediaUploadId = const Value.absent(), + this.mediaDownloadId = const Value.absent(), + this.responseToMessageId = const Value.absent(), + this.responseToOtherMessageId = const Value.absent(), + this.acknowledgeByUser = const Value.absent(), + this.mediaStored = const Value.absent(), + this.downloadState = const Value.absent(), + this.acknowledgeByServer = const Value.absent(), + this.errorWhileSending = const Value.absent(), + this.mediaRetransmissionState = const Value.absent(), + required String kind, + this.contentJson = const Value.absent(), + this.openedAt = const Value.absent(), + this.sendAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : contactId = Value(contactId), + kind = Value(kind); + static Insertable custom({ + Expression? contactId, + Expression? messageId, + Expression? messageOtherId, + Expression? mediaUploadId, + Expression? mediaDownloadId, + Expression? responseToMessageId, + Expression? responseToOtherMessageId, + Expression? acknowledgeByUser, + Expression? mediaStored, + Expression? downloadState, + Expression? acknowledgeByServer, + Expression? errorWhileSending, + Expression? mediaRetransmissionState, + 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 (mediaUploadId != null) 'media_upload_id': mediaUploadId, + if (mediaDownloadId != null) 'media_download_id': mediaDownloadId, + 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 (mediaStored != null) 'media_stored': mediaStored, + if (downloadState != null) 'download_state': downloadState, + if (acknowledgeByServer != null) + 'acknowledge_by_server': acknowledgeByServer, + if (errorWhileSending != null) 'error_while_sending': errorWhileSending, + if (mediaRetransmissionState != null) + 'media_retransmission_state': mediaRetransmissionState, + 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? mediaUploadId, + Value? mediaDownloadId, + Value? responseToMessageId, + Value? responseToOtherMessageId, + Value? acknowledgeByUser, + Value? mediaStored, + Value? downloadState, + Value? acknowledgeByServer, + Value? errorWhileSending, + Value? mediaRetransmissionState, + Value? kind, + Value? contentJson, + Value? openedAt, + Value? sendAt, + Value? updatedAt}) { + return MessagesCompanion( + contactId: contactId ?? this.contactId, + messageId: messageId ?? this.messageId, + messageOtherId: messageOtherId ?? this.messageOtherId, + mediaUploadId: mediaUploadId ?? this.mediaUploadId, + mediaDownloadId: mediaDownloadId ?? this.mediaDownloadId, + responseToMessageId: responseToMessageId ?? this.responseToMessageId, + responseToOtherMessageId: + responseToOtherMessageId ?? this.responseToOtherMessageId, + acknowledgeByUser: acknowledgeByUser ?? this.acknowledgeByUser, + mediaStored: mediaStored ?? this.mediaStored, + downloadState: downloadState ?? this.downloadState, + acknowledgeByServer: acknowledgeByServer ?? this.acknowledgeByServer, + errorWhileSending: errorWhileSending ?? this.errorWhileSending, + mediaRetransmissionState: + mediaRetransmissionState ?? this.mediaRetransmissionState, + 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 (mediaUploadId.present) { + map['media_upload_id'] = Variable(mediaUploadId.value); + } + if (mediaDownloadId.present) { + map['media_download_id'] = Variable(mediaDownloadId.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 (mediaStored.present) { + map['media_stored'] = Variable(mediaStored.value); + } + if (downloadState.present) { + map['download_state'] = Variable(downloadState.value); + } + if (acknowledgeByServer.present) { + map['acknowledge_by_server'] = Variable(acknowledgeByServer.value); + } + if (errorWhileSending.present) { + map['error_while_sending'] = Variable(errorWhileSending.value); + } + if (mediaRetransmissionState.present) { + map['media_retransmission_state'] = + Variable(mediaRetransmissionState.value); + } + if (kind.present) { + map['kind'] = Variable(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('mediaUploadId: $mediaUploadId, ') + ..write('mediaDownloadId: $mediaDownloadId, ') + ..write('responseToMessageId: $responseToMessageId, ') + ..write('responseToOtherMessageId: $responseToOtherMessageId, ') + ..write('acknowledgeByUser: $acknowledgeByUser, ') + ..write('mediaStored: $mediaStored, ') + ..write('downloadState: $downloadState, ') + ..write('acknowledgeByServer: $acknowledgeByServer, ') + ..write('errorWhileSending: $errorWhileSending, ') + ..write('mediaRetransmissionState: $mediaRetransmissionState, ') + ..write('kind: $kind, ') + ..write('contentJson: $contentJson, ') + ..write('openedAt: $openedAt, ') + ..write('sendAt: $sendAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class MediaUploads extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MediaUploads(this.attachedDatabase, [this._alias]); + late final GeneratedColumn mediaUploadId = GeneratedColumn( + 'media_upload_id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn state = GeneratedColumn( + 'state', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'pending\'')); + late final GeneratedColumn metadata = GeneratedColumn( + 'metadata', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn messageIds = GeneratedColumn( + 'message_ids', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn encryptionData = GeneratedColumn( + 'encryption_data', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => + [mediaUploadId, state, metadata, messageIds, encryptionData]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'media_uploads'; + @override + Set get $primaryKey => {mediaUploadId}; + @override + MediaUploadsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MediaUploadsData( + mediaUploadId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}media_upload_id'])!, + state: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}state'])!, + metadata: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}metadata']), + messageIds: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}message_ids']), + encryptionData: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}encryption_data']), + ); + } + + @override + MediaUploads createAlias(String alias) { + return MediaUploads(attachedDatabase, alias); + } +} + +class MediaUploadsData extends DataClass + implements Insertable { + final int mediaUploadId; + final String state; + final String? metadata; + final String? messageIds; + final String? encryptionData; + const MediaUploadsData( + {required this.mediaUploadId, + required this.state, + this.metadata, + this.messageIds, + this.encryptionData}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['media_upload_id'] = Variable(mediaUploadId); + map['state'] = Variable(state); + if (!nullToAbsent || metadata != null) { + map['metadata'] = Variable(metadata); + } + if (!nullToAbsent || messageIds != null) { + map['message_ids'] = Variable(messageIds); + } + if (!nullToAbsent || encryptionData != null) { + map['encryption_data'] = Variable(encryptionData); + } + return map; + } + + MediaUploadsCompanion toCompanion(bool nullToAbsent) { + return MediaUploadsCompanion( + mediaUploadId: Value(mediaUploadId), + state: Value(state), + metadata: metadata == null && nullToAbsent + ? const Value.absent() + : Value(metadata), + messageIds: messageIds == null && nullToAbsent + ? const Value.absent() + : Value(messageIds), + encryptionData: encryptionData == null && nullToAbsent + ? const Value.absent() + : Value(encryptionData), + ); + } + + factory MediaUploadsData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MediaUploadsData( + mediaUploadId: serializer.fromJson(json['mediaUploadId']), + state: serializer.fromJson(json['state']), + metadata: serializer.fromJson(json['metadata']), + messageIds: serializer.fromJson(json['messageIds']), + encryptionData: serializer.fromJson(json['encryptionData']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'mediaUploadId': serializer.toJson(mediaUploadId), + 'state': serializer.toJson(state), + 'metadata': serializer.toJson(metadata), + 'messageIds': serializer.toJson(messageIds), + 'encryptionData': serializer.toJson(encryptionData), + }; + } + + MediaUploadsData copyWith( + {int? mediaUploadId, + String? state, + Value metadata = const Value.absent(), + Value messageIds = const Value.absent(), + Value encryptionData = const Value.absent()}) => + MediaUploadsData( + mediaUploadId: mediaUploadId ?? this.mediaUploadId, + state: state ?? this.state, + metadata: metadata.present ? metadata.value : this.metadata, + messageIds: messageIds.present ? messageIds.value : this.messageIds, + encryptionData: + encryptionData.present ? encryptionData.value : this.encryptionData, + ); + MediaUploadsData copyWithCompanion(MediaUploadsCompanion data) { + return MediaUploadsData( + mediaUploadId: data.mediaUploadId.present + ? data.mediaUploadId.value + : this.mediaUploadId, + state: data.state.present ? data.state.value : this.state, + metadata: data.metadata.present ? data.metadata.value : this.metadata, + messageIds: + data.messageIds.present ? data.messageIds.value : this.messageIds, + encryptionData: data.encryptionData.present + ? data.encryptionData.value + : this.encryptionData, + ); + } + + @override + String toString() { + return (StringBuffer('MediaUploadsData(') + ..write('mediaUploadId: $mediaUploadId, ') + ..write('state: $state, ') + ..write('metadata: $metadata, ') + ..write('messageIds: $messageIds, ') + ..write('encryptionData: $encryptionData') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(mediaUploadId, state, metadata, messageIds, encryptionData); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MediaUploadsData && + other.mediaUploadId == this.mediaUploadId && + other.state == this.state && + other.metadata == this.metadata && + other.messageIds == this.messageIds && + other.encryptionData == this.encryptionData); +} + +class MediaUploadsCompanion extends UpdateCompanion { + final Value mediaUploadId; + final Value state; + final Value metadata; + final Value messageIds; + final Value encryptionData; + const MediaUploadsCompanion({ + this.mediaUploadId = const Value.absent(), + this.state = const Value.absent(), + this.metadata = const Value.absent(), + this.messageIds = const Value.absent(), + this.encryptionData = const Value.absent(), + }); + MediaUploadsCompanion.insert({ + this.mediaUploadId = const Value.absent(), + this.state = const Value.absent(), + this.metadata = const Value.absent(), + this.messageIds = const Value.absent(), + this.encryptionData = const Value.absent(), + }); + static Insertable custom({ + Expression? mediaUploadId, + Expression? state, + Expression? metadata, + Expression? messageIds, + Expression? encryptionData, + }) { + return RawValuesInsertable({ + if (mediaUploadId != null) 'media_upload_id': mediaUploadId, + if (state != null) 'state': state, + if (metadata != null) 'metadata': metadata, + if (messageIds != null) 'message_ids': messageIds, + if (encryptionData != null) 'encryption_data': encryptionData, + }); + } + + MediaUploadsCompanion copyWith( + {Value? mediaUploadId, + Value? state, + Value? metadata, + Value? messageIds, + Value? encryptionData}) { + return MediaUploadsCompanion( + mediaUploadId: mediaUploadId ?? this.mediaUploadId, + state: state ?? this.state, + metadata: metadata ?? this.metadata, + messageIds: messageIds ?? this.messageIds, + encryptionData: encryptionData ?? this.encryptionData, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (mediaUploadId.present) { + map['media_upload_id'] = Variable(mediaUploadId.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (metadata.present) { + map['metadata'] = Variable(metadata.value); + } + if (messageIds.present) { + map['message_ids'] = Variable(messageIds.value); + } + if (encryptionData.present) { + map['encryption_data'] = Variable(encryptionData.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MediaUploadsCompanion(') + ..write('mediaUploadId: $mediaUploadId, ') + ..write('state: $state, ') + ..write('metadata: $metadata, ') + ..write('messageIds: $messageIds, ') + ..write('encryptionData: $encryptionData') + ..write(')')) + .toString(); + } +} + +class SignalIdentityKeyStores extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalIdentityKeyStores(this.attachedDatabase, [this._alias]); + late final GeneratedColumn deviceId = GeneratedColumn( + 'device_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn identityKey = + GeneratedColumn('identity_key', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + @override + List get $columns => + [deviceId, name, identityKey, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_identity_key_stores'; + @override + Set get $primaryKey => {deviceId, name}; + @override + SignalIdentityKeyStoresData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalIdentityKeyStoresData( + deviceId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}device_id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + identityKey: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}identity_key'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + ); + } + + @override + SignalIdentityKeyStores createAlias(String alias) { + return SignalIdentityKeyStores(attachedDatabase, alias); + } +} + +class SignalIdentityKeyStoresData extends DataClass + implements Insertable { + final int deviceId; + final String name; + final i2.Uint8List identityKey; + final DateTime createdAt; + const SignalIdentityKeyStoresData( + {required this.deviceId, + required this.name, + required this.identityKey, + required this.createdAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['device_id'] = Variable(deviceId); + map['name'] = Variable(name); + map['identity_key'] = Variable(identityKey); + map['created_at'] = Variable(createdAt); + return map; + } + + SignalIdentityKeyStoresCompanion toCompanion(bool nullToAbsent) { + return SignalIdentityKeyStoresCompanion( + deviceId: Value(deviceId), + name: Value(name), + identityKey: Value(identityKey), + createdAt: Value(createdAt), + ); + } + + factory SignalIdentityKeyStoresData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalIdentityKeyStoresData( + deviceId: serializer.fromJson(json['deviceId']), + name: serializer.fromJson(json['name']), + identityKey: serializer.fromJson(json['identityKey']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'deviceId': serializer.toJson(deviceId), + 'name': serializer.toJson(name), + 'identityKey': serializer.toJson(identityKey), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SignalIdentityKeyStoresData copyWith( + {int? deviceId, + String? name, + i2.Uint8List? identityKey, + DateTime? createdAt}) => + SignalIdentityKeyStoresData( + deviceId: deviceId ?? this.deviceId, + name: name ?? this.name, + identityKey: identityKey ?? this.identityKey, + createdAt: createdAt ?? this.createdAt, + ); + SignalIdentityKeyStoresData copyWithCompanion( + SignalIdentityKeyStoresCompanion data) { + return SignalIdentityKeyStoresData( + deviceId: data.deviceId.present ? data.deviceId.value : this.deviceId, + name: data.name.present ? data.name.value : this.name, + identityKey: + data.identityKey.present ? data.identityKey.value : this.identityKey, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SignalIdentityKeyStoresData(') + ..write('deviceId: $deviceId, ') + ..write('name: $name, ') + ..write('identityKey: $identityKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + deviceId, name, $driftBlobEquality.hash(identityKey), createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalIdentityKeyStoresData && + other.deviceId == this.deviceId && + other.name == this.name && + $driftBlobEquality.equals(other.identityKey, this.identityKey) && + other.createdAt == this.createdAt); +} + +class SignalIdentityKeyStoresCompanion + extends UpdateCompanion { + final Value deviceId; + final Value name; + final Value identityKey; + final Value createdAt; + final Value rowid; + const SignalIdentityKeyStoresCompanion({ + this.deviceId = const Value.absent(), + this.name = const Value.absent(), + this.identityKey = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + SignalIdentityKeyStoresCompanion.insert({ + required int deviceId, + required String name, + required i2.Uint8List identityKey, + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : deviceId = Value(deviceId), + name = Value(name), + identityKey = Value(identityKey); + static Insertable custom({ + Expression? deviceId, + Expression? name, + Expression? identityKey, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (deviceId != null) 'device_id': deviceId, + if (name != null) 'name': name, + if (identityKey != null) 'identity_key': identityKey, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + SignalIdentityKeyStoresCompanion copyWith( + {Value? deviceId, + Value? name, + Value? identityKey, + Value? createdAt, + Value? rowid}) { + return SignalIdentityKeyStoresCompanion( + deviceId: deviceId ?? this.deviceId, + name: name ?? this.name, + identityKey: identityKey ?? this.identityKey, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (identityKey.present) { + map['identity_key'] = Variable(identityKey.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalIdentityKeyStoresCompanion(') + ..write('deviceId: $deviceId, ') + ..write('name: $name, ') + ..write('identityKey: $identityKey, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class SignalPreKeyStores extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalPreKeyStores(this.attachedDatabase, [this._alias]); + late final GeneratedColumn preKeyId = GeneratedColumn( + 'pre_key_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn preKey = + GeneratedColumn('pre_key', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + @override + List get $columns => [preKeyId, preKey, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_pre_key_stores'; + @override + Set get $primaryKey => {preKeyId}; + @override + SignalPreKeyStoresData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalPreKeyStoresData( + preKeyId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}pre_key_id'])!, + preKey: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}pre_key'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + ); + } + + @override + SignalPreKeyStores createAlias(String alias) { + return SignalPreKeyStores(attachedDatabase, alias); + } +} + +class SignalPreKeyStoresData extends DataClass + implements Insertable { + final int preKeyId; + final i2.Uint8List preKey; + final DateTime createdAt; + const SignalPreKeyStoresData( + {required this.preKeyId, required this.preKey, required this.createdAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['pre_key_id'] = Variable(preKeyId); + map['pre_key'] = Variable(preKey); + map['created_at'] = Variable(createdAt); + return map; + } + + SignalPreKeyStoresCompanion toCompanion(bool nullToAbsent) { + return SignalPreKeyStoresCompanion( + preKeyId: Value(preKeyId), + preKey: Value(preKey), + createdAt: Value(createdAt), + ); + } + + factory SignalPreKeyStoresData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalPreKeyStoresData( + preKeyId: serializer.fromJson(json['preKeyId']), + preKey: serializer.fromJson(json['preKey']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'preKeyId': serializer.toJson(preKeyId), + 'preKey': serializer.toJson(preKey), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SignalPreKeyStoresData copyWith( + {int? preKeyId, i2.Uint8List? preKey, DateTime? createdAt}) => + SignalPreKeyStoresData( + preKeyId: preKeyId ?? this.preKeyId, + preKey: preKey ?? this.preKey, + createdAt: createdAt ?? this.createdAt, + ); + SignalPreKeyStoresData copyWithCompanion(SignalPreKeyStoresCompanion data) { + return SignalPreKeyStoresData( + preKeyId: data.preKeyId.present ? data.preKeyId.value : this.preKeyId, + preKey: data.preKey.present ? data.preKey.value : this.preKey, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SignalPreKeyStoresData(') + ..write('preKeyId: $preKeyId, ') + ..write('preKey: $preKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(preKeyId, $driftBlobEquality.hash(preKey), createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalPreKeyStoresData && + other.preKeyId == this.preKeyId && + $driftBlobEquality.equals(other.preKey, this.preKey) && + other.createdAt == this.createdAt); +} + +class SignalPreKeyStoresCompanion + extends UpdateCompanion { + final Value preKeyId; + final Value preKey; + final Value createdAt; + const SignalPreKeyStoresCompanion({ + this.preKeyId = const Value.absent(), + this.preKey = const Value.absent(), + this.createdAt = const Value.absent(), + }); + SignalPreKeyStoresCompanion.insert({ + this.preKeyId = const Value.absent(), + required i2.Uint8List preKey, + this.createdAt = const Value.absent(), + }) : preKey = Value(preKey); + static Insertable custom({ + Expression? preKeyId, + Expression? preKey, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (preKeyId != null) 'pre_key_id': preKeyId, + if (preKey != null) 'pre_key': preKey, + if (createdAt != null) 'created_at': createdAt, + }); + } + + SignalPreKeyStoresCompanion copyWith( + {Value? preKeyId, + Value? preKey, + Value? createdAt}) { + return SignalPreKeyStoresCompanion( + preKeyId: preKeyId ?? this.preKeyId, + preKey: preKey ?? this.preKey, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (preKeyId.present) { + map['pre_key_id'] = Variable(preKeyId.value); + } + if (preKey.present) { + map['pre_key'] = Variable(preKey.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalPreKeyStoresCompanion(') + ..write('preKeyId: $preKeyId, ') + ..write('preKey: $preKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class SignalSenderKeyStores extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalSenderKeyStores(this.attachedDatabase, [this._alias]); + late final GeneratedColumn senderKeyName = GeneratedColumn( + 'sender_key_name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn senderKey = + GeneratedColumn('sender_key', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + @override + List get $columns => [senderKeyName, senderKey]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_sender_key_stores'; + @override + Set get $primaryKey => {senderKeyName}; + @override + SignalSenderKeyStoresData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalSenderKeyStoresData( + senderKeyName: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}sender_key_name'])!, + senderKey: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}sender_key'])!, + ); + } + + @override + SignalSenderKeyStores createAlias(String alias) { + return SignalSenderKeyStores(attachedDatabase, alias); + } +} + +class SignalSenderKeyStoresData extends DataClass + implements Insertable { + final String senderKeyName; + final i2.Uint8List senderKey; + const SignalSenderKeyStoresData( + {required this.senderKeyName, required this.senderKey}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['sender_key_name'] = Variable(senderKeyName); + map['sender_key'] = Variable(senderKey); + return map; + } + + SignalSenderKeyStoresCompanion toCompanion(bool nullToAbsent) { + return SignalSenderKeyStoresCompanion( + senderKeyName: Value(senderKeyName), + senderKey: Value(senderKey), + ); + } + + factory SignalSenderKeyStoresData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalSenderKeyStoresData( + senderKeyName: serializer.fromJson(json['senderKeyName']), + senderKey: serializer.fromJson(json['senderKey']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'senderKeyName': serializer.toJson(senderKeyName), + 'senderKey': serializer.toJson(senderKey), + }; + } + + SignalSenderKeyStoresData copyWith( + {String? senderKeyName, i2.Uint8List? senderKey}) => + SignalSenderKeyStoresData( + senderKeyName: senderKeyName ?? this.senderKeyName, + senderKey: senderKey ?? this.senderKey, + ); + SignalSenderKeyStoresData copyWithCompanion( + SignalSenderKeyStoresCompanion data) { + return SignalSenderKeyStoresData( + senderKeyName: data.senderKeyName.present + ? data.senderKeyName.value + : this.senderKeyName, + senderKey: data.senderKey.present ? data.senderKey.value : this.senderKey, + ); + } + + @override + String toString() { + return (StringBuffer('SignalSenderKeyStoresData(') + ..write('senderKeyName: $senderKeyName, ') + ..write('senderKey: $senderKey') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(senderKeyName, $driftBlobEquality.hash(senderKey)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalSenderKeyStoresData && + other.senderKeyName == this.senderKeyName && + $driftBlobEquality.equals(other.senderKey, this.senderKey)); +} + +class SignalSenderKeyStoresCompanion + extends UpdateCompanion { + final Value senderKeyName; + final Value senderKey; + final Value rowid; + const SignalSenderKeyStoresCompanion({ + this.senderKeyName = const Value.absent(), + this.senderKey = const Value.absent(), + this.rowid = const Value.absent(), + }); + SignalSenderKeyStoresCompanion.insert({ + required String senderKeyName, + required i2.Uint8List senderKey, + this.rowid = const Value.absent(), + }) : senderKeyName = Value(senderKeyName), + senderKey = Value(senderKey); + static Insertable custom({ + Expression? senderKeyName, + Expression? senderKey, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (senderKeyName != null) 'sender_key_name': senderKeyName, + if (senderKey != null) 'sender_key': senderKey, + if (rowid != null) 'rowid': rowid, + }); + } + + SignalSenderKeyStoresCompanion copyWith( + {Value? senderKeyName, + Value? senderKey, + Value? rowid}) { + return SignalSenderKeyStoresCompanion( + senderKeyName: senderKeyName ?? this.senderKeyName, + senderKey: senderKey ?? this.senderKey, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (senderKeyName.present) { + map['sender_key_name'] = Variable(senderKeyName.value); + } + if (senderKey.present) { + map['sender_key'] = Variable(senderKey.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalSenderKeyStoresCompanion(') + ..write('senderKeyName: $senderKeyName, ') + ..write('senderKey: $senderKey, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class SignalSessionStores extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalSessionStores(this.attachedDatabase, [this._alias]); + late final GeneratedColumn deviceId = GeneratedColumn( + 'device_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn sessionRecord = + GeneratedColumn('session_record', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + @override + List get $columns => + [deviceId, name, sessionRecord, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_session_stores'; + @override + Set get $primaryKey => {deviceId, name}; + @override + SignalSessionStoresData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalSessionStoresData( + deviceId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}device_id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + sessionRecord: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}session_record'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + ); + } + + @override + SignalSessionStores createAlias(String alias) { + return SignalSessionStores(attachedDatabase, alias); + } +} + +class SignalSessionStoresData extends DataClass + implements Insertable { + final int deviceId; + final String name; + final i2.Uint8List sessionRecord; + final DateTime createdAt; + const SignalSessionStoresData( + {required this.deviceId, + required this.name, + required this.sessionRecord, + required this.createdAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['device_id'] = Variable(deviceId); + map['name'] = Variable(name); + map['session_record'] = Variable(sessionRecord); + map['created_at'] = Variable(createdAt); + return map; + } + + SignalSessionStoresCompanion toCompanion(bool nullToAbsent) { + return SignalSessionStoresCompanion( + deviceId: Value(deviceId), + name: Value(name), + sessionRecord: Value(sessionRecord), + createdAt: Value(createdAt), + ); + } + + factory SignalSessionStoresData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalSessionStoresData( + deviceId: serializer.fromJson(json['deviceId']), + name: serializer.fromJson(json['name']), + sessionRecord: serializer.fromJson(json['sessionRecord']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'deviceId': serializer.toJson(deviceId), + 'name': serializer.toJson(name), + 'sessionRecord': serializer.toJson(sessionRecord), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SignalSessionStoresData copyWith( + {int? deviceId, + String? name, + i2.Uint8List? sessionRecord, + DateTime? createdAt}) => + SignalSessionStoresData( + deviceId: deviceId ?? this.deviceId, + name: name ?? this.name, + sessionRecord: sessionRecord ?? this.sessionRecord, + createdAt: createdAt ?? this.createdAt, + ); + SignalSessionStoresData copyWithCompanion(SignalSessionStoresCompanion data) { + return SignalSessionStoresData( + deviceId: data.deviceId.present ? data.deviceId.value : this.deviceId, + name: data.name.present ? data.name.value : this.name, + sessionRecord: data.sessionRecord.present + ? data.sessionRecord.value + : this.sessionRecord, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SignalSessionStoresData(') + ..write('deviceId: $deviceId, ') + ..write('name: $name, ') + ..write('sessionRecord: $sessionRecord, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + deviceId, name, $driftBlobEquality.hash(sessionRecord), createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalSessionStoresData && + other.deviceId == this.deviceId && + other.name == this.name && + $driftBlobEquality.equals(other.sessionRecord, this.sessionRecord) && + other.createdAt == this.createdAt); +} + +class SignalSessionStoresCompanion + extends UpdateCompanion { + final Value deviceId; + final Value name; + final Value sessionRecord; + final Value createdAt; + final Value rowid; + const SignalSessionStoresCompanion({ + this.deviceId = const Value.absent(), + this.name = const Value.absent(), + this.sessionRecord = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + SignalSessionStoresCompanion.insert({ + required int deviceId, + required String name, + required i2.Uint8List sessionRecord, + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : deviceId = Value(deviceId), + name = Value(name), + sessionRecord = Value(sessionRecord); + static Insertable custom({ + Expression? deviceId, + Expression? name, + Expression? sessionRecord, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (deviceId != null) 'device_id': deviceId, + if (name != null) 'name': name, + if (sessionRecord != null) 'session_record': sessionRecord, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + SignalSessionStoresCompanion copyWith( + {Value? deviceId, + Value? name, + Value? sessionRecord, + Value? createdAt, + Value? rowid}) { + return SignalSessionStoresCompanion( + deviceId: deviceId ?? this.deviceId, + name: name ?? this.name, + sessionRecord: sessionRecord ?? this.sessionRecord, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (sessionRecord.present) { + map['session_record'] = Variable(sessionRecord.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalSessionStoresCompanion(') + ..write('deviceId: $deviceId, ') + ..write('name: $name, ') + ..write('sessionRecord: $sessionRecord, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class SignalContactPreKeys extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalContactPreKeys(this.attachedDatabase, [this._alias]); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn preKeyId = GeneratedColumn( + 'pre_key_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn preKey = + GeneratedColumn('pre_key', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + @override + List get $columns => + [contactId, preKeyId, preKey, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_contact_pre_keys'; + @override + Set get $primaryKey => {contactId, preKeyId}; + @override + SignalContactPreKeysData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalContactPreKeysData( + contactId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}contact_id'])!, + preKeyId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}pre_key_id'])!, + preKey: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}pre_key'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + ); + } + + @override + SignalContactPreKeys createAlias(String alias) { + return SignalContactPreKeys(attachedDatabase, alias); + } +} + +class SignalContactPreKeysData extends DataClass + implements Insertable { + final int contactId; + final int preKeyId; + final i2.Uint8List preKey; + final DateTime createdAt; + const SignalContactPreKeysData( + {required this.contactId, + required this.preKeyId, + required this.preKey, + required this.createdAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['contact_id'] = Variable(contactId); + map['pre_key_id'] = Variable(preKeyId); + map['pre_key'] = Variable(preKey); + map['created_at'] = Variable(createdAt); + return map; + } + + SignalContactPreKeysCompanion toCompanion(bool nullToAbsent) { + return SignalContactPreKeysCompanion( + contactId: Value(contactId), + preKeyId: Value(preKeyId), + preKey: Value(preKey), + createdAt: Value(createdAt), + ); + } + + factory SignalContactPreKeysData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalContactPreKeysData( + contactId: serializer.fromJson(json['contactId']), + preKeyId: serializer.fromJson(json['preKeyId']), + preKey: serializer.fromJson(json['preKey']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'contactId': serializer.toJson(contactId), + 'preKeyId': serializer.toJson(preKeyId), + 'preKey': serializer.toJson(preKey), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SignalContactPreKeysData copyWith( + {int? contactId, + int? preKeyId, + i2.Uint8List? preKey, + DateTime? createdAt}) => + SignalContactPreKeysData( + contactId: contactId ?? this.contactId, + preKeyId: preKeyId ?? this.preKeyId, + preKey: preKey ?? this.preKey, + createdAt: createdAt ?? this.createdAt, + ); + SignalContactPreKeysData copyWithCompanion( + SignalContactPreKeysCompanion data) { + return SignalContactPreKeysData( + contactId: data.contactId.present ? data.contactId.value : this.contactId, + preKeyId: data.preKeyId.present ? data.preKeyId.value : this.preKeyId, + preKey: data.preKey.present ? data.preKey.value : this.preKey, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SignalContactPreKeysData(') + ..write('contactId: $contactId, ') + ..write('preKeyId: $preKeyId, ') + ..write('preKey: $preKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + contactId, preKeyId, $driftBlobEquality.hash(preKey), createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalContactPreKeysData && + other.contactId == this.contactId && + other.preKeyId == this.preKeyId && + $driftBlobEquality.equals(other.preKey, this.preKey) && + other.createdAt == this.createdAt); +} + +class SignalContactPreKeysCompanion + extends UpdateCompanion { + final Value contactId; + final Value preKeyId; + final Value preKey; + final Value createdAt; + final Value rowid; + const SignalContactPreKeysCompanion({ + this.contactId = const Value.absent(), + this.preKeyId = const Value.absent(), + this.preKey = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + SignalContactPreKeysCompanion.insert({ + required int contactId, + required int preKeyId, + required i2.Uint8List preKey, + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : contactId = Value(contactId), + preKeyId = Value(preKeyId), + preKey = Value(preKey); + static Insertable custom({ + Expression? contactId, + Expression? preKeyId, + Expression? preKey, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (contactId != null) 'contact_id': contactId, + if (preKeyId != null) 'pre_key_id': preKeyId, + if (preKey != null) 'pre_key': preKey, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + SignalContactPreKeysCompanion copyWith( + {Value? contactId, + Value? preKeyId, + Value? preKey, + Value? createdAt, + Value? rowid}) { + return SignalContactPreKeysCompanion( + contactId: contactId ?? this.contactId, + preKeyId: preKeyId ?? this.preKeyId, + preKey: preKey ?? this.preKey, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (preKeyId.present) { + map['pre_key_id'] = Variable(preKeyId.value); + } + if (preKey.present) { + map['pre_key'] = Variable(preKey.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalContactPreKeysCompanion(') + ..write('contactId: $contactId, ') + ..write('preKeyId: $preKeyId, ') + ..write('preKey: $preKey, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class SignalContactSignedPreKeys extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalContactSignedPreKeys(this.attachedDatabase, [this._alias]); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn signedPreKeyId = GeneratedColumn( + 'signed_pre_key_id', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn signedPreKey = + GeneratedColumn('signed_pre_key', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn signedPreKeySignature = + GeneratedColumn( + 'signed_pre_key_signature', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)')); + @override + List get $columns => [ + contactId, + signedPreKeyId, + signedPreKey, + signedPreKeySignature, + createdAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_contact_signed_pre_keys'; + @override + Set get $primaryKey => {contactId}; + @override + SignalContactSignedPreKeysData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalContactSignedPreKeysData( + contactId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}contact_id'])!, + signedPreKeyId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}signed_pre_key_id'])!, + signedPreKey: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}signed_pre_key'])!, + signedPreKeySignature: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}signed_pre_key_signature'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + ); + } + + @override + SignalContactSignedPreKeys createAlias(String alias) { + return SignalContactSignedPreKeys(attachedDatabase, alias); + } +} + +class SignalContactSignedPreKeysData extends DataClass + implements Insertable { + final int contactId; + final int signedPreKeyId; + final i2.Uint8List signedPreKey; + final i2.Uint8List signedPreKeySignature; + final DateTime createdAt; + const SignalContactSignedPreKeysData( + {required this.contactId, + required this.signedPreKeyId, + required this.signedPreKey, + required this.signedPreKeySignature, + required this.createdAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['contact_id'] = Variable(contactId); + map['signed_pre_key_id'] = Variable(signedPreKeyId); + map['signed_pre_key'] = Variable(signedPreKey); + map['signed_pre_key_signature'] = + Variable(signedPreKeySignature); + map['created_at'] = Variable(createdAt); + return map; + } + + SignalContactSignedPreKeysCompanion toCompanion(bool nullToAbsent) { + return SignalContactSignedPreKeysCompanion( + contactId: Value(contactId), + signedPreKeyId: Value(signedPreKeyId), + signedPreKey: Value(signedPreKey), + signedPreKeySignature: Value(signedPreKeySignature), + createdAt: Value(createdAt), + ); + } + + factory SignalContactSignedPreKeysData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalContactSignedPreKeysData( + contactId: serializer.fromJson(json['contactId']), + signedPreKeyId: serializer.fromJson(json['signedPreKeyId']), + signedPreKey: serializer.fromJson(json['signedPreKey']), + signedPreKeySignature: + serializer.fromJson(json['signedPreKeySignature']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'contactId': serializer.toJson(contactId), + 'signedPreKeyId': serializer.toJson(signedPreKeyId), + 'signedPreKey': serializer.toJson(signedPreKey), + 'signedPreKeySignature': + serializer.toJson(signedPreKeySignature), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SignalContactSignedPreKeysData copyWith( + {int? contactId, + int? signedPreKeyId, + i2.Uint8List? signedPreKey, + i2.Uint8List? signedPreKeySignature, + DateTime? createdAt}) => + SignalContactSignedPreKeysData( + contactId: contactId ?? this.contactId, + signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId, + signedPreKey: signedPreKey ?? this.signedPreKey, + signedPreKeySignature: + signedPreKeySignature ?? this.signedPreKeySignature, + createdAt: createdAt ?? this.createdAt, + ); + SignalContactSignedPreKeysData copyWithCompanion( + SignalContactSignedPreKeysCompanion data) { + return SignalContactSignedPreKeysData( + contactId: data.contactId.present ? data.contactId.value : this.contactId, + signedPreKeyId: data.signedPreKeyId.present + ? data.signedPreKeyId.value + : this.signedPreKeyId, + signedPreKey: data.signedPreKey.present + ? data.signedPreKey.value + : this.signedPreKey, + signedPreKeySignature: data.signedPreKeySignature.present + ? data.signedPreKeySignature.value + : this.signedPreKeySignature, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SignalContactSignedPreKeysData(') + ..write('contactId: $contactId, ') + ..write('signedPreKeyId: $signedPreKeyId, ') + ..write('signedPreKey: $signedPreKey, ') + ..write('signedPreKeySignature: $signedPreKeySignature, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + contactId, + signedPreKeyId, + $driftBlobEquality.hash(signedPreKey), + $driftBlobEquality.hash(signedPreKeySignature), + createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalContactSignedPreKeysData && + other.contactId == this.contactId && + other.signedPreKeyId == this.signedPreKeyId && + $driftBlobEquality.equals(other.signedPreKey, this.signedPreKey) && + $driftBlobEquality.equals( + other.signedPreKeySignature, this.signedPreKeySignature) && + other.createdAt == this.createdAt); +} + +class SignalContactSignedPreKeysCompanion + extends UpdateCompanion { + final Value contactId; + final Value signedPreKeyId; + final Value signedPreKey; + final Value signedPreKeySignature; + final Value createdAt; + const SignalContactSignedPreKeysCompanion({ + this.contactId = const Value.absent(), + this.signedPreKeyId = const Value.absent(), + this.signedPreKey = const Value.absent(), + this.signedPreKeySignature = const Value.absent(), + this.createdAt = const Value.absent(), + }); + SignalContactSignedPreKeysCompanion.insert({ + this.contactId = const Value.absent(), + required int signedPreKeyId, + required i2.Uint8List signedPreKey, + required i2.Uint8List signedPreKeySignature, + this.createdAt = const Value.absent(), + }) : signedPreKeyId = Value(signedPreKeyId), + signedPreKey = Value(signedPreKey), + signedPreKeySignature = Value(signedPreKeySignature); + static Insertable custom({ + Expression? contactId, + Expression? signedPreKeyId, + Expression? signedPreKey, + Expression? signedPreKeySignature, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (contactId != null) 'contact_id': contactId, + if (signedPreKeyId != null) 'signed_pre_key_id': signedPreKeyId, + if (signedPreKey != null) 'signed_pre_key': signedPreKey, + if (signedPreKeySignature != null) + 'signed_pre_key_signature': signedPreKeySignature, + if (createdAt != null) 'created_at': createdAt, + }); + } + + SignalContactSignedPreKeysCompanion copyWith( + {Value? contactId, + Value? signedPreKeyId, + Value? signedPreKey, + Value? signedPreKeySignature, + Value? createdAt}) { + return SignalContactSignedPreKeysCompanion( + contactId: contactId ?? this.contactId, + signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId, + signedPreKey: signedPreKey ?? this.signedPreKey, + signedPreKeySignature: + signedPreKeySignature ?? this.signedPreKeySignature, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (signedPreKeyId.present) { + map['signed_pre_key_id'] = Variable(signedPreKeyId.value); + } + if (signedPreKey.present) { + map['signed_pre_key'] = Variable(signedPreKey.value); + } + if (signedPreKeySignature.present) { + map['signed_pre_key_signature'] = + Variable(signedPreKeySignature.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalContactSignedPreKeysCompanion(') + ..write('contactId: $contactId, ') + ..write('signedPreKeyId: $signedPreKeyId, ') + ..write('signedPreKey: $signedPreKey, ') + ..write('signedPreKeySignature: $signedPreKeySignature, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class MessageRetransmissions extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MessageRetransmissions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn retransmissionId = GeneratedColumn( + 'retransmission_id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES contacts (user_id) ON DELETE CASCADE')); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', aliasedName, true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES messages (message_id) ON DELETE CASCADE')); + late final GeneratedColumn plaintextContent = + GeneratedColumn('plaintext_content', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn pushData = + GeneratedColumn('push_data', aliasedName, true, + type: DriftSqlType.blob, requiredDuringInsert: false); + late final GeneratedColumn encryptedHash = + GeneratedColumn('encrypted_hash', aliasedName, true, + type: DriftSqlType.blob, requiredDuringInsert: false); + late final GeneratedColumn retryCount = GeneratedColumn( + 'retry_count', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + late final GeneratedColumn lastRetry = GeneratedColumn( + 'last_retry', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn acknowledgeByServerAt = + GeneratedColumn('acknowledge_by_server_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + retransmissionId, + contactId, + messageId, + plaintextContent, + pushData, + encryptedHash, + retryCount, + lastRetry, + acknowledgeByServerAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'message_retransmissions'; + @override + Set get $primaryKey => {retransmissionId}; + @override + MessageRetransmissionsData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MessageRetransmissionsData( + retransmissionId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}retransmission_id'])!, + contactId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}contact_id'])!, + messageId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}message_id']), + plaintextContent: attachedDatabase.typeMapping.read( + DriftSqlType.blob, data['${effectivePrefix}plaintext_content'])!, + pushData: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}push_data']), + encryptedHash: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}encrypted_hash']), + retryCount: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}retry_count'])!, + lastRetry: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}last_retry']), + acknowledgeByServerAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}acknowledge_by_server_at']), + ); + } + + @override + MessageRetransmissions createAlias(String alias) { + return MessageRetransmissions(attachedDatabase, alias); + } +} + +class MessageRetransmissionsData extends DataClass + implements Insertable { + final int retransmissionId; + final int contactId; + final int? messageId; + final i2.Uint8List plaintextContent; + final i2.Uint8List? pushData; + final i2.Uint8List? encryptedHash; + final int retryCount; + final DateTime? lastRetry; + final DateTime? acknowledgeByServerAt; + const MessageRetransmissionsData( + {required this.retransmissionId, + required this.contactId, + this.messageId, + required this.plaintextContent, + this.pushData, + this.encryptedHash, + required this.retryCount, + this.lastRetry, + this.acknowledgeByServerAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['retransmission_id'] = Variable(retransmissionId); + map['contact_id'] = Variable(contactId); + if (!nullToAbsent || messageId != null) { + map['message_id'] = Variable(messageId); + } + map['plaintext_content'] = Variable(plaintextContent); + if (!nullToAbsent || pushData != null) { + map['push_data'] = Variable(pushData); + } + if (!nullToAbsent || encryptedHash != null) { + map['encrypted_hash'] = Variable(encryptedHash); + } + map['retry_count'] = Variable(retryCount); + if (!nullToAbsent || lastRetry != null) { + map['last_retry'] = Variable(lastRetry); + } + if (!nullToAbsent || acknowledgeByServerAt != null) { + map['acknowledge_by_server_at'] = + Variable(acknowledgeByServerAt); + } + return map; + } + + MessageRetransmissionsCompanion toCompanion(bool nullToAbsent) { + return MessageRetransmissionsCompanion( + retransmissionId: Value(retransmissionId), + contactId: Value(contactId), + messageId: messageId == null && nullToAbsent + ? const Value.absent() + : Value(messageId), + plaintextContent: Value(plaintextContent), + pushData: pushData == null && nullToAbsent + ? const Value.absent() + : Value(pushData), + encryptedHash: encryptedHash == null && nullToAbsent + ? const Value.absent() + : Value(encryptedHash), + retryCount: Value(retryCount), + lastRetry: lastRetry == null && nullToAbsent + ? const Value.absent() + : Value(lastRetry), + acknowledgeByServerAt: acknowledgeByServerAt == null && nullToAbsent + ? const Value.absent() + : Value(acknowledgeByServerAt), + ); + } + + factory MessageRetransmissionsData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessageRetransmissionsData( + retransmissionId: serializer.fromJson(json['retransmissionId']), + contactId: serializer.fromJson(json['contactId']), + messageId: serializer.fromJson(json['messageId']), + plaintextContent: + serializer.fromJson(json['plaintextContent']), + pushData: serializer.fromJson(json['pushData']), + encryptedHash: serializer.fromJson(json['encryptedHash']), + retryCount: serializer.fromJson(json['retryCount']), + lastRetry: serializer.fromJson(json['lastRetry']), + acknowledgeByServerAt: + serializer.fromJson(json['acknowledgeByServerAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'retransmissionId': serializer.toJson(retransmissionId), + 'contactId': serializer.toJson(contactId), + 'messageId': serializer.toJson(messageId), + 'plaintextContent': serializer.toJson(plaintextContent), + 'pushData': serializer.toJson(pushData), + 'encryptedHash': serializer.toJson(encryptedHash), + 'retryCount': serializer.toJson(retryCount), + 'lastRetry': serializer.toJson(lastRetry), + 'acknowledgeByServerAt': + serializer.toJson(acknowledgeByServerAt), + }; + } + + MessageRetransmissionsData copyWith( + {int? retransmissionId, + int? contactId, + Value messageId = const Value.absent(), + i2.Uint8List? plaintextContent, + Value pushData = const Value.absent(), + Value encryptedHash = const Value.absent(), + int? retryCount, + Value lastRetry = const Value.absent(), + Value acknowledgeByServerAt = const Value.absent()}) => + MessageRetransmissionsData( + retransmissionId: retransmissionId ?? this.retransmissionId, + contactId: contactId ?? this.contactId, + messageId: messageId.present ? messageId.value : this.messageId, + plaintextContent: plaintextContent ?? this.plaintextContent, + pushData: pushData.present ? pushData.value : this.pushData, + encryptedHash: + encryptedHash.present ? encryptedHash.value : this.encryptedHash, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry.present ? lastRetry.value : this.lastRetry, + acknowledgeByServerAt: acknowledgeByServerAt.present + ? acknowledgeByServerAt.value + : this.acknowledgeByServerAt, + ); + MessageRetransmissionsData copyWithCompanion( + MessageRetransmissionsCompanion data) { + return MessageRetransmissionsData( + retransmissionId: data.retransmissionId.present + ? data.retransmissionId.value + : this.retransmissionId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + plaintextContent: data.plaintextContent.present + ? data.plaintextContent.value + : this.plaintextContent, + pushData: data.pushData.present ? data.pushData.value : this.pushData, + encryptedHash: data.encryptedHash.present + ? data.encryptedHash.value + : this.encryptedHash, + retryCount: + data.retryCount.present ? data.retryCount.value : this.retryCount, + lastRetry: data.lastRetry.present ? data.lastRetry.value : this.lastRetry, + acknowledgeByServerAt: data.acknowledgeByServerAt.present + ? data.acknowledgeByServerAt.value + : this.acknowledgeByServerAt, + ); + } + + @override + String toString() { + return (StringBuffer('MessageRetransmissionsData(') + ..write('retransmissionId: $retransmissionId, ') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('plaintextContent: $plaintextContent, ') + ..write('pushData: $pushData, ') + ..write('encryptedHash: $encryptedHash, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') + ..write('acknowledgeByServerAt: $acknowledgeByServerAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + retransmissionId, + contactId, + messageId, + $driftBlobEquality.hash(plaintextContent), + $driftBlobEquality.hash(pushData), + $driftBlobEquality.hash(encryptedHash), + retryCount, + lastRetry, + acknowledgeByServerAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessageRetransmissionsData && + other.retransmissionId == this.retransmissionId && + other.contactId == this.contactId && + other.messageId == this.messageId && + $driftBlobEquality.equals( + other.plaintextContent, this.plaintextContent) && + $driftBlobEquality.equals(other.pushData, this.pushData) && + $driftBlobEquality.equals(other.encryptedHash, this.encryptedHash) && + other.retryCount == this.retryCount && + other.lastRetry == this.lastRetry && + other.acknowledgeByServerAt == this.acknowledgeByServerAt); +} + +class MessageRetransmissionsCompanion + extends UpdateCompanion { + final Value retransmissionId; + final Value contactId; + final Value messageId; + final Value plaintextContent; + final Value pushData; + final Value encryptedHash; + final Value retryCount; + final Value lastRetry; + final Value acknowledgeByServerAt; + const MessageRetransmissionsCompanion({ + this.retransmissionId = const Value.absent(), + this.contactId = const Value.absent(), + this.messageId = const Value.absent(), + this.plaintextContent = const Value.absent(), + this.pushData = const Value.absent(), + this.encryptedHash = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), + this.acknowledgeByServerAt = const Value.absent(), + }); + MessageRetransmissionsCompanion.insert({ + this.retransmissionId = const Value.absent(), + required int contactId, + this.messageId = const Value.absent(), + required i2.Uint8List plaintextContent, + this.pushData = const Value.absent(), + this.encryptedHash = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), + this.acknowledgeByServerAt = const Value.absent(), + }) : contactId = Value(contactId), + plaintextContent = Value(plaintextContent); + static Insertable custom({ + Expression? retransmissionId, + Expression? contactId, + Expression? messageId, + Expression? plaintextContent, + Expression? pushData, + Expression? encryptedHash, + Expression? retryCount, + Expression? lastRetry, + Expression? acknowledgeByServerAt, + }) { + return RawValuesInsertable({ + if (retransmissionId != null) 'retransmission_id': retransmissionId, + if (contactId != null) 'contact_id': contactId, + if (messageId != null) 'message_id': messageId, + if (plaintextContent != null) 'plaintext_content': plaintextContent, + if (pushData != null) 'push_data': pushData, + if (encryptedHash != null) 'encrypted_hash': encryptedHash, + if (retryCount != null) 'retry_count': retryCount, + if (lastRetry != null) 'last_retry': lastRetry, + if (acknowledgeByServerAt != null) + 'acknowledge_by_server_at': acknowledgeByServerAt, + }); + } + + MessageRetransmissionsCompanion copyWith( + {Value? retransmissionId, + Value? contactId, + Value? messageId, + Value? plaintextContent, + Value? pushData, + Value? encryptedHash, + Value? retryCount, + Value? lastRetry, + Value? acknowledgeByServerAt}) { + return MessageRetransmissionsCompanion( + retransmissionId: retransmissionId ?? this.retransmissionId, + contactId: contactId ?? this.contactId, + messageId: messageId ?? this.messageId, + plaintextContent: plaintextContent ?? this.plaintextContent, + pushData: pushData ?? this.pushData, + encryptedHash: encryptedHash ?? this.encryptedHash, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry ?? this.lastRetry, + acknowledgeByServerAt: + acknowledgeByServerAt ?? this.acknowledgeByServerAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (retransmissionId.present) { + map['retransmission_id'] = Variable(retransmissionId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (plaintextContent.present) { + map['plaintext_content'] = Variable(plaintextContent.value); + } + if (pushData.present) { + map['push_data'] = Variable(pushData.value); + } + if (encryptedHash.present) { + map['encrypted_hash'] = Variable(encryptedHash.value); + } + if (retryCount.present) { + map['retry_count'] = Variable(retryCount.value); + } + if (lastRetry.present) { + map['last_retry'] = Variable(lastRetry.value); + } + if (acknowledgeByServerAt.present) { + map['acknowledge_by_server_at'] = + Variable(acknowledgeByServerAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessageRetransmissionsCompanion(') + ..write('retransmissionId: $retransmissionId, ') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('plaintextContent: $plaintextContent, ') + ..write('pushData: $pushData, ') + ..write('encryptedHash: $encryptedHash, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') + ..write('acknowledgeByServerAt: $acknowledgeByServerAt') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV17 extends GeneratedDatabase { + DatabaseAtV17(QueryExecutor e) : super(e); + late final Contacts contacts = Contacts(this); + late final Messages messages = Messages(this); + late final MediaUploads mediaUploads = MediaUploads(this); + late final SignalIdentityKeyStores signalIdentityKeyStores = + SignalIdentityKeyStores(this); + late final SignalPreKeyStores signalPreKeyStores = SignalPreKeyStores(this); + late final SignalSenderKeyStores signalSenderKeyStores = + SignalSenderKeyStores(this); + late final SignalSessionStores signalSessionStores = + SignalSessionStores(this); + late final SignalContactPreKeys signalContactPreKeys = + SignalContactPreKeys(this); + late final SignalContactSignedPreKeys signalContactSignedPreKeys = + SignalContactSignedPreKeys(this); + late final MessageRetransmissions messageRetransmissions = + MessageRetransmissions(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + contacts, + messages, + mediaUploads, + signalIdentityKeyStores, + signalPreKeyStores, + signalSenderKeyStores, + signalSessionStores, + signalContactPreKeys, + signalContactSignedPreKeys, + messageRetransmissions + ]; + @override + int get schemaVersion => 17; +}