diff --git a/CHANGELOG.md b/CHANGELOG.md index 5888736d..008bc369 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.3.7 + +- Fix: Multiple UI issues + +## 0.3.6 + +- Improve: Visibility of the verification badge + +## 0.3.5 + +- Fix: Performance issue caused by an out-of-sync Signal session +- Fix: Multiple smaller bug fixes + ## 0.3.3 - Fix: Multiple UI issues diff --git a/README.md b/README.md index 0b1e68ee..c4ba2a2a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,6 @@ If you decide to give twonly a try, please keep in mind that it is still in its - End-to-End encryption using the [Signal Protocol](https://de.wikipedia.org/wiki/Signal-Protokoll) - Open Source and can be downloaded directly from GitHub - No email or phone number required to register -- Privacy friendly - Everything is stored on the device - The backend is hosted exclusively in Europe ## Roadmap @@ -43,7 +42,6 @@ If you decide to give twonly a try, please keep in mind that it is still in its ### Currently - Focus on user-friendliness so that people enjoy using the app - - User discovery without a phone number - Passwordless recovery without a phone number - Implementation of features so that Snapchat can actually be replaced - E2EE cloud backup of memories @@ -63,36 +61,9 @@ If you discover a security issue in twonly, please adhere to the coordinated vul us your report to security@twonly.eu. We also offer for critical security issues a small bug bounties, but we can not guarantee a bounty currently :/ - - -## Development - -
-Setup Instructions (macOS) - -## Building - -Some dependencies are downloaded directly from the source as there are some new changes which are not yet published on -pub.dev or because they require some special installation. - -```bash -git submodule update --init --recursive - -cd dependencies/flutter_zxing -git submodule update --init --recursive -./scripts/update_ios_macos_src.s -``` - -## Debugging files - -```bash -run-as eu.twonly.testing ls /data/user/0/eu.twonly.testing/ -``` - -
+## Contribution +Currently there are still some core features and rewrites open I want to do like the switch to the MLS protocol involving a huge rewrite in Rust. Because of this, contributions are currently not wanted. You can still view the code (if you find any security issues please contact me!). Also, issues are currently closed. If you find a bug, please use the in-app option as there you can upload your debug log which helps a lot. ## Signing Keys diff --git a/lib/app.dart b/lib/app.dart index c327d972..8a0dc086 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -5,9 +5,13 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; +import 'package:twonly/src/constants/keyvalue.keys.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/localization/generated/app_localizations.dart'; +import 'package:twonly/src/model/json/onboarding_state.model.dart'; import 'package:twonly/src/providers/routing.provider.dart'; import 'package:twonly/src/providers/settings.provider.dart'; +import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/pow.dart'; import 'package:twonly/src/visual/components/app_outdated.comp.dart'; @@ -18,7 +22,7 @@ import 'package:twonly/src/visual/views/home.view.dart'; import 'package:twonly/src/visual/views/onboarding/onboarding.view.dart'; import 'package:twonly/src/visual/views/onboarding/register.view.dart'; import 'package:twonly/src/visual/views/onboarding/setup.view.dart'; -import 'package:twonly/src/visual/views/recovery.view.dart'; +import 'package:twonly/src/visual/views/recovery_from_secure_storage.view.dart'; import 'package:twonly/src/visual/views/unlock_twonly.view.dart'; class App extends StatefulWidget { @@ -151,6 +155,10 @@ class _AppMainWidgetState extends State { Future initAsync() async { Log.info('AppWidgetState: initAsync started'); + final onboardingState = await KeyValueStore.getModel( + KeyValueKeys.onboardingState, + ); + _showOnboarding = !onboardingState.hasOnboardingFinished; if (userService.isUserCreated) { if (_initialPage != 0) { final count = await twonlyDB.contactsDao.getContactsCount(); @@ -180,8 +188,19 @@ class _AppMainWidgetState extends State { } else { _proofOfWork = (null, disabled); } + + if (onboardingState.hasStartedPasswordlessRecovery || + onboardingState.emailRecoveryRequested) { + WidgetsBinding.instance.addPostFrameCallback((_) { + routerProvider.push(Routes.recoverPasswordless); + }); + } } + // await PasswordlessRecoveryService.handleRecoveryLink( + // 'https://me.twonly.eu/r/#7fdb8f08-0927-4e44-8761-038993414e48/1p7SKEzpxE3wSW9FQw60EUI4OpSW2U4EskdXLw8xg48', + // ); + setState(() { _isLoaded = true; }); @@ -223,9 +242,13 @@ class _AppMainWidgetState extends State { } } else if (_showOnboarding) { child = OnboardingView( - callbackOnSuccess: () => setState(() { - _showOnboarding = false; - }), + callbackOnSuccess: () async { + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (state) => state.hasOnboardingFinished = true, + ); + if (mounted) setState(() => _showOnboarding = false); + }, ); } else { child = RegisterView( diff --git a/lib/core/bridge/callbacks/user_discovery.dart b/lib/core/bridge/callbacks/user_discovery.dart new file mode 100644 index 00000000..569a0013 --- /dev/null +++ b/lib/core/bridge/callbacks/user_discovery.dart @@ -0,0 +1,173 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.12.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../../bridge.dart'; +import '../../frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone` + +class UserDiscoveryStoreFlutter { + const UserDiscoveryStoreFlutter(); + + Future getAnnouncedUserByPublicId({ + required PlatformInt64 publicId, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetAnnouncedUserByPublicId( + that: this, + publicId: publicId, + ); + + Future getConfig() => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetConfig( + that: this, + ); + + Future getContactPromotion({ + required PlatformInt64 contactId, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactPromotion( + that: this, + contactId: contactId, + ); + + Future getContactVersion({ + required PlatformInt64 contactId, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactVersion( + that: this, + contactId: contactId, + ); + + Future> getOtherPromotionsByPublicId({ + required PlatformInt64 publicId, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOtherPromotionsByPublicId( + that: this, + publicId: publicId, + ); + + Future> getOwnPromotionsAfterVersion({ + required int version, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOwnPromotionsAfterVersion( + that: this, + version: version, + ); + + Future getShareForContact({ + required PlatformInt64 contactId, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetShareForContact( + that: this, + contactId: contactId, + ); + + Future pushNewUserRelation({ + required PlatformInt64 fromContactId, + required AnnouncedUser announcedUser, + PlatformInt64? publicKeyVerifiedTimestamp, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushNewUserRelation( + that: this, + fromContactId: fromContactId, + announcedUser: announcedUser, + publicKeyVerifiedTimestamp: publicKeyVerifiedTimestamp, + ); + + Future pushOwnPromotionAndClearOldVersion({ + required PlatformInt64 contactId, + required int version, + required List promotion, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushOwnPromotionAndClearOldVersion( + that: this, + contactId: contactId, + version: version, + promotion: promotion, + ); + + Future setContactVersion({ + required PlatformInt64 contactId, + required List update, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetContactVersion( + that: this, + contactId: contactId, + update: update, + ); + + Future setShares({required List shares}) => RustLib + .instance + .api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetShares( + that: this, + shares: shares, + ); + + Future storeOtherPromotion({ + required OtherPromotion promotion, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterStoreOtherPromotion( + that: this, + promotion: promotion, + ); + + Future updateConfig({required String update}) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterUpdateConfig( + that: this, + update: update, + ); + + @override + int get hashCode => 0; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UserDiscoveryStoreFlutter && runtimeType == other.runtimeType; +} + +class UserDiscoveryUtilsFlutter { + const UserDiscoveryUtilsFlutter(); + + Future signData({required List inputData}) => RustLib + .instance + .api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterSignData( + that: this, + inputData: inputData, + ); + + Future verifySignature({ + required List inputData, + required List pubkey, + required List signature, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifySignature( + that: this, + inputData: inputData, + pubkey: pubkey, + signature: signature, + ); + + Future verifyStoredPubkey({ + required PlatformInt64 fromContactId, + required List pubkey, + }) => RustLib.instance.api + .crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifyStoredPubkey( + that: this, + fromContactId: fromContactId, + pubkey: pubkey, + ); + + @override + int get hashCode => 0; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UserDiscoveryUtilsFlutter && runtimeType == other.runtimeType; +} diff --git a/lib/core/bridge/wrapper.dart b/lib/core/bridge/wrapper.dart new file mode 100644 index 00000000..a81cf112 --- /dev/null +++ b/lib/core/bridge/wrapper.dart @@ -0,0 +1,37 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.12.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +class RustUtils { + const RustUtils(); + + static Future> generateShares({ + required List secret, + required int total, + required int threshold, + }) => RustLib.instance.api.crateBridgeWrapperRustUtilsGenerateShares( + secret: secret, + total: total, + threshold: threshold, + ); + + static Future recoverSecret({ + required List shares, + required int threshold, + }) => RustLib.instance.api.crateBridgeWrapperRustUtilsRecoverSecret( + shares: shares, + threshold: threshold, + ); + + @override + int get hashCode => 0; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is RustUtils && runtimeType == other.runtimeType; +} diff --git a/lib/core/bridge/wrapper/key_manager.dart b/lib/core/bridge/wrapper/key_manager.dart index 46c57e90..6e6e5095 100644 --- a/lib/core/bridge/wrapper/key_manager.dart +++ b/lib/core/bridge/wrapper/key_manager.dart @@ -20,6 +20,12 @@ class RustKeyManager { static Future getUserId() => RustLib.instance.api .crateBridgeWrapperKeyManagerRustKeyManagerGetUserId(); + static Future importSerialized({required List serializedBytes}) => + RustLib.instance.api + .crateBridgeWrapperKeyManagerRustKeyManagerImportSerialized( + serializedBytes: serializedBytes, + ); + static Future importSignalIdentity({ required List identityKeyPairStructure, required PlatformInt64 registrationId, @@ -53,6 +59,10 @@ class RustKeyManager { signedPreKeyId: signedPreKeyId, ); + /// Serialize the key_manager. Needed for the passwordless_recovery feature. + static Future serialize() => RustLib.instance.api + .crateBridgeWrapperKeyManagerRustKeyManagerSerialize(); + static Future setUserId({required PlatformInt64 userId}) => RustLib .instance .api diff --git a/lib/core/frb_generated.dart b/lib/core/frb_generated.dart index 8570e08f..b0b840d8 100644 --- a/lib/core/frb_generated.dart +++ b/lib/core/frb_generated.dart @@ -5,6 +5,8 @@ import 'bridge.dart'; import 'bridge/callbacks.dart'; +import 'bridge/callbacks/user_discovery.dart'; +import 'bridge/wrapper.dart'; import 'bridge/wrapper/backup.dart'; import 'bridge/wrapper/key_manager.dart'; import 'bridge/wrapper/user_discovery.dart'; @@ -74,7 +76,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.12.0'; @override - int get rustContentHash => -1867463121; + int get rustContentHash => 340781866; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -210,6 +212,10 @@ abstract class RustLibApi extends BaseApi { Future crateBridgeWrapperKeyManagerRustKeyManagerGetUserId(); + Future crateBridgeWrapperKeyManagerRustKeyManagerImportSerialized({ + required List serializedBytes, + }); + Future crateBridgeWrapperKeyManagerRustKeyManagerImportSignalIdentity({ required List identityKeyPairStructure, required PlatformInt64 registrationId, @@ -230,6 +236,8 @@ abstract class RustLibApi extends BaseApi { required PlatformInt64 signedPreKeyId, }); + Future crateBridgeWrapperKeyManagerRustKeyManagerSerialize(); + Future crateBridgeWrapperKeyManagerRustKeyManagerSetUserId({ required PlatformInt64 userId, }); @@ -238,6 +246,120 @@ abstract class RustLibApi extends BaseApi { required PlatformInt64 signedPreKeyId, required List record, }); + + Future> crateBridgeWrapperRustUtilsGenerateShares({ + required List secret, + required int total, + required int threshold, + }); + + Future crateBridgeWrapperRustUtilsRecoverSecret({ + required List shares, + required int threshold, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetAnnouncedUserByPublicId({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 publicId, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetConfig({ + required UserDiscoveryStoreFlutter that, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactPromotion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactVersion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + }); + + Future> + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOtherPromotionsByPublicId({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 publicId, + }); + + Future> + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOwnPromotionsAfterVersion({ + required UserDiscoveryStoreFlutter that, + required int version, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetShareForContact({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushNewUserRelation({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 fromContactId, + required AnnouncedUser announcedUser, + PlatformInt64? publicKeyVerifiedTimestamp, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushOwnPromotionAndClearOldVersion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + required int version, + required List promotion, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetContactVersion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + required List update, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetShares({ + required UserDiscoveryStoreFlutter that, + required List shares, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterStoreOtherPromotion({ + required UserDiscoveryStoreFlutter that, + required OtherPromotion promotion, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterUpdateConfig({ + required UserDiscoveryStoreFlutter that, + required String update, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterSignData({ + required UserDiscoveryUtilsFlutter that, + required List inputData, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifySignature({ + required UserDiscoveryUtilsFlutter that, + required List inputData, + required List pubkey, + required List signature, + }); + + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifyStoredPubkey({ + required UserDiscoveryUtilsFlutter that, + required PlatformInt64 fromContactId, + required List pubkey, + }); } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -1116,6 +1238,41 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: [], ); + @override + Future crateBridgeWrapperKeyManagerRustKeyManagerImportSerialized({ + required List serializedBytes, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_prim_u_8_loose(serializedBytes, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 21, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeWrapperKeyManagerRustKeyManagerImportSerializedConstMeta, + argValues: [serializedBytes], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeWrapperKeyManagerRustKeyManagerImportSerializedConstMeta => + const TaskConstMeta( + debugName: "rust_key_manager_import_serialized", + argNames: ["serializedBytes"], + ); + @override Future crateBridgeWrapperKeyManagerRustKeyManagerImportSignalIdentity({ required List identityKeyPairStructure, @@ -1135,7 +1292,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 21, + funcId: 22, port: port_, ); }, @@ -1179,7 +1336,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 22, + funcId: 23, port: port_, ); }, @@ -1212,7 +1369,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 23, + funcId: 24, port: port_, ); }, @@ -1244,7 +1401,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 24, + funcId: 25, port: port_, ); }, @@ -1279,7 +1436,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 25, + funcId: 26, port: port_, ); }, @@ -1302,6 +1459,38 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["signedPreKeyId"], ); + @override + Future crateBridgeWrapperKeyManagerRustKeyManagerSerialize() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 27, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeWrapperKeyManagerRustKeyManagerSerializeConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeWrapperKeyManagerRustKeyManagerSerializeConstMeta => + const TaskConstMeta( + debugName: "rust_key_manager_serialize", + argNames: [], + ); + @override Future crateBridgeWrapperKeyManagerRustKeyManagerSetUserId({ required PlatformInt64 userId, @@ -1314,7 +1503,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 26, + funcId: 28, port: port_, ); }, @@ -1351,7 +1540,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 27, + funcId: 29, port: port_, ); }, @@ -1374,6 +1563,717 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["signedPreKeyId", "record"], ); + @override + Future> crateBridgeWrapperRustUtilsGenerateShares({ + required List secret, + required int total, + required int threshold, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_prim_u_8_loose(secret, serializer); + sse_encode_u_8(total, serializer); + sse_encode_u_8(threshold, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 30, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: kCrateBridgeWrapperRustUtilsGenerateSharesConstMeta, + argValues: [secret, total, threshold], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateBridgeWrapperRustUtilsGenerateSharesConstMeta => + const TaskConstMeta( + debugName: "rust_utils_generate_shares", + argNames: ["secret", "total", "threshold"], + ); + + @override + Future crateBridgeWrapperRustUtilsRecoverSecret({ + required List shares, + required int threshold, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_list_prim_u_8_strict(shares, serializer); + sse_encode_u_8(threshold, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 31, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: kCrateBridgeWrapperRustUtilsRecoverSecretConstMeta, + argValues: [shares, threshold], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateBridgeWrapperRustUtilsRecoverSecretConstMeta => + const TaskConstMeta( + debugName: "rust_utils_recover_secret", + argNames: ["shares", "threshold"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetAnnouncedUserByPublicId({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 publicId, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(publicId, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 32, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_opt_box_autoadd_announced_user, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetAnnouncedUserByPublicIdConstMeta, + argValues: [that, publicId], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetAnnouncedUserByPublicIdConstMeta => + const TaskConstMeta( + debugName: + "user_discovery_store_flutter_get_announced_user_by_public_id", + argNames: ["that", "publicId"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetConfig({ + required UserDiscoveryStoreFlutter that, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 33, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetConfigConstMeta, + argValues: [that], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetConfigConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_get_config", + argNames: ["that"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactPromotion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(contactId, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 34, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_opt_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactPromotionConstMeta, + argValues: [that, contactId], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactPromotionConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_get_contact_promotion", + argNames: ["that", "contactId"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactVersion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(contactId, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 35, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_opt_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactVersionConstMeta, + argValues: [that, contactId], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactVersionConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_get_contact_version", + argNames: ["that", "contactId"], + ); + + @override + Future> + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOtherPromotionsByPublicId({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 publicId, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(publicId, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 36, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_other_promotion, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOtherPromotionsByPublicIdConstMeta, + argValues: [that, publicId], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOtherPromotionsByPublicIdConstMeta => + const TaskConstMeta( + debugName: + "user_discovery_store_flutter_get_other_promotions_by_public_id", + argNames: ["that", "publicId"], + ); + + @override + Future> + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOwnPromotionsAfterVersion({ + required UserDiscoveryStoreFlutter that, + required int version, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_u_32(version, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 37, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOwnPromotionsAfterVersionConstMeta, + argValues: [that, version], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOwnPromotionsAfterVersionConstMeta => + const TaskConstMeta( + debugName: + "user_discovery_store_flutter_get_own_promotions_after_version", + argNames: ["that", "version"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetShareForContact({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(contactId, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 38, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetShareForContactConstMeta, + argValues: [that, contactId], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetShareForContactConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_get_share_for_contact", + argNames: ["that", "contactId"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushNewUserRelation({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 fromContactId, + required AnnouncedUser announcedUser, + PlatformInt64? publicKeyVerifiedTimestamp, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(fromContactId, serializer); + sse_encode_box_autoadd_announced_user(announcedUser, serializer); + sse_encode_opt_box_autoadd_i_64( + publicKeyVerifiedTimestamp, + serializer, + ); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 39, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushNewUserRelationConstMeta, + argValues: [ + that, + fromContactId, + announcedUser, + publicKeyVerifiedTimestamp, + ], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushNewUserRelationConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_push_new_user_relation", + argNames: [ + "that", + "fromContactId", + "announcedUser", + "publicKeyVerifiedTimestamp", + ], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushOwnPromotionAndClearOldVersion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + required int version, + required List promotion, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(contactId, serializer); + sse_encode_u_32(version, serializer); + sse_encode_list_prim_u_8_loose(promotion, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 40, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushOwnPromotionAndClearOldVersionConstMeta, + argValues: [that, contactId, version, promotion], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushOwnPromotionAndClearOldVersionConstMeta => + const TaskConstMeta( + debugName: + "user_discovery_store_flutter_push_own_promotion_and_clear_old_version", + argNames: ["that", "contactId", "version", "promotion"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetContactVersion({ + required UserDiscoveryStoreFlutter that, + required PlatformInt64 contactId, + required List update, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_i_64(contactId, serializer); + sse_encode_list_prim_u_8_loose(update, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 41, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetContactVersionConstMeta, + argValues: [that, contactId, update], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetContactVersionConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_set_contact_version", + argNames: ["that", "contactId", "update"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetShares({ + required UserDiscoveryStoreFlutter that, + required List shares, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_list_list_prim_u_8_strict(shares, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 42, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetSharesConstMeta, + argValues: [that, shares], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetSharesConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_set_shares", + argNames: ["that", "shares"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterStoreOtherPromotion({ + required UserDiscoveryStoreFlutter that, + required OtherPromotion promotion, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_box_autoadd_other_promotion(promotion, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 43, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterStoreOtherPromotionConstMeta, + argValues: [that, promotion], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterStoreOtherPromotionConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_store_other_promotion", + argNames: ["that", "promotion"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterUpdateConfig({ + required UserDiscoveryStoreFlutter that, + required String update, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_store_flutter(that, serializer); + sse_encode_String(update, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 44, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterUpdateConfigConstMeta, + argValues: [that, update], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterUpdateConfigConstMeta => + const TaskConstMeta( + debugName: "user_discovery_store_flutter_update_config", + argNames: ["that", "update"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterSignData({ + required UserDiscoveryUtilsFlutter that, + required List inputData, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_utils_flutter(that, serializer); + sse_encode_list_prim_u_8_loose(inputData, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 45, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterSignDataConstMeta, + argValues: [that, inputData], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterSignDataConstMeta => + const TaskConstMeta( + debugName: "user_discovery_utils_flutter_sign_data", + argNames: ["that", "inputData"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifySignature({ + required UserDiscoveryUtilsFlutter that, + required List inputData, + required List pubkey, + required List signature, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_utils_flutter(that, serializer); + sse_encode_list_prim_u_8_loose(inputData, serializer); + sse_encode_list_prim_u_8_loose(pubkey, serializer); + sse_encode_list_prim_u_8_loose(signature, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 46, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifySignatureConstMeta, + argValues: [that, inputData, pubkey, signature], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifySignatureConstMeta => + const TaskConstMeta( + debugName: "user_discovery_utils_flutter_verify_signature", + argNames: ["that", "inputData", "pubkey", "signature"], + ); + + @override + Future + crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifyStoredPubkey({ + required UserDiscoveryUtilsFlutter that, + required PlatformInt64 fromContactId, + required List pubkey, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_user_discovery_utils_flutter(that, serializer); + sse_encode_i_64(fromContactId, serializer); + sse_encode_list_prim_u_8_loose(pubkey, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 47, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: sse_decode_AnyhowException, + ), + constMeta: + kCrateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifyStoredPubkeyConstMeta, + argValues: [that, fromContactId, pubkey], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifyStoredPubkeyConstMeta => + const TaskConstMeta( + debugName: "user_discovery_utils_flutter_verify_stored_pubkey", + argNames: ["that", "fromContactId", "pubkey"], + ); + Future Function( int, ) @@ -2002,6 +2902,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dco_decode_init_config(raw); } + @protected + OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_other_promotion(raw); + } + + @protected + UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_user_discovery_store_flutter(raw); + } + + @protected + UserDiscoveryUtilsFlutter dco_decode_box_autoadd_user_discovery_utils_flutter( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_user_discovery_utils_flutter(raw); + } + @protected FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2190,6 +3112,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return RustKeyManager(); } + @protected + RustUtils dco_decode_rust_utils(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 0) + throw Exception('unexpected arr length: expect 0 but see ${arr.length}'); + return RustUtils(); + } + @protected int dco_decode_u_32(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2214,6 +3145,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return; } + @protected + UserDiscoveryStoreFlutter dco_decode_user_discovery_store_flutter( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 0) + throw Exception('unexpected arr length: expect 0 but see ${arr.length}'); + return UserDiscoveryStoreFlutter(); + } + + @protected + UserDiscoveryUtilsFlutter dco_decode_user_discovery_utils_flutter( + dynamic raw, + ) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 0) + throw Exception('unexpected arr length: expect 0 but see ${arr.length}'); + return UserDiscoveryUtilsFlutter(); + } + @protected BigInt dco_decode_usize(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2318,6 +3271,30 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (sse_decode_init_config(deserializer)); } + @protected + OtherPromotion sse_decode_box_autoadd_other_promotion( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_other_promotion(deserializer)); + } + + @protected + UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_user_discovery_store_flutter(deserializer)); + } + + @protected + UserDiscoveryUtilsFlutter sse_decode_box_autoadd_user_discovery_utils_flutter( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_user_discovery_utils_flutter(deserializer)); + } + @protected FlutterUserDiscovery sse_decode_flutter_user_discovery( SseDeserializer deserializer, @@ -2548,6 +3525,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return RustKeyManager(); } + @protected + RustUtils sse_decode_rust_utils(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return RustUtils(); + } + @protected int sse_decode_u_32(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -2572,6 +3555,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } + @protected + UserDiscoveryStoreFlutter sse_decode_user_discovery_store_flutter( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return UserDiscoveryStoreFlutter(); + } + + @protected + UserDiscoveryUtilsFlutter sse_decode_user_discovery_utils_flutter( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return UserDiscoveryUtilsFlutter(); + } + @protected BigInt sse_decode_usize(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -2877,6 +3876,33 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_init_config(self, serializer); } + @protected + void sse_encode_box_autoadd_other_promotion( + OtherPromotion self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_other_promotion(self, serializer); + } + + @protected + void sse_encode_box_autoadd_user_discovery_store_flutter( + UserDiscoveryStoreFlutter self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_user_discovery_store_flutter(self, serializer); + } + + @protected + void sse_encode_box_autoadd_user_discovery_utils_flutter( + UserDiscoveryUtilsFlutter self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_user_discovery_utils_flutter(self, serializer); + } + @protected void sse_encode_flutter_user_discovery( FlutterUserDiscovery self, @@ -3108,6 +4134,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } + @protected + void sse_encode_rust_utils(RustUtils self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + } + @protected void sse_encode_u_32(int self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3131,6 +4162,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } + @protected + void sse_encode_user_discovery_store_flutter( + UserDiscoveryStoreFlutter self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + } + + @protected + void sse_encode_user_discovery_utils_flutter( + UserDiscoveryUtilsFlutter self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + } + @protected void sse_encode_usize(BigInt self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/lib/core/frb_generated.io.dart b/lib/core/frb_generated.io.dart index 938d45c5..6b1600a0 100644 --- a/lib/core/frb_generated.io.dart +++ b/lib/core/frb_generated.io.dart @@ -5,6 +5,8 @@ import 'bridge.dart'; import 'bridge/callbacks.dart'; +import 'bridge/callbacks/user_discovery.dart'; +import 'bridge/wrapper.dart'; import 'bridge/wrapper/backup.dart'; import 'bridge/wrapper/key_manager.dart'; import 'bridge/wrapper/user_discovery.dart'; @@ -134,6 +136,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig dco_decode_box_autoadd_init_config(dynamic raw); + @protected + OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw); + + @protected + UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter( + dynamic raw, + ); + + @protected + UserDiscoveryUtilsFlutter dco_decode_box_autoadd_user_discovery_utils_flutter( + dynamic raw, + ); + @protected FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw); @@ -205,6 +220,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustKeyManager dco_decode_rust_key_manager(dynamic raw); + @protected + RustUtils dco_decode_rust_utils(dynamic raw); + @protected int dco_decode_u_32(dynamic raw); @@ -217,6 +235,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void dco_decode_unit(dynamic raw); + @protected + UserDiscoveryStoreFlutter dco_decode_user_discovery_store_flutter( + dynamic raw, + ); + + @protected + UserDiscoveryUtilsFlutter dco_decode_user_discovery_utils_flutter( + dynamic raw, + ); + @protected BigInt dco_decode_usize(dynamic raw); @@ -266,6 +294,21 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer); + @protected + OtherPromotion sse_decode_box_autoadd_other_promotion( + SseDeserializer deserializer, + ); + + @protected + UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter( + SseDeserializer deserializer, + ); + + @protected + UserDiscoveryUtilsFlutter sse_decode_box_autoadd_user_discovery_utils_flutter( + SseDeserializer deserializer, + ); + @protected FlutterUserDiscovery sse_decode_flutter_user_discovery( SseDeserializer deserializer, @@ -357,6 +400,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer); + @protected + RustUtils sse_decode_rust_utils(SseDeserializer deserializer); + @protected int sse_decode_u_32(SseDeserializer deserializer); @@ -369,6 +415,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_decode_unit(SseDeserializer deserializer); + @protected + UserDiscoveryStoreFlutter sse_decode_user_discovery_store_flutter( + SseDeserializer deserializer, + ); + + @protected + UserDiscoveryUtilsFlutter sse_decode_user_discovery_utils_flutter( + SseDeserializer deserializer, + ); + @protected BigInt sse_decode_usize(SseDeserializer deserializer); @@ -517,6 +573,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_box_autoadd_other_promotion( + OtherPromotion self, + SseSerializer serializer, + ); + + @protected + void sse_encode_box_autoadd_user_discovery_store_flutter( + UserDiscoveryStoreFlutter self, + SseSerializer serializer, + ); + + @protected + void sse_encode_box_autoadd_user_discovery_utils_flutter( + UserDiscoveryUtilsFlutter self, + SseSerializer serializer, + ); + @protected void sse_encode_flutter_user_discovery( FlutterUserDiscovery self, @@ -634,6 +708,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_rust_utils(RustUtils self, SseSerializer serializer); + @protected void sse_encode_u_32(int self, SseSerializer serializer); @@ -646,6 +723,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_unit(void self, SseSerializer serializer); + @protected + void sse_encode_user_discovery_store_flutter( + UserDiscoveryStoreFlutter self, + SseSerializer serializer, + ); + + @protected + void sse_encode_user_discovery_utils_flutter( + UserDiscoveryUtilsFlutter self, + SseSerializer serializer, + ); + @protected void sse_encode_usize(BigInt self, SseSerializer serializer); diff --git a/lib/core/frb_generated.web.dart b/lib/core/frb_generated.web.dart index 23c61b25..ed0ca4ee 100644 --- a/lib/core/frb_generated.web.dart +++ b/lib/core/frb_generated.web.dart @@ -8,6 +8,8 @@ import 'bridge.dart'; import 'bridge/callbacks.dart'; +import 'bridge/callbacks/user_discovery.dart'; +import 'bridge/wrapper.dart'; import 'bridge/wrapper/backup.dart'; import 'bridge/wrapper/key_manager.dart'; import 'bridge/wrapper/user_discovery.dart'; @@ -136,6 +138,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig dco_decode_box_autoadd_init_config(dynamic raw); + @protected + OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw); + + @protected + UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter( + dynamic raw, + ); + + @protected + UserDiscoveryUtilsFlutter dco_decode_box_autoadd_user_discovery_utils_flutter( + dynamic raw, + ); + @protected FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw); @@ -207,6 +222,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustKeyManager dco_decode_rust_key_manager(dynamic raw); + @protected + RustUtils dco_decode_rust_utils(dynamic raw); + @protected int dco_decode_u_32(dynamic raw); @@ -219,6 +237,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void dco_decode_unit(dynamic raw); + @protected + UserDiscoveryStoreFlutter dco_decode_user_discovery_store_flutter( + dynamic raw, + ); + + @protected + UserDiscoveryUtilsFlutter dco_decode_user_discovery_utils_flutter( + dynamic raw, + ); + @protected BigInt dco_decode_usize(dynamic raw); @@ -268,6 +296,21 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer); + @protected + OtherPromotion sse_decode_box_autoadd_other_promotion( + SseDeserializer deserializer, + ); + + @protected + UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter( + SseDeserializer deserializer, + ); + + @protected + UserDiscoveryUtilsFlutter sse_decode_box_autoadd_user_discovery_utils_flutter( + SseDeserializer deserializer, + ); + @protected FlutterUserDiscovery sse_decode_flutter_user_discovery( SseDeserializer deserializer, @@ -359,6 +402,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer); + @protected + RustUtils sse_decode_rust_utils(SseDeserializer deserializer); + @protected int sse_decode_u_32(SseDeserializer deserializer); @@ -371,6 +417,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_decode_unit(SseDeserializer deserializer); + @protected + UserDiscoveryStoreFlutter sse_decode_user_discovery_store_flutter( + SseDeserializer deserializer, + ); + + @protected + UserDiscoveryUtilsFlutter sse_decode_user_discovery_utils_flutter( + SseDeserializer deserializer, + ); + @protected BigInt sse_decode_usize(SseDeserializer deserializer); @@ -519,6 +575,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_box_autoadd_other_promotion( + OtherPromotion self, + SseSerializer serializer, + ); + + @protected + void sse_encode_box_autoadd_user_discovery_store_flutter( + UserDiscoveryStoreFlutter self, + SseSerializer serializer, + ); + + @protected + void sse_encode_box_autoadd_user_discovery_utils_flutter( + UserDiscoveryUtilsFlutter self, + SseSerializer serializer, + ); + @protected void sse_encode_flutter_user_discovery( FlutterUserDiscovery self, @@ -636,6 +710,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_rust_utils(RustUtils self, SseSerializer serializer); + @protected void sse_encode_u_32(int self, SseSerializer serializer); @@ -648,6 +725,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_unit(void self, SseSerializer serializer); + @protected + void sse_encode_user_discovery_store_flutter( + UserDiscoveryStoreFlutter self, + SseSerializer serializer, + ); + + @protected + void sse_encode_user_discovery_utils_flutter( + UserDiscoveryUtilsFlutter self, + SseSerializer serializer, + ); + @protected void sse_encode_usize(BigInt self, SseSerializer serializer); diff --git a/lib/src/callbacks/user_discovery.callbacks.dart b/lib/src/callbacks/user_discovery.callbacks.dart index 86bfc4df..1c0753b2 100644 --- a/lib/src/callbacks/user_discovery.callbacks.dart +++ b/lib/src/callbacks/user_discovery.callbacks.dart @@ -314,9 +314,11 @@ class UserDiscoveryCallbacks { static Future getContactPromotion(int contactId) async { try { - final row = await (twonlyDB.select( - twonlyDB.userDiscoveryOwnPromotions, - )..where((tbl) => tbl.contactId.equals(contactId))).getSingleOrNull(); + final query = twonlyDB.select(twonlyDB.userDiscoveryOwnPromotions) + ..where((tbl) => tbl.contactId.equals(contactId)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.versionId)]) + ..limit(1); + final row = await query.getSingleOrNull(); return row?.promotion; } catch (e) { Log.error(e); diff --git a/lib/src/channels/video_compression.channel.dart b/lib/src/channels/video_compression.channel.dart index f2f72993..58790b59 100644 --- a/lib/src/channels/video_compression.channel.dart +++ b/lib/src/channels/video_compression.channel.dart @@ -36,7 +36,7 @@ abstract class VideoCompressionChannel { }); return outputPath; } on PlatformException catch (e) { - Log.error('Failed to compress video: $e'); + Log.warn('Failed to compress video: $e'); return null; } finally { _currentProgressCallback = null; diff --git a/lib/src/constants/keyvalue.keys.dart b/lib/src/constants/keyvalue.keys.dart index 57db9890..9737cb9d 100644 --- a/lib/src/constants/keyvalue.keys.dart +++ b/lib/src/constants/keyvalue.keys.dart @@ -3,4 +3,5 @@ class KeyValueKeys { 'last_periodic_task_execution'; static const String currentBackupState = 'current_backup_state'; static const String backupRecoveryState = 'backup_recovery_state'; + static const String onboardingState = 'onboarding_state'; } diff --git a/lib/src/constants/routes.keys.dart b/lib/src/constants/routes.keys.dart index 017745f0..43fc9cb0 100644 --- a/lib/src/constants/routes.keys.dart +++ b/lib/src/constants/routes.keys.dart @@ -16,6 +16,7 @@ class Routes { static String profileContact(int contactId) => '/profile/contact/$contactId'; static const String cameraQRScanner = '/camera/qr_scanner'; + static const String recoverPasswordless = '/recover/passwordless'; static const String settings = '/settings'; static const String settingsProfile = '/settings/profile'; @@ -35,8 +36,6 @@ class Routes { '/settings/privacy/block_users'; static const String settingsPrivacyUserDiscovery = '/settings/privacy/user_discovery'; - static const String settingsPrivacyProfileSelection = - '/settings/privacy/profile_selection'; static const String settingsNotification = '/settings/notification'; static const String settingsStorage = '/settings/storage_data'; static const String settingsStorageManage = '/settings/storage_data/manage'; diff --git a/lib/src/database/daos/groups.dao.dart b/lib/src/database/daos/groups.dao.dart index 929dc2e0..aa5d69c4 100644 --- a/lib/src/database/daos/groups.dao.dart +++ b/lib/src/database/daos/groups.dao.dart @@ -128,7 +128,7 @@ class GroupsDao extends DatabaseAccessor with _$GroupsDaoMixin { final result = await _insertGroup(insertGroup); if (result != null) { - await into(groupMembers).insert( + await into(groupMembers).insertOnConflictUpdate( GroupMembersCompanion( groupId: Value(result.groupId), contactId: Value( diff --git a/lib/src/database/daos/key_verification.dao.dart b/lib/src/database/daos/key_verification.dao.dart index 20fc47d8..eef18d6e 100644 --- a/lib/src/database/daos/key_verification.dao.dart +++ b/lib/src/database/daos/key_verification.dao.dart @@ -259,6 +259,54 @@ class KeyVerificationDao extends DatabaseAccessor }); } + Stream watchUnverifiedGroupMembersCount(String groupId) { + final gm = groupMembers; + final directKv = alias(keyVerifications, 'directKv'); + final ur = userDiscoveryUserRelations; + final verifierKv = alias(keyVerifications, 'verifierKv'); + + final query = select(gm).join([ + leftOuterJoin(directKv, directKv.contactId.equalsExp(gm.contactId)), + leftOuterJoin( + ur, + ur.announcedUserId.equalsExp(gm.contactId) & + ur.publicKeyVerifiedTimestamp.isNotNull() & + ur.fromContactId.equalsExp(gm.contactId).not(), + ), + leftOuterJoin( + verifierKv, + verifierKv.contactId.equalsExp(ur.fromContactId), + ), + ])..where(gm.groupId.equals(groupId)); + + return query.watch().map((rows) { + if (rows.isEmpty) return 0; + + final memberTrustMap = {}; + + for (final row in rows) { + final contactId = row.readTable(gm).contactId; + final isDirect = row.readTableOrNull(directKv) != null; + final isPartial = row.readTableOrNull(verifierKv) != null; + + final current = + memberTrustMap[contactId] ?? (direct: false, partial: false); + memberTrustMap[contactId] = ( + direct: current.direct || isDirect, + partial: current.partial || isPartial, + ); + } + + var count = 0; + for (final trust in memberTrustMap.values) { + if (!trust.direct && !trust.partial) { + count++; + } + } + return count; + }); + } + Future addKeyVerification( int contactId, VerificationType type, { diff --git a/lib/src/database/daos/mediafiles.dao.dart b/lib/src/database/daos/mediafiles.dao.dart index 78f2d92f..889beb4e 100644 --- a/lib/src/database/daos/mediafiles.dao.dart +++ b/lib/src/database/daos/mediafiles.dao.dart @@ -1,12 +1,13 @@ import 'package:drift/drift.dart'; import 'package:hashlib/random.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; +import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/log.dart'; part 'mediafiles.dao.g.dart'; -@DriftAccessor(tables: [MediaFiles]) +@DriftAccessor(tables: [MediaFiles, Messages]) class MediaFilesDao extends DatabaseAccessor with _$MediaFilesDaoMixin { // this constructor is required so that the main database can create an instance @@ -142,7 +143,9 @@ class MediaFilesDao extends DatabaseAccessor final query = (select(mediaFiles)..where((t) => t.stored.equals(true))).join([]) ..groupBy([ - const CustomExpression('COALESCE(stored_file_hash, media_id)') + const CustomExpression( + 'COALESCE(stored_file_hash, media_id)', + ), ]); return query.map((row) => row.readTable(mediaFiles)).watch(); } @@ -154,6 +157,17 @@ class MediaFilesDao extends DatabaseAccessor .watch(); } + Stream> watchMediaFilesForGroup(String groupId) { + final query = select(mediaFiles).join([ + innerJoin( + db.messages, + db.messages.mediaId.equalsExp(mediaFiles.mediaId), + useColumns: false, + ), + ])..where(db.messages.groupId.equals(groupId)); + return query.map((row) => row.readTable(mediaFiles)).watch(); + } + Future updateAllRetransmissionUploadingState() async { await (update(mediaFiles)..where( (t) => diff --git a/lib/src/database/daos/mediafiles.dao.g.dart b/lib/src/database/daos/mediafiles.dao.g.dart index 3bc0ce86..6d9c5f56 100644 --- a/lib/src/database/daos/mediafiles.dao.g.dart +++ b/lib/src/database/daos/mediafiles.dao.g.dart @@ -5,6 +5,9 @@ part of 'mediafiles.dao.dart'; // ignore_for_file: type=lint mixin _$MediaFilesDaoMixin on DatabaseAccessor { $MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles; + $GroupsTable get groups => attachedDatabase.groups; + $ContactsTable get contacts => attachedDatabase.contacts; + $MessagesTable get messages => attachedDatabase.messages; MediaFilesDaoManager get managers => MediaFilesDaoManager(this); } @@ -13,4 +16,10 @@ class MediaFilesDaoManager { MediaFilesDaoManager(this._db); $$MediaFilesTableTableManager get mediaFiles => $$MediaFilesTableTableManager(_db.attachedDatabase, _db.mediaFiles); + $$GroupsTableTableManager get groups => + $$GroupsTableTableManager(_db.attachedDatabase, _db.groups); + $$ContactsTableTableManager get contacts => + $$ContactsTableTableManager(_db.attachedDatabase, _db.contacts); + $$MessagesTableTableManager get messages => + $$MessagesTableTableManager(_db.attachedDatabase, _db.messages); } diff --git a/lib/src/database/daos/messages.dao.dart b/lib/src/database/daos/messages.dao.dart index 532ddd92..a336d0ca 100644 --- a/lib/src/database/daos/messages.dao.dart +++ b/lib/src/database/daos/messages.dao.dart @@ -32,15 +32,25 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { MessagesDao(super.db); Stream> watchMessageNotOpened(String groupId) { - return (select(messages) + final query = + select(messages).join([ + leftOuterJoin( + mediaFiles, + mediaFiles.mediaId.equalsExp(messages.mediaId), + ), + ]) ..where( - (t) => - t.openedAt.isNull() & - t.groupId.equals(groupId) & - t.isDeletedFromSender.equals(false), + messages.openedAt.isNull() & + messages.groupId.equals(groupId) & + messages.isDeletedFromSender.equals(false) & + (messages.mediaId.isNull() | + mediaFiles.downloadState.isNull() | + mediaFiles.downloadState + .equals(DownloadState.reuploadRequested.name) + .not()), ) - ..orderBy([(t) => OrderingTerm.desc(t.createdAt)])) - .watch(); + ..orderBy([OrderingTerm.desc(messages.createdAt)]); + return query.map((row) => row.readTable(messages)).watch(); } Stream> watchMediaNotOpened(String groupId) { @@ -52,9 +62,10 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { ), ]) ..where( - mediaFiles.downloadState - .equals(DownloadState.reuploadRequested.name) - .not() & + (mediaFiles.downloadState.isNull() | + mediaFiles.downloadState + .equals(DownloadState.reuploadRequested.name) + .not()) & mediaFiles.type.equals(MediaType.audio.name).not() & messages.openedAt.isNull() & messages.groupId.equals(groupId) & @@ -73,8 +84,7 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { mediaFiles, mediaFiles.mediaId.equalsExp(messages.mediaId), ), - ]) - ..where( + ])..where( messages.openedAt.isNull() & messages.mediaId.isNotNull() & messages.type.equals(MessageType.media.name) & @@ -92,19 +102,29 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { milliseconds: group!.deleteMessagesAfterMilliseconds, ), ); - return (select(messages) + final query = + select(messages).join([ + leftOuterJoin( + mediaFiles, + mediaFiles.mediaId.equalsExp(messages.mediaId), + ), + ]) ..where( - (t) => - t.groupId.equals(groupId) & + messages.groupId.equals(groupId) & // messages in groups will only be removed in case all members have received it... // so ensuring that this message is not shown in the messages anymore - (t.openedAt.isBiggerThanValue(deletionTime) | - t.openedAt.isNull() | - t.mediaStored.equals(true)), + (messages.openedAt.isBiggerThanValue(deletionTime) | + messages.openedAt.isNull() | + messages.mediaStored.equals(true)) & + (mediaFiles.downloadState + .equals(DownloadState.reuploadRequested.name) + .not() | + mediaFiles.downloadState.isNull()), ) - ..orderBy([(t) => OrderingTerm.desc(t.createdAt)]) - ..limit(1)) - .watchSingleOrNull(); + ..orderBy([OrderingTerm.desc(messages.createdAt)]) + ..limit(1); + + return query.map((row) => row.readTable(messages)).watchSingleOrNull(); } Future>> watchByGroupId(String groupId) async { @@ -264,26 +284,28 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { String text, DateTime timestamp, ) async { - final msg = await getMessageById(messageId).getSingleOrNull(); - if (msg == null || msg.content == null || msg.senderId != contactId) { - return; - } - await into(messageHistories).insert( - MessageHistoriesCompanion( - messageId: Value(messageId), - content: Value(msg.content), - createdAt: Value(timestamp), - ), - ); - await (update(messages)..where( - (t) => t.messageId.equals(messageId), - )) - .write( - MessagesCompanion( - content: Value(text), - modifiedAt: Value(timestamp), - ), - ); + await transaction(() async { + final msg = await getMessageById(messageId).getSingleOrNull(); + if (msg == null || msg.content == null || msg.senderId != contactId) { + return; + } + await into(messageHistories).insert( + MessageHistoriesCompanion( + messageId: Value(messageId), + content: Value(msg.content), + createdAt: Value(timestamp), + ), + ); + await (update(messages)..where( + (t) => t.messageId.equals(messageId), + )) + .write( + MessagesCompanion( + content: Value(text), + modifiedAt: Value(timestamp), + ), + ); + }); } Future handleMessagesOpened( @@ -291,21 +313,37 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { List messageIds, DateTime timestamp, ) async { + if (contactId.present) { + final contactExists = await twonlyDB.contactsDao.getContactById( + contactId.value, + ); + if (contactExists == null) { + Log.info( + 'handleMessagesOpened: Contact ${contactId.value} does not exist in database, ignoring messages opened action.', + ); + return; + } + } for (final messageId in messageIds) { try { - var actionTimestamp = timestamp; - final msg = await getMessageById(messageId).getSingleOrNull(); - if (msg != null && actionTimestamp.isBefore(msg.createdAt)) { - Log.warn( - 'Receiver clock skew detected for message $messageId. ' - 'Action timestamp $actionTimestamp is before message creation ${msg.createdAt}. ' - 'Clamping to creation time.', - ); - actionTimestamp = msg.createdAt; - } - - final ts = actionTimestamp; await transaction(() async { + final msg = await getMessageById(messageId).getSingleOrNull(); + if (msg == null) { + Log.info( + 'handleMessagesOpened: Message $messageId does not exist in database, skipping.', + ); + return; + } + var ts = timestamp; + if (ts.isBefore(msg.createdAt)) { + Log.warn( + 'Receiver clock skew detected for message $messageId. ' + 'Action timestamp $ts is before message creation ${msg.createdAt}. ' + 'Clamping to creation time.', + ); + ts = msg.createdAt; + } + await into(messageActions).insertOnConflictUpdate( MessageActionsCompanion( messageId: Value(messageId), @@ -339,7 +377,7 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { messages, )..where((tbl) => tbl.messageId.equals(messageId))).write( MessagesCompanion( - openedAt: Value(actionTimestamp), + openedAt: Value(timestamp), ), ); } @@ -348,7 +386,11 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { 'handleMessagesOpened completed for message $messageId', ); } catch (e) { - Log.error('handleMessagesOpened failed for $messageId: $e'); + Log.warn('handleMessagesOpened failed for $messageId: $e'); + Log.error( + 'handleMessagesOpened failed for: $e', + onlyIfSentryEnabled: true, + ); } } } @@ -358,7 +400,21 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { String messageId, DateTime timestamp, ) async { + final contactExists = await twonlyDB.contactsDao.getContactById(contactId); + if (contactExists == null) { + Log.info( + 'handleMessageAckByServer: Contact $contactId does not exist in database, ignoring message ack.', + ); + return; + } await transaction(() async { + final msg = await getMessageById(messageId).getSingleOrNull(); + if (msg == null) { + Log.info( + 'handleMessageAckByServer: Message $messageId does not exist in database, skipping.', + ); + return; + } await into(messageActions).insertOnConflictUpdate( MessageActionsCompanion( messageId: Value(messageId), @@ -472,6 +528,15 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { .getSingleOrNull(); } + Stream watchLastMessageAction(String messageId) { + return (((select(messageActions)..where( + (t) => t.messageId.equals(messageId), + )) + ..orderBy([(t) => OrderingTerm.desc(t.actionAt)])) + ..limit(1)) + .watchSingleOrNull(); + } + Future deleteMessagesById(String messageId) { return (delete(messages)..where((t) => t.messageId.equals(messageId))).go(); } diff --git a/lib/src/database/daos/reactions.dao.dart b/lib/src/database/daos/reactions.dao.dart index a54deb20..0644ae31 100644 --- a/lib/src/database/daos/reactions.dao.dart +++ b/lib/src/database/daos/reactions.dao.dart @@ -30,11 +30,15 @@ class ReactionsDao extends DatabaseAccessor with _$ReactionsDaoMixin { .getMessageById(messageId) .getSingleOrNull(); if (msg == null) { - Log.error('updateReaction: Message $messageId not found!'); + Log.warn('updateReaction: Message $messageId not found!'); return; } if (msg.groupId != groupId) { - Log.error('updateReaction: Message groupId ${msg.groupId} != $groupId'); + Log.warn('updateReaction: Message groupId ${msg.groupId} != $groupId'); + Log.error( + 'updateReaction: Message groupId mismatch', + onlyIfSentryEnabled: true, + ); return; } diff --git a/lib/src/database/daos/receipts.dao.dart b/lib/src/database/daos/receipts.dao.dart index 8e5ea2fb..f79a2281 100644 --- a/lib/src/database/daos/receipts.dao.dart +++ b/lib/src/database/daos/receipts.dao.dart @@ -53,7 +53,7 @@ class ReceiptsDao extends DatabaseAccessor with _$ReceiptsDaoMixin { )) .go(); } - + Future deleteReceiptsByMessageId(String messageId) async { await (delete(receipts)..where( (t) => t.messageId.equals(messageId), @@ -201,8 +201,8 @@ class ReceiptsDao extends DatabaseAccessor with _$ReceiptsDaoMixin { ); final updatedReceipt = await getReceiptById(newReceiptId); if (updatedReceipt == null) { - Log.error( - 'Tried to change the receipt ID, but could not get the updated receipt...', + Log.warn( + '[$oldReceiptId] Tried to change the receipt ID to $newReceiptId, but could not get the updated receipt...', ); } return updatedReceipt; @@ -253,6 +253,9 @@ class ReceiptsDao extends DatabaseAccessor with _$ReceiptsDaoMixin { Future gotReceipt(String receiptId) async { await into( receivedReceipts, - ).insert(ReceivedReceiptsCompanion(receiptId: Value(receiptId))); + ).insert( + ReceivedReceiptsCompanion(receiptId: Value(receiptId)), + mode: InsertMode.insertOrIgnore, + ); } } diff --git a/lib/src/database/daos/user_discovery.dao.dart b/lib/src/database/daos/user_discovery.dao.dart index 07104a01..7c8fb157 100644 --- a/lib/src/database/daos/user_discovery.dao.dart +++ b/lib/src/database/daos/user_discovery.dao.dart @@ -1,4 +1,5 @@ import 'package:drift/drift.dart'; +import 'package:twonly/locator.dart'; import 'package:twonly/src/database/tables/contacts.table.dart'; import 'package:twonly/src/database/tables/user_discovery.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; @@ -184,23 +185,51 @@ class UserDiscoveryDao extends DatabaseAccessor results[user]!.add(relationData); } + final threshold = userService.currentUser.userDiscoveryThreshold; + results.removeWhere((user, relations) => relations.length < threshold); + return results; }); } Stream watchNewAnnouncementsWithDataCount() { - final countExp = userDiscoveryAnnouncedUsers.announcedUserId.count(); + final announcedContact = alias(contacts, 'announcedContact'); + final query = + select(userDiscoveryAnnouncedUsers).join([ + innerJoin( + userDiscoveryUserRelations, + userDiscoveryUserRelations.announcedUserId.equalsExp( + userDiscoveryAnnouncedUsers.announcedUserId, + ), + ), + leftOuterJoin( + announcedContact, + announcedContact.userId.equalsExp( + userDiscoveryAnnouncedUsers.announcedUserId, + ), + ), + ])..where( + // Filters: Has a username AND has not been shown to the user yet AND is not an existing contact + userDiscoveryAnnouncedUsers.username.isNotNull() & + userDiscoveryAnnouncedUsers.wasShownToTheUser.equals(false) & + userDiscoveryAnnouncedUsers.isHidden.equals(false) & + (announcedContact.userId.isNull() | + announcedContact.deletedByUser.equals(true)), + ); - final query = selectOnly(userDiscoveryAnnouncedUsers) - ..addColumns([countExp]) - ..where( - // Filters: Has a username AND has not been shown to the user yet - userDiscoveryAnnouncedUsers.username.isNotNull() & - userDiscoveryAnnouncedUsers.wasShownToTheUser.equals(false) & - userDiscoveryAnnouncedUsers.isHidden.equals(false), - ); + return query.watch().map((rows) { + final relationCounts = {}; + for (final row in rows) { + final announcedUserId = row + .readTable(userDiscoveryAnnouncedUsers) + .announcedUserId; + relationCounts[announcedUserId] = + (relationCounts[announcedUserId] ?? 0) + 1; + } - return query.watchSingle().map((row) => row.read(countExp) ?? 0); + final threshold = userService.currentUser.userDiscoveryThreshold; + return relationCounts.values.where((count) => count >= threshold).length; + }); } Future markAllValidAnnouncedUsersAsShown() async { @@ -235,9 +264,9 @@ class UserDiscoveryDao extends DatabaseAccessor } Stream watchAnnouncedUser(int id) { - return (select(userDiscoveryAnnouncedUsers) - ..where((tbl) => tbl.announcedUserId.equals(id))) - .watchSingleOrNull(); + return (select( + userDiscoveryAnnouncedUsers, + )..where((tbl) => tbl.announcedUserId.equals(id))).watchSingleOrNull(); } Stream> watchAllAnnouncedUsers() => diff --git a/lib/src/database/schemas/twonly_db/drift_schema_v21.json b/lib/src/database/schemas/twonly_db/drift_schema_v21.json new file mode 100644 index 00000000..8684872c --- /dev/null +++ b/lib/src/database/schemas/twonly_db/drift_schema_v21.json @@ -0,0 +1,3127 @@ +{ + "_meta": { + "description": "This file contains a serialized version of schema entities for drift.", + "version": "1.3.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, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "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_compressed", + "getter_name": "avatarSvgCompressed", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "sender_profile_counter", + "getter_name": "senderProfileCounter", + "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": "deleted_by_user", + "getter_name": "deletedByUser", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"deleted_by_user\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"deleted_by_user\" 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": "account_deleted", + "getter_name": "accountDeleted", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"account_deleted\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"account_deleted\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "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": "user_discovery_version", + "getter_name": "userDiscoveryVersion", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "user_discovery_excluded", + "getter_name": "userDiscoveryExcluded", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"user_discovery_excluded\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"user_discovery_excluded\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "user_discovery_manual_approved", + "getter_name": "userDiscoveryManualApproved", + "moor_type": "bool", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "CHECK (\"user_discovery_manual_approved\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"user_discovery_manual_approved\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_is_trusted_friend", + "getter_name": "recoveryIsTrustedFriend", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"recovery_is_trusted_friend\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"recovery_is_trusted_friend\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_last_heartbeat", + "getter_name": "recoveryLastHeartbeat", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_secret_share", + "getter_name": "recoverySecretShare", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_contacts_secret_share", + "getter_name": "recoveryContactsSecretShare", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_contacts_last_heartbeat", + "getter_name": "recoveryContactsLastHeartbeat", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ask_for_friend_promotions", + "getter_name": "askForFriendPromotions", + "moor_type": "bool", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "CHECK (\"ask_for_friend_promotions\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"ask_for_friend_promotions\" IN (0, 1))" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "media_send_counter", + "getter_name": "mediaSendCounter", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "media_received_counter", + "getter_name": "mediaReceivedCounter", + "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": [], + "type": "table", + "data": { + "name": "groups", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_group_admin", + "getter_name": "isGroupAdmin", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_group_admin\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_group_admin\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_direct_chat", + "getter_name": "isDirectChat", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_direct_chat\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_direct_chat\" 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": "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": "joined_group", + "getter_name": "joinedGroup", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"joined_group\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"joined_group\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "left_group", + "getter_name": "leftGroup", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"left_group\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"left_group\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "deleted_content", + "getter_name": "deletedContent", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"deleted_content\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"deleted_content\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "state_version_id", + "getter_name": "stateVersionId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "state_encryption_key", + "getter_name": "stateEncryptionKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "my_group_private_key", + "getter_name": "myGroupPrivateKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "group_name", + "getter_name": "groupName", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "draft_message", + "getter_name": "draftMessage", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "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": "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_milliseconds", + "getter_name": "deleteMessagesAfterMilliseconds", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('86400000')", + "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": "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": "flame_counter", + "getter_name": "flameCounter", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "max_flame_counter", + "getter_name": "maxFlameCounter", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "max_flame_counter_from", + "getter_name": "maxFlameCounterFrom", + "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": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "group_id" + ] + } + }, + { + "id": 2, + "references": [], + "type": "table", + "data": { + "name": "media_files", + "was_declared_in_moor": false, + "columns": [ + { + "name": "media_id", + "getter_name": "mediaId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(MediaType.values)", + "dart_type_name": "MediaType" + } + }, + { + "name": "upload_state", + "getter_name": "uploadState", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(UploadState.values)", + "dart_type_name": "UploadState" + } + }, + { + "name": "download_state", + "getter_name": "downloadState", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(DownloadState.values)", + "dart_type_name": "DownloadState" + } + }, + { + "name": "requires_authentication", + "getter_name": "requiresAuthentication", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"requires_authentication\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"requires_authentication\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "stored", + "getter_name": "stored", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"stored\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"stored\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_draft_media", + "getter_name": "isDraftMedia", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_draft_media\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_draft_media\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_favorite", + "getter_name": "isFavorite", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_favorite\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_favorite\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "has_crop_analyzed", + "getter_name": "hasCropAnalyzed", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"has_crop_analyzed\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"has_crop_analyzed\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "pre_progressing_process", + "getter_name": "preProgressingProcess", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "reupload_requested_by", + "getter_name": "reuploadRequestedBy", + "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": "display_limit_in_milliseconds", + "getter_name": "displayLimitInMilliseconds", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "remove_audio", + "getter_name": "removeAudio", + "moor_type": "bool", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "CHECK (\"remove_audio\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"remove_audio\" IN (0, 1))" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "download_token", + "getter_name": "downloadToken", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "encryption_key", + "getter_name": "encryptionKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "encryption_mac", + "getter_name": "encryptionMac", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "encryption_nonce", + "getter_name": "encryptionNonce", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "stored_file_hash", + "getter_name": "storedFileHash", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "has_thumbnail", + "getter_name": "hasThumbnail", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"has_thumbnail\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"has_thumbnail\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "size_in_bytes", + "getter_name": "sizeInBytes", + "moor_type": "int", + "nullable": true, + "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": [] + }, + { + "name": "created_at_month", + "getter_name": "createdAtMonth", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "media_id" + ] + } + }, + { + "id": 3, + "references": [ + 1, + 0, + 2 + ], + "type": "table", + "data": { + "name": "messages", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "message_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "sender_id", + "getter_name": "senderId", + "moor_type": "int", + "nullable": true, + "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": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "content", + "getter_name": "content", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "media_id", + "getter_name": "mediaId", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "REFERENCES media_files (media_id) ON DELETE SET NULL", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES media_files (media_id) ON DELETE SET NULL" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "media_files", + "column": "media_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "setNull" + } + } + ] + }, + { + "name": "additional_message_data", + "getter_name": "additionalMessageData", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "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": "media_reopened", + "getter_name": "mediaReopened", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"media_reopened\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"media_reopened\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "download_token", + "getter_name": "downloadToken", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "quotes_message_id", + "getter_name": "quotesMessageId", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_deleted_from_sender", + "getter_name": "isDeletedFromSender", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_deleted_from_sender\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_deleted_from_sender\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "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": "opened_by_all", + "getter_name": "openedByAll", + "moor_type": "dateTime", + "nullable": true, + "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": [] + }, + { + "name": "modified_at", + "getter_name": "modifiedAt", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ack_by_user", + "getter_name": "ackByUser", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ack_by_server", + "getter_name": "ackByServer", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "message_id" + ] + } + }, + { + "id": 4, + "references": [ + 3, + 0 + ], + "type": "table", + "data": { + "name": "message_histories", + "was_declared_in_moor": false, + "columns": [ + { + "name": "id", + "getter_name": "id", + "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_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "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": "contact_id", + "getter_name": "contactId", + "moor_type": "int", + "nullable": true, + "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": "content", + "getter_name": "content", + "moor_type": "string", + "nullable": true, + "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": [] + } + }, + { + "id": 5, + "references": [ + 3, + 0 + ], + "type": "table", + "data": { + "name": "reactions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "message_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "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": "emoji", + "getter_name": "emoji", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "sender_id", + "getter_name": "senderId", + "moor_type": "int", + "nullable": true, + "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": "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": [ + "message_id", + "sender_id", + "emoji" + ] + } + }, + { + "id": 6, + "references": [ + 1, + 0 + ], + "type": "table", + "data": { + "name": "group_members", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": "member_state", + "getter_name": "memberState", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(MemberState.values)", + "dart_type_name": "MemberState" + } + }, + { + "name": "group_public_key", + "getter_name": "groupPublicKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "last_chat_opened", + "getter_name": "lastChatOpened", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "last_type_indicator", + "getter_name": "lastTypeIndicator", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "last_message", + "getter_name": "lastMessage", + "moor_type": "dateTime", + "nullable": true, + "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": [ + "group_id", + "contact_id" + ] + } + }, + { + "id": 7, + "references": [ + 0, + 3 + ], + "type": "table", + "data": { + "name": "receipts", + "was_declared_in_moor": false, + "columns": [ + { + "name": "receipt_id", + "getter_name": "receiptId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "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": "string", + "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": "message", + "getter_name": "message", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "contact_will_sends_receipt", + "getter_name": "contactWillSendsReceipt", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"contact_will_sends_receipt\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"contact_will_sends_receipt\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('1')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "will_be_retried_by_media_upload", + "getter_name": "willBeRetriedByMediaUpload", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"will_be_retried_by_media_upload\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"will_be_retried_by_media_upload\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "mark_for_retry", + "getter_name": "markForRetry", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "mark_for_retry_after_accepted", + "getter_name": "markForRetryAfterAccepted", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ack_by_server_at", + "getter_name": "ackByServerAt", + "moor_type": "dateTime", + "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": "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": [ + "receipt_id" + ] + } + }, + { + "id": 8, + "references": [], + "type": "table", + "data": { + "name": "received_receipts", + "was_declared_in_moor": false, + "columns": [ + { + "name": "receipt_id", + "getter_name": "receiptId", + "moor_type": "string", + "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": [ + "receipt_id" + ] + } + }, + { + "id": 9, + "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": 10, + "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": 11, + "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": 12, + "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": 13, + "references": [], + "type": "table", + "data": { + "name": "signal_signed_pre_key_stores", + "was_declared_in_moor": false, + "columns": [ + { + "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": "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": [ + "signed_pre_key_id" + ] + } + }, + { + "id": 14, + "references": [ + 3, + 0 + ], + "type": "table", + "data": { + "name": "message_actions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "message_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "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": "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": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(MessageActionType.values)", + "dart_type_name": "MessageActionType" + } + }, + { + "name": "action_at", + "getter_name": "actionAt", + "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": [ + "message_id", + "contact_id", + "type" + ] + } + }, + { + "id": 15, + "references": [ + 1, + 0 + ], + "type": "table", + "data": { + "name": "group_histories", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_history_id", + "getter_name": "groupHistoryId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "contact_id", + "getter_name": "contactId", + "moor_type": "int", + "nullable": true, + "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": "affected_contact_id", + "getter_name": "affectedContactId", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "old_group_name", + "getter_name": "oldGroupName", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "new_group_name", + "getter_name": "newGroupName", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "new_delete_messages_after_milliseconds", + "getter_name": "newDeleteMessagesAfterMilliseconds", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(GroupActionType.values)", + "dart_type_name": "GroupActionType" + } + }, + { + "name": "action_at", + "getter_name": "actionAt", + "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": [ + "group_history_id" + ] + } + }, + { + "id": 16, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "key_verifications", + "was_declared_in_moor": false, + "columns": [ + { + "name": "verification_id", + "getter_name": "verificationId", + "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": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(VerificationType.values)", + "dart_type_name": "VerificationType" + } + }, + { + "name": "verified_by", + "getter_name": "verifiedBy", + "moor_type": "int", + "nullable": true, + "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": "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": [] + } + }, + { + "id": 17, + "references": [], + "type": "table", + "data": { + "name": "verification_tokens", + "was_declared_in_moor": false, + "columns": [ + { + "name": "token_id", + "getter_name": "tokenId", + "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": "token", + "getter_name": "token", + "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": [] + } + }, + { + "id": 18, + "references": [], + "type": "table", + "data": { + "name": "user_discovery_announced_users", + "was_declared_in_moor": false, + "columns": [ + { + "name": "announced_user_id", + "getter_name": "announcedUserId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "announced_public_key", + "getter_name": "announcedPublicKey", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "public_id", + "getter_name": "publicId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "UNIQUE", + "dialectAwareDefaultConstraints": { + "sqlite": "UNIQUE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + "unique" + ] + }, + { + "name": "username", + "getter_name": "username", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "was_shown_to_the_user", + "getter_name": "wasShownToTheUser", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"was_shown_to_the_user\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"was_shown_to_the_user\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_hidden", + "getter_name": "isHidden", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_hidden\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_hidden\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "was_asked_friends", + "getter_name": "wasAskedFriends", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"was_asked_friends\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"was_asked_friends\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "announced_user_id" + ] + } + }, + { + "id": 19, + "references": [ + 18, + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_user_relations", + "was_declared_in_moor": false, + "columns": [ + { + "name": "announced_user_id", + "getter_name": "announcedUserId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES user_discovery_announced_users (announced_user_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES user_discovery_announced_users (announced_user_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "user_discovery_announced_users", + "column": "announced_user_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "from_contact_id", + "getter_name": "fromContactId", + "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": "public_key_verified_timestamp", + "getter_name": "publicKeyVerifiedTimestamp", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "announced_user_id", + "from_contact_id" + ] + } + }, + { + "id": 20, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_other_promotions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "from_contact_id", + "getter_name": "fromContactId", + "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": "promotion_id", + "getter_name": "promotionId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "public_id", + "getter_name": "publicId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "threshold", + "getter_name": "threshold", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "announcement_share", + "getter_name": "announcementShare", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "public_key_verified_timestamp", + "getter_name": "publicKeyVerifiedTimestamp", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "from_contact_id", + "public_id" + ] + } + }, + { + "id": 21, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_own_promotions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "version_id", + "getter_name": "versionId", + "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": "promotion", + "getter_name": "promotion", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [] + } + }, + { + "id": 22, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_shares", + "was_declared_in_moor": false, + "columns": [ + { + "name": "share_id", + "getter_name": "shareId", + "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": "share", + "getter_name": "share", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "contact_id", + "getter_name": "contactId", + "moor_type": "int", + "nullable": true, + "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" + } + } + ] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [] + } + }, + { + "id": 23, + "references": [], + "type": "table", + "data": { + "name": "shortcuts", + "was_declared_in_moor": false, + "columns": [ + { + "name": "id", + "getter_name": "id", + "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": "emoji", + "getter_name": "emoji", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "UNIQUE", + "dialectAwareDefaultConstraints": { + "sqlite": "UNIQUE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + "unique" + ] + }, + { + "name": "usage_counter", + "getter_name": "usageCounter", + "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": [] + } + }, + { + "id": 24, + "references": [ + 23, + 1 + ], + "type": "table", + "data": { + "name": "shortcut_members", + "was_declared_in_moor": false, + "columns": [ + { + "name": "shortcut_id", + "getter_name": "shortcutId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES shortcuts (id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES shortcuts (id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "shortcuts", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "shortcut_id", + "group_id" + ] + } + } + ], + "fixed_sql": [ + { + "name": "contacts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"contacts\" (\"user_id\" INTEGER NOT NULL, \"username\" TEXT NOT NULL, \"display_name\" TEXT NULL, \"nick_name\" TEXT NULL, \"avatar_svg_compressed\" BLOB NULL, \"sender_profile_counter\" INTEGER NOT NULL DEFAULT 0, \"accepted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"accepted\" IN (0, 1)), \"deleted_by_user\" INTEGER NOT NULL DEFAULT 0 CHECK (\"deleted_by_user\" IN (0, 1)), \"requested\" INTEGER NOT NULL DEFAULT 0 CHECK (\"requested\" IN (0, 1)), \"blocked\" INTEGER NOT NULL DEFAULT 0 CHECK (\"blocked\" IN (0, 1)), \"verified\" INTEGER NOT NULL DEFAULT 0 CHECK (\"verified\" IN (0, 1)), \"account_deleted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"account_deleted\" IN (0, 1)), \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"user_discovery_version\" BLOB NULL, \"user_discovery_excluded\" INTEGER NOT NULL DEFAULT 0 CHECK (\"user_discovery_excluded\" IN (0, 1)), \"user_discovery_manual_approved\" INTEGER NULL DEFAULT 0 CHECK (\"user_discovery_manual_approved\" IN (0, 1)), \"recovery_is_trusted_friend\" INTEGER NOT NULL DEFAULT 0 CHECK (\"recovery_is_trusted_friend\" IN (0, 1)), \"recovery_last_heartbeat\" INTEGER NULL, \"recovery_secret_share\" BLOB NULL, \"recovery_contacts_secret_share\" BLOB NULL, \"recovery_contacts_last_heartbeat\" INTEGER NULL, \"ask_for_friend_promotions\" INTEGER NULL CHECK (\"ask_for_friend_promotions\" IN (0, 1)), \"media_send_counter\" INTEGER NOT NULL DEFAULT 0, \"media_received_counter\" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (\"user_id\"));" + } + ] + }, + { + "name": "groups", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"groups\" (\"group_id\" TEXT NOT NULL, \"is_group_admin\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_group_admin\" IN (0, 1)), \"is_direct_chat\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_direct_chat\" IN (0, 1)), \"pinned\" INTEGER NOT NULL DEFAULT 0 CHECK (\"pinned\" IN (0, 1)), \"archived\" INTEGER NOT NULL DEFAULT 0 CHECK (\"archived\" IN (0, 1)), \"joined_group\" INTEGER NOT NULL DEFAULT 0 CHECK (\"joined_group\" IN (0, 1)), \"left_group\" INTEGER NOT NULL DEFAULT 0 CHECK (\"left_group\" IN (0, 1)), \"deleted_content\" INTEGER NOT NULL DEFAULT 0 CHECK (\"deleted_content\" IN (0, 1)), \"state_version_id\" INTEGER NOT NULL DEFAULT 0, \"state_encryption_key\" BLOB NULL, \"my_group_private_key\" BLOB NULL, \"group_name\" TEXT NOT NULL, \"draft_message\" TEXT NULL, \"total_media_counter\" INTEGER NOT NULL DEFAULT 0, \"also_best_friend\" INTEGER NOT NULL DEFAULT 0 CHECK (\"also_best_friend\" IN (0, 1)), \"delete_messages_after_milliseconds\" INTEGER NOT NULL DEFAULT 86400000, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"last_message_send\" INTEGER NULL, \"last_message_received\" INTEGER NULL, \"last_flame_counter_change\" INTEGER NULL, \"last_flame_sync\" INTEGER NULL, \"flame_counter\" INTEGER NOT NULL DEFAULT 0, \"max_flame_counter\" INTEGER NOT NULL DEFAULT 0, \"max_flame_counter_from\" INTEGER NULL, \"last_message_exchange\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"group_id\"));" + } + ] + }, + { + "name": "media_files", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"media_files\" (\"media_id\" TEXT NOT NULL, \"type\" TEXT NOT NULL, \"upload_state\" TEXT NULL, \"download_state\" TEXT NULL, \"requires_authentication\" INTEGER NOT NULL DEFAULT 0 CHECK (\"requires_authentication\" IN (0, 1)), \"stored\" INTEGER NOT NULL DEFAULT 0 CHECK (\"stored\" IN (0, 1)), \"is_draft_media\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_draft_media\" IN (0, 1)), \"is_favorite\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_favorite\" IN (0, 1)), \"has_crop_analyzed\" INTEGER NOT NULL DEFAULT 0 CHECK (\"has_crop_analyzed\" IN (0, 1)), \"pre_progressing_process\" INTEGER NULL, \"reupload_requested_by\" TEXT NULL, \"display_limit_in_milliseconds\" INTEGER NULL, \"remove_audio\" INTEGER NULL CHECK (\"remove_audio\" IN (0, 1)), \"download_token\" BLOB NULL, \"encryption_key\" BLOB NULL, \"encryption_mac\" BLOB NULL, \"encryption_nonce\" BLOB NULL, \"stored_file_hash\" BLOB NULL, \"has_thumbnail\" INTEGER NOT NULL DEFAULT 0 CHECK (\"has_thumbnail\" IN (0, 1)), \"size_in_bytes\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"created_at_month\" TEXT NULL, PRIMARY KEY (\"media_id\"));" + } + ] + }, + { + "name": "messages", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"messages\" (\"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, \"message_id\" TEXT NOT NULL, \"sender_id\" INTEGER NULL REFERENCES contacts (user_id), \"type\" TEXT NOT NULL, \"content\" TEXT NULL, \"media_id\" TEXT NULL REFERENCES media_files (media_id) ON DELETE SET NULL, \"additional_message_data\" BLOB NULL, \"media_stored\" INTEGER NOT NULL DEFAULT 0 CHECK (\"media_stored\" IN (0, 1)), \"media_reopened\" INTEGER NOT NULL DEFAULT 0 CHECK (\"media_reopened\" IN (0, 1)), \"download_token\" BLOB NULL, \"quotes_message_id\" TEXT NULL, \"is_deleted_from_sender\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_deleted_from_sender\" IN (0, 1)), \"opened_at\" INTEGER NULL, \"opened_by_all\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"modified_at\" INTEGER NULL, \"ack_by_user\" INTEGER NULL, \"ack_by_server\" INTEGER NULL, PRIMARY KEY (\"message_id\"));" + } + ] + }, + { + "name": "message_histories", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"message_histories\" (\"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"message_id\" TEXT NOT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"contact_id\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"content\" TEXT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)));" + } + ] + }, + { + "name": "reactions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"reactions\" (\"message_id\" TEXT NOT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"emoji\" TEXT NOT NULL, \"sender_id\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"message_id\", \"sender_id\", \"emoji\"));" + } + ] + }, + { + "name": "group_members", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"group_members\" (\"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id), \"member_state\" TEXT NULL, \"group_public_key\" BLOB NULL, \"last_chat_opened\" INTEGER NULL, \"last_type_indicator\" INTEGER NULL, \"last_message\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"group_id\", \"contact_id\"));" + } + ] + }, + { + "name": "receipts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"receipts\" (\"receipt_id\" TEXT NOT NULL, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"message_id\" TEXT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"message\" BLOB NOT NULL, \"contact_will_sends_receipt\" INTEGER NOT NULL DEFAULT 1 CHECK (\"contact_will_sends_receipt\" IN (0, 1)), \"will_be_retried_by_media_upload\" INTEGER NOT NULL DEFAULT 0 CHECK (\"will_be_retried_by_media_upload\" IN (0, 1)), \"mark_for_retry\" INTEGER NULL, \"mark_for_retry_after_accepted\" INTEGER NULL, \"ack_by_server_at\" INTEGER NULL, \"retry_count\" INTEGER NOT NULL DEFAULT 0, \"last_retry\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"receipt_id\"));" + } + ] + }, + { + "name": "received_receipts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"received_receipts\" (\"receipt_id\" TEXT NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"receipt_id\"));" + } + ] + }, + { + "name": "signal_identity_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_identity_key_stores\" (\"device_id\" INTEGER NOT NULL, \"name\" TEXT NOT NULL, \"identity_key\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"device_id\", \"name\"));" + } + ] + }, + { + "name": "signal_pre_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_pre_key_stores\" (\"pre_key_id\" INTEGER NOT NULL, \"pre_key\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"pre_key_id\"));" + } + ] + }, + { + "name": "signal_sender_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_sender_key_stores\" (\"sender_key_name\" TEXT NOT NULL, \"sender_key\" BLOB NOT NULL, PRIMARY KEY (\"sender_key_name\"));" + } + ] + }, + { + "name": "signal_session_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_session_stores\" (\"device_id\" INTEGER NOT NULL, \"name\" TEXT NOT NULL, \"session_record\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"device_id\", \"name\"));" + } + ] + }, + { + "name": "signal_signed_pre_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_signed_pre_key_stores\" (\"signed_pre_key_id\" INTEGER NOT NULL, \"signed_pre_key\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"signed_pre_key_id\"));" + } + ] + }, + { + "name": "message_actions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"message_actions\" (\"message_id\" TEXT NOT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"type\" TEXT NOT NULL, \"action_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"message_id\", \"contact_id\", \"type\"));" + } + ] + }, + { + "name": "group_histories", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"group_histories\" (\"group_history_id\" TEXT NOT NULL, \"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, \"contact_id\" INTEGER NULL REFERENCES contacts (user_id), \"affected_contact_id\" INTEGER NULL, \"old_group_name\" TEXT NULL, \"new_group_name\" TEXT NULL, \"new_delete_messages_after_milliseconds\" INTEGER NULL, \"type\" TEXT NOT NULL, \"action_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"group_history_id\"));" + } + ] + }, + { + "name": "key_verifications", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"key_verifications\" (\"verification_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"type\" TEXT NOT NULL, \"verified_by\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)));" + } + ] + }, + { + "name": "verification_tokens", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"verification_tokens\" (\"token_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"token\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)));" + } + ] + }, + { + "name": "user_discovery_announced_users", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_announced_users\" (\"announced_user_id\" INTEGER NOT NULL, \"announced_public_key\" BLOB NOT NULL, \"public_id\" INTEGER NOT NULL UNIQUE, \"username\" TEXT NULL, \"was_shown_to_the_user\" INTEGER NOT NULL DEFAULT 0 CHECK (\"was_shown_to_the_user\" IN (0, 1)), \"is_hidden\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_hidden\" IN (0, 1)), \"was_asked_friends\" INTEGER NOT NULL DEFAULT 0 CHECK (\"was_asked_friends\" IN (0, 1)), PRIMARY KEY (\"announced_user_id\"));" + } + ] + }, + { + "name": "user_discovery_user_relations", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_user_relations\" (\"announced_user_id\" INTEGER NOT NULL REFERENCES user_discovery_announced_users (announced_user_id) ON DELETE CASCADE, \"from_contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"public_key_verified_timestamp\" INTEGER NULL, PRIMARY KEY (\"announced_user_id\", \"from_contact_id\"));" + } + ] + }, + { + "name": "user_discovery_other_promotions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_other_promotions\" (\"from_contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"promotion_id\" INTEGER NOT NULL, \"public_id\" INTEGER NOT NULL, \"threshold\" INTEGER NOT NULL, \"announcement_share\" BLOB NOT NULL, \"public_key_verified_timestamp\" INTEGER NULL, PRIMARY KEY (\"from_contact_id\", \"public_id\"));" + } + ] + }, + { + "name": "user_discovery_own_promotions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_own_promotions\" (\"version_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"promotion\" BLOB NOT NULL);" + } + ] + }, + { + "name": "user_discovery_shares", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_shares\" (\"share_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"share\" BLOB NOT NULL, \"contact_id\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE);" + } + ] + }, + { + "name": "shortcuts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"shortcuts\" (\"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"emoji\" TEXT NOT NULL UNIQUE, \"usage_counter\" INTEGER NOT NULL DEFAULT 0);" + } + ] + }, + { + "name": "shortcut_members", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"shortcut_members\" (\"shortcut_id\" INTEGER NOT NULL REFERENCES shortcuts (id) ON DELETE CASCADE, \"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, PRIMARY KEY (\"shortcut_id\", \"group_id\"));" + } + ] + } + ] +} \ No newline at end of file diff --git a/lib/src/database/schemas/twonly_db/drift_schema_v22.json b/lib/src/database/schemas/twonly_db/drift_schema_v22.json new file mode 100644 index 00000000..7bd12746 --- /dev/null +++ b/lib/src/database/schemas/twonly_db/drift_schema_v22.json @@ -0,0 +1,3137 @@ +{ + "_meta": { + "description": "This file contains a serialized version of schema entities for drift.", + "version": "1.3.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, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "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_compressed", + "getter_name": "avatarSvgCompressed", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "sender_profile_counter", + "getter_name": "senderProfileCounter", + "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": "deleted_by_user", + "getter_name": "deletedByUser", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"deleted_by_user\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"deleted_by_user\" 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": "account_deleted", + "getter_name": "accountDeleted", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"account_deleted\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"account_deleted\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "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": "user_discovery_version", + "getter_name": "userDiscoveryVersion", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "user_discovery_excluded", + "getter_name": "userDiscoveryExcluded", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"user_discovery_excluded\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"user_discovery_excluded\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "user_discovery_manual_approved", + "getter_name": "userDiscoveryManualApproved", + "moor_type": "bool", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "CHECK (\"user_discovery_manual_approved\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"user_discovery_manual_approved\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_is_trusted_friend", + "getter_name": "recoveryIsTrustedFriend", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"recovery_is_trusted_friend\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"recovery_is_trusted_friend\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_last_heartbeat", + "getter_name": "recoveryLastHeartbeat", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_secret_share", + "getter_name": "recoverySecretShare", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_contacts_secret_share", + "getter_name": "recoveryContactsSecretShare", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_contacts_last_heartbeat", + "getter_name": "recoveryContactsLastHeartbeat", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "recovery_contacts_threshold", + "getter_name": "recoveryContactsThreshold", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ask_for_friend_promotions", + "getter_name": "askForFriendPromotions", + "moor_type": "bool", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "CHECK (\"ask_for_friend_promotions\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"ask_for_friend_promotions\" IN (0, 1))" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "media_send_counter", + "getter_name": "mediaSendCounter", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "media_received_counter", + "getter_name": "mediaReceivedCounter", + "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": [], + "type": "table", + "data": { + "name": "groups", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_group_admin", + "getter_name": "isGroupAdmin", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_group_admin\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_group_admin\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_direct_chat", + "getter_name": "isDirectChat", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_direct_chat\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_direct_chat\" 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": "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": "joined_group", + "getter_name": "joinedGroup", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"joined_group\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"joined_group\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "left_group", + "getter_name": "leftGroup", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"left_group\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"left_group\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "deleted_content", + "getter_name": "deletedContent", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"deleted_content\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"deleted_content\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "state_version_id", + "getter_name": "stateVersionId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "state_encryption_key", + "getter_name": "stateEncryptionKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "my_group_private_key", + "getter_name": "myGroupPrivateKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "group_name", + "getter_name": "groupName", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "draft_message", + "getter_name": "draftMessage", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "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": "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_milliseconds", + "getter_name": "deleteMessagesAfterMilliseconds", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('86400000')", + "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": "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": "flame_counter", + "getter_name": "flameCounter", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "max_flame_counter", + "getter_name": "maxFlameCounter", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "max_flame_counter_from", + "getter_name": "maxFlameCounterFrom", + "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": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "group_id" + ] + } + }, + { + "id": 2, + "references": [], + "type": "table", + "data": { + "name": "media_files", + "was_declared_in_moor": false, + "columns": [ + { + "name": "media_id", + "getter_name": "mediaId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(MediaType.values)", + "dart_type_name": "MediaType" + } + }, + { + "name": "upload_state", + "getter_name": "uploadState", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(UploadState.values)", + "dart_type_name": "UploadState" + } + }, + { + "name": "download_state", + "getter_name": "downloadState", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(DownloadState.values)", + "dart_type_name": "DownloadState" + } + }, + { + "name": "requires_authentication", + "getter_name": "requiresAuthentication", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"requires_authentication\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"requires_authentication\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "stored", + "getter_name": "stored", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"stored\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"stored\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_draft_media", + "getter_name": "isDraftMedia", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_draft_media\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_draft_media\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_favorite", + "getter_name": "isFavorite", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_favorite\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_favorite\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "has_crop_analyzed", + "getter_name": "hasCropAnalyzed", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"has_crop_analyzed\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"has_crop_analyzed\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "pre_progressing_process", + "getter_name": "preProgressingProcess", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "reupload_requested_by", + "getter_name": "reuploadRequestedBy", + "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": "display_limit_in_milliseconds", + "getter_name": "displayLimitInMilliseconds", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "remove_audio", + "getter_name": "removeAudio", + "moor_type": "bool", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "CHECK (\"remove_audio\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"remove_audio\" IN (0, 1))" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "download_token", + "getter_name": "downloadToken", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "encryption_key", + "getter_name": "encryptionKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "encryption_mac", + "getter_name": "encryptionMac", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "encryption_nonce", + "getter_name": "encryptionNonce", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "stored_file_hash", + "getter_name": "storedFileHash", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "has_thumbnail", + "getter_name": "hasThumbnail", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"has_thumbnail\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"has_thumbnail\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "size_in_bytes", + "getter_name": "sizeInBytes", + "moor_type": "int", + "nullable": true, + "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": [] + }, + { + "name": "created_at_month", + "getter_name": "createdAtMonth", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "media_id" + ] + } + }, + { + "id": 3, + "references": [ + 1, + 0, + 2 + ], + "type": "table", + "data": { + "name": "messages", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "message_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "sender_id", + "getter_name": "senderId", + "moor_type": "int", + "nullable": true, + "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": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "content", + "getter_name": "content", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "media_id", + "getter_name": "mediaId", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "defaultConstraints": "REFERENCES media_files (media_id) ON DELETE SET NULL", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES media_files (media_id) ON DELETE SET NULL" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "media_files", + "column": "media_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "setNull" + } + } + ] + }, + { + "name": "additional_message_data", + "getter_name": "additionalMessageData", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "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": "media_reopened", + "getter_name": "mediaReopened", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"media_reopened\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"media_reopened\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "download_token", + "getter_name": "downloadToken", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "quotes_message_id", + "getter_name": "quotesMessageId", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_deleted_from_sender", + "getter_name": "isDeletedFromSender", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_deleted_from_sender\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_deleted_from_sender\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "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": "opened_by_all", + "getter_name": "openedByAll", + "moor_type": "dateTime", + "nullable": true, + "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": [] + }, + { + "name": "modified_at", + "getter_name": "modifiedAt", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ack_by_user", + "getter_name": "ackByUser", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ack_by_server", + "getter_name": "ackByServer", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "message_id" + ] + } + }, + { + "id": 4, + "references": [ + 3, + 0 + ], + "type": "table", + "data": { + "name": "message_histories", + "was_declared_in_moor": false, + "columns": [ + { + "name": "id", + "getter_name": "id", + "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_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "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": "contact_id", + "getter_name": "contactId", + "moor_type": "int", + "nullable": true, + "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": "content", + "getter_name": "content", + "moor_type": "string", + "nullable": true, + "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": [] + } + }, + { + "id": 5, + "references": [ + 3, + 0 + ], + "type": "table", + "data": { + "name": "reactions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "message_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "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": "emoji", + "getter_name": "emoji", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "sender_id", + "getter_name": "senderId", + "moor_type": "int", + "nullable": true, + "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": "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": [ + "message_id", + "sender_id", + "emoji" + ] + } + }, + { + "id": 6, + "references": [ + 1, + 0 + ], + "type": "table", + "data": { + "name": "group_members", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": "member_state", + "getter_name": "memberState", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(MemberState.values)", + "dart_type_name": "MemberState" + } + }, + { + "name": "group_public_key", + "getter_name": "groupPublicKey", + "moor_type": "blob", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "last_chat_opened", + "getter_name": "lastChatOpened", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "last_type_indicator", + "getter_name": "lastTypeIndicator", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "last_message", + "getter_name": "lastMessage", + "moor_type": "dateTime", + "nullable": true, + "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": [ + "group_id", + "contact_id" + ] + } + }, + { + "id": 7, + "references": [ + 0, + 3 + ], + "type": "table", + "data": { + "name": "receipts", + "was_declared_in_moor": false, + "columns": [ + { + "name": "receipt_id", + "getter_name": "receiptId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "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": "string", + "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": "message", + "getter_name": "message", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "contact_will_sends_receipt", + "getter_name": "contactWillSendsReceipt", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"contact_will_sends_receipt\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"contact_will_sends_receipt\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('1')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "will_be_retried_by_media_upload", + "getter_name": "willBeRetriedByMediaUpload", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"will_be_retried_by_media_upload\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"will_be_retried_by_media_upload\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "mark_for_retry", + "getter_name": "markForRetry", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "mark_for_retry_after_accepted", + "getter_name": "markForRetryAfterAccepted", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "ack_by_server_at", + "getter_name": "ackByServerAt", + "moor_type": "dateTime", + "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": "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": [ + "receipt_id" + ] + } + }, + { + "id": 8, + "references": [], + "type": "table", + "data": { + "name": "received_receipts", + "was_declared_in_moor": false, + "columns": [ + { + "name": "receipt_id", + "getter_name": "receiptId", + "moor_type": "string", + "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": [ + "receipt_id" + ] + } + }, + { + "id": 9, + "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": 10, + "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": 11, + "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": 12, + "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": 13, + "references": [], + "type": "table", + "data": { + "name": "signal_signed_pre_key_stores", + "was_declared_in_moor": false, + "columns": [ + { + "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": "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": [ + "signed_pre_key_id" + ] + } + }, + { + "id": 14, + "references": [ + 3, + 0 + ], + "type": "table", + "data": { + "name": "message_actions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "message_id", + "getter_name": "messageId", + "moor_type": "string", + "nullable": false, + "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": "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": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(MessageActionType.values)", + "dart_type_name": "MessageActionType" + } + }, + { + "name": "action_at", + "getter_name": "actionAt", + "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": [ + "message_id", + "contact_id", + "type" + ] + } + }, + { + "id": 15, + "references": [ + 1, + 0 + ], + "type": "table", + "data": { + "name": "group_histories", + "was_declared_in_moor": false, + "columns": [ + { + "name": "group_history_id", + "getter_name": "groupHistoryId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "contact_id", + "getter_name": "contactId", + "moor_type": "int", + "nullable": true, + "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": "affected_contact_id", + "getter_name": "affectedContactId", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "old_group_name", + "getter_name": "oldGroupName", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "new_group_name", + "getter_name": "newGroupName", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "new_delete_messages_after_milliseconds", + "getter_name": "newDeleteMessagesAfterMilliseconds", + "moor_type": "int", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(GroupActionType.values)", + "dart_type_name": "GroupActionType" + } + }, + { + "name": "action_at", + "getter_name": "actionAt", + "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": [ + "group_history_id" + ] + } + }, + { + "id": 16, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "key_verifications", + "was_declared_in_moor": false, + "columns": [ + { + "name": "verification_id", + "getter_name": "verificationId", + "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": "type", + "getter_name": "type", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumNameConverter(VerificationType.values)", + "dart_type_name": "VerificationType" + } + }, + { + "name": "verified_by", + "getter_name": "verifiedBy", + "moor_type": "int", + "nullable": true, + "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": "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": [] + } + }, + { + "id": 17, + "references": [], + "type": "table", + "data": { + "name": "verification_tokens", + "was_declared_in_moor": false, + "columns": [ + { + "name": "token_id", + "getter_name": "tokenId", + "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": "token", + "getter_name": "token", + "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": [] + } + }, + { + "id": 18, + "references": [], + "type": "table", + "data": { + "name": "user_discovery_announced_users", + "was_declared_in_moor": false, + "columns": [ + { + "name": "announced_user_id", + "getter_name": "announcedUserId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "announced_public_key", + "getter_name": "announcedPublicKey", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "public_id", + "getter_name": "publicId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "UNIQUE", + "dialectAwareDefaultConstraints": { + "sqlite": "UNIQUE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + "unique" + ] + }, + { + "name": "username", + "getter_name": "username", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "was_shown_to_the_user", + "getter_name": "wasShownToTheUser", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"was_shown_to_the_user\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"was_shown_to_the_user\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "is_hidden", + "getter_name": "isHidden", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_hidden\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_hidden\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "was_asked_friends", + "getter_name": "wasAskedFriends", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"was_asked_friends\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"was_asked_friends\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "announced_user_id" + ] + } + }, + { + "id": 19, + "references": [ + 18, + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_user_relations", + "was_declared_in_moor": false, + "columns": [ + { + "name": "announced_user_id", + "getter_name": "announcedUserId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES user_discovery_announced_users (announced_user_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES user_discovery_announced_users (announced_user_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "user_discovery_announced_users", + "column": "announced_user_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "from_contact_id", + "getter_name": "fromContactId", + "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": "public_key_verified_timestamp", + "getter_name": "publicKeyVerifiedTimestamp", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "announced_user_id", + "from_contact_id" + ] + } + }, + { + "id": 20, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_other_promotions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "from_contact_id", + "getter_name": "fromContactId", + "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": "promotion_id", + "getter_name": "promotionId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "public_id", + "getter_name": "publicId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "threshold", + "getter_name": "threshold", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "announcement_share", + "getter_name": "announcementShare", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "public_key_verified_timestamp", + "getter_name": "publicKeyVerifiedTimestamp", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "from_contact_id", + "public_id" + ] + } + }, + { + "id": 21, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_own_promotions", + "was_declared_in_moor": false, + "columns": [ + { + "name": "version_id", + "getter_name": "versionId", + "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": "promotion", + "getter_name": "promotion", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [] + } + }, + { + "id": 22, + "references": [ + 0 + ], + "type": "table", + "data": { + "name": "user_discovery_shares", + "was_declared_in_moor": false, + "columns": [ + { + "name": "share_id", + "getter_name": "shareId", + "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": "share", + "getter_name": "share", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "contact_id", + "getter_name": "contactId", + "moor_type": "int", + "nullable": true, + "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" + } + } + ] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [] + } + }, + { + "id": 23, + "references": [], + "type": "table", + "data": { + "name": "shortcuts", + "was_declared_in_moor": false, + "columns": [ + { + "name": "id", + "getter_name": "id", + "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": "emoji", + "getter_name": "emoji", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "UNIQUE", + "dialectAwareDefaultConstraints": { + "sqlite": "UNIQUE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + "unique" + ] + }, + { + "name": "usage_counter", + "getter_name": "usageCounter", + "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": [] + } + }, + { + "id": 24, + "references": [ + 23, + 1 + ], + "type": "table", + "data": { + "name": "shortcut_members", + "was_declared_in_moor": false, + "columns": [ + { + "name": "shortcut_id", + "getter_name": "shortcutId", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES shortcuts (id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES shortcuts (id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "shortcuts", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "group_id", + "getter_name": "groupId", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE", + "dialectAwareDefaultConstraints": { + "sqlite": "REFERENCES \"groups\" (group_id) ON DELETE CASCADE" + }, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [ + { + "foreign_key": { + "to": { + "table": "groups", + "column": "group_id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + } + ], + "is_virtual": false, + "without_rowid": false, + "constraints": [], + "explicit_pk": [ + "shortcut_id", + "group_id" + ] + } + } + ], + "fixed_sql": [ + { + "name": "contacts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"contacts\" (\"user_id\" INTEGER NOT NULL, \"username\" TEXT NOT NULL, \"display_name\" TEXT NULL, \"nick_name\" TEXT NULL, \"avatar_svg_compressed\" BLOB NULL, \"sender_profile_counter\" INTEGER NOT NULL DEFAULT 0, \"accepted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"accepted\" IN (0, 1)), \"deleted_by_user\" INTEGER NOT NULL DEFAULT 0 CHECK (\"deleted_by_user\" IN (0, 1)), \"requested\" INTEGER NOT NULL DEFAULT 0 CHECK (\"requested\" IN (0, 1)), \"blocked\" INTEGER NOT NULL DEFAULT 0 CHECK (\"blocked\" IN (0, 1)), \"verified\" INTEGER NOT NULL DEFAULT 0 CHECK (\"verified\" IN (0, 1)), \"account_deleted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"account_deleted\" IN (0, 1)), \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"user_discovery_version\" BLOB NULL, \"user_discovery_excluded\" INTEGER NOT NULL DEFAULT 0 CHECK (\"user_discovery_excluded\" IN (0, 1)), \"user_discovery_manual_approved\" INTEGER NULL DEFAULT 0 CHECK (\"user_discovery_manual_approved\" IN (0, 1)), \"recovery_is_trusted_friend\" INTEGER NOT NULL DEFAULT 0 CHECK (\"recovery_is_trusted_friend\" IN (0, 1)), \"recovery_last_heartbeat\" INTEGER NULL, \"recovery_secret_share\" BLOB NULL, \"recovery_contacts_secret_share\" BLOB NULL, \"recovery_contacts_last_heartbeat\" INTEGER NULL, \"recovery_contacts_threshold\" INTEGER NULL, \"ask_for_friend_promotions\" INTEGER NULL CHECK (\"ask_for_friend_promotions\" IN (0, 1)), \"media_send_counter\" INTEGER NOT NULL DEFAULT 0, \"media_received_counter\" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (\"user_id\"));" + } + ] + }, + { + "name": "groups", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"groups\" (\"group_id\" TEXT NOT NULL, \"is_group_admin\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_group_admin\" IN (0, 1)), \"is_direct_chat\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_direct_chat\" IN (0, 1)), \"pinned\" INTEGER NOT NULL DEFAULT 0 CHECK (\"pinned\" IN (0, 1)), \"archived\" INTEGER NOT NULL DEFAULT 0 CHECK (\"archived\" IN (0, 1)), \"joined_group\" INTEGER NOT NULL DEFAULT 0 CHECK (\"joined_group\" IN (0, 1)), \"left_group\" INTEGER NOT NULL DEFAULT 0 CHECK (\"left_group\" IN (0, 1)), \"deleted_content\" INTEGER NOT NULL DEFAULT 0 CHECK (\"deleted_content\" IN (0, 1)), \"state_version_id\" INTEGER NOT NULL DEFAULT 0, \"state_encryption_key\" BLOB NULL, \"my_group_private_key\" BLOB NULL, \"group_name\" TEXT NOT NULL, \"draft_message\" TEXT NULL, \"total_media_counter\" INTEGER NOT NULL DEFAULT 0, \"also_best_friend\" INTEGER NOT NULL DEFAULT 0 CHECK (\"also_best_friend\" IN (0, 1)), \"delete_messages_after_milliseconds\" INTEGER NOT NULL DEFAULT 86400000, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"last_message_send\" INTEGER NULL, \"last_message_received\" INTEGER NULL, \"last_flame_counter_change\" INTEGER NULL, \"last_flame_sync\" INTEGER NULL, \"flame_counter\" INTEGER NOT NULL DEFAULT 0, \"max_flame_counter\" INTEGER NOT NULL DEFAULT 0, \"max_flame_counter_from\" INTEGER NULL, \"last_message_exchange\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"group_id\"));" + } + ] + }, + { + "name": "media_files", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"media_files\" (\"media_id\" TEXT NOT NULL, \"type\" TEXT NOT NULL, \"upload_state\" TEXT NULL, \"download_state\" TEXT NULL, \"requires_authentication\" INTEGER NOT NULL DEFAULT 0 CHECK (\"requires_authentication\" IN (0, 1)), \"stored\" INTEGER NOT NULL DEFAULT 0 CHECK (\"stored\" IN (0, 1)), \"is_draft_media\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_draft_media\" IN (0, 1)), \"is_favorite\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_favorite\" IN (0, 1)), \"has_crop_analyzed\" INTEGER NOT NULL DEFAULT 0 CHECK (\"has_crop_analyzed\" IN (0, 1)), \"pre_progressing_process\" INTEGER NULL, \"reupload_requested_by\" TEXT NULL, \"display_limit_in_milliseconds\" INTEGER NULL, \"remove_audio\" INTEGER NULL CHECK (\"remove_audio\" IN (0, 1)), \"download_token\" BLOB NULL, \"encryption_key\" BLOB NULL, \"encryption_mac\" BLOB NULL, \"encryption_nonce\" BLOB NULL, \"stored_file_hash\" BLOB NULL, \"has_thumbnail\" INTEGER NOT NULL DEFAULT 0 CHECK (\"has_thumbnail\" IN (0, 1)), \"size_in_bytes\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"created_at_month\" TEXT NULL, PRIMARY KEY (\"media_id\"));" + } + ] + }, + { + "name": "messages", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"messages\" (\"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, \"message_id\" TEXT NOT NULL, \"sender_id\" INTEGER NULL REFERENCES contacts (user_id), \"type\" TEXT NOT NULL, \"content\" TEXT NULL, \"media_id\" TEXT NULL REFERENCES media_files (media_id) ON DELETE SET NULL, \"additional_message_data\" BLOB NULL, \"media_stored\" INTEGER NOT NULL DEFAULT 0 CHECK (\"media_stored\" IN (0, 1)), \"media_reopened\" INTEGER NOT NULL DEFAULT 0 CHECK (\"media_reopened\" IN (0, 1)), \"download_token\" BLOB NULL, \"quotes_message_id\" TEXT NULL, \"is_deleted_from_sender\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_deleted_from_sender\" IN (0, 1)), \"opened_at\" INTEGER NULL, \"opened_by_all\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"modified_at\" INTEGER NULL, \"ack_by_user\" INTEGER NULL, \"ack_by_server\" INTEGER NULL, PRIMARY KEY (\"message_id\"));" + } + ] + }, + { + "name": "message_histories", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"message_histories\" (\"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"message_id\" TEXT NOT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"contact_id\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"content\" TEXT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)));" + } + ] + }, + { + "name": "reactions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"reactions\" (\"message_id\" TEXT NOT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"emoji\" TEXT NOT NULL, \"sender_id\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"message_id\", \"sender_id\", \"emoji\"));" + } + ] + }, + { + "name": "group_members", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"group_members\" (\"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id), \"member_state\" TEXT NULL, \"group_public_key\" BLOB NULL, \"last_chat_opened\" INTEGER NULL, \"last_type_indicator\" INTEGER NULL, \"last_message\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"group_id\", \"contact_id\"));" + } + ] + }, + { + "name": "receipts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"receipts\" (\"receipt_id\" TEXT NOT NULL, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"message_id\" TEXT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"message\" BLOB NOT NULL, \"contact_will_sends_receipt\" INTEGER NOT NULL DEFAULT 1 CHECK (\"contact_will_sends_receipt\" IN (0, 1)), \"will_be_retried_by_media_upload\" INTEGER NOT NULL DEFAULT 0 CHECK (\"will_be_retried_by_media_upload\" IN (0, 1)), \"mark_for_retry\" INTEGER NULL, \"mark_for_retry_after_accepted\" INTEGER NULL, \"ack_by_server_at\" INTEGER NULL, \"retry_count\" INTEGER NOT NULL DEFAULT 0, \"last_retry\" INTEGER NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"receipt_id\"));" + } + ] + }, + { + "name": "received_receipts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"received_receipts\" (\"receipt_id\" TEXT NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"receipt_id\"));" + } + ] + }, + { + "name": "signal_identity_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_identity_key_stores\" (\"device_id\" INTEGER NOT NULL, \"name\" TEXT NOT NULL, \"identity_key\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"device_id\", \"name\"));" + } + ] + }, + { + "name": "signal_pre_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_pre_key_stores\" (\"pre_key_id\" INTEGER NOT NULL, \"pre_key\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"pre_key_id\"));" + } + ] + }, + { + "name": "signal_sender_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_sender_key_stores\" (\"sender_key_name\" TEXT NOT NULL, \"sender_key\" BLOB NOT NULL, PRIMARY KEY (\"sender_key_name\"));" + } + ] + }, + { + "name": "signal_session_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_session_stores\" (\"device_id\" INTEGER NOT NULL, \"name\" TEXT NOT NULL, \"session_record\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"device_id\", \"name\"));" + } + ] + }, + { + "name": "signal_signed_pre_key_stores", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"signal_signed_pre_key_stores\" (\"signed_pre_key_id\" INTEGER NOT NULL, \"signed_pre_key\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"signed_pre_key_id\"));" + } + ] + }, + { + "name": "message_actions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"message_actions\" (\"message_id\" TEXT NOT NULL REFERENCES messages (message_id) ON DELETE CASCADE, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"type\" TEXT NOT NULL, \"action_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"message_id\", \"contact_id\", \"type\"));" + } + ] + }, + { + "name": "group_histories", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"group_histories\" (\"group_history_id\" TEXT NOT NULL, \"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, \"contact_id\" INTEGER NULL REFERENCES contacts (user_id), \"affected_contact_id\" INTEGER NULL, \"old_group_name\" TEXT NULL, \"new_group_name\" TEXT NULL, \"new_delete_messages_after_milliseconds\" INTEGER NULL, \"type\" TEXT NOT NULL, \"action_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), PRIMARY KEY (\"group_history_id\"));" + } + ] + }, + { + "name": "key_verifications", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"key_verifications\" (\"verification_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"type\" TEXT NOT NULL, \"verified_by\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)));" + } + ] + }, + { + "name": "verification_tokens", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"verification_tokens\" (\"token_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"token\" BLOB NOT NULL, \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)));" + } + ] + }, + { + "name": "user_discovery_announced_users", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_announced_users\" (\"announced_user_id\" INTEGER NOT NULL, \"announced_public_key\" BLOB NOT NULL, \"public_id\" INTEGER NOT NULL UNIQUE, \"username\" TEXT NULL, \"was_shown_to_the_user\" INTEGER NOT NULL DEFAULT 0 CHECK (\"was_shown_to_the_user\" IN (0, 1)), \"is_hidden\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_hidden\" IN (0, 1)), \"was_asked_friends\" INTEGER NOT NULL DEFAULT 0 CHECK (\"was_asked_friends\" IN (0, 1)), PRIMARY KEY (\"announced_user_id\"));" + } + ] + }, + { + "name": "user_discovery_user_relations", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_user_relations\" (\"announced_user_id\" INTEGER NOT NULL REFERENCES user_discovery_announced_users (announced_user_id) ON DELETE CASCADE, \"from_contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"public_key_verified_timestamp\" INTEGER NULL, PRIMARY KEY (\"announced_user_id\", \"from_contact_id\"));" + } + ] + }, + { + "name": "user_discovery_other_promotions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_other_promotions\" (\"from_contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"promotion_id\" INTEGER NOT NULL, \"public_id\" INTEGER NOT NULL, \"threshold\" INTEGER NOT NULL, \"announcement_share\" BLOB NOT NULL, \"public_key_verified_timestamp\" INTEGER NULL, PRIMARY KEY (\"from_contact_id\", \"public_id\"));" + } + ] + }, + { + "name": "user_discovery_own_promotions", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_own_promotions\" (\"version_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"promotion\" BLOB NOT NULL);" + } + ] + }, + { + "name": "user_discovery_shares", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_shares\" (\"share_id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"share\" BLOB NOT NULL, \"contact_id\" INTEGER NULL REFERENCES contacts (user_id) ON DELETE CASCADE);" + } + ] + }, + { + "name": "shortcuts", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"shortcuts\" (\"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"emoji\" TEXT NOT NULL UNIQUE, \"usage_counter\" INTEGER NOT NULL DEFAULT 0);" + } + ] + }, + { + "name": "shortcut_members", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"shortcut_members\" (\"shortcut_id\" INTEGER NOT NULL REFERENCES shortcuts (id) ON DELETE CASCADE, \"group_id\" TEXT NOT NULL REFERENCES \"groups\" (group_id) ON DELETE CASCADE, PRIMARY KEY (\"shortcut_id\", \"group_id\"));" + } + ] + } + ] +} \ No newline at end of file diff --git a/lib/src/database/signal/signal_pre_key_store.dart b/lib/src/database/signal/signal_pre_key_store.dart index 10ae75c7..dc7cac75 100644 --- a/lib/src/database/signal/signal_pre_key_store.dart +++ b/lib/src/database/signal/signal_pre_key_store.dart @@ -42,7 +42,9 @@ class SignalPreKeyStore extends PreKeyStore { ); try { - await twonlyDB.into(twonlyDB.signalPreKeyStores).insert(preKeyCompanion); + await twonlyDB + .into(twonlyDB.signalPreKeyStores) + .insert(preKeyCompanion, mode: InsertMode.insertOrReplace); } catch (e) { Log.error('$e'); } diff --git a/lib/src/database/tables/contacts.table.dart b/lib/src/database/tables/contacts.table.dart index e70cecd2..b456e534 100644 --- a/lib/src/database/tables/contacts.table.dart +++ b/lib/src/database/tables/contacts.table.dart @@ -36,6 +36,11 @@ class Contacts extends Table { DateTimeColumn get recoveryLastHeartbeat => dateTime().nullable()(); BlobColumn get recoverySecretShare => blob().nullable()(); + // This is the share from the contact in case the contact has selected this user as his trusted friend. + BlobColumn get recoveryContactsSecretShare => blob().nullable()(); + DateTimeColumn get recoveryContactsLastHeartbeat => dateTime().nullable()(); + IntColumn get recoveryContactsThreshold => integer().nullable()(); + BoolColumn get askForFriendPromotions => boolean().nullable()(); IntColumn get mediaSendCounter => integer().withDefault(const Constant(0))(); diff --git a/lib/src/database/twonly.db.dart b/lib/src/database/twonly.db.dart index 10ed6bf5..77c01266 100644 --- a/lib/src/database/twonly.db.dart +++ b/lib/src/database/twonly.db.dart @@ -82,7 +82,7 @@ class TwonlyDB extends _$TwonlyDB { TwonlyDB.forTesting(DatabaseConnection super.connection); @override - int get schemaVersion => 20; + int get schemaVersion => 22; static QueryExecutor _openConnection() { final connection = driftDatabase( @@ -259,6 +259,22 @@ class TwonlyDB extends _$TwonlyDB { schema.contacts.recoverySecretShare, ); }, + from20To21: (m, schema) async { + await m.addColumn( + schema.contacts, + schema.contacts.recoveryContactsSecretShare, + ); + await m.addColumn( + schema.contacts, + schema.contacts.recoveryContactsLastHeartbeat, + ); + }, + from21To22: (m, schema) async { + await m.addColumn( + schema.contacts, + schema.contacts.recoveryContactsThreshold, + ); + }, )(m, from, to); }, ); diff --git a/lib/src/database/twonly.db.g.dart b/lib/src/database/twonly.db.g.dart index 2f206802..504d9ea0 100644 --- a/lib/src/database/twonly.db.g.dart +++ b/lib/src/database/twonly.db.g.dart @@ -252,6 +252,39 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> { type: DriftSqlType.blob, requiredDuringInsert: false, ); + static const VerificationMeta _recoveryContactsSecretShareMeta = + const VerificationMeta('recoveryContactsSecretShare'); + @override + late final GeneratedColumn recoveryContactsSecretShare = + GeneratedColumn( + 'recovery_contacts_secret_share', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + ); + static const VerificationMeta _recoveryContactsLastHeartbeatMeta = + const VerificationMeta('recoveryContactsLastHeartbeat'); + @override + late final GeneratedColumn recoveryContactsLastHeartbeat = + GeneratedColumn( + 'recovery_contacts_last_heartbeat', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const VerificationMeta _recoveryContactsThresholdMeta = + const VerificationMeta('recoveryContactsThreshold'); + @override + late final GeneratedColumn recoveryContactsThreshold = + GeneratedColumn( + 'recovery_contacts_threshold', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); static const VerificationMeta _askForFriendPromotionsMeta = const VerificationMeta('askForFriendPromotions'); @override @@ -310,6 +343,9 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> { recoveryIsTrustedFriend, recoveryLastHeartbeat, recoverySecretShare, + recoveryContactsSecretShare, + recoveryContactsLastHeartbeat, + recoveryContactsThreshold, askForFriendPromotions, mediaSendCounter, mediaReceivedCounter, @@ -475,6 +511,33 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> { ), ); } + if (data.containsKey('recovery_contacts_secret_share')) { + context.handle( + _recoveryContactsSecretShareMeta, + recoveryContactsSecretShare.isAcceptableOrUnknown( + data['recovery_contacts_secret_share']!, + _recoveryContactsSecretShareMeta, + ), + ); + } + if (data.containsKey('recovery_contacts_last_heartbeat')) { + context.handle( + _recoveryContactsLastHeartbeatMeta, + recoveryContactsLastHeartbeat.isAcceptableOrUnknown( + data['recovery_contacts_last_heartbeat']!, + _recoveryContactsLastHeartbeatMeta, + ), + ); + } + if (data.containsKey('recovery_contacts_threshold')) { + context.handle( + _recoveryContactsThresholdMeta, + recoveryContactsThreshold.isAcceptableOrUnknown( + data['recovery_contacts_threshold']!, + _recoveryContactsThresholdMeta, + ), + ); + } if (data.containsKey('ask_for_friend_promotions')) { context.handle( _askForFriendPromotionsMeta, @@ -587,6 +650,18 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> { DriftSqlType.blob, data['${effectivePrefix}recovery_secret_share'], ), + recoveryContactsSecretShare: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}recovery_contacts_secret_share'], + ), + recoveryContactsLastHeartbeat: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}recovery_contacts_last_heartbeat'], + ), + recoveryContactsThreshold: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_contacts_threshold'], + ), askForFriendPromotions: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}ask_for_friend_promotions'], @@ -628,6 +703,9 @@ class Contact extends DataClass implements Insertable { final bool recoveryIsTrustedFriend; final DateTime? recoveryLastHeartbeat; final Uint8List? recoverySecretShare; + final Uint8List? recoveryContactsSecretShare; + final DateTime? recoveryContactsLastHeartbeat; + final int? recoveryContactsThreshold; final bool? askForFriendPromotions; final int mediaSendCounter; final int mediaReceivedCounter; @@ -651,6 +729,9 @@ class Contact extends DataClass implements Insertable { required this.recoveryIsTrustedFriend, this.recoveryLastHeartbeat, this.recoverySecretShare, + this.recoveryContactsSecretShare, + this.recoveryContactsLastHeartbeat, + this.recoveryContactsThreshold, this.askForFriendPromotions, required this.mediaSendCounter, required this.mediaReceivedCounter, @@ -695,6 +776,21 @@ class Contact extends DataClass implements Insertable { if (!nullToAbsent || recoverySecretShare != null) { map['recovery_secret_share'] = Variable(recoverySecretShare); } + if (!nullToAbsent || recoveryContactsSecretShare != null) { + map['recovery_contacts_secret_share'] = Variable( + recoveryContactsSecretShare, + ); + } + if (!nullToAbsent || recoveryContactsLastHeartbeat != null) { + map['recovery_contacts_last_heartbeat'] = Variable( + recoveryContactsLastHeartbeat, + ); + } + if (!nullToAbsent || recoveryContactsThreshold != null) { + map['recovery_contacts_threshold'] = Variable( + recoveryContactsThreshold, + ); + } if (!nullToAbsent || askForFriendPromotions != null) { map['ask_for_friend_promotions'] = Variable(askForFriendPromotions); } @@ -739,6 +835,18 @@ class Contact extends DataClass implements Insertable { recoverySecretShare: recoverySecretShare == null && nullToAbsent ? const Value.absent() : Value(recoverySecretShare), + recoveryContactsSecretShare: + recoveryContactsSecretShare == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsSecretShare), + recoveryContactsLastHeartbeat: + recoveryContactsLastHeartbeat == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsLastHeartbeat), + recoveryContactsThreshold: + recoveryContactsThreshold == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsThreshold), askForFriendPromotions: askForFriendPromotions == null && nullToAbsent ? const Value.absent() : Value(askForFriendPromotions), @@ -788,6 +896,15 @@ class Contact extends DataClass implements Insertable { recoverySecretShare: serializer.fromJson( json['recoverySecretShare'], ), + recoveryContactsSecretShare: serializer.fromJson( + json['recoveryContactsSecretShare'], + ), + recoveryContactsLastHeartbeat: serializer.fromJson( + json['recoveryContactsLastHeartbeat'], + ), + recoveryContactsThreshold: serializer.fromJson( + json['recoveryContactsThreshold'], + ), askForFriendPromotions: serializer.fromJson( json['askForFriendPromotions'], ), @@ -828,6 +945,15 @@ class Contact extends DataClass implements Insertable { recoveryLastHeartbeat, ), 'recoverySecretShare': serializer.toJson(recoverySecretShare), + 'recoveryContactsSecretShare': serializer.toJson( + recoveryContactsSecretShare, + ), + 'recoveryContactsLastHeartbeat': serializer.toJson( + recoveryContactsLastHeartbeat, + ), + 'recoveryContactsThreshold': serializer.toJson( + recoveryContactsThreshold, + ), 'askForFriendPromotions': serializer.toJson( askForFriendPromotions, ), @@ -856,6 +982,9 @@ class Contact extends DataClass implements Insertable { bool? recoveryIsTrustedFriend, Value recoveryLastHeartbeat = const Value.absent(), Value recoverySecretShare = const Value.absent(), + Value recoveryContactsSecretShare = const Value.absent(), + Value recoveryContactsLastHeartbeat = const Value.absent(), + Value recoveryContactsThreshold = const Value.absent(), Value askForFriendPromotions = const Value.absent(), int? mediaSendCounter, int? mediaReceivedCounter, @@ -890,6 +1019,15 @@ class Contact extends DataClass implements Insertable { recoverySecretShare: recoverySecretShare.present ? recoverySecretShare.value : this.recoverySecretShare, + recoveryContactsSecretShare: recoveryContactsSecretShare.present + ? recoveryContactsSecretShare.value + : this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat.present + ? recoveryContactsLastHeartbeat.value + : this.recoveryContactsLastHeartbeat, + recoveryContactsThreshold: recoveryContactsThreshold.present + ? recoveryContactsThreshold.value + : this.recoveryContactsThreshold, askForFriendPromotions: askForFriendPromotions.present ? askForFriendPromotions.value : this.askForFriendPromotions, @@ -939,6 +1077,15 @@ class Contact extends DataClass implements Insertable { recoverySecretShare: data.recoverySecretShare.present ? data.recoverySecretShare.value : this.recoverySecretShare, + recoveryContactsSecretShare: data.recoveryContactsSecretShare.present + ? data.recoveryContactsSecretShare.value + : this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: data.recoveryContactsLastHeartbeat.present + ? data.recoveryContactsLastHeartbeat.value + : this.recoveryContactsLastHeartbeat, + recoveryContactsThreshold: data.recoveryContactsThreshold.present + ? data.recoveryContactsThreshold.value + : this.recoveryContactsThreshold, askForFriendPromotions: data.askForFriendPromotions.present ? data.askForFriendPromotions.value : this.askForFriendPromotions, @@ -973,6 +1120,11 @@ class Contact extends DataClass implements Insertable { ..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ') ..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ') ..write('recoverySecretShare: $recoverySecretShare, ') + ..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ') + ..write( + 'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ', + ) + ..write('recoveryContactsThreshold: $recoveryContactsThreshold, ') ..write('askForFriendPromotions: $askForFriendPromotions, ') ..write('mediaSendCounter: $mediaSendCounter, ') ..write('mediaReceivedCounter: $mediaReceivedCounter') @@ -1001,6 +1153,9 @@ class Contact extends DataClass implements Insertable { recoveryIsTrustedFriend, recoveryLastHeartbeat, $driftBlobEquality.hash(recoverySecretShare), + $driftBlobEquality.hash(recoveryContactsSecretShare), + recoveryContactsLastHeartbeat, + recoveryContactsThreshold, askForFriendPromotions, mediaSendCounter, mediaReceivedCounter, @@ -1038,6 +1193,13 @@ class Contact extends DataClass implements Insertable { other.recoverySecretShare, this.recoverySecretShare, ) && + $driftBlobEquality.equals( + other.recoveryContactsSecretShare, + this.recoveryContactsSecretShare, + ) && + other.recoveryContactsLastHeartbeat == + this.recoveryContactsLastHeartbeat && + other.recoveryContactsThreshold == this.recoveryContactsThreshold && other.askForFriendPromotions == this.askForFriendPromotions && other.mediaSendCounter == this.mediaSendCounter && other.mediaReceivedCounter == this.mediaReceivedCounter); @@ -1063,6 +1225,9 @@ class ContactsCompanion extends UpdateCompanion { final Value recoveryIsTrustedFriend; final Value recoveryLastHeartbeat; final Value recoverySecretShare; + final Value recoveryContactsSecretShare; + final Value recoveryContactsLastHeartbeat; + final Value recoveryContactsThreshold; final Value askForFriendPromotions; final Value mediaSendCounter; final Value mediaReceivedCounter; @@ -1086,6 +1251,9 @@ class ContactsCompanion extends UpdateCompanion { this.recoveryIsTrustedFriend = const Value.absent(), this.recoveryLastHeartbeat = const Value.absent(), this.recoverySecretShare = const Value.absent(), + this.recoveryContactsSecretShare = const Value.absent(), + this.recoveryContactsLastHeartbeat = const Value.absent(), + this.recoveryContactsThreshold = const Value.absent(), this.askForFriendPromotions = const Value.absent(), this.mediaSendCounter = const Value.absent(), this.mediaReceivedCounter = const Value.absent(), @@ -1110,6 +1278,9 @@ class ContactsCompanion extends UpdateCompanion { this.recoveryIsTrustedFriend = const Value.absent(), this.recoveryLastHeartbeat = const Value.absent(), this.recoverySecretShare = const Value.absent(), + this.recoveryContactsSecretShare = const Value.absent(), + this.recoveryContactsLastHeartbeat = const Value.absent(), + this.recoveryContactsThreshold = const Value.absent(), this.askForFriendPromotions = const Value.absent(), this.mediaSendCounter = const Value.absent(), this.mediaReceivedCounter = const Value.absent(), @@ -1134,6 +1305,9 @@ class ContactsCompanion extends UpdateCompanion { Expression? recoveryIsTrustedFriend, Expression? recoveryLastHeartbeat, Expression? recoverySecretShare, + Expression? recoveryContactsSecretShare, + Expression? recoveryContactsLastHeartbeat, + Expression? recoveryContactsThreshold, Expression? askForFriendPromotions, Expression? mediaSendCounter, Expression? mediaReceivedCounter, @@ -1166,6 +1340,12 @@ class ContactsCompanion extends UpdateCompanion { 'recovery_last_heartbeat': recoveryLastHeartbeat, if (recoverySecretShare != null) 'recovery_secret_share': recoverySecretShare, + if (recoveryContactsSecretShare != null) + 'recovery_contacts_secret_share': recoveryContactsSecretShare, + if (recoveryContactsLastHeartbeat != null) + 'recovery_contacts_last_heartbeat': recoveryContactsLastHeartbeat, + if (recoveryContactsThreshold != null) + 'recovery_contacts_threshold': recoveryContactsThreshold, if (askForFriendPromotions != null) 'ask_for_friend_promotions': askForFriendPromotions, if (mediaSendCounter != null) 'media_send_counter': mediaSendCounter, @@ -1194,6 +1374,9 @@ class ContactsCompanion extends UpdateCompanion { Value? recoveryIsTrustedFriend, Value? recoveryLastHeartbeat, Value? recoverySecretShare, + Value? recoveryContactsSecretShare, + Value? recoveryContactsLastHeartbeat, + Value? recoveryContactsThreshold, Value? askForFriendPromotions, Value? mediaSendCounter, Value? mediaReceivedCounter, @@ -1222,6 +1405,12 @@ class ContactsCompanion extends UpdateCompanion { recoveryLastHeartbeat: recoveryLastHeartbeat ?? this.recoveryLastHeartbeat, recoverySecretShare: recoverySecretShare ?? this.recoverySecretShare, + recoveryContactsSecretShare: + recoveryContactsSecretShare ?? this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: + recoveryContactsLastHeartbeat ?? this.recoveryContactsLastHeartbeat, + recoveryContactsThreshold: + recoveryContactsThreshold ?? this.recoveryContactsThreshold, askForFriendPromotions: askForFriendPromotions ?? this.askForFriendPromotions, mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter, @@ -1303,6 +1492,21 @@ class ContactsCompanion extends UpdateCompanion { recoverySecretShare.value, ); } + if (recoveryContactsSecretShare.present) { + map['recovery_contacts_secret_share'] = Variable( + recoveryContactsSecretShare.value, + ); + } + if (recoveryContactsLastHeartbeat.present) { + map['recovery_contacts_last_heartbeat'] = Variable( + recoveryContactsLastHeartbeat.value, + ); + } + if (recoveryContactsThreshold.present) { + map['recovery_contacts_threshold'] = Variable( + recoveryContactsThreshold.value, + ); + } if (askForFriendPromotions.present) { map['ask_for_friend_promotions'] = Variable( askForFriendPromotions.value, @@ -1339,6 +1543,11 @@ class ContactsCompanion extends UpdateCompanion { ..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ') ..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ') ..write('recoverySecretShare: $recoverySecretShare, ') + ..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ') + ..write( + 'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ', + ) + ..write('recoveryContactsThreshold: $recoveryContactsThreshold, ') ..write('askForFriendPromotions: $askForFriendPromotions, ') ..write('mediaSendCounter: $mediaSendCounter, ') ..write('mediaReceivedCounter: $mediaReceivedCounter') @@ -13121,6 +13330,9 @@ typedef $$ContactsTableCreateCompanionBuilder = Value recoveryIsTrustedFriend, Value recoveryLastHeartbeat, Value recoverySecretShare, + Value recoveryContactsSecretShare, + Value recoveryContactsLastHeartbeat, + Value recoveryContactsThreshold, Value askForFriendPromotions, Value mediaSendCounter, Value mediaReceivedCounter, @@ -13146,6 +13358,9 @@ typedef $$ContactsTableUpdateCompanionBuilder = Value recoveryIsTrustedFriend, Value recoveryLastHeartbeat, Value recoverySecretShare, + Value recoveryContactsSecretShare, + Value recoveryContactsLastHeartbeat, + Value recoveryContactsThreshold, Value askForFriendPromotions, Value mediaSendCounter, Value mediaReceivedCounter, @@ -13525,6 +13740,23 @@ class $$ContactsTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get recoveryContactsSecretShare => + $composableBuilder( + column: $table.recoveryContactsSecretShare, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get recoveryContactsLastHeartbeat => + $composableBuilder( + column: $table.recoveryContactsLastHeartbeat, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get recoveryContactsThreshold => $composableBuilder( + column: $table.recoveryContactsThreshold, + builder: (column) => ColumnFilters(column), + ); + ColumnFilters get askForFriendPromotions => $composableBuilder( column: $table.askForFriendPromotions, builder: (column) => ColumnFilters(column), @@ -13928,6 +14160,23 @@ class $$ContactsTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get recoveryContactsSecretShare => + $composableBuilder( + column: $table.recoveryContactsSecretShare, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get recoveryContactsLastHeartbeat => + $composableBuilder( + column: $table.recoveryContactsLastHeartbeat, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get recoveryContactsThreshold => $composableBuilder( + column: $table.recoveryContactsThreshold, + builder: (column) => ColumnOrderings(column), + ); + ColumnOrderings get askForFriendPromotions => $composableBuilder( column: $table.askForFriendPromotions, builder: (column) => ColumnOrderings(column), @@ -14032,6 +14281,23 @@ class $$ContactsTableAnnotationComposer builder: (column) => column, ); + GeneratedColumn get recoveryContactsSecretShare => + $composableBuilder( + column: $table.recoveryContactsSecretShare, + builder: (column) => column, + ); + + GeneratedColumn get recoveryContactsLastHeartbeat => + $composableBuilder( + column: $table.recoveryContactsLastHeartbeat, + builder: (column) => column, + ); + + GeneratedColumn get recoveryContactsThreshold => $composableBuilder( + column: $table.recoveryContactsThreshold, + builder: (column) => column, + ); + GeneratedColumn get askForFriendPromotions => $composableBuilder( column: $table.askForFriendPromotions, builder: (column) => column, @@ -14395,6 +14661,11 @@ class $$ContactsTableTableManager Value recoveryIsTrustedFriend = const Value.absent(), Value recoveryLastHeartbeat = const Value.absent(), Value recoverySecretShare = const Value.absent(), + Value recoveryContactsSecretShare = + const Value.absent(), + Value recoveryContactsLastHeartbeat = + const Value.absent(), + Value recoveryContactsThreshold = const Value.absent(), Value askForFriendPromotions = const Value.absent(), Value mediaSendCounter = const Value.absent(), Value mediaReceivedCounter = const Value.absent(), @@ -14418,6 +14689,9 @@ class $$ContactsTableTableManager recoveryIsTrustedFriend: recoveryIsTrustedFriend, recoveryLastHeartbeat: recoveryLastHeartbeat, recoverySecretShare: recoverySecretShare, + recoveryContactsSecretShare: recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat, + recoveryContactsThreshold: recoveryContactsThreshold, askForFriendPromotions: askForFriendPromotions, mediaSendCounter: mediaSendCounter, mediaReceivedCounter: mediaReceivedCounter, @@ -14443,6 +14717,11 @@ class $$ContactsTableTableManager Value recoveryIsTrustedFriend = const Value.absent(), Value recoveryLastHeartbeat = const Value.absent(), Value recoverySecretShare = const Value.absent(), + Value recoveryContactsSecretShare = + const Value.absent(), + Value recoveryContactsLastHeartbeat = + const Value.absent(), + Value recoveryContactsThreshold = const Value.absent(), Value askForFriendPromotions = const Value.absent(), Value mediaSendCounter = const Value.absent(), Value mediaReceivedCounter = const Value.absent(), @@ -14466,6 +14745,9 @@ class $$ContactsTableTableManager recoveryIsTrustedFriend: recoveryIsTrustedFriend, recoveryLastHeartbeat: recoveryLastHeartbeat, recoverySecretShare: recoverySecretShare, + recoveryContactsSecretShare: recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat, + recoveryContactsThreshold: recoveryContactsThreshold, askForFriendPromotions: askForFriendPromotions, mediaSendCounter: mediaSendCounter, mediaReceivedCounter: mediaReceivedCounter, diff --git a/lib/src/database/twonly.db.steps.dart b/lib/src/database/twonly.db.steps.dart index 79b53547..daf37fed 100644 --- a/lib/src/database/twonly.db.steps.dart +++ b/lib/src/database/twonly.db.steps.dart @@ -10537,6 +10537,1069 @@ i1.GeneratedColumn _column_251(String aliasedName) => type: i1.DriftSqlType.blob, $customConstraints: 'NULL', ); + +final class Schema21 extends i0.VersionedSchema { + Schema21({required super.database}) : super(version: 21); + @override + late final List entities = [ + contacts, + groups, + mediaFiles, + messages, + messageHistories, + reactions, + groupMembers, + receipts, + receivedReceipts, + signalIdentityKeyStores, + signalPreKeyStores, + signalSenderKeyStores, + signalSessionStores, + signalSignedPreKeyStores, + messageActions, + groupHistories, + keyVerifications, + verificationTokens, + userDiscoveryAnnouncedUsers, + userDiscoveryUserRelations, + userDiscoveryOtherPromotions, + userDiscoveryOwnPromotions, + userDiscoveryShares, + shortcuts, + shortcutMembers, + ]; + late final Shape56 contacts = Shape56( + source: i0.VersionedTable( + entityName: 'contacts', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(user_id)'], + columns: [ + _column_106, + _column_107, + _column_108, + _column_109, + _column_110, + _column_111, + _column_112, + _column_113, + _column_114, + _column_115, + _column_116, + _column_117, + _column_118, + _column_211, + _column_212, + _column_213, + _column_249, + _column_250, + _column_251, + _column_252, + _column_253, + _column_247, + _column_214, + _column_215, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape23 groups = Shape23( + source: i0.VersionedTable( + entityName: 'groups', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(group_id)'], + columns: [ + _column_119, + _column_120, + _column_121, + _column_122, + _column_123, + _column_124, + _column_125, + _column_126, + _column_127, + _column_128, + _column_129, + _column_130, + _column_131, + _column_132, + _column_133, + _column_134, + _column_118, + _column_135, + _column_136, + _column_137, + _column_138, + _column_139, + _column_140, + _column_141, + _column_142, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape51 mediaFiles = Shape51( + source: i0.VersionedTable( + entityName: 'media_files', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(media_id)'], + columns: [ + _column_143, + _column_144, + _column_145, + _column_146, + _column_147, + _column_148, + _column_149, + _column_239, + _column_240, + _column_207, + _column_150, + _column_151, + _column_152, + _column_153, + _column_154, + _column_155, + _column_156, + _column_157, + _column_244, + _column_245, + _column_118, + _column_241, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape25 messages = Shape25( + source: i0.VersionedTable( + entityName: 'messages', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(message_id)'], + columns: [ + _column_158, + _column_159, + _column_160, + _column_144, + _column_161, + _column_162, + _column_163, + _column_164, + _column_165, + _column_153, + _column_166, + _column_167, + _column_168, + _column_169, + _column_118, + _column_170, + _column_171, + _column_172, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape26 messageHistories = Shape26( + source: i0.VersionedTable( + entityName: 'message_histories', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_173, + _column_174, + _column_175, + _column_161, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape27 reactions = Shape27( + source: i0.VersionedTable( + entityName: 'reactions', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(message_id, sender_id, emoji)'], + columns: [_column_174, _column_176, _column_177, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape38 groupMembers = Shape38( + source: i0.VersionedTable( + entityName: 'group_members', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(group_id, contact_id)'], + columns: [ + _column_158, + _column_178, + _column_179, + _column_180, + _column_209, + _column_210, + _column_181, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape37 receipts = Shape37( + source: i0.VersionedTable( + entityName: 'receipts', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(receipt_id)'], + columns: [ + _column_182, + _column_183, + _column_184, + _column_185, + _column_186, + _column_208, + _column_187, + _column_188, + _column_189, + _column_190, + _column_191, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape30 receivedReceipts = Shape30( + source: i0.VersionedTable( + entityName: 'received_receipts', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(receipt_id)'], + columns: [_column_182, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape31 signalIdentityKeyStores = Shape31( + source: i0.VersionedTable( + entityName: 'signal_identity_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(device_id, name)'], + columns: [_column_192, _column_193, _column_194, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape32 signalPreKeyStores = Shape32( + source: i0.VersionedTable( + entityName: 'signal_pre_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(pre_key_id)'], + columns: [_column_195, _column_196, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 signalSenderKeyStores = Shape11( + source: i0.VersionedTable( + entityName: 'signal_sender_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(sender_key_name)'], + columns: [_column_197, _column_198], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape33 signalSessionStores = Shape33( + source: i0.VersionedTable( + entityName: 'signal_session_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(device_id, name)'], + columns: [_column_192, _column_193, _column_199, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape50 signalSignedPreKeyStores = Shape50( + source: i0.VersionedTable( + entityName: 'signal_signed_pre_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(signed_pre_key_id)'], + columns: [_column_242, _column_243, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape34 messageActions = Shape34( + source: i0.VersionedTable( + entityName: 'message_actions', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(message_id, contact_id, type)'], + columns: [_column_174, _column_183, _column_144, _column_200], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape35 groupHistories = Shape35( + source: i0.VersionedTable( + entityName: 'group_histories', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(group_history_id)'], + columns: [ + _column_201, + _column_158, + _column_202, + _column_203, + _column_204, + _column_205, + _column_206, + _column_144, + _column_200, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape54 keyVerifications = Shape54( + source: i0.VersionedTable( + entityName: 'key_verifications', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_216, + _column_183, + _column_144, + _column_248, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape41 verificationTokens = Shape41( + source: i0.VersionedTable( + entityName: 'verification_tokens', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_217, _column_218, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape52 userDiscoveryAnnouncedUsers = Shape52( + source: i0.VersionedTable( + entityName: 'user_discovery_announced_users', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(announced_user_id)'], + columns: [ + _column_219, + _column_220, + _column_221, + _column_222, + _column_223, + _column_224, + _column_246, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape43 userDiscoveryUserRelations = Shape43( + source: i0.VersionedTable( + entityName: 'user_discovery_user_relations', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(announced_user_id, from_contact_id)'], + columns: [_column_225, _column_226, _column_227], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape44 userDiscoveryOtherPromotions = Shape44( + source: i0.VersionedTable( + entityName: 'user_discovery_other_promotions', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(from_contact_id, public_id)'], + columns: [ + _column_226, + _column_228, + _column_229, + _column_230, + _column_231, + _column_227, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape45 userDiscoveryOwnPromotions = Shape45( + source: i0.VersionedTable( + entityName: 'user_discovery_own_promotions', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_232, _column_183, _column_233], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape46 userDiscoveryShares = Shape46( + source: i0.VersionedTable( + entityName: 'user_discovery_shares', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_234, _column_235, _column_175], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape47 shortcuts = Shape47( + source: i0.VersionedTable( + entityName: 'shortcuts', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_173, _column_236, _column_237], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape48 shortcutMembers = Shape48( + source: i0.VersionedTable( + entityName: 'shortcut_members', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(shortcut_id, group_id)'], + columns: [_column_238, _column_158], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape56 extends i0.VersionedTable { + Shape56({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get userId => + columnsByName['user_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get username => + columnsByName['username']! as i1.GeneratedColumn; + i1.GeneratedColumn get displayName => + columnsByName['display_name']! as i1.GeneratedColumn; + i1.GeneratedColumn get nickName => + columnsByName['nick_name']! as i1.GeneratedColumn; + i1.GeneratedColumn get avatarSvgCompressed => + columnsByName['avatar_svg_compressed']! + as i1.GeneratedColumn; + i1.GeneratedColumn get senderProfileCounter => + columnsByName['sender_profile_counter']! as i1.GeneratedColumn; + i1.GeneratedColumn get accepted => + columnsByName['accepted']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedByUser => + columnsByName['deleted_by_user']! as i1.GeneratedColumn; + i1.GeneratedColumn get requested => + columnsByName['requested']! as i1.GeneratedColumn; + i1.GeneratedColumn get blocked => + columnsByName['blocked']! as i1.GeneratedColumn; + i1.GeneratedColumn get verified => + columnsByName['verified']! as i1.GeneratedColumn; + i1.GeneratedColumn get accountDeleted => + columnsByName['account_deleted']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get userDiscoveryVersion => + columnsByName['user_discovery_version']! + as i1.GeneratedColumn; + i1.GeneratedColumn get userDiscoveryExcluded => + columnsByName['user_discovery_excluded']! as i1.GeneratedColumn; + i1.GeneratedColumn get userDiscoveryManualApproved => + columnsByName['user_discovery_manual_approved']! + as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryIsTrustedFriend => + columnsByName['recovery_is_trusted_friend']! as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryLastHeartbeat => + columnsByName['recovery_last_heartbeat']! as i1.GeneratedColumn; + i1.GeneratedColumn get recoverySecretShare => + columnsByName['recovery_secret_share']! + as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryContactsSecretShare => + columnsByName['recovery_contacts_secret_share']! + as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryContactsLastHeartbeat => + columnsByName['recovery_contacts_last_heartbeat']! + as i1.GeneratedColumn; + i1.GeneratedColumn get askForFriendPromotions => + columnsByName['ask_for_friend_promotions']! as i1.GeneratedColumn; + i1.GeneratedColumn get mediaSendCounter => + columnsByName['media_send_counter']! as i1.GeneratedColumn; + i1.GeneratedColumn get mediaReceivedCounter => + columnsByName['media_received_counter']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_252(String aliasedName) => + i1.GeneratedColumn( + 'recovery_contacts_secret_share', + aliasedName, + true, + type: i1.DriftSqlType.blob, + $customConstraints: 'NULL', + ); +i1.GeneratedColumn _column_253(String aliasedName) => + i1.GeneratedColumn( + 'recovery_contacts_last_heartbeat', + aliasedName, + true, + type: i1.DriftSqlType.int, + $customConstraints: 'NULL', + ); + +final class Schema22 extends i0.VersionedSchema { + Schema22({required super.database}) : super(version: 22); + @override + late final List entities = [ + contacts, + groups, + mediaFiles, + messages, + messageHistories, + reactions, + groupMembers, + receipts, + receivedReceipts, + signalIdentityKeyStores, + signalPreKeyStores, + signalSenderKeyStores, + signalSessionStores, + signalSignedPreKeyStores, + messageActions, + groupHistories, + keyVerifications, + verificationTokens, + userDiscoveryAnnouncedUsers, + userDiscoveryUserRelations, + userDiscoveryOtherPromotions, + userDiscoveryOwnPromotions, + userDiscoveryShares, + shortcuts, + shortcutMembers, + ]; + late final Shape57 contacts = Shape57( + source: i0.VersionedTable( + entityName: 'contacts', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(user_id)'], + columns: [ + _column_106, + _column_107, + _column_108, + _column_109, + _column_110, + _column_111, + _column_112, + _column_113, + _column_114, + _column_115, + _column_116, + _column_117, + _column_118, + _column_211, + _column_212, + _column_213, + _column_249, + _column_250, + _column_251, + _column_252, + _column_253, + _column_254, + _column_247, + _column_214, + _column_215, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape23 groups = Shape23( + source: i0.VersionedTable( + entityName: 'groups', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(group_id)'], + columns: [ + _column_119, + _column_120, + _column_121, + _column_122, + _column_123, + _column_124, + _column_125, + _column_126, + _column_127, + _column_128, + _column_129, + _column_130, + _column_131, + _column_132, + _column_133, + _column_134, + _column_118, + _column_135, + _column_136, + _column_137, + _column_138, + _column_139, + _column_140, + _column_141, + _column_142, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape51 mediaFiles = Shape51( + source: i0.VersionedTable( + entityName: 'media_files', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(media_id)'], + columns: [ + _column_143, + _column_144, + _column_145, + _column_146, + _column_147, + _column_148, + _column_149, + _column_239, + _column_240, + _column_207, + _column_150, + _column_151, + _column_152, + _column_153, + _column_154, + _column_155, + _column_156, + _column_157, + _column_244, + _column_245, + _column_118, + _column_241, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape25 messages = Shape25( + source: i0.VersionedTable( + entityName: 'messages', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(message_id)'], + columns: [ + _column_158, + _column_159, + _column_160, + _column_144, + _column_161, + _column_162, + _column_163, + _column_164, + _column_165, + _column_153, + _column_166, + _column_167, + _column_168, + _column_169, + _column_118, + _column_170, + _column_171, + _column_172, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape26 messageHistories = Shape26( + source: i0.VersionedTable( + entityName: 'message_histories', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_173, + _column_174, + _column_175, + _column_161, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape27 reactions = Shape27( + source: i0.VersionedTable( + entityName: 'reactions', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(message_id, sender_id, emoji)'], + columns: [_column_174, _column_176, _column_177, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape38 groupMembers = Shape38( + source: i0.VersionedTable( + entityName: 'group_members', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(group_id, contact_id)'], + columns: [ + _column_158, + _column_178, + _column_179, + _column_180, + _column_209, + _column_210, + _column_181, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape37 receipts = Shape37( + source: i0.VersionedTable( + entityName: 'receipts', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(receipt_id)'], + columns: [ + _column_182, + _column_183, + _column_184, + _column_185, + _column_186, + _column_208, + _column_187, + _column_188, + _column_189, + _column_190, + _column_191, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape30 receivedReceipts = Shape30( + source: i0.VersionedTable( + entityName: 'received_receipts', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(receipt_id)'], + columns: [_column_182, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape31 signalIdentityKeyStores = Shape31( + source: i0.VersionedTable( + entityName: 'signal_identity_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(device_id, name)'], + columns: [_column_192, _column_193, _column_194, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape32 signalPreKeyStores = Shape32( + source: i0.VersionedTable( + entityName: 'signal_pre_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(pre_key_id)'], + columns: [_column_195, _column_196, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 signalSenderKeyStores = Shape11( + source: i0.VersionedTable( + entityName: 'signal_sender_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(sender_key_name)'], + columns: [_column_197, _column_198], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape33 signalSessionStores = Shape33( + source: i0.VersionedTable( + entityName: 'signal_session_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(device_id, name)'], + columns: [_column_192, _column_193, _column_199, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape50 signalSignedPreKeyStores = Shape50( + source: i0.VersionedTable( + entityName: 'signal_signed_pre_key_stores', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(signed_pre_key_id)'], + columns: [_column_242, _column_243, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape34 messageActions = Shape34( + source: i0.VersionedTable( + entityName: 'message_actions', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(message_id, contact_id, type)'], + columns: [_column_174, _column_183, _column_144, _column_200], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape35 groupHistories = Shape35( + source: i0.VersionedTable( + entityName: 'group_histories', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(group_history_id)'], + columns: [ + _column_201, + _column_158, + _column_202, + _column_203, + _column_204, + _column_205, + _column_206, + _column_144, + _column_200, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape54 keyVerifications = Shape54( + source: i0.VersionedTable( + entityName: 'key_verifications', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_216, + _column_183, + _column_144, + _column_248, + _column_118, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape41 verificationTokens = Shape41( + source: i0.VersionedTable( + entityName: 'verification_tokens', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_217, _column_218, _column_118], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape52 userDiscoveryAnnouncedUsers = Shape52( + source: i0.VersionedTable( + entityName: 'user_discovery_announced_users', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(announced_user_id)'], + columns: [ + _column_219, + _column_220, + _column_221, + _column_222, + _column_223, + _column_224, + _column_246, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape43 userDiscoveryUserRelations = Shape43( + source: i0.VersionedTable( + entityName: 'user_discovery_user_relations', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(announced_user_id, from_contact_id)'], + columns: [_column_225, _column_226, _column_227], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape44 userDiscoveryOtherPromotions = Shape44( + source: i0.VersionedTable( + entityName: 'user_discovery_other_promotions', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(from_contact_id, public_id)'], + columns: [ + _column_226, + _column_228, + _column_229, + _column_230, + _column_231, + _column_227, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape45 userDiscoveryOwnPromotions = Shape45( + source: i0.VersionedTable( + entityName: 'user_discovery_own_promotions', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_232, _column_183, _column_233], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape46 userDiscoveryShares = Shape46( + source: i0.VersionedTable( + entityName: 'user_discovery_shares', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_234, _column_235, _column_175], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape47 shortcuts = Shape47( + source: i0.VersionedTable( + entityName: 'shortcuts', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_173, _column_236, _column_237], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape48 shortcutMembers = Shape48( + source: i0.VersionedTable( + entityName: 'shortcut_members', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(shortcut_id, group_id)'], + columns: [_column_238, _column_158], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape57 extends i0.VersionedTable { + Shape57({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get userId => + columnsByName['user_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get username => + columnsByName['username']! as i1.GeneratedColumn; + i1.GeneratedColumn get displayName => + columnsByName['display_name']! as i1.GeneratedColumn; + i1.GeneratedColumn get nickName => + columnsByName['nick_name']! as i1.GeneratedColumn; + i1.GeneratedColumn get avatarSvgCompressed => + columnsByName['avatar_svg_compressed']! + as i1.GeneratedColumn; + i1.GeneratedColumn get senderProfileCounter => + columnsByName['sender_profile_counter']! as i1.GeneratedColumn; + i1.GeneratedColumn get accepted => + columnsByName['accepted']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedByUser => + columnsByName['deleted_by_user']! as i1.GeneratedColumn; + i1.GeneratedColumn get requested => + columnsByName['requested']! as i1.GeneratedColumn; + i1.GeneratedColumn get blocked => + columnsByName['blocked']! as i1.GeneratedColumn; + i1.GeneratedColumn get verified => + columnsByName['verified']! as i1.GeneratedColumn; + i1.GeneratedColumn get accountDeleted => + columnsByName['account_deleted']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get userDiscoveryVersion => + columnsByName['user_discovery_version']! + as i1.GeneratedColumn; + i1.GeneratedColumn get userDiscoveryExcluded => + columnsByName['user_discovery_excluded']! as i1.GeneratedColumn; + i1.GeneratedColumn get userDiscoveryManualApproved => + columnsByName['user_discovery_manual_approved']! + as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryIsTrustedFriend => + columnsByName['recovery_is_trusted_friend']! as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryLastHeartbeat => + columnsByName['recovery_last_heartbeat']! as i1.GeneratedColumn; + i1.GeneratedColumn get recoverySecretShare => + columnsByName['recovery_secret_share']! + as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryContactsSecretShare => + columnsByName['recovery_contacts_secret_share']! + as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryContactsLastHeartbeat => + columnsByName['recovery_contacts_last_heartbeat']! + as i1.GeneratedColumn; + i1.GeneratedColumn get recoveryContactsThreshold => + columnsByName['recovery_contacts_threshold']! as i1.GeneratedColumn; + i1.GeneratedColumn get askForFriendPromotions => + columnsByName['ask_for_friend_promotions']! as i1.GeneratedColumn; + i1.GeneratedColumn get mediaSendCounter => + columnsByName['media_send_counter']! as i1.GeneratedColumn; + i1.GeneratedColumn get mediaReceivedCounter => + columnsByName['media_received_counter']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_254(String aliasedName) => + i1.GeneratedColumn( + 'recovery_contacts_threshold', + aliasedName, + true, + type: i1.DriftSqlType.int, + $customConstraints: 'NULL', + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, @@ -10557,6 +11620,8 @@ i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema18 schema) from17To18, required Future Function(i1.Migrator m, Schema19 schema) from18To19, required Future Function(i1.Migrator m, Schema20 schema) from19To20, + required Future Function(i1.Migrator m, Schema21 schema) from20To21, + required Future Function(i1.Migrator m, Schema22 schema) from21To22, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -10655,6 +11720,16 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from19To20(migrator, schema); return 20; + case 20: + final schema = Schema21(database: database); + final migrator = i1.Migrator(database, schema); + await from20To21(migrator, schema); + return 21; + case 21: + final schema = Schema22(database: database); + final migrator = i1.Migrator(database, schema); + await from21To22(migrator, schema); + return 22; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -10681,6 +11756,8 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema18 schema) from17To18, required Future Function(i1.Migrator m, Schema19 schema) from18To19, required Future Function(i1.Migrator m, Schema20 schema) from19To20, + required Future Function(i1.Migrator m, Schema21 schema) from20To21, + required Future Function(i1.Migrator m, Schema22 schema) from21To22, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, @@ -10702,5 +11779,7 @@ i1.OnUpgrade stepByStep({ from17To18: from17To18, from18To19: from18To19, from19To20: from19To20, + from20To21: from20To21, + from21To22: from21To22, ), ); diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart index efc85060..67645aec 100644 --- a/lib/src/localization/generated/app_localizations.dart +++ b/lib/src/localization/generated/app_localizations.dart @@ -149,7 +149,7 @@ abstract class AppLocalizations { /// No description provided for @onboardingNotProductBody. /// /// In en, this message translates to: - /// **'twonly is financed by donations and an optional subscription. Your data will never be sold.'** + /// **'twonly is financed by an optional subscription. Your data will never be sold.'** String get onboardingNotProductBody; /// No description provided for @registerUsernameSlogan. @@ -590,48 +590,6 @@ abstract class AppLocalizations { /// **'{len} contact(s)'** String settingsPrivacyBlockUsersCount(Object len); - /// No description provided for @settingsPrivacyProfileSelectionTitle. - /// - /// In en, this message translates to: - /// **'Security Profile'** - String get settingsPrivacyProfileSelectionTitle; - - /// No description provided for @securityProfileTitle. - /// - /// In en, this message translates to: - /// **'Security Profile'** - String get securityProfileTitle; - - /// No description provided for @securityProfileSubtitle. - /// - /// In en, this message translates to: - /// **'Choose the level of protection that fits your daily use. This can be changed at any time in your settings.'** - String get securityProfileSubtitle; - - /// No description provided for @securityProfileNormalTitle. - /// - /// In en, this message translates to: - /// **'Normal Protection'** - String get securityProfileNormalTitle; - - /// No description provided for @securityProfileNormalDesc. - /// - /// In en, this message translates to: - /// **'Good balance between a convenient mode without bothering you too much.'** - String get securityProfileNormalDesc; - - /// No description provided for @securityProfileStrictTitle. - /// - /// In en, this message translates to: - /// **'Strict Protection'** - String get securityProfileStrictTitle; - - /// No description provided for @securityProfileStrictDesc. - /// - /// In en, this message translates to: - /// **'Maximum anti-phishing protection but may be inconvenient.'** - String get securityProfileStrictDesc; - /// No description provided for @settingsNotification. /// /// In en, this message translates to: @@ -896,18 +854,48 @@ abstract class AppLocalizations { /// **'Verify contacts'** String get contactVerifyNumberTitle; + /// No description provided for @verifyUserIdentity. + /// + /// In en, this message translates to: + /// **'Verify {username}\'s identity'** + String verifyUserIdentity(Object username); + /// No description provided for @contactVerifyNumberSubtitle. /// /// In en, this message translates to: /// **'Verify the identity of your contacts to make sure you are texting the right person.'** String get contactVerifyNumberSubtitle; + /// No description provided for @inChatContactNotVerified. + /// + /// In en, this message translates to: + /// **'Contact not verified.'** + String get inChatContactNotVerified; + + /// No description provided for @groupMembersNotVerified. + /// + /// In en, this message translates to: + /// **'{count} members are not verified.'** + String groupMembersNotVerified(Object count); + /// No description provided for @userVerifiedTitle. /// /// In en, this message translates to: /// **'Contact verified'** String get userVerifiedTitle; + /// No description provided for @scanUserQrCode. + /// + /// In en, this message translates to: + /// **'Scan {username}\'s QR code'** + String scanUserQrCode(Object username); + + /// No description provided for @openOwnQrCode. + /// + /// In en, this message translates to: + /// **'Open your own QR code'** + String get openOwnQrCode; + /// No description provided for @contactVerifiedBy. /// /// In en, this message translates to: @@ -1604,6 +1592,18 @@ abstract class AppLocalizations { /// **'Change password'** String get backupChangePassword; + /// No description provided for @backupChangePasswordAuthReason. + /// + /// In en, this message translates to: + /// **'Changing backup password'** + String get backupChangePasswordAuthReason; + + /// No description provided for @backupChangePasswordAuthFailed. + /// + /// In en, this message translates to: + /// **'You can only change your password after you have authenticated!'** + String get backupChangePasswordAuthFailed; + /// No description provided for @twonlySafeRecoverTitle. /// /// In en, this message translates to: @@ -2372,6 +2372,12 @@ abstract class AppLocalizations { /// **'Let a friend scan this QR code to add you'** String get addContactQrSheetSubtext; + /// No description provided for @letUserScanQrCode. + /// + /// In en, this message translates to: + /// **'Let {username} scan this QR code'** + String letUserScanQrCode(Object username); + /// No description provided for @finishSetupCardTitle. /// /// In en, this message translates to: @@ -2729,7 +2735,7 @@ abstract class AppLocalizations { /// No description provided for @verificationBadgeGeneralDesc. /// /// In en, this message translates to: - /// **'The badge *protects you from scammers and attackers*. It will be displayed next to a contact that has been *manually verified* by you or a friend.'** + /// **'The badge gives you the peace of mind that you are messaging the *right person* and your *messages remain confidential*.'** String get verificationBadgeGeneralDesc; /// No description provided for @verificationBadgeGreenDesc. @@ -2756,12 +2762,30 @@ abstract class AppLocalizations { /// **'Scan now'** String get scanNow; + /// No description provided for @qrScannerVerifyHint. + /// + /// In en, this message translates to: + /// **'To verify, the other person must open their QR code (Chat Lists > QR code button at the bottom right)'** + String get qrScannerVerifyHint; + + /// No description provided for @qrScannerVerifyUserHint. + /// + /// In en, this message translates to: + /// **'To verify, {username} must open their QR code (Chat Lists > QR code button at the bottom right)'** + String qrScannerVerifyUserHint(Object username); + /// No description provided for @openQrCode. /// /// In en, this message translates to: /// **'Open QR code'** String get openQrCode; + /// No description provided for @letFriendScanQrToVerify. + /// + /// In en, this message translates to: + /// **'Let a friend scan this QR code to verify you'** + String get letFriendScanQrToVerify; + /// No description provided for @deleteVerificationTitle. /// /// In en, this message translates to: @@ -3659,6 +3683,456 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Reset'** String get avatarCustomizeReset; + + /// No description provided for @passwordlessRecovery. + /// + /// In en, this message translates to: + /// **'Passwordless Recovery'** + String get passwordlessRecovery; + + /// No description provided for @passwordlessRecoveryNotConfigured. + /// + /// In en, this message translates to: + /// **'Not configured'** + String get passwordlessRecoveryNotConfigured; + + /// No description provided for @passwordlessRecoveryTestPin. + /// + /// In en, this message translates to: + /// **'Test PIN'** + String get passwordlessRecoveryTestPin; + + /// No description provided for @passwordlessRecoveryTestPinTitle. + /// + /// In en, this message translates to: + /// **'Test PIN'** + String get passwordlessRecoveryTestPinTitle; + + /// No description provided for @passwordlessRecoveryTestPinHint. + /// + /// In en, this message translates to: + /// **'Enter your PIN'** + String get passwordlessRecoveryTestPinHint; + + /// No description provided for @passwordlessRecoveryTestPinCorrect. + /// + /// In en, this message translates to: + /// **'PIN is correct!'** + String get passwordlessRecoveryTestPinCorrect; + + /// No description provided for @passwordlessRecoveryTestPinIncorrect. + /// + /// In en, this message translates to: + /// **'Incorrect PIN.'** + String get passwordlessRecoveryTestPinIncorrect; + + /// No description provided for @passwordlessRecoveryTest. + /// + /// In en, this message translates to: + /// **'Test'** + String get passwordlessRecoveryTest; + + /// No description provided for @passwordlessRecoverySecondFactorNone. + /// + /// In en, this message translates to: + /// **'None'** + String get passwordlessRecoverySecondFactorNone; + + /// No description provided for @passwordlessRecoverySecondFactorEmailLabel. + /// + /// In en, this message translates to: + /// **'Email ({email})'** + String passwordlessRecoverySecondFactorEmailLabel(Object email); + + /// No description provided for @passwordlessRecoverySecondFactorPin. + /// + /// In en, this message translates to: + /// **'PIN'** + String get passwordlessRecoverySecondFactorPin; + + /// No description provided for @passwordlessRecoverySecondFactorEmail. + /// + /// In en, this message translates to: + /// **'Email'** + String get passwordlessRecoverySecondFactorEmail; + + /// No description provided for @passwordlessRecoveryModify. + /// + /// In en, this message translates to: + /// **'Modify Recovery Settings'** + String get passwordlessRecoveryModify; + + /// No description provided for @passwordlessRecoveryModifyDesc. + /// + /// In en, this message translates to: + /// **'Update your recovery configuration'** + String get passwordlessRecoveryModifyDesc; + + /// No description provided for @passwordlessRecoverySecondFactor. + /// + /// In en, this message translates to: + /// **'Second Factor'** + String get passwordlessRecoverySecondFactor; + + /// No description provided for @passwordlessRecoveryNoFriendsFound. + /// + /// In en, this message translates to: + /// **'No trusted friends found.'** + String get passwordlessRecoveryNoFriendsFound; + + /// No description provided for @passwordlessRecoveryActiveFriends. + /// + /// In en, this message translates to: + /// **'Active Friends'** + String get passwordlessRecoveryActiveFriends; + + /// No description provided for @passwordlessRecoveryActiveFriendsDesc. + /// + /// In en, this message translates to: + /// **'These trusted friends are actively using twonly and can probably help you recover your account.'** + String get passwordlessRecoveryActiveFriendsDesc; + + /// No description provided for @passwordlessRecoveryInactiveFriends. + /// + /// In en, this message translates to: + /// **'Inactive Friends'** + String get passwordlessRecoveryInactiveFriends; + + /// No description provided for @passwordlessRecoveryInactiveFriendsDesc. + /// + /// In en, this message translates to: + /// **'These friends either have not yet received their share or do not use twonly actively anymore, which could mean that they maybe cannot help.'** + String get passwordlessRecoveryInactiveFriendsDesc; + + /// No description provided for @passwordlessRecoveryNotEnoughFriends. + /// + /// In en, this message translates to: + /// **'Not enough friends selected. You need at least 3.'** + String get passwordlessRecoveryNotEnoughFriends; + + /// No description provided for @passwordlessRecoveryLoading. + /// + /// In en, this message translates to: + /// **'Loading...'** + String get passwordlessRecoveryLoading; + + /// No description provided for @passwordlessRecoveryEnableSuccess. + /// + /// In en, this message translates to: + /// **'Passwordless recovery successfully enabled!'** + String get passwordlessRecoveryEnableSuccess; + + /// No description provided for @passwordlessRecoveryEnableFailed. + /// + /// In en, this message translates to: + /// **'Failed to enable passwordless recovery.'** + String get passwordlessRecoveryEnableFailed; + + /// No description provided for @passwordlessRecoveryNeedAtLeast3. + /// + /// In en, this message translates to: + /// **'You need at least 3 trusted friends.'** + String get passwordlessRecoveryNeedAtLeast3; + + /// No description provided for @passwordlessRecoveryInvalidPin. + /// + /// In en, this message translates to: + /// **'Invalid PIN'** + String get passwordlessRecoveryInvalidPin; + + /// No description provided for @passwordlessRecoveryEnterPin. + /// + /// In en, this message translates to: + /// **'Please enter a PIN.'** + String get passwordlessRecoveryEnterPin; + + /// No description provided for @passwordlessRecoveryPinMinLength. + /// + /// In en, this message translates to: + /// **'PIN must be at least 4 digits.'** + String get passwordlessRecoveryPinMinLength; + + /// No description provided for @passwordlessRecoveryEnterEmail. + /// + /// In en, this message translates to: + /// **'Please enter an email address.'** + String get passwordlessRecoveryEnterEmail; + + /// No description provided for @passwordlessRecoveryEnableBtn. + /// + /// In en, this message translates to: + /// **'Enable Passwordless Recovery'** + String get passwordlessRecoveryEnableBtn; + + /// No description provided for @passwordlessRecoveryRecoverBtn. + /// + /// In en, this message translates to: + /// **'Recover passwordless'** + String get passwordlessRecoveryRecoverBtn; + + /// No description provided for @passwordlessRecoveryModifyBtn. + /// + /// In en, this message translates to: + /// **'Modify Passwordless Recovery'** + String get passwordlessRecoveryModifyBtn; + + /// No description provided for @passwordlessRecoveryStatusEnabled. + /// + /// In en, this message translates to: + /// **'Enabled • {count} trusted friends'** + String passwordlessRecoveryStatusEnabled(num count); + + /// No description provided for @passwordlessRecoveryInfoHowItWorks. + /// + /// In en, this message translates to: + /// **'How it works'** + String get passwordlessRecoveryInfoHowItWorks; + + /// No description provided for @passwordlessRecoveryInfoHowItWorksDesc. + /// + /// In en, this message translates to: + /// **'Because twonly operates without central user accounts or phone numbers to maximize privacy, we rely on a decentralized recovery mechanism. Using Shamir\'s Secret Sharing, your cryptographic identity is split into independent shares and distributed among your trusted friends. To restore access, a predefined threshold of these friends must combine their shares.'** + String get passwordlessRecoveryInfoHowItWorksDesc; + + /// No description provided for @passwordlessRecoveryInfoWhySecondFactor. + /// + /// In en, this message translates to: + /// **'Why a Second Factor?'** + String get passwordlessRecoveryInfoWhySecondFactor; + + /// No description provided for @passwordlessRecoveryInfoWhySecondFactorDesc. + /// + /// In en, this message translates to: + /// **'The second factor (Email or PIN) serves as a vital cryptographic safeguard against malicious collusion. If your trusted friends were to coordinate their shares behind your back, they still wouldn\'t be able to decrypt your identity without the second factor key.'** + String get passwordlessRecoveryInfoWhySecondFactorDesc; + + /// No description provided for @passwordlessRecoveryInfoGotIt. + /// + /// In en, this message translates to: + /// **'Got it'** + String get passwordlessRecoveryInfoGotIt; + + /// No description provided for @passwordlessRecoveryMethod. + /// + /// In en, this message translates to: + /// **'Second factor method'** + String get passwordlessRecoveryMethod; + + /// No description provided for @passwordlessRecoveryMethodNoneDesc. + /// + /// In en, this message translates to: + /// **'Without second-factor, your friends could collaborate to recover your account. Therefore, it is recommended to configure a second-factor.'** + String get passwordlessRecoveryMethodNoneDesc; + + /// No description provided for @passwordlessRecoveryMethodPinHint. + /// + /// In en, this message translates to: + /// **'Enter PIN'** + String get passwordlessRecoveryMethodPinHint; + + /// No description provided for @passwordlessRecoveryMethodEmailHint. + /// + /// In en, this message translates to: + /// **'Enter recovery email address'** + String get passwordlessRecoveryMethodEmailHint; + + /// No description provided for @passwordlessRecoveryMethodEmailDesc. + /// + /// In en, this message translates to: + /// **'Your email address is *never stored on the server* and is only sent to it in the event of a recovery.'** + String get passwordlessRecoveryMethodEmailDesc; + + /// No description provided for @passwordlessRecoveryThresholdDesc. + /// + /// In en, this message translates to: + /// **'To recover your account you need {count} of your selected trusted friends.'** + String passwordlessRecoveryThresholdDesc(num count); + + /// No description provided for @passwordlessRecoveryThresholdTitle. + /// + /// In en, this message translates to: + /// **'Required trusted friends for recovery'** + String get passwordlessRecoveryThresholdTitle; + + /// No description provided for @passwordlessRecoverySelectFriendsNeeded. + /// + /// In en, this message translates to: + /// **'Select friends ({count} more needed)'** + String passwordlessRecoverySelectFriendsNeeded(num count); + + /// No description provided for @passwordlessRecoverySelectFriends. + /// + /// In en, this message translates to: + /// **'Select trusted friends'** + String get passwordlessRecoverySelectFriends; + + /// No description provided for @passwordlessRecoveryNoFriendsSelected. + /// + /// In en, this message translates to: + /// **'No trusted friends selected yet'** + String get passwordlessRecoveryNoFriendsSelected; + + /// No description provided for @passwordlessRecoverySubtitle. + /// + /// In en, this message translates to: + /// **'Recover your identity without a password.'** + String get passwordlessRecoverySubtitle; + + /// No description provided for @recoverPasswordlessExplanation. + /// + /// In en, this message translates to: + /// **'If enabled, you can recover your account by asking your selected friends to help you.'** + String get recoverPasswordlessExplanation; + + /// No description provided for @recoverPasswordlessNotificationCardTitle. + /// + /// In en, this message translates to: + /// **'Enable notifications'** + String get recoverPasswordlessNotificationCardTitle; + + /// No description provided for @recoverPasswordlessNotificationCardSubtitle. + /// + /// In en, this message translates to: + /// **'Get notified when a friend has helped you recover your account.'** + String get recoverPasswordlessNotificationCardSubtitle; + + /// No description provided for @recoverPasswordlessNotificationCardBtn. + /// + /// In en, this message translates to: + /// **'Enable push notifications'** + String get recoverPasswordlessNotificationCardBtn; + + /// No description provided for @recoverPasswordlessQrInstructions. + /// + /// In en, this message translates to: + /// **'Let friends scan the QR code or share the link.'** + String get recoverPasswordlessQrInstructions; + + /// No description provided for @recoverPasswordlessShareBtn. + /// + /// In en, this message translates to: + /// **'Share Recovery Link'** + String get recoverPasswordlessShareBtn; + + /// No description provided for @recoverPasswordlessCopyBtn. + /// + /// In en, this message translates to: + /// **'Copy'** + String get recoverPasswordlessCopyBtn; + + /// No description provided for @recoverPasswordlessCopiedSnackbar. + /// + /// In en, this message translates to: + /// **'Link copied to clipboard!'** + String get recoverPasswordlessCopiedSnackbar; + + /// No description provided for @passwordlessRecoveryNoShareStored. + /// + /// In en, this message translates to: + /// **'No recovery share stored for this contact.'** + String get passwordlessRecoveryNoShareStored; + + /// No description provided for @passwordlessRecoveryShareSent. + /// + /// In en, this message translates to: + /// **'Recovery share successfully sent!'** + String get passwordlessRecoveryShareSent; + + /// No description provided for @passwordlessRecoveryNetworkError. + /// + /// In en, this message translates to: + /// **'Network error, please ensure you have internet'** + String get passwordlessRecoveryNetworkError; + + /// No description provided for @passwordlessRecoveryInvalidEmail. + /// + /// In en, this message translates to: + /// **'The email address is invalid.'** + String get passwordlessRecoveryInvalidEmail; + + /// No description provided for @passwordlessRecoveryResendEmail. + /// + /// In en, this message translates to: + /// **'Resend recovery email'** + String get passwordlessRecoveryResendEmail; + + /// No description provided for @passwordlessRecoveryHelpAFriend. + /// + /// In en, this message translates to: + /// **'Help a Friend'** + String get passwordlessRecoveryHelpAFriend; + + /// No description provided for @passwordlessRecoverySelectContactDesc. + /// + /// In en, this message translates to: + /// **'Please select the contact who requested recovery below to send them their recovery key.'** + String get passwordlessRecoverySelectContactDesc; + + /// No description provided for @passwordlessRecoverySearchContacts. + /// + /// In en, this message translates to: + /// **'Search contacts...'** + String get passwordlessRecoverySearchContacts; + + /// No description provided for @passwordlessRecoveryNoContactsFound. + /// + /// In en, this message translates to: + /// **'No contacts found'** + String get passwordlessRecoveryNoContactsFound; + + /// No description provided for @passwordlessRecoveryCantHelpHim. + /// + /// In en, this message translates to: + /// **'You can\'t help him'** + String get passwordlessRecoveryCantHelpHim; + + /// No description provided for @passwordlessRecoveryDoesAskedYou. + /// + /// In en, this message translates to: + /// **'Does {username} has asked you?'** + String passwordlessRecoveryDoesAskedYou(Object username); + + /// No description provided for @passwordlessRecoveryVerifySourceDesc. + /// + /// In en, this message translates to: + /// **'Please ensure that you actualy received the link/qr code from your friend! If you are unsure, please verify again and then click again on the link or come back.'** + String get passwordlessRecoveryVerifySourceDesc; + + /// No description provided for @passwordlessRecoveryNo. + /// + /// In en, this message translates to: + /// **'No'** + String get passwordlessRecoveryNo; + + /// No description provided for @passwordlessRecoveryYes. + /// + /// In en, this message translates to: + /// **'Yes'** + String get passwordlessRecoveryYes; + + /// No description provided for @passwordlessRecoveryYesWithTimer. + /// + /// In en, this message translates to: + /// **'Yes ({seconds}s)'** + String passwordlessRecoveryYesWithTimer(Object seconds); + + /// No description provided for @recoverPasswordlessBeingRecoveredLabel. + /// + /// In en, this message translates to: + /// **'Account being recovered'** + String get recoverPasswordlessBeingRecoveredLabel; + + /// No description provided for @recoverPasswordlessSharesReceived. + /// + /// In en, this message translates to: + /// **'{received} of {threshold} friends have shared'** + String recoverPasswordlessSharesReceived(Object received, Object threshold); + + /// No description provided for @recoverPasswordlessRecoverNowBtn. + /// + /// In en, this message translates to: + /// **'Recover now'** + String get recoverPasswordlessRecoverNowBtn; } class _AppLocalizationsDelegate diff --git a/lib/src/localization/generated/app_localizations_de.dart b/lib/src/localization/generated/app_localizations_de.dart index 1c5f4270..7f7046a2 100644 --- a/lib/src/localization/generated/app_localizations_de.dart +++ b/lib/src/localization/generated/app_localizations_de.dart @@ -38,7 +38,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get onboardingNotProductBody => - 'twonly wird durch Spenden und ein optionales Abonnement finanziert. Deine Daten werden niemals verkauft.'; + 'twonly wird durch ein optionales Abonnement finanziert. Deine Daten werden niemals verkauft.'; @override String get registerUsernameSlogan => 'Konto erstellen'; @@ -268,30 +268,6 @@ class AppLocalizationsDe extends AppLocalizations { return '$len Kontakt(e)'; } - @override - String get settingsPrivacyProfileSelectionTitle => 'Sicherheitsprofil'; - - @override - String get securityProfileTitle => 'Sicherheitsprofil'; - - @override - String get securityProfileSubtitle => - 'Wähle das Schutzniveau, das zu deiner täglichen Nutzung passt. Dies kann jederzeit in den Einstellungen geändert werden.'; - - @override - String get securityProfileNormalTitle => 'Normaler Schutz'; - - @override - String get securityProfileNormalDesc => - 'Gute Balance zwischen Komfort und Sicherheit, ohne dich zu sehr einzuschränken.'; - - @override - String get securityProfileStrictTitle => 'Strikter Schutz'; - - @override - String get securityProfileStrictDesc => - 'Maximaler Schutz vor Phishing, kann aber unkomfortabel sein.'; - @override String get settingsNotification => 'Benachrichtigung'; @@ -404,7 +380,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get contactUsLastWarning => - 'Dies sind die Informationen, die an uns gesendet werden. Bitte prüfen Sie sie und klicke dann auf „Abschicken“.'; + 'Dies sind die Informationen, die an uns gesendet werden. Bitte prüfe sie und klicke dann auf „Abschicken“.'; @override String get contactUsSuccess => 'Feedback erfolgreich übermittelt!'; @@ -435,13 +411,34 @@ class AppLocalizationsDe extends AppLocalizations { @override String get contactVerifyNumberTitle => 'Kontakte verifizieren'; + @override + String verifyUserIdentity(Object username) { + return 'Identität von $username verifizieren'; + } + @override String get contactVerifyNumberSubtitle => 'Überprüfe die Identität deiner Kontakte, um sicherzugehen, dass du mit der richtigen Person schreibst.'; + @override + String get inChatContactNotVerified => 'Kontakt nicht verifiziert.'; + + @override + String groupMembersNotVerified(Object count) { + return '$count Mitglieder sind nicht verifiziert.'; + } + @override String get userVerifiedTitle => 'Kontakt verifiziert'; + @override + String scanUserQrCode(Object username) { + return 'QR-Code von $username scannen'; + } + + @override + String get openOwnQrCode => 'Eigenen QR-Code öffnen'; + @override String contactVerifiedBy(Object username) { return 'Verifiziert von $username'; @@ -841,7 +838,14 @@ class AppLocalizationsDe extends AppLocalizations { String get backupTwonlySaveNow => 'Jetzt speichern'; @override - String get backupChangePassword => 'Password ändern'; + String get backupChangePassword => 'Passwort ändern'; + + @override + String get backupChangePasswordAuthReason => 'Backup-Passwort ändern'; + + @override + String get backupChangePasswordAuthFailed => + 'Du kannst dein Passwort nur ändern, wenn du dich authentifiziert hast!'; @override String get twonlySafeRecoverTitle => 'Backup wiederherstellen'; @@ -1310,6 +1314,11 @@ class AppLocalizationsDe extends AppLocalizations { String get addContactQrSheetSubtext => 'Lass einen Freund diesen QR-Code scannen, um dich hinzuzufügen'; + @override + String letUserScanQrCode(Object username) { + return 'Lass $username diesen QR-Code scannen'; + } + @override String get finishSetupCardTitle => 'Profil vervollständigen'; @@ -1535,7 +1544,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get verificationBadgeGeneralDesc => - 'Der Haken *schützt dich vor Betrügern und Angreifern*. Es wird neben einem Kontakt angezeigt, der von dir oder einem Freund *manuell überprüft* wurde.'; + 'Der Haken gibt dir die Sicherheit, dass du mit der *richtigen Person* schreibst und deine *Nachrichten vertraulich* bleiben.'; @override String get verificationBadgeGreenDesc => @@ -1552,9 +1561,22 @@ class AppLocalizationsDe extends AppLocalizations { @override String get scanNow => 'Jetzt scannen'; + @override + String get qrScannerVerifyHint => + 'Zum Verifizieren muss die andere Person ihren QR-Code öffnen (Chat-Liste > QR-Code-Button unten rechts)'; + + @override + String qrScannerVerifyUserHint(Object username) { + return 'Zum Verifizieren muss $username seinen QR-Code öffnen (Chat-Liste > QR-Code-Button unten rechts)'; + } + @override String get openQrCode => 'QR-Code öffnen'; + @override + String get letFriendScanQrToVerify => + 'Lass einen Freund diesen QR-Code scannen, um euch zu verifizieren'; + @override String get deleteVerificationTitle => 'Verifizierung löschen?'; @@ -2102,4 +2124,274 @@ class AppLocalizationsDe extends AppLocalizations { @override String get avatarCustomizeReset => 'Zurücksetzen'; + + @override + String get passwordlessRecovery => 'Passwortloses Backup'; + + @override + String get passwordlessRecoveryNotConfigured => 'Nicht konfiguriert'; + + @override + String get passwordlessRecoveryTestPin => 'PIN testen'; + + @override + String get passwordlessRecoveryTestPinTitle => 'PIN testen'; + + @override + String get passwordlessRecoveryTestPinHint => 'Gib deine PIN ein'; + + @override + String get passwordlessRecoveryTestPinCorrect => 'PIN ist korrekt!'; + + @override + String get passwordlessRecoveryTestPinIncorrect => 'Falsche PIN.'; + + @override + String get passwordlessRecoveryTest => 'Testen'; + + @override + String get passwordlessRecoverySecondFactorNone => 'Keiner'; + + @override + String passwordlessRecoverySecondFactorEmailLabel(Object email) { + return 'E-Mail ($email)'; + } + + @override + String get passwordlessRecoverySecondFactorPin => 'PIN'; + + @override + String get passwordlessRecoverySecondFactorEmail => 'E-Mail'; + + @override + String get passwordlessRecoveryModify => 'Einstellungen bearbeiten'; + + @override + String get passwordlessRecoveryModifyDesc => + 'Aktualisiere deine Backupkonfiguration'; + + @override + String get passwordlessRecoverySecondFactor => 'Zweiter Faktor'; + + @override + String get passwordlessRecoveryNoFriendsFound => 'Keine Freunde gefunden.'; + + @override + String get passwordlessRecoveryActiveFriends => 'Aktive Freunde'; + + @override + String get passwordlessRecoveryActiveFriendsDesc => + 'Diese Freunde nutzen twonly aktiv und können dir wahrscheinlich helfen, dein Konto wiederherzustellen.'; + + @override + String get passwordlessRecoveryInactiveFriends => 'Inaktive Freunde'; + + @override + String get passwordlessRecoveryInactiveFriendsDesc => + 'Diese Freunde haben entweder ihren Teil noch nicht erhalten oder nutzen twonly nicht mehr aktiv, was bedeutet, dass sie möglicherweise nicht helfen können.'; + + @override + String get passwordlessRecoveryNotEnoughFriends => + 'Nicht genügend Freunde ausgewählt. Du brauchst mindestens 3.'; + + @override + String get passwordlessRecoveryLoading => 'Wird geladen...'; + + @override + String get passwordlessRecoveryEnableSuccess => + 'Passwortloses Backup erfolgreich aktiviert!'; + + @override + String get passwordlessRecoveryEnableFailed => + 'Fehler beim Aktivieren des passwortlosen Backups.'; + + @override + String get passwordlessRecoveryNeedAtLeast3 => + 'Du brauchst mindestens 3 Freunde.'; + + @override + String get passwordlessRecoveryInvalidPin => 'Ungültige PIN'; + + @override + String get passwordlessRecoveryEnterPin => 'Bitte gib eine PIN ein.'; + + @override + String get passwordlessRecoveryPinMinLength => + 'Die PIN muss mindestens 4 Ziffern lang sein.'; + + @override + String get passwordlessRecoveryEnterEmail => + 'Bitte gib eine E-Mail-Adresse ein.'; + + @override + String get passwordlessRecoveryEnableBtn => 'Passwortloses Backup aktivieren'; + + @override + String get passwordlessRecoveryRecoverBtn => 'Passwortlos wiederherstellen'; + + @override + String get passwordlessRecoveryModifyBtn => 'Passwortloses Backup bearbeiten'; + + @override + String passwordlessRecoveryStatusEnabled(num count) { + return 'Aktiviert • $count Freunde'; + } + + @override + String get passwordlessRecoveryInfoHowItWorks => 'Wie es funktioniert'; + + @override + String get passwordlessRecoveryInfoHowItWorksDesc => + 'Da twonly ohne zentrale Benutzerkonten oder Telefonnummern funktioniert, um deine Privatsphäre zu maximieren, setzen wir auf einen dezentralen Wiederherstellungsmechanismus. Mit Shamir\'s Secret Sharing wird deine kryptografische Identität in unabhängige Teile aufgeteilt und an deine Freunde verteilt. Um den Zugriff wiederherzustellen, muss ein vorher festgelegter Schwellenwert dieser Freunde ihre Teile kombinieren.'; + + @override + String get passwordlessRecoveryInfoWhySecondFactor => + 'Warum ein zweiter Faktor?'; + + @override + String get passwordlessRecoveryInfoWhySecondFactorDesc => + 'Der zweite Faktor (E-Mail oder PIN) dient als wichtiger kryptografischer Schutz vor bösartiger Absprache. Wenn sich deine Freunde hinter deinem Rücken absprechen würden, könnten sie deine Identität ohne den Schlüssel für den zweiten Faktor trotzdem nicht entschlüsseln.'; + + @override + String get passwordlessRecoveryInfoGotIt => 'Verstanden'; + + @override + String get passwordlessRecoveryMethod => 'Methode für den zweiten Faktor'; + + @override + String get passwordlessRecoveryMethodNoneDesc => + 'Ohne zweiten Faktor könnten sich deine Freunde absprechen, um dein Konto wiederherzustellen. Daher wird empfohlen, einen zweiten Faktor zu konfigurieren.'; + + @override + String get passwordlessRecoveryMethodPinHint => 'PIN eingeben'; + + @override + String get passwordlessRecoveryMethodEmailHint => + 'Wiederherstellungs-E-Mail-Adresse eingeben'; + + @override + String get passwordlessRecoveryMethodEmailDesc => + 'Deine E-Mail-Adresse wird *niemals auf dem Server gespeichert* und nur im Falle einer Wiederherstellung an ihn gesendet.'; + + @override + String passwordlessRecoveryThresholdDesc(num count) { + return 'Um dein Konto wiederherzustellen, brauchst du $count deiner ausgewählten Freunde.'; + } + + @override + String get passwordlessRecoveryThresholdTitle => + 'Benötigte Freunde für das Backup'; + + @override + String passwordlessRecoverySelectFriendsNeeded(num count) { + return 'Freunde auswählen ($count weitere benötigt)'; + } + + @override + String get passwordlessRecoverySelectFriends => 'Freunde auswählen'; + + @override + String get passwordlessRecoveryNoFriendsSelected => + 'Noch keine Freunde ausgewählt'; + + @override + String get passwordlessRecoverySubtitle => + 'Stelle deine Identität ohne Passwort wieder her.'; + + @override + String get recoverPasswordlessExplanation => + 'Wenn du die passwortlose Wiederherstellung aktiviert hast, kannst du dein Konto mithilfe deiner Freunde wiederherstellen.'; + + @override + String get recoverPasswordlessNotificationCardTitle => + 'Benachrichtigungen aktivieren'; + + @override + String get recoverPasswordlessNotificationCardSubtitle => + 'Lass dich benachrichtigen, wenn ein Freund dir geholfen hat, dein Konto wiederherzustellen.'; + + @override + String get recoverPasswordlessNotificationCardBtn => + 'Push-Benachrichtigungen aktivieren'; + + @override + String get recoverPasswordlessQrInstructions => + 'Lass deine Freunde den QR-Code scannen oder teile den Link.'; + + @override + String get recoverPasswordlessShareBtn => 'Wiederherstellungs-Link teilen'; + + @override + String get recoverPasswordlessCopyBtn => 'Kopieren'; + + @override + String get recoverPasswordlessCopiedSnackbar => + 'Link in die Zwischenablage kopiert!'; + + @override + String get passwordlessRecoveryNoShareStored => + 'Kein Wiederherstellungs-Teil für diesen Kontakt gespeichert.'; + + @override + String get passwordlessRecoveryShareSent => + 'Wiederherstellungs-Teil erfolgreich gesendet!'; + + @override + String get passwordlessRecoveryNetworkError => + 'Netzwerkfehler, bitte stelle sicher, dass du Internet hast'; + + @override + String get passwordlessRecoveryInvalidEmail => + 'Die E-Mail-Adresse ist ungültig.'; + + @override + String get passwordlessRecoveryResendEmail => 'E-Mail erneut senden'; + + @override + String get passwordlessRecoveryHelpAFriend => 'Einem Freund helfen'; + + @override + String get passwordlessRecoverySelectContactDesc => + 'Wähle den Kontakt aus, dem du helfen möchtest.'; + + @override + String get passwordlessRecoverySearchContacts => 'Kontakte suchen...'; + + @override + String get passwordlessRecoveryNoContactsFound => 'Keine Kontakte gefunden'; + + @override + String get passwordlessRecoveryCantHelpHim => 'Du kannst ihm nicht helfen'; + + @override + String passwordlessRecoveryDoesAskedYou(Object username) { + return 'Hat $username dich gefragt?'; + } + + @override + String get passwordlessRecoveryVerifySourceDesc => + 'Bitte stelle sicher, dass du den Link/QR-Code tatsächlich von deinem Freund erhalten hast! Wenn du dir unsicher bist, verifiziere dies bitte erneut und klicke dann noch einmal auf den Link oder kehre zurück.'; + + @override + String get passwordlessRecoveryNo => 'Nein'; + + @override + String get passwordlessRecoveryYes => 'Ja'; + + @override + String passwordlessRecoveryYesWithTimer(Object seconds) { + return 'Ja (${seconds}s)'; + } + + @override + String get recoverPasswordlessBeingRecoveredLabel => + 'Konto wird wiederhergestellt'; + + @override + String recoverPasswordlessSharesReceived(Object received, Object threshold) { + return '$received von $threshold Freunden haben geteilt'; + } + + @override + String get recoverPasswordlessRecoverNowBtn => 'Jetzt wiederherstellen'; } diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart index 6674af20..41a6b5eb 100644 --- a/lib/src/localization/generated/app_localizations_en.dart +++ b/lib/src/localization/generated/app_localizations_en.dart @@ -37,7 +37,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get onboardingNotProductBody => - 'twonly is financed by donations and an optional subscription. Your data will never be sold.'; + 'twonly is financed by an optional subscription. Your data will never be sold.'; @override String get registerUsernameSlogan => 'Create your account'; @@ -265,30 +265,6 @@ class AppLocalizationsEn extends AppLocalizations { return '$len contact(s)'; } - @override - String get settingsPrivacyProfileSelectionTitle => 'Security Profile'; - - @override - String get securityProfileTitle => 'Security Profile'; - - @override - String get securityProfileSubtitle => - 'Choose the level of protection that fits your daily use. This can be changed at any time in your settings.'; - - @override - String get securityProfileNormalTitle => 'Normal Protection'; - - @override - String get securityProfileNormalDesc => - 'Good balance between a convenient mode without bothering you too much.'; - - @override - String get securityProfileStrictTitle => 'Strict Protection'; - - @override - String get securityProfileStrictDesc => - 'Maximum anti-phishing protection but may be inconvenient.'; - @override String get settingsNotification => 'Notification'; @@ -431,13 +407,34 @@ class AppLocalizationsEn extends AppLocalizations { @override String get contactVerifyNumberTitle => 'Verify contacts'; + @override + String verifyUserIdentity(Object username) { + return 'Verify $username\'s identity'; + } + @override String get contactVerifyNumberSubtitle => 'Verify the identity of your contacts to make sure you are texting the right person.'; + @override + String get inChatContactNotVerified => 'Contact not verified.'; + + @override + String groupMembersNotVerified(Object count) { + return '$count members are not verified.'; + } + @override String get userVerifiedTitle => 'Contact verified'; + @override + String scanUserQrCode(Object username) { + return 'Scan $username\'s QR code'; + } + + @override + String get openOwnQrCode => 'Open your own QR code'; + @override String contactVerifiedBy(Object username) { return 'Verified by $username'; @@ -838,6 +835,13 @@ class AppLocalizationsEn extends AppLocalizations { @override String get backupChangePassword => 'Change password'; + @override + String get backupChangePasswordAuthReason => 'Changing backup password'; + + @override + String get backupChangePasswordAuthFailed => + 'You can only change your password after you have authenticated!'; + @override String get twonlySafeRecoverTitle => 'Restore backup'; @@ -1302,6 +1306,11 @@ class AppLocalizationsEn extends AppLocalizations { String get addContactQrSheetSubtext => 'Let a friend scan this QR code to add you'; + @override + String letUserScanQrCode(Object username) { + return 'Let $username scan this QR code'; + } + @override String get finishSetupCardTitle => 'Complete your profile'; @@ -1522,7 +1531,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get verificationBadgeGeneralDesc => - 'The badge *protects you from scammers and attackers*. It will be displayed next to a contact that has been *manually verified* by you or a friend.'; + 'The badge gives you the peace of mind that you are messaging the *right person* and your *messages remain confidential*.'; @override String get verificationBadgeGreenDesc => @@ -1539,9 +1548,22 @@ class AppLocalizationsEn extends AppLocalizations { @override String get scanNow => 'Scan now'; + @override + String get qrScannerVerifyHint => + 'To verify, the other person must open their QR code (Chat Lists > QR code button at the bottom right)'; + + @override + String qrScannerVerifyUserHint(Object username) { + return 'To verify, $username must open their QR code (Chat Lists > QR code button at the bottom right)'; + } + @override String get openQrCode => 'Open QR code'; + @override + String get letFriendScanQrToVerify => + 'Let a friend scan this QR code to verify you'; + @override String get deleteVerificationTitle => 'Delete verification?'; @@ -2087,4 +2109,270 @@ class AppLocalizationsEn extends AppLocalizations { @override String get avatarCustomizeReset => 'Reset'; + + @override + String get passwordlessRecovery => 'Passwordless Recovery'; + + @override + String get passwordlessRecoveryNotConfigured => 'Not configured'; + + @override + String get passwordlessRecoveryTestPin => 'Test PIN'; + + @override + String get passwordlessRecoveryTestPinTitle => 'Test PIN'; + + @override + String get passwordlessRecoveryTestPinHint => 'Enter your PIN'; + + @override + String get passwordlessRecoveryTestPinCorrect => 'PIN is correct!'; + + @override + String get passwordlessRecoveryTestPinIncorrect => 'Incorrect PIN.'; + + @override + String get passwordlessRecoveryTest => 'Test'; + + @override + String get passwordlessRecoverySecondFactorNone => 'None'; + + @override + String passwordlessRecoverySecondFactorEmailLabel(Object email) { + return 'Email ($email)'; + } + + @override + String get passwordlessRecoverySecondFactorPin => 'PIN'; + + @override + String get passwordlessRecoverySecondFactorEmail => 'Email'; + + @override + String get passwordlessRecoveryModify => 'Modify Recovery Settings'; + + @override + String get passwordlessRecoveryModifyDesc => + 'Update your recovery configuration'; + + @override + String get passwordlessRecoverySecondFactor => 'Second Factor'; + + @override + String get passwordlessRecoveryNoFriendsFound => 'No trusted friends found.'; + + @override + String get passwordlessRecoveryActiveFriends => 'Active Friends'; + + @override + String get passwordlessRecoveryActiveFriendsDesc => + 'These trusted friends are actively using twonly and can probably help you recover your account.'; + + @override + String get passwordlessRecoveryInactiveFriends => 'Inactive Friends'; + + @override + String get passwordlessRecoveryInactiveFriendsDesc => + 'These friends either have not yet received their share or do not use twonly actively anymore, which could mean that they maybe cannot help.'; + + @override + String get passwordlessRecoveryNotEnoughFriends => + 'Not enough friends selected. You need at least 3.'; + + @override + String get passwordlessRecoveryLoading => 'Loading...'; + + @override + String get passwordlessRecoveryEnableSuccess => + 'Passwordless recovery successfully enabled!'; + + @override + String get passwordlessRecoveryEnableFailed => + 'Failed to enable passwordless recovery.'; + + @override + String get passwordlessRecoveryNeedAtLeast3 => + 'You need at least 3 trusted friends.'; + + @override + String get passwordlessRecoveryInvalidPin => 'Invalid PIN'; + + @override + String get passwordlessRecoveryEnterPin => 'Please enter a PIN.'; + + @override + String get passwordlessRecoveryPinMinLength => + 'PIN must be at least 4 digits.'; + + @override + String get passwordlessRecoveryEnterEmail => 'Please enter an email address.'; + + @override + String get passwordlessRecoveryEnableBtn => 'Enable Passwordless Recovery'; + + @override + String get passwordlessRecoveryRecoverBtn => 'Recover passwordless'; + + @override + String get passwordlessRecoveryModifyBtn => 'Modify Passwordless Recovery'; + + @override + String passwordlessRecoveryStatusEnabled(num count) { + return 'Enabled • $count trusted friends'; + } + + @override + String get passwordlessRecoveryInfoHowItWorks => 'How it works'; + + @override + String get passwordlessRecoveryInfoHowItWorksDesc => + 'Because twonly operates without central user accounts or phone numbers to maximize privacy, we rely on a decentralized recovery mechanism. Using Shamir\'s Secret Sharing, your cryptographic identity is split into independent shares and distributed among your trusted friends. To restore access, a predefined threshold of these friends must combine their shares.'; + + @override + String get passwordlessRecoveryInfoWhySecondFactor => 'Why a Second Factor?'; + + @override + String get passwordlessRecoveryInfoWhySecondFactorDesc => + 'The second factor (Email or PIN) serves as a vital cryptographic safeguard against malicious collusion. If your trusted friends were to coordinate their shares behind your back, they still wouldn\'t be able to decrypt your identity without the second factor key.'; + + @override + String get passwordlessRecoveryInfoGotIt => 'Got it'; + + @override + String get passwordlessRecoveryMethod => 'Second factor method'; + + @override + String get passwordlessRecoveryMethodNoneDesc => + 'Without second-factor, your friends could collaborate to recover your account. Therefore, it is recommended to configure a second-factor.'; + + @override + String get passwordlessRecoveryMethodPinHint => 'Enter PIN'; + + @override + String get passwordlessRecoveryMethodEmailHint => + 'Enter recovery email address'; + + @override + String get passwordlessRecoveryMethodEmailDesc => + 'Your email address is *never stored on the server* and is only sent to it in the event of a recovery.'; + + @override + String passwordlessRecoveryThresholdDesc(num count) { + return 'To recover your account you need $count of your selected trusted friends.'; + } + + @override + String get passwordlessRecoveryThresholdTitle => + 'Required trusted friends for recovery'; + + @override + String passwordlessRecoverySelectFriendsNeeded(num count) { + return 'Select friends ($count more needed)'; + } + + @override + String get passwordlessRecoverySelectFriends => 'Select trusted friends'; + + @override + String get passwordlessRecoveryNoFriendsSelected => + 'No trusted friends selected yet'; + + @override + String get passwordlessRecoverySubtitle => + 'Recover your identity without a password.'; + + @override + String get recoverPasswordlessExplanation => + 'If enabled, you can recover your account by asking your selected friends to help you.'; + + @override + String get recoverPasswordlessNotificationCardTitle => 'Enable notifications'; + + @override + String get recoverPasswordlessNotificationCardSubtitle => + 'Get notified when a friend has helped you recover your account.'; + + @override + String get recoverPasswordlessNotificationCardBtn => + 'Enable push notifications'; + + @override + String get recoverPasswordlessQrInstructions => + 'Let friends scan the QR code or share the link.'; + + @override + String get recoverPasswordlessShareBtn => 'Share Recovery Link'; + + @override + String get recoverPasswordlessCopyBtn => 'Copy'; + + @override + String get recoverPasswordlessCopiedSnackbar => 'Link copied to clipboard!'; + + @override + String get passwordlessRecoveryNoShareStored => + 'No recovery share stored for this contact.'; + + @override + String get passwordlessRecoveryShareSent => + 'Recovery share successfully sent!'; + + @override + String get passwordlessRecoveryNetworkError => + 'Network error, please ensure you have internet'; + + @override + String get passwordlessRecoveryInvalidEmail => + 'The email address is invalid.'; + + @override + String get passwordlessRecoveryResendEmail => 'Resend recovery email'; + + @override + String get passwordlessRecoveryHelpAFriend => 'Help a Friend'; + + @override + String get passwordlessRecoverySelectContactDesc => + 'Please select the contact who requested recovery below to send them their recovery key.'; + + @override + String get passwordlessRecoverySearchContacts => 'Search contacts...'; + + @override + String get passwordlessRecoveryNoContactsFound => 'No contacts found'; + + @override + String get passwordlessRecoveryCantHelpHim => 'You can\'t help him'; + + @override + String passwordlessRecoveryDoesAskedYou(Object username) { + return 'Does $username has asked you?'; + } + + @override + String get passwordlessRecoveryVerifySourceDesc => + 'Please ensure that you actualy received the link/qr code from your friend! If you are unsure, please verify again and then click again on the link or come back.'; + + @override + String get passwordlessRecoveryNo => 'No'; + + @override + String get passwordlessRecoveryYes => 'Yes'; + + @override + String passwordlessRecoveryYesWithTimer(Object seconds) { + return 'Yes (${seconds}s)'; + } + + @override + String get recoverPasswordlessBeingRecoveredLabel => + 'Account being recovered'; + + @override + String recoverPasswordlessSharesReceived(Object received, Object threshold) { + return '$received of $threshold friends have shared'; + } + + @override + String get recoverPasswordlessRecoverNowBtn => 'Recover now'; } diff --git a/lib/src/localization/translations b/lib/src/localization/translations index 80b21d7e..b7d703c8 160000 --- a/lib/src/localization/translations +++ b/lib/src/localization/translations @@ -1 +1 @@ -Subproject commit 80b21d7e566a12d105f573cb7224b6cdfe37048b +Subproject commit b7d703c82a5ac0b6776847cb4eb6d637620f6585 diff --git a/lib/src/model/json/onboarding_state.model.dart b/lib/src/model/json/onboarding_state.model.dart new file mode 100644 index 00000000..cdd4eac4 --- /dev/null +++ b/lib/src/model/json/onboarding_state.model.dart @@ -0,0 +1,65 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'onboarding_state.model.g.dart'; + +/// Holds information about a single trusted friend who has already shared their +/// recovery part. +@JsonSerializable() +class ReceivedRecoveryShare { + ReceivedRecoveryShare({ + required this.messageId, + required this.trustedFriendDisplayName, + required this.myDisplayName, + required this.myUserId, + required this.myAvatarSvg, + required this.threshold, + required this.sharedSecretDataBytes, + }); + + factory ReceivedRecoveryShare.fromJson(Map json) => + _$ReceivedRecoveryShareFromJson(json); + + final int messageId; + + final String trustedFriendDisplayName; + + final String myDisplayName; + final int myUserId; + final List? myAvatarSvg; + + final int threshold; + + final List sharedSecretDataBytes; + + Map toJson() => _$ReceivedRecoveryShareToJson(this); +} + +@JsonSerializable() +class OnboardingState { + OnboardingState({ + this.hasOnboardingFinished = false, + this.hasStartedPasswordlessRecovery = false, + this.notificationId, + this.downloadAuthToken, + this.serverRegistered = false, + this.encryptionKey, + this.emailRecoveryRequested = false, + }); + + factory OnboardingState.fromJson(Map json) => + _$OnboardingStateFromJson(json); + + bool hasOnboardingFinished; + bool hasStartedPasswordlessRecovery; + + String? notificationId; + List? downloadAuthToken; + bool serverRegistered; + List? encryptionKey; + bool emailRecoveryRequested; + + + List receivedShares = []; + + Map toJson() => _$OnboardingStateToJson(this); +} diff --git a/lib/src/model/json/onboarding_state.model.g.dart b/lib/src/model/json/onboarding_state.model.g.dart new file mode 100644 index 00000000..67ba5930 --- /dev/null +++ b/lib/src/model/json/onboarding_state.model.g.dart @@ -0,0 +1,67 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'onboarding_state.model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ReceivedRecoveryShare _$ReceivedRecoveryShareFromJson( + Map json, +) => ReceivedRecoveryShare( + messageId: (json['messageId'] as num).toInt(), + trustedFriendDisplayName: json['trustedFriendDisplayName'] as String, + myDisplayName: json['myDisplayName'] as String, + myUserId: (json['myUserId'] as num).toInt(), + myAvatarSvg: (json['myAvatarSvg'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + threshold: (json['threshold'] as num).toInt(), + sharedSecretDataBytes: (json['sharedSecretDataBytes'] as List) + .map((e) => (e as num).toInt()) + .toList(), +); + +Map _$ReceivedRecoveryShareToJson( + ReceivedRecoveryShare instance, +) => { + 'messageId': instance.messageId, + 'trustedFriendDisplayName': instance.trustedFriendDisplayName, + 'myDisplayName': instance.myDisplayName, + 'myUserId': instance.myUserId, + 'myAvatarSvg': instance.myAvatarSvg, + 'threshold': instance.threshold, + 'sharedSecretDataBytes': instance.sharedSecretDataBytes, +}; + +OnboardingState _$OnboardingStateFromJson(Map json) => + OnboardingState( + hasOnboardingFinished: json['hasOnboardingFinished'] as bool? ?? false, + hasStartedPasswordlessRecovery: + json['hasStartedPasswordlessRecovery'] as bool? ?? false, + notificationId: json['notificationId'] as String?, + downloadAuthToken: (json['downloadAuthToken'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + serverRegistered: json['serverRegistered'] as bool? ?? false, + encryptionKey: (json['encryptionKey'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + emailRecoveryRequested: + json['emailRecoveryRequested'] as bool? ?? false, + ) + ..receivedShares = (json['receivedShares'] as List) + .map((e) => ReceivedRecoveryShare.fromJson(e as Map)) + .toList(); + +Map _$OnboardingStateToJson(OnboardingState instance) => + { + 'hasOnboardingFinished': instance.hasOnboardingFinished, + 'hasStartedPasswordlessRecovery': instance.hasStartedPasswordlessRecovery, + 'notificationId': instance.notificationId, + 'downloadAuthToken': instance.downloadAuthToken, + 'serverRegistered': instance.serverRegistered, + 'encryptionKey': instance.encryptionKey, + 'emailRecoveryRequested': instance.emailRecoveryRequested, + 'receivedShares': instance.receivedShares, + }; diff --git a/lib/src/model/json/userdata.model.dart b/lib/src/model/json/userdata.model.dart index e1d27a81..e16ca159 100644 --- a/lib/src/model/json/userdata.model.dart +++ b/lib/src/model/json/userdata.model.dart @@ -40,9 +40,6 @@ class UserData { @JsonKey(defaultValue: SetupProfile.standard) SetupProfile setupProfile = SetupProfile.standard; - @JsonKey(defaultValue: SecurityProfile.normal) - SecurityProfile securityProfile = SecurityProfile.normal; - // --- SUBSCRIPTION DTA --- @JsonKey(defaultValue: 'Free') @@ -208,22 +205,31 @@ class TwonlySafeBackup { @JsonSerializable() class PasswordLessRecovery { - PasswordLessRecovery({ - this.email, - this.pinSeed, - this.pinUnlockToken, - this.threshold, - this.lastHeartbeat, - }); + PasswordLessRecovery(this.threshold); factory PasswordLessRecovery.fromJson(Map json) => _$PasswordLessRecoveryFromJson(json); + // Only stored, so the user can see his deposit email address... String? email; - String? pinSeed; - String? pinUnlockToken; - int? threshold; - DateTime? lastHeartbeat; + + // <-- + // Data shared with trusted friends + + int threshold; + // Trusted friends are able to brute-force the pin -> Server delets after X tries + List? pinSeed; + List? pinUnlockToken; + + // Stored not on the server, so the server is unable to link a email to a user until the actuall recovery or can + // brute-force the pin + List? encryptedServerKeyNonce; + // ---> + + // Checking with the server that the server data is valid and not delted throug the pin protection for example. + DateTime? lastServerHeartbeat; + DateTime? lastContactHeartbeat; + List? encryptedServerKey; Map toJson() => _$PasswordLessRecoveryToJson(this); } diff --git a/lib/src/model/json/userdata.model.g.dart b/lib/src/model/json/userdata.model.g.dart index 7dc20fc9..e92748ef 100644 --- a/lib/src/model/json/userdata.model.g.dart +++ b/lib/src/model/json/userdata.model.g.dart @@ -23,12 +23,6 @@ UserData _$UserDataFromJson(Map json) => ..setupProfile = $enumDecodeNullable(_$SetupProfileEnumMap, json['setupProfile']) ?? SetupProfile.standard - ..securityProfile = - $enumDecodeNullable( - _$SecurityProfileEnumMap, - json['securityProfile'], - ) ?? - SecurityProfile.normal ..subscriptionPlanIdStore = json['subscriptionPlanIdStore'] as String? ..lastImageSend = json['lastImageSend'] == null ? null @@ -132,7 +126,6 @@ Map _$UserDataToJson(UserData instance) => { 'isDeveloper': instance.isDeveloper, 'deviceId': instance.deviceId, 'setupProfile': _$SetupProfileEnumMap[instance.setupProfile]!, - 'securityProfile': _$SecurityProfileEnumMap[instance.securityProfile]!, 'subscriptionPlan': instance.subscriptionPlan, 'subscriptionPlanIdStore': instance.subscriptionPlanIdStore, 'lastImageSend': instance.lastImageSend?.toIso8601String(), @@ -192,12 +185,6 @@ Map _$UserDataToJson(UserData instance) => { const _$SetupProfileEnumMap = { SetupProfile.standard: 'standard', SetupProfile.customized: 'customized', - SetupProfile.maximum: 'maximum', -}; - -const _$SecurityProfileEnumMap = { - SecurityProfile.normal: 'normal', - SecurityProfile.strict: 'strict', }; const _$ThemeModeEnumMap = { @@ -243,22 +230,37 @@ const _$LastBackupUploadStateEnumMap = { PasswordLessRecovery _$PasswordLessRecoveryFromJson( Map json, -) => PasswordLessRecovery( - email: json['email'] as String?, - pinSeed: json['pinSeed'] as String?, - pinUnlockToken: json['pinUnlockToken'] as String?, - threshold: (json['threshold'] as num?)?.toInt(), - lastHeartbeat: json['lastHeartbeat'] == null +) => PasswordLessRecovery((json['threshold'] as num).toInt()) + ..email = json['email'] as String? + ..pinSeed = (json['pinSeed'] as List?) + ?.map((e) => (e as num).toInt()) + .toList() + ..pinUnlockToken = (json['pinUnlockToken'] as List?) + ?.map((e) => (e as num).toInt()) + .toList() + ..encryptedServerKeyNonce = + (json['encryptedServerKeyNonce'] as List?) + ?.map((e) => (e as num).toInt()) + .toList() + ..lastServerHeartbeat = json['lastServerHeartbeat'] == null ? null - : DateTime.parse(json['lastHeartbeat'] as String), -); + : DateTime.parse(json['lastServerHeartbeat'] as String) + ..lastContactHeartbeat = json['lastContactHeartbeat'] == null + ? null + : DateTime.parse(json['lastContactHeartbeat'] as String) + ..encryptedServerKey = (json['encryptedServerKey'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(); Map _$PasswordLessRecoveryToJson( PasswordLessRecovery instance, ) => { 'email': instance.email, + 'threshold': instance.threshold, 'pinSeed': instance.pinSeed, 'pinUnlockToken': instance.pinUnlockToken, - 'threshold': instance.threshold, - 'lastHeartbeat': instance.lastHeartbeat?.toIso8601String(), + 'encryptedServerKeyNonce': instance.encryptedServerKeyNonce, + 'lastServerHeartbeat': instance.lastServerHeartbeat?.toIso8601String(), + 'lastContactHeartbeat': instance.lastContactHeartbeat?.toIso8601String(), + 'encryptedServerKey': instance.encryptedServerKey, }; diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart index dc424110..b592eea1 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart @@ -809,6 +809,301 @@ class Handshake_AuthenticateWithLoginToken extends $pb.GeneratedMessage { void clearInBackground() => $_clearField(5); } +class Handshake_GetServerKeyForPasswordLessRecovery + extends $pb.GeneratedMessage { + factory Handshake_GetServerKeyForPasswordLessRecovery({ + $fixnum.Int64? userId, + $core.List<$core.int>? encryptedServerKeyNone, + $core.List<$core.int>? pinUnlockToken, + $core.List<$core.int>? pinProtectionKey, + $core.String? email, + }) { + final result = create(); + if (userId != null) result.userId = userId; + if (encryptedServerKeyNone != null) + result.encryptedServerKeyNone = encryptedServerKeyNone; + if (pinUnlockToken != null) result.pinUnlockToken = pinUnlockToken; + if (pinProtectionKey != null) result.pinProtectionKey = pinProtectionKey; + if (email != null) result.email = email; + return result; + } + + Handshake_GetServerKeyForPasswordLessRecovery._(); + + factory Handshake_GetServerKeyForPasswordLessRecovery.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Handshake_GetServerKeyForPasswordLessRecovery.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Handshake.GetServerKeyForPasswordLessRecovery', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), + createEmptyInstance: create) + ..aInt64(1, _omitFieldNames ? '' : 'userId') + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'encryptedServerKeyNone', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 3, _omitFieldNames ? '' : 'pinUnlockToken', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 4, _omitFieldNames ? '' : 'pinProtectionKey', $pb.PbFieldType.OY) + ..aOS(5, _omitFieldNames ? '' : 'email') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Handshake_GetServerKeyForPasswordLessRecovery clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Handshake_GetServerKeyForPasswordLessRecovery copyWith( + void Function(Handshake_GetServerKeyForPasswordLessRecovery) + updates) => + super.copyWith((message) => + updates(message as Handshake_GetServerKeyForPasswordLessRecovery)) + as Handshake_GetServerKeyForPasswordLessRecovery; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Handshake_GetServerKeyForPasswordLessRecovery create() => + Handshake_GetServerKeyForPasswordLessRecovery._(); + @$core.override + Handshake_GetServerKeyForPasswordLessRecovery createEmptyInstance() => + create(); + @$core.pragma('dart2js:noInline') + static Handshake_GetServerKeyForPasswordLessRecovery getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + Handshake_GetServerKeyForPasswordLessRecovery>(create); + static Handshake_GetServerKeyForPasswordLessRecovery? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get userId => $_getI64(0); + @$pb.TagNumber(1) + set userId($fixnum.Int64 value) => $_setInt64(0, value); + @$pb.TagNumber(1) + $core.bool hasUserId() => $_has(0); + @$pb.TagNumber(1) + void clearUserId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get encryptedServerKeyNone => $_getN(1); + @$pb.TagNumber(2) + set encryptedServerKeyNone($core.List<$core.int> value) => + $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasEncryptedServerKeyNone() => $_has(1); + @$pb.TagNumber(2) + void clearEncryptedServerKeyNone() => $_clearField(2); + + @$pb.TagNumber(3) + $core.List<$core.int> get pinUnlockToken => $_getN(2); + @$pb.TagNumber(3) + set pinUnlockToken($core.List<$core.int> value) => $_setBytes(2, value); + @$pb.TagNumber(3) + $core.bool hasPinUnlockToken() => $_has(2); + @$pb.TagNumber(3) + void clearPinUnlockToken() => $_clearField(3); + + @$pb.TagNumber(4) + $core.List<$core.int> get pinProtectionKey => $_getN(3); + @$pb.TagNumber(4) + set pinProtectionKey($core.List<$core.int> value) => $_setBytes(3, value); + @$pb.TagNumber(4) + $core.bool hasPinProtectionKey() => $_has(3); + @$pb.TagNumber(4) + void clearPinProtectionKey() => $_clearField(4); + + @$pb.TagNumber(5) + $core.String get email => $_getSZ(4); + @$pb.TagNumber(5) + set email($core.String value) => $_setString(4, value); + @$pb.TagNumber(5) + $core.bool hasEmail() => $_has(4); + @$pb.TagNumber(5) + void clearEmail() => $_clearField(5); +} + +class Handshake_RegisterPasswordlessNotification extends $pb.GeneratedMessage { + factory Handshake_RegisterPasswordlessNotification({ + $core.String? notificationId, + $core.List<$core.int>? downloadAuthToken, + $core.String? langCode, + $core.String? googleFcm, + }) { + final result = create(); + if (notificationId != null) result.notificationId = notificationId; + if (downloadAuthToken != null) result.downloadAuthToken = downloadAuthToken; + if (langCode != null) result.langCode = langCode; + if (googleFcm != null) result.googleFcm = googleFcm; + return result; + } + + Handshake_RegisterPasswordlessNotification._(); + + factory Handshake_RegisterPasswordlessNotification.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Handshake_RegisterPasswordlessNotification.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Handshake.RegisterPasswordlessNotification', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'notificationId') + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'downloadAuthToken', $pb.PbFieldType.OY) + ..aOS(3, _omitFieldNames ? '' : 'langCode') + ..aOS(4, _omitFieldNames ? '' : 'googleFcm') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Handshake_RegisterPasswordlessNotification clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Handshake_RegisterPasswordlessNotification copyWith( + void Function(Handshake_RegisterPasswordlessNotification) updates) => + super.copyWith((message) => + updates(message as Handshake_RegisterPasswordlessNotification)) + as Handshake_RegisterPasswordlessNotification; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Handshake_RegisterPasswordlessNotification create() => + Handshake_RegisterPasswordlessNotification._(); + @$core.override + Handshake_RegisterPasswordlessNotification createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Handshake_RegisterPasswordlessNotification getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + Handshake_RegisterPasswordlessNotification>(create); + static Handshake_RegisterPasswordlessNotification? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get notificationId => $_getSZ(0); + @$pb.TagNumber(1) + set notificationId($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasNotificationId() => $_has(0); + @$pb.TagNumber(1) + void clearNotificationId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get downloadAuthToken => $_getN(1); + @$pb.TagNumber(2) + set downloadAuthToken($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasDownloadAuthToken() => $_has(1); + @$pb.TagNumber(2) + void clearDownloadAuthToken() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get langCode => $_getSZ(2); + @$pb.TagNumber(3) + set langCode($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasLangCode() => $_has(2); + @$pb.TagNumber(3) + void clearLangCode() => $_clearField(3); + + @$pb.TagNumber(4) + $core.String get googleFcm => $_getSZ(3); + @$pb.TagNumber(4) + set googleFcm($core.String value) => $_setString(3, value); + @$pb.TagNumber(4) + $core.bool hasGoogleFcm() => $_has(3); + @$pb.TagNumber(4) + void clearGoogleFcm() => $_clearField(4); +} + +class Handshake_CheckForPasswordlessNotification extends $pb.GeneratedMessage { + factory Handshake_CheckForPasswordlessNotification({ + $core.String? notificationId, + $core.List<$core.int>? downloadAuthToken, + $core.Iterable<$fixnum.Int64>? alreadyReceivedMessageIds, + }) { + final result = create(); + if (notificationId != null) result.notificationId = notificationId; + if (downloadAuthToken != null) result.downloadAuthToken = downloadAuthToken; + if (alreadyReceivedMessageIds != null) + result.alreadyReceivedMessageIds.addAll(alreadyReceivedMessageIds); + return result; + } + + Handshake_CheckForPasswordlessNotification._(); + + factory Handshake_CheckForPasswordlessNotification.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Handshake_CheckForPasswordlessNotification.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Handshake.CheckForPasswordlessNotification', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'notificationId') + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'downloadAuthToken', $pb.PbFieldType.OY) + ..p<$fixnum.Int64>(3, _omitFieldNames ? '' : 'alreadyReceivedMessageIds', + $pb.PbFieldType.K6) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Handshake_CheckForPasswordlessNotification clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Handshake_CheckForPasswordlessNotification copyWith( + void Function(Handshake_CheckForPasswordlessNotification) updates) => + super.copyWith((message) => + updates(message as Handshake_CheckForPasswordlessNotification)) + as Handshake_CheckForPasswordlessNotification; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Handshake_CheckForPasswordlessNotification create() => + Handshake_CheckForPasswordlessNotification._(); + @$core.override + Handshake_CheckForPasswordlessNotification createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Handshake_CheckForPasswordlessNotification getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + Handshake_CheckForPasswordlessNotification>(create); + static Handshake_CheckForPasswordlessNotification? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get notificationId => $_getSZ(0); + @$pb.TagNumber(1) + set notificationId($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasNotificationId() => $_has(0); + @$pb.TagNumber(1) + void clearNotificationId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get downloadAuthToken => $_getN(1); + @$pb.TagNumber(2) + set downloadAuthToken($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasDownloadAuthToken() => $_has(1); + @$pb.TagNumber(2) + void clearDownloadAuthToken() => $_clearField(2); + + @$pb.TagNumber(3) + $pb.PbList<$fixnum.Int64> get alreadyReceivedMessageIds => $_getList(2); +} + enum Handshake_Handshake { register, getAuthChallenge, @@ -817,6 +1112,9 @@ enum Handshake_Handshake { requestPOW, authenticateWithLoginToken, getUseridByUsername, + getServerKeyForPasswordlessRecovery, + registerPasswordlessNotification, + checkForPasswordlessNotification, notSet } @@ -829,6 +1127,12 @@ class Handshake extends $pb.GeneratedMessage { Handshake_RequestPOW? requestPOW, Handshake_AuthenticateWithLoginToken? authenticateWithLoginToken, Handshake_GetUserIdByUsername? getUseridByUsername, + Handshake_GetServerKeyForPasswordLessRecovery? + getServerKeyForPasswordlessRecovery, + Handshake_RegisterPasswordlessNotification? + registerPasswordlessNotification, + Handshake_CheckForPasswordlessNotification? + checkForPasswordlessNotification, }) { final result = create(); if (register != null) result.register = register; @@ -840,6 +1144,15 @@ class Handshake extends $pb.GeneratedMessage { result.authenticateWithLoginToken = authenticateWithLoginToken; if (getUseridByUsername != null) result.getUseridByUsername = getUseridByUsername; + if (getServerKeyForPasswordlessRecovery != null) + result.getServerKeyForPasswordlessRecovery = + getServerKeyForPasswordlessRecovery; + if (registerPasswordlessNotification != null) + result.registerPasswordlessNotification = + registerPasswordlessNotification; + if (checkForPasswordlessNotification != null) + result.checkForPasswordlessNotification = + checkForPasswordlessNotification; return result; } @@ -861,6 +1174,9 @@ class Handshake extends $pb.GeneratedMessage { 5: Handshake_Handshake.requestPOW, 6: Handshake_Handshake.authenticateWithLoginToken, 7: Handshake_Handshake.getUseridByUsername, + 8: Handshake_Handshake.getServerKeyForPasswordlessRecovery, + 9: Handshake_Handshake.registerPasswordlessNotification, + 10: Handshake_Handshake.checkForPasswordlessNotification, 0: Handshake_Handshake.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo( @@ -868,7 +1184,7 @@ class Handshake extends $pb.GeneratedMessage { package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create) - ..oo(0, [1, 2, 3, 4, 5, 6, 7]) + ..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) ..aOM(1, _omitFieldNames ? '' : 'register', subBuilder: Handshake_Register.create) ..aOM( @@ -887,6 +1203,15 @@ class Handshake extends $pb.GeneratedMessage { ..aOM( 7, _omitFieldNames ? '' : 'getUseridByUsername', subBuilder: Handshake_GetUserIdByUsername.create) + ..aOM( + 8, _omitFieldNames ? '' : 'getServerKeyForPasswordlessRecovery', + subBuilder: Handshake_GetServerKeyForPasswordLessRecovery.create) + ..aOM( + 9, _omitFieldNames ? '' : 'registerPasswordlessNotification', + subBuilder: Handshake_RegisterPasswordlessNotification.create) + ..aOM( + 10, _omitFieldNames ? '' : 'checkForPasswordlessNotification', + subBuilder: Handshake_CheckForPasswordlessNotification.create) ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -914,6 +1239,9 @@ class Handshake extends $pb.GeneratedMessage { @$pb.TagNumber(5) @$pb.TagNumber(6) @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) Handshake_Handshake whichHandshake() => _Handshake_HandshakeByTag[$_whichOneof(0)]!; @$pb.TagNumber(1) @@ -923,6 +1251,9 @@ class Handshake extends $pb.GeneratedMessage { @$pb.TagNumber(5) @$pb.TagNumber(6) @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) void clearHandshake() => $_clearField($_whichOneof(0)); @$pb.TagNumber(1) @@ -1006,6 +1337,51 @@ class Handshake extends $pb.GeneratedMessage { void clearGetUseridByUsername() => $_clearField(7); @$pb.TagNumber(7) Handshake_GetUserIdByUsername ensureGetUseridByUsername() => $_ensure(6); + + @$pb.TagNumber(8) + Handshake_GetServerKeyForPasswordLessRecovery + get getServerKeyForPasswordlessRecovery => $_getN(7); + @$pb.TagNumber(8) + set getServerKeyForPasswordlessRecovery( + Handshake_GetServerKeyForPasswordLessRecovery value) => + $_setField(8, value); + @$pb.TagNumber(8) + $core.bool hasGetServerKeyForPasswordlessRecovery() => $_has(7); + @$pb.TagNumber(8) + void clearGetServerKeyForPasswordlessRecovery() => $_clearField(8); + @$pb.TagNumber(8) + Handshake_GetServerKeyForPasswordLessRecovery + ensureGetServerKeyForPasswordlessRecovery() => $_ensure(7); + + @$pb.TagNumber(9) + Handshake_RegisterPasswordlessNotification + get registerPasswordlessNotification => $_getN(8); + @$pb.TagNumber(9) + set registerPasswordlessNotification( + Handshake_RegisterPasswordlessNotification value) => + $_setField(9, value); + @$pb.TagNumber(9) + $core.bool hasRegisterPasswordlessNotification() => $_has(8); + @$pb.TagNumber(9) + void clearRegisterPasswordlessNotification() => $_clearField(9); + @$pb.TagNumber(9) + Handshake_RegisterPasswordlessNotification + ensureRegisterPasswordlessNotification() => $_ensure(8); + + @$pb.TagNumber(10) + Handshake_CheckForPasswordlessNotification + get checkForPasswordlessNotification => $_getN(9); + @$pb.TagNumber(10) + set checkForPasswordlessNotification( + Handshake_CheckForPasswordlessNotification value) => + $_setField(10, value); + @$pb.TagNumber(10) + $core.bool hasCheckForPasswordlessNotification() => $_has(9); + @$pb.TagNumber(10) + void clearCheckForPasswordlessNotification() => $_clearField(10); + @$pb.TagNumber(10) + Handshake_CheckForPasswordlessNotification + ensureCheckForPasswordlessNotification() => $_ensure(9); } class ApplicationData_TextMessage extends $pb.GeneratedMessage { @@ -2182,6 +2558,160 @@ class ApplicationData_Deprecated extends $pb.GeneratedMessage { static ApplicationData_Deprecated? _defaultInstance; } +class ApplicationData_RegisterPasswordLessRecovery + extends $pb.GeneratedMessage { + factory ApplicationData_RegisterPasswordLessRecovery({ + $core.List<$core.int>? encryptedServerKey, + $core.List<$core.int>? pinUnlockToken, + }) { + final result = create(); + if (encryptedServerKey != null) + result.encryptedServerKey = encryptedServerKey; + if (pinUnlockToken != null) result.pinUnlockToken = pinUnlockToken; + return result; + } + + ApplicationData_RegisterPasswordLessRecovery._(); + + factory ApplicationData_RegisterPasswordLessRecovery.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory ApplicationData_RegisterPasswordLessRecovery.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'ApplicationData.RegisterPasswordLessRecovery', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 1, _omitFieldNames ? '' : 'encryptedServerKey', $pb.PbFieldType.OY, + protoName: 'encryptedServerKey') + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'pinUnlockToken', $pb.PbFieldType.OY, + protoName: 'pinUnlockToken') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ApplicationData_RegisterPasswordLessRecovery clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ApplicationData_RegisterPasswordLessRecovery copyWith( + void Function(ApplicationData_RegisterPasswordLessRecovery) + updates) => + super.copyWith((message) => + updates(message as ApplicationData_RegisterPasswordLessRecovery)) + as ApplicationData_RegisterPasswordLessRecovery; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ApplicationData_RegisterPasswordLessRecovery create() => + ApplicationData_RegisterPasswordLessRecovery._(); + @$core.override + ApplicationData_RegisterPasswordLessRecovery createEmptyInstance() => + create(); + @$core.pragma('dart2js:noInline') + static ApplicationData_RegisterPasswordLessRecovery getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + ApplicationData_RegisterPasswordLessRecovery>(create); + static ApplicationData_RegisterPasswordLessRecovery? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get encryptedServerKey => $_getN(0); + @$pb.TagNumber(1) + set encryptedServerKey($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(1) + $core.bool hasEncryptedServerKey() => $_has(0); + @$pb.TagNumber(1) + void clearEncryptedServerKey() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get pinUnlockToken => $_getN(1); + @$pb.TagNumber(2) + set pinUnlockToken($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasPinUnlockToken() => $_has(1); + @$pb.TagNumber(2) + void clearPinUnlockToken() => $_clearField(2); +} + +class ApplicationData_PasswordlessNotification extends $pb.GeneratedMessage { + factory ApplicationData_PasswordlessNotification({ + $core.String? notificationId, + $core.List<$core.int>? encryptedMessage, + }) { + final result = create(); + if (notificationId != null) result.notificationId = notificationId; + if (encryptedMessage != null) result.encryptedMessage = encryptedMessage; + return result; + } + + ApplicationData_PasswordlessNotification._(); + + factory ApplicationData_PasswordlessNotification.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory ApplicationData_PasswordlessNotification.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'ApplicationData.PasswordlessNotification', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'notificationId') + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'encryptedMessage', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ApplicationData_PasswordlessNotification clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ApplicationData_PasswordlessNotification copyWith( + void Function(ApplicationData_PasswordlessNotification) updates) => + super.copyWith((message) => + updates(message as ApplicationData_PasswordlessNotification)) + as ApplicationData_PasswordlessNotification; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ApplicationData_PasswordlessNotification create() => + ApplicationData_PasswordlessNotification._(); + @$core.override + ApplicationData_PasswordlessNotification createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static ApplicationData_PasswordlessNotification getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + ApplicationData_PasswordlessNotification>(create); + static ApplicationData_PasswordlessNotification? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get notificationId => $_getSZ(0); + @$pb.TagNumber(1) + set notificationId($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasNotificationId() => $_has(0); + @$pb.TagNumber(1) + void clearNotificationId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get encryptedMessage => $_getN(1); + @$pb.TagNumber(2) + set encryptedMessage($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasEncryptedMessage() => $_has(1); + @$pb.TagNumber(2) + void clearEncryptedMessage() => $_clearField(2); +} + enum ApplicationData_ApplicationData { textMessage, getUserByUsername, @@ -2209,6 +2739,8 @@ enum ApplicationData_ApplicationData { ipaForceCheck, addAdditionalUser, setLoginToken, + registerPasswordlessRecovery, + passwordlessNotification, notSet } @@ -2240,6 +2772,8 @@ class ApplicationData extends $pb.GeneratedMessage { ApplicationData_IPAForceCheck? ipaForceCheck, ApplicationData_AddAdditionalUser? addAdditionalUser, ApplicationData_SetLoginToken? setLoginToken, + ApplicationData_RegisterPasswordLessRecovery? registerPasswordlessRecovery, + ApplicationData_PasswordlessNotification? passwordlessNotification, }) { final result = create(); if (textMessage != null) result.textMessage = textMessage; @@ -2274,6 +2808,10 @@ class ApplicationData extends $pb.GeneratedMessage { if (ipaForceCheck != null) result.ipaForceCheck = ipaForceCheck; if (addAdditionalUser != null) result.addAdditionalUser = addAdditionalUser; if (setLoginToken != null) result.setLoginToken = setLoginToken; + if (registerPasswordlessRecovery != null) + result.registerPasswordlessRecovery = registerPasswordlessRecovery; + if (passwordlessNotification != null) + result.passwordlessNotification = passwordlessNotification; return result; } @@ -2314,6 +2852,8 @@ class ApplicationData extends $pb.GeneratedMessage { 28: ApplicationData_ApplicationData.ipaForceCheck, 29: ApplicationData_ApplicationData.addAdditionalUser, 30: ApplicationData_ApplicationData.setLoginToken, + 31: ApplicationData_ApplicationData.registerPasswordlessRecovery, + 32: ApplicationData_ApplicationData.passwordlessNotification, 0: ApplicationData_ApplicationData.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo( @@ -2347,7 +2887,9 @@ class ApplicationData extends $pb.GeneratedMessage { 27, 28, 29, - 30 + 30, + 31, + 32 ]) ..aOM(1, _omitFieldNames ? '' : 'textMessage', protoName: 'textMessage', @@ -2439,6 +2981,12 @@ class ApplicationData extends $pb.GeneratedMessage { ..aOM( 30, _omitFieldNames ? '' : 'setLoginToken', subBuilder: ApplicationData_SetLoginToken.create) + ..aOM( + 31, _omitFieldNames ? '' : 'registerPasswordlessRecovery', + subBuilder: ApplicationData_RegisterPasswordLessRecovery.create) + ..aOM( + 32, _omitFieldNames ? '' : 'passwordlessNotification', + subBuilder: ApplicationData_PasswordlessNotification.create) ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -2486,6 +3034,8 @@ class ApplicationData extends $pb.GeneratedMessage { @$pb.TagNumber(28) @$pb.TagNumber(29) @$pb.TagNumber(30) + @$pb.TagNumber(31) + @$pb.TagNumber(32) ApplicationData_ApplicationData whichApplicationData() => _ApplicationData_ApplicationDataByTag[$_whichOneof(0)]!; @$pb.TagNumber(1) @@ -2514,6 +3064,8 @@ class ApplicationData extends $pb.GeneratedMessage { @$pb.TagNumber(28) @$pb.TagNumber(29) @$pb.TagNumber(30) + @$pb.TagNumber(31) + @$pb.TagNumber(32) void clearApplicationData() => $_clearField($_whichOneof(0)); @$pb.TagNumber(1) @@ -2819,6 +3371,36 @@ class ApplicationData extends $pb.GeneratedMessage { void clearSetLoginToken() => $_clearField(30); @$pb.TagNumber(30) ApplicationData_SetLoginToken ensureSetLoginToken() => $_ensure(25); + + @$pb.TagNumber(31) + ApplicationData_RegisterPasswordLessRecovery + get registerPasswordlessRecovery => $_getN(26); + @$pb.TagNumber(31) + set registerPasswordlessRecovery( + ApplicationData_RegisterPasswordLessRecovery value) => + $_setField(31, value); + @$pb.TagNumber(31) + $core.bool hasRegisterPasswordlessRecovery() => $_has(26); + @$pb.TagNumber(31) + void clearRegisterPasswordlessRecovery() => $_clearField(31); + @$pb.TagNumber(31) + ApplicationData_RegisterPasswordLessRecovery + ensureRegisterPasswordlessRecovery() => $_ensure(26); + + @$pb.TagNumber(32) + ApplicationData_PasswordlessNotification get passwordlessNotification => + $_getN(27); + @$pb.TagNumber(32) + set passwordlessNotification( + ApplicationData_PasswordlessNotification value) => + $_setField(32, value); + @$pb.TagNumber(32) + $core.bool hasPasswordlessNotification() => $_has(27); + @$pb.TagNumber(32) + void clearPasswordlessNotification() => $_clearField(32); + @$pb.TagNumber(32) + ApplicationData_PasswordlessNotification ensurePasswordlessNotification() => + $_ensure(27); } class Response_PreKey extends $pb.GeneratedMessage { diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart index 9ea3c1f3..56e88824 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart @@ -152,6 +152,33 @@ const Handshake$json = { '9': 0, '10': 'getUseridByUsername' }, + { + '1': 'get_server_key_for_passwordless_recovery', + '3': 8, + '4': 1, + '5': 11, + '6': '.client_to_server.Handshake.GetServerKeyForPasswordLessRecovery', + '9': 0, + '10': 'getServerKeyForPasswordlessRecovery' + }, + { + '1': 'register_passwordless_notification', + '3': 9, + '4': 1, + '5': 11, + '6': '.client_to_server.Handshake.RegisterPasswordlessNotification', + '9': 0, + '10': 'registerPasswordlessNotification' + }, + { + '1': 'check_for_passwordless_notification', + '3': 10, + '4': 1, + '5': 11, + '6': '.client_to_server.Handshake.CheckForPasswordlessNotification', + '9': 0, + '10': 'checkForPasswordlessNotification' + }, ], '3': [ Handshake_RequestPOW$json, @@ -160,7 +187,10 @@ const Handshake$json = { Handshake_GetUserIdByUsername$json, Handshake_GetAuthToken$json, Handshake_Authenticate$json, - Handshake_AuthenticateWithLoginToken$json + Handshake_AuthenticateWithLoginToken$json, + Handshake_GetServerKeyForPasswordLessRecovery$json, + Handshake_RegisterPasswordlessNotification$json, + Handshake_CheckForPasswordlessNotification$json ], '8': [ {'1': 'Handshake'}, @@ -303,6 +333,95 @@ const Handshake_AuthenticateWithLoginToken$json = { ], }; +@$core.Deprecated('Use handshakeDescriptor instead') +const Handshake_GetServerKeyForPasswordLessRecovery$json = { + '1': 'GetServerKeyForPasswordLessRecovery', + '2': [ + {'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'}, + { + '1': 'encrypted_server_key_none', + '3': 2, + '4': 1, + '5': 12, + '10': 'encryptedServerKeyNone' + }, + { + '1': 'pin_unlock_token', + '3': 3, + '4': 1, + '5': 12, + '9': 0, + '10': 'pinUnlockToken', + '17': true + }, + { + '1': 'pin_protection_key', + '3': 4, + '4': 1, + '5': 12, + '9': 1, + '10': 'pinProtectionKey', + '17': true + }, + {'1': 'email', '3': 5, '4': 1, '5': 9, '9': 2, '10': 'email', '17': true}, + ], + '8': [ + {'1': '_pin_unlock_token'}, + {'1': '_pin_protection_key'}, + {'1': '_email'}, + ], +}; + +@$core.Deprecated('Use handshakeDescriptor instead') +const Handshake_RegisterPasswordlessNotification$json = { + '1': 'RegisterPasswordlessNotification', + '2': [ + {'1': 'notification_id', '3': 1, '4': 1, '5': 9, '10': 'notificationId'}, + { + '1': 'download_auth_token', + '3': 2, + '4': 1, + '5': 12, + '10': 'downloadAuthToken' + }, + {'1': 'lang_code', '3': 3, '4': 1, '5': 9, '10': 'langCode'}, + { + '1': 'google_fcm', + '3': 4, + '4': 1, + '5': 9, + '9': 0, + '10': 'googleFcm', + '17': true + }, + ], + '8': [ + {'1': '_google_fcm'}, + ], +}; + +@$core.Deprecated('Use handshakeDescriptor instead') +const Handshake_CheckForPasswordlessNotification$json = { + '1': 'CheckForPasswordlessNotification', + '2': [ + {'1': 'notification_id', '3': 1, '4': 1, '5': 9, '10': 'notificationId'}, + { + '1': 'download_auth_token', + '3': 2, + '4': 1, + '5': 12, + '10': 'downloadAuthToken' + }, + { + '1': 'already_received_message_ids', + '3': 3, + '4': 3, + '5': 3, + '10': 'alreadyReceivedMessageIds' + }, + ], +}; + /// Descriptor for `Handshake`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List handshakeDescriptor = $convert.base64Decode( 'CglIYW5kc2hha2USQgoIcmVnaXN0ZXIYASABKAsyJC5jbGllbnRfdG9fc2VydmVyLkhhbmRzaG' @@ -316,26 +435,47 @@ final $typed_data.Uint8List handshakeDescriptor = $convert.base64Decode( 'MjYuY2xpZW50X3RvX3NlcnZlci5IYW5kc2hha2UuQXV0aGVudGljYXRlV2l0aExvZ2luVG9rZW' '5IAFIaYXV0aGVudGljYXRlV2l0aExvZ2luVG9rZW4SZgoWZ2V0X3VzZXJpZF9ieV91c2VybmFt' 'ZRgHIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuSGFuZHNoYWtlLkdldFVzZXJJZEJ5VXNlcm5hbW' - 'VIAFITZ2V0VXNlcmlkQnlVc2VybmFtZRoMCgpSZXF1ZXN0UE9XGsoDCghSZWdpc3RlchIaCgh1' - 'c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUSJAoLaW52aXRlX2NvZGUYAiABKAlIAFIKaW52aXRlQ2' - '9kZYgBARIuChNwdWJsaWNfaWRlbnRpdHlfa2V5GAMgASgMUhFwdWJsaWNJZGVudGl0eUtleRIj' - 'Cg1zaWduZWRfcHJla2V5GAQgASgMUgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaW' - 'duYXR1cmUYBSABKAxSFXNpZ25lZFByZWtleVNpZ25hdHVyZRIoChBzaWduZWRfcHJla2V5X2lk' - 'GAYgASgDUg5zaWduZWRQcmVrZXlJZBInCg9yZWdpc3RyYXRpb25faWQYByABKANSDnJlZ2lzdH' - 'JhdGlvbklkEhUKBmlzX2lvcxgIIAEoCFIFaXNJb3MSGwoJbGFuZ19jb2RlGAkgASgJUghsYW5n' - 'Q29kZRIiCg1wcm9vZl9vZl93b3JrGAogASgDUgtwcm9vZk9mV29yaxIkCgtsb2dpbl90b2tlbh' - 'gLIAEoDEgBUgpsb2dpblRva2VuiAEBQg4KDF9pbnZpdGVfY29kZUIOCgxfbG9naW5fdG9rZW4a' - 'EgoQR2V0QXV0aENoYWxsZW5nZRoxChNHZXRVc2VySWRCeVVzZXJuYW1lEhoKCHVzZXJuYW1lGA' - 'EgASgJUgh1c2VybmFtZRpDCgxHZXRBdXRoVG9rZW4SFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklk' - 'EhoKCHJlc3BvbnNlGAIgASgMUghyZXNwb25zZRroAQoMQXV0aGVudGljYXRlEhcKB3VzZXJfaW' - 'QYASABKANSBnVzZXJJZBIdCgphdXRoX3Rva2VuGAIgASgMUglhdXRoVG9rZW4SJAoLYXBwX3Zl' - 'cnNpb24YAyABKAlIAFIKYXBwVmVyc2lvbogBARIgCglkZXZpY2VfaWQYBCABKANIAVIIZGV2aW' - 'NlSWSIAQESKAoNaW5fYmFja2dyb3VuZBgFIAEoCEgCUgxpbkJhY2tncm91bmSIAQFCDgoMX2Fw' - 'cF92ZXJzaW9uQgwKCl9kZXZpY2VfaWRCEAoOX2luX2JhY2tncm91bmQaxgEKGkF1dGhlbnRpY2' - 'F0ZVdpdGhMb2dpblRva2VuEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIsChJzZWNyZXRfbG9n' - 'aW5fdG9rZW4YAiABKAxSEHNlY3JldExvZ2luVG9rZW4SHwoLYXBwX3ZlcnNpb24YAyABKAlSCm' - 'FwcFZlcnNpb24SGwoJZGV2aWNlX2lkGAQgASgDUghkZXZpY2VJZBIjCg1pbl9iYWNrZ3JvdW5k' - 'GAUgASgIUgxpbkJhY2tncm91bmRCCwoJSGFuZHNoYWtl'); + 'VIAFITZ2V0VXNlcmlkQnlVc2VybmFtZRKYAQooZ2V0X3NlcnZlcl9rZXlfZm9yX3Bhc3N3b3Jk' + 'bGVzc19yZWNvdmVyeRgIIAEoCzI/LmNsaWVudF90b19zZXJ2ZXIuSGFuZHNoYWtlLkdldFNlcn' + 'ZlcktleUZvclBhc3N3b3JkTGVzc1JlY292ZXJ5SABSI2dldFNlcnZlcktleUZvclBhc3N3b3Jk' + 'bGVzc1JlY292ZXJ5EowBCiJyZWdpc3Rlcl9wYXNzd29yZGxlc3Nfbm90aWZpY2F0aW9uGAkgAS' + 'gLMjwuY2xpZW50X3RvX3NlcnZlci5IYW5kc2hha2UuUmVnaXN0ZXJQYXNzd29yZGxlc3NOb3Rp' + 'ZmljYXRpb25IAFIgcmVnaXN0ZXJQYXNzd29yZGxlc3NOb3RpZmljYXRpb24SjQEKI2NoZWNrX2' + 'Zvcl9wYXNzd29yZGxlc3Nfbm90aWZpY2F0aW9uGAogASgLMjwuY2xpZW50X3RvX3NlcnZlci5I' + 'YW5kc2hha2UuQ2hlY2tGb3JQYXNzd29yZGxlc3NOb3RpZmljYXRpb25IAFIgY2hlY2tGb3JQYX' + 'Nzd29yZGxlc3NOb3RpZmljYXRpb24aDAoKUmVxdWVzdFBPVxrKAwoIUmVnaXN0ZXISGgoIdXNl' + 'cm5hbWUYASABKAlSCHVzZXJuYW1lEiQKC2ludml0ZV9jb2RlGAIgASgJSABSCmludml0ZUNvZG' + 'WIAQESLgoTcHVibGljX2lkZW50aXR5X2tleRgDIAEoDFIRcHVibGljSWRlbnRpdHlLZXkSIwoN' + 'c2lnbmVkX3ByZWtleRgEIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ25lZF9wcmVrZXlfc2lnbm' + 'F0dXJlGAUgASgMUhVzaWduZWRQcmVrZXlTaWduYXR1cmUSKAoQc2lnbmVkX3ByZWtleV9pZBgG' + 'IAEoA1IOc2lnbmVkUHJla2V5SWQSJwoPcmVnaXN0cmF0aW9uX2lkGAcgASgDUg5yZWdpc3RyYX' + 'Rpb25JZBIVCgZpc19pb3MYCCABKAhSBWlzSW9zEhsKCWxhbmdfY29kZRgJIAEoCVIIbGFuZ0Nv' + 'ZGUSIgoNcHJvb2Zfb2Zfd29yaxgKIAEoA1ILcHJvb2ZPZldvcmsSJAoLbG9naW5fdG9rZW4YCy' + 'ABKAxIAVIKbG9naW5Ub2tlbogBAUIOCgxfaW52aXRlX2NvZGVCDgoMX2xvZ2luX3Rva2VuGhIK' + 'EEdldEF1dGhDaGFsbGVuZ2UaMQoTR2V0VXNlcklkQnlVc2VybmFtZRIaCgh1c2VybmFtZRgBIA' + 'EoCVIIdXNlcm5hbWUaQwoMR2V0QXV0aFRva2VuEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIa' + 'CghyZXNwb25zZRgCIAEoDFIIcmVzcG9uc2Ua6AEKDEF1dGhlbnRpY2F0ZRIXCgd1c2VyX2lkGA' + 'EgASgDUgZ1c2VySWQSHQoKYXV0aF90b2tlbhgCIAEoDFIJYXV0aFRva2VuEiQKC2FwcF92ZXJz' + 'aW9uGAMgASgJSABSCmFwcFZlcnNpb26IAQESIAoJZGV2aWNlX2lkGAQgASgDSAFSCGRldmljZU' + 'lkiAEBEigKDWluX2JhY2tncm91bmQYBSABKAhIAlIMaW5CYWNrZ3JvdW5kiAEBQg4KDF9hcHBf' + 'dmVyc2lvbkIMCgpfZGV2aWNlX2lkQhAKDl9pbl9iYWNrZ3JvdW5kGsYBChpBdXRoZW50aWNhdG' + 'VXaXRoTG9naW5Ub2tlbhIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSLAoSc2VjcmV0X2xvZ2lu' + 'X3Rva2VuGAIgASgMUhBzZWNyZXRMb2dpblRva2VuEh8KC2FwcF92ZXJzaW9uGAMgASgJUgphcH' + 'BWZXJzaW9uEhsKCWRldmljZV9pZBgEIAEoA1IIZGV2aWNlSWQSIwoNaW5fYmFja2dyb3VuZBgF' + 'IAEoCFIMaW5CYWNrZ3JvdW5kGqwCCiNHZXRTZXJ2ZXJLZXlGb3JQYXNzd29yZExlc3NSZWNvdm' + 'VyeRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSOQoZZW5jcnlwdGVkX3NlcnZlcl9rZXlfbm9u' + 'ZRgCIAEoDFIWZW5jcnlwdGVkU2VydmVyS2V5Tm9uZRItChBwaW5fdW5sb2NrX3Rva2VuGAMgAS' + 'gMSABSDnBpblVubG9ja1Rva2VuiAEBEjEKEnBpbl9wcm90ZWN0aW9uX2tleRgEIAEoDEgBUhBw' + 'aW5Qcm90ZWN0aW9uS2V5iAEBEhkKBWVtYWlsGAUgASgJSAJSBWVtYWlsiAEBQhMKEV9waW5fdW' + '5sb2NrX3Rva2VuQhUKE19waW5fcHJvdGVjdGlvbl9rZXlCCAoGX2VtYWlsGssBCiBSZWdpc3Rl' + 'clBhc3N3b3JkbGVzc05vdGlmaWNhdGlvbhInCg9ub3RpZmljYXRpb25faWQYASABKAlSDm5vdG' + 'lmaWNhdGlvbklkEi4KE2Rvd25sb2FkX2F1dGhfdG9rZW4YAiABKAxSEWRvd25sb2FkQXV0aFRv' + 'a2VuEhsKCWxhbmdfY29kZRgDIAEoCVIIbGFuZ0NvZGUSIgoKZ29vZ2xlX2ZjbRgEIAEoCUgAUg' + 'lnb29nbGVGY22IAQFCDQoLX2dvb2dsZV9mY20avAEKIENoZWNrRm9yUGFzc3dvcmRsZXNzTm90' + 'aWZpY2F0aW9uEicKD25vdGlmaWNhdGlvbl9pZBgBIAEoCVIObm90aWZpY2F0aW9uSWQSLgoTZG' + '93bmxvYWRfYXV0aF90b2tlbhgCIAEoDFIRZG93bmxvYWRBdXRoVG9rZW4SPwocYWxyZWFkeV9y' + 'ZWNlaXZlZF9tZXNzYWdlX2lkcxgDIAMoA1IZYWxyZWFkeVJlY2VpdmVkTWVzc2FnZUlkc0ILCg' + 'lIYW5kc2hha2U='); @$core.Deprecated('Use applicationDataDescriptor instead') const ApplicationData$json = { @@ -575,6 +715,24 @@ const ApplicationData$json = { '9': 0, '10': 'setLoginToken' }, + { + '1': 'register_passwordless_recovery', + '3': 31, + '4': 1, + '5': 11, + '6': '.client_to_server.ApplicationData.RegisterPasswordLessRecovery', + '9': 0, + '10': 'registerPasswordlessRecovery' + }, + { + '1': 'passwordless_notification', + '3': 32, + '4': 1, + '5': 11, + '6': '.client_to_server.ApplicationData.PasswordlessNotification', + '9': 0, + '10': 'passwordlessNotification' + }, ], '3': [ ApplicationData_TextMessage$json, @@ -596,7 +754,9 @@ const ApplicationData$json = { ApplicationData_DeleteAccount$json, ApplicationData_AddAdditionalUser$json, ApplicationData_SetLoginToken$json, - ApplicationData_Deprecated$json + ApplicationData_Deprecated$json, + ApplicationData_RegisterPasswordLessRecovery$json, + ApplicationData_PasswordlessNotification$json ], '8': [ {'1': 'ApplicationData'}, @@ -775,6 +935,47 @@ const ApplicationData_Deprecated$json = { '1': 'Deprecated', }; +@$core.Deprecated('Use applicationDataDescriptor instead') +const ApplicationData_RegisterPasswordLessRecovery$json = { + '1': 'RegisterPasswordLessRecovery', + '2': [ + { + '1': 'encryptedServerKey', + '3': 1, + '4': 1, + '5': 12, + '10': 'encryptedServerKey' + }, + { + '1': 'pinUnlockToken', + '3': 2, + '4': 1, + '5': 12, + '9': 0, + '10': 'pinUnlockToken', + '17': true + }, + ], + '8': [ + {'1': '_pinUnlockToken'}, + ], +}; + +@$core.Deprecated('Use applicationDataDescriptor instead') +const ApplicationData_PasswordlessNotification$json = { + '1': 'PasswordlessNotification', + '2': [ + {'1': 'notification_id', '3': 1, '4': 1, '5': 9, '10': 'notificationId'}, + { + '1': 'encrypted_message', + '3': 2, + '4': 1, + '5': 12, + '10': 'encryptedMessage' + }, + ], +}; + /// Descriptor for `ApplicationData`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode( 'Cg9BcHBsaWNhdGlvbkRhdGESUQoLdGV4dE1lc3NhZ2UYASABKAsyLS5jbGllbnRfdG9fc2Vydm' @@ -820,27 +1021,36 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode( 'dmVBZGRpdGlvbmFsVXNlchJjChFhZGRBZGRpdGlvbmFsVXNlchgdIAEoCzIzLmNsaWVudF90b1' '9zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkFkZEFkZGl0aW9uYWxVc2VySABSEWFkZEFkZGl0aW9u' 'YWxVc2VyElkKD3NldF9sb2dpbl90b2tlbhgeIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbG' - 'ljYXRpb25EYXRhLlNldExvZ2luVG9rZW5IAFINc2V0TG9naW5Ub2tlbhpqCgtUZXh0TWVzc2Fn' - 'ZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEoDFIEYm9keRIgCglwdXNoX2' - 'RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YRovChFHZXRVc2VyQnlVc2Vy' - 'bmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hhbmdlVXNlcm5hbWUSGgoIdX' - 'Nlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCmdvb2ds' - 'ZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEgASgDUg' - 'Z1c2VySWQaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFwoVR2V0QWRkQWNjb3VudHNJbnZpdGVzGhUK' - 'E0dldEN1cnJlbnRQbGFuSW5mb3MaLwoUUmVtb3ZlQWRkaXRpb25hbFVzZXISFwoHdXNlcl9pZB' - 'gBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCgd1c2VyX2lkGAEgASgDUgZ1' - 'c2VySWQaMgoXR2V0U2lnbmVkUHJlS2V5QnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlck' - 'lkGpsBChJVcGRhdGVTaWduZWRQcmVLZXkSKAoQc2lnbmVkX3ByZWtleV9pZBgBIAEoA1IOc2ln' - 'bmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtleRgCIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ2' - '5lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUhVzaWduZWRQcmVrZXlTaWduYXR1cmUaNQoMRG93' - 'bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3dubG9hZFRva2VuGk4KClJlcG' - '9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgBIAEoA1IOcmVwb3J0ZWRVc2VySWQSFgoGcmVh' - 'c29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2hhc2USHQoKcHJvZHVjdF9pZBgBIAEoCVIJcH' - 'JvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291cmNlEisKEXZlcmlmaWNhdGlvbl9kYXRhGAMg' - 'ASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQUZvcmNlQ2hlY2saDwoNRGVsZXRlQWNjb3VudB' - 'osChFBZGRBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaMAoNU2V0TG9n' - 'aW5Ub2tlbhIfCgtsb2dpbl90b2tlbhgBIAEoDFIKbG9naW5Ub2tlbhoMCgpEZXByZWNhdGVkQh' - 'EKD0FwcGxpY2F0aW9uRGF0YQ=='); + 'ljYXRpb25EYXRhLlNldExvZ2luVG9rZW5IAFINc2V0TG9naW5Ub2tlbhKGAQoecmVnaXN0ZXJf' + 'cGFzc3dvcmRsZXNzX3JlY292ZXJ5GB8gASgLMj4uY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdG' + 'lvbkRhdGEuUmVnaXN0ZXJQYXNzd29yZExlc3NSZWNvdmVyeUgAUhxyZWdpc3RlclBhc3N3b3Jk' + 'bGVzc1JlY292ZXJ5EnkKGXBhc3N3b3JkbGVzc19ub3RpZmljYXRpb24YICABKAsyOi5jbGllbn' + 'RfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5QYXNzd29yZGxlc3NOb3RpZmljYXRpb25IAFIY' + 'cGFzc3dvcmRsZXNzTm90aWZpY2F0aW9uGmoKC1RleHRNZXNzYWdlEhcKB3VzZXJfaWQYASABKA' + 'NSBnVzZXJJZBISCgRib2R5GAMgASgMUgRib2R5EiAKCXB1c2hfZGF0YRgEIAEoDEgAUghwdXNo' + 'RGF0YYgBAUIMCgpfcHVzaF9kYXRhGi8KEUdldFVzZXJCeVVzZXJuYW1lEhoKCHVzZXJuYW1lGA' + 'EgASgJUgh1c2VybmFtZRosCg5DaGFuZ2VVc2VybmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNl' + 'cm5hbWUaNQoUVXBkYXRlR29vZ2xlRmNtVG9rZW4SHQoKZ29vZ2xlX2ZjbRgBIAEoCVIJZ29vZ2' + 'xlRmNtGiYKC0dldFVzZXJCeUlkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBoTChFHZXRBdmFp' + 'bGFibGVQbGFucxoXChVHZXRBZGRBY2NvdW50c0ludml0ZXMaFQoTR2V0Q3VycmVudFBsYW5Jbm' + 'ZvcxovChRSZW1vdmVBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaLQoS' + 'R2V0UHJla2V5c0J5VXNlcklkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBoyChdHZXRTaWduZW' + 'RQcmVLZXlCeVVzZXJJZBIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQamwEKElVwZGF0ZVNpZ25l' + 'ZFByZUtleRIoChBzaWduZWRfcHJla2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1zaW' + 'duZWRfcHJla2V5GAIgASgMUgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1' + 'cmUYAyABKAxSFXNpZ25lZFByZWtleVNpZ25hdHVyZRo1CgxEb3dubG9hZERvbmUSJQoOZG93bm' + 'xvYWRfdG9rZW4YASABKAxSDWRvd25sb2FkVG9rZW4aTgoKUmVwb3J0VXNlchIoChByZXBvcnRl' + 'ZF91c2VyX2lkGAEgASgDUg5yZXBvcnRlZFVzZXJJZBIWCgZyZWFzb24YAiABKAlSBnJlYXNvbh' + 'pxCgtJUEFQdXJjaGFzZRIdCgpwcm9kdWN0X2lkGAEgASgJUglwcm9kdWN0SWQSFgoGc291cmNl' + 'GAIgASgJUgZzb3VyY2USKwoRdmVyaWZpY2F0aW9uX2RhdGEYAyABKAlSEHZlcmlmaWNhdGlvbk' + 'RhdGEaDwoNSVBBRm9yY2VDaGVjaxoPCg1EZWxldGVBY2NvdW50GiwKEUFkZEFkZGl0aW9uYWxV' + 'c2VyEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBowCg1TZXRMb2dpblRva2VuEh8KC2xvZ2luX3' + 'Rva2VuGAEgASgMUgpsb2dpblRva2VuGgwKCkRlcHJlY2F0ZWQajgEKHFJlZ2lzdGVyUGFzc3dv' + 'cmRMZXNzUmVjb3ZlcnkSLgoSZW5jcnlwdGVkU2VydmVyS2V5GAEgASgMUhJlbmNyeXB0ZWRTZX' + 'J2ZXJLZXkSKwoOcGluVW5sb2NrVG9rZW4YAiABKAxIAFIOcGluVW5sb2NrVG9rZW6IAQFCEQoP' + 'X3BpblVubG9ja1Rva2VuGnAKGFBhc3N3b3JkbGVzc05vdGlmaWNhdGlvbhInCg9ub3RpZmljYX' + 'Rpb25faWQYASABKAlSDm5vdGlmaWNhdGlvbklkEisKEWVuY3J5cHRlZF9tZXNzYWdlGAIgASgM' + 'UhBlbmNyeXB0ZWRNZXNzYWdlQhEKD0FwcGxpY2F0aW9uRGF0YQ=='); @$core.Deprecated('Use responseDescriptor instead') const Response$json = { diff --git a/lib/src/model/protobuf/api/websocket/error.pbenum.dart b/lib/src/model/protobuf/api/websocket/error.pbenum.dart index 6b30ef0a..29107e9d 100644 --- a/lib/src/model/protobuf/api/websocket/error.pbenum.dart +++ b/lib/src/model/protobuf/api/websocket/error.pbenum.dart @@ -93,6 +93,10 @@ class ErrorCode extends $pb.ProtobufEnum { ErrorCode._(1035, _omitEnumNames ? '' : 'UserIsNotInFreePlan'); static const ErrorCode ForegroundSessionConnected = ErrorCode._(1036, _omitEnumNames ? '' : 'ForegroundSessionConnected'); + static const ErrorCode NoRecoveryData = + ErrorCode._(1037, _omitEnumNames ? '' : 'NoRecoveryData'); + static const ErrorCode InvalidRecoveryData = + ErrorCode._(1038, _omitEnumNames ? '' : 'InvalidRecoveryData'); static const $core.List values = [ Unknown, @@ -134,6 +138,8 @@ class ErrorCode extends $pb.ProtobufEnum { IPAPaymentExpired, UserIsNotInFreePlan, ForegroundSessionConnected, + NoRecoveryData, + InvalidRecoveryData, ]; static final $core.Map<$core.int, ErrorCode> _byValue = diff --git a/lib/src/model/protobuf/api/websocket/error.pbjson.dart b/lib/src/model/protobuf/api/websocket/error.pbjson.dart index 4f58fd73..f1b421ab 100644 --- a/lib/src/model/protobuf/api/websocket/error.pbjson.dart +++ b/lib/src/model/protobuf/api/websocket/error.pbjson.dart @@ -58,6 +58,8 @@ const ErrorCode$json = { {'1': 'IPAPaymentExpired', '2': 1034}, {'1': 'UserIsNotInFreePlan', '2': 1035}, {'1': 'ForegroundSessionConnected', '2': 1036}, + {'1': 'NoRecoveryData', '2': 1037}, + {'1': 'InvalidRecoveryData', '2': 1038}, ], }; @@ -80,4 +82,5 @@ final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode( 'EIUIEhcKEkFwcFZlcnNpb25PdXRkYXRlZBCGCBIYChNOZXdEZXZpY2VSZWdpc3RlcmVkEIcIEh' 'cKEkludmFsaWRQcm9vZk9mV29yaxCICBIZChRSZWdpc3RyYXRpb25EaXNhYmxlZBCJCBIWChFJ' 'UEFQYXltZW50RXhwaXJlZBCKCBIYChNVc2VySXNOb3RJbkZyZWVQbGFuEIsIEh8KGkZvcmVncm' - '91bmRTZXNzaW9uQ29ubmVjdGVkEIwI'); + '91bmRTZXNzaW9uQ29ubmVjdGVkEIwIEhMKDk5vUmVjb3ZlcnlEYXRhEI0IEhgKE0ludmFsaWRS' + 'ZWNvdmVyeURhdGEQjgg='); diff --git a/lib/src/model/protobuf/api/websocket/server_to_client.pb.dart b/lib/src/model/protobuf/api/websocket/server_to_client.pb.dart index 6c10ff5d..fdeb45a6 100644 --- a/lib/src/model/protobuf/api/websocket/server_to_client.pb.dart +++ b/lib/src/model/protobuf/api/websocket/server_to_client.pb.dart @@ -1505,6 +1505,136 @@ class Response_ProofOfWork extends $pb.GeneratedMessage { void clearDifficulty() => $_clearField(2); } +class Response_PasswordlessNotificationMessage extends $pb.GeneratedMessage { + factory Response_PasswordlessNotificationMessage({ + $fixnum.Int64? id, + $core.List<$core.int>? encryptedMessage, + }) { + final result = create(); + if (id != null) result.id = id; + if (encryptedMessage != null) result.encryptedMessage = encryptedMessage; + return result; + } + + Response_PasswordlessNotificationMessage._(); + + factory Response_PasswordlessNotificationMessage.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Response_PasswordlessNotificationMessage.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Response.PasswordlessNotificationMessage', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'), + createEmptyInstance: create) + ..aInt64(1, _omitFieldNames ? '' : 'id') + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'encryptedMessage', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Response_PasswordlessNotificationMessage clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Response_PasswordlessNotificationMessage copyWith( + void Function(Response_PasswordlessNotificationMessage) updates) => + super.copyWith((message) => + updates(message as Response_PasswordlessNotificationMessage)) + as Response_PasswordlessNotificationMessage; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Response_PasswordlessNotificationMessage create() => + Response_PasswordlessNotificationMessage._(); + @$core.override + Response_PasswordlessNotificationMessage createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Response_PasswordlessNotificationMessage getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + Response_PasswordlessNotificationMessage>(create); + static Response_PasswordlessNotificationMessage? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get id => $_getI64(0); + @$pb.TagNumber(1) + set id($fixnum.Int64 value) => $_setInt64(0, value); + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get encryptedMessage => $_getN(1); + @$pb.TagNumber(2) + set encryptedMessage($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasEncryptedMessage() => $_has(1); + @$pb.TagNumber(2) + void clearEncryptedMessage() => $_clearField(2); +} + +class Response_PasswordlessNotificationMessages extends $pb.GeneratedMessage { + factory Response_PasswordlessNotificationMessages({ + $core.Iterable? messages, + }) { + final result = create(); + if (messages != null) result.messages.addAll(messages); + return result; + } + + Response_PasswordlessNotificationMessages._(); + + factory Response_PasswordlessNotificationMessages.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Response_PasswordlessNotificationMessages.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Response.PasswordlessNotificationMessages', + package: + const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'messages', + subBuilder: Response_PasswordlessNotificationMessage.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Response_PasswordlessNotificationMessages clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Response_PasswordlessNotificationMessages copyWith( + void Function(Response_PasswordlessNotificationMessages) updates) => + super.copyWith((message) => + updates(message as Response_PasswordlessNotificationMessages)) + as Response_PasswordlessNotificationMessages; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Response_PasswordlessNotificationMessages create() => + Response_PasswordlessNotificationMessages._(); + @$core.override + Response_PasswordlessNotificationMessages createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Response_PasswordlessNotificationMessages getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + Response_PasswordlessNotificationMessages>(create); + static Response_PasswordlessNotificationMessages? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get messages => + $_getList(0); +} + enum Response_Ok_Ok { none, userid, @@ -1521,6 +1651,8 @@ enum Response_Ok_Ok { downloadtokens, signedprekey, proofOfWork, + passwordlessRecoveryServerKey, + passwordlessNotificationMessages, notSet } @@ -1541,6 +1673,8 @@ class Response_Ok extends $pb.GeneratedMessage { Response_DownloadTokens? downloadtokens, Response_SignedPreKey? signedprekey, Response_ProofOfWork? proofOfWork, + $core.List<$core.int>? passwordlessRecoveryServerKey, + Response_PasswordlessNotificationMessages? passwordlessNotificationMessages, }) { final result = create(); if (none != null) result.none = none; @@ -1559,6 +1693,11 @@ class Response_Ok extends $pb.GeneratedMessage { if (downloadtokens != null) result.downloadtokens = downloadtokens; if (signedprekey != null) result.signedprekey = signedprekey; if (proofOfWork != null) result.proofOfWork = proofOfWork; + if (passwordlessRecoveryServerKey != null) + result.passwordlessRecoveryServerKey = passwordlessRecoveryServerKey; + if (passwordlessNotificationMessages != null) + result.passwordlessNotificationMessages = + passwordlessNotificationMessages; return result; } @@ -1587,6 +1726,8 @@ class Response_Ok extends $pb.GeneratedMessage { 13: Response_Ok_Ok.downloadtokens, 14: Response_Ok_Ok.signedprekey, 15: Response_Ok_Ok.proofOfWork, + 16: Response_Ok_Ok.passwordlessRecoveryServerKey, + 17: Response_Ok_Ok.passwordlessNotificationMessages, 0: Response_Ok_Ok.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo( @@ -1594,7 +1735,7 @@ class Response_Ok extends $pb.GeneratedMessage { package: const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'), createEmptyInstance: create) - ..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) + ..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]) ..aOB(1, _omitFieldNames ? '' : 'None', protoName: 'None') ..aInt64(2, _omitFieldNames ? '' : 'userid') ..a<$core.List<$core.int>>( @@ -1624,6 +1765,13 @@ class Response_Ok extends $pb.GeneratedMessage { subBuilder: Response_SignedPreKey.create) ..aOM(15, _omitFieldNames ? '' : 'proofOfWork', protoName: 'proofOfWork', subBuilder: Response_ProofOfWork.create) + ..a<$core.List<$core.int>>( + 16, + _omitFieldNames ? '' : 'passwordlessRecoveryServerKey', + $pb.PbFieldType.OY) + ..aOM( + 17, _omitFieldNames ? '' : 'passwordlessNotificationMessages', + subBuilder: Response_PasswordlessNotificationMessages.create) ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -1660,6 +1808,8 @@ class Response_Ok extends $pb.GeneratedMessage { @$pb.TagNumber(13) @$pb.TagNumber(14) @$pb.TagNumber(15) + @$pb.TagNumber(16) + @$pb.TagNumber(17) Response_Ok_Ok whichOk() => _Response_Ok_OkByTag[$_whichOneof(0)]!; @$pb.TagNumber(1) @$pb.TagNumber(2) @@ -1676,6 +1826,8 @@ class Response_Ok extends $pb.GeneratedMessage { @$pb.TagNumber(13) @$pb.TagNumber(14) @$pb.TagNumber(15) + @$pb.TagNumber(16) + @$pb.TagNumber(17) void clearOk() => $_clearField($_whichOneof(0)); @$pb.TagNumber(1) @@ -1835,6 +1987,31 @@ class Response_Ok extends $pb.GeneratedMessage { void clearProofOfWork() => $_clearField(15); @$pb.TagNumber(15) Response_ProofOfWork ensureProofOfWork() => $_ensure(14); + + @$pb.TagNumber(16) + $core.List<$core.int> get passwordlessRecoveryServerKey => $_getN(15); + @$pb.TagNumber(16) + set passwordlessRecoveryServerKey($core.List<$core.int> value) => + $_setBytes(15, value); + @$pb.TagNumber(16) + $core.bool hasPasswordlessRecoveryServerKey() => $_has(15); + @$pb.TagNumber(16) + void clearPasswordlessRecoveryServerKey() => $_clearField(16); + + @$pb.TagNumber(17) + Response_PasswordlessNotificationMessages + get passwordlessNotificationMessages => $_getN(16); + @$pb.TagNumber(17) + set passwordlessNotificationMessages( + Response_PasswordlessNotificationMessages value) => + $_setField(17, value); + @$pb.TagNumber(17) + $core.bool hasPasswordlessNotificationMessages() => $_has(16); + @$pb.TagNumber(17) + void clearPasswordlessNotificationMessages() => $_clearField(17); + @$pb.TagNumber(17) + Response_PasswordlessNotificationMessages + ensurePasswordlessNotificationMessages() => $_ensure(16); } enum Response_Response { ok, error, notSet } diff --git a/lib/src/model/protobuf/api/websocket/server_to_client.pbjson.dart b/lib/src/model/protobuf/api/websocket/server_to_client.pbjson.dart index 5a936e2b..5a400286 100644 --- a/lib/src/model/protobuf/api/websocket/server_to_client.pbjson.dart +++ b/lib/src/model/protobuf/api/websocket/server_to_client.pbjson.dart @@ -176,6 +176,8 @@ const Response$json = { Response_UploadToken$json, Response_DownloadTokens$json, Response_ProofOfWork$json, + Response_PasswordlessNotificationMessage$json, + Response_PasswordlessNotificationMessages$json, Response_Ok$json ], '8': [ @@ -509,6 +511,36 @@ const Response_ProofOfWork$json = { ], }; +@$core.Deprecated('Use responseDescriptor instead') +const Response_PasswordlessNotificationMessage$json = { + '1': 'PasswordlessNotificationMessage', + '2': [ + {'1': 'id', '3': 1, '4': 1, '5': 3, '10': 'id'}, + { + '1': 'encrypted_message', + '3': 2, + '4': 1, + '5': 12, + '10': 'encryptedMessage' + }, + ], +}; + +@$core.Deprecated('Use responseDescriptor instead') +const Response_PasswordlessNotificationMessages$json = { + '1': 'PasswordlessNotificationMessages', + '2': [ + { + '1': 'messages', + '3': 1, + '4': 3, + '5': 11, + '6': '.server_to_client.Response.PasswordlessNotificationMessage', + '10': 'messages' + }, + ], +}; + @$core.Deprecated('Use responseDescriptor instead') const Response_Ok$json = { '1': 'Ok', @@ -623,6 +655,23 @@ const Response_Ok$json = { '9': 0, '10': 'proofOfWork' }, + { + '1': 'passwordless_recovery_server_key', + '3': 16, + '4': 1, + '5': 12, + '9': 0, + '10': 'passwordlessRecoveryServerKey' + }, + { + '1': 'passwordless_notification_messages', + '3': 17, + '4': 1, + '5': 11, + '6': '.server_to_client.Response.PasswordlessNotificationMessages', + '9': 0, + '10': 'passwordlessNotificationMessages' + }, ], '8': [ {'1': 'Ok'}, @@ -677,22 +726,29 @@ final $typed_data.Uint8List responseDescriptor = $convert.base64Decode( 'cKD2Rvd25sb2FkX3Rva2VucxgCIAMoDFIOZG93bmxvYWRUb2tlbnMaOQoORG93bmxvYWRUb2tl' 'bnMSJwoPZG93bmxvYWRfdG9rZW5zGAEgAygMUg5kb3dubG9hZFRva2VucxpFCgtQcm9vZk9mV2' '9yaxIWCgZwcmVmaXgYASABKAlSBnByZWZpeBIeCgpkaWZmaWN1bHR5GAIgASgDUgpkaWZmaWN1' - 'bHR5GtcHCgJPaxIUCgROb25lGAEgASgISABSBE5vbmUSGAoGdXNlcmlkGAIgASgDSABSBnVzZX' - 'JpZBImCg1hdXRoY2hhbGxlbmdlGAMgASgMSABSDWF1dGhjaGFsbGVuZ2USSgoLdXBsb2FkdG9r' - 'ZW4YBCABKAsyJi5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlVwbG9hZFRva2VuSABSC3VwbG' - '9hZHRva2VuEkEKCHVzZXJkYXRhGAUgASgLMiMuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5V' - 'c2VyRGF0YUgAUgh1c2VyZGF0YRIeCglhdXRodG9rZW4YBiABKAxIAFIJYXV0aHRva2VuEkoKDG' - 'RlcHJlY2F0ZWRfNxgHIAEoCzIlLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuRGVwcmVjYXRl' - 'ZEgAUgtkZXByZWNhdGVkNxJQCg1hdXRoZW50aWNhdGVkGAggASgLMiguc2VydmVyX3RvX2NsaW' - 'VudC5SZXNwb25zZS5BdXRoZW50aWNhdGVkSABSDWF1dGhlbnRpY2F0ZWQSOAoFcGxhbnMYCSAB' - 'KAsyIC5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlBsYW5zSABSBXBsYW5zEk0KDHBsYW5iYW' - 'xsYW5jZRgKIAEoCzInLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUGxhbkJhbGxhbmNlSABS' - 'DHBsYW5iYWxsYW5jZRJMCg1kZXByZWNhdGVkXzExGAsgASgLMiUuc2VydmVyX3RvX2NsaWVudC' - '5SZXNwb25zZS5EZXByZWNhdGVkSABSDGRlcHJlY2F0ZWQxMRJfChJhZGRhY2NvdW50c2ludml0' - 'ZXMYDCABKAsyLS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkFkZEFjY291bnRzSW52aXRlc0' - 'gAUhJhZGRhY2NvdW50c2ludml0ZXMSUwoOZG93bmxvYWR0b2tlbnMYDSABKAsyKS5zZXJ2ZXJf' - 'dG9fY2xpZW50LlJlc3BvbnNlLkRvd25sb2FkVG9rZW5zSABSDmRvd25sb2FkdG9rZW5zEk0KDH' - 'NpZ25lZHByZWtleRgOIAEoCzInLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuU2lnbmVkUHJl' - 'S2V5SABSDHNpZ25lZHByZWtleRJKCgtwcm9vZk9mV29yaxgPIAEoCzImLnNlcnZlcl90b19jbG' - 'llbnQuUmVzcG9uc2UuUHJvb2ZPZldvcmtIAFILcHJvb2ZPZldvcmtCBAoCT2tCCgoIUmVzcG9u' - 'c2U='); + 'bHR5Gl4KH1Bhc3N3b3JkbGVzc05vdGlmaWNhdGlvbk1lc3NhZ2USDgoCaWQYASABKANSAmlkEi' + 'sKEWVuY3J5cHRlZF9tZXNzYWdlGAIgASgMUhBlbmNyeXB0ZWRNZXNzYWdlGnoKIFBhc3N3b3Jk' + 'bGVzc05vdGlmaWNhdGlvbk1lc3NhZ2VzElYKCG1lc3NhZ2VzGAEgAygLMjouc2VydmVyX3RvX2' + 'NsaWVudC5SZXNwb25zZS5QYXNzd29yZGxlc3NOb3RpZmljYXRpb25NZXNzYWdlUghtZXNzYWdl' + 'cxqwCQoCT2sSFAoETm9uZRgBIAEoCEgAUgROb25lEhgKBnVzZXJpZBgCIAEoA0gAUgZ1c2VyaW' + 'QSJgoNYXV0aGNoYWxsZW5nZRgDIAEoDEgAUg1hdXRoY2hhbGxlbmdlEkoKC3VwbG9hZHRva2Vu' + 'GAQgASgLMiYuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5VcGxvYWRUb2tlbkgAUgt1cGxvYW' + 'R0b2tlbhJBCgh1c2VyZGF0YRgFIAEoCzIjLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuVXNl' + 'ckRhdGFIAFIIdXNlcmRhdGESHgoJYXV0aHRva2VuGAYgASgMSABSCWF1dGh0b2tlbhJKCgxkZX' + 'ByZWNhdGVkXzcYByABKAsyJS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkRlcHJlY2F0ZWRI' + 'AFILZGVwcmVjYXRlZDcSUAoNYXV0aGVudGljYXRlZBgIIAEoCzIoLnNlcnZlcl90b19jbGllbn' + 'QuUmVzcG9uc2UuQXV0aGVudGljYXRlZEgAUg1hdXRoZW50aWNhdGVkEjgKBXBsYW5zGAkgASgL' + 'MiAuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QbGFuc0gAUgVwbGFucxJNCgxwbGFuYmFsbG' + 'FuY2UYCiABKAsyJy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlBsYW5CYWxsYW5jZUgAUgxw' + 'bGFuYmFsbGFuY2USTAoNZGVwcmVjYXRlZF8xMRgLIAEoCzIlLnNlcnZlcl90b19jbGllbnQuUm' + 'VzcG9uc2UuRGVwcmVjYXRlZEgAUgxkZXByZWNhdGVkMTESXwoSYWRkYWNjb3VudHNpbnZpdGVz' + 'GAwgASgLMi0uc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5BZGRBY2NvdW50c0ludml0ZXNIAF' + 'ISYWRkYWNjb3VudHNpbnZpdGVzElMKDmRvd25sb2FkdG9rZW5zGA0gASgLMikuc2VydmVyX3Rv' + 'X2NsaWVudC5SZXNwb25zZS5Eb3dubG9hZFRva2Vuc0gAUg5kb3dubG9hZHRva2VucxJNCgxzaW' + 'duZWRwcmVrZXkYDiABKAsyJy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlNpZ25lZFByZUtl' + 'eUgAUgxzaWduZWRwcmVrZXkSSgoLcHJvb2ZPZldvcmsYDyABKAsyJi5zZXJ2ZXJfdG9fY2xpZW' + '50LlJlc3BvbnNlLlByb29mT2ZXb3JrSABSC3Byb29mT2ZXb3JrEkkKIHBhc3N3b3JkbGVzc19y' + 'ZWNvdmVyeV9zZXJ2ZXJfa2V5GBAgASgMSABSHXBhc3N3b3JkbGVzc1JlY292ZXJ5U2VydmVyS2' + 'V5EosBCiJwYXNzd29yZGxlc3Nfbm90aWZpY2F0aW9uX21lc3NhZ2VzGBEgASgLMjsuc2VydmVy' + 'X3RvX2NsaWVudC5SZXNwb25zZS5QYXNzd29yZGxlc3NOb3RpZmljYXRpb25NZXNzYWdlc0gAUi' + 'BwYXNzd29yZGxlc3NOb3RpZmljYXRpb25NZXNzYWdlc0IECgJPa0IKCghSZXNwb25zZQ=='); diff --git a/lib/src/model/protobuf/client/generated/messages.pb.dart b/lib/src/model/protobuf/client/generated/messages.pb.dart index e805edfb..c81430ad 100644 --- a/lib/src/model/protobuf/client/generated/messages.pb.dart +++ b/lib/src/model/protobuf/client/generated/messages.pb.dart @@ -1839,6 +1839,154 @@ class EncryptedContent_KeyVerificationProof extends $pb.GeneratedMessage { void clearCalculatedMac() => $_clearField(1); } +class EncryptedContent_PasswordLessRecovery extends $pb.GeneratedMessage { + factory EncryptedContent_PasswordLessRecovery({ + $core.List<$core.int>? recoverySecretShare, + $core.bool? delete, + $fixnum.Int64? threshold, + }) { + final result = create(); + if (recoverySecretShare != null) + result.recoverySecretShare = recoverySecretShare; + if (delete != null) result.delete = delete; + if (threshold != null) result.threshold = threshold; + return result; + } + + EncryptedContent_PasswordLessRecovery._(); + + factory EncryptedContent_PasswordLessRecovery.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory EncryptedContent_PasswordLessRecovery.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'EncryptedContent.PasswordLessRecovery', + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 1, _omitFieldNames ? '' : 'recoverySecretShare', $pb.PbFieldType.OY, + protoName: 'recoverySecretShare') + ..aOB(2, _omitFieldNames ? '' : 'delete') + ..aInt64(3, _omitFieldNames ? '' : 'threshold') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + EncryptedContent_PasswordLessRecovery clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + EncryptedContent_PasswordLessRecovery copyWith( + void Function(EncryptedContent_PasswordLessRecovery) updates) => + super.copyWith((message) => + updates(message as EncryptedContent_PasswordLessRecovery)) + as EncryptedContent_PasswordLessRecovery; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedContent_PasswordLessRecovery create() => + EncryptedContent_PasswordLessRecovery._(); + @$core.override + EncryptedContent_PasswordLessRecovery createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static EncryptedContent_PasswordLessRecovery getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + EncryptedContent_PasswordLessRecovery>(create); + static EncryptedContent_PasswordLessRecovery? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get recoverySecretShare => $_getN(0); + @$pb.TagNumber(1) + set recoverySecretShare($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(1) + $core.bool hasRecoverySecretShare() => $_has(0); + @$pb.TagNumber(1) + void clearRecoverySecretShare() => $_clearField(1); + + @$pb.TagNumber(2) + $core.bool get delete => $_getBF(1); + @$pb.TagNumber(2) + set delete($core.bool value) => $_setBool(1, value); + @$pb.TagNumber(2) + $core.bool hasDelete() => $_has(1); + @$pb.TagNumber(2) + void clearDelete() => $_clearField(2); + + @$pb.TagNumber(3) + $fixnum.Int64 get threshold => $_getI64(2); + @$pb.TagNumber(3) + set threshold($fixnum.Int64 value) => $_setInt64(2, value); + @$pb.TagNumber(3) + $core.bool hasThreshold() => $_has(2); + @$pb.TagNumber(3) + void clearThreshold() => $_clearField(3); +} + +class EncryptedContent_PasswordLessRecoveryHeartbeat + extends $pb.GeneratedMessage { + factory EncryptedContent_PasswordLessRecoveryHeartbeat({ + $core.List<$core.int>? hash, + }) { + final result = create(); + if (hash != null) result.hash = hash; + return result; + } + + EncryptedContent_PasswordLessRecoveryHeartbeat._(); + + factory EncryptedContent_PasswordLessRecoveryHeartbeat.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory EncryptedContent_PasswordLessRecoveryHeartbeat.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'EncryptedContent.PasswordLessRecoveryHeartbeat', + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 1, _omitFieldNames ? '' : 'hash', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + EncryptedContent_PasswordLessRecoveryHeartbeat clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + EncryptedContent_PasswordLessRecoveryHeartbeat copyWith( + void Function(EncryptedContent_PasswordLessRecoveryHeartbeat) + updates) => + super.copyWith((message) => updates( + message as EncryptedContent_PasswordLessRecoveryHeartbeat)) + as EncryptedContent_PasswordLessRecoveryHeartbeat; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedContent_PasswordLessRecoveryHeartbeat create() => + EncryptedContent_PasswordLessRecoveryHeartbeat._(); + @$core.override + EncryptedContent_PasswordLessRecoveryHeartbeat createEmptyInstance() => + create(); + @$core.pragma('dart2js:noInline') + static EncryptedContent_PasswordLessRecoveryHeartbeat getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + EncryptedContent_PasswordLessRecoveryHeartbeat>(create); + static EncryptedContent_PasswordLessRecoveryHeartbeat? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get hash => $_getN(0); + @$pb.TagNumber(1) + set hash($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(1) + $core.bool hasHash() => $_has(0); + @$pb.TagNumber(1) + void clearHash() => $_clearField(1); +} + class EncryptedContent extends $pb.GeneratedMessage { factory EncryptedContent({ $core.String? groupId, @@ -1865,6 +2013,9 @@ class EncryptedContent extends $pb.GeneratedMessage { EncryptedContent_UserDiscoveryUpdate? userDiscoveryUpdate, EncryptedContent_KeyVerificationProof? keyVerificationProof, $core.bool? askForFriendPromotions, + EncryptedContent_PasswordLessRecovery? passwordlessRecovery, + EncryptedContent_PasswordLessRecoveryHeartbeat? + passwordlessRecoveryHeartbeat, }) { final result = create(); if (groupId != null) result.groupId = groupId; @@ -1899,6 +2050,10 @@ class EncryptedContent extends $pb.GeneratedMessage { result.keyVerificationProof = keyVerificationProof; if (askForFriendPromotions != null) result.askForFriendPromotions = askForFriendPromotions; + if (passwordlessRecovery != null) + result.passwordlessRecovery = passwordlessRecovery; + if (passwordlessRecoveryHeartbeat != null) + result.passwordlessRecoveryHeartbeat = passwordlessRecoveryHeartbeat; return result; } @@ -1971,6 +2126,12 @@ class EncryptedContent extends $pb.GeneratedMessage { 24, _omitFieldNames ? '' : 'keyVerificationProof', subBuilder: EncryptedContent_KeyVerificationProof.create) ..aOB(25, _omitFieldNames ? '' : 'askForFriendPromotions') + ..aOM( + 26, _omitFieldNames ? '' : 'passwordlessRecovery', + subBuilder: EncryptedContent_PasswordLessRecovery.create) + ..aOM( + 27, _omitFieldNames ? '' : 'passwordlessRecoveryHeartbeat', + subBuilder: EncryptedContent_PasswordLessRecoveryHeartbeat.create) ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -2263,6 +2424,34 @@ class EncryptedContent extends $pb.GeneratedMessage { $core.bool hasAskForFriendPromotions() => $_has(23); @$pb.TagNumber(25) void clearAskForFriendPromotions() => $_clearField(25); + + @$pb.TagNumber(26) + EncryptedContent_PasswordLessRecovery get passwordlessRecovery => $_getN(24); + @$pb.TagNumber(26) + set passwordlessRecovery(EncryptedContent_PasswordLessRecovery value) => + $_setField(26, value); + @$pb.TagNumber(26) + $core.bool hasPasswordlessRecovery() => $_has(24); + @$pb.TagNumber(26) + void clearPasswordlessRecovery() => $_clearField(26); + @$pb.TagNumber(26) + EncryptedContent_PasswordLessRecovery ensurePasswordlessRecovery() => + $_ensure(24); + + @$pb.TagNumber(27) + EncryptedContent_PasswordLessRecoveryHeartbeat + get passwordlessRecoveryHeartbeat => $_getN(25); + @$pb.TagNumber(27) + set passwordlessRecoveryHeartbeat( + EncryptedContent_PasswordLessRecoveryHeartbeat value) => + $_setField(27, value); + @$pb.TagNumber(27) + $core.bool hasPasswordlessRecoveryHeartbeat() => $_has(25); + @$pb.TagNumber(27) + void clearPasswordlessRecoveryHeartbeat() => $_clearField(27); + @$pb.TagNumber(27) + EncryptedContent_PasswordLessRecoveryHeartbeat + ensurePasswordlessRecoveryHeartbeat() => $_ensure(25); } const $core.bool _omitFieldNames = diff --git a/lib/src/model/protobuf/client/generated/messages.pbjson.dart b/lib/src/model/protobuf/client/generated/messages.pbjson.dart index 96f73817..229661ab 100644 --- a/lib/src/model/protobuf/client/generated/messages.pbjson.dart +++ b/lib/src/model/protobuf/client/generated/messages.pbjson.dart @@ -385,6 +385,26 @@ const EncryptedContent$json = { '10': 'keyVerificationProof', '17': true }, + { + '1': 'passwordless_recovery', + '3': 26, + '4': 1, + '5': 11, + '6': '.EncryptedContent.PasswordLessRecovery', + '9': 24, + '10': 'passwordlessRecovery', + '17': true + }, + { + '1': 'passwordless_recovery_heartbeat', + '3': 27, + '4': 1, + '5': 11, + '6': '.EncryptedContent.PasswordLessRecoveryHeartbeat', + '9': 25, + '10': 'passwordlessRecoveryHeartbeat', + '17': true + }, ], '3': [ EncryptedContent_ErrorMessages$json, @@ -405,7 +425,9 @@ const EncryptedContent$json = { EncryptedContent_TypingIndicator$json, EncryptedContent_UserDiscoveryRequest$json, EncryptedContent_UserDiscoveryUpdate$json, - EncryptedContent_KeyVerificationProof$json + EncryptedContent_KeyVerificationProof$json, + EncryptedContent_PasswordLessRecovery$json, + EncryptedContent_PasswordLessRecoveryHeartbeat$json ], '8': [ {'1': '_group_id'}, @@ -432,6 +454,8 @@ const EncryptedContent$json = { {'1': '_user_discovery_request'}, {'1': '_user_discovery_update'}, {'1': '_key_verification_proof'}, + {'1': '_passwordless_recovery'}, + {'1': '_passwordless_recovery_heartbeat'}, ], }; @@ -955,6 +979,35 @@ const EncryptedContent_KeyVerificationProof$json = { ], }; +@$core.Deprecated('Use encryptedContentDescriptor instead') +const EncryptedContent_PasswordLessRecovery$json = { + '1': 'PasswordLessRecovery', + '2': [ + { + '1': 'recoverySecretShare', + '3': 1, + '4': 1, + '5': 12, + '9': 0, + '10': 'recoverySecretShare', + '17': true + }, + {'1': 'delete', '3': 2, '4': 1, '5': 8, '10': 'delete'}, + {'1': 'threshold', '3': 3, '4': 1, '5': 3, '10': 'threshold'}, + ], + '8': [ + {'1': '_recoverySecretShare'}, + ], +}; + +@$core.Deprecated('Use encryptedContentDescriptor instead') +const EncryptedContent_PasswordLessRecoveryHeartbeat$json = { + '1': 'PasswordLessRecoveryHeartbeat', + '2': [ + {'1': 'hash', '3': 1, '4': 1, '5': 12, '10': 'hash'}, + ], +}; + /// Descriptor for `EncryptedContent`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode( 'ChBFbmNyeXB0ZWRDb250ZW50Eh4KCGdyb3VwX2lkGAIgASgJSABSB2dyb3VwSWSIAQESKQoOaX' @@ -988,79 +1041,88 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode( 'aXNjb3ZlcnlSZXF1ZXN0iAEBEl4KFXVzZXJfZGlzY292ZXJ5X3VwZGF0ZRgXIAEoCzIlLkVuY3' 'J5cHRlZENvbnRlbnQuVXNlckRpc2NvdmVyeVVwZGF0ZUgWUhN1c2VyRGlzY292ZXJ5VXBkYXRl' 'iAEBEmEKFmtleV92ZXJpZmljYXRpb25fcHJvb2YYGCABKAsyJi5FbmNyeXB0ZWRDb250ZW50Lk' - 'tleVZlcmlmaWNhdGlvblByb29mSBdSFGtleVZlcmlmaWNhdGlvblByb29miAEBGpYCCg1FcnJv' - 'ck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZX' - 'MuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRfaWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0' - 'SWQinAEKBFR5cGUSPAo4RVJST1JfUFJPQ0VTU0lOR19NRVNTQUdFX0NSRUFURURfQUNDT1VOVF' - '9SRVFVRVNUX0lOU1RFQUQQABIYChRVTktOT1dOX01FU1NBR0VfVFlQRRACEhcKE1NFU1NJT05f' - 'T1VUX09GX1NZTkMQAxIjCh9HUk9VUF9OT1RfRk9VTkRfT1JfTk9UX0FfTUVNQkVSEAQahwEKC0' - 'dyb3VwQ3JlYXRlEhsKCXN0YXRlX2tleRgDIAEoDFIIc3RhdGVLZXkSKAoQZ3JvdXBfcHVibGlj' - 'X2tleRgEIAEoDFIOZ3JvdXBQdWJsaWNLZXkSIgoKZ3JvdXBfbmFtZRgFIAEoCUgAUglncm91cE' - '5hbWWIAQFCDQoLX2dyb3VwX25hbWUaNQoJR3JvdXBKb2luEigKEGdyb3VwX3B1YmxpY19rZXkY' - 'ASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3VwUHVibGljS2V5GsgCCgtHcm91cF' - 'VwZGF0ZRIqChFncm91cF9hY3Rpb25fdHlwZRgBIAEoCVIPZ3JvdXBBY3Rpb25UeXBlEjMKE2Fm' - 'ZmVjdGVkX2NvbnRhY3RfaWQYAiABKANIAFIRYWZmZWN0ZWRDb250YWN0SWSIAQESKQoObmV3X2' - 'dyb3VwX25hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElcKJm5ld19kZWxldGVfbWVzc2Fn' - 'ZXNfYWZ0ZXJfbWlsbGlzZWNvbmRzGAQgASgDSAJSIm5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaW' - 'xsaXNlY29uZHOIAQFCFgoUX2FmZmVjdGVkX2NvbnRhY3RfaWRCEQoPX25ld19ncm91cF9uYW1l' - 'QikKJ19uZXdfZGVsZXRlX21lc3NhZ2VzX2FmdGVyX21pbGxpc2Vjb25kcxqvAQoLVGV4dE1lc3' - 'NhZ2USKgoRc2VuZGVyX21lc3NhZ2VfaWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBISCgR0ZXh0' - 'GAIgASgJUgR0ZXh0EhwKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1wEi0KEHF1b3RlX21lc3' - 'NhZ2VfaWQYBCABKAlIAFIOcXVvdGVNZXNzYWdlSWSIAQFCEwoRX3F1b3RlX21lc3NhZ2VfaWQa' - 'zgEKFUFkZGl0aW9uYWxEYXRhTWVzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2' - 'VuZGVyTWVzc2FnZUlkEhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhIKBHR5cGUYAyAB' - 'KAlSBHR5cGUSOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYBCABKAxIAFIVYWRkaXRpb25hbE' - '1lc3NhZ2VEYXRhiAEBQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRpkCghSZWFjdGlvbhIq' - 'ChF0YXJnZXRfbWVzc2FnZV9pZBgBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEhQKBWVtb2ppGAIgAS' - 'gJUgVlbW9qaRIWCgZyZW1vdmUYAyABKAhSBnJlbW92ZRq+AgoNTWVzc2FnZVVwZGF0ZRI4CgR0' - 'eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5NZXNzYWdlVXBkYXRlLlR5cGVSBHR5cGUSLw' - 'oRc2VuZGVyX21lc3NhZ2VfaWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlkiAEBEj0KG211bHRp' - 'cGxlX3RhcmdldF9tZXNzYWdlX2lkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYWdlSWRzEh' - 'cKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVzdGFtcCIt' - 'CgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhQKEl9zZW5kZX' - 'JfbWVzc2FnZV9pZEIHCgVfdGV4dBqFBgoFTWVkaWESKgoRc2VuZGVyX21lc3NhZ2VfaWQYASAB' - 'KAlSD3NlbmRlck1lc3NhZ2VJZBIwCgR0eXBlGAIgASgOMhwuRW5jcnlwdGVkQ29udGVudC5NZW' - 'RpYS5UeXBlUgR0eXBlEkYKHWRpc3BsYXlfbGltaXRfaW5fbWlsbGlzZWNvbmRzGAMgASgDSABS' - 'GmRpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRziAEBEjcKF3JlcXVpcmVzX2F1dGhlbnRpY2F0aW' - '9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGlt' - 'ZXN0YW1wEi0KEHF1b3RlX21lc3NhZ2VfaWQYBiABKAlIAVIOcXVvdGVNZXNzYWdlSWSIAQESKg' - 'oOZG93bmxvYWRfdG9rZW4YByABKAxIAlINZG93bmxvYWRUb2tlbogBARIqCg5lbmNyeXB0aW9u' - 'X2tleRgIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEioKDmVuY3J5cHRpb25fbWFjGAkgASgMSA' - 'RSDWVuY3J5cHRpb25NYWOIAQESLgoQZW5jcnlwdGlvbl9ub25jZRgKIAEoDEgFUg9lbmNyeXB0' - 'aW9uTm9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYCyABKAxIBlIVYWRkaXRpb2' - '5hbE1lc3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUkVVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJ' - 'REVPEAISBwoDR0lGEAMSCQoFQVVESU8QBEIgCh5fZGlzcGxheV9saW1pdF9pbl9taWxsaXNlY2' - '9uZHNCEwoRX3F1b3RlX21lc3NhZ2VfaWRCEQoPX2Rvd25sb2FkX3Rva2VuQhEKD19lbmNyeXB0' - 'aW9uX2tleUIRCg9fZW5jcnlwdGlvbl9tYWNCEwoRX2VuY3J5cHRpb25fbm9uY2VCGgoYX2FkZG' - 'l0aW9uYWxfbWVzc2FnZV9kYXRhGqkBCgtNZWRpYVVwZGF0ZRI2CgR0eXBlGAEgASgOMiIuRW5j' - 'cnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEioKEXRhcmdldF9tZXNzYWdlX2' - 'lkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JF' - 'RBABEhQKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgAS' - 'gOMiUuRW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUS' - 'CwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0VQVBACGqQCCg1Db250YWN0VXBkYXRlEj' - 'gKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlw' - 'ZRI3ChVhdmF0YXJfc3ZnX2NvbXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZI' - 'gBARIfCgh1c2VybmFtZRgDIAEoCUgBUgh1c2VybmFtZYgBARImCgxkaXNwbGF5X25hbWUYBCAB' - 'KAlIAlILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCGA' - 'oWX2F2YXRhcl9zdmdfY29tcHJlc3NlZEILCglfdXNlcm5hbWVCDwoNX2Rpc3BsYXlfbmFtZRrZ' - 'AQoIUHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3J5cHRlZENvbnRlbnQuUHVzaEtleXMuVH' - 'lwZVIEdHlwZRIaCgZrZXlfaWQYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5GAMgASgMSAFSA2tl' - 'eYgBARIiCgpjcmVhdGVkX2F0GAQgASgDSAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUV' - 'VFU1QQABIKCgZVUERBVEUQAUIJCgdfa2V5X2lkQgYKBF9rZXlCDQoLX2NyZWF0ZWRfYXQarwEK' - 'CUZsYW1lU3luYxIjCg1mbGFtZV9jb3VudGVyGAEgASgDUgxmbGFtZUNvdW50ZXISOQoZbGFzdF' - '9mbGFtZV9jb3VudGVyX2NoYW5nZRgCIAEoA1IWbGFzdEZsYW1lQ291bnRlckNoYW5nZRIfCgti' - 'ZXN0X2ZyaWVuZBgDIAEoCFIKYmVzdEZyaWVuZBIhCgxmb3JjZV91cGRhdGUYBCABKAhSC2Zvcm' - 'NlVXBkYXRlGk0KD1R5cGluZ0luZGljYXRvchIbCglpc190eXBpbmcYASABKAhSCGlzVHlwaW5n' - 'Eh0KCmNyZWF0ZWRfYXQYAiABKANSCWNyZWF0ZWRBdBo/ChRVc2VyRGlzY292ZXJ5UmVxdWVzdB' - 'InCg9jdXJyZW50X3ZlcnNpb24YASABKAxSDmN1cnJlbnRWZXJzaW9uGjEKE1VzZXJEaXNjb3Zl' - 'cnlVcGRhdGUSGgoIbWVzc2FnZXMYASADKAxSCG1lc3NhZ2VzGj0KFEtleVZlcmlmaWNhdGlvbl' - 'Byb29mEiUKDmNhbGN1bGF0ZWRfbWFjGAEgASgMUg1jYWxjdWxhdGVkTWFjQgsKCV9ncm91cF9p' - 'ZEIRCg9faXNfZGlyZWN0X2NoYXRCGQoXX3NlbmRlcl9wcm9maWxlX2NvdW50ZXJCIAoeX3Nlbm' - 'Rlcl91c2VyX2Rpc2NvdmVyeV92ZXJzaW9uQhwKGl9hc2tfZm9yX2ZyaWVuZF9wcm9tb3Rpb25z' - 'QhEKD19tZXNzYWdlX3VwZGF0ZUIICgZfbWVkaWFCDwoNX21lZGlhX3VwZGF0ZUIRCg9fY29udG' - 'FjdF91cGRhdGVCEgoQX2NvbnRhY3RfcmVxdWVzdEINCgtfZmxhbWVfc3luY0IMCgpfcHVzaF9r' - 'ZXlzQgsKCV9yZWFjdGlvbkIPCg1fdGV4dF9tZXNzYWdlQg8KDV9ncm91cF9jcmVhdGVCDQoLX2' - 'dyb3VwX2pvaW5CDwoNX2dyb3VwX3VwZGF0ZUIaChhfcmVzZW5kX2dyb3VwX3B1YmxpY19rZXlC' - 'EQoPX2Vycm9yX21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFfbWVzc2FnZUITChFfdHlwaW' - '5nX2luZGljYXRvckIZChdfdXNlcl9kaXNjb3ZlcnlfcmVxdWVzdEIYChZfdXNlcl9kaXNjb3Zl' - 'cnlfdXBkYXRlQhkKF19rZXlfdmVyaWZpY2F0aW9uX3Byb29m'); + 'tleVZlcmlmaWNhdGlvblByb29mSBdSFGtleVZlcmlmaWNhdGlvblByb29miAEBEmAKFXBhc3N3' + 'b3JkbGVzc19yZWNvdmVyeRgaIAEoCzImLkVuY3J5cHRlZENvbnRlbnQuUGFzc3dvcmRMZXNzUm' + 'Vjb3ZlcnlIGFIUcGFzc3dvcmRsZXNzUmVjb3ZlcnmIAQESfAofcGFzc3dvcmRsZXNzX3JlY292' + 'ZXJ5X2hlYXJ0YmVhdBgbIAEoCzIvLkVuY3J5cHRlZENvbnRlbnQuUGFzc3dvcmRMZXNzUmVjb3' + 'ZlcnlIZWFydGJlYXRIGVIdcGFzc3dvcmRsZXNzUmVjb3ZlcnlIZWFydGJlYXSIAQEalgIKDUVy' + 'cm9yTWVzc2FnZXMSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNzYW' + 'dlcy5UeXBlUgR0eXBlEiwKEnJlbGF0ZWRfcmVjZWlwdF9pZBgCIAEoCVIQcmVsYXRlZFJlY2Vp' + 'cHRJZCKcAQoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX01FU1NBR0VfQ1JFQVRFRF9BQ0NPVU' + '5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVTU0FHRV9UWVBFEAISFwoTU0VTU0lP' + 'Tl9PVVRfT0ZfU1lOQxADEiMKH0dST1VQX05PVF9GT1VORF9PUl9OT1RfQV9NRU1CRVIQBBqHAQ' + 'oLR3JvdXBDcmVhdGUSGwoJc3RhdGVfa2V5GAMgASgMUghzdGF0ZUtleRIoChBncm91cF9wdWJs' + 'aWNfa2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRIiCgpncm91cF9uYW1lGAUgASgJSABSCWdyb3' + 'VwTmFtZYgBAUINCgtfZ3JvdXBfbmFtZRo1CglHcm91cEpvaW4SKAoQZ3JvdXBfcHVibGljX2tl' + 'eRgBIAEoDFIOZ3JvdXBQdWJsaWNLZXkaFgoUUmVzZW5kR3JvdXBQdWJsaWNLZXkayAIKC0dyb3' + 'VwVXBkYXRlEioKEWdyb3VwX2FjdGlvbl90eXBlGAEgASgJUg9ncm91cEFjdGlvblR5cGUSMwoT' + 'YWZmZWN0ZWRfY29udGFjdF9pZBgCIAEoA0gAUhFhZmZlY3RlZENvbnRhY3RJZIgBARIpCg5uZX' + 'dfZ3JvdXBfbmFtZRgDIAEoCUgBUgxuZXdHcm91cE5hbWWIAQESVwombmV3X2RlbGV0ZV9tZXNz' + 'YWdlc19hZnRlcl9taWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTWVzc2FnZXNBZnRlck' + '1pbGxpc2Vjb25kc4gBAUIWChRfYWZmZWN0ZWRfY29udGFjdF9pZEIRCg9fbmV3X2dyb3VwX25h' + 'bWVCKQonX25ld19kZWxldGVfbWVzc2FnZXNfYWZ0ZXJfbWlsbGlzZWNvbmRzGq8BCgtUZXh0TW' + 'Vzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVyTWVzc2FnZUlkEhIKBHRl' + 'eHQYAiABKAlSBHRleHQSHAoJdGltZXN0YW1wGAMgASgDUgl0aW1lc3RhbXASLQoQcXVvdGVfbW' + 'Vzc2FnZV9pZBgEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUITChFfcXVvdGVfbWVzc2FnZV9p' + 'ZBrOAQoVQWRkaXRpb25hbERhdGFNZXNzYWdlEioKEXNlbmRlcl9tZXNzYWdlX2lkGAEgASgJUg' + '9zZW5kZXJNZXNzYWdlSWQSHAoJdGltZXN0YW1wGAIgASgDUgl0aW1lc3RhbXASEgoEdHlwZRgD' + 'IAEoCVIEdHlwZRI7ChdhZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRgEIAEoDEgAUhVhZGRpdGlvbm' + 'FsTWVzc2FnZURhdGGIAQFCGgoYX2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGmQKCFJlYWN0aW9u' + 'EioKEXRhcmdldF9tZXNzYWdlX2lkGAEgASgJUg90YXJnZXRNZXNzYWdlSWQSFAoFZW1vamkYAi' + 'ABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVtb3ZlGr4CCg1NZXNzYWdlVXBkYXRlEjgK' + 'BHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk1lc3NhZ2VVcGRhdGUuVHlwZVIEdHlwZR' + 'IvChFzZW5kZXJfbWVzc2FnZV9pZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESPQobbXVs' + 'dGlwbGVfdGFyZ2V0X21lc3NhZ2VfaWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZH' + 'MSFwoEdGV4dBgEIAEoCUgBUgR0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1w' + 'Ii0KBFR5cGUSCgoGREVMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCFAoSX3Nlbm' + 'Rlcl9tZXNzYWdlX2lkQgcKBV90ZXh0GoUGCgVNZWRpYRIqChFzZW5kZXJfbWVzc2FnZV9pZBgB' + 'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW50Lk' + '1lZGlhLlR5cGVSBHR5cGUSRgodZGlzcGxheV9saW1pdF9pbl9taWxsaXNlY29uZHMYAyABKANI' + 'AFIaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNwoXcmVxdWlyZXNfYXV0aGVudGljYX' + 'Rpb24YBCABKAhSFnJlcXVpcmVzQXV0aGVudGljYXRpb24SHAoJdGltZXN0YW1wGAUgASgDUgl0' + 'aW1lc3RhbXASLQoQcXVvdGVfbWVzc2FnZV9pZBgGIAEoCUgBUg5xdW90ZU1lc3NhZ2VJZIgBAR' + 'IqCg5kb3dubG9hZF90b2tlbhgHIAEoDEgCUg1kb3dubG9hZFRva2VuiAEBEioKDmVuY3J5cHRp' + 'b25fa2V5GAggASgMSANSDWVuY3J5cHRpb25LZXmIAQESKgoOZW5jcnlwdGlvbl9tYWMYCSABKA' + 'xIBFINZW5jcnlwdGlvbk1hY4gBARIuChBlbmNyeXB0aW9uX25vbmNlGAogASgMSAVSD2VuY3J5' + 'cHRpb25Ob25jZYgBARI7ChdhZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRgLIAEoDEgGUhVhZGRpdG' + 'lvbmFsTWVzc2FnZURhdGGIAQEiPgoEVHlwZRIMCghSRVVQTE9BRBAAEgkKBUlNQUdFEAESCQoF' + 'VklERU8QAhIHCgNHSUYQAxIJCgVBVURJTxAEQiAKHl9kaXNwbGF5X2xpbWl0X2luX21pbGxpc2' + 'Vjb25kc0ITChFfcXVvdGVfbWVzc2FnZV9pZEIRCg9fZG93bmxvYWRfdG9rZW5CEQoPX2VuY3J5' + 'cHRpb25fa2V5QhEKD19lbmNyeXB0aW9uX21hY0ITChFfZW5jcnlwdGlvbl9ub25jZUIaChhfYW' + 'RkaXRpb25hbF9tZXNzYWdlX2RhdGEaqQEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4yIi5F' + 'bmNyeXB0ZWRDb250ZW50Lk1lZGlhVXBkYXRlLlR5cGVSBHR5cGUSKgoRdGFyZ2V0X21lc3NhZ2' + 'VfaWQYAiABKAlSD3RhcmdldE1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RP' + 'UkVEEAESFAoQREVDUllQVElPTl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYAS' + 'ABKA4yJS5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlw' + 'ZRILCgdSRVFVRVNUEAASCgoGUkVKRUNUEAESCgoGQUNDRVBUEAIapAIKDUNvbnRhY3RVcGRhdG' + 'USOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0' + 'eXBlEjcKFWF2YXRhcl9zdmdfY29tcHJlc3NlZBgCIAEoDEgAUhNhdmF0YXJTdmdDb21wcmVzc2' + 'VkiAEBEh8KCHVzZXJuYW1lGAMgASgJSAFSCHVzZXJuYW1liAEBEiYKDGRpc3BsYXlfbmFtZRgE' + 'IAEoCUgCUgtkaXNwbGF5TmFtZYgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAU' + 'IYChZfYXZhdGFyX3N2Z19jb21wcmVzc2VkQgsKCV91c2VybmFtZUIPCg1fZGlzcGxheV9uYW1l' + 'GtkBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy' + '5UeXBlUgR0eXBlEhoKBmtleV9pZBgCIAEoA0gAUgVrZXlJZIgBARIVCgNrZXkYAyABKAxIAVID' + 'a2V5iAEBEiIKCmNyZWF0ZWRfYXQYBCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBFR5cGUSCwoHUk' + 'VRVUVTVBAAEgoKBlVQREFURRABQgkKB19rZXlfaWRCBgoEX2tleUINCgtfY3JlYXRlZF9hdBqv' + 'AQoJRmxhbWVTeW5jEiMKDWZsYW1lX2NvdW50ZXIYASABKANSDGZsYW1lQ291bnRlchI5ChlsYX' + 'N0X2ZsYW1lX2NvdW50ZXJfY2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh8K' + 'C2Jlc3RfZnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiEKDGZvcmNlX3VwZGF0ZRgEIAEoCFILZm' + '9yY2VVcGRhdGUaTQoPVHlwaW5nSW5kaWNhdG9yEhsKCWlzX3R5cGluZxgBIAEoCFIIaXNUeXBp' + 'bmcSHQoKY3JlYXRlZF9hdBgCIAEoA1IJY3JlYXRlZEF0Gj8KFFVzZXJEaXNjb3ZlcnlSZXF1ZX' + 'N0EicKD2N1cnJlbnRfdmVyc2lvbhgBIAEoDFIOY3VycmVudFZlcnNpb24aMQoTVXNlckRpc2Nv' + 'dmVyeVVwZGF0ZRIaCghtZXNzYWdlcxgBIAMoDFIIbWVzc2FnZXMaPQoUS2V5VmVyaWZpY2F0aW' + '9uUHJvb2YSJQoOY2FsY3VsYXRlZF9tYWMYASABKAxSDWNhbGN1bGF0ZWRNYWMamwEKFFBhc3N3' + 'b3JkTGVzc1JlY292ZXJ5EjUKE3JlY292ZXJ5U2VjcmV0U2hhcmUYASABKAxIAFITcmVjb3Zlcn' + 'lTZWNyZXRTaGFyZYgBARIWCgZkZWxldGUYAiABKAhSBmRlbGV0ZRIcCgl0aHJlc2hvbGQYAyAB' + 'KANSCXRocmVzaG9sZEIWChRfcmVjb3ZlcnlTZWNyZXRTaGFyZRozCh1QYXNzd29yZExlc3NSZW' + 'NvdmVyeUhlYXJ0YmVhdBISCgRoYXNoGAEgASgMUgRoYXNoQgsKCV9ncm91cF9pZEIRCg9faXNf' + 'ZGlyZWN0X2NoYXRCGQoXX3NlbmRlcl9wcm9maWxlX2NvdW50ZXJCIAoeX3NlbmRlcl91c2VyX2' + 'Rpc2NvdmVyeV92ZXJzaW9uQhwKGl9hc2tfZm9yX2ZyaWVuZF9wcm9tb3Rpb25zQhEKD19tZXNz' + 'YWdlX3VwZGF0ZUIICgZfbWVkaWFCDwoNX21lZGlhX3VwZGF0ZUIRCg9fY29udGFjdF91cGRhdG' + 'VCEgoQX2NvbnRhY3RfcmVxdWVzdEINCgtfZmxhbWVfc3luY0IMCgpfcHVzaF9rZXlzQgsKCV9y' + 'ZWFjdGlvbkIPCg1fdGV4dF9tZXNzYWdlQg8KDV9ncm91cF9jcmVhdGVCDQoLX2dyb3VwX2pvaW' + '5CDwoNX2dyb3VwX3VwZGF0ZUIaChhfcmVzZW5kX2dyb3VwX3B1YmxpY19rZXlCEQoPX2Vycm9y' + 'X21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFfbWVzc2FnZUITChFfdHlwaW5nX2luZGljYX' + 'RvckIZChdfdXNlcl9kaXNjb3ZlcnlfcmVxdWVzdEIYChZfdXNlcl9kaXNjb3ZlcnlfdXBkYXRl' + 'QhkKF19rZXlfdmVyaWZpY2F0aW9uX3Byb29mQhgKFl9wYXNzd29yZGxlc3NfcmVjb3ZlcnlCIg' + 'ogX3Bhc3N3b3JkbGVzc19yZWNvdmVyeV9oZWFydGJlYXQ='); diff --git a/lib/src/model/protobuf/client/generated/passwordless_recovery.pb.dart b/lib/src/model/protobuf/client/generated/passwordless_recovery.pb.dart new file mode 100644 index 00000000..ef98afd9 --- /dev/null +++ b/lib/src/model/protobuf/client/generated/passwordless_recovery.pb.dart @@ -0,0 +1,544 @@ +// This is a generated file - do not edit. +// +// Generated from passwordless_recovery.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports + +import 'dart:core' as $core; + +import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:protobuf/protobuf.dart' as $pb; + +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + +/// Send from the person who tries to recover their account. +/// This can be done via a link, which will then be opend in the app of the contact. +/// The contact than has to manualy select from which user he got the request. +/// -> Using this phishing is harder, as the user has to manualy select the user to recovery +/// -> The user who wants to recover his account does not need to remember her old username +class RecoveryRequest extends $pb.GeneratedMessage { + factory RecoveryRequest({ + $core.String? notificationId, + $core.List<$core.int>? publicKey, + }) { + final result = create(); + if (notificationId != null) result.notificationId = notificationId; + if (publicKey != null) result.publicKey = publicKey; + return result; + } + + RecoveryRequest._(); + + factory RecoveryRequest.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory RecoveryRequest.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'RecoveryRequest', + package: const $pb.PackageName( + _omitMessageNames ? '' : 'passwordless_recovery'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'notificationId') + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'publicKey', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RecoveryRequest clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RecoveryRequest copyWith(void Function(RecoveryRequest) updates) => + super.copyWith((message) => updates(message as RecoveryRequest)) + as RecoveryRequest; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RecoveryRequest create() => RecoveryRequest._(); + @$core.override + RecoveryRequest createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static RecoveryRequest getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static RecoveryRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get notificationId => $_getSZ(0); + @$pb.TagNumber(1) + set notificationId($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasNotificationId() => $_has(0); + @$pb.TagNumber(1) + void clearNotificationId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get publicKey => $_getN(1); + @$pb.TagNumber(2) + set publicKey($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasPublicKey() => $_has(1); + @$pb.TagNumber(2) + void clearPublicKey() => $_clearField(2); +} + +/// Used as envelope for TrustedFriendShare and RecoveryData +class EncryptedEnvelope extends $pb.GeneratedMessage { + factory EncryptedEnvelope({ + $core.List<$core.int>? encryptedData, + $core.List<$core.int>? iv, + $core.List<$core.int>? mac, + }) { + final result = create(); + if (encryptedData != null) result.encryptedData = encryptedData; + if (iv != null) result.iv = iv; + if (mac != null) result.mac = mac; + return result; + } + + EncryptedEnvelope._(); + + factory EncryptedEnvelope.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory EncryptedEnvelope.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'EncryptedEnvelope', + package: const $pb.PackageName( + _omitMessageNames ? '' : 'passwordless_recovery'), + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 1, _omitFieldNames ? '' : 'encryptedData', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'iv', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 3, _omitFieldNames ? '' : 'mac', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + EncryptedEnvelope clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + EncryptedEnvelope copyWith(void Function(EncryptedEnvelope) updates) => + super.copyWith((message) => updates(message as EncryptedEnvelope)) + as EncryptedEnvelope; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedEnvelope create() => EncryptedEnvelope._(); + @$core.override + EncryptedEnvelope createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static EncryptedEnvelope getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static EncryptedEnvelope? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get encryptedData => $_getN(0); + @$pb.TagNumber(1) + set encryptedData($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(1) + $core.bool hasEncryptedData() => $_has(0); + @$pb.TagNumber(1) + void clearEncryptedData() => $_clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get iv => $_getN(1); + @$pb.TagNumber(2) + set iv($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(2) + $core.bool hasIv() => $_has(1); + @$pb.TagNumber(2) + void clearIv() => $_clearField(2); + + @$pb.TagNumber(3) + $core.List<$core.int> get mac => $_getN(2); + @$pb.TagNumber(3) + set mac($core.List<$core.int> value) => $_setBytes(2, value); + @$pb.TagNumber(3) + $core.bool hasMac() => $_has(2); + @$pb.TagNumber(3) + void clearMac() => $_clearField(3); +} + +class TrustedFriendShare_User extends $pb.GeneratedMessage { + factory TrustedFriendShare_User({ + $fixnum.Int64? userId, + $core.String? displayName, + $core.List<$core.int>? avatar, + }) { + final result = create(); + if (userId != null) result.userId = userId; + if (displayName != null) result.displayName = displayName; + if (avatar != null) result.avatar = avatar; + return result; + } + + TrustedFriendShare_User._(); + + factory TrustedFriendShare_User.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory TrustedFriendShare_User.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'TrustedFriendShare.User', + package: const $pb.PackageName( + _omitMessageNames ? '' : 'passwordless_recovery'), + createEmptyInstance: create) + ..aInt64(1, _omitFieldNames ? '' : 'userId') + ..aOS(2, _omitFieldNames ? '' : 'displayName') + ..a<$core.List<$core.int>>( + 3, _omitFieldNames ? '' : 'avatar', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + TrustedFriendShare_User clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + TrustedFriendShare_User copyWith( + void Function(TrustedFriendShare_User) updates) => + super.copyWith((message) => updates(message as TrustedFriendShare_User)) + as TrustedFriendShare_User; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static TrustedFriendShare_User create() => TrustedFriendShare_User._(); + @$core.override + TrustedFriendShare_User createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static TrustedFriendShare_User getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static TrustedFriendShare_User? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get userId => $_getI64(0); + @$pb.TagNumber(1) + set userId($fixnum.Int64 value) => $_setInt64(0, value); + @$pb.TagNumber(1) + $core.bool hasUserId() => $_has(0); + @$pb.TagNumber(1) + void clearUserId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get displayName => $_getSZ(1); + @$pb.TagNumber(2) + set displayName($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasDisplayName() => $_has(1); + @$pb.TagNumber(2) + void clearDisplayName() => $_clearField(2); + + @$pb.TagNumber(3) + $core.List<$core.int> get avatar => $_getN(2); + @$pb.TagNumber(3) + set avatar($core.List<$core.int> value) => $_setBytes(2, value); + @$pb.TagNumber(3) + $core.bool hasAvatar() => $_has(2); + @$pb.TagNumber(3) + void clearAvatar() => $_clearField(3); +} + +/// Send from the trusted friend to +/// This is encrypted with the received public key. +class TrustedFriendShare extends $pb.GeneratedMessage { + factory TrustedFriendShare({ + TrustedFriendShare_User? trustedFriend, + TrustedFriendShare_User? shareUser, + $core.int? threshold, + $core.List<$core.int>? sharedSecretData, + }) { + final result = create(); + if (trustedFriend != null) result.trustedFriend = trustedFriend; + if (shareUser != null) result.shareUser = shareUser; + if (threshold != null) result.threshold = threshold; + if (sharedSecretData != null) result.sharedSecretData = sharedSecretData; + return result; + } + + TrustedFriendShare._(); + + factory TrustedFriendShare.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory TrustedFriendShare.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'TrustedFriendShare', + package: const $pb.PackageName( + _omitMessageNames ? '' : 'passwordless_recovery'), + createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'trustedFriend', + subBuilder: TrustedFriendShare_User.create) + ..aOM(2, _omitFieldNames ? '' : 'shareUser', + subBuilder: TrustedFriendShare_User.create) + ..aI(3, _omitFieldNames ? '' : 'threshold') + ..a<$core.List<$core.int>>( + 4, _omitFieldNames ? '' : 'sharedSecretData', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + TrustedFriendShare clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + TrustedFriendShare copyWith(void Function(TrustedFriendShare) updates) => + super.copyWith((message) => updates(message as TrustedFriendShare)) + as TrustedFriendShare; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static TrustedFriendShare create() => TrustedFriendShare._(); + @$core.override + TrustedFriendShare createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static TrustedFriendShare getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static TrustedFriendShare? _defaultInstance; + + /// This allows to display the user which user has send him his recovery data. + @$pb.TagNumber(1) + TrustedFriendShare_User get trustedFriend => $_getN(0); + @$pb.TagNumber(1) + set trustedFriend(TrustedFriendShare_User value) => $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasTrustedFriend() => $_has(0); + @$pb.TagNumber(1) + void clearTrustedFriend() => $_clearField(1); + @$pb.TagNumber(1) + TrustedFriendShare_User ensureTrustedFriend() => $_ensure(0); + + /// This allows to display the userdata, showing that he is recovering the correct person. + @$pb.TagNumber(2) + TrustedFriendShare_User get shareUser => $_getN(1); + @$pb.TagNumber(2) + set shareUser(TrustedFriendShare_User value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasShareUser() => $_has(1); + @$pb.TagNumber(2) + void clearShareUser() => $_clearField(2); + @$pb.TagNumber(2) + TrustedFriendShare_User ensureShareUser() => $_ensure(1); + + /// The minimum threshold required to decrypte the shares. + @$pb.TagNumber(3) + $core.int get threshold => $_getIZ(2); + @$pb.TagNumber(3) + set threshold($core.int value) => $_setSignedInt32(2, value); + @$pb.TagNumber(3) + $core.bool hasThreshold() => $_has(2); + @$pb.TagNumber(3) + void clearThreshold() => $_clearField(3); + + /// The actual share which will become: SharedSecretData + @$pb.TagNumber(4) + $core.List<$core.int> get sharedSecretData => $_getN(3); + @$pb.TagNumber(4) + set sharedSecretData($core.List<$core.int> value) => $_setBytes(3, value); + @$pb.TagNumber(4) + $core.bool hasSharedSecretData() => $_has(3); + @$pb.TagNumber(4) + void clearSharedSecretData() => $_clearField(4); +} + +/// The user identity keys. Can be used to restore the data backup. +class RecoveryData extends $pb.GeneratedMessage { + factory RecoveryData({ + $fixnum.Int64? userId, + $core.List<$core.int>? keyManager, + }) { + final result = create(); + if (userId != null) result.userId = userId; + if (keyManager != null) result.keyManager = keyManager; + return result; + } + + RecoveryData._(); + + factory RecoveryData.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory RecoveryData.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'RecoveryData', + package: const $pb.PackageName( + _omitMessageNames ? '' : 'passwordless_recovery'), + createEmptyInstance: create) + ..aInt64(1, _omitFieldNames ? '' : 'userId') + ..a<$core.List<$core.int>>( + 3, _omitFieldNames ? '' : 'keyManager', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RecoveryData clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RecoveryData copyWith(void Function(RecoveryData) updates) => + super.copyWith((message) => updates(message as RecoveryData)) + as RecoveryData; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RecoveryData create() => RecoveryData._(); + @$core.override + RecoveryData createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static RecoveryData getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static RecoveryData? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get userId => $_getI64(0); + @$pb.TagNumber(1) + set userId($fixnum.Int64 value) => $_setInt64(0, value); + @$pb.TagNumber(1) + $core.bool hasUserId() => $_has(0); + @$pb.TagNumber(1) + void clearUserId() => $_clearField(1); + + @$pb.TagNumber(3) + $core.List<$core.int> get keyManager => $_getN(1); + @$pb.TagNumber(3) + set keyManager($core.List<$core.int> value) => $_setBytes(1, value); + @$pb.TagNumber(3) + $core.bool hasKeyManager() => $_has(1); + @$pb.TagNumber(3) + void clearKeyManager() => $_clearField(3); +} + +/// After received all shares this is decrypted by the user restoring its own +class SharedSecretData extends $pb.GeneratedMessage { + factory SharedSecretData({ + $core.List<$core.int>? recoveryData, + $core.List<$core.int>? encryptedServerKeyNonce, + $core.List<$core.int>? pinSeed, + $core.List<$core.int>? pinUnlockToken, + $core.String? emailHint, + }) { + final result = create(); + if (recoveryData != null) result.recoveryData = recoveryData; + if (encryptedServerKeyNonce != null) + result.encryptedServerKeyNonce = encryptedServerKeyNonce; + if (pinSeed != null) result.pinSeed = pinSeed; + if (pinUnlockToken != null) result.pinUnlockToken = pinUnlockToken; + if (emailHint != null) result.emailHint = emailHint; + return result; + } + + SharedSecretData._(); + + factory SharedSecretData.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SharedSecretData.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SharedSecretData', + package: const $pb.PackageName( + _omitMessageNames ? '' : 'passwordless_recovery'), + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 1, _omitFieldNames ? '' : 'recoveryData', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 3, _omitFieldNames ? '' : 'encryptedServerKeyNonce', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 4, _omitFieldNames ? '' : 'pinSeed', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 5, _omitFieldNames ? '' : 'pinUnlockToken', $pb.PbFieldType.OY) + ..aOS(6, _omitFieldNames ? '' : 'emailHint') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SharedSecretData clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SharedSecretData copyWith(void Function(SharedSecretData) updates) => + super.copyWith((message) => updates(message as SharedSecretData)) + as SharedSecretData; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SharedSecretData create() => SharedSecretData._(); + @$core.override + SharedSecretData createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SharedSecretData getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static SharedSecretData? _defaultInstance; + + /// The recovery data is encrypted in case a second factor was chosen. + @$pb.TagNumber(1) + $core.List<$core.int> get recoveryData => $_getN(0); + @$pb.TagNumber(1) + set recoveryData($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(1) + $core.bool hasRecoveryData() => $_has(0); + @$pb.TagNumber(1) + void clearRecoveryData() => $_clearField(1); + + @$pb.TagNumber(3) + $core.List<$core.int> get encryptedServerKeyNonce => $_getN(1); + @$pb.TagNumber(3) + set encryptedServerKeyNonce($core.List<$core.int> value) => + $_setBytes(1, value); + @$pb.TagNumber(3) + $core.bool hasEncryptedServerKeyNonce() => $_has(1); + @$pb.TagNumber(3) + void clearEncryptedServerKeyNonce() => $_clearField(3); + + @$pb.TagNumber(4) + $core.List<$core.int> get pinSeed => $_getN(2); + @$pb.TagNumber(4) + set pinSeed($core.List<$core.int> value) => $_setBytes(2, value); + @$pb.TagNumber(4) + $core.bool hasPinSeed() => $_has(2); + @$pb.TagNumber(4) + void clearPinSeed() => $_clearField(4); + + @$pb.TagNumber(5) + $core.List<$core.int> get pinUnlockToken => $_getN(3); + @$pb.TagNumber(5) + set pinUnlockToken($core.List<$core.int> value) => $_setBytes(3, value); + @$pb.TagNumber(5) + $core.bool hasPinUnlockToken() => $_has(3); + @$pb.TagNumber(5) + void clearPinUnlockToken() => $_clearField(5); + + @$pb.TagNumber(6) + $core.String get emailHint => $_getSZ(4); + @$pb.TagNumber(6) + set emailHint($core.String value) => $_setString(4, value); + @$pb.TagNumber(6) + $core.bool hasEmailHint() => $_has(4); + @$pb.TagNumber(6) + void clearEmailHint() => $_clearField(6); +} + +const $core.bool _omitFieldNames = + $core.bool.fromEnvironment('protobuf.omit_field_names'); +const $core.bool _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/src/model/protobuf/client/generated/passwordless_recovery.pbenum.dart b/lib/src/model/protobuf/client/generated/passwordless_recovery.pbenum.dart new file mode 100644 index 00000000..ac5dfc09 --- /dev/null +++ b/lib/src/model/protobuf/client/generated/passwordless_recovery.pbenum.dart @@ -0,0 +1,11 @@ +// This is a generated file - do not edit. +// +// Generated from passwordless_recovery.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports diff --git a/lib/src/model/protobuf/client/generated/passwordless_recovery.pbjson.dart b/lib/src/model/protobuf/client/generated/passwordless_recovery.pbjson.dart new file mode 100644 index 00000000..0a12fc1f --- /dev/null +++ b/lib/src/model/protobuf/client/generated/passwordless_recovery.pbjson.dart @@ -0,0 +1,170 @@ +// This is a generated file - do not edit. +// +// Generated from passwordless_recovery.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports +// ignore_for_file: unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use recoveryRequestDescriptor instead') +const RecoveryRequest$json = { + '1': 'RecoveryRequest', + '2': [ + {'1': 'notification_id', '3': 1, '4': 1, '5': 9, '10': 'notificationId'}, + {'1': 'public_key', '3': 2, '4': 1, '5': 12, '10': 'publicKey'}, + ], +}; + +/// Descriptor for `RecoveryRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List recoveryRequestDescriptor = $convert.base64Decode( + 'Cg9SZWNvdmVyeVJlcXVlc3QSJwoPbm90aWZpY2F0aW9uX2lkGAEgASgJUg5ub3RpZmljYXRpb2' + '5JZBIdCgpwdWJsaWNfa2V5GAIgASgMUglwdWJsaWNLZXk='); + +@$core.Deprecated('Use encryptedEnvelopeDescriptor instead') +const EncryptedEnvelope$json = { + '1': 'EncryptedEnvelope', + '2': [ + {'1': 'encrypted_data', '3': 1, '4': 1, '5': 12, '10': 'encryptedData'}, + {'1': 'iv', '3': 2, '4': 1, '5': 12, '10': 'iv'}, + {'1': 'mac', '3': 3, '4': 1, '5': 12, '10': 'mac'}, + ], +}; + +/// Descriptor for `EncryptedEnvelope`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List encryptedEnvelopeDescriptor = $convert.base64Decode( + 'ChFFbmNyeXB0ZWRFbnZlbG9wZRIlCg5lbmNyeXB0ZWRfZGF0YRgBIAEoDFINZW5jcnlwdGVkRG' + 'F0YRIOCgJpdhgCIAEoDFICaXYSEAoDbWFjGAMgASgMUgNtYWM='); + +@$core.Deprecated('Use trustedFriendShareDescriptor instead') +const TrustedFriendShare$json = { + '1': 'TrustedFriendShare', + '2': [ + { + '1': 'trusted_friend', + '3': 1, + '4': 1, + '5': 11, + '6': '.passwordless_recovery.TrustedFriendShare.User', + '10': 'trustedFriend' + }, + { + '1': 'share_user', + '3': 2, + '4': 1, + '5': 11, + '6': '.passwordless_recovery.TrustedFriendShare.User', + '10': 'shareUser' + }, + {'1': 'threshold', '3': 3, '4': 1, '5': 5, '10': 'threshold'}, + { + '1': 'shared_secret_data', + '3': 4, + '4': 1, + '5': 12, + '10': 'sharedSecretData' + }, + ], + '3': [TrustedFriendShare_User$json], +}; + +@$core.Deprecated('Use trustedFriendShareDescriptor instead') +const TrustedFriendShare_User$json = { + '1': 'User', + '2': [ + {'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'}, + {'1': 'display_name', '3': 2, '4': 1, '5': 9, '10': 'displayName'}, + {'1': 'avatar', '3': 3, '4': 1, '5': 12, '10': 'avatar'}, + ], +}; + +/// Descriptor for `TrustedFriendShare`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List trustedFriendShareDescriptor = $convert.base64Decode( + 'ChJUcnVzdGVkRnJpZW5kU2hhcmUSVQoOdHJ1c3RlZF9mcmllbmQYASABKAsyLi5wYXNzd29yZG' + 'xlc3NfcmVjb3ZlcnkuVHJ1c3RlZEZyaWVuZFNoYXJlLlVzZXJSDXRydXN0ZWRGcmllbmQSTQoK' + 'c2hhcmVfdXNlchgCIAEoCzIuLnBhc3N3b3JkbGVzc19yZWNvdmVyeS5UcnVzdGVkRnJpZW5kU2' + 'hhcmUuVXNlclIJc2hhcmVVc2VyEhwKCXRocmVzaG9sZBgDIAEoBVIJdGhyZXNob2xkEiwKEnNo' + 'YXJlZF9zZWNyZXRfZGF0YRgEIAEoDFIQc2hhcmVkU2VjcmV0RGF0YRpaCgRVc2VyEhcKB3VzZX' + 'JfaWQYASABKANSBnVzZXJJZBIhCgxkaXNwbGF5X25hbWUYAiABKAlSC2Rpc3BsYXlOYW1lEhYK' + 'BmF2YXRhchgDIAEoDFIGYXZhdGFy'); + +@$core.Deprecated('Use recoveryDataDescriptor instead') +const RecoveryData$json = { + '1': 'RecoveryData', + '2': [ + {'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'}, + {'1': 'key_manager', '3': 3, '4': 1, '5': 12, '10': 'keyManager'}, + ], +}; + +/// Descriptor for `RecoveryData`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List recoveryDataDescriptor = $convert.base64Decode( + 'CgxSZWNvdmVyeURhdGESFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEh8KC2tleV9tYW5hZ2VyGA' + 'MgASgMUgprZXlNYW5hZ2Vy'); + +@$core.Deprecated('Use sharedSecretDataDescriptor instead') +const SharedSecretData$json = { + '1': 'SharedSecretData', + '2': [ + {'1': 'recovery_data', '3': 1, '4': 1, '5': 12, '10': 'recoveryData'}, + { + '1': 'encrypted_server_key_nonce', + '3': 3, + '4': 1, + '5': 12, + '9': 0, + '10': 'encryptedServerKeyNonce', + '17': true + }, + { + '1': 'pin_seed', + '3': 4, + '4': 1, + '5': 12, + '9': 1, + '10': 'pinSeed', + '17': true + }, + { + '1': 'pin_unlock_token', + '3': 5, + '4': 1, + '5': 12, + '9': 2, + '10': 'pinUnlockToken', + '17': true + }, + { + '1': 'email_hint', + '3': 6, + '4': 1, + '5': 9, + '9': 3, + '10': 'emailHint', + '17': true + }, + ], + '8': [ + {'1': '_encrypted_server_key_nonce'}, + {'1': '_pin_seed'}, + {'1': '_pin_unlock_token'}, + {'1': '_email_hint'}, + ], +}; + +/// Descriptor for `SharedSecretData`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List sharedSecretDataDescriptor = $convert.base64Decode( + 'ChBTaGFyZWRTZWNyZXREYXRhEiMKDXJlY292ZXJ5X2RhdGEYASABKAxSDHJlY292ZXJ5RGF0YR' + 'JAChplbmNyeXB0ZWRfc2VydmVyX2tleV9ub25jZRgDIAEoDEgAUhdlbmNyeXB0ZWRTZXJ2ZXJL' + 'ZXlOb25jZYgBARIeCghwaW5fc2VlZBgEIAEoDEgBUgdwaW5TZWVkiAEBEi0KEHBpbl91bmxvY2' + 'tfdG9rZW4YBSABKAxIAlIOcGluVW5sb2NrVG9rZW6IAQESIgoKZW1haWxfaGludBgGIAEoCUgD' + 'UgllbWFpbEhpbnSIAQFCHQobX2VuY3J5cHRlZF9zZXJ2ZXJfa2V5X25vbmNlQgsKCV9waW5fc2' + 'VlZEITChFfcGluX3VubG9ja190b2tlbkINCgtfZW1haWxfaGludA=='); diff --git a/lib/src/model/protobuf/client/messages.proto b/lib/src/model/protobuf/client/messages.proto index e9ed3067..a209bab2 100644 --- a/lib/src/model/protobuf/client/messages.proto +++ b/lib/src/model/protobuf/client/messages.proto @@ -58,6 +58,8 @@ message EncryptedContent { optional UserDiscoveryRequest user_discovery_request = 22; optional UserDiscoveryUpdate user_discovery_update = 23; optional KeyVerificationProof key_verification_proof = 24; + optional PasswordLessRecovery passwordless_recovery = 26; + optional PasswordLessRecoveryHeartbeat passwordless_recovery_heartbeat = 27; message ErrorMessages { enum Type { @@ -82,9 +84,7 @@ message EncryptedContent { bytes group_public_key = 1; } - message ResendGroupPublicKey { - - } + message ResendGroupPublicKey {} message GroupUpdate { string group_action_type = 1; // GroupActionType.name @@ -217,4 +217,14 @@ message EncryptedContent { bytes calculated_mac = 1; } + message PasswordLessRecovery { + optional bytes recoverySecretShare = 1; + bool delete = 2; + int64 threshold = 3; + } + + message PasswordLessRecoveryHeartbeat { + bytes hash = 1; + } + } \ No newline at end of file diff --git a/rust/src/passwordless_recovery/types.proto b/lib/src/model/protobuf/client/passwordless_recovery.proto similarity index 58% rename from rust/src/passwordless_recovery/types.proto rename to lib/src/model/protobuf/client/passwordless_recovery.proto index ef911d77..39243e19 100644 --- a/rust/src/passwordless_recovery/types.proto +++ b/lib/src/model/protobuf/client/passwordless_recovery.proto @@ -12,7 +12,7 @@ package passwordless_recovery; // -> Using this phishing is harder, as the user has to manualy select the user to recovery // -> The user who wants to recover his account does not need to remember her old username message RecoveryRequest { - int64 temp_id = 1; + string notification_id = 1; bytes public_key = 2; } @@ -46,40 +46,24 @@ message TrustedFriendShare { } } -// After received all shares this is decrypted by the user restoring its own -message SharedSecretData { - - // No second factor was selected - optional RecoveryData recovery_data = 1; - - - // Server has - optional SecondFactorMail second_factor_mail = 2; - optional SecondFactorPin second_factor_pin = 3; - - // The recovery data in case a second factor was used - // The decryption key is loaded from the server either using the PIN or the MAIL - optional bytes recovery_data_encrypted = 4; - - - message SecondFactorPin { - // Required to try the PIN to get the share from the server. - // This prevents that someone else can lock the pin, as the server only - // allows 3 tries then after 1 day again 3 tries until the key is deleted. - bytes unlock_token = 1; - // This never is send to the server but used to hash the pin before sending it to the server. - // This prevents that the server every knows the shot 4-diget PIN. - bytes pin_seed = 2; - } - - message SecondFactorMail {} - -} - -// The data which is recovered at the end. -// The backup_master_key allows to recover the actual backup uploaded in the background to the server. -// In case the backup is not available any more the user can use its user_id and his private_key to requister as a new user. +// The user identity keys. Can be used to restore the data backup. message RecoveryData { int64 user_id = 1; bytes key_manager = 3; } + +// After received all shares this is decrypted by the user restoring its own +message SharedSecretData { + + // The recovery data is encrypted in case a second factor was chosen. + bytes recovery_data = 1; + + optional bytes encrypted_server_key_nonce = 3; + + optional bytes pin_seed = 4; + optional bytes pin_unlock_token = 5; + + optional string email_hint = 6; + +} + diff --git a/lib/src/providers/routing.provider.dart b/lib/src/providers/routing.provider.dart index 55b3ef1e..44228345 100644 --- a/lib/src/providers/routing.provider.dart +++ b/lib/src/providers/routing.provider.dart @@ -13,7 +13,8 @@ import 'package:twonly/src/visual/views/contact/add_new_contact.view.dart'; import 'package:twonly/src/visual/views/contact/contact.view.dart'; import 'package:twonly/src/visual/views/groups/group.view.dart'; import 'package:twonly/src/visual/views/groups/group_create_select_members.view.dart'; -import 'package:twonly/src/visual/views/onboarding/recover.view.dart'; +import 'package:twonly/src/visual/views/onboarding/recover_password.view.dart'; +import 'package:twonly/src/visual/views/onboarding/recover_passwordless.view.dart'; import 'package:twonly/src/visual/views/public_profile.view.dart'; import 'package:twonly/src/visual/views/settings/account.view.dart'; import 'package:twonly/src/visual/views/settings/appearance.view.dart'; @@ -39,7 +40,6 @@ import 'package:twonly/src/visual/views/settings/help/help.view.dart'; import 'package:twonly/src/visual/views/settings/notification.view.dart'; import 'package:twonly/src/visual/views/settings/privacy.view.dart'; import 'package:twonly/src/visual/views/settings/privacy/block_users.view.dart'; -import 'package:twonly/src/visual/views/settings/privacy/profile_selection.view.dart'; import 'package:twonly/src/visual/views/settings/privacy/user_discovery.view.dart'; import 'package:twonly/src/visual/views/settings/profile/modify_avatar.view.dart'; import 'package:twonly/src/visual/views/settings/profile/profile.view.dart'; @@ -58,6 +58,13 @@ final routerProvider = GoRouter( path: Routes.home, builder: (context, state) => const AppMainWidget(initialPage: 1), ), + GoRoute( + path: Routes.recoverPasswordless, + builder: (context, state) { + final token = state.extra as String?; + return RecoverPasswordless(initialEmailToken: token); + }, + ), // Chats GoRoute( @@ -135,7 +142,13 @@ final routerProvider = GoRouter( GoRoute( path: Routes.cameraQRScanner, builder: (context, state) { - return const QrCodeScannerView(); + final extra = state.extra as Map?; + final contact = extra?['contact'] as Contact?; + final openToVerify = extra?['openToVerify'] as bool? ?? false; + return QrCodeScannerView( + contact: contact, + openToVerify: openToVerify, + ); }, ), @@ -206,10 +219,6 @@ final routerProvider = GoRouter( path: 'user_discovery', builder: (context, state) => const UserDiscoverySettingsView(), ), - GoRoute( - path: 'profile_selection', - builder: (context, state) => const ProfileSelectionSettingsView(), - ), ], ), GoRoute( @@ -240,7 +249,9 @@ final routerProvider = GoRouter( routes: [ GoRoute( path: 'verifybadge', - builder: (context, state) => const VerificationBadeFaqView(), + builder: (context, state) => VerificationBadeFaqView( + contact: state.extra as Contact?, + ), ), ], ), diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index 2e2bbc61..b173766f 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -34,6 +34,7 @@ import 'package:twonly/src/services/flame.service.dart'; import 'package:twonly/src/services/group.service.dart'; import 'package:twonly/src/services/notifications/fcm.notifications.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart'; import 'package:twonly/src/services/signal/identity.signal.dart'; import 'package:twonly/src/services/signal/protocol_state.signal.dart'; import 'package:twonly/src/services/signal/utils.signal.dart'; @@ -139,6 +140,7 @@ class ApiService { unawaited(fetchGroupStatesForUnjoinedGroups()); unawaited(fetchMissingGroupPublicKey()); unawaited(checkForDeletedUsernames()); + unawaited(PasswordlessRecoveryService.performHeartbeat()); unawaited(UserDiscoveryService.checkForNewAnnouncedUsers()); @@ -684,11 +686,11 @@ class ApiService { final req = createClientToServerFromHandshake(handshake); final result = await sendRequestSync(req, authenticated: false); if (result.isError) { - Log.error('could not request proof of work params', result); + Log.error('could not request proof of work params', error: result); if (result.error == ErrorCode.RegistrationDisabled) { return (null, true); } - Log.error('could not request proof of work params', result); + Log.error('could not request proof of work params', error: result); return (null, false); } return (result.value.proofOfWork as Response_ProofOfWork, false); @@ -758,6 +760,85 @@ class ApiService { return sendRequestSync(req, contactId: userId.toInt()); } + Future registerPasswordLessRecovery( + List encryptedServerKey, + List? pinUnlockToken, + ) async { + final req = createClientToServerFromApplicationData( + ApplicationData( + registerPasswordlessRecovery: + ApplicationData_RegisterPasswordLessRecovery( + encryptedServerKey: encryptedServerKey, + pinUnlockToken: pinUnlockToken, + ), + ), + ); + return sendRequestSync(req); + } + + Future getServerKeyForPasswordlessRecovery({ + required int userId, + List? encryptedServerKeyNone, + List? pinUnlockToken, + List? pinProtectionKey, + String? email, + }) async { + final get = Handshake_GetServerKeyForPasswordLessRecovery() + ..userId = Int64(userId); + if (encryptedServerKeyNone != null) { + get.encryptedServerKeyNone = encryptedServerKeyNone; + } + if (pinUnlockToken != null) { + get.pinUnlockToken = pinUnlockToken; + } + if (pinProtectionKey != null) { + get.pinProtectionKey = pinProtectionKey; + } + if (email != null) { + get.email = email; + } + + final handshake = Handshake()..getServerKeyForPasswordlessRecovery = get; + final req = createClientToServerFromHandshake(handshake); + return sendRequestSync(req, authenticated: false); + } + + + Future submitRecoveryShare({ + required String notificationId, + required List encryptedMessage, + }) async { + final req = createClientToServerFromApplicationData( + ApplicationData( + passwordlessNotification: ApplicationData_PasswordlessNotification( + notificationId: notificationId, + encryptedMessage: encryptedMessage, + ), + ), + ); + return sendRequestSync(req); + } + + Future registerPasswordlessNotification({ + required String notificationId, + required List downloadAuthToken, + required String langCode, + required String? googleFcm, + }) async { + final registerNotif = Handshake_RegisterPasswordlessNotification() + ..notificationId = notificationId + ..downloadAuthToken = downloadAuthToken + ..langCode = langCode; + if (googleFcm != null) { + registerNotif.googleFcm = googleFcm; + } + + final handshake = Handshake() + ..registerPasswordlessNotification = registerNotif; + final req = createClientToServerFromHandshake(handshake); + return sendRequestSync(req, authenticated: false); + } + Future addAdditionalUser(Int64 userId) async { final get = ApplicationData_AddAdditionalUser()..userId = userId; final appData = ApplicationData()..addAdditionalUser = get; @@ -870,4 +951,30 @@ class ApiService { final req = createClientToServerFromApplicationData(appData); return sendRequestSync(req, contactId: target); } + + /// Polls the server for new passwordless recovery notification messages. + /// [alreadyReceivedIds] prevents the server from sending duplicates. + Future checkForPasswordlessNotification({ + required String notificationId, + required List downloadAuthToken, + List? alreadyReceivedIds, + }) async { + final check = Handshake_CheckForPasswordlessNotification() + ..notificationId = notificationId + ..downloadAuthToken = downloadAuthToken; + if (alreadyReceivedIds != null && alreadyReceivedIds.isNotEmpty) { + check.alreadyReceivedMessageIds.addAll(alreadyReceivedIds); + } + + final handshake = Handshake()..checkForPasswordlessNotification = check; + final req = createClientToServerFromHandshake(handshake); + final res = await sendRequestSync(req, authenticated: false); + if (res.isSuccess) { + final ok = res.value as server.Response_Ok; + if (ok.hasPasswordlessNotificationMessages()) { + return ok.passwordlessNotificationMessages; + } + } + return null; + } } diff --git a/lib/src/services/api/client2client/errors.c2c.dart b/lib/src/services/api/client2client/errors.c2c.dart index 2079475e..4547385b 100644 --- a/lib/src/services/api/client2client/errors.c2c.dart +++ b/lib/src/services/api/client2client/errors.c2c.dart @@ -17,7 +17,7 @@ Future handleErrorMessage( String receiptId, { String? groupId, }) async { - Log.error('[$receiptId] Got error from $fromUserId: $error'); + Log.warn('[$receiptId] Got error from $fromUserId: $error'); switch (error.type) { case EncryptedContent_ErrorMessages_Type @@ -38,14 +38,14 @@ Future handleErrorMessage( break; // The other user initiated a new signal session, so ignore the error in this case, as the new session works... case EncryptedContent_ErrorMessages_Type.GROUP_NOT_FOUND_OR_NOT_A_MEMBER: if (groupId == null) { - Log.error( + Log.warn( '[$receiptId] GROUP_NOT_FOUND_OR_NOT_A_MEMBER error received, but groupId is null.', ); return; } final group = await twonlyDB.groupsDao.getGroup(groupId); if (group == null) { - Log.error( + Log.warn( '[$receiptId] GROUP_NOT_FOUND_OR_NOT_A_MEMBER error received, but group $groupId is not found in database.', ); return; diff --git a/lib/src/services/api/client2client/media.c2c.dart b/lib/src/services/api/client2client/media.c2c.dart index ca282a71..29ab1a62 100644 --- a/lib/src/services/api/client2client/media.c2c.dart +++ b/lib/src/services/api/client2client/media.c2c.dart @@ -14,7 +14,7 @@ import 'package:twonly/src/services/flame.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/log.dart'; -Future handleMedia( +Future handleMedia( int fromUserId, String groupId, EncryptedContent_Media media, @@ -36,7 +36,7 @@ Future handleMedia( Log.warn( '[$receiptId] Got reupload for a message that either does not exists (${message == null}) or senderId = ${message?.senderId}', ); - return; + return false; } // in case there was already a downloaded file delete it... @@ -64,7 +64,7 @@ Future handleMedia( unawaited(startDownloadMedia(mediaFile, false)); } - return; + return true; case EncryptedContent_Media_Type.IMAGE: mediaType = MediaType.image; case EncryptedContent_Media_Type.VIDEO: @@ -85,13 +85,13 @@ Future handleMedia( Log.warn( '[$receiptId] $fromUserId tried to modify the message from ${messageTmp.senderId}.', ); - return; + return false; } if (messageTmp.mediaId == null) { Log.warn( '[$receiptId] This message already exit without a mediaId. Message is dropped.', ); - return; + return false; } final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById( messageTmp.mediaId!, @@ -100,7 +100,7 @@ Future handleMedia( Log.warn( '[$receiptId] This message and media file already exit and was not requested again. Dropping it.', ); - return; + return false; } if (mediaFile != null) { @@ -145,9 +145,13 @@ Future handleMedia( if (mediaFile == null) { Log.error('[$receiptId] Could not insert media file into database'); - return; + return false; } + Log.info( + '[$receiptId] Inserting media message: messageId=${media.senderMessageId}, mediaId=${mediaFile!.mediaId}', + ); + message = await twonlyDB.messagesDao.insertMessage( MessagesCompanion( messageId: Value(media.senderMessageId), @@ -186,6 +190,7 @@ Future handleMedia( ); unawaited(startDownloadMedia(mediaFile!, false)); + return true; } else { if (mediaFile == null && message == null) { Log.error( @@ -200,6 +205,7 @@ Future handleMedia( '[$receiptId] Could not insert new message as the message is empty.', ); } + return false; } } diff --git a/lib/src/services/api/client2client/text_message.c2c.dart b/lib/src/services/api/client2client/text_message.c2c.dart index 7c5cc01c..26cbf46a 100644 --- a/lib/src/services/api/client2client/text_message.c2c.dart +++ b/lib/src/services/api/client2client/text_message.c2c.dart @@ -7,7 +7,7 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/services/api/utils.api.dart'; import 'package:twonly/src/utils/log.dart'; -Future handleTextMessage( +Future handleTextMessage( int fromUserId, String groupId, EncryptedContent_TextMessage textMessage, @@ -26,7 +26,7 @@ Future handleTextMessage( Log.warn( '[$receiptId] $fromUserId tried to overwrite message from ${existing.senderId}. Dropping.', ); - return; + return false; } final message = await twonlyDB.messagesDao.insertMessage( @@ -50,4 +50,5 @@ Future handleTextMessage( if (message != null) { Log.info('[$receiptId] Inserted a new text message with ID: ${message.messageId}'); } + return message != null; } diff --git a/lib/src/services/api/mediafiles/download.api.dart b/lib/src/services/api/mediafiles/download.api.dart index 0218275d..57aa5327 100644 --- a/lib/src/services/api/mediafiles/download.api.dart +++ b/lib/src/services/api/mediafiles/download.api.dart @@ -143,8 +143,12 @@ Future handleDownloadStatusUpdate(TaskStatusUpdate update) async { failed = false; } else { failed = true; + Log.warn( + '[$mediaId] Got invalid response status code: ${update.responseStatusCode}', + ); Log.error( 'Got invalid response status code: ${update.responseStatusCode}', + onlyIfSentryEnabled: true, ); } } else { @@ -227,7 +231,7 @@ Future startDownloadMedia(MediaFile media, bool force) async { try { await downloadFileFast(media, apiUrl, mediaService.encryptedPath); } catch (e) { - Log.error('Fast download failed: $e'); + Log.warn('Fast download failed: $e'); await FileDownloader().enqueue(task); } } catch (e) { @@ -251,9 +255,13 @@ Future downloadFileFast( return; } else { if (response.statusCode == 404 || response.statusCode == 403) { - Log.error( + Log.warn( 'Got ${response.statusCode} from server for media ID ${media.mediaId}. Requesting upload again', ); + Log.error( + 'Got ${response.statusCode} from server for media ID.', + onlyIfSentryEnabled: true, + ); // Message was deleted from the server. Requesting it again from the sender to upload it again... await requestMediaReupload(media.mediaId); return; @@ -264,6 +272,13 @@ Future downloadFileFast( } Future requestMediaReupload(String mediaId) async { + await twonlyDB.mediaFilesDao.updateMedia( + mediaId, + const MediaFilesCompanion( + downloadState: Value(DownloadState.reuploadRequested), + ), + ); + final messages = await twonlyDB.messagesDao.getMessagesByMediaId(mediaId); for (final message in messages) { @@ -277,12 +292,6 @@ Future requestMediaReupload(String mediaId) async { ), ), ); - await twonlyDB.mediaFilesDao.updateMedia( - mediaId, - const MediaFilesCompanion( - downloadState: Value(DownloadState.reuploadRequested), - ), - ); } } @@ -293,7 +302,7 @@ Future handleEncryptedFile(String mediaId) async { action: () async { final mediaService = await MediaFileService.fromMediaId(mediaId); if (mediaService == null) { - Log.error('Media file not found in database.'); + Log.warn('[$mediaId] Media file not found in database.'); return; } diff --git a/lib/src/services/api/mediafiles/media_background.api.dart b/lib/src/services/api/mediafiles/media_background.api.dart index d736e2d1..92afafc1 100644 --- a/lib/src/services/api/mediafiles/media_background.api.dart +++ b/lib/src/services/api/mediafiles/media_background.api.dart @@ -80,9 +80,13 @@ Future handleUploadStatusUpdate(TaskStatusUpdate update) async { await markUploadAsSuccessful(media); return; } - Log.error( + Log.warn( 'Got HTTP error ${update.responseStatusCode} for $mediaId', ); + Log.error( + 'Got HTTP error ${update.responseStatusCode} for media.', + onlyIfSentryEnabled: true, + ); } if (update.status == TaskStatus.notFound) { @@ -118,9 +122,13 @@ Future handleUploadStatusUpdate(TaskStatusUpdate update) async { if (update.status == TaskStatus.failed || update.status == TaskStatus.canceled) { - Log.error( + Log.warn( 'Background upload failed for $mediaId with status ${update.status} and ${update.responseStatusCode}. ', ); + Log.error( + 'Background upload failed with status ${update.status} and ${update.responseStatusCode}.', + onlyIfSentryEnabled: true, + ); final mediaService = MediaFileService(media); // in case the media file is already uploaded to not reqtry diff --git a/lib/src/services/api/mediafiles/upload.api.dart b/lib/src/services/api/mediafiles/upload.api.dart index ea4cc518..205e04ab 100644 --- a/lib/src/services/api/mediafiles/upload.api.dart +++ b/lib/src/services/api/mediafiles/upload.api.dart @@ -439,6 +439,10 @@ Future _startBackgroundMediaUploadInternal( // Refresh the media file state inside the mutex await mediaService.updateFromDB(); + if (mediaService.mediaFile.uploadState == UploadState.uploading) { + await _checkAndRecoverMissingUploadRequest(mediaService); + } + if (mediaService.mediaFile.uploadState == UploadState.initialized || mediaService.mediaFile.uploadState == UploadState.preprocessing) { Log.info( @@ -672,7 +676,31 @@ Future _createUploadRequest(MediaFileService media) async { await media.uploadRequestPath.writeAsBytes(uploadRequestBytes); } +Future _checkAndRecoverMissingUploadRequest( + MediaFileService media, { + bool triggerBackgroundUpload = false, +}) async { + if (!media.uploadRequestPath.existsSync()) { + Log.warn( + 'UploadRequestPath for media ${media.mediaFile.mediaId} does not exist. Reverting to preprocessing.', + ); + await media.setUploadState(UploadState.preprocessing); + if (triggerBackgroundUpload) { + unawaited(startBackgroundMediaUpload(media)); + } + return true; + } + return false; +} + Future _uploadUploadRequest(MediaFileService media) async { + if (await _checkAndRecoverMissingUploadRequest( + media, + triggerBackgroundUpload: true, + )) { + return; + } + final currentMedia = await twonlyDB.mediaFilesDao.getMediaFileById( media.mediaFile.mediaId, ); @@ -722,6 +750,13 @@ Future uploadFileFastOrEnqueue( UploadTask task, MediaFileService media, ) async { + if (await _checkAndRecoverMissingUploadRequest( + media, + triggerBackgroundUpload: true, + )) { + return; + } + final requestMultipart = http.MultipartRequest( 'POST', Uri.parse(task.url), diff --git a/lib/src/services/api/messages.api.dart b/lib/src/services/api/messages.api.dart index 19333cc5..c180451b 100644 --- a/lib/src/services/api/messages.api.dart +++ b/lib/src/services/api/messages.api.dart @@ -101,14 +101,18 @@ Future<(Uint8List, Uint8List?)?> _tryToSendCompleteMessageInternal({ if (receiptId == null && receipt == null) return null; try { - if (receipt == null) { - // ignore: parameter_assignments - receipt = await twonlyDB.receiptsDao.getReceiptById(receiptId!); - if (receipt == null) { - Log.warn('[$receiptId] Receipt not found.'); - return null; - } + final targetReceiptId = receipt?.receiptId ?? receiptId!; + final loadedReceipt = await twonlyDB.receiptsDao.getReceiptById( + targetReceiptId, + ); + if (loadedReceipt == null) { + Log.info( + '[$targetReceiptId] Receipt not found (might have been processed or deleted).', + ); + return null; } + // ignore: parameter_assignments + receipt = loadedReceipt; if (receipt.retryCount >= 2) { // After two retries, change the receiptId. This addresses a bug where the receiver received the message and marked it as received, @@ -138,7 +142,7 @@ Future<(Uint8List, Uint8List?)?> _tryToSendCompleteMessageInternal({ if (!onlyReturnEncryptedData && receipt.ackByServerAt != null && receipt.markForRetry == null) { - Log.error('Message already uploaded and mark for retry is not set!'); + Log.info('Message already uploaded and mark for retry is not set.'); return null; } diff --git a/lib/src/services/api/server_messages.api.dart b/lib/src/services/api/server_messages.api.dart index 1a3b1275..88bfd3e0 100644 --- a/lib/src/services/api/server_messages.api.dart +++ b/lib/src/services/api/server_messages.api.dart @@ -33,6 +33,7 @@ import 'package:twonly/src/services/group.service.dart'; import 'package:twonly/src/services/key_verification.service.dart'; import 'package:twonly/src/services/notifications/background.notifications.dart'; import 'package:twonly/src/services/notifications/fcm.notifications.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart'; import 'package:twonly/src/services/signal/encryption.signal.dart'; import 'package:twonly/src/services/signal/session.signal.dart'; import 'package:twonly/src/utils/log.dart'; @@ -55,9 +56,13 @@ Future handleServerMessage(server.ServerToClient msg) async { Log.info( 'Got ${msg.v0.newMessages.newMessages.length} messages from the server.', ); + final brokenSessionsInCurrentBatch = {}; for (final newMessage in msg.v0.newMessages.newMessages) { try { - await handleClient2ClientMessage(newMessage); + await handleClient2ClientMessage( + newMessage, + brokenSessionsInCurrentBatch: brokenSessionsInCurrentBatch, + ); } catch (e) { Log.error(e); } @@ -82,7 +87,10 @@ DateTime lastPushKeyRequest = clock.now().subtract(const Duration(hours: 1)); final Map _messageLocks = {}; -Future handleClient2ClientMessage(NewMessage newMessage) async { +Future handleClient2ClientMessage( + NewMessage newMessage, { + Set? brokenSessionsInCurrentBatch, +}) async { final body = Uint8List.fromList(newMessage.body); final message = Message.fromBuffer(body); final receiptId = message.receiptId; @@ -96,7 +104,11 @@ Future handleClient2ClientMessage(NewMessage newMessage) async { } await mutex.protect(() async { try { - await _handleClient2ClientMessage(newMessage, message); + await _handleClient2ClientMessage( + newMessage, + message, + brokenSessionsInCurrentBatch: brokenSessionsInCurrentBatch, + ); } finally { _messageLocks.remove(receiptId); } @@ -105,11 +117,27 @@ Future handleClient2ClientMessage(NewMessage newMessage) async { Future _handleClient2ClientMessage( NewMessage newMessage, - Message message, -) async { + Message message, { + Set? brokenSessionsInCurrentBatch, +}) async { final fromUserId = newMessage.fromUserId.toInt(); final receiptId = message.receiptId; + if (brokenSessionsInCurrentBatch?.contains(fromUserId) == true) { + // This happens when a session goes out of sync (e.g. wrong message order). + // We skip the remaining messages in the batch because each failed decryption + // attempt is extremely slow (~1.2s) and would otherwise freeze the app. + // By returning early, we skip gotReceipt() and error responses. + // The server still deletes the batch since we ACK the entire batch later. + // The sender keeps the message unacknowledged. Once they process our SESSION_OUT_OF_SYNC + // error and establish a new session, their retry logic will automatically re-encrypt + // and re-send these messages with the new keys. + Log.info( + 'Skipping message from $fromUserId - session known broken in this batch', + ); + return; + } + if (await twonlyDB.receiptsDao.isDuplicated(receiptId)) { return; } @@ -199,6 +227,7 @@ Future _handleClient2ClientMessage( encryptedContentRaw, message.type, receiptId, + brokenSessionsInCurrentBatch: brokenSessionsInCurrentBatch, ); if (plainTextContent != null) { response = Message( @@ -252,7 +281,11 @@ Future _handleClient2ClientMessage( await twonlyDB.receiptsDao.gotReceipt(receiptId); Log.info('[$receiptId] Finished processing'); } catch (e) { - Log.error('[$receiptId] Error marking message as received: $e'); + Log.warn('[$receiptId] Error marking message as received: $e'); + Log.error( + 'Error marking message as received: $e', + onlyIfSentryEnabled: true, + ); } } @@ -260,13 +293,15 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw( int fromUserId, Uint8List encryptedContentRaw, Message_Type messageType, - String receiptId, -) async { + String receiptId, { + Set? brokenSessionsInCurrentBatch, +}) async { Log.info('[$receiptId] calling signalDecryptMessage'); var (encryptedContent, decryptionErrorType) = await signalDecryptMessage( fromUserId, encryptedContentRaw, messageType.value, + brokenSessionsInCurrentBatch: brokenSessionsInCurrentBatch, ); if (encryptedContent == null) { @@ -283,7 +318,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw( Log.info('[$receiptId] Calling handleEncryptedMessage'); - final (a, b) = await handleEncryptedMessage( + final result = await handleEncryptedMessage( fromUserId, encryptedContent, messageType, @@ -292,9 +327,9 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw( Log.info('[$receiptId] Finished handleEncryptedMessage'); - if (a == null && b == null) { + if (result.responseCipherText == null && result.responsePlaintext == null) { unawaited(FcmNotificationService.updateLastServerMessageTimestamp()); - if (Platform.isAndroid) { + if (Platform.isAndroid && result.showPushNotification) { // Message was handled without any error. Show push notification to the user for Android. await showPushNotificationFromServerMessages( fromUserId, @@ -303,10 +338,10 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw( } } - return (a, b); + return (result.responseCipherText, result.responsePlaintext); } -Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( +Future handleEncryptedMessage( int fromUserId, EncryptedContent content, Message_Type messageType, @@ -347,13 +382,12 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.contactRequest, receiptId, )) { - return ( - null, - PlaintextContent() + return DecryptedMessageResult( + responsePlaintext: PlaintextContent() ..retryControlError = PlaintextContent_RetryErrorMessage(), ); } - return (null, null); + return const DecryptedMessageResult(); } if (content.hasErrorMessages()) { @@ -363,7 +397,25 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( receiptId, groupId: content.hasGroupId() ? content.groupId : null, ); - return (null, null); + return const DecryptedMessageResult(showPushNotification: false); + } + + if (content.hasPasswordlessRecovery()) { + await PasswordlessRecoveryService.handlePasswordlessRecovery( + fromUserId, + content.passwordlessRecovery, + receiptId, + ); + return const DecryptedMessageResult(); + } + + if (content.hasPasswordlessRecoveryHeartbeat()) { + await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat( + fromUserId, + content.passwordlessRecoveryHeartbeat, + receiptId, + ); + return const DecryptedMessageResult(); } if (content.hasContactUpdate()) { @@ -373,7 +425,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( senderProfileCounter, receiptId, ); - return (null, null); + return const DecryptedMessageResult(showPushNotification: false); } if (content.hasUserDiscoveryRequest()) { @@ -382,7 +434,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.userDiscoveryRequest, receiptId, ); - return (null, null); + return const DecryptedMessageResult(showPushNotification: false); } if (content.hasUserDiscoveryUpdate()) { @@ -391,12 +443,12 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.userDiscoveryUpdate, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasPushKeys()) { await handlePushKey(fromUserId, content.pushKeys, receiptId); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasMessageUpdate()) { @@ -405,7 +457,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.messageUpdate, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasKeyVerificationProof()) { @@ -413,7 +465,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( fromUserId, content.keyVerificationProof.calculatedMac, ); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasMediaUpdate()) { @@ -422,12 +474,19 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.mediaUpdate, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } if (!content.hasGroupId()) { - Log.error('[$receiptId] Messages should have a groupId $fromUserId.'); - return (null, null); + final type = _getEncryptedContentType(content); + Log.warn( + '[$receiptId] Messages should have a groupId $fromUserId. Type: $type', + ); + Log.error( + 'Messages should have a groupId. Type: $type', + onlyIfSentryEnabled: true, + ); + return const DecryptedMessageResult(); } if (content.hasGroupCreate()) { @@ -437,7 +496,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.groupCreate, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } /// Verify that the user is (still) in that group... @@ -453,18 +512,17 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( ); if (contact == null || !contact.accepted || contact.deletedByUser) { await handleNewContactRequest(fromUserId); - Log.error( + Log.warn( '[$receiptId] User tries to send message to direct chat while the user does not exist!', ); - return ( - EncryptedContent( + return DecryptedMessageResult( + responseCipherText: EncryptedContent( errorMessages: EncryptedContent_ErrorMessages( type: EncryptedContent_ErrorMessages_Type .ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD, relatedReceiptId: receiptId, ), ), - null, ); } Log.info( @@ -478,22 +536,21 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( ); } else { if (content.hasGroupJoin()) { - Log.error( + Log.warn( '[$receiptId] Got group join message, but group does not exist yet, retry later. As probably the GroupCreate was not yet received.', ); // In case the group join was received before the GroupCreate the sender should send it later again. - return ( - null, - PlaintextContent() + return DecryptedMessageResult( + responsePlaintext: PlaintextContent() ..retryControlError = PlaintextContent_RetryErrorMessage(), ); } - Log.error( + Log.warn( '[$receiptId] User $fromUserId tried to access group ${content.groupId}. Sending GROUP_NOT_FOUND_OR_NOT_A_MEMBER error.', ); - return ( - EncryptedContent( + return DecryptedMessageResult( + responseCipherText: EncryptedContent( groupId: content.groupId, errorMessages: EncryptedContent_ErrorMessages( type: EncryptedContent_ErrorMessages_Type @@ -501,14 +558,13 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( relatedReceiptId: receiptId, ), ), - null, ); } } if (content.hasFlameSync()) { await handleFlameSync(content.groupId, content.flameSync, receiptId); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasGroupUpdate()) { @@ -518,7 +574,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.groupUpdate, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasGroupJoin()) { @@ -528,13 +584,12 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.groupJoin, receiptId, )) { - return ( - null, - PlaintextContent() + return DecryptedMessageResult( + responsePlaintext: PlaintextContent() ..retryControlError = PlaintextContent_RetryErrorMessage(), ); } - return (null, null); + return const DecryptedMessageResult(); } if (content.hasResendGroupPublicKey()) { @@ -544,7 +599,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.groupJoin, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasAdditionalDataMessage()) { @@ -554,17 +609,17 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.additionalDataMessage, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasTextMessage()) { - await handleTextMessage( + final isNewText = await handleTextMessage( fromUserId, content.groupId, content.textMessage, receiptId, ); - return (null, null); + return DecryptedMessageResult(showPushNotification: isNewText); } if (content.hasReaction()) { @@ -574,17 +629,17 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( content.reaction, receiptId, ); - return (null, null); + return const DecryptedMessageResult(); } if (content.hasMedia()) { - await handleMedia( + final isNewMedia = await handleMedia( fromUserId, content.groupId, content.media, receiptId, ); - return (null, null); + return DecryptedMessageResult(showPushNotification: isNewMedia); } if (content.hasTypingIndicator()) { @@ -596,5 +651,39 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( ); } - return (null, null); + return const DecryptedMessageResult(); +} + +String _getEncryptedContentType(EncryptedContent content) { + if (content.hasMessageUpdate()) return 'messageUpdate'; + if (content.hasMedia()) return 'media'; + if (content.hasMediaUpdate()) return 'mediaUpdate'; + if (content.hasContactUpdate()) return 'contactUpdate'; + if (content.hasContactRequest()) return 'contactRequest'; + if (content.hasFlameSync()) return 'flameSync'; + if (content.hasPushKeys()) return 'pushKeys'; + if (content.hasReaction()) return 'reaction'; + if (content.hasTextMessage()) return 'textMessage'; + if (content.hasGroupCreate()) return 'groupCreate'; + if (content.hasGroupJoin()) return 'groupJoin'; + if (content.hasGroupUpdate()) return 'groupUpdate'; + if (content.hasResendGroupPublicKey()) return 'resendGroupPublicKey'; + if (content.hasErrorMessages()) return 'errorMessages'; + if (content.hasAdditionalDataMessage()) return 'additionalDataMessage'; + if (content.hasTypingIndicator()) return 'typingIndicator'; + if (content.hasUserDiscoveryRequest()) return 'userDiscoveryRequest'; + if (content.hasUserDiscoveryUpdate()) return 'userDiscoveryUpdate'; + if (content.hasKeyVerificationProof()) return 'keyVerificationProof'; + return 'unknown'; +} + +class DecryptedMessageResult { + const DecryptedMessageResult({ + this.responseCipherText, + this.responsePlaintext, + this.showPushNotification = true, + }); + final EncryptedContent? responseCipherText; + final PlaintextContent? responsePlaintext; + final bool showPushNotification; } diff --git a/lib/src/services/backup.service.dart b/lib/src/services/backup.service.dart index 952869d8..42fb4e67 100644 --- a/lib/src/services/backup.service.dart +++ b/lib/src/services/backup.service.dart @@ -105,8 +105,9 @@ class BackupService { ))) { final backupId = await RustBackupIdentity.getBackupId(); if (backupId == null) { - Log.error('No backup password was set by the user.'); + Log.warn('No backup password was set by the user.'); backup.identityState = LastBackupUploadState.failed; + await UserService.update((u) => u.isBackupEnabled = false); } else { Log.info('Performing a identity backup.'); final encryptedBackup = @@ -162,7 +163,7 @@ class BackupService { (backupDownloadToken, backupArchive) = await RustBackupArchive.createBackupArchive(); } catch (e) { - Log.error(e); + Log.warn('Creating archive backup failed: $e'); return; } Log.info( @@ -324,6 +325,26 @@ class BackupService { return _nextBackupStage(); } + static Future startPasswordlessBackupRecovery( + int userId, + String username, + Uint8List keyManagerBytes, + ) async { + final state = BackupRecovery( + username: username, + password: '', + userId: userId, + )..state = BackupRecoveryState.archiveBackupStarted; + + await deleteLocalUserData(); + + // Import KeyManager keys into secure storage & in-memory key manager + await RustKeyManager.importSerialized(serializedBytes: keyManagerBytes); + + await KeyValueStore.put(KeyValueKeys.backupRecoveryState, state.toJson()); + return _nextBackupStage(); + } + static Future<(Uint8List?, RecoveryError?)> _downloadBackup( String backupServerUrl, ) async { @@ -337,7 +358,7 @@ class BackupService { }, ); } catch (e) { - Log.error('Error fetching backup: $e'); + Log.warn('Error fetching backup: $e'); return (null, RecoveryError.noInternet); } diff --git a/lib/src/services/intent/links.intent.dart b/lib/src/services/intent/links.intent.dart index 39fa5a65..c8042ebc 100644 --- a/lib/src/services/intent/links.intent.dart +++ b/lib/src/services/intent/links.intent.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'dart:typed_data'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_sharing_intent/flutter_sharing_intent.dart'; import 'package:flutter_sharing_intent/model/sharing_file.dart'; @@ -13,6 +13,8 @@ import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/tables/contacts.table.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/services/api/mediafiles/upload.api.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart' + show PasswordlessRecoveryService; import 'package:twonly/src/services/signal/session.signal.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -30,6 +32,12 @@ Future handleIntentUrl(BuildContext context, Uri uri) async { // Check if this is the QR code link which was // therefore scanned with the system camera + if (kDebugMode && + uri.toString().startsWith(PasswordlessRecoveryService.linkPrefix)) { + await PasswordlessRecoveryService.handleRecoveryLink(uri.toString()); + return true; + } + if (uri.toString().startsWith(QrCodeUtils.linkPrefix)) { final result = await QrCodeUtils.handleQrCodeLink(uri.toString()); diff --git a/lib/src/services/mediafiles/compression.service.dart b/lib/src/services/mediafiles/compression.service.dart index 9f2796a2..f6398425 100644 --- a/lib/src/services/mediafiles/compression.service.dart +++ b/lib/src/services/mediafiles/compression.service.dart @@ -33,17 +33,18 @@ Future compressImage( Log.info('Compressed images size in bytes: ${compressedBytes.length}'); - if (compressedBytes.length >= 1 * 1000 * 1000) { + if (compressedBytes.length >= 2 * 1000 * 1000) { // if the media file is over 1MB compress it with 60% final tmpCompressedBytes = await FlutterImageCompress.compressWithFile( sourceFile.path, format: CompressFormat.webp, quality: 60, ); - if (tmpCompressedBytes != null) { + if (tmpCompressedBytes == null) { Log.error( 'Could not compress media file with 60%: $sourceFile. Sending original 90% compressed file.', ); + } else { compressedBytes = tmpCompressedBytes; } } @@ -106,11 +107,11 @@ Future compressAndOverlayVideo(MediaFileService media) async { }, ); } catch (e) { - Log.error('during video compression: $e'); + Log.warn('during video compression: $e'); } if (compressedPath == null) { - Log.error('Could not compress video using original video.'); + Log.warn('Could not compress video using original video.'); // as a fall back use the non compressed version media.ffmpegOutputPath.copySync(media.tempPath.path); } diff --git a/lib/src/services/mediafiles/mediafile.service.dart b/lib/src/services/mediafiles/mediafile.service.dart index e0b9c585..cbe1039e 100644 --- a/lib/src/services/mediafiles/mediafile.service.dart +++ b/lib/src/services/mediafiles/mediafile.service.dart @@ -252,7 +252,7 @@ class MediaFileService { Future compressMedia() async { if (!originalPath.existsSync()) { - Log.error('Could not compress as original media does not exists.'); + Log.warn('Could not compress as original media does not exists.'); return; } @@ -296,7 +296,8 @@ class MediaFileService { (thumbnailPath.existsSync() && thumbnailPath.lengthSync() > 0) || mediaFile.type == MediaType.audio || ((mediaFile.type == MediaType.image || mediaFile.type == MediaType.gif) && - storedPath.existsSync() && storedPath.lengthSync() > 0); + storedPath.existsSync() && + storedPath.lengthSync() > 0); Future storeMediaFile() async { Log.info('Storing media file ${mediaFile.mediaId}'); @@ -327,8 +328,8 @@ class MediaFileService { } } } else { - Log.error( - 'Could not store image neither as ${tempPath.path} does not exists.', + Log.warn( + 'Could not store image locally as ${tempPath.path} does not exist.', ); } unawaited(createThumbnail()); @@ -469,7 +470,7 @@ class MediaFileService { format: CompressFormat.webp, quality: 90, ); - + if (webpBytes.isNotEmpty) { await storedPath.writeAsBytes(webpBytes); } else { @@ -503,7 +504,7 @@ class MediaFileService { ); await updateFromDB(); } catch (e) { - Log.error( + Log.warn( 'Error auto-cropping transparent borders for mediaId ${mediaFile.mediaId}: $e', ); await twonlyDB.mediaFilesDao.updateMedia( diff --git a/lib/src/services/mediafiles/thumbnail.service.dart b/lib/src/services/mediafiles/thumbnail.service.dart index 35fd86a6..ef733657 100644 --- a/lib/src/services/mediafiles/thumbnail.service.dart +++ b/lib/src/services/mediafiles/thumbnail.service.dart @@ -112,7 +112,7 @@ Future createThumbnailsForImage( ); return true; } else { - Log.error('Compressed image thumbnail is empty or missing.'); + Log.warn('Compressed image thumbnail is empty or missing.'); try { if (destinationFile.existsSync()) { destinationFile.deleteSync(); diff --git a/lib/src/services/passwordless_recovery.service.dart b/lib/src/services/passwordless_recovery.service.dart index 147a4c7c..67e0e067 100644 --- a/lib/src/services/passwordless_recovery.service.dart +++ b/lib/src/services/passwordless_recovery.service.dart @@ -1,115 +1,640 @@ import 'dart:async'; -import 'dart:convert' show base64Encode, utf8; -import 'dart:typed_data'; +import 'dart:convert' show base64Url, utf8; -import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart' - show FlutterChacha20; -import 'package:cryptography_plus/cryptography_plus.dart' show SecretKey; -import 'package:hashlib/hashlib.dart' show Scrypt; +import 'package:clock/clock.dart'; +import 'package:collection/collection.dart'; +import 'package:crypto/crypto.dart' hide Hmac; +import 'package:cryptography_plus/cryptography_plus.dart' + show Hmac, Mac, SecretBox, SecretKey, Xchacha20; +import 'package:drift/drift.dart'; +import 'package:fixnum/fixnum.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/core/bridge/wrapper.dart'; +import 'package:twonly/core/bridge/wrapper/key_manager.dart'; import 'package:twonly/locator.dart'; +import 'package:twonly/src/constants/keyvalue.keys.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart' + show getContactDisplayName; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/model/json/onboarding_state.model.dart'; import 'package:twonly/src/model/json/userdata.model.dart' show PasswordLessRecovery; -import 'package:twonly/src/model/protobuf/client/generated/passwordless_recovery/types.pb.dart'; +import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart' + as pb; +import 'package:twonly/src/model/protobuf/client/generated/passwordless_recovery.pb.dart'; +import 'package:twonly/src/providers/routing.provider.dart'; +import 'package:twonly/src/services/api/messages.api.dart'; +import 'package:twonly/src/services/user.service.dart'; +import 'package:twonly/src/utils/avatars.dart' show getAvatarSvg; +import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart'; -enum SecondFactorType { none, pin, email } +enum SecondFactorType { email, pin, none } class PasswordlessRecoveryService { + static final StreamController onEmailTokenReceived = + StreamController.broadcast(); + + static String linkPrefix = 'https://me.twonly.eu/r/#'; + + static final Set _handledNotificationIds = {}; + + static Future handleRecoveryLink(String link) async { + final hashIndex = link.indexOf('#'); + if (hashIndex == -1) return; + + final fragment = link.substring(hashIndex + 1); + final parts = fragment.split('/'); + if (parts.length < 2) { + if (fragment.isNotEmpty) { + onEmailTokenReceived.add(fragment); + final context = rootNavigatorKey.currentContext; + if (context != null && context.mounted) { + unawaited(context.push(Routes.recoverPasswordless, extra: fragment)); + } + } + return; + } + + final notificationId = parts[0]; + final base64Key = parts[1]; + + if (_handledNotificationIds.contains(notificationId)) { + Log.info( + 'Notification ID $notificationId was already handled, skipping.', + ); + return; + } + _handledNotificationIds.add(notificationId); + + final encryptionKey = base64Url.decode(base64Url.normalize(base64Key)); + + final context = rootNavigatorKey.currentContext; + if (context != null && context.mounted) { + unawaited( + context.navPush( + HelpAFriendPasswordlessRecoveryView( + notificationId: notificationId, + encryptionKey: encryptionKey, + ), + ), + ); + } + } + + static Future submitRecoveryShare( + String notificationId, + List encryptionKey, + Contact contact, + ) async { + try { + final share = contact.recoveryContactsSecretShare; + if (share == null) { + Log.warn('Contact does not have a recovery share stored.'); + return false; + } + + final trustedFriend = TrustedFriendShare_User( + userId: Int64(userService.currentUser.userId), + displayName: userService.currentUser.displayName, + avatar: userService.currentUser.avatarSvg != null + ? utf8.encode(userService.currentUser.avatarSvg!) + : null, + ); + + final shareUser = TrustedFriendShare_User( + userId: Int64(contact.userId), + displayName: getContactDisplayName(contact), + avatar: contact.avatarSvgCompressed != null + ? utf8.encode(getAvatarSvg(contact.avatarSvgCompressed!)) + : null, + ); + + final trustedFriendShare = TrustedFriendShare( + trustedFriend: trustedFriend, + shareUser: shareUser, + threshold: contact.recoveryContactsThreshold, + sharedSecretData: share, + ); + + final xchacha20 = Xchacha20.poly1305Aead(); + final secretBox = await xchacha20.encrypt( + trustedFriendShare.writeToBuffer(), + secretKey: SecretKey(encryptionKey), + nonce: xchacha20.newNonce(), + ); + + final envelope = EncryptedEnvelope( + encryptedData: secretBox.cipherText, + iv: secretBox.nonce, + mac: secretBox.mac.bytes, + ); + + final res = await apiService.submitRecoveryShare( + notificationId: notificationId, + encryptedMessage: envelope.writeToBuffer(), + ); + return res.isSuccess; + } catch (e) { + Log.error('Failed to submit recovery share', error: e); + return false; + } + } + static Future enablePasswordlessRecovery({ required List trustedFriendIds, required SecondFactorType secondFactorType, required String secondFactorValue, required int threshold, }) async { + // 1. Get all currently trusted friends contacts and send them delete messages + final oldTrustedFriends = await (twonlyDB.select( + twonlyDB.contacts, + )..where((t) => t.recoveryIsTrustedFriend.equals(true))).get(); + + for (final contact in oldTrustedFriends) { + try { + await sendCipherText( + contact.userId, + pb.EncryptedContent( + passwordlessRecovery: pb.EncryptedContent_PasswordLessRecovery( + delete: true, + ), + ), + ); + } catch (e) { + Log.error( + 'Failed to send delete PasswordLessRecovery message to contact ${contact.userId}: $e', + ); + } + } + + // 1. Reset current recovery data to ensure a clean state + await twonlyDB.contactsDao.resetRecoveryDataForAllContacts(); + await UserService.update((u) => u.passwordLessRecovery = null); - // 2. Update the user model configuration + final config = PasswordLessRecovery(threshold); + final xchacha20 = Xchacha20.poly1305Aead(); - final config = PasswordLessRecovery(); + // 2. If enabled, handle the second factor and create serverKey - final serverKey = getRandomUint8List(32); - Uint8List? protectedEmailServerKey; - Uint8List? pinUnlockToken; - Uint8List? protectedPin; + Uint8List? serverKey; + Uint8List? encryptedServerKey; + + SecretKey? secondFactorEncryptedServerKeyKey; + String? emailHint; switch (secondFactorType) { case SecondFactorType.email: - final emailSeed = getRandomUint8List(32); - - // Store it, so the user can see it again. - config.email = secondFactorValue; - - final chacha20 = FlutterChacha20.poly1305Aead(); - - final scrypt = Scrypt( - cost: 65536, - salt: emailSeed, - ); - - final protectedEmailKey = scrypt - .convert(utf8.encode(secondFactorValue)) - .bytes; - - // there must be a emailSeed like for the pin... // The serverKey is encrypted with the email; this protects the server from seeing the user's email address, while // also ensuring that the server can only send the real secret to the user's configured email, as a different // email will result in a different secret key. - final secretBox = await chacha20.encrypt( - serverKey, - secretKey: SecretKey( - Uint8List.fromList(protectedEmailKey), - ), - nonce: chacha20.newNonce(), - ); + // This is only stored so the user can see there email, and verify that he has set the valid mail... + config.email = secondFactorValue; + emailHint = createEmailHint(secondFactorValue); - protectedEmailServerKey = EncryptedEnvelope( - encryptedData: secretBox.cipherText, - // iv: secretBox.nonce, - mac: secretBox.mac.bytes, - ).writeToBuffer(); + // E-Mail Protection: + // - Server can only learn the email during recovery. Ensured as the server gets the NONCE and MAC to decrpyt during recovery. + // - Trusted-friends: Server key is send to the mail, they whould need access to the user's mail account. + secondFactorEncryptedServerKeyKey = SecretKey( + Uint8List.fromList(sha256.convert(utf8.encode(config.email!)).bytes), + ); case SecondFactorType.pin: - final pinSeed = getRandomUint8List(32); - pinUnlockToken = getRandomUint8List(32); - // The pin seed is to ensure that the server does never learns the real 4-digit pin. The seed is never send to - // the server. As the pin are heavily protected against brute-forcing and will be discared by the server after X - // tries, this prevents that a malicous user trigger this deletion... - config.pinSeed = base64Encode(pinSeed); - config.pinUnlockToken = base64Encode(pinUnlockToken); + // The pin seed - never shared with the server - ensures that the server is unable to brute-force real user's pin. + config.pinSeed = getRandomUint8List(32); - final scrypt = Scrypt( - cost: 65536, - salt: pinSeed, + // As the pin is heavily protected against brute-forcing e.g. will be deleted by the server after X tries, the + // unlock token is required to prevent a malicous user (except the trusted friends) to triger this deletion. + config.pinUnlockToken = getRandomUint8List(32); + + // Brute-force protection for the user's pin: + // - Server: Does not know the seed. + // - Trusted friends: + // Can only check the result X times before the server deletes the key. As they do not have + // the mac and the cypher text they are unable to brute-force the pin localy. And the server only allows 10 + // tries. + final pinProtectionKey = await Hmac.sha256().calculateMac( + Uint8List.fromList(utf8.encode(secondFactorValue)), + secretKey: SecretKey(config.pinSeed!), ); - final key = scrypt.convert(utf8.encode(secondFactorValue)).bytes; - protectedPin = key.sublist(0, 32); + // To restore the user has to provide the server with this encryption key. The server then can verify the + // correct pin was entered, when decrypting the server key as he also receives the mac and nonce from the user + // during recovery. Only when the mac is correct the server provides the user with the serverKey. + secondFactorEncryptedServerKeyKey = SecretKey(pinProtectionKey.bytes); case SecondFactorType.none: } - // 3. Use the second factor and generate the shares... + if (secondFactorEncryptedServerKeyKey != null) { + // The server key is used to encrypt the RecoveryData of the users. This ensures that when the trusted friends + // colaberate, they additional need the serverKey to decrypt the user's key. + serverKey = getRandomUint8List(32); - // Generate the shares, and store them in the contacrs.table. + final secretBox = await xchacha20.encrypt( + serverKey, + secretKey: secondFactorEncryptedServerKeyKey, + nonce: xchacha20.newNonce(), + ); - Log.info('Enabling passwordless recovery with:'); - Log.info(' - Trusted Friends: $trustedFriendIds'); - Log.info(' - Second Factor Type: $secondFactorType'); - Log.info(' - Second Factor Value: $secondFactorValue'); - Log.info(' - Threshold: $threshold'); + // The server only gets the encrypted server key and the mac. Because the server does not know the nonce (192-bit + // because of XChaCha), he is unable to decrypt the server key without the help of the trusted friends. This + // ensures that the server never learns the users orginal pin, as he is missing the pin_seed and also unable to + // brute-force the email of the user as he does not have the nonce. + encryptedServerKey = Uint8List.fromList([ + ...secretBox.cipherText, + ...secretBox.mac.bytes, + ]); - // Use the serverKey to protect the recovery_data_encrypted + config + ..encryptedServerKeyNonce = secretBox.nonce + ..encryptedServerKey = encryptedServerKey; + } - // 4. Send the data to the server: + // 3. Using shamir's secret to generate the shares for the users. - // to th server - // protectedPin, pinUnlockToken, protectedEmailServerKey; + // 3.1. Create the SharedSecretData - // 5. send to the contacts / implement the heartbeat check, and notify the user in case the hearbeath shows that to few users are active... + var recoveryData = RecoveryData( + userId: Int64(userService.currentUser.userId), + keyManager: await RustKeyManager.serialize(), + ).writeToBuffer(); + if (serverKey != null) { + // Second factor was enabled, so encrypt the recoveryData using the serverKey. + + final secretBox = await xchacha20.encrypt( + recoveryData, + secretKey: SecretKey(serverKey), + nonce: xchacha20.newNonce(), + ); + + recoveryData = EncryptedEnvelope( + encryptedData: secretBox.cipherText, + iv: secretBox.nonce, + mac: secretBox.mac.bytes, + ).writeToBuffer(); + } + + final sharedSecretData = SharedSecretData( + recoveryData: recoveryData, + pinSeed: config.pinSeed, + pinUnlockToken: config.pinUnlockToken, + emailHint: emailHint, + encryptedServerKeyNonce: config.encryptedServerKeyNonce, + ).writeToBuffer(); + + // 3.2. Use the amount of trusted friends to generate the shares + + final List shares; + try { + shares = await RustUtils.generateShares( + secret: sharedSecretData, + total: trustedFriendIds.length, + threshold: threshold, + ); + + if (shares.length != trustedFriendIds.length) { + Log.error('shares.length != trustedFriendIds.length'); + return false; + } + } catch (e) { + Log.error('Failed to generate secret shares: $e'); + return false; + } + + await UserService.update((u) => u.passwordLessRecovery = config); + + // 3.4. Store the shares in the contact's rows + for (final contactId in trustedFriendIds) { + await twonlyDB.contactsDao.updateContact( + contactId, + ContactsCompanion( + recoveryIsTrustedFriend: const Value(true), + recoveryLastHeartbeat: const Value(null), + recoverySecretShare: Value(shares.removeLast()), + ), + ); + } + + unawaited(performHeartbeat()); + + // The passwordless is configured sucessfully. return true; } + + static Future testPin(String pin) async { + final config = userService.currentUser.passwordLessRecovery; + if (config?.pinSeed == null || config?.encryptedServerKey == null) { + return false; + } + + try { + final pinProtectionKey = await Hmac.sha256().calculateMac( + Uint8List.fromList(utf8.encode(pin)), + secretKey: SecretKey(config!.pinSeed!), + ); + + final secondFactorEncryptedServerKeyKey = SecretKey( + pinProtectionKey.bytes, + ); + + final xchacha20 = Xchacha20.poly1305Aead(); + + final combined = config.encryptedServerKey!; + final cipherText = combined.sublist(0, combined.length - 16); + final macBytes = combined.sublist(combined.length - 16); + + final secretBox = SecretBox( + cipherText, + nonce: config.encryptedServerKeyNonce!, + mac: Mac(macBytes), + ); + + await xchacha20.decrypt( + secretBox, + secretKey: secondFactorEncryptedServerKeyKey, + ); + + return true; + } catch (e) { + Log.error('Failed to test pin: $e'); + return false; + } + } + + static Future performHeartbeat() async { + final config = userService.currentUser.passwordLessRecovery; + + if (config != null) { + final lastHeartbeat = config.lastServerHeartbeat; + final isOlderThanAMonth = + lastHeartbeat != null && + clock.now().difference(lastHeartbeat).inDays > 20; + + if ((lastHeartbeat == null || isOlderThanAMonth) && + config.encryptedServerKey != null) { + final res = await apiService.registerPasswordLessRecovery( + config.encryptedServerKey!, + config.pinUnlockToken, + ); + + if (res.isSuccess) { + await UserService.update((u) { + u.passwordLessRecovery?.lastServerHeartbeat = clock.now(); + }); + } + } + + final lastContactHeartbeat = config.lastContactHeartbeat; + final isContactHeartbeatOlderThan24h = + lastContactHeartbeat == null || + clock.now().difference(lastContactHeartbeat).inHours >= 24; + + if (isContactHeartbeatOlderThan24h) { + // Get all contacts where recoveryLastHeartbeat is NULL. Then for each contacts send. + // recoveryLastHeartbeat is ONLY updated in case the contact has responded. + final pendingShares = + await (twonlyDB.select(twonlyDB.contacts)..where( + (t) => + t.recoveryIsTrustedFriend.equals(true) & + t.recoveryLastHeartbeat.isNull() & + t.recoverySecretShare.isNotNull(), + )) + .get(); + + for (final contact in pendingShares) { + try { + await sendCipherText( + contact.userId, + pb.EncryptedContent( + passwordlessRecovery: pb.EncryptedContent_PasswordLessRecovery( + recoverySecretShare: contact.recoverySecretShare, + delete: false, + threshold: Int64(config.threshold), + ), + ), + ); + } catch (e) { + Log.error( + 'Failed to send PasswordLessRecovery share to contact ${contact.userId}: $e', + ); + } + } + + await UserService.update((u) { + u.passwordLessRecovery?.lastContactHeartbeat = clock.now(); + }); + } + } + + // Send heartbeat to the friends I am a trusted friend. + final oneWeekAgo = clock.now().subtract(const Duration(days: 7)); + final trustedFriendsToNotify = + await (twonlyDB.select(twonlyDB.contacts)..where( + (t) => + t.recoveryContactsSecretShare.isNotNull() & + (t.recoveryContactsLastHeartbeat.isNull() | + t.recoveryContactsLastHeartbeat.isSmallerThanValue( + oneWeekAgo, + )), + )) + .get(); + + for (final contact in trustedFriendsToNotify) { + try { + final share = contact.recoveryContactsSecretShare!; + final hash = sha256.convert(share).bytes; + + await sendCipherText( + contact.userId, + pb.EncryptedContent( + passwordlessRecoveryHeartbeat: + pb.EncryptedContent_PasswordLessRecoveryHeartbeat( + hash: hash, + ), + ), + ); + + await twonlyDB.contactsDao.updateContact( + contact.userId, + ContactsCompanion( + recoveryContactsLastHeartbeat: Value(clock.now()), + ), + ); + } catch (e) { + Log.error( + 'Failed to send PasswordLessRecoveryHeartbeat to contact ${contact.userId}: $e', + ); + } + } + } + + static Future handlePasswordlessRecovery( + int fromUserId, + pb.EncryptedContent_PasswordLessRecovery msg, + String receiptId, + ) async { + if (msg.delete) { + Log.info( + '[$receiptId] Received request to delete passwordless recovery share from contact $fromUserId', + ); + await twonlyDB.contactsDao.updateContact( + fromUserId, + const ContactsCompanion( + recoveryContactsSecretShare: Value(null), + recoveryContactsLastHeartbeat: Value(null), + ), + ); + } else if (msg.hasRecoverySecretShare() && msg.hasThreshold()) { + Log.info( + '[$receiptId] Received new passwordless recovery share from contact $fromUserId', + ); + await twonlyDB.contactsDao.updateContact( + fromUserId, + ContactsCompanion( + recoveryContactsSecretShare: Value( + Uint8List.fromList(msg.recoverySecretShare), + ), + recoveryContactsThreshold: Value(msg.threshold.toInt()), + recoveryContactsLastHeartbeat: const Value( + null, // this will trigger that a heartbeat will be send... + ), + ), + ); + } + unawaited(performHeartbeat()); + } + + static Future handlePasswordlessRecoveryHeartbeat( + int fromUserId, + pb.EncryptedContent_PasswordLessRecoveryHeartbeat msg, + String receiptId, + ) async { + Log.info( + '[$receiptId] Received passwordless recovery heartbeat from contact $fromUserId', + ); + final contact = await twonlyDB.contactsDao.getContactById(fromUserId); + final storedShare = contact?.recoverySecretShare; + + if (storedShare == null) { + unawaited( + sendCipherText( + fromUserId, + pb.EncryptedContent( + passwordlessRecovery: pb.EncryptedContent_PasswordLessRecovery( + delete: true, + ), + ), + ), + ); + Log.warn( + '[$receiptId] Received passwordless recovery heartbeat from $fromUserId but we did not send him a secret share.', + ); + return; + } + + final computedHash = sha256.convert(storedShare).bytes; + final recoveryLastHeartbeat = + const ListEquality().equals(computedHash, msg.hash) + ? clock.now() + : null; // The stored share not valid (maybe a old backup was restored). This will cause the performHeartbeat to resend him his share + Log.info( + '[$receiptId] Got heartbeat: ($recoveryLastHeartbeat)', + ); + await twonlyDB.contactsDao.updateContact( + fromUserId, + ContactsCompanion( + recoveryLastHeartbeat: Value(recoveryLastHeartbeat), + ), + ); + } + + static Future checkAndStorePasswordlessMessages( + OnboardingState state, + ) async { + if (!state.serverRegistered || + state.notificationId == null || + state.downloadAuthToken == null || + state.encryptionKey == null) { + return false; + } + + final alreadyReceivedIds = state.receivedShares + .map((s) => Int64(s.messageId)) + .toList(); + + final response = await apiService.checkForPasswordlessNotification( + notificationId: state.notificationId!, + downloadAuthToken: state.downloadAuthToken!, + alreadyReceivedIds: alreadyReceivedIds, + ); + + if (response == null || response.messages.isEmpty) { + return false; + } + + final xchacha20 = Xchacha20.poly1305Aead(); + final secretKey = SecretKey(state.encryptionKey!); + var didUpdate = false; + + for (final msg in response.messages) { + final msgId = msg.id.toInt(); + + try { + final envelope = EncryptedEnvelope.fromBuffer(msg.encryptedMessage); + final secretBox = SecretBox( + envelope.encryptedData, + nonce: envelope.iv, + mac: Mac(envelope.mac), + ); + final plaintext = await xchacha20.decrypt( + secretBox, + secretKey: secretKey, + ); + + final share = TrustedFriendShare.fromBuffer(plaintext); + + final receivedShare = ReceivedRecoveryShare( + messageId: msgId, + trustedFriendDisplayName: share.trustedFriend.displayName, + myDisplayName: share.shareUser.displayName, + myUserId: share.shareUser.userId.toInt(), + myAvatarSvg: share.shareUser.hasAvatar() + ? share.shareUser.avatar + : null, + threshold: share.threshold, + sharedSecretDataBytes: share.sharedSecretData, + ); + + state.receivedShares.add(receivedShare); + didUpdate = true; + + Log.info( + 'Received recovery share from ${share.trustedFriend.displayName} ' + 'for user ${share.shareUser.displayName}', + ); + } catch (e) { + Log.error( + 'Failed to decrypt/parse passwordless notification message $msgId: $e', + ); + } + } + + if (didUpdate) { + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (s) => s.receivedShares = state.receivedShares, + ); + } + + return didUpdate; + } } diff --git a/lib/src/services/profile.service.dart b/lib/src/services/profile.service.dart index 5a80f5cb..8c37b7c5 100644 --- a/lib/src/services/profile.service.dart +++ b/lib/src/services/profile.service.dart @@ -1,8 +1 @@ -enum SetupProfile { standard, customized, maximum } - -enum SecurityProfile { normal, strict } - -extension SecurityProfileExtension on SecurityProfile { - bool get showWarningForNonVerifiedContacts => this == SecurityProfile.strict; - bool get showOnlyVerifiedInChatViewList => this == SecurityProfile.normal; -} +enum SetupProfile { standard, customized } diff --git a/lib/src/services/signal/encryption.signal.dart b/lib/src/services/signal/encryption.signal.dart index f13c9960..5c29910e 100644 --- a/lib/src/services/signal/encryption.signal.dart +++ b/lib/src/services/signal/encryption.signal.dart @@ -38,8 +38,9 @@ Future<(EncryptedContent?, PlaintextContent_DecryptionErrorMessage_Type?)> signalDecryptMessage( int fromUserId, Uint8List encryptedContentRaw, - int type, -) async { + int type, { + Set? brokenSessionsInCurrentBatch, +}) async { // Hold the lock only for the cryptographic operation, not for network I/O Log.info('Acquiring lockingSignalProtocol for $fromUserId'); final ( @@ -74,6 +75,7 @@ signalDecryptMessage( ); } + recordResyncAttempt(fromUserId, success: true); return (EncryptedContent.fromBuffer(plaintext), null, false); } on InvalidKeyIdException catch (e) { Log.warn(e); @@ -113,22 +115,25 @@ signalDecryptMessage( // Handle session resync OUTSIDE the lock to avoid holding it during // network round-trips (which can block for up to 60 seconds) - if (needsResync && !resyncedUsers.contains(fromUserId)) { - if (await handleSessionResync(fromUserId)) { - // This flag prevents from resyncing the session the client received - // multiple new messages from the server he could not decrypt - resyncedUsers.add(fromUserId); + if (needsResync) { + brokenSessionsInCurrentBatch?.add(fromUserId); + if (shouldAttemptResync(fromUserId)) { + if (await handleSessionResync(fromUserId)) { + // This flag prevents from resyncing the session the client received + // multiple new messages from the server he could not decrypt + recordResyncAttempt(fromUserId, success: false); - // This message contains a new PreKeyBundle establishing a new signal - // session - await sendCipherText( - fromUserId, - EncryptedContent( - errorMessages: EncryptedContent_ErrorMessages( - type: EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC, + // This message contains a new PreKeyBundle establishing a new signal + // session + await sendCipherText( + fromUserId, + EncryptedContent( + errorMessages: EncryptedContent_ErrorMessages( + type: EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC, + ), ), - ), - ); + ); + } } } diff --git a/lib/src/services/signal/protocol_state.signal.dart b/lib/src/services/signal/protocol_state.signal.dart index 9d797473..58f830e8 100644 --- a/lib/src/services/signal/protocol_state.signal.dart +++ b/lib/src/services/signal/protocol_state.signal.dart @@ -1,12 +1,36 @@ +import 'dart:math'; import 'package:mutex/mutex.dart'; /// Unified lock for all Signal protocol operations (encryption, decryption, session management). final lockingSignalProtocol = Mutex(); /// Tracking users who have already been resynced in the current session. -final resyncedUsers = {}; +final Map _resyncAttempts = {}; -/// Reset the resync tracking set. -void resetResyncedUsers() { - resyncedUsers.clear(); +const int maxResyncAttempts = 3; + +bool shouldAttemptResync(int userId) { + final attempt = _resyncAttempts[userId]; + if (attempt == null) return true; + if (attempt.failureCount >= maxResyncAttempts) return false; + + final cooldown = Duration(minutes: 5 * pow(5, attempt.failureCount - 1).toInt()); + return DateTime.now().difference(attempt.lastAttempt) > cooldown; +} + +void recordResyncAttempt(int userId, {required bool success}) { + if (success) { + _resyncAttempts.remove(userId); + } else { + final current = _resyncAttempts[userId]; + _resyncAttempts[userId] = ( + failureCount: (current?.failureCount ?? 0) + 1, + lastAttempt: DateTime.now(), + ); + } +} + +/// Reset the resync tracking set (currently unused, backoff handles expiry naturally). +void resetResyncedUsers() { + // No-op. We want the backoff state to persist across reconnects. } diff --git a/lib/src/services/user_study.service.dart b/lib/src/services/user_study.service.dart index 0a28c893..ff738051 100644 --- a/lib/src/services/user_study.service.dart +++ b/lib/src/services/user_study.service.dart @@ -1,4 +1,6 @@ +import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:http/http.dart' as http; import 'package:twonly/locator.dart'; @@ -128,6 +130,12 @@ Future handleUserStudyUpload() async { }); } } catch (e) { - Log.error(e); + if (e is http.ClientException || + e is SocketException || + e is TimeoutException) { + Log.warn('Error uploading user study data: $e'); + } else { + Log.error(e); + } } } diff --git a/lib/src/utils/keyvalue.dart b/lib/src/utils/keyvalue.dart index f879a208..b1ce6202 100644 --- a/lib/src/utils/keyvalue.dart +++ b/lib/src/utils/keyvalue.dart @@ -1,12 +1,39 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:mutex/mutex.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/model/json/backup.model.dart'; +import 'package:twonly/src/model/json/onboarding_state.model.dart'; import 'package:twonly/src/utils/exclusive_access.utils.dart'; import 'package:twonly/src/utils/log.dart'; +typedef ModelFactory = ({ + Object Function(Map json) fromJson, + Map Function(Object value) toJson, + Object Function() defaultValue, +}); + class KeyValueStore { + static final Map _registry = { + OnboardingState: ( + fromJson: OnboardingState.fromJson, + toJson: (val) => (val as OnboardingState).toJson(), + defaultValue: OnboardingState.new, + ), + CurrentBackupStatus: ( + fromJson: CurrentBackupStatus.fromJson, + toJson: (val) => (val as CurrentBackupStatus).toJson(), + defaultValue: CurrentBackupStatus.new, + ), + BackupRecovery: ( + fromJson: BackupRecovery.fromJson, + toJson: (val) => (val as BackupRecovery).toJson(), + defaultValue: () => BackupRecovery(username: '', password: '', userId: 0), + ), + }; + static final Map _mutexes = {}; static Mutex _getMutex(String key) { @@ -65,4 +92,77 @@ class KeyValueStore { } }); } + + static Future update({ + required String key, + required FutureOr Function(T value) update, + }) async { + final factory = _registry[T]; + if (factory == null) { + throw ArgumentError('Type $T is not registered in KeyValueStore.'); + } + return _exclusive(key, () async { + T val; + final file = await _getFilePath(key); + try { + if (file.existsSync()) { + final contents = await file.readAsString(); + val = factory.fromJson(jsonDecode(contents) as Map) as T; + } else { + val = factory.defaultValue() as T; + } + } catch (e) { + Log.warn('Error reading file. Resetting to default.: $e'); + val = factory.defaultValue() as T; + } + + await update(val); + + try { + await file.parent.create(recursive: true); + await file.writeAsString(jsonEncode(factory.toJson(val as Object))); + } catch (e) { + Log.error('Error writing file: $e'); + } + return val; + }); + } + + static Future getModel(String key) async { + final factory = _registry[T]; + if (factory == null) { + throw ArgumentError('Type $T is not registered in KeyValueStore.'); + } + return _exclusive(key, () async { + final file = await _getFilePath(key); + try { + if (file.existsSync()) { + final contents = await file.readAsString(); + return factory.fromJson(jsonDecode(contents) as Map) as T; + } + } catch (e) { + Log.warn('Error reading file. Returning default.: $e'); + } + return factory.defaultValue() as T; + }); + } + + static Future getModelOrNull(String key) async { + final factory = _registry[T]; + if (factory == null) { + throw ArgumentError('Type $T is not registered in KeyValueStore.'); + } + return _exclusive(key, () async { + final file = await _getFilePath(key); + try { + if (file.existsSync()) { + final contents = await file.readAsString(); + return factory.fromJson(jsonDecode(contents) as Map) as T; + } + } catch (e) { + Log.warn('Error reading file.: $e'); + } + return null; + }); + } } diff --git a/lib/src/utils/log.dart b/lib/src/utils/log.dart index 63a73b36..9af0d49e 100644 --- a/lib/src/utils/log.dart +++ b/lib/src/utils/log.dart @@ -41,10 +41,14 @@ class Log { } static void error( - Object? messageInput, [ + Object? messageInput, { Object? error, StackTrace? stackTrace, - ]) { + bool onlyIfSentryEnabled = false, + }) { + if (!AppState.allowErrorTrackingViaSentry && onlyIfSentryEnabled) { + return; + } final message = filterLogMessage('$messageInput'); if (AppState.allowErrorTrackingViaSentry) { try { diff --git a/lib/src/utils/misc.dart b/lib/src/utils/misc.dart index 0ddc1fbe..2c8428eb 100644 --- a/lib/src/utils/misc.dart +++ b/lib/src/utils/misc.dart @@ -16,6 +16,7 @@ import 'package:provider/provider.dart'; import 'package:twonly/src/localization/generated/app_localizations.dart'; import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'; import 'package:twonly/src/providers/settings.provider.dart'; +import 'package:twonly/src/services/backup.service.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -93,10 +94,10 @@ Future saveVideoToGallery( if (!hasAccess) { await Gal.requestAccess(toAlbum: true); } - + var pathToSave = videoPath; File? tempFile; - + try { if (name != null) { final file = File(videoPath); @@ -405,3 +406,43 @@ String joinWithAnd(List items, String andWord) { if (items.length == 1) return items.first; return '${items.sublist(0, items.length - 1).join(', ')} $andWord ${items.last}'; } + +String createEmailHint(String email) { + final parts = email.split('@'); + if (parts.length != 2) return email; + + final local = parts[0]; + final domain = parts[1]; + + final localMasked = local.length > 2 + ? '${local[0]}${'*' * (local.length - 2)}${local[local.length - 1]}' + : local; + + final domainParts = domain.split('.'); + if (domainParts.isEmpty) return '$localMasked@$domain'; + + final domainName = domainParts[0]; + final domainNameMasked = domainName.length > 1 + ? '${domainName[0]}${'*' * (domainName.length - 1)}' + : domainName; + + final restDomain = domainParts.skip(1).join('.'); + return '$localMasked@$domainNameMasked${restDomain.isNotEmpty ? '.$restDomain' : ''}'; +} + +extension RecoveryErrorLocalization on RecoveryError { + String toLocalizedString(BuildContext context) { + switch (this) { + case RecoveryError.noInternet: + return context.lang.recoverErrorNoInternet; + case RecoveryError.usernameNotValid: + return context.lang.recoverErrorUsernameNotValid; + case RecoveryError.passwordInvalid: + return context.lang.recoverErrorPasswordInvalid; + case RecoveryError.tryAgainLater: + return context.lang.recoverErrorTryAgainLater; + case RecoveryError.unkownError: + return context.lang.recoverErrorUnknown; + } + } +} diff --git a/lib/src/visual/components/avatar_icon.comp.dart b/lib/src/visual/components/avatar_icon.comp.dart index eb23a144..a11f675b 100644 --- a/lib/src/visual/components/avatar_icon.comp.dart +++ b/lib/src/visual/components/avatar_icon.comp.dart @@ -16,12 +16,14 @@ class AvatarIcon extends StatefulWidget { this.contactId, this.myAvatar = false, this.fontSize = 20, + this.svg, this.color, }); final Group? group; final int? contactId; final bool myAvatar; final double? fontSize; + final String? svg; final Color? color; @override @@ -145,7 +147,12 @@ class _AvatarIconState extends State { Widget avatars = Container(); - if (widget.myAvatar) { + if (widget.svg != null) { + avatars = SvgPicture.string( + widget.svg!, + errorBuilder: errorBuilder, + ); + } else if (widget.myAvatar) { if (_myAvatarPath != null) { avatars = Image.file( File(_myAvatarPath!), diff --git a/lib/src/visual/components/profile_qr_code.comp.dart b/lib/src/visual/components/profile_qr_code.comp.dart index 5c194703..c10eb852 100644 --- a/lib/src/visual/components/profile_qr_code.comp.dart +++ b/lib/src/visual/components/profile_qr_code.comp.dart @@ -2,6 +2,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:qr_flutter/qr_flutter.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/avatars.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/qr.utils.dart'; @@ -16,6 +18,65 @@ class ProfileQrCodeComp extends StatefulWidget { final double size; final bool showAvatar; + static Future showSheet( + BuildContext context, { + Contact? contact, + bool openToVerify = false, + }) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) { + return Container( + decoration: BoxDecoration( + color: context.color.surface, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(24), + ), + ), + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(width: double.infinity), + Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: context.color.onSurface.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(height: 24), + Text( + openToVerify + ? (contact != null + ? context.lang.letUserScanQrCode( + getContactDisplayName(contact), + ) + : context.lang.letFriendScanQrToVerify) + : (contact != null + ? context.lang.letUserScanQrCode( + getContactDisplayName(contact), + ) + : context.lang.addContactQrSheetSubtext), + style: TextStyle( + fontSize: 14, + color: context.color.onSurface.withValues(alpha: 0.6), + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + const ProfileQrCodeComp(), + const SizedBox(height: 34), + ], + ), + ); + }, + ); + } + @override State createState() => _ProfileQrCodeCompState(); } diff --git a/lib/src/visual/components/verification_badge.comp.dart b/lib/src/visual/components/verification_badge.comp.dart index a2cb731f..0d096caa 100644 --- a/lib/src/visual/components/verification_badge.comp.dart +++ b/lib/src/visual/components/verification_badge.comp.dart @@ -33,7 +33,7 @@ class VerificationBadgeComp extends StatefulWidget { } class _VerificationBadgeCompState extends State { - bool _isVerified = false; + late bool _isVerified = widget.contact?.verified ?? true; bool _isSharedVerified = false; int _verifiedByTransferredTrustCount = 0; int _sharedByVerifiedCount = 0; diff --git a/lib/src/visual/components/verification_badge_info.comp.dart b/lib/src/visual/components/verification_badge_info.comp.dart index 5d0bbc6c..eb78ba4a 100644 --- a/lib/src/visual/components/verification_badge_info.comp.dart +++ b/lib/src/visual/components/verification_badge_info.comp.dart @@ -2,10 +2,11 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart' show FaIcon, FontAwesomeIcons; import 'package:go_router/go_router.dart'; -import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; -import 'package:twonly/src/services/profile.service.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/profile_qr_code.comp.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; import 'package:twonly/src/visual/elements/svg_icon.element.dart'; import 'package:twonly/src/visual/themes/light.dart'; @@ -15,12 +16,62 @@ const colorVerificationBadgeYellow = Color.fromARGB(255, 0, 182, 238); class VerificationBadgeInfo extends StatelessWidget { const VerificationBadgeInfo({ this.displayButtons = false, + this.contact, super.key, }); final bool displayButtons; + final Contact? contact; @override Widget build(BuildContext context) { + final scanButton = MyButton( + variant: MyButtonVariant.primaryDense, + onPressed: () => context.push( + Routes.cameraQRScanner, + extra: { + if (contact != null) 'contact': contact, + 'openToVerify': true, + }, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + const FaIcon(FontAwesomeIcons.camera), + const SizedBox(width: 6), + Text( + contact != null + ? context.lang.scanUserQrCode( + getContactDisplayName(contact!), + ) + : context.lang.scanNow, + ), + ], + ), + ); + + final openButton = MyButton( + variant: MyButtonVariant.primaryDense, + onPressed: () => ProfileQrCodeComp.showSheet( + context, + contact: contact, + openToVerify: true, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + const FaIcon(FontAwesomeIcons.qrcode), + const SizedBox(width: 6), + Text( + contact != null + ? context.lang.openOwnQrCode + : context.lang.openQrCode, + ), + ], + ), + ); + return Column( children: [ RichText( @@ -40,56 +91,45 @@ class VerificationBadgeInfo extends StatelessWidget { icon: const SvgIcon(assetPath: SvgIcons.verifiedGreen, size: 40), description: context.lang.verificationBadgeGreenDesc, boldTextColor: primaryColor, - onTap: () => context.push(Routes.cameraQRScanner), + onTap: () => context.push( + Routes.cameraQRScanner, + extra: { + if (contact != null) 'contact': contact, + 'openToVerify': true, + }, + ), ), if (displayButtons) Padding( padding: const EdgeInsets.only(bottom: 20), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IntrinsicWidth( - child: MyButton( - variant: MyButtonVariant.primaryDense, - onPressed: () => context.push(Routes.cameraQRScanner), - child: Row( - children: [ - const FaIcon(FontAwesomeIcons.camera), - const SizedBox(width: 6), - Text(context.lang.scanNow), - ], - ), + child: contact != null + ? Column( + mainAxisSize: MainAxisSize.min, + children: [ + scanButton, + const SizedBox(height: 8), + openButton, + ], + ) + : Row( + mainAxisSize: MainAxisSize.min, + children: [ + IntrinsicWidth(child: scanButton), + const SizedBox(width: 8), + IntrinsicWidth(child: openButton), + ], ), - ), - const SizedBox(width: 8), - IntrinsicWidth( - child: MyButton( - variant: MyButtonVariant.primaryDense, - onPressed: () => context.push(Routes.settingsPublicProfile), - child: Row( - children: [ - const FaIcon(FontAwesomeIcons.qrcode), - const SizedBox(width: 6), - Text(context.lang.openQrCode), - ], - ), - ), - ), - ], - ), ), - if (userService.currentUser.securityProfile != SecurityProfile.strict || - userService.currentUser.isUserDiscoveryEnabled) - _buildItem( - context, - icon: const SvgIcon( - assetPath: SvgIcons.verifiedGreen, - size: 40, - color: colorVerificationBadgeYellow, - ), - description: context.lang.verificationBadgeYellowDesc, - boldTextColor: colorVerificationBadgeYellow, + _buildItem( + context, + icon: const SvgIcon( + assetPath: SvgIcons.verifiedGreen, + size: 40, + color: colorVerificationBadgeYellow, ), + description: context.lang.verificationBadgeYellowDesc, + boldTextColor: colorVerificationBadgeYellow, + ), _buildItem( context, icon: const SvgIcon(assetPath: SvgIcons.verifiedRed, size: 40), diff --git a/lib/src/visual/context_menu/group.context_menu.dart b/lib/src/visual/context_menu/group.context_menu.dart index 02fe6a17..63c8d881 100644 --- a/lib/src/visual/context_menu/group.context_menu.dart +++ b/lib/src/visual/context_menu/group.context_menu.dart @@ -20,6 +20,7 @@ class GroupContextMenu extends StatelessWidget { @override Widget build(BuildContext context) { + final navigator = Navigator.of(context); return ContextMenu( items: [ if (!group.archived) @@ -27,9 +28,7 @@ class GroupContextMenu extends StatelessWidget { title: context.lang.contextMenuArchiveUser, onTap: () async { const update = GroupsCompanion(archived: Value(true)); - if (context.mounted) { - await twonlyDB.groupsDao.updateGroup(group.groupId, update); - } + await twonlyDB.groupsDao.updateGroup(group.groupId, update); }, icon: Icons.archive_outlined, ), @@ -38,15 +37,13 @@ class GroupContextMenu extends StatelessWidget { title: context.lang.contextMenuUndoArchiveUser, onTap: () async { const update = GroupsCompanion(archived: Value(false)); - if (context.mounted) { - await twonlyDB.groupsDao.updateGroup(group.groupId, update); - } + await twonlyDB.groupsDao.updateGroup(group.groupId, update); }, icon: Icons.unarchive_outlined, ), ContextMenuItem( title: context.lang.contextMenuOpenChat, - onTap: () => context.push(Routes.chatsMessages(group.groupId)), + onTap: () => navigator.context.push(Routes.chatsMessages(group.groupId)), icon: FontAwesomeIcons.comments, ), if (!group.archived) @@ -56,9 +53,7 @@ class GroupContextMenu extends StatelessWidget { : context.lang.contextMenuPin, onTap: () async { final update = GroupsCompanion(pinned: Value(!group.pinned)); - if (context.mounted) { - await twonlyDB.groupsDao.updateGroup(group.groupId, update); - } + await twonlyDB.groupsDao.updateGroup(group.groupId, update); }, icon: group.pinned ? FontAwesomeIcons.thumbtackSlash @@ -69,9 +64,9 @@ class GroupContextMenu extends StatelessWidget { icon: FontAwesomeIcons.trashCan, onTap: () async { final ok = await showAlertDialog( - context, - context.lang.deleteTitle, - context.lang.groupContextMenuDeleteGroup, + navigator.context, + navigator.context.lang.deleteTitle, + navigator.context.lang.groupContextMenuDeleteGroup, ); if (ok) { await twonlyDB.messagesDao.deleteMessagesByGroupId(group.groupId); diff --git a/lib/src/visual/context_menu/user.context_menu.dart b/lib/src/visual/context_menu/user.context_menu.dart index 1a35873d..59278a86 100644 --- a/lib/src/visual/context_menu/user.context_menu.dart +++ b/lib/src/visual/context_menu/user.context_menu.dart @@ -17,12 +17,13 @@ class UserContextMenu extends StatelessWidget { @override Widget build(BuildContext context) { + final navigator = Navigator.of(context); return ContextMenu( minWidth: 150, items: [ ContextMenuItem( title: context.lang.contextMenuUserProfile, - onTap: () => context.push(Routes.profileContact(contact.userId)), + onTap: () => navigator.context.push(Routes.profileContact(contact.userId)), icon: FontAwesomeIcons.user, ), ], diff --git a/lib/src/visual/elements/contact_chip.element.dart b/lib/src/visual/elements/contact_chip.element.dart new file mode 100644 index 00000000..d6506396 --- /dev/null +++ b/lib/src/visual/elements/contact_chip.element.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; +import 'package:twonly/src/visual/components/verification_badge.comp.dart'; +import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart'; +import 'package:twonly/src/visual/views/contact/contact.view.dart'; + +class ContactChip extends StatelessWidget { + const ContactChip({ + required this.contact, + this.onTap, + super.key, + }); + + final Contact contact; + final void Function(int)? onTap; + + @override + Widget build(BuildContext context) { + return ReactiveTapFeedback( + onTap: () { + if (onTap != null) { + onTap!(contact.userId); + } else { + context.navPush( + ContactView( + contact.userId, + key: ValueKey(contact.userId), + ), + ); + } + }, + child: Chip( + avatar: AvatarIcon( + contactId: contact.userId, + fontSize: 10, + ), + label: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + getContactDisplayName(contact), + style: const TextStyle(fontSize: 14), + overflow: TextOverflow.ellipsis, + ), + const SizedBox(width: 6), + VerificationBadgeComp( + contact: contact, + size: 12, + clickable: false, + ), + if (onTap != null) ...[ + const SizedBox(width: 15), + const FaIcon( + FontAwesomeIcons.xmark, + color: Colors.grey, + size: 12, + ), + ], + ], + ), + ), + ); + } +} diff --git a/lib/src/visual/elements/my_button.element.dart b/lib/src/visual/elements/my_button.element.dart index 94766724..ee7ca730 100644 --- a/lib/src/visual/elements/my_button.element.dart +++ b/lib/src/visual/elements/my_button.element.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter/physics.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart'; import 'package:twonly/src/visual/themes/light.dart'; enum MyButtonVariant { @@ -32,72 +32,10 @@ class MyButton extends StatefulWidget { State createState() => _MyButtonState(); } -class _MyButtonState extends State - with SingleTickerProviderStateMixin { - late final AnimationController _controller; - - @override - void initState() { - super.initState(); - _controller = - AnimationController( - vsync: this, - lowerBound: double.negativeInfinity, - upperBound: double.infinity, - value: 0, - )..addListener(() { - setState(() {}); - }); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _onTapDown(TapDownDetails details) { - if (widget.onPressed != null || widget.onLongPress != null) { - _controller.animateTo( - 1, - duration: const Duration(milliseconds: 60), - curve: Curves.easeOut, - ); - } - } - - void _onTapUp(TapUpDetails details) { - if (widget.onPressed != null || widget.onLongPress != null) { - _bounce(); - } - } - - void _onTapCancel() { - if (widget.onPressed != null || widget.onLongPress != null) { - _bounce(); - } - } - - void _bounce() { - const spring = SpringDescription( - mass: 1, - stiffness: 400, - damping: 15, - ); - final simulation = SpringSimulation( - spring, - _controller.value, - 0, - _controller.velocity, - ); - _controller.animateWith(simulation); - } +class _MyButtonState extends State { @override Widget build(BuildContext context) { - // 0 (unpressed) -> scale 1.0 - // 1 (pressed) -> scale 0.98 (subtle bounce) - final scale = 1.0 - (_controller.value * 0.02); final isEnabled = widget.onPressed != null || widget.onLongPress != null; final isDark = isDarkMode(context); final disabledBgColor = isDark @@ -265,18 +203,11 @@ class _MyButtonState extends State child: widget.child, ); - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTapDown: isEnabled ? _onTapDown : null, - onTapUp: isEnabled ? _onTapUp : null, - onTapCancel: isEnabled ? _onTapCancel : null, + return ReactiveTapFeedback( onTap: widget.onPressed, onLongPress: widget.onLongPress, - child: Transform.scale( - scale: scale, - child: AbsorbPointer( - child: childButton, - ), + child: AbsorbPointer( + child: childButton, ), ); } diff --git a/lib/src/visual/elements/my_icon_button.element.dart b/lib/src/visual/elements/my_icon_button.element.dart index 8ec26b70..f982240b 100644 --- a/lib/src/visual/elements/my_icon_button.element.dart +++ b/lib/src/visual/elements/my_icon_button.element.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter/physics.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart'; import 'package:twonly/src/visual/themes/light.dart'; enum MyIconButtonVariant { @@ -26,69 +26,10 @@ class MyIconButton extends StatefulWidget { State createState() => _MyIconButtonState(); } -class _MyIconButtonState extends State - with SingleTickerProviderStateMixin { - late final AnimationController _controller; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - vsync: this, - lowerBound: double.negativeInfinity, - upperBound: double.infinity, - value: 0, - )..addListener(() { - setState(() {}); - }); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _onTapDown(TapDownDetails details) { - if (widget.onPressed != null || widget.onLongPress != null) { - _controller.animateTo( - 1, - duration: const Duration(milliseconds: 60), - curve: Curves.easeOut, - ); - } - } - - void _onTapUp(TapUpDetails details) { - if (widget.onPressed != null || widget.onLongPress != null) { - _bounce(); - } - } - - void _onTapCancel() { - if (widget.onPressed != null || widget.onLongPress != null) { - _bounce(); - } - } - - void _bounce() { - const spring = SpringDescription( - mass: 1, - stiffness: 400, - damping: 15, - ); - final simulation = SpringSimulation( - spring, - _controller.value, - 0, - _controller.velocity, - ); - _controller.animateWith(simulation); - } +class _MyIconButtonState extends State { @override Widget build(BuildContext context) { - final scale = 1.0 - (_controller.value * 0.02); final isEnabled = widget.onPressed != null || widget.onLongPress != null; final isDark = isDarkMode(context); final disabledBgColor = isDark @@ -127,18 +68,11 @@ class _MyIconButtonState extends State child: widget.icon, ); - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTapDown: isEnabled ? _onTapDown : null, - onTapUp: isEnabled ? _onTapUp : null, - onTapCancel: isEnabled ? _onTapCancel : null, + return ReactiveTapFeedback( onTap: widget.onPressed, onLongPress: widget.onLongPress, - child: Transform.scale( - scale: scale, - child: AbsorbPointer( - child: childButton, - ), + child: AbsorbPointer( + child: childButton, ), ); } diff --git a/lib/src/visual/elements/my_input.element.dart b/lib/src/visual/elements/my_input.element.dart index afe439f7..2274f554 100644 --- a/lib/src/visual/elements/my_input.element.dart +++ b/lib/src/visual/elements/my_input.element.dart @@ -1,8 +1,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/physics.dart'; import 'package:flutter/services.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart'; class MyInput extends StatefulWidget { const MyInput({ @@ -18,6 +18,8 @@ class MyInput extends StatefulWidget { this.errorText, this.obscureText = false, this.dense = false, + this.readOnly = false, + this.fontWeight, super.key, }); @@ -33,70 +35,16 @@ class MyInput extends StatefulWidget { final String? errorText; final bool obscureText; final bool dense; + final bool readOnly; + final FontWeight? fontWeight; @override State createState() => _MyInputState(); } -class _MyInputState extends State with SingleTickerProviderStateMixin { - late final AnimationController _controller; - - @override - void initState() { - super.initState(); - _controller = - AnimationController( - vsync: this, - lowerBound: double.negativeInfinity, - upperBound: double.infinity, - value: 0, - )..addListener(() { - setState(() {}); - }); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _onTapDown(TapDownDetails details) { - _controller.animateTo( - 1, - duration: const Duration(milliseconds: 60), - curve: Curves.easeOut, - ); - } - - void _onTapUp(TapUpDetails details) { - _bounce(); - } - - void _onTapCancel() { - _bounce(); - } - - void _bounce() { - const spring = SpringDescription( - mass: 1, - stiffness: 400, - damping: 15, - ); - final simulation = SpringSimulation( - spring, - _controller.value, - 0, - _controller.velocity, - ); - _controller.animateWith(simulation); - } - +class _MyInputState extends State { @override Widget build(BuildContext context) { - // 0 (unpressed) -> scale 1.0 - // 1 (pressed) -> scale 0.98 (subtle bounce) - final scale = 1.0 - (_controller.value * 0.02); final isDark = isDarkMode(context); final inputFillColor = isDark @@ -115,125 +63,121 @@ class _MyInputState extends State with SingleTickerProviderStateMixin { ? Colors.white.withValues(alpha: 0.6) : Colors.black.withValues(alpha: 0.6); - return GestureDetector( + return ReactiveTapFeedback( behavior: HitTestBehavior.translucent, - onTapDown: _onTapDown, - onTapUp: _onTapUp, - onTapCancel: _onTapCancel, - child: Transform.scale( - scale: scale, - child: TextField( - controller: widget.controller, - onChanged: widget.onChanged, - onSubmitted: widget.onSubmitted, - onTapOutside: (event) { - final pointer = event.pointer; - final startPosition = event.position; - var moved = false; + alwaysAnimate: true, + child: TextField( + controller: widget.controller, + readOnly: widget.readOnly, + onChanged: widget.onChanged, + onSubmitted: widget.onSubmitted, + onTapOutside: (event) { + final pointer = event.pointer; + final startPosition = event.position; + var moved = false; - void handlePointerEvent(PointerEvent routeEvent) { - if (routeEvent is PointerMoveEvent) { - if ((routeEvent.position - startPosition).distance > 10) { - moved = true; - } - } else if (routeEvent is PointerUpEvent) { - GestureBinding.instance.pointerRouter.removeRoute( - pointer, - handlePointerEvent, - ); - if (!moved) { - FocusManager.instance.primaryFocus?.unfocus(); - } - } else if (routeEvent is PointerCancelEvent) { - GestureBinding.instance.pointerRouter.removeRoute( - pointer, - handlePointerEvent, - ); + void handlePointerEvent(PointerEvent routeEvent) { + if (routeEvent is PointerMoveEvent) { + if ((routeEvent.position - startPosition).distance > 10) { + moved = true; } + } else if (routeEvent is PointerUpEvent) { + GestureBinding.instance.pointerRouter.removeRoute( + pointer, + handlePointerEvent, + ); + if (!moved) { + FocusManager.instance.primaryFocus?.unfocus(); + } + } else if (routeEvent is PointerCancelEvent) { + GestureBinding.instance.pointerRouter.removeRoute( + pointer, + handlePointerEvent, + ); } + } - GestureBinding.instance.pointerRouter.addRoute( - pointer, - handlePointerEvent, - ); - }, - inputFormatters: widget.inputFormatters, - keyboardType: widget.keyboardType, - autofocus: widget.autofocus, - obscureText: widget.obscureText, - style: TextStyle( + GestureBinding.instance.pointerRouter.addRoute( + pointer, + handlePointerEvent, + ); + }, + inputFormatters: widget.inputFormatters, + keyboardType: widget.keyboardType, + autofocus: widget.autofocus, + obscureText: widget.obscureText, + style: TextStyle( + fontSize: widget.dense ? 16 : 18, + fontWeight: widget.fontWeight ?? FontWeight.w500, + color: isDark ? Colors.white : Colors.black87, + ), + decoration: InputDecoration( + isDense: widget.dense, + hintText: widget.hintText, + hintStyle: TextStyle( + color: inputHintColor, fontSize: widget.dense ? 16 : 18, - fontWeight: FontWeight.w500, - color: isDark ? Colors.white : Colors.black87, ), - decoration: InputDecoration( - isDense: widget.dense, - hintText: widget.hintText, - hintStyle: TextStyle( - color: inputHintColor, - fontSize: widget.dense ? 16 : 18, + filled: true, + fillColor: inputFillColor, + contentPadding: EdgeInsets.symmetric( + vertical: widget.dense ? 13 : 18, + horizontal: widget.dense ? 13 : 24, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), + borderSide: BorderSide( + color: inputBorderColor, ), - filled: true, - fillColor: inputFillColor, - contentPadding: EdgeInsets.symmetric( - vertical: widget.dense ? 13 : 18, - horizontal: widget.dense ? 13 : 24, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), + borderSide: BorderSide( + color: inputBorderColor, ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), - borderSide: BorderSide( - color: inputBorderColor, - ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), + borderSide: BorderSide( + color: isDark ? Colors.white : Colors.black87, + width: 2, ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), - borderSide: BorderSide( - color: inputBorderColor, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), - borderSide: BorderSide( - color: isDark ? Colors.white : Colors.black87, - width: 2, - ), - ), - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), - borderSide: const BorderSide( - color: Colors.redAccent, - ), - ), - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), - borderSide: const BorderSide( - color: Colors.redAccent, - width: 2, - ), - ), - errorStyle: const TextStyle( + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), + borderSide: const BorderSide( color: Colors.redAccent, - fontSize: 14, - fontWeight: FontWeight.w500, ), - errorText: widget.errorText, - prefixIcon: widget.prefixIcon != null - ? IconTheme( - data: IconThemeData( - color: prefixIconColor, - ), - child: widget.prefixIcon!, - ) - : null, - suffixIcon: widget.suffixIcon != null - ? IconTheme( - data: IconThemeData( - color: isDark ? Colors.white : Colors.black87, - ), - child: widget.suffixIcon!, - ) - : null, ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), + borderSide: const BorderSide( + color: Colors.redAccent, + width: 2, + ), + ), + errorStyle: const TextStyle( + color: Colors.redAccent, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + errorText: widget.errorText, + prefixIcon: widget.prefixIcon != null + ? IconTheme( + data: IconThemeData( + color: prefixIconColor, + ), + child: widget.prefixIcon!, + ) + : null, + suffixIcon: widget.suffixIcon != null + ? IconTheme( + data: IconThemeData( + color: isDark ? Colors.white : Colors.black87, + ), + child: widget.suffixIcon!, + ) + : null, ), ), ); diff --git a/lib/src/visual/elements/reactive_tap_feedback.element.dart b/lib/src/visual/elements/reactive_tap_feedback.element.dart new file mode 100644 index 00000000..fe599d29 --- /dev/null +++ b/lib/src/visual/elements/reactive_tap_feedback.element.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; + +class ReactiveTapFeedback extends StatefulWidget { + const ReactiveTapFeedback({ + required this.child, + this.onTap, + this.onLongPress, + this.behavior = HitTestBehavior.opaque, + this.alwaysAnimate = false, + super.key, + }); + + final Widget child; + final VoidCallback? onTap; + final VoidCallback? onLongPress; + final HitTestBehavior behavior; + final bool alwaysAnimate; + + @override + State createState() => _ReactiveTapFeedbackState(); +} + +class _ReactiveTapFeedbackState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + lowerBound: double.negativeInfinity, + upperBound: double.infinity, + value: 0, + )..addListener(() { + setState(() {}); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + bool get _shouldAnimate => + widget.alwaysAnimate || + widget.onTap != null || + widget.onLongPress != null; + + void _onTapDown(TapDownDetails details) { + if (_shouldAnimate) { + _controller.animateTo( + 1, + duration: const Duration(milliseconds: 60), + curve: Curves.easeOut, + ); + } + } + + void _onTapUp(TapUpDetails details) { + if (_shouldAnimate) { + _bounce(); + } + } + + void _onTapCancel() { + if (_shouldAnimate) { + _bounce(); + } + } + + void _bounce() { + const spring = SpringDescription( + mass: 1, + stiffness: 400, + damping: 15, + ); + final simulation = SpringSimulation( + spring, + _controller.value, + 0, + _controller.velocity, + ); + _controller.animateWith(simulation); + } + + @override + Widget build(BuildContext context) { + // 0 (unpressed) -> scale 1.0 + // 1 (pressed) -> scale 0.98 (subtle bounce) + final scale = 1.0 - (_controller.value * 0.02); + + return GestureDetector( + behavior: widget.behavior, + onTapDown: _shouldAnimate ? _onTapDown : null, + onTapUp: _shouldAnimate ? _onTapUp : null, + onTapCancel: _shouldAnimate ? _onTapCancel : null, + onTap: widget.onTap, + onLongPress: widget.onLongPress, + child: Transform.scale( + scale: scale, + child: widget.child, + ), + ); + } +} diff --git a/lib/src/visual/helpers/screenshot.helper.dart b/lib/src/visual/helpers/screenshot.helper.dart index 654de721..7ded4f3b 100644 --- a/lib/src/visual/helpers/screenshot.helper.dart +++ b/lib/src/visual/helpers/screenshot.helper.dart @@ -28,7 +28,7 @@ class ScreenshotImageHelper { try { return imageBytes = await imageBytesFuture; } catch (e) { - Log.error('Could not resolve imageBytesFuture: $e'); + Log.warn('Could not resolve imageBytesFuture: $e'); return null; } } @@ -36,14 +36,14 @@ class ScreenshotImageHelper { try { return imageBytes = await file!.readAsBytes(); } catch (e) { - Log.error('Could not read bytes from file: $e'); + Log.warn('Could not read bytes from file: $e'); return null; } } if (image == null) return null; final img = await image!.toByteData(format: io.ImageByteFormat.png); if (img == null) { - Log.error('Got no image'); + Log.warn('Got no image'); return null; } return imageBytes = img.buffer.asUint8List(); @@ -94,7 +94,7 @@ class ScreenshotController { }); return completer.future; } - Log.error(e); + Log.warn('Could not capture screenshot: $e'); } return null; } diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart index 49810100..e6533042 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart @@ -289,12 +289,18 @@ class _CameraPreviewViewState extends State { mc.cameraController == null) { return; } - await mc.cameraController?.setZoomLevel( - newScale.clamp( - mc.selectedCameraDetails.minAvailableZoom, - mc.selectedCameraDetails.maxAvailableZoom, - ), - ); + try { + if (mc.cameraController!.value.isInitialized) { + await mc.cameraController!.setZoomLevel( + newScale.clamp( + mc.selectedCameraDetails.minAvailableZoom, + mc.selectedCameraDetails.maxAvailableZoom, + ), + ); + } + } on CameraException catch (e) { + Log.warn('Failed to set zoom level: $e'); + } setState(() { mc.selectedCameraDetails.scaleFactor = newScale; }); @@ -491,9 +497,16 @@ class _CameraPreviewViewState extends State { (_basePanY - (details.localPosition.dy as double)) / 30) .clamp(1, mc.selectedCameraDetails.maxAvailableZoom); }); - await mc.cameraController!.setZoomLevel( - mc.selectedCameraDetails.scaleFactor, - ); + try { + if (mc.cameraController != null && + mc.cameraController!.value.isInitialized) { + await mc.cameraController!.setZoomLevel( + mc.selectedCameraDetails.scaleFactor, + ); + } + } on CameraException catch (e) { + Log.warn('Failed to set zoom level: $e'); + } if (!userService.currentUser.hasZoomed) { await UserService.update((u) => u.hasZoomed = true); } diff --git a/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart b/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart index 8f722fc4..551f0869 100644 --- a/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart +++ b/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart @@ -14,6 +14,8 @@ import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart' + show PasswordlessRecoveryService; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/qr.utils.dart'; @@ -52,6 +54,7 @@ class ScannedNewProfile { class MainCameraController { void Function()? setState; + void Function(Contact contact)? onVerificationSuccessDismissed; CameraController? cameraController; ScreenshotController screenshotController = ScreenshotController(); SelectedCameraDetails selectedCameraDetails = SelectedCameraDetails(); @@ -178,7 +181,8 @@ class MainCameraController { selectedCameraDetails.isZoomAble = false; - if (cameraController == null || !cameraController!.value.isInitialized) { + final currentController = cameraController; + if (currentController == null || !currentController.value.isInitialized) { final controllerToDispose = cameraController; cameraController = null; if (controllerToDispose != null) { @@ -188,7 +192,7 @@ class MainCameraController { final hasMic = await micPermissionFuture; if (sessionId != _cameraSessionId) return; - cameraController = CameraController( + final controller = CameraController( AppEnvironment.cameras[cameraId], ResolutionPreset.high, enableAudio: hasMic, @@ -196,22 +200,60 @@ class MainCameraController { ? ImageFormatGroup.nv21 : ImageFormatGroup.bgra8888, ); + + var assignedToGlobal = false; try { - _initializeFuture = cameraController?.initialize(); + _initializeFuture = controller.initialize(); await _initializeFuture; - await cameraController!.startImageStream(_processCameraImage); - await cameraController!.setZoomLevel(selectedCameraDetails.scaleFactor); - if (userService.currentUser.videoStabilizationEnabled && !kDebugMode) { - await cameraController!.setVideoStabilizationMode( - VideoStabilizationMode.level1, + if (sessionId != _cameraSessionId) { + unawaited(controller.dispose()); + return; + } + + if (!controller.value.isInitialized) { + throw CameraException( + 'Uninitialized CameraController', + 'CameraController was not initialized after initialize() completed.', ); } + + await controller.startImageStream(_processCameraImage); + if (sessionId != _cameraSessionId) { + unawaited(controller.dispose()); + return; + } + + await controller.setZoomLevel(selectedCameraDetails.scaleFactor); + if (sessionId != _cameraSessionId) { + unawaited(controller.dispose()); + return; + } + + if (userService.currentUser.videoStabilizationEnabled && !kDebugMode) { + await controller.setVideoStabilizationMode( + VideoStabilizationMode.level1, + ); + if (sessionId != _cameraSessionId) { + unawaited(controller.dispose()); + return; + } + } + + cameraController = controller; + assignedToGlobal = true; } catch (e) { - Log.error('Error initializing camera: $e'); - final controllerToDispose = cameraController; - cameraController = null; - if (controllerToDispose != null) { - unawaited(controllerToDispose.dispose()); + if (e is CameraException) { + Log.warn('Camera initialization failed (CameraException): $e'); + } else { + Log.error('Error initializing camera: $e'); + } + if (!assignedToGlobal) { + unawaited(controller.dispose()); + } else { + if (cameraController == controller) { + cameraController = null; + } + unawaited(controller.dispose()); } initCameraStarted = false; return; @@ -219,35 +261,64 @@ class MainCameraController { } else { try { if (!isVideoRecording) { - await cameraController!.stopImageStream(); + try { + await currentController.stopImageStream(); + } catch (e) { + Log.warn('Could not stop image stream: $e'); + } } + if (sessionId != _cameraSessionId) return; + selectedCameraDetails.scaleFactor = 1; - await cameraController!.setZoomLevel(1); - await cameraController!.setDescription( + await currentController.setZoomLevel(1); + if (sessionId != _cameraSessionId) return; + + await currentController.setDescription( AppEnvironment.cameras[cameraId], ); + if (sessionId != _cameraSessionId) return; + if (!isVideoRecording) { - await cameraController!.startImageStream(_processCameraImage); + await currentController.startImageStream(_processCameraImage); } } catch (e) { - Log.error('Error switching camera description: $e'); + if (e is CameraException) { + Log.warn('Camera description switch failed (CameraException): $e'); + } else { + Log.error('Error switching camera description: $e'); + } initCameraStarted = false; return; } } + if (sessionId != _cameraSessionId) return; + final controller = cameraController; + if (controller == null) { + initCameraStarted = false; + return; + } + try { - await cameraController!.lockCaptureOrientation( + await controller.lockCaptureOrientation( DeviceOrientation.portraitUp, ); - await cameraController!.setFlashMode( + if (sessionId != _cameraSessionId) return; + + await controller.setFlashMode( selectedCameraDetails.isFlashOn ? FlashMode.always : FlashMode.off, ); - selectedCameraDetails.maxAvailableZoom = await cameraController! + if (sessionId != _cameraSessionId) return; + + selectedCameraDetails.maxAvailableZoom = await controller .getMaxZoomLevel(); - selectedCameraDetails.minAvailableZoom = await cameraController! + if (sessionId != _cameraSessionId) return; + + selectedCameraDetails.minAvailableZoom = await controller .getMinZoomLevel(); + if (sessionId != _cameraSessionId) return; + selectedCameraDetails ..isZoomAble = selectedCameraDetails.maxAvailableZoom != @@ -263,12 +334,15 @@ class MainCameraController { initCameraStarted = false; setState?.call(); } catch (e) { - Log.error('Error post-initializing camera: $e'); - final controllerToDispose = cameraController; - cameraController = null; - if (controllerToDispose != null) { - unawaited(controllerToDispose.dispose()); + if (e is CameraException) { + Log.warn('Camera post-initialization failed (CameraException): $e'); + } else { + Log.error('Error post-initializing camera: $e'); } + if (cameraController == controller) { + cameraController = null; + } + unawaited(controller.dispose()); initCameraStarted = false; return; } @@ -348,8 +422,9 @@ class MainCameraController { } InputImage? _inputImageFromCameraImage(CameraImage image) { - if (cameraController == null) return null; - final camera = cameraController!.description; + final controller = cameraController; + if (controller == null) return null; + final camera = controller.description; final sensorOrientation = camera.sensorOrientation; InputImageRotation? rotation; @@ -358,7 +433,7 @@ class MainCameraController { rotation = InputImageRotationValue.fromRawValue(sensorOrientation); } else if (Platform.isAndroid) { var rotationCompensation = - _orientations[cameraController!.value.deviceOrientation]; + _orientations[controller.value.deviceOrientation]; if (rotationCompensation == null) return null; if (camera.lensDirection == CameraLensDirection.front) { // front-facing @@ -408,14 +483,15 @@ class MainCameraController { if (_isBusy) return; _isBusy = true; final barcodes = await _barcodeScanner.processImage(inputImage); + final controller = cameraController; if (inputImage.metadata?.size != null && inputImage.metadata?.rotation != null && - cameraController != null) { + controller != null) { final painter = BarcodeDetectorPainter( barcodes, inputImage.metadata!.size, inputImage.metadata!.rotation, - cameraController!.description.lensDirection, + controller.description.lensDirection, ); qrCodePain = CustomPaint(painter: painter); @@ -431,6 +507,11 @@ class MainCameraController { if (barcode.displayValue == null) continue; final link = barcode.displayValue!; + if (link.startsWith(PasswordlessRecoveryService.linkPrefix)) { + await PasswordlessRecoveryService.handleRecoveryLink(link); + continue; + } + if (link.startsWith(QrCodeUtils.linkPrefix)) { if (_handledProfileLinks.contains(link)) continue; _handledProfileLinks.add(link); @@ -476,6 +557,7 @@ class MainCameraController { final context = cameraPreviewKey.currentContext; if (verificationOk && context != null && context.mounted) { await VerificationSuccessDialog.show(context, contact); + onVerificationSuccessDismissed?.call(contact); } } continue; @@ -501,9 +583,10 @@ class MainCameraController { if (_isBusyFaces) return; _isBusyFaces = true; final faces = await _faceDetector.processImage(inputImage); + final controller = cameraController; if (inputImage.metadata?.size != null && inputImage.metadata?.rotation != null && - cameraController != null) { + controller != null) { if (faces.isNotEmpty) { CustomPainter? painter; switch (_currentFilterType) { @@ -512,7 +595,7 @@ class MainCameraController { faces, inputImage.metadata!.size, inputImage.metadata!.rotation, - cameraController!.description.lensDirection, + controller.description.lensDirection, ); case FaceFilterType.beardUpperLipGreen: painter = BeardFilterPainter( @@ -520,7 +603,7 @@ class MainCameraController { faces, inputImage.metadata!.size, inputImage.metadata!.rotation, - cameraController!.description.lensDirection, + controller.description.lensDirection, ); case FaceFilterType.none: break; diff --git a/lib/src/visual/views/camera/camera_preview_components/permissions_view.dart b/lib/src/visual/views/camera/camera_preview_components/permissions_view.dart index fe90a189..9249020f 100644 --- a/lib/src/visual/views/camera/camera_preview_components/permissions_view.dart +++ b/lib/src/visual/views/camera/camera_preview_components/permissions_view.dart @@ -81,6 +81,7 @@ class PermissionHandlerViewState extends State } Future _checkAndTriggerSuccess() async { + if (!mounted) return; if (_isSuccessTriggered) return; final route = ModalRoute.of(context); if (route != null && !route.isCurrent) { diff --git a/lib/src/visual/views/camera/camera_qr_scanner.view.dart b/lib/src/visual/views/camera/camera_qr_scanner.view.dart index 06aa8ee9..d23b1b7d 100644 --- a/lib/src/visual/views/camera/camera_qr_scanner.view.dart +++ b/lib/src/visual/views/camera/camera_qr_scanner.view.dart @@ -1,12 +1,23 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart'; class QrCodeScannerView extends StatefulWidget { - const QrCodeScannerView({super.key}); + const QrCodeScannerView({ + this.contact, + this.openToVerify = false, + super.key, + }); + + final Contact? contact; + final bool openToVerify; + @override State createState() => QrCodeScannerViewState(); } @@ -20,6 +31,17 @@ class QrCodeScannerViewState extends State { _mainCameraController.setState = () { if (mounted) setState(() {}); }; + if (widget.openToVerify && widget.contact != null) { + _mainCameraController.onVerificationSuccessDismissed = (contact) { + if (mounted) { + Navigator.popUntil(context, (route) { + final name = route.settings.name; + if (name == null) return true; + return !name.contains('qr_scanner') && !name.contains('verifybadge'); + }); + } + }; + } Permission.camera.isGranted.then((hasPermission) { if (hasPermission && mounted) { unawaited(_mainCameraController.selectCamera(0, true)); @@ -56,6 +78,31 @@ class QrCodeScannerViewState extends State { isVisible: true, ), ), + if (widget.openToVerify) + Positioned( + left: 16, + right: 16, + bottom: MediaQuery.paddingOf(context).bottom + 20, + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.7), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + widget.contact != null + ? context.lang.qrScannerVerifyUserHint( + getContactDisplayName(widget.contact!), + ) + : context.lang.qrScannerVerifyHint, + style: const TextStyle( + color: Colors.white, + fontSize: 14, + ), + textAlign: TextAlign.center, + ), + ), + ), ], ), ); diff --git a/lib/src/visual/views/camera/share_image_editor.view.dart b/lib/src/visual/views/camera/share_image_editor.view.dart index afa4382e..e2795911 100644 --- a/lib/src/visual/views/camera/share_image_editor.view.dart +++ b/lib/src/visual/views/camera/share_image_editor.view.dart @@ -499,7 +499,7 @@ class _ShareImageEditorView extends State { pixelRatio: pixelRatio, ); if (image == null) { - Log.error('screenshotController did not return image bytes'); + Log.warn('screenshotController did not return image bytes'); return null; } @@ -544,7 +544,7 @@ class _ShareImageEditorView extends State { if (image == null) return null; final bytes = await image.getBytes(); if (bytes == null) { - Log.error('imageBytes are empty'); + Log.warn('imageBytes are empty'); return null; } if (media.type == MediaType.image || media.type == MediaType.gif) { @@ -667,6 +667,25 @@ class _ShareImageEditorView extends State { } } + Widget _buildScreenshotViewer() { + return Screenshot( + controller: screenshotController, + child: LayersViewer( + layers: layers.where((x) => !x.isDeleted).toList(), + onUpdate: () { + for (final layer in layers) { + layer.isEditing = false; + if (layer.isDeleted) { + removedLayers.add(layer); + } + } + layers = layers.where((x) => !x.isDeleted).toList(); + setState(() {}); + }, + ), + ); + } + @override Widget build(BuildContext context) { pixelRatio = MediaQuery.of(context).devicePixelRatio; @@ -784,26 +803,27 @@ class _ShareImageEditorView extends State { width: currentImage.width / pixelRatio, child: Stack( children: [ - if (videoController != null) + if (videoController != null && + videoController!.value.isInitialized) Positioned.fill( - child: VideoPlayer(videoController!), - ), - Screenshot( - controller: screenshotController, - child: LayersViewer( - layers: layers.where((x) => !x.isDeleted).toList(), - onUpdate: () { - for (final layer in layers) { - layer.isEditing = false; - if (layer.isDeleted) { - removedLayers.add(layer); - } - } - layers = layers.where((x) => !x.isDeleted).toList(); - setState(() {}); - }, - ), - ), + child: Center( + child: AspectRatio( + aspectRatio: videoController!.value.aspectRatio, + child: Stack( + children: [ + Positioned.fill( + child: VideoPlayer(videoController!), + ), + Positioned.fill( + child: _buildScreenshotViewer(), + ), + ], + ), + ), + ), + ) + else + _buildScreenshotViewer(), ], ), ), diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart index 946d0408..7d3eb4ee 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart @@ -50,7 +50,13 @@ Future> getStickerIndex() async { return res; } } catch (e) { - Log.error('$e'); + if (e is http.ClientException || + e is SocketException || + e is TimeoutException) { + Log.warn('Could not load stickers index: $e'); + } else { + Log.error('Could not load stickers index: $e'); + } return res; } } diff --git a/lib/src/visual/views/chats/chat_list_components/group_list_item.comp.dart b/lib/src/visual/views/chats/chat_list_components/group_list_item.comp.dart index 77a19625..19cf9f51 100644 --- a/lib/src/visual/views/chats/chat_list_components/group_list_item.comp.dart +++ b/lib/src/visual/views/chats/chat_list_components/group_list_item.comp.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; -import 'package:mutex/mutex.dart'; + import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; @@ -11,7 +11,6 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/services/api/mediafiles/download.api.dart'; -import 'package:twonly/src/services/profile.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; import 'package:twonly/src/visual/components/flame_counter.comp.dart'; @@ -36,13 +35,13 @@ class _UserListItem extends State { Message? _currentMessage; List _messagesNotOpened = []; - late StreamSubscription> _messagesNotOpenedStream; + StreamSubscription>? _messagesNotOpenedStream; Message? _lastMessage; Reaction? _lastReaction; - late StreamSubscription _lastMessageStream; - late StreamSubscription _lastReactionStream; - late StreamSubscription> _lastMediaFilesStream; + StreamSubscription? _lastMessageStream; + StreamSubscription? _lastReactionStream; + StreamSubscription>? _lastMediaFilesStream; List _previewMessages = []; final List _previewMediaFiles = []; @@ -57,70 +56,76 @@ class _UserListItem extends State { @override void dispose() { - _messagesNotOpenedStream.cancel(); - _lastReactionStream.cancel(); - _lastMessageStream.cancel(); - _lastMediaFilesStream.cancel(); + _messagesNotOpenedStream?.cancel(); + _lastReactionStream?.cancel(); + _lastMessageStream?.cancel(); + _lastMediaFilesStream?.cancel(); super.dispose(); } Future initStreams() async { - _lastMessageStream = - (await twonlyDB.messagesDao.watchLastMessage( - widget.group.groupId, - )).listen((update) { - protectUpdateState.protect(() async { - await updateState(update, _messagesNotOpened); + final lastMsgStream = await twonlyDB.messagesDao.watchLastMessage( + widget.group.groupId, + ); + if (!mounted) return; + _lastMessageStream = lastMsgStream.listen((update) { + _updateState(update, _messagesNotOpened); + }); + + _lastReactionStream = twonlyDB.reactionsDao + .watchLastReactions(widget.group.groupId) + .listen((update) { + if (!mounted) return; + setState(() { + _lastReaction = update; }); }); - _lastReactionStream = twonlyDB.reactionsDao.watchLastReactions(widget.group.groupId).listen((update) { - if (!mounted) return; - setState(() { - _lastReaction = update; - }); - }); + _messagesNotOpenedStream = twonlyDB.messagesDao + .watchMessageNotOpened(widget.group.groupId) + .listen((update) { + _updateState(_lastMessage, update); + }); - _messagesNotOpenedStream = twonlyDB.messagesDao.watchMessageNotOpened(widget.group.groupId).listen((update) { - protectUpdateState.protect(() async { - await updateState(_lastMessage, update); - }); - }); - - _lastMediaFilesStream = twonlyDB.mediaFilesDao.watchNewestMediaFiles().listen((mediaFiles) { - if (!mounted) return; - for (final mediaFile in mediaFiles) { - final index = _previewMediaFiles.indexWhere( - (t) => t.mediaId == mediaFile.mediaId, - ); - if (index >= 0) { - _previewMediaFiles[index] = mediaFile; - } - } - setState(() {}); - }); + _lastMediaFilesStream = twonlyDB.mediaFilesDao + .watchMediaFilesForGroup(widget.group.groupId) + .listen((mediaFiles) { + if (!mounted) return; + for (final mediaFile in mediaFiles) { + final index = _previewMediaFiles.indexWhere( + (t) => t.mediaId == mediaFile.mediaId, + ); + if (index >= 0) { + _previewMediaFiles[index] = mediaFile; + } + } + setState(() {}); + }); final groupContacts = await twonlyDB.groupsDao.getGroupContact( widget.group.groupId, ); + if (!mounted) return; if (groupContacts.length == 1) { _receiverDeletedAccount = groupContacts.first.accountDeleted; } } - Mutex protectUpdateState = Mutex(); - - Future updateState( + void _updateState( Message? newLastMessage, List newMessagesNotOpened, - ) async { + ) { + if (!mounted) return; if (newLastMessage == null) { // there are no messages at all _currentMessage = null; _previewMessages = []; } else if (newMessagesNotOpened.isNotEmpty) { - // Filter for the preview non opened messages. First messages which where send but not yet opened by the other side. - final receivedMessages = newMessagesNotOpened.where((x) => x.senderId != null).toList(); + // Filter for the preview non opened messages. First messages which where + // send but not yet opened by the other side. + final receivedMessages = newMessagesNotOpened + .where((x) => x.senderId != null) + .toList(); if (receivedMessages.isNotEmpty) { _previewMessages = receivedMessages; @@ -131,8 +136,9 @@ class _UserListItem extends State { } } else { // there are no not opened messages show just the last message in the table - // only shows the last message in case there was no newer messages which already got deleted - // This prevents, that it will show that a images got stored 10 days ago... + // only shows the last message in case there was no newer messages which + // already got deleted. This prevents showing that an image got stored 10 + // days ago... if (newLastMessage.createdAt.isAfter( widget.group.lastMessageExchange.subtract(const Duration(days: 2)), )) { @@ -144,7 +150,9 @@ class _UserListItem extends State { } } - final msgs = _previewMessages.where((x) => x.type == MessageType.media.name).toList(); + final msgs = _previewMessages + .where((x) => x.type == MessageType.media.name) + .toList(); if (msgs.isNotEmpty && msgs.first.type == MessageType.media.name && !msgs.first.isDeletedFromSender && @@ -155,20 +163,30 @@ class _UserListItem extends State { _hasNonOpenedMediaFile = false; } + _lastMessage = newLastMessage; + _messagesNotOpened = newMessagesNotOpened; + setState(() {}); + + // Only fetch on first load when a mediaId is not yet cached. + _fetchMissingMediaFiles(); + } + + /// Fetches any media files referenced by preview messages but not yet in the + /// local cache. Fire-and-forget; updates state when results arrive. + Future _fetchMissingMediaFiles() async { for (final message in _previewMessages) { - if (message.mediaId != null && !_previewMediaFiles.any((t) => t.mediaId == message.mediaId)) { + if (message.mediaId != null && + !_previewMediaFiles.any((t) => t.mediaId == message.mediaId)) { final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById( message.mediaId!, ); - if (mediaFile != null) { - _previewMediaFiles.add(mediaFile); + if (mediaFile != null && mounted) { + setState(() { + _previewMediaFiles.add(mediaFile); + }); } } } - - _lastMessage = newLastMessage; - _messagesNotOpened = newMessagesNotOpened; - if (mounted) setState(() {}); } Future onTap() async { @@ -181,7 +199,9 @@ class _UserListItem extends State { } if (_hasNonOpenedMediaFile) { - final msgs = _previewMessages.where((x) => x.type == MessageType.media.name).toList(); + final msgs = _previewMessages + .where((x) => x.type == MessageType.media.name) + .toList(); final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById( msgs.first.mediaId!, ); @@ -221,7 +241,7 @@ class _UserListItem extends State { const SizedBox(width: 3), VerificationBadgeComp( group: widget.group, - showOnlyIfVerified: userService.currentUser.securityProfile.showOnlyVerifiedInChatViewList, + showOnlyIfVerified: true, clickable: false, size: 12, ), @@ -235,6 +255,7 @@ class _UserListItem extends State { : Row( children: [ LastMessageTimeComp( + key: ValueKey(widget.group.groupId), dateTime: widget.group.lastMessageExchange, ), FlameCounterWidget( @@ -256,7 +277,11 @@ class _UserListItem extends State { ), const Text('•'), const SizedBox(width: 5), - if (_currentMessage != null) LastMessageTimeComp(message: _currentMessage), + if (_currentMessage != null) + LastMessageTimeComp( + key: ValueKey(widget.group.groupId), + message: _currentMessage, + ), FlameCounterWidget( groupId: widget.group.groupId, prefix: true, @@ -270,7 +295,9 @@ class _UserListItem extends State { widget.group.groupId, ); if (!context.mounted) return; - await context.push(Routes.profileContact(contacts.first.userId)); + await context.push( + Routes.profileContact(contacts.first.userId), + ); return; } else { await context.push(Routes.profileGroup(widget.group.groupId)); @@ -283,7 +310,9 @@ class _UserListItem extends State { : IconButton( onPressed: () { if (_hasNonOpenedMediaFile) { - context.push(Routes.chatsMessages(widget.group.groupId)); + context.push( + Routes.chatsMessages(widget.group.groupId), + ); } else { context.push( Routes.chatsCameraSendTo, @@ -292,7 +321,9 @@ class _UserListItem extends State { } }, icon: FaIcon( - _hasNonOpenedMediaFile ? FontAwesomeIcons.solidComments : FontAwesomeIcons.camera, + _hasNonOpenedMediaFile + ? FontAwesomeIcons.solidComments + : FontAwesomeIcons.camera, color: context.color.outline.withAlpha(150), ), ), diff --git a/lib/src/visual/views/chats/chat_list_components/last_message_time.comp.dart b/lib/src/visual/views/chats/chat_list_components/last_message_time.comp.dart index 3621e45a..7cefd901 100644 --- a/lib/src/visual/views/chats/chat_list_components/last_message_time.comp.dart +++ b/lib/src/visual/views/chats/chat_list_components/last_message_time.comp.dart @@ -19,41 +19,59 @@ class LastMessageTimeComp extends StatefulWidget { class _LastMessageTimeCompState extends State { Timer? updateTime; int lastMessageInSeconds = 0; + DateTime? targetTime; + StreamSubscription? _actionSubscription; @override void initState() { super.initState(); - // Change the color every 200 milliseconds - updateTime = Timer.periodic(const Duration(milliseconds: 500), ( - timer, - ) async { - if (widget.message != null) { - final lastAction = await twonlyDB.messagesDao.getLastMessageAction( - widget.message!.messageId, - ); - lastMessageInSeconds = clock - .now() - .difference(lastAction?.actionAt ?? widget.message!.createdAt) - .inSeconds; - } else if (widget.dateTime != null) { - lastMessageInSeconds = clock - .now() - .difference(widget.dateTime!) - .inSeconds; - } - if (mounted) { - setState(() { - if (lastMessageInSeconds < 0) { - lastMessageInSeconds = 0; - } - }); - } + _loadTargetTime(); + + updateTime = Timer.periodic( + const Duration(milliseconds: 500), + (_) => _updateSeconds(), + ); + } + + @override + void didUpdateWidget(LastMessageTimeComp oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.message?.messageId != widget.message?.messageId || + oldWidget.dateTime != widget.dateTime) { + _loadTargetTime(); + } + } + + void _loadTargetTime() { + _actionSubscription?.cancel(); + _actionSubscription = null; + + if (widget.message != null) { + _actionSubscription = twonlyDB.messagesDao + .watchLastMessageAction(widget.message!.messageId) + .listen((lastAction) { + targetTime = lastAction?.actionAt ?? widget.message!.createdAt; + _updateSeconds(); + }); + } else if (widget.dateTime != null) { + targetTime = widget.dateTime; + _updateSeconds(); + } + } + + void _updateSeconds() { + if (targetTime == null || !mounted) return; + + final seconds = clock.now().difference(targetTime!).inSeconds; + setState(() { + lastMessageInSeconds = seconds < 0 ? 0 : seconds; }); } @override void dispose() { updateTime?.cancel(); + _actionSubscription?.cancel(); super.dispose(); } diff --git a/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart b/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart index c11e9084..40c38bcb 100644 --- a/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart +++ b/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart @@ -20,9 +20,9 @@ class _TypingIndicatorSubtitleCompState extends State { List _groupMembers = []; - late StreamSubscription> membersSub; + StreamSubscription>? membersSub; - late Timer _periodicUpdate; + Timer? _periodicUpdate; @override void initState() { @@ -50,8 +50,8 @@ class _TypingIndicatorSubtitleCompState @override void dispose() { - membersSub.cancel(); - _periodicUpdate.cancel(); + membersSub?.cancel(); + _periodicUpdate?.cancel(); super.dispose(); } diff --git a/lib/src/visual/views/chats/chat_messages.view.dart b/lib/src/visual/views/chats/chat_messages.view.dart index f3daa1de..fcde1654 100644 --- a/lib/src/visual/views/chats/chat_messages.view.dart +++ b/lib/src/visual/views/chats/chat_messages.view.dart @@ -4,7 +4,6 @@ import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; -import 'package:mutex/mutex.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; @@ -25,6 +24,7 @@ import 'package:twonly/src/visual/views/chats/chat_messages_components/blink.com import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_group_action.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_list_entry.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_date_chip.dart'; +import 'package:twonly/src/visual/views/chats/chat_messages_components/in_chat_group_overview.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/message_input.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/response_container.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/typing_indicator.dart'; @@ -87,18 +87,8 @@ class _ChatMessagesViewState extends State super.dispose(); } - Mutex protectMessageUpdating = Mutex(); - - // @override - // void didChangeAppLifecycleState(AppLifecycleState state) { - // if (state == AppLifecycleState.resumed) { - // protectMessageUpdating.protect(() async { - // await setMessages(allMessages, groupActions); - // }); - // } - // } - bool _isViewActive() { + if (!mounted) return false; return !AppState.isAppInBackground && (ModalRoute.of(context)?.isCurrent ?? false); } @@ -112,33 +102,29 @@ class _ChatMessagesViewState extends State _group = newGroup; }); - protectMessageUpdating.protect(() async { - if (groupActionsSub == null) { - final actionsStream = twonlyDB.groupsDao.watchGroupActions( - newGroup.groupId, - ); - groupActionsSub = actionsStream.listen((update) async { - groupActions = update; - await setMessages(allMessages, update); - }); + if (groupActionsSub == null) { + final actionsStream = twonlyDB.groupsDao.watchGroupActions( + newGroup.groupId, + ); + groupActionsSub = actionsStream.listen((update) async { + groupActions = update; + await setMessages(allMessages, update); + }); - final contactsStream = twonlyDB.contactsDao.watchAllContacts(); - contactSub = contactsStream.listen((contacts) { - for (final contact in contacts) { - userIdToContact[contact.userId] = contact; - } - }); - } - }); + final contactsStream = twonlyDB.contactsDao.watchAllContacts(); + contactSub = contactsStream.listen((contacts) { + for (final contact in contacts) { + userIdToContact[contact.userId] = contact; + } + }); + } }); final msgStream = await twonlyDB.messagesDao.watchByGroupId(widget.groupId); messageSub = msgStream.listen((update) async { allMessages = update; - await protectMessageUpdating.protect(() async { - await setMessages(update, groupActions); - _hasReceivedFirstMessageBatch = true; - }); + await setMessages(update, groupActions); + _hasReceivedFirstMessageBatch = true; }); final groupContacts = await twonlyDB.groupsDao.getGroupContact( @@ -165,7 +151,7 @@ class _ChatMessagesViewState extends State List groupActions, ) async { if (_isViewActive()) { - await flutterLocalNotificationsPlugin.cancelAll(); + unawaited(flutterLocalNotificationsPlugin.cancelAll()); } for (final msg in newMessages) { @@ -229,18 +215,34 @@ class _ChatMessagesViewState extends State if (_isViewActive()) { for (final contactId in openedMessages.keys) { - await notifyContactAboutOpeningMessage( - contactId, - openedMessages[contactId]!, + unawaited( + notifyContactAboutOpeningMessage( + contactId, + openedMessages[contactId]!, + ), ); } } + final wasSentByMe = + _hasReceivedFirstMessageBatch && + newMessages.isNotEmpty && + newMessages.last.senderId == null; + if (!mounted) return; setState(() { messages = chatItems.reversed.toList(); }); + if (wasSentByMe && itemScrollController.isAttached) { + unawaited( + itemScrollController.scrollTo( + index: 0, + duration: const Duration(milliseconds: 150), + ), + ); + } + final items = await MemoryItem.convertFromMessages(storedMediaFiles); if (!mounted) return; galleryItems = items.values.toList(); @@ -325,64 +327,72 @@ class _ChatMessagesViewState extends State child: Column( children: [ Expanded( - child: ScrollablePositionedList.builder( - reverse: true, - itemCount: messages.length + 1 + 1, - itemScrollController: itemScrollController, - itemBuilder: (context, i) { - if (i == 0) { - return userService.currentUser.typingIndicators - ? TypingIndicator(group: group) - : Container(); - } - i -= 1; - if (i == messages.length) { - return const Padding( - padding: EdgeInsetsGeometry.only(top: 10), - ); - } - if (messages[i].isDate) { - return ChatDateChip( - item: messages[i], - ); - } else if (messages[i].isGroupAction) { - return ChatGroupAction( - key: Key(messages[i].groupAction!.groupHistoryId), - action: messages[i].groupAction!, - ); - } else { - final chatMessage = messages[i].message!; - return BlinkWidget( - key: Key('blink_${chatMessage.messageId}'), - enabled: focusedScrollItem == i, - child: AnimatedNewMessage( - key: Key('anim_${chatMessage.messageId}'), - messageId: chatMessage.messageId, - animateIds: _animateMessageIds, - child: ChatListEntry( - key: Key(chatMessage.messageId), - message: messages[i].message!, - nextMessage: (i > 0) - ? messages[i - 1].message - : null, - prevMessage: ((i + 1) < messages.length) - ? messages[i + 1].message - : null, + child: Align( + alignment: Alignment.topCenter, + child: ScrollablePositionedList.builder( + shrinkWrap: true, + reverse: true, + itemCount: messages.length + 1 + 1, + itemScrollController: itemScrollController, + itemBuilder: (context, i) { + if (i == 0) { + return userService.currentUser.typingIndicators + ? TypingIndicator(group: group) + : Container(); + } + i -= 1; + if (i == messages.length) { + return Padding( + key: Key('overview_${group.groupId}'), + padding: const EdgeInsets.only(top: 10), + child: InChatGroupOverview( group: group, - galleryItems: galleryItems, - userIdToContact: userIdToContact, - scrollToMessage: scrollToMessage, - onResponseTriggered: () { - setState(() { - quotesMessage = chatMessage; - }); - textFieldFocus?.requestFocus(); - }, ), - ), - ); - } - }, + ); + } + if (messages[i].isDate) { + return ChatDateChip( + item: messages[i], + ); + } else if (messages[i].isGroupAction) { + return ChatGroupAction( + key: Key(messages[i].groupAction!.groupHistoryId), + action: messages[i].groupAction!, + ); + } else { + final chatMessage = messages[i].message!; + return BlinkWidget( + key: Key('blink_${chatMessage.messageId}'), + enabled: focusedScrollItem == i, + child: AnimatedNewMessage( + key: Key('anim_${chatMessage.messageId}'), + messageId: chatMessage.messageId, + animateIds: _animateMessageIds, + child: ChatListEntry( + key: Key(chatMessage.messageId), + message: messages[i].message!, + nextMessage: (i > 0) + ? messages[i - 1].message + : null, + prevMessage: ((i + 1) < messages.length) + ? messages[i + 1].message + : null, + group: group, + galleryItems: galleryItems, + userIdToContact: userIdToContact, + scrollToMessage: scrollToMessage, + onResponseTriggered: () { + setState(() { + quotesMessage = chatMessage; + }); + textFieldFocus?.requestFocus(); + }, + ), + ), + ); + } + }, + ), ), ), if (quotesMessage != null) diff --git a/lib/src/visual/views/chats/chat_messages_components/in_chat_group_overview.dart b/lib/src/visual/views/chats/chat_messages_components/in_chat_group_overview.dart new file mode 100644 index 00000000..d44f4e78 --- /dev/null +++ b/lib/src/visual/views/chats/chat_messages_components/in_chat_group_overview.dart @@ -0,0 +1,221 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; +import 'package:twonly/src/database/daos/key_verification.dao.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; +import 'package:twonly/src/visual/components/verification_badge.comp.dart'; +import 'package:twonly/src/visual/elements/my_button.element.dart'; + +class InChatGroupOverview extends StatefulWidget { + const InChatGroupOverview({ + required this.group, + super.key, + }); + + final Group group; + + @override + State createState() => _InChatGroupOverviewState(); +} + +class _InChatGroupOverviewState extends State + with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + Contact? _directContact; + StreamSubscription? _verificationSub; + StreamSubscription>? _transferredTrustSub; + StreamSubscription? _unverifiedCountSub; + bool _isVerified = true; + int _unverifiedCount = 0; + List<(KeyVerification, Contact?)> _verifications = []; + List<(Contact, DateTime)> _transferredTrust = []; + + @override + void initState() { + super.initState(); + _initVerificationCheck(); + } + + void _updateVerificationState() { + if (mounted) { + setState(() { + _isVerified = + _directContact?.verified == true || + _verifications.isNotEmpty || + _transferredTrust.isNotEmpty; + }); + } + } + + Future _initVerificationCheck() async { + if (widget.group.isDirectChat) { + final contacts = await twonlyDB.groupsDao.getGroupContact( + widget.group.groupId, + ); + if (contacts.isNotEmpty) { + _directContact = contacts.first; + _verificationSub = twonlyDB.keyVerificationDao + .watchContactVerification(_directContact!.userId) + .listen((verifications) { + _verifications = verifications; + _updateVerificationState(); + }); + _transferredTrustSub = twonlyDB.keyVerificationDao + .watchTransferredTrustVerifications(_directContact!.userId) + .listen((transferredTrust) { + _transferredTrust = transferredTrust; + _updateVerificationState(); + }); + } + } else { + _verificationSub = twonlyDB.keyVerificationDao + .watchAllGroupMembersVerified(widget.group.groupId) + .listen((status) { + if (mounted) { + setState(() { + _isVerified = status == VerificationStatus.trusted; + }); + } + }); + _unverifiedCountSub = twonlyDB.keyVerificationDao + .watchUnverifiedGroupMembersCount(widget.group.groupId) + .listen((count) { + if (mounted) { + setState(() { + _unverifiedCount = count; + }); + } + }); + } + } + + @override + void dispose() { + _verificationSub?.cancel(); + _transferredTrustSub?.cancel(); + _unverifiedCountSub?.cancel(); + super.dispose(); + } + + void _onVerifyPressed() { + if (widget.group.isDirectChat && _directContact != null) { + context.push( + Routes.settingsHelpFaqVerifyBadge, + extra: _directContact, + ); + } else { + context.push(Routes.profileGroup(widget.group.groupId)); + } + } + + @override + Widget build(BuildContext context) { + super.build(context); + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14), + child: Center( + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.topCenter, + children: [ + Container( + margin: const EdgeInsets.only(top: 40), + constraints: const BoxConstraints(maxWidth: 250), + decoration: BoxDecoration( + border: Border.all( + color: Theme.of(context).colorScheme.outlineVariant, + ), + borderRadius: BorderRadius.circular(24), + ), + padding: const EdgeInsets.only( + top: 56, + left: 14, + right: 14, + bottom: 14, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: Text( + widget.group.groupName, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox(width: 8), + VerificationBadgeComp( + group: widget.group, + size: 20, + clickable: false, + ), + ], + ), + if (!_isVerified) ...[ + const SizedBox(height: 5), + Card( + elevation: 0, + color: Theme.of( + context, + ).colorScheme.error.withValues(alpha: 0.1), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 14, + ), + child: Column( + children: [ + Text( + widget.group.isDirectChat + ? context.lang.inChatContactNotVerified + : context.lang.groupMembersNotVerified( + _unverifiedCount, + ), + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onErrorContainer, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 4), + MyButton( + variant: MyButtonVariant.secondaryDense, + onPressed: _onVerifyPressed, + child: Text(context.lang.unverifiedWarningButton), + ), + ], + ), + ), + ), + ], + ], + ), + ), + Positioned( + top: 0, + child: SizedBox( + width: 80, + height: 80, + child: AvatarIcon(group: widget.group, fontSize: 40), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart b/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart index 829d405c..58febb11 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart @@ -99,6 +99,7 @@ class MessageContextMenu extends StatelessWidget { @override Widget build(BuildContext context) { + final navigator = Navigator.of(context); return ContextMenu( items: [ if (!message.isDeletedFromSender) @@ -107,7 +108,7 @@ class MessageContextMenu extends StatelessWidget { onTap: () async { final layer = await showModalBottomSheet( - context: context, + context: navigator.context, backgroundColor: Colors.black, builder: (context) { return const EmojiPickerBottom(); @@ -138,7 +139,7 @@ class MessageContextMenu extends StatelessWidget { if (mediaFileService?.canBeOpenedAgain ?? false) ContextMenuItem( title: context.lang.contextMenuViewAgain, - onTap: () => reopenMediaFile(context), + onTap: () => reopenMediaFile(navigator.context), icon: FontAwesomeIcons.clockRotateLeft, ), if (!message.isDeletedFromSender) @@ -155,7 +156,7 @@ class MessageContextMenu extends StatelessWidget { ContextMenuItem( title: context.lang.edit, onTap: () async { - await editTextMessage(context, message); + await editTextMessage(navigator.context, message); }, icon: FontAwesomeIcons.pencil, ), @@ -172,13 +173,13 @@ class MessageContextMenu extends StatelessWidget { title: context.lang.delete, onTap: () async { final delete = await showAlertDialog( - context, - context.lang.deleteTitle, + navigator.context, + navigator.context.lang.deleteTitle, null, customOk: (message.senderId == null && !message.isDeletedFromSender) - ? context.lang.deleteOkBtnForAll - : context.lang.deleteOkBtnForMe, + ? navigator.context.lang.deleteOkBtnForAll + : navigator.context.lang.deleteOkBtnForMe, ); if (delete) { if (message.senderId == null && !message.isDeletedFromSender) { @@ -209,8 +210,7 @@ class MessageContextMenu extends StatelessWidget { ContextMenuItem( title: context.lang.info, onTap: () async { - await Navigator.push( - context, + await navigator.push( MaterialPageRoute( builder: (context) { return MessageInfoView( diff --git a/lib/src/visual/views/chats/chat_messages_components/message_input.dart b/lib/src/visual/views/chats/chat_messages_components/message_input.dart index 494a7297..fe71d894 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_input.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_input.dart @@ -21,7 +21,6 @@ import 'package:twonly/src/visual/views/chats/chat_messages_components/bottom_sh import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/message_input_components/ask_for_friend_promotions.comp.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/message_input_components/sparks.comp.dart'; -import 'package:twonly/src/visual/views/chats/chat_messages_components/message_input_components/unverified_contact_warning.comp.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/message_input_components/user_discovery_manual_approval.comp.dart'; import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart'; @@ -283,11 +282,9 @@ class _MessageInputState extends State { flameOnRightSide: true, ), ), - UnverifiedContactWarningComp( - group: widget.group, - child: Padding( - padding: const EdgeInsets.only(left: 10, bottom: 10), - child: Row( + Padding( + padding: const EdgeInsets.only(left: 10, bottom: 10), + child: Row( children: [ Expanded( child: Container( @@ -589,7 +586,6 @@ class _MessageInputState extends State { ], ), ), - ), Offstage( offstage: !_emojiShowing, child: EmojiPicker( diff --git a/lib/src/visual/views/chats/chat_messages_components/message_input_components/unverified_contact_warning.comp.dart b/lib/src/visual/views/chats/chat_messages_components/message_input_components/unverified_contact_warning.comp.dart deleted file mode 100644 index 5d569fa7..00000000 --- a/lib/src/visual/views/chats/chat_messages_components/message_input_components/unverified_contact_warning.comp.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:twonly/locator.dart'; -import 'package:twonly/src/constants/routes.keys.dart'; -import 'package:twonly/src/database/daos/key_verification.dao.dart'; -import 'package:twonly/src/database/twonly.db.dart'; -import 'package:twonly/src/services/profile.service.dart'; -import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/components/verification_badge.comp.dart'; - -class UnverifiedContactWarningComp extends StatelessWidget { - const UnverifiedContactWarningComp({ - required this.group, - required this.child, - super.key, - }); - - final Group group; - final Widget child; - - @override - Widget build(BuildContext context) { - return StreamBuilder( - stream: userService.onUserUpdated, - builder: (context, _) { - if (!userService - .currentUser - .securityProfile - .showWarningForNonVerifiedContacts) { - return child; - } - return StreamBuilder( - stream: twonlyDB.keyVerificationDao.watchAllGroupMembersVerified( - group.groupId, - ), - builder: (context, snapshot) { - final status = snapshot.data; - if (status == null || status == VerificationStatus.trusted) { - return child; - } - - return Container( - margin: const EdgeInsets.only(top: 8), - decoration: BoxDecoration( - color: context.color.errorContainer.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(24), - border: Border.all( - color: context.color.error.withValues(alpha: 0.5), - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(12, 10, 12, 8), - child: Row( - children: [ - VerificationBadgeComp( - group: group, - size: 24, - clickable: false, - ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - group.isDirectChat - ? context.lang.unverifiedWarningDirectTitle - : context.lang.unverifiedWarningGroupTitle, - style: TextStyle( - color: context.color.onErrorContainer, - fontWeight: FontWeight.bold, - fontSize: 13, - height: 1.2, - ), - ), - const SizedBox(height: 4), - RichText( - text: TextSpan( - style: TextStyle( - color: context.color.onErrorContainer, - fontSize: 11, - ), - children: formattedText( - context, - context.lang.unverifiedWarningBody, - textColor: context.color.onErrorContainer, - ), - ), - ), - ], - ), - ), - const SizedBox(width: 10), - SizedBox( - height: 30, - child: FilledButton.tonal( - style: FilledButton.styleFrom( - backgroundColor: context.color.onErrorContainer, - foregroundColor: context.color.errorContainer, - padding: const EdgeInsets.symmetric( - horizontal: 10, - ), - textStyle: const TextStyle( - fontSize: 11, - fontWeight: FontWeight.bold, - ), - ), - onPressed: () async { - if (group.isDirectChat) { - await context.push( - Routes.settingsHelpFaqVerifyBadge, - ); - } else { - await context.push( - Routes.profileGroup(group.groupId), - ); - } - }, - child: Text(context.lang.unverifiedWarningButton), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 5, - ), - child: child, - ), - ], - ), - ); - }, - ); - }, - ); - } -} diff --git a/lib/src/visual/views/chats/media_viewer.view.dart b/lib/src/visual/views/chats/media_viewer.view.dart index 403f0fe4..6e99ff8b 100644 --- a/lib/src/visual/views/chats/media_viewer.view.dart +++ b/lib/src/visual/views/chats/media_viewer.view.dart @@ -137,6 +137,7 @@ class _MediaViewerViewState extends State { final Mutex _messageUpdateLock = Mutex(); bool _isViewActive() { + if (!mounted) return false; return !AppState.isAppInBackground && (ModalRoute.of(context)?.isCurrent ?? false); } @@ -358,12 +359,17 @@ class _MediaViewerViewState extends State { if (!mounted) return; if (!currentMediaLocal.tempPath.existsSync()) { - Log.error('Temp media file not found...'); + Log.warn( + 'Temp media file not found for media ID: ${currentMediaLocal.mediaFile.mediaId}', + ); await handleMediaError(currentMediaLocal.mediaFile); return advanceToNextMediaOrExit(); } // The server can now delete the encrypted bytes, as the users has sucessfully opened it. + Log.info( + 'Calling downloadDone for media ID: ${currentMediaLocal.mediaFile.mediaId}', + ); unawaited( apiService.downloadDone(currentMediaLocal.mediaFile.downloadToken!), ); @@ -468,7 +474,11 @@ class _MediaViewerViewState extends State { ..play(); }) .catchError((Object err, StackTrace st) { - Log.error('Video player initialization error', err, st); + Log.error( + 'Video player initialization error', + error: err, + stackTrace: st, + ); return null; }); } @@ -511,12 +521,16 @@ class _MediaViewerViewState extends State { } Future onPressedSaveToGallery() async { + final media = currentMedia; + final msg = currentMessage; + if (media == null || msg == null) return; + setState(() { imageSaving = true; }); - await currentMedia!.storeMediaFile(); + await media.storeMediaFile(); await twonlyDB.messagesDao.updateMessageId( - currentMessage!.messageId, + msg.messageId, const MessagesCompanion( mediaStored: Value(true), ), @@ -526,7 +540,7 @@ class _MediaViewerViewState extends State { pb.EncryptedContent( mediaUpdate: pb.EncryptedContent_MediaUpdate( type: pb.EncryptedContent_MediaUpdate_Type.STORED, - targetMessageId: currentMessage!.messageId, + targetMessageId: msg.messageId, ), ), ); @@ -553,11 +567,14 @@ class _MediaViewerViewState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ if (currentMedia != null && + currentMessage != null && !currentMedia!.mediaFile.requiresAuthentication && currentMedia!.mediaFile.displayLimitInMilliseconds == null) MyIconButton( variant: MyIconButtonVariant.secondary, - onPressed: (currentMedia == null) ? null : onPressedSaveToGallery, + onPressed: (currentMedia == null || currentMessage == null) + ? null + : onPressedSaveToGallery, icon: imageSaving ? const SizedBox( width: 16, diff --git a/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart b/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart index 0033f4eb..5734011a 100644 --- a/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart +++ b/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart @@ -21,16 +21,14 @@ class MediaContentRenderer extends StatelessWidget { Widget build(BuildContext context) { return Stack( children: [ - if (videoController != null) + if (videoController != null && videoController!.value.isInitialized) Positioned.fill( - child: PhotoView.customChild( - initialScale: PhotoViewComputedScale.contained, - minScale: PhotoViewComputedScale.contained, - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - child: VideoPlayer( - videoController!, + child: Center( + child: AspectRatio( + aspectRatio: videoController!.value.aspectRatio, + child: VideoPlayer( + videoController!, + ), ), ), ) diff --git a/lib/src/visual/views/chats/media_viewer_components/media_viewer_message_input.comp.dart b/lib/src/visual/views/chats/media_viewer_components/media_viewer_message_input.comp.dart index d69cef2c..acdbf358 100644 --- a/lib/src/visual/views/chats/media_viewer_components/media_viewer_message_input.comp.dart +++ b/lib/src/visual/views/chats/media_viewer_components/media_viewer_message_input.comp.dart @@ -36,6 +36,7 @@ class MediaViewerMessageInput extends StatelessWidget { child: MyInput( dense: true, autofocus: true, + fontWeight: FontWeight.normal, controller: controller, hintText: context.lang.chatListDetailInput, onChanged: (value) {}, diff --git a/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart b/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart index 71d419fa..1aae402d 100644 --- a/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart +++ b/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart @@ -208,10 +208,16 @@ class EmojiFloatWidgetState extends State @override void initState() { super.initState(); - _ticker = createTicker(_tick)..start(); + _ticker = createTicker(_tick); } void _tick(Duration elapsed) { + if (_particles.isEmpty) { + _ticker.stop(); + _lastTick = Duration.zero; + return; + } + final dt = (_lastTick == Duration.zero) ? 0.016 : (elapsed - _lastTick).inMicroseconds / 1e6; @@ -258,7 +264,12 @@ class EmojiFloatWidgetState extends State ), ); } - setState(() {}); + if (!_ticker.isActive) { + _lastTick = Duration.zero; + _ticker.start(); + } else { + if (mounted) setState(() {}); + } } @override diff --git a/lib/src/visual/views/contact/add_new_contact.view.dart b/lib/src/visual/views/contact/add_new_contact.view.dart index 615aaa1c..dfd3acf4 100644 --- a/lib/src/visual/views/contact/add_new_contact.view.dart +++ b/lib/src/visual/views/contact/add_new_contact.view.dart @@ -102,52 +102,6 @@ class _SearchUsernameView extends State { await SharePlus.instance.share(params); } - void _showMyQrCode() { - // ignore: inference_failure_on_function_invocation - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (context) { - return Container( - decoration: BoxDecoration( - color: context.color.surface, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(24), - ), - ), - padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(width: double.infinity), - Container( - width: 40, - height: 4, - decoration: BoxDecoration( - color: context.color.onSurface.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(2), - ), - ), - const SizedBox(height: 24), - const ProfileQrCodeComp(), - const SizedBox(height: 24), - Text( - context.lang.addContactQrSheetSubtext, - style: TextStyle( - fontSize: 14, - color: context.color.onSurface.withValues(alpha: 0.6), - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 16), - ], - ), - ); - }, - ); - } - @override void dispose() { _contactsStream.cancel(); @@ -344,7 +298,7 @@ class _SearchUsernameView extends State { Expanded( child: MyButton( variant: MyButtonVariant.secondaryDense, - onPressed: _showMyQrCode, + onPressed: () => ProfileQrCodeComp.showSheet(context), child: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart b/lib/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart index a8cd0bde..fc91aea1 100644 --- a/lib/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart +++ b/lib/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart @@ -93,18 +93,25 @@ class _VerificationExpansionTileCompState contact: widget.contact, size: 20, ), - text: context.lang.contactVerifyNumberTitle, + text: context.lang.verifyUserIdentity( + getContactDisplayName(widget.contact), + ), onTap: () async { - await context.push(Routes.settingsHelpFaqVerifyBadge); + await context.push( + Routes.settingsHelpFaqVerifyBadge, + extra: widget.contact, + ); if (mounted) setState(() {}); }, ); } final sharedVerifierIds = _keyVerifications - .where((pair) => - pair.$1.type == VerificationType.contactSharedByVerified && - pair.$1.verifiedBy != null) + .where( + (pair) => + pair.$1.type == VerificationType.contactSharedByVerified && + pair.$1.verifiedBy != null, + ) .map((pair) => pair.$1.verifiedBy!) .toSet(); diff --git a/lib/src/visual/views/critical_error.view.dart b/lib/src/visual/views/critical_error.view.dart index c39a7108..c847514f 100644 --- a/lib/src/visual/views/critical_error.view.dart +++ b/lib/src/visual/views/critical_error.view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:restart_app/restart_app.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/views/onboarding/recover.view.dart'; +import 'package:twonly/src/visual/views/onboarding/recover_password.view.dart'; import 'package:twonly/src/visual/views/settings/help/contact_us.view.dart'; class CriticalErrorView extends StatelessWidget { diff --git a/lib/src/visual/views/groups/group_create_select_members.view.dart b/lib/src/visual/views/groups/group_create_select_members.view.dart index b46a3c90..e44315e7 100644 --- a/lib/src/visual/views/groups/group_create_select_members.view.dart +++ b/lib/src/visual/views/groups/group_create_select_members.view.dart @@ -14,6 +14,7 @@ import 'package:twonly/src/visual/components/flame_counter.comp.dart'; import 'package:twonly/src/visual/components/snackbar.dart'; import 'package:twonly/src/visual/context_menu/user.context_menu.dart'; import 'package:twonly/src/visual/decorations/input_text.decoration.dart'; +import 'package:twonly/src/visual/elements/contact_chip.element.dart'; import 'package:twonly/src/visual/views/groups/group_create_select_group_name.view.dart'; class GroupCreateSelectMembersView extends StatefulWidget { @@ -184,10 +185,12 @@ class _StartNewChatView extends State { return Wrap( spacing: 8, children: selected.map((w) { - return _Chip( - contact: allContacts.firstWhere( - (t) => t.userId == w, - ), + final contact = allContacts.firstWhere( + (t) => t.userId == w, + ); + return ContactChip( + key: ValueKey(contact.userId), + contact: contact, onTap: toggleSelectedUser, ); }).toList(), @@ -257,41 +260,3 @@ class _StartNewChatView extends State { ); } } - -class _Chip extends StatelessWidget { - const _Chip({ - required this.contact, - required this.onTap, - }); - final Contact contact; - final void Function(int) onTap; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => onTap(contact.userId), - child: Chip( - avatar: AvatarIcon( - contactId: contact.userId, - fontSize: 10, - ), - label: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - getContactDisplayName(contact), - style: const TextStyle(fontSize: 14), - overflow: TextOverflow.ellipsis, - ), - const SizedBox(width: 15), - const FaIcon( - FontAwesomeIcons.xmark, - color: Colors.grey, - size: 12, - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/visual/views/groups/group_member.context.dart b/lib/src/visual/views/groups/group_member.context.dart index a8a820d3..251a9010 100644 --- a/lib/src/visual/views/groups/group_member.context.dart +++ b/lib/src/visual/views/groups/group_member.context.dart @@ -118,6 +118,7 @@ class GroupMemberContextMenu extends StatelessWidget { @override Widget build(BuildContext context) { + final navigator = Navigator.of(context); return ContextMenu( items: [ if (contact.accepted) @@ -131,15 +132,15 @@ class GroupMemberContextMenu extends StatelessWidget { // create return; } - if (!context.mounted) return; - await context.push(Routes.chatsMessages(directChat.groupId)); + if (!navigator.mounted) return; + await navigator.context.push(Routes.chatsMessages(directChat.groupId)); }, icon: FontAwesomeIcons.message, ), if (!contact.accepted) ContextMenuItem( title: context.lang.createContactRequest, - onTap: () => _makeContactRequest(context), + onTap: () => _makeContactRequest(navigator.context), icon: FontAwesomeIcons.userPlus, ), if (member.groupPublicKey != null && @@ -147,7 +148,7 @@ class GroupMemberContextMenu extends StatelessWidget { member.memberState == MemberState.normal) ContextMenuItem( title: context.lang.makeAdmin, - onTap: () => _makeContactAdmin(context), + onTap: () => _makeContactAdmin(navigator.context), icon: FontAwesomeIcons.key, ), if (member.groupPublicKey != null && @@ -155,13 +156,13 @@ class GroupMemberContextMenu extends StatelessWidget { member.memberState == MemberState.admin) ContextMenuItem( title: context.lang.removeAdmin, - onTap: () => _removeContactAsAdmin(context), + onTap: () => _removeContactAsAdmin(navigator.context), icon: FontAwesomeIcons.key, ), if (group.isGroupAdmin && member.groupPublicKey != null) ContextMenuItem( title: context.lang.removeFromGroup, - onTap: () => _removeContactFromGroup(context), + onTap: () => _removeContactFromGroup(navigator.context), icon: FontAwesomeIcons.rightFromBracket, ), ], diff --git a/lib/src/visual/views/onboarding/components/animated_bell_icon.comp.dart b/lib/src/visual/views/onboarding/components/animated_bell_icon.comp.dart new file mode 100644 index 00000000..be2b2b9a --- /dev/null +++ b/lib/src/visual/views/onboarding/components/animated_bell_icon.comp.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; + +class AnimatedBellIcon extends StatefulWidget { + const AnimatedBellIcon({super.key, this.color}); + final Color? color; + + @override + State createState() => _AnimatedBellIconState(); +} + +class _AnimatedBellIconState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _controller; + late final Animation _animation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 1000), + vsync: this, + ); + + _animation = Tween( + begin: -0.02, + end: 0.02, + ).animate(_controller); + + _controller.repeat(reverse: true); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RotationTransition( + turns: _animation, + alignment: Alignment.topCenter, + child: Icon( + Icons.notifications_active_rounded, + size: 32, + color: widget.color ?? context.color.primary, + ), + ); + } +} diff --git a/lib/src/visual/views/onboarding/onboarding.view.dart b/lib/src/visual/views/onboarding/onboarding.view.dart index f0df4f08..bfc1106a 100644 --- a/lib/src/visual/views/onboarding/onboarding.view.dart +++ b/lib/src/visual/views/onboarding/onboarding.view.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:introduction_screen/introduction_screen.dart'; import 'package:lottie/lottie.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; @@ -56,6 +58,7 @@ class OnboardingView extends StatelessWidget { ), PageViewModel( title: context.lang.onboardingNotProductTitle, + useScrollView: false, bodyWidget: Column( children: [ Text( @@ -67,20 +70,41 @@ class OnboardingView extends StatelessWidget { padding: const EdgeInsets.only( left: 50, right: 50, - top: 20, + top: 10, ), child: MyButton( + variant: MyButtonVariant.primaryMiddle, onPressed: callbackOnSuccess, child: Text(context.lang.registerSubmitButton), ), ), + Padding( + padding: const EdgeInsets.only( + left: 50, + right: 50, + top: 10, + ), + child: MyButton( + onPressed: () { + callbackOnSuccess(); + context.push(Routes.settingsBackupRecovery); + }, + variant: MyButtonVariant.secondaryDense, + child: Text( + context.lang.twonlySafeRecoverBtn, + ), + ), + ), ], ), image: Center( child: Padding( - padding: const EdgeInsets.only(top: 60), - child: Lottie.asset( - 'assets/animations/donation.lottie', + padding: const EdgeInsets.only(top: 40), + child: SizedBox( + height: 200, + child: Lottie.asset( + 'assets/animations/donation.lottie', + ), ), ), ), diff --git a/lib/src/visual/views/onboarding/recover.view.dart b/lib/src/visual/views/onboarding/recover_password.view.dart similarity index 84% rename from lib/src/visual/views/onboarding/recover.view.dart rename to lib/src/visual/views/onboarding/recover_password.view.dart index f659c972..425c9271 100644 --- a/lib/src/visual/views/onboarding/recover.view.dart +++ b/lib/src/visual/views/onboarding/recover_password.view.dart @@ -1,7 +1,13 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:restart_app/restart_app.dart'; +import 'package:twonly/src/constants/keyvalue.keys.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; +import 'package:twonly/src/model/json/onboarding_state.model.dart'; import 'package:twonly/src/services/backup.service.dart'; +import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/snackbar.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; @@ -34,23 +40,10 @@ class _BackupRecoveryViewState extends State { if (!mounted) return; if (error != null) { - String errorMessage; - switch (error) { - case RecoveryError.noInternet: - errorMessage = context.lang.recoverErrorNoInternet; - case RecoveryError.usernameNotValid: - errorMessage = context.lang.recoverErrorUsernameNotValid; - case RecoveryError.passwordInvalid: - errorMessage = context.lang.recoverErrorPasswordInvalid; - case RecoveryError.tryAgainLater: - errorMessage = context.lang.recoverErrorTryAgainLater; - case RecoveryError.unkownError: - errorMessage = context.lang.recoverErrorUnknown; - } setState(() { isLoading = false; }); - return showSnackbar(context, errorMessage); + return showSnackbar(context, error.toLocalizedString(context)); } await Restart.restartApp( @@ -64,7 +57,6 @@ class _BackupRecoveryViewState extends State { }); } - @override Widget build(BuildContext context) { final isDark = isDarkMode(context); @@ -175,6 +167,24 @@ class _BackupRecoveryViewState extends State { ) : Text(context.lang.twonlySafeRecoverBtn), ), + const SizedBox(height: 16), + if (kDebugMode) + MyButton( + variant: MyButtonVariant.secondary, + onPressed: () async { + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (state) => + state.hasStartedPasswordlessRecovery = true, + ); + if (context.mounted) { + await context.push(Routes.recoverPasswordless); + } + }, + child: Text( + context.lang.passwordlessRecoveryRecoverBtn, + ), + ), const Spacer(), const SizedBox(height: 40), ], diff --git a/lib/src/visual/views/onboarding/recover_passwordless.view.dart b/lib/src/visual/views/onboarding/recover_passwordless.view.dart new file mode 100644 index 00000000..78bb8fc9 --- /dev/null +++ b/lib/src/visual/views/onboarding/recover_passwordless.view.dart @@ -0,0 +1,876 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:cryptography_plus/cryptography_plus.dart' + show Hmac, Mac, SecretBox, SecretKey, Xchacha20; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hashlib/random.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:restart_app/restart_app.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:twonly/core/bridge/wrapper.dart' show RustUtils; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/constants/keyvalue.keys.dart'; +import 'package:twonly/src/model/json/onboarding_state.model.dart'; +import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'; +import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart' + as server; +import 'package:twonly/src/model/protobuf/client/generated/passwordless_recovery.pb.dart'; +import 'package:twonly/src/services/backup.service.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart'; +import 'package:twonly/src/utils/keyvalue.dart'; +import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; +import 'package:twonly/src/visual/components/snackbar.dart'; +import 'package:twonly/src/visual/elements/my_button.element.dart'; +import 'package:twonly/src/visual/elements/my_input.element.dart'; +import 'package:twonly/src/visual/views/onboarding/components/animated_bell_icon.comp.dart'; + +class RecoverPasswordless extends StatefulWidget { + const RecoverPasswordless({this.initialEmailToken, super.key}); + + final String? initialEmailToken; + + @override + State createState() => _RecoverPasswordlessState(); +} + +class _RecoverPasswordlessState extends State { + bool _isLoading = true; + bool _notificationsEnabled = false; + String _shareUrl = ''; + + OnboardingState? _onboardingState; + Timer? _pollTimer; + + SharedSecretData? _reconstructedSecret; + bool _isReconstructing = false; + String? _reconstructionError; + + bool _isRecovering = false; + final TextEditingController _secondFactorController = TextEditingController(); + + StreamSubscription? _emailTokenSubscription; + + @override + void initState() { + super.initState(); + _secondFactorController.text = widget.initialEmailToken ?? ''; + _emailTokenSubscription = PasswordlessRecoveryService + .onEmailTokenReceived + .stream + .listen((token) async { + if (mounted) { + final state = _onboardingState; + if (state != null && !state.emailRecoveryRequested) { + state.emailRecoveryRequested = true; + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (s) => s.emailRecoveryRequested = true, + ); + } + setState(() { + _secondFactorController.text = token; + }); + } + }); + _initAsync(); + } + + @override + void dispose() { + _emailTokenSubscription?.cancel(); + _pollTimer?.cancel(); + _secondFactorController.dispose(); + super.dispose(); + } + + Future _initAsync() async { + try { + // 1. Load OnboardingState + final state = await KeyValueStore.getModel( + KeyValueKeys.onboardingState, + ); + + if (widget.initialEmailToken != null && !state.emailRecoveryRequested) { + state.emailRecoveryRequested = true; + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (s) => s.emailRecoveryRequested = true, + ); + } + + // 2. Generate fields if they are missing + if (state.notificationId == null) { + state + ..notificationId = uuid.v4() + ..downloadAuthToken = getRandomUint8List(32) + ..encryptionKey = getRandomUint8List(32); + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (s) { + s + ..notificationId = state.notificationId + ..downloadAuthToken = state.downloadAuthToken + ..encryptionKey = state.encryptionKey; + }, + ); + } + + // 3. Construct URL + final keyBytes = state.encryptionKey!; + final base64Key = base64Url.encode(keyBytes).replaceAll('=', ''); + _shareUrl = 'https://me.twonly.eu/r/#${state.notificationId}/$base64Key'; + if (!kReleaseMode) Log.info(_shareUrl); + + // 4. Check FCM Notification Settings + final settings = await FirebaseMessaging.instance + .getNotificationSettings(); + _notificationsEnabled = + settings.authorizationStatus == AuthorizationStatus.authorized || + settings.authorizationStatus == AuthorizationStatus.provisional; + + // 5. Register with the server if not registered yet + if (!state.serverRegistered) { + await _registerPasswordlessNotification(state); + } + + _onboardingState = state; + + // 6. If already registered, do an immediate check and start the poll timer + if (state.serverRegistered) _startPollTimer(); + + _checkAndReconstruct(); + } catch (e) { + Log.warn('Error during passwordless recovery initialization: $e'); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + + void _startPollTimer() { + if (_pollTimer != null) return; + _pollTimer = Timer.periodic(const Duration(seconds: 10), (_) { + _pollForMessages(); + }); + unawaited(_pollForMessages()); + } + + Future _pollForMessages() async { + if (_onboardingState == null) return; + final didUpdate = + await PasswordlessRecoveryService.checkAndStorePasswordlessMessages( + _onboardingState!, + ); + // _onboardingState was changed in checkAndStorePasswordlessMessages + if (didUpdate && mounted) { + setState(() => ()); + _checkAndReconstruct(); + } + } + + void _checkAndReconstruct() { + final state = _onboardingState; + if (state != null) { + final shares = state.receivedShares; + if (shares.isNotEmpty) { + final threshold = shares.first.threshold; + if (shares.length >= threshold && + _reconstructedSecret == null && + !_isReconstructing) { + unawaited(_reconstructSecret()); + } + } + } + } + + Future _reconstructSecret() async { + final state = _onboardingState; + if (state == null) return; + final shares = state.receivedShares; + if (shares.isEmpty) return; + final threshold = shares.first.threshold; + if (shares.length < threshold) return; + + setState(() { + _isReconstructing = true; + _reconstructionError = null; + }); + + try { + final shareBytesList = shares + .map((s) => Uint8List.fromList(s.sharedSecretDataBytes)) + .toList(); + final secretBytes = await RustUtils.recoverSecret( + shares: shareBytesList, + threshold: threshold, + ); + final sharedSecretData = SharedSecretData.fromBuffer(secretBytes); + if (mounted) { + setState(() { + _reconstructedSecret = sharedSecretData; + }); + } + } catch (e) { + Log.error('Failed to reconstruct secret: $e'); + if (mounted) { + setState(() { + _reconstructionError = e.toString(); + }); + } + } finally { + if (mounted) { + setState(() { + _isReconstructing = false; + }); + } + } + } + + Future _recoverNow(List shares) async { + final reconstructed = _reconstructedSecret; + if (reconstructed == null) return; + + setState(() { + _isRecovering = true; + }); + + try { + final userId = shares.first.myUserId; + Uint8List? serverKey; + + if (reconstructed.hasPinSeed()) { + final pin = _secondFactorController.text.trim(); + if (pin.isEmpty) { + showSnackbar( + context, + context.lang.passwordlessRecoveryEnterPin, + ); + setState(() { + _isRecovering = false; + }); + return; + } + + // Calculate pinProtectionKey + final pinProtectionKey = await Hmac.sha256().calculateMac( + Uint8List.fromList(utf8.encode(pin)), + secretKey: SecretKey(reconstructed.pinSeed), + ); + + // Fetch serverKey + final res = await apiService.getServerKeyForPasswordlessRecovery( + userId: userId, + pinUnlockToken: reconstructed.pinUnlockToken, + pinProtectionKey: pinProtectionKey.bytes, + encryptedServerKeyNone: reconstructed.encryptedServerKeyNonce, + ); + + if (res.isError) { + if (mounted) { + showSnackbar( + context, + context.lang.passwordlessRecoveryTestPinIncorrect, + ); + } + setState(() { + _isRecovering = false; + }); + return; + } + + final ok = res.value as server.Response_Ok; + serverKey = Uint8List.fromList(ok.passwordlessRecoveryServerKey); + } else if (reconstructed.hasEmailHint()) { + final state = _onboardingState; + if (state == null) return; + + if (!state.emailRecoveryRequested) { + // Stage 1: Send Email + final email = _secondFactorController.text.trim(); + if (email.isEmpty) { + showSnackbar( + context, + context.lang.passwordlessRecoveryEnterEmail, + ); + setState(() { + _isRecovering = false; + }); + return; + } + + // Fetch serverKey (sends recovery email) + final res = await apiService.getServerKeyForPasswordlessRecovery( + userId: userId, + email: email, + encryptedServerKeyNone: reconstructed.encryptedServerKeyNonce, + ); + + if (res.isError) { + if (mounted) { + final isInternalError = res.error == ErrorCode.InternalError; + showSnackbar( + context, + isInternalError + ? context.lang.passwordlessRecoveryNetworkError + : context.lang.passwordlessRecoveryInvalidEmail, + ); + } + setState(() { + _isRecovering = false; + }); + return; + } + + // Success - server sent recovery email. Store in state + state.emailRecoveryRequested = true; + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (s) => s.emailRecoveryRequested = true, + ); + + _secondFactorController.clear(); + if (mounted) { + showSnackbar( + context, + context.lang.passwordlessRecoveryShareSent, + level: SnackbarLevel.success, + ); + } + setState(() { + _isRecovering = false; + }); + return; + } else { + // Stage 2: Token verification + final token = _secondFactorController.text.trim(); + if (token.isEmpty) { + if (mounted) { + showSnackbar( + context, + 'Please enter the recovery token from your email.', + ); + } + setState(() { + _isRecovering = false; + }); + return; + } + + try { + serverKey = base64Url.decode(base64Url.normalize(token)); + } catch (e) { + if (mounted) { + showSnackbar( + context, + 'Invalid verification token format.', + ); + } + setState(() { + _isRecovering = false; + }); + return; + } + } + } + + // Decrypt recoveryData using serverKey if present + List recoveryDataBytes; + if (serverKey != null) { + final envelope = EncryptedEnvelope.fromBuffer( + reconstructed.recoveryData, + ); + final secretBox = SecretBox( + envelope.encryptedData, + nonce: envelope.iv, + mac: Mac(envelope.mac), + ); + final xchacha20 = Xchacha20.poly1305Aead(); + recoveryDataBytes = await xchacha20.decrypt( + secretBox, + secretKey: SecretKey(serverKey), + ); + } else { + recoveryDataBytes = reconstructed.recoveryData; + } + + final recoveryData = RecoveryData.fromBuffer(recoveryDataBytes); + + // Start full passwordless recovery + final error = await BackupService.startPasswordlessBackupRecovery( + recoveryData.userId.toInt(), + shares.first.myDisplayName, + Uint8List.fromList(recoveryData.keyManager), + ); + + if (!mounted) return; + + if (error != null) { + showSnackbar( + context, + error.toLocalizedString(context), + ); + setState(() { + _isRecovering = false; + }); + return; + } + + // Successful! Restart the app to apply restored keymanager/archive database + await Restart.restartApp( + notificationTitle: context.lang.recoverSuccessTitle, + notificationBody: context.lang.recoverSuccessBody, + forceKill: true, + ); + } catch (e) { + Log.error('Failed to recover passwordless: $e'); + if (mounted) { + showSnackbar( + context, + '${context.lang.recoverErrorUnknown}: $e', + ); + } + } finally { + if (mounted) { + setState(() { + _isRecovering = false; + }); + } + } + } + + Future _registerPasswordlessNotification(OnboardingState state) async { + final fcmToken = await FirebaseMessaging.instance.getToken(); + if (!mounted) return; + final res = await apiService.registerPasswordlessNotification( + notificationId: state.notificationId!, + downloadAuthToken: state.downloadAuthToken!, + langCode: Localizations.localeOf(context).languageCode, + googleFcm: fcmToken, + ); + if (res.isSuccess) { + state.serverRegistered = true; + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (s) => s.serverRegistered = true, + ); + _startPollTimer(); + } + } + + Future _requestNotificationPermission() async { + try { + final settings = await FirebaseMessaging.instance.requestPermission(); + if (settings.authorizationStatus == AuthorizationStatus.authorized || + settings.authorizationStatus == AuthorizationStatus.provisional) { + if (mounted) setState(() => _notificationsEnabled = true); + final state = _onboardingState; + if (state != null) { + await _registerPasswordlessNotification(state); + } + } + } catch (e) { + Log.error('Error requesting notification permission: $e'); + } + } + + Widget _buildProgressSection( + BuildContext context, + bool isDark, + List shares, + ) { + final first = shares.first; + final threshold = first.threshold; + final thresholdReached = shares.length >= threshold; + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Column( + children: [ + AvatarIcon( + svg: utf8.decode(first.myAvatarSvg ?? []), + fontSize: 60, + ), + const SizedBox(height: 12), + Text( + first.myDisplayName, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + const SizedBox(height: 24), + + Row( + children: [ + Text( + context.lang.recoverPasswordlessSharesReceived( + shares.length, + threshold, + ), + style: Theme.of(context).textTheme.bodyMedium, + ), + const Spacer(), + if (thresholdReached) + const Icon( + Icons.check_circle_rounded, + color: Colors.green, + size: 20, + ), + ], + ), + const SizedBox(height: 8), + ClipRRect( + borderRadius: BorderRadius.circular(8), + child: LinearProgressIndicator( + value: shares.length / threshold, + minHeight: 8, + backgroundColor: isDark + ? Colors.white.withValues(alpha: 0.1) + : Colors.black.withValues(alpha: 0.08), + valueColor: AlwaysStoppedAnimation( + thresholdReached ? Colors.green : context.color.primary, + ), + ), + ), + const SizedBox(height: 16), + + ...shares.map( + (share) => Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text( + share.trustedFriendDisplayName, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ), + + const SizedBox(height: 24), + + if (thresholdReached) ...[ + if (_isReconstructing) + const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 24), + child: CircularProgressIndicator(), + ), + ) + else if (_reconstructionError != null) + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + 'Reconstruction failed: $_reconstructionError', + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ), + ) + else if (_reconstructedSecret != null) ...[ + if (_reconstructedSecret!.hasPinSeed()) ...[ + MyInput( + controller: _secondFactorController, + hintText: context.lang.passwordlessRecoveryMethodPinHint, + keyboardType: TextInputType.number, + ), + const SizedBox(height: 16), + ] else if (_reconstructedSecret!.hasEmailHint()) ...[ + if (_onboardingState != null && + !_onboardingState!.emailRecoveryRequested) ...[ + MyInput( + controller: _secondFactorController, + hintText: _reconstructedSecret!.emailHint, + prefixIcon: const Icon(Icons.email_rounded), + keyboardType: TextInputType.emailAddress, + ), + const SizedBox(height: 16), + ] else ...[ + MyInput( + controller: _secondFactorController, + hintText: 'Enter recovery token', + prefixIcon: const Icon(Icons.key_rounded), + ), + const SizedBox(height: 16), + ], + ], + MyButton( + onPressed: _isRecovering ? null : () => _recoverNow(shares), + child: _isRecovering + ? const SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + strokeWidth: 2.5, + color: Colors.black87, + ), + ) + : Text( + _reconstructedSecret!.hasEmailHint() && + _onboardingState != null && + !_onboardingState!.emailRecoveryRequested + ? 'Send recovery email' + : context.lang.recoverPasswordlessRecoverNowBtn, + ), + ), + const SizedBox(height: 16), + if (_reconstructedSecret!.hasEmailHint() && + _onboardingState != null && + _onboardingState!.emailRecoveryRequested) ...[ + MyButton( + onPressed: _isRecovering + ? null + : () async { + final state = _onboardingState; + if (state == null) return; + setState(() { + state.emailRecoveryRequested = false; + _secondFactorController.clear(); + }); + await KeyValueStore.update( + key: KeyValueKeys.onboardingState, + update: (s) => s.emailRecoveryRequested = false, + ); + }, + variant: MyButtonVariant.secondary, + child: Text(context.lang.passwordlessRecoveryResendEmail), + ), + const SizedBox(height: 16), + ], + ], + ], + ], + ); + } + + @override + Widget build(BuildContext context) { + final isDark = isDarkMode(context); + final shares = _onboardingState?.receivedShares ?? []; + final hasShares = shares.isNotEmpty; + final threshold = hasShares ? shares.first.threshold : 0; + final thresholdReached = hasShares && shares.length >= threshold; + + if (_isLoading) { + return Scaffold( + appBar: AppBar( + title: Text(context.lang.passwordlessRecoveryRecoverBtn), + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new_rounded), + color: isDark ? Colors.white70 : Colors.black54, + iconSize: 20, + onPressed: () => Navigator.of(context).pop(), + ), + ), + body: const Center( + child: CircularProgressIndicator(), + ), + ); + } + + return Scaffold( + appBar: AppBar( + title: Text(context.lang.passwordlessRecoveryRecoverBtn), + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new_rounded), + color: isDark ? Colors.white70 : Colors.black54, + iconSize: 20, + onPressed: () => Navigator.of(context).pop(), + ), + ), + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (!hasShares) ...[ + Text( + context.lang.recoverPasswordlessExplanation, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), + ), + const SizedBox(height: 24), + ], + + if (hasShares) _buildProgressSection(context, isDark, shares), + + if (!_notificationsEnabled && !hasShares) ...[ + Container( + decoration: BoxDecoration( + color: isDark + ? Colors.white.withValues(alpha: 0.05) + : Colors.black.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: isDark + ? Colors.white.withValues(alpha: 0.08) + : Colors.black.withValues(alpha: 0.06), + ), + ), + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const AnimatedBellIcon(), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context + .lang + .recoverPasswordlessNotificationCardTitle, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 4), + Text( + context + .lang + .recoverPasswordlessNotificationCardSubtitle, + style: const TextStyle(fontSize: 13), + ), + const SizedBox(height: 12), + MyButton( + variant: MyButtonVariant.secondaryDense, + onPressed: _requestNotificationPermission, + child: Text( + context + .lang + .recoverPasswordlessNotificationCardBtn, + ), + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 32), + ], + + if (!thresholdReached) ...[ + if (!hasShares) + Container( + decoration: BoxDecoration( + color: isDark + ? Colors.white.withValues(alpha: 0.05) + : Colors.black.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: isDark + ? Colors.white.withValues(alpha: 0.08) + : Colors.black.withValues(alpha: 0.06), + ), + ), + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Icon( + Icons.info_outline_rounded, + color: isDark ? Colors.white70 : Colors.black54, + size: 22, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + context.lang.recoverPasswordlessQrInstructions, + style: const TextStyle(fontSize: 14, height: 1.4), + ), + ), + ], + ), + ), + + if (!hasShares) const SizedBox(height: 20), + + Center( + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(24), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues( + alpha: isDark ? 0.3 : 0.08, + ), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + child: QrImageView.withQr( + qr: QrCode.fromData( + data: _shareUrl, + errorCorrectLevel: QrErrorCorrectLevel.M, + ), + eyeStyle: const QrEyeStyle( + color: Colors.black, + borderRadius: 4, + ), + dataModuleStyle: const QrDataModuleStyle( + color: Colors.black, + borderRadius: 4, + ), + gapless: false, + size: 200, + ), + ), + ), + const SizedBox(height: 32), + + MyInput( + controller: TextEditingController(text: _shareUrl), + readOnly: true, + hintText: '', + prefixIcon: const Icon(Icons.link_rounded), + suffixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.copy_rounded), + tooltip: context.lang.recoverPasswordlessCopyBtn, + onPressed: () { + Clipboard.setData( + ClipboardData(text: _shareUrl), + ); + showSnackbar( + context, + context.lang.recoverPasswordlessCopiedSnackbar, + level: SnackbarLevel.success, + ); + }, + ), + IconButton( + icon: const Icon(Icons.share_rounded), + tooltip: context.lang.recoverPasswordlessShareBtn, + onPressed: () { + final params = ShareParams( + text: _shareUrl, + ); + SharePlus.instance.share(params); + }, + ), + ], + ), + ), + const SizedBox(height: 24), + ], + ], + ), + ), + ), + ); + } +} diff --git a/lib/src/visual/views/onboarding/register.view.dart b/lib/src/visual/views/onboarding/register.view.dart index e3a4dd59..d30926f1 100644 --- a/lib/src/visual/views/onboarding/register.view.dart +++ b/lib/src/visual/views/onboarding/register.view.dart @@ -169,7 +169,7 @@ class _RegisterViewState extends State { await apiService.authenticate(); widget.callbackOnSuccess(); } catch (e, stack) { - Log.error('Error creating new user', e, stack); + Log.error('Error creating new user', error: e, stackTrace: stack); if (mounted) { setState(() { _isTryingToRegister = false; diff --git a/lib/src/visual/views/onboarding/setup.view.dart b/lib/src/visual/views/onboarding/setup.view.dart index 57f4704e..285ab90d 100644 --- a/lib/src/visual/views/onboarding/setup.view.dart +++ b/lib/src/visual/views/onboarding/setup.view.dart @@ -10,17 +10,13 @@ import 'package:twonly/src/visual/views/onboarding/setup/backup.setup.dart'; import 'package:twonly/src/visual/views/onboarding/setup/let_your_friends_find_you.setup.dart'; import 'package:twonly/src/visual/views/onboarding/setup/profile.setup.dart'; import 'package:twonly/src/visual/views/onboarding/setup/profile_selection.setup.dart'; -import 'package:twonly/src/visual/views/onboarding/setup/security_profile.setup.dart'; import 'package:twonly/src/visual/views/onboarding/setup/share_your_friends.setup.dart'; -import 'package:twonly/src/visual/views/onboarding/setup/verification_badge.setup.dart'; import 'package:twonly/src/visual/views/settings/privacy/user_discovery/components/user_discovery_setup.comp.dart'; enum SetupPages { profile, backup, - verificationBadge, profileSelection, - securityProfile, shareYourFriends, letYourFriendsFindYou, } @@ -40,23 +36,13 @@ extension SetupPagesExtension on SetupPages { return [ SetupPages.profile, SetupPages.backup, - SetupPages.verificationBadge, - SetupPages.profileSelection, - ]; - case SetupProfile.maximum: - return [ - SetupPages.profile, - SetupPages.backup, - SetupPages.verificationBadge, SetupPages.profileSelection, ]; case SetupProfile.customized: return [ SetupPages.profile, SetupPages.backup, - SetupPages.verificationBadge, SetupPages.profileSelection, - SetupPages.securityProfile, SetupPages.shareYourFriends, SetupPages.letYourFriendsFindYou, ]; @@ -223,10 +209,6 @@ class _SetupViewState extends State { return const BackupSetupPage(); case SetupPages.profileSelection: return const ProfileSelectionSetup(); - case SetupPages.securityProfile: - return const SecurityProfileSetup(); - case SetupPages.verificationBadge: - return const VerificationBadgeSetupPage(); case SetupPages.shareYourFriends: return ShareYourFriendsSetupPage(state: state); case SetupPages.letYourFriendsFindYou: diff --git a/lib/src/visual/views/onboarding/setup/backup.setup.dart b/lib/src/visual/views/onboarding/setup/backup.setup.dart index aeff4d5c..5fb49224 100644 --- a/lib/src/visual/views/onboarding/setup/backup.setup.dart +++ b/lib/src/visual/views/onboarding/setup/backup.setup.dart @@ -22,10 +22,6 @@ class _BackupSetupPageState extends State { final TextEditingController _repeatedPasswordCtrl = TextEditingController(); Future onPressedEnableTwonlySafe() async { - setState(() { - _isLoading = true; - }); - if (!await isSecurePassword(_passwordCtrl.text)) { if (!mounted) return true; final ignore = await showAlertDialog( @@ -37,13 +33,14 @@ class _BackupSetupPageState extends State { ); if (!mounted) return true; if (ignore) { - setState(() { - _isLoading = false; - }); return true; } } + setState(() { + _isLoading = true; + }); + await Future.delayed(const Duration(milliseconds: 100)); await BackupService.updateBackupPassword(_passwordCtrl.text); diff --git a/lib/src/visual/views/onboarding/setup/components/profile_card.comp.dart b/lib/src/visual/views/onboarding/setup/components/profile_card.comp.dart index 9f1206cc..fe8bd5a3 100644 --- a/lib/src/visual/views/onboarding/setup/components/profile_card.comp.dart +++ b/lib/src/visual/views/onboarding/setup/components/profile_card.comp.dart @@ -12,7 +12,7 @@ class SafetyProfileCard extends StatelessWidget { super.key, }); - final Object profile; + final SetupProfile profile; final bool isSelected; final VoidCallback onTap; final bool isHovered; @@ -32,60 +32,24 @@ class SafetyProfileCard extends StatelessWidget { final IconData icon; final String? badgeText; - if (profile is SetupProfile) { - switch (profile as SetupProfile) { - case SetupProfile.standard: - title = context.lang.onboardingProfileSelectionDefaultTitle; - subtitle = Text( - context.lang.onboardingProfileSelectionDefaultDesc, - style: bodyMediumStyle, - ); - icon = Icons.bolt_rounded; - badgeText = context.lang.onboardingProfileSelectionDefaultBadge; - case SetupProfile.customized: - title = context.lang.onboardingProfileSelectionCustomizeTitle; - subtitle = Text( - context.lang.onboardingProfileSelectionCustomizeDesc, - style: bodyMediumStyle, - ); - icon = Icons.tune_rounded; - badgeText = null; - case SetupProfile.maximum: - title = context.lang.onboardingProfileSelectionStrictTitle; - subtitle = RichText( - text: TextSpan( - style: bodyMediumStyle, - children: formattedText( - context, - context.lang.onboardingProfileSelectionStrictDesc, - boldTextColor: context.color.onSurface, - ), - ), - ); - icon = Icons.lock_outline_rounded; - badgeText = null; - } - } else if (profile is SecurityProfile) { - switch (profile as SecurityProfile) { - case SecurityProfile.normal: - title = context.lang.securityProfileNormalTitle; - subtitle = Text( - context.lang.securityProfileNormalDesc, - style: bodyMediumStyle, - ); - icon = Icons.shield_outlined; - badgeText = null; - case SecurityProfile.strict: - title = context.lang.securityProfileStrictTitle; - subtitle = Text( - context.lang.securityProfileStrictDesc, - style: bodyMediumStyle, - ); - icon = Icons.verified_user_outlined; - badgeText = null; - } - } else { - throw ArgumentError('Invalid profile type: $profile'); + switch (profile) { + case SetupProfile.standard: + title = context.lang.onboardingProfileSelectionDefaultTitle; + subtitle = Text( + context.lang.onboardingProfileSelectionDefaultDesc, + style: bodyMediumStyle, + ); + icon = Icons.bolt_rounded; + badgeText = context.lang.onboardingProfileSelectionDefaultBadge; + case SetupProfile.customized: + title = context.lang.onboardingProfileSelectionCustomizeTitle; + subtitle = Text( + context.lang.onboardingProfileSelectionCustomizeDesc, + style: bodyMediumStyle, + ); + icon = Icons.tune_rounded; + badgeText = null; + } return MouseRegion( diff --git a/lib/src/visual/views/onboarding/setup/profile_selection.setup.dart b/lib/src/visual/views/onboarding/setup/profile_selection.setup.dart index abe4f776..f6bba8be 100644 --- a/lib/src/visual/views/onboarding/setup/profile_selection.setup.dart +++ b/lib/src/visual/views/onboarding/setup/profile_selection.setup.dart @@ -21,11 +21,6 @@ class _ProfileSelectionSetupState extends State { Future _onProfileTapped(SetupProfile profile) async { await UserService.update((user) { user.setupProfile = profile; - if (profile == SetupProfile.standard) { - user.securityProfile = SecurityProfile.normal; - } else if (profile == SetupProfile.maximum) { - user.securityProfile = SecurityProfile.strict; - } }); } @@ -76,16 +71,7 @@ class _ProfileSelectionSetupState extends State { }), onTap: () => _onProfileTapped(SetupProfile.customized), ), - const SizedBox(height: 16), - SafetyProfileCard( - profile: SetupProfile.maximum, - isSelected: selectedProfile == SetupProfile.maximum, - isHovered: _hoveredProfile == SetupProfile.maximum, - onHover: (hovered) => setState(() { - _hoveredProfile = hovered ? SetupProfile.maximum : null; - }), - onTap: () => _onProfileTapped(SetupProfile.maximum), - ), + const SizedBox(height: 40), NextButtonComp( key: ValueKey(selectedProfile), diff --git a/lib/src/visual/views/onboarding/setup/security_profile.setup.dart b/lib/src/visual/views/onboarding/setup/security_profile.setup.dart deleted file mode 100644 index 0bde4049..00000000 --- a/lib/src/visual/views/onboarding/setup/security_profile.setup.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:twonly/locator.dart'; -import 'package:twonly/src/services/profile.service.dart'; -import 'package:twonly/src/services/user.service.dart'; -import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/views/onboarding/setup/components/next_button.comp.dart'; -import 'package:twonly/src/visual/views/onboarding/setup/components/profile_card.comp.dart'; - -class SecurityProfileSetup extends StatefulWidget { - const SecurityProfileSetup({super.key}); - - @override - State createState() => _SecurityProfileSetupState(); -} - -class _SecurityProfileSetupState extends State { - SecurityProfile? _hoveredProfile; - - Future _onProfileTapped(SecurityProfile profile) async { - await UserService.update((user) { - user.securityProfile = profile; - }); - } - - @override - Widget build(BuildContext context) { - return StreamBuilder( - stream: userService.onUserUpdated, - builder: (context, snapshot) { - final user = userService.currentUser; - final selectedProfile = user.securityProfile; - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - context.lang.securityProfileTitle, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 8), - Text( - context.lang.securityProfileSubtitle, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: context.color.onSurfaceVariant, - ), - ), - const SizedBox(height: 32), - SafetyProfileCard( - profile: SecurityProfile.normal, - isSelected: selectedProfile == SecurityProfile.normal, - isHovered: _hoveredProfile == SecurityProfile.normal, - onHover: (hovered) => setState(() { - _hoveredProfile = hovered ? SecurityProfile.normal : null; - }), - onTap: () => _onProfileTapped(SecurityProfile.normal), - ), - const SizedBox(height: 16), - SafetyProfileCard( - profile: SecurityProfile.strict, - isSelected: selectedProfile == SecurityProfile.strict, - isHovered: _hoveredProfile == SecurityProfile.strict, - onHover: (hovered) => setState(() { - _hoveredProfile = hovered ? SecurityProfile.strict : null; - }), - onTap: () => _onProfileTapped(SecurityProfile.strict), - ), - const SizedBox(height: 40), - const NextButtonComp(), - ], - ); - }, - ); - } -} diff --git a/lib/src/visual/views/onboarding/setup/verification_badge.setup.dart b/lib/src/visual/views/onboarding/setup/verification_badge.setup.dart deleted file mode 100644 index 555dfb4d..00000000 --- a/lib/src/visual/views/onboarding/setup/verification_badge.setup.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/components/verification_badge_info.comp.dart'; -import 'package:twonly/src/visual/views/onboarding/setup/components/next_button.comp.dart'; - -class VerificationBadgeSetupPage extends StatelessWidget { - const VerificationBadgeSetupPage({super.key}); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - context.lang.onboardingVerificationBadgeTitle, - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 30), - const VerificationBadgeInfo(), - const SizedBox(height: 60), - const NextButtonComp(), - ], - ); - } -} diff --git a/lib/src/visual/views/public_profile.view.dart b/lib/src/visual/views/public_profile.view.dart index cda31a15..c9b588be 100644 --- a/lib/src/visual/views/public_profile.view.dart +++ b/lib/src/visual/views/public_profile.view.dart @@ -83,7 +83,12 @@ class _PublicProfileViewState extends State { BetterListTile( leading: const FaIcon(FontAwesomeIcons.qrcode), text: context.lang.scanOtherProfile, - onTap: () => context.push(Routes.cameraQRScanner), + onTap: () => context.push( + Routes.cameraQRScanner, + extra: { + 'openToVerify': true, + }, + ), ), BetterListTile( leading: const FaIcon( @@ -98,7 +103,8 @@ class _PublicProfileViewState extends State { ), onTap: () { final params = ShareParams( - text: 'https://me.twonly.eu/${userService.currentUser.username}#${base64Url.encode(_publicKey!)}', + text: + 'https://me.twonly.eu/${userService.currentUser.username}#${base64Url.encode(_publicKey!)}', ); SharePlus.instance.share(params); }, diff --git a/lib/src/visual/views/recovery.view.dart b/lib/src/visual/views/recovery_from_secure_storage.view.dart similarity index 90% rename from lib/src/visual/views/recovery.view.dart rename to lib/src/visual/views/recovery_from_secure_storage.view.dart index c9d683c2..2a1397a0 100644 --- a/lib/src/visual/views/recovery.view.dart +++ b/lib/src/visual/views/recovery_from_secure_storage.view.dart @@ -30,22 +30,9 @@ class _RecoveryViewState extends State { if (!mounted) return; if (error != null) { - String msg; - switch (error) { - case RecoveryError.noInternet: - msg = context.lang.recoverErrorNoInternet; - case RecoveryError.usernameNotValid: - msg = context.lang.recoverErrorUsernameNotValid; - case RecoveryError.passwordInvalid: - msg = context.lang.recoverErrorPasswordInvalid; - case RecoveryError.tryAgainLater: - msg = context.lang.recoverErrorTryAgainLater; - case RecoveryError.unkownError: - msg = context.lang.recoverErrorUnknown; - } setState(() { _isLoading = false; - _errorMessage = msg; + _errorMessage = error.toLocalizedString(context); _showRegisterNewPrompt = true; }); return; diff --git a/lib/src/visual/views/settings/backup/backup_settings.view.dart b/lib/src/visual/views/settings/backup/backup_settings.view.dart index c0182bee..54c0e749 100644 --- a/lib/src/visual/views/settings/backup/backup_settings.view.dart +++ b/lib/src/visual/views/settings/backup/backup_settings.view.dart @@ -9,6 +9,7 @@ import 'package:twonly/src/model/json/backup.model.dart'; import 'package:twonly/src/services/backup.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; +import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/status.passwordless_recovery.comp.dart'; import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart'; class BackupView extends StatefulWidget { @@ -103,6 +104,10 @@ class _BackupViewState extends State { textAlign: TextAlign.center, ), const SizedBox(height: 8), + + if (userService.currentUser.passwordLessRecovery != null) + const PasswordLessRecoveryStatus(), + if (userService.currentUser.isBackupEnabled) Column( children: [ diff --git a/lib/src/visual/views/settings/backup/backup_setup.view.dart b/lib/src/visual/views/settings/backup/backup_setup.view.dart index 5dab3c22..bf19d729 100644 --- a/lib/src/visual/views/settings/backup/backup_setup.view.dart +++ b/lib/src/visual/views/settings/backup/backup_setup.view.dart @@ -6,6 +6,7 @@ import 'package:twonly/src/services/backup.service.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/alert.dialog.dart'; +import 'package:twonly/src/visual/components/snackbar.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; import 'package:twonly/src/visual/views/settings/backup/components/backup_setup.comp.dart'; @@ -26,13 +27,9 @@ class SetupBackupView extends StatefulWidget { class _SetupBackupViewState extends State { bool _isLoading = false; final TextEditingController _passwordController = TextEditingController(); - final TextEditingController _repeadedController = TextEditingController(); + final TextEditingController _repeatedController = TextEditingController(); Future _updateBackupPassword() async { - setState(() { - _isLoading = true; - }); - if (!await isSecurePassword(_passwordController.text)) { if (!mounted) return; final ignore = await showAlertDialog( @@ -43,14 +40,25 @@ class _SetupBackupViewState extends State { customOk: context.lang.backupInsecurePasswordCancel, ); if (ignore) { - if (mounted) { - setState(() { - _isLoading = false; - }); - } return; } } + setState(() { + _isLoading = true; + }); + + if (!mounted) return; + final verified = await authenticateUser( + context.lang.backupChangePasswordAuthReason, + ); + if (!mounted) return; + if (!verified) { + showSnackbar( + context, + context.lang.backupChangePasswordAuthFailed, + ); + return; + } await Future.delayed(const Duration(milliseconds: 100)); await BackupService.updateBackupPassword(_passwordController.text); @@ -108,15 +116,15 @@ class _SetupBackupViewState extends State { ), const SizedBox(height: 5), BackupPasswordTextField( - controller: _repeadedController, + controller: _repeatedController, labelText: context.lang.passwordRepeated, onChanged: (value) => setState(() {}), ), PasswordRequirementText( text: context.lang.passwordRepeatedNotEqual, showError: - _passwordController.text != _repeadedController.text && - _repeadedController.text.isNotEmpty, + _passwordController.text != _repeatedController.text && + _repeatedController.text.isNotEmpty, ), const SizedBox(height: 10), Text( @@ -131,7 +139,7 @@ class _SetupBackupViewState extends State { onPressed: (!_isLoading && (_passwordController.text == - _repeadedController.text && + _repeatedController.text && _passwordController.text.length >= 8 || !kReleaseMode)) ? _updateBackupPassword diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/components/passwordless_recovery_info_sheet.comp.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/components/passwordless_recovery_info_sheet.comp.dart new file mode 100644 index 00000000..9a4e6198 --- /dev/null +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/components/passwordless_recovery_info_sheet.comp.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/elements/my_button.element.dart'; + +void showPasswordlessRecoveryInfoSheet(BuildContext context) { + // ignore: inference_failure_on_function_invocation + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) { + return const PasswordlessRecoveryInfoSheet(); + }, + ); +} + +class PasswordlessRecoveryInfoSheet extends StatelessWidget { + const PasswordlessRecoveryInfoSheet({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: context.color.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(28)), + ), + child: SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + children: [ + Icon( + Icons.info_outline_rounded, + color: context.color.primary, + size: 28, + ), + const SizedBox(width: 12), + Text( + context.lang.passwordlessRecoveryInfoHowItWorks, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 24), + Text( + context.lang.passwordlessRecoveryInfoHowItWorksDesc, + ), + const SizedBox(height: 16), + Text( + context.lang.passwordlessRecoveryInfoWhySecondFactor, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Text( + context.lang.passwordlessRecoveryInfoWhySecondFactorDesc, + ), + const SizedBox(height: 32), + MyButton( + onPressed: () => Navigator.pop(context), + child: Text(context.lang.passwordlessRecoveryInfoGotIt), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart index 90fc9309..24dcf175 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart @@ -9,32 +9,38 @@ class _FactorOption { const _FactorOption({ required this.type, required this.icon, - required this.label, }); final SecondFactorType type; final IconData icon; - final String label; } const _options = [ _FactorOption( type: SecondFactorType.none, icon: Icons.block_rounded, - label: 'None', ), _FactorOption( type: SecondFactorType.pin, icon: Icons.dialpad_rounded, - label: 'PIN', ), _FactorOption( type: SecondFactorType.email, icon: Icons.email_rounded, - label: 'Email', ), ]; +String _getLabel(BuildContext context, SecondFactorType type) { + switch (type) { + case SecondFactorType.none: + return context.lang.passwordlessRecoverySecondFactorNone; + case SecondFactorType.pin: + return context.lang.passwordlessRecoverySecondFactorPin; + case SecondFactorType.email: + return context.lang.passwordlessRecoverySecondFactorEmail; + } +} + class SecondFactorPicker extends StatelessWidget { const SecondFactorPicker({ required this.selected, @@ -55,8 +61,8 @@ class SecondFactorPicker extends StatelessWidget { Widget build(BuildContext context) { return Column( children: [ - const Text( - 'Second factor method', + Text( + context.lang.passwordlessRecoveryMethod, textAlign: TextAlign.center, ), const SizedBox(height: 8), @@ -120,7 +126,7 @@ class SecondFactorPicker extends StatelessWidget { ), const SizedBox(height: 6), Text( - option.label, + _getLabel(context, option.type), style: TextStyle( fontWeight: FontWeight.bold, color: isSelected @@ -144,8 +150,8 @@ class SecondFactorPicker extends StatelessWidget { child: Column( children: switch (selected) { SecondFactorType.none => [ - const Text( - 'Without second-factor, your friends could collaborate to recover your account. Therefore, it is recommended to configure a second-factor.', + Text( + context.lang.passwordlessRecoveryMethodNoneDesc, textAlign: TextAlign.center, ), ], @@ -153,7 +159,7 @@ class SecondFactorPicker extends StatelessWidget { MyInput( key: const ValueKey('pin_input'), controller: pinController, - hintText: 'Enter PIN', + hintText: context.lang.passwordlessRecoveryMethodPinHint, keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], onChanged: (_) => onInputChanged(), @@ -163,7 +169,7 @@ class SecondFactorPicker extends StatelessWidget { MyInput( key: const ValueKey('email_input'), controller: emailController, - hintText: 'Enter recovery email address', + hintText: context.lang.passwordlessRecoveryMethodEmailHint, keyboardType: TextInputType.emailAddress, onChanged: (_) => onInputChanged(), ), @@ -172,7 +178,7 @@ class SecondFactorPicker extends StatelessWidget { text: TextSpan( children: formattedText( context, - 'Your email address is *never stored on the server* and is only sent to it in the event of a recovery.', + context.lang.passwordlessRecoveryMethodEmailDesc, ), style: TextStyle( color: context.color.onSurface, diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/components/status.passwordless_recovery.comp.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/components/status.passwordless_recovery.comp.dart new file mode 100644 index 00000000..1f952618 --- /dev/null +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/components/status.passwordless_recovery.comp.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/settings.passwordless_recovery.view.dart'; + +class PasswordLessRecoveryStatus extends StatelessWidget { + const PasswordLessRecoveryStatus({super.key}); + + @override + Widget build(BuildContext context) { + return StreamBuilder>( + stream: (twonlyDB.select( + twonlyDB.contacts, + )..where((t) => t.recoveryIsTrustedFriend.equals(true))).watch(), + builder: (context, snapshot) { + final trustedFriendsCount = snapshot.data?.length ?? 0; + + return Card( + elevation: 0, + color: context.color.surfaceContainer, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: InkWell( + borderRadius: BorderRadius.circular(16), + onTap: () { + context.navPush(const PasswordLessRecoverySettings()); + }, + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: context.color.primary.withValues(alpha: 0.1), + shape: BoxShape.circle, + ), + child: FaIcon( + FontAwesomeIcons.shieldHeart, + color: context.color.primary, + size: 24, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.lang.passwordlessRecovery, + style: Theme.of(context).textTheme.titleMedium + ?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + context.lang.passwordlessRecoveryStatusEnabled(trustedFriendsCount), + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith( + color: context.color.onSurfaceVariant, + ), + ), + ], + ), + ), + Icon( + Icons.chevron_right_rounded, + color: context.color.onSurfaceVariant, + ), + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart index 6cea8112..15993964 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart @@ -44,7 +44,7 @@ class ThresholdPicker extends StatelessWidget { if (options.length == 1) { return Text( - 'To recover your account you need ${options.first} of your selected trusted friends.', + context.lang.passwordlessRecoveryThresholdDesc(options.first), textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: context.color.onSurfaceVariant, @@ -56,7 +56,7 @@ class ThresholdPicker extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( - 'Required trusted friends for recovery', + context.lang.passwordlessRecoveryThresholdTitle, textAlign: TextAlign.center, style: Theme.of(context).textTheme.labelMedium?.copyWith( color: context.color.onSurfaceVariant, diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/components/trusted_friends_card.comp.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/components/trusted_friends_card.comp.dart index 3e7de549..5af468b5 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/components/trusted_friends_card.comp.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/components/trusted_friends_card.comp.dart @@ -1,10 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; -import 'package:twonly/src/visual/components/verification_badge.comp.dart'; +import 'package:twonly/src/visual/elements/contact_chip.element.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; class TrustedFriendsCard extends StatelessWidget { @@ -44,10 +41,10 @@ class TrustedFriendsCard extends StatelessWidget { spacing: 4, alignment: WrapAlignment.center, children: selectedContacts.map((contact) { - return _ContactChip( + return ContactChip( key: ValueKey(contact.userId), contact: contact, - onRemove: onRemoveContact, + onTap: onRemoveContact, ); }).toList(), ), @@ -64,8 +61,10 @@ class TrustedFriendsCard extends StatelessWidget { const SizedBox(width: 8), Text( needsMoreFriends - ? 'Select friends ($missingCount more needed)' - : 'Select trusted friends', + ? context.lang.passwordlessRecoverySelectFriendsNeeded( + missingCount, + ) + : context.lang.passwordlessRecoverySelectFriends, ), ], ), @@ -87,7 +86,7 @@ class TrustedFriendsCard extends StatelessWidget { ), const SizedBox(height: 12), Text( - 'No trusted friends selected yet', + context.lang.passwordlessRecoveryNoFriendsSelected, style: TextStyle( fontSize: 14, color: context.color.onSurfaceVariant, @@ -100,49 +99,3 @@ class TrustedFriendsCard extends StatelessWidget { ); } } - -class _ContactChip extends StatelessWidget { - const _ContactChip({ - required this.contact, - required this.onRemove, - super.key, - }); - - final Contact contact; - final void Function(int) onRemove; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => onRemove(contact.userId), - child: Chip( - avatar: AvatarIcon( - contactId: contact.userId, - fontSize: 10, - ), - label: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - getContactDisplayName(contact), - style: const TextStyle(fontSize: 14), - overflow: TextOverflow.ellipsis, - ), - const SizedBox(width: 6), - VerificationBadgeComp( - contact: contact, - size: 12, - clickable: false, - ), - const SizedBox(width: 15), - const FaIcon( - FontAwesomeIcons.xmark, - color: Colors.grey, - size: 12, - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart new file mode 100644 index 00000000..9f4caa80 --- /dev/null +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart @@ -0,0 +1,324 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart' + show getContactDisplayName; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; +import 'package:twonly/src/visual/components/snackbar.dart'; +import 'package:twonly/src/visual/components/verification_badge.comp.dart'; +import 'package:twonly/src/visual/elements/my_button.element.dart'; +import 'package:twonly/src/visual/elements/my_input.element.dart'; + +class HelpAFriendPasswordlessRecoveryView extends StatefulWidget { + const HelpAFriendPasswordlessRecoveryView({ + required this.notificationId, + required this.encryptionKey, + super.key, + }); + + final String notificationId; + final List encryptionKey; + + @override + State createState() => + _HelpAFriendPasswordlessRecoveryViewState(); +} + +class _HelpAFriendPasswordlessRecoveryViewState + extends State { + final TextEditingController _searchController = TextEditingController(); + List _contacts = []; + bool _isLoading = false; + late StreamSubscription> _contactSub; + + @override + void initState() { + super.initState(); + _contactSub = twonlyDB.contactsDao.watchAllAcceptedContacts().listen(( + update, + ) { + if (mounted) { + setState(() { + _contacts = update; + }); + } + }); + } + + @override + void dispose() { + _searchController.dispose(); + _contactSub.cancel(); + super.dispose(); + } + + Future _onContactSelected(Contact contact) async { + if (contact.recoveryContactsSecretShare == null) { + showSnackbar( + context, + context.lang.passwordlessRecoveryNoShareStored, + level: SnackbarLevel.warning, + ); + return; + } + + await showDialog( + context: context, + builder: (context) { + return ConfirmRecoveryDialog( + contact: contact, + onConfirm: () => _submitShare(contact), + ); + }, + ); + } + + Future _submitShare(Contact contact) async { + setState(() => _isLoading = true); + final res = await PasswordlessRecoveryService.submitRecoveryShare( + widget.notificationId, + widget.encryptionKey, + contact, + ); + if (!mounted) return; + setState(() => _isLoading = false); + + if (res) { + showSnackbar( + context, + context.lang.passwordlessRecoveryShareSent, + level: SnackbarLevel.success, + ); + Navigator.pop(context); + } else { + showSnackbar( + context, + context.lang.passwordlessRecoveryNetworkError, + ); + } + } + + @override + Widget build(BuildContext context) { + final isDark = isDarkMode(context); + final query = _searchController.text.trim().toLowerCase(); + + final filteredContacts = + _contacts.where((c) { + final name = getContactDisplayName(c).toLowerCase(); + final username = c.username.toLowerCase(); + return name.contains(query) || username.contains(query); + }).toList()..sort((a, b) { + final aHasShare = a.recoveryContactsSecretShare != null; + final bHasShare = b.recoveryContactsSecretShare != null; + if (aHasShare && !bHasShare) return -1; + if (!aHasShare && bHasShare) return 1; + return getContactDisplayName(a).compareTo(getContactDisplayName(b)); + }); + + return Scaffold( + appBar: AppBar(title: Text(context.lang.passwordlessRecoveryHelpAFriend)), + body: SafeArea( + child: _isLoading + ? const Center(child: CircularProgressIndicator()) + : Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + decoration: BoxDecoration( + color: isDark + ? Colors.white.withValues(alpha: 0.05) + : Colors.black.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: isDark + ? Colors.white.withValues(alpha: 0.08) + : Colors.black.withValues(alpha: 0.06), + ), + ), + padding: const EdgeInsets.all(16), + child: Row( + children: [ + const Icon( + Icons.info_outline_rounded, + size: 22, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + context.lang.passwordlessRecoverySelectContactDesc, + style: const TextStyle( + fontSize: 14, + height: 1.4, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 20), + MyInput( + controller: _searchController, + hintText: context.lang.passwordlessRecoverySearchContacts, + prefixIcon: const Icon(Icons.search), + onChanged: (val) => setState(() {}), + ), + const SizedBox(height: 16), + Expanded( + child: filteredContacts.isEmpty + ? Center( + child: Text( + context.lang.passwordlessRecoveryNoContactsFound, + ), + ) + : ListView.builder( + itemCount: filteredContacts.length, + itemBuilder: (context, index) { + final contact = filteredContacts[index]; + final hasShare = + contact.recoveryContactsSecretShare != null; + return Opacity( + opacity: hasShare ? 1.0 : 0.5, + child: ListTile( + leading: AvatarIcon( + contactId: contact.userId, + ), + title: Row( + children: [ + Flexible( + child: Text( + getContactDisplayName(contact), + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 4), + VerificationBadgeComp( + contact: contact, + size: 14, + clickable: false, + ), + ], + ), + subtitle: hasShare + ? null + : Text( + context.lang.passwordlessRecoveryCantHelpHim, + style: const TextStyle( + color: Colors.grey, + ), + ), + onTap: () => _onContactSelected(contact), + ), + ); + }, + ), + ), + ], + ), + ), + ), + ); + } +} + +class ConfirmRecoveryDialog extends StatefulWidget { + const ConfirmRecoveryDialog({ + required this.contact, + required this.onConfirm, + super.key, + }); + + final Contact contact; + final VoidCallback onConfirm; + + @override + State createState() => _ConfirmRecoveryDialogState(); +} + +class _ConfirmRecoveryDialogState extends State { + int _secondsRemaining = 10; + Timer? _timer; + + @override + void initState() { + super.initState(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (mounted) { + setState(() { + if (_secondsRemaining > 0) { + _secondsRemaining--; + } else { + _timer?.cancel(); + } + }); + } + }); + } + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final username = widget.contact.username; + final isEnabled = _secondsRemaining == 0; + + return AlertDialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + title: Text( + context.lang.passwordlessRecoveryDoesAskedYou(username), + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + context.lang.passwordlessRecoveryVerifySourceDesc, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).colorScheme.error, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + actions: [ + MyButton( + variant: MyButtonVariant.text, + onPressed: () => Navigator.pop(context), + child: Text(context.lang.passwordlessRecoveryNo), + ), + MyButton( + variant: MyButtonVariant.primaryMiddle, + onPressed: isEnabled + ? () { + Navigator.pop(context); + widget.onConfirm(); + } + : null, + child: Text( + isEnabled + ? context.lang.passwordlessRecoveryYes + : context.lang.passwordlessRecoveryYesWithTimer( + _secondsRemaining.toString(), + ), + ), + ), + ], + ); + } +} diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/settings.passwordless_recovery.view.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/settings.passwordless_recovery.view.dart new file mode 100644 index 00000000..f03b2bbe --- /dev/null +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/settings.passwordless_recovery.view.dart @@ -0,0 +1,329 @@ +import 'package:flutter/material.dart'; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/snackbar.dart'; +import 'package:twonly/src/visual/elements/contact_chip.element.dart'; +import 'package:twonly/src/visual/elements/my_button.element.dart'; +import 'package:twonly/src/visual/elements/my_input.element.dart'; +import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/passwordless_recovery_info_sheet.comp.dart'; +import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart'; + +class PasswordLessRecoverySettings extends StatelessWidget { + const PasswordLessRecoverySettings({super.key}); + + Future _testPin( + BuildContext context, + ) async { + final pinController = TextEditingController(); + final result = await showDialog( + context: context, + builder: (context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24), + ), + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + context.lang.passwordlessRecoveryTestPinTitle, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + MyInput( + controller: pinController, + obscureText: true, + hintText: context.lang.passwordlessRecoveryTestPinHint, + keyboardType: TextInputType.number, + autofocus: true, + ), + const SizedBox(height: 32), + Row( + children: [ + Expanded( + child: MyButton( + variant: MyButtonVariant.secondary, + onPressed: () => Navigator.pop(context), + child: Text(context.lang.cancel), + ), + ), + const SizedBox(width: 12), + Expanded( + child: MyButton( + onPressed: () => + Navigator.pop(context, pinController.text), + child: Text(context.lang.passwordlessRecoveryTest), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + + if (result == null || result.isEmpty) return; + + if (!context.mounted) return; + + final success = await PasswordlessRecoveryService.testPin(result); + + if (!context.mounted) return; + + if (success) { + showSnackbar( + context, + context.lang.passwordlessRecoveryTestPinCorrect, + level: SnackbarLevel.success, + ); + } else { + showSnackbar(context, context.lang.passwordlessRecoveryTestPinIncorrect); + } + } + + @override + Widget build(BuildContext context) { + final config = userService.currentUser.passwordLessRecovery; + + if (config == null) { + return Scaffold( + appBar: AppBar(title: Text(context.lang.passwordlessRecovery)), + body: Center( + child: Text(context.lang.passwordlessRecoveryNotConfigured), + ), + ); + } + + var secondFactorLabel = context.lang.passwordlessRecoverySecondFactorNone; + var secondFactorIcon = Icons.lock_outline_rounded; + Widget? actionButton; + + if (config.email != null) { + secondFactorLabel = context.lang + .passwordlessRecoverySecondFactorEmailLabel(config.email!); + secondFactorIcon = Icons.email_outlined; + } else if (config.pinSeed != null) { + secondFactorLabel = context.lang.passwordlessRecoverySecondFactorPin; + secondFactorIcon = Icons.pin_outlined; + actionButton = MyButton( + variant: MyButtonVariant.secondaryDense, + onPressed: () => _testPin(context), + child: Text(context.lang.passwordlessRecoveryTestPin), + ); + } + + return Scaffold( + appBar: AppBar( + title: Text(context.lang.passwordlessRecovery), + actions: [ + IconButton( + onPressed: () => showPasswordlessRecoveryInfoSheet(context), + icon: const Icon(Icons.info_outline_rounded), + ), + ], + ), + body: ListView( + padding: const EdgeInsets.all(16), + children: [ + MyButton( + variant: MyButtonVariant.secondary, + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const PasswordLessRecoverySetup(), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.manage_accounts_rounded, size: 18), + const SizedBox(width: 8), + Text( + context.lang.passwordlessRecoveryModify, + style: const TextStyle(fontSize: 14), + ), + ], + ), + const SizedBox(height: 2), + Text( + context.lang.passwordlessRecoveryModifyDesc, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: context.color.onSurfaceVariant, + fontSize: 12, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + const SizedBox(height: 24), + _buildInfoCard( + context, + title: context.lang.passwordlessRecoverySecondFactor, + value: secondFactorLabel, + icon: secondFactorIcon, + action: actionButton, + ), + const SizedBox(height: 24), + StreamBuilder>( + stream: (twonlyDB.select( + twonlyDB.contacts, + )..where((t) => t.recoveryIsTrustedFriend.equals(true))).watch(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator.adaptive(), + ); + } + + final friends = snapshot.data!; + if (friends.isEmpty) { + return Text(context.lang.passwordlessRecoveryNoFriendsFound); + } + + final activeFriends = friends.where((c) { + final lastHeartbeat = c.recoveryLastHeartbeat; + if (lastHeartbeat == null) return false; + return DateTime.now().difference(lastHeartbeat).inDays <= 14; + }).toList(); + + final inactiveFriends = friends.where((c) { + final lastHeartbeat = c.recoveryLastHeartbeat; + if (lastHeartbeat == null) return true; + return DateTime.now().difference(lastHeartbeat).inDays > 14; + }).toList(); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (activeFriends.isNotEmpty) ...[ + Text( + context.lang.passwordlessRecoveryActiveFriends, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + context.lang.passwordlessRecoveryActiveFriendsDesc, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: context.color.onSurfaceVariant, + ), + ), + const SizedBox(height: 16), + _buildFriendsGridCard( + context, + friends: activeFriends, + isActive: true, + ), + const SizedBox(height: 24), + ], + if (inactiveFriends.isNotEmpty) ...[ + Text( + context.lang.passwordlessRecoveryInactiveFriends, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + context.lang.passwordlessRecoveryInactiveFriendsDesc, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: context.color.onSurfaceVariant, + ), + ), + const SizedBox(height: 16), + _buildFriendsGridCard( + context, + friends: inactiveFriends, + isActive: false, + ), + ], + ], + ); + }, + ), + ], + ), + ); + } + + Widget _buildInfoCard( + BuildContext context, { + required String title, + required String value, + required IconData icon, + Widget? action, + }) { + return Card( + elevation: 0, + color: context.color.surfaceContainer, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Icon(icon, color: context.color.primary), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: context.color.onSurfaceVariant, + ), + ), + Text( + value, + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + ), + ?action, + ], + ), + ), + ); + } + + Widget _buildFriendsGridCard( + BuildContext context, { + required List friends, + required bool isActive, + }) { + return SizedBox( + width: double.infinity, + child: Wrap( + spacing: 8, + runSpacing: 8, + children: friends.map((contact) { + return Opacity( + opacity: isActive ? 1.0 : 0.5, + child: ContactChip( + contact: contact, + ), + ); + }).toList(), + ), + ); + } +} diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart index 88fcad5f..669694ee 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/locator.dart'; @@ -9,6 +10,7 @@ import 'package:twonly/src/services/passwordless_recovery.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/snackbar.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; +import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/passwordless_recovery_info_sheet.comp.dart'; import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart'; import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart'; import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/trusted_friends_card.comp.dart'; @@ -24,11 +26,12 @@ class PasswordLessRecoverySetup extends StatefulWidget { class _PasswordLessRecoverySetupState extends State { List _selectedContacts = []; - SecondFactorType _secondFactor = SecondFactorType.pin; + SecondFactorType _secondFactor = SecondFactorType.email; final _pinController = TextEditingController(); final _emailController = TextEditingController(); bool _isLoading = false; int _threshold = 2; + bool _isModify = false; @override void initState() { @@ -53,24 +56,45 @@ class _PasswordLessRecoverySetupState extends State { if (!mounted) return; final verified = await _loadVerifiedContacts(); + final config = userService.currentUser.passwordLessRecovery; - contacts.sortBy((c) => c.mediaSendCounter); - final verifiedContacts = contacts - .where( - (c) => - verified.contains(c.userId) & - c.accepted & - !c.blocked & - !c.accountDeleted & - !c.deletedByUser, - ) - .toList(); - setState(() { - _selectedContacts = verifiedContacts.sublist( - 0, - min(8, verifiedContacts.length), - ); - }); + if (config != null) { + final selectedContacts = contacts + .where((c) => c.recoveryIsTrustedFriend) + .toList(); + + setState(() { + _isModify = true; + _selectedContacts = selectedContacts; + if (config.email != null) { + _secondFactor = SecondFactorType.email; + _emailController.text = config.email!; + } else if (config.pinSeed != null) { + _secondFactor = SecondFactorType.pin; + } else { + _secondFactor = SecondFactorType.none; + } + _threshold = max(2, (selectedContacts.length / 2).ceil()); + }); + } else { + contacts.sortBy((c) => c.mediaSendCounter); + final verifiedContacts = contacts + .where( + (c) => + verified.contains(c.userId) & + c.accepted & + !c.blocked & + !c.accountDeleted & + !c.deletedByUser, + ) + .toList(); + setState(() { + _selectedContacts = verifiedContacts.sublist( + 0, + min(8, verifiedContacts.length), + ); + }); + } } @override @@ -90,7 +114,7 @@ class _PasswordLessRecoverySetupState extends State { contactCount: _selectedContacts.length, ); - int get _minSelectedFriends => _validThreshold + 2; + int get _minSelectedFriends => _validThreshold + (kReleaseMode ? 2 : 0); // --- Validation --- @@ -161,7 +185,7 @@ class _PasswordLessRecoverySetupState extends State { if (success) { showSnackbar( context, - 'Passwordless recovery enabled successfully!', + context.lang.passwordlessRecoveryEnableSuccess, level: SnackbarLevel.success, ); Navigator.pop(context); @@ -178,14 +202,20 @@ class _PasswordLessRecoverySetupState extends State { onTap: () => FocusScope.of(context).unfocus(), child: Scaffold( appBar: AppBar( - title: const Text('Passwordless Recovery'), + title: Text(context.lang.passwordlessRecovery), + actions: [ + IconButton( + onPressed: () => showPasswordlessRecoveryInfoSheet(context), + icon: const Icon(Icons.info_outline_rounded), + ), + ], ), body: SafeArea( child: ListView( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), children: [ Text( - 'Recover your identity without a password.', + context.lang.passwordlessRecoverySubtitle, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: context.color.onSurfaceVariant, @@ -242,9 +272,15 @@ class _PasswordLessRecoverySetupState extends State { ), ) else - const Icon(Icons.check_circle_outline_rounded, size: 20), + const FaIcon( + FontAwesomeIcons.shieldHeart, + ), const SizedBox(width: 8), - const Text('Enable Passwordless Recovery'), + Text( + _isModify + ? context.lang.passwordlessRecoveryModifyBtn + : context.lang.passwordlessRecoveryEnableBtn, + ), ], ), ), diff --git a/lib/src/visual/views/settings/help/faq/verification_badge_faq.view.dart b/lib/src/visual/views/settings/help/faq/verification_badge_faq.view.dart index c5010cc3..1970ee57 100644 --- a/lib/src/visual/views/settings/help/faq/verification_badge_faq.view.dart +++ b/lib/src/visual/views/settings/help/faq/verification_badge_faq.view.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/verification_badge_info.comp.dart'; class VerificationBadeFaqView extends StatefulWidget { - const VerificationBadeFaqView({super.key}); + const VerificationBadeFaqView({super.key, this.contact}); + + final Contact? contact; @override State createState() => @@ -19,9 +22,10 @@ class _VerificationBadeFaqViewState extends State { ), body: ListView( padding: const EdgeInsets.all(40), - children: const [ + children: [ VerificationBadgeInfo( displayButtons: true, + contact: widget.contact, ), ], ), diff --git a/lib/src/visual/views/settings/privacy.view.dart b/lib/src/visual/views/settings/privacy.view.dart index fca2affe..252dc3b7 100644 --- a/lib/src/visual/views/settings/privacy.view.dart +++ b/lib/src/visual/views/settings/privacy.view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; -import 'package:twonly/src/services/profile.service.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/verification_badge_info.comp.dart'; @@ -83,18 +82,6 @@ class _PrivacyViewState extends State { setState(() {}); }, ), - ListTile( - title: Text(context.lang.settingsPrivacyProfileSelectionTitle), - subtitle: Text( - userService.currentUser.securityProfile == SecurityProfile.strict - ? context.lang.securityProfileStrictTitle - : context.lang.securityProfileNormalTitle, - ), - onTap: () async { - await context.push(Routes.settingsPrivacyProfileSelection); - setState(() {}); - }, - ), const Divider(), ListTile( title: Text(context.lang.settingsTypingIndication), diff --git a/lib/src/visual/views/settings/privacy/profile_selection.view.dart b/lib/src/visual/views/settings/privacy/profile_selection.view.dart deleted file mode 100644 index dfd76c86..00000000 --- a/lib/src/visual/views/settings/privacy/profile_selection.view.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:twonly/locator.dart'; -import 'package:twonly/src/services/profile.service.dart'; -import 'package:twonly/src/services/user.service.dart'; -import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/views/onboarding/setup/components/profile_card.comp.dart'; - -class ProfileSelectionSettingsView extends StatefulWidget { - const ProfileSelectionSettingsView({super.key}); - - @override - State createState() => - _ProfileSelectionSettingsViewState(); -} - -class _ProfileSelectionSettingsViewState - extends State { - SecurityProfile? _hoveredProfile; - - Future _onProfileTapped(SecurityProfile profile) async { - await UserService.update((user) { - user.securityProfile = profile; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(context.lang.securityProfileTitle), - ), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(24), - child: StreamBuilder( - stream: userService.onUserUpdated, - builder: (context, snapshot) { - final user = userService.currentUser; - final selectedProfile = user.securityProfile; - - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - context.lang.securityProfileSubtitle, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: context.color.onSurfaceVariant, - ), - ), - const SizedBox(height: 32), - SafetyProfileCard( - profile: SecurityProfile.normal, - isSelected: selectedProfile == SecurityProfile.normal, - isHovered: _hoveredProfile == SecurityProfile.normal, - onHover: (hovered) => setState(() { - _hoveredProfile = hovered ? SecurityProfile.normal : null; - }), - onTap: () => _onProfileTapped(SecurityProfile.normal), - ), - const SizedBox(height: 16), - SafetyProfileCard( - profile: SecurityProfile.strict, - isSelected: selectedProfile == SecurityProfile.strict, - isHovered: _hoveredProfile == SecurityProfile.strict, - onHover: (hovered) => setState(() { - _hoveredProfile = hovered ? SecurityProfile.strict : null; - }), - onTap: () => _onProfileTapped(SecurityProfile.strict), - ), - ], - ); - }, - ), - ), - ), - ); - } -} diff --git a/lib/src/visual/views/settings/subscription/select_additional_users.view.dart b/lib/src/visual/views/settings/subscription/select_additional_users.view.dart index 6f5b45b8..e62bb2e2 100644 --- a/lib/src/visual/views/settings/subscription/select_additional_users.view.dart +++ b/lib/src/visual/views/settings/subscription/select_additional_users.view.dart @@ -13,6 +13,7 @@ import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; import 'package:twonly/src/visual/components/flame_counter.comp.dart'; import 'package:twonly/src/visual/context_menu/user.context_menu.dart'; import 'package:twonly/src/visual/decorations/input_text.decoration.dart'; +import 'package:twonly/src/visual/elements/contact_chip.element.dart'; class SelectAdditionalUsers extends StatefulWidget { const SelectAdditionalUsers({ @@ -156,10 +157,12 @@ class _SelectAdditionalUsers extends State { return Wrap( spacing: 8, children: selected.map((w) { - return _Chip( - contact: allContacts.firstWhere( - (t) => t.userId == w, - ), + final contact = allContacts.firstWhere( + (t) => t.userId == w, + ); + return ContactChip( + key: ValueKey(contact.userId), + contact: contact, onTap: toggleSelectedUser, ); }).toList(), @@ -229,41 +232,3 @@ class _SelectAdditionalUsers extends State { ); } } - -class _Chip extends StatelessWidget { - const _Chip({ - required this.contact, - required this.onTap, - }); - final Contact contact; - final void Function(int) onTap; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => onTap(contact.userId), - child: Chip( - avatar: AvatarIcon( - contactId: contact.userId, - fontSize: 10, - ), - label: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - getContactDisplayName(contact), - style: const TextStyle(fontSize: 14), - overflow: TextOverflow.ellipsis, - ), - const SizedBox(width: 15), - const FaIcon( - FontAwesomeIcons.xmark, - color: Colors.grey, - size: 12, - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/visual/views/settings/subscription/subscription.view.dart b/lib/src/visual/views/settings/subscription/subscription.view.dart index 12c36e69..587e3f4c 100644 --- a/lib/src/visual/views/settings/subscription/subscription.view.dart +++ b/lib/src/visual/views/settings/subscription/subscription.view.dart @@ -46,6 +46,7 @@ class _SubscriptionViewState extends State { additionalOwnerName = ownerId.toString(); } } + if (!mounted) return; setState(() {}); await apiService.forceIpaCheck(); } @@ -224,6 +225,7 @@ class _PlanCardState extends State { }); await context.read().buy(product); await widget.onPurchase!(); + if (!mounted) return; setState(() { _isLoading = null; }); diff --git a/lib/src/visual/views/shared/select_contacts.view.dart b/lib/src/visual/views/shared/select_contacts.view.dart index 96d42abe..5a0d9038 100644 --- a/lib/src/visual/views/shared/select_contacts.view.dart +++ b/lib/src/visual/views/shared/select_contacts.view.dart @@ -14,6 +14,7 @@ import 'package:twonly/src/visual/components/flame_counter.comp.dart'; import 'package:twonly/src/visual/components/verification_badge.comp.dart'; import 'package:twonly/src/visual/context_menu/user.context_menu.dart'; import 'package:twonly/src/visual/decorations/input_text.decoration.dart'; +import 'package:twonly/src/visual/elements/contact_chip.element.dart'; class SelectedContactView { const SelectedContactView({ @@ -86,9 +87,9 @@ class _SelectAdditionalUsers extends State { Future _loadVerifiedContacts() async { final kvs = await twonlyDB.select(twonlyDB.keyVerifications).get(); - final urs = await (twonlyDB.select(twonlyDB.userDiscoveryUserRelations) - ..where((u) => u.publicKeyVerifiedTimestamp.isNotNull())) - .get(); + final urs = await (twonlyDB.select( + twonlyDB.userDiscoveryUserRelations, + )..where((u) => u.publicKeyVerifiedTimestamp.isNotNull())).get(); if (!mounted) return; setState(() { @@ -223,7 +224,7 @@ class _SelectAdditionalUsers extends State { if (contact == null) { return const SizedBox.shrink(); } - return _Chip( + return ContactChip( key: ValueKey(contact.userId), contact: contact, onTap: toggleSelectedUser, @@ -242,7 +243,8 @@ class _SelectAdditionalUsers extends State { } final user = contacts[i]; final isVerified = verifiedUserIds.contains(user.userId); - final isSelectionDisabled = widget.onlyVerified && !isVerified; + final isSelectionDisabled = + widget.onlyVerified && !isVerified; return UserContextMenu( key: ValueKey(user.userId), contact: user, @@ -270,16 +272,18 @@ class _SelectAdditionalUsers extends State { ), subtitle: (_alreadySelected.contains(user.userId)) ? (widget.text.alreadySelectedSubtitle != null - ? Text(widget.text.alreadySelectedSubtitle!) - : Text(context.lang.alreadyInGroup)) + ? Text(widget.text.alreadySelectedSubtitle!) + : Text(context.lang.alreadyInGroup)) : (isSelectionDisabled - ? Text( - context.lang.contactNotVerified, - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), - ) - : null), + ? Text( + context.lang.contactNotVerified, + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.error, + ), + ) + : null), leading: AvatarIcon( contactId: user.userId, fontSize: 13, @@ -322,48 +326,3 @@ class _SelectAdditionalUsers extends State { ); } } - -class _Chip extends StatelessWidget { - const _Chip({ - required this.contact, - required this.onTap, - super.key, - }); - final Contact contact; - final void Function(int) onTap; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => onTap(contact.userId), - child: Chip( - avatar: AvatarIcon( - contactId: contact.userId, - fontSize: 10, - ), - label: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - getContactDisplayName(contact), - style: const TextStyle(fontSize: 14), - overflow: TextOverflow.ellipsis, - ), - const SizedBox(width: 4), - VerificationBadgeComp( - contact: contact, - size: 12, - clickable: false, - ), - const SizedBox(width: 15), - const FaIcon( - FontAwesomeIcons.xmark, - color: Colors.grey, - size: 12, - ), - ], - ), - ), - ); - } -} diff --git a/pubspec.yaml b/pubspec.yaml index 8db2d32a..d55ded6c 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.3.3+147 +version: 0.3.7+153 environment: sdk: ^3.11.0 diff --git a/rust/build.rs b/rust/build.rs index 58d91327..135361e9 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -1,6 +1,5 @@ use std::io::Result; fn main() -> Result<()> { prost_build::compile_protos(&["src/user_discovery/types.proto"], &["src/"])?; - prost_build::compile_protos(&["src/passwordless_recovery/types.proto"], &["src/"])?; Ok(()) } diff --git a/rust/src/backup/backup_archive.rs b/rust/src/backup/backup_archive.rs index 86d748e4..ca631223 100644 --- a/rust/src/backup/backup_archive.rs +++ b/rust/src/backup/backup_archive.rs @@ -57,7 +57,7 @@ impl BackupArchive { if is_db { // To avoid write-lock conflicts with Dart (which has the live database open in write mode), - // we copy the database file first, then open the copy in write mode to perform the backup. + // we copy the database file first, then open the copy to perform the backup. let temp_copy_path = backup_data_dir.join(format!("{}.temp_copy", file_name)); std::fs::copy(&file_path, &temp_copy_path)?; @@ -72,13 +72,14 @@ impl BackupArchive { .await?; // Close database connection to release file lock before removing it - drop(db); + db.pool.close().await; remove_file(&temp_copy_path)?; // Perform integrity check of the new database file let backup_db = - Database::new(&backup_database_file, encryption_key.as_deref(), false).await?; + Database::new(&backup_database_file, encryption_key.as_deref(), true).await?; backup_db.check_integrity().await?; + backup_db.pool.close().await; } else { let file_backup = backup_data_dir.join(file_name); std::fs::copy(file_path, file_backup)?; diff --git a/rust/src/bridge/wrapper/key_manager.rs b/rust/src/bridge/wrapper/key_manager.rs index d7ba4b14..06ef4490 100644 --- a/rust/src/bridge/wrapper/key_manager.rs +++ b/rust/src/bridge/wrapper/key_manager.rs @@ -108,4 +108,21 @@ impl RustKeyManager { crate::keys::KeyManager::remove_from_keychain(&ctx.secure_storage)?; Ok(()) } + + /// Serialize the key_manager. Needed for the passwordless_recovery feature. + pub async fn serialize() -> Result> { + let ctx = get_twonly_flutter()?; + let key_manager = ctx.key_manager.lock().await; + let serialized_bytes = postcard::to_allocvec(&*key_manager)?; + Ok(serialized_bytes) + } + + pub async fn import_serialized(serialized_bytes: Vec) -> Result<()> { + let ctx = get_twonly_flutter()?; + let key_manager: crate::keys::KeyManager = postcard::from_bytes(&serialized_bytes)?; + key_manager.store_to_keychain(&ctx.secure_storage)?; + *ctx.key_manager.lock().await = key_manager; + Ok(()) + } } + diff --git a/rust/src/bridge/wrapper/mod.rs b/rust/src/bridge/wrapper/mod.rs index 75173c53..756bf38c 100644 --- a/rust/src/bridge/wrapper/mod.rs +++ b/rust/src/bridge/wrapper/mod.rs @@ -1,3 +1,29 @@ pub mod backup; pub mod key_manager; pub mod user_discovery; + +use crate::error::Result; +use blahaj::{Share, Sharks}; + +pub struct RustUtils {} + +impl RustUtils { + pub fn generate_shares(secret: Vec, total: u8, threshold: u8) -> Result>> { + let sharks = Sharks(threshold); + let dealer = sharks.dealer(&secret); + let shares = dealer.take(total as usize).map(|x| Vec::from(&x)).collect(); + Ok(shares) + } + + pub fn recover_secret(shares: Vec>, threshold: u8) -> Result> { + let sharks = Sharks(threshold); + let shares: Vec = shares + .iter() + .filter_map(|x| Share::try_from(x.as_slice()).ok()) + .collect(); + let secret = sharks + .recover(&shares) + .map_err(|e| crate::error::TwonlyError::Generic(e.to_string()))?; + Ok(secret) + } +} diff --git a/rust/src/database/mod.rs b/rust/src/database/mod.rs index 665459dc..03084689 100644 --- a/rust/src/database/mod.rs +++ b/rust/src/database/mod.rs @@ -29,7 +29,7 @@ impl Database { .journal_mode(sqlx::sqlite::SqliteJournalMode::Delete) .foreign_keys(true) .read_only(read_only) - .busy_timeout(Duration::from_millis(5000)) + .busy_timeout(Duration::from_secs(30)) .pragma("synchronous", "FULL") .pragma("recursive_triggers", "ON") .log_slow_statements(tracing::log::LevelFilter::Warn, Duration::from_millis(500)); @@ -39,7 +39,7 @@ impl Database { } let pool = SqlitePoolOptions::new() - .acquire_timeout(Duration::from_secs(5)) + .acquire_timeout(Duration::from_secs(30)) .max_connections(10) .connect_with(connect_options) .await?; diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index c63d3a4b..c95d58d1 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -26,6 +26,8 @@ // Section: imports +use crate::user_discovery::traits::UserDiscoveryStore; +use crate::user_discovery::traits::UserDiscoveryUtils; use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; use flutter_rust_bridge::{Handler, IntoIntoDart}; @@ -38,7 +40,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1867463121; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 340781866; // Section: executor @@ -467,6 +469,46 @@ fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_user_id_impl( }, ) } +fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_serialized_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "rust_key_manager_import_serialized", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_serialized_bytes = >::sse_decode(&mut deserializer); + deserializer.end(); + move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || async move { + let output_ok = + crate::bridge::wrapper::key_manager::RustKeyManager::import_serialized( + api_serialized_bytes, + ) + .await?; + Ok(output_ok) + })() + .await, + ) + } + }, + ) +} fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_signal_identity_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -544,6 +586,43 @@ fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_signed_pre })().await) } }) } +fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_serialize_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "rust_key_manager_serialize", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + deserializer.end(); + move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || async move { + let output_ok = + crate::bridge::wrapper::key_manager::RustKeyManager::serialize() + .await?; + Ok(output_ok) + })() + .await, + ) + } + }, + ) +} fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -600,6 +679,349 @@ let api_record = >::sse_decode(&mut deserializer);deserializer.end(); mo })().await) } }) } +fn wire__crate__bridge__wrapper__rust_utils_generate_shares_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "rust_utils_generate_shares", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_secret = >::sse_decode(&mut deserializer); + let api_total = ::sse_decode(&mut deserializer); + let api_threshold = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::bridge::wrapper::RustUtils::generate_shares( + api_secret, + api_total, + api_threshold, + )?; + Ok(output_ok) + })(), + ) + } + }, + ) +} +fn wire__crate__bridge__wrapper__rust_utils_recover_secret_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "rust_utils_recover_secret", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_shares = >>::sse_decode(&mut deserializer); + let api_threshold = ::sse_decode(&mut deserializer); + deserializer.end(); + move |context| { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::bridge::wrapper::RustUtils::recover_secret( + api_shares, + api_threshold, + )?; + Ok(output_ok) + })(), + ) + } + }, + ) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_announced_user_by_public_id_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_get_announced_user_by_public_id", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_public_id = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_announced_user_by_public_id(&api_that, api_public_id).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_config_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_get_config", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_config(&api_that).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_promotion_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_get_contact_promotion", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_contact_id = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_contact_promotion(&api_that, api_contact_id).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_version_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_get_contact_version", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_contact_id = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_contact_version(&api_that, api_contact_id).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_other_promotions_by_public_id_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_get_other_promotions_by_public_id", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_public_id = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_other_promotions_by_public_id(&api_that, api_public_id).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_own_promotions_after_version_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_get_own_promotions_after_version", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_version = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_own_promotions_after_version(&api_that, api_version).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_share_for_contact_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_get_share_for_contact", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_contact_id = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_share_for_contact(&api_that, api_contact_id).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_new_user_relation_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_push_new_user_relation", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_from_contact_id = ::sse_decode(&mut deserializer); +let api_announced_user = ::sse_decode(&mut deserializer); +let api_public_key_verified_timestamp = >::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::push_new_user_relation(&api_that, api_from_contact_id, api_announced_user, api_public_key_verified_timestamp).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_own_promotion_and_clear_old_version_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_push_own_promotion_and_clear_old_version", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_contact_id = ::sse_decode(&mut deserializer); +let api_version = ::sse_decode(&mut deserializer); +let api_promotion = >::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::push_own_promotion_and_clear_old_version(&api_that, api_contact_id, api_version, api_promotion).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_contact_version_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_set_contact_version", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_contact_id = ::sse_decode(&mut deserializer); +let api_update = >::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::set_contact_version(&api_that, api_contact_id, api_update).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_shares_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_set_shares", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_shares = >>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::set_shares(&api_that, api_shares).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_store_other_promotion_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_store_other_promotion", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_promotion = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::store_other_promotion(&api_that, api_promotion).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_update_config_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_store_flutter_update_config", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_update = ::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::update_config(&api_that, api_update).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_sign_data_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_utils_flutter_sign_data", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_input_data = >::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter::sign_data(&api_that, &api_input_data).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_signature_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_utils_flutter_verify_signature", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_input_data = >::sse_decode(&mut deserializer); +let api_pubkey = >::sse_decode(&mut deserializer); +let api_signature = >::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter::verify_signature(&api_that, &api_input_data, &api_pubkey, &api_signature).await?; Ok(output_ok) + })().await) + } }) +} +fn wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_stored_pubkey_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "user_discovery_utils_flutter_verify_stored_pubkey", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_that = ::sse_decode(&mut deserializer); +let api_from_contact_id = ::sse_decode(&mut deserializer); +let api_pubkey = >::sse_decode(&mut deserializer);deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter::verify_stored_pubkey(&api_that, api_from_contact_id, &api_pubkey).await?; Ok(output_ok) + })().await) + } }) +} // Section: static_checks @@ -1356,6 +1778,13 @@ impl SseDecode for crate::bridge::wrapper::key_manager::RustKeyManager { } } +impl SseDecode for crate::bridge::wrapper::RustUtils { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + return crate::bridge::wrapper::RustUtils {}; + } +} + impl SseDecode for u32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1383,6 +1812,20 @@ impl SseDecode for () { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} } +impl SseDecode for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + return crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter {}; + } +} + +impl SseDecode for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + return crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter {}; + } +} + impl SseDecode for usize { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1426,13 +1869,33 @@ fn pde_ffi_dispatcher_primary_impl( 18 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_login_token_impl(port, ptr, rust_vec_len, data_len), 19 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_signal_identity_impl(port, ptr, rust_vec_len, data_len), 20 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_user_id_impl(port, ptr, rust_vec_len, data_len), -21 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_signal_identity_impl(port, ptr, rust_vec_len, data_len), -22 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_load_signed_prekey_impl(port, ptr, rust_vec_len, data_len), -23 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_load_signed_prekeys_impl(port, ptr, rust_vec_len, data_len), -24 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_key_manager_impl(port, ptr, rust_vec_len, data_len), -25 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_signed_prekey_impl(port, ptr, rust_vec_len, data_len), -26 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl(port, ptr, rust_vec_len, data_len), -27 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_store_signed_prekey_impl(port, ptr, rust_vec_len, data_len), +21 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_serialized_impl(port, ptr, rust_vec_len, data_len), +22 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_signal_identity_impl(port, ptr, rust_vec_len, data_len), +23 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_load_signed_prekey_impl(port, ptr, rust_vec_len, data_len), +24 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_load_signed_prekeys_impl(port, ptr, rust_vec_len, data_len), +25 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_key_manager_impl(port, ptr, rust_vec_len, data_len), +26 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_signed_prekey_impl(port, ptr, rust_vec_len, data_len), +27 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_serialize_impl(port, ptr, rust_vec_len, data_len), +28 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl(port, ptr, rust_vec_len, data_len), +29 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_store_signed_prekey_impl(port, ptr, rust_vec_len, data_len), +30 => wire__crate__bridge__wrapper__rust_utils_generate_shares_impl(port, ptr, rust_vec_len, data_len), +31 => wire__crate__bridge__wrapper__rust_utils_recover_secret_impl(port, ptr, rust_vec_len, data_len), +32 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_announced_user_by_public_id_impl(port, ptr, rust_vec_len, data_len), +33 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_config_impl(port, ptr, rust_vec_len, data_len), +34 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_promotion_impl(port, ptr, rust_vec_len, data_len), +35 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_version_impl(port, ptr, rust_vec_len, data_len), +36 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_other_promotions_by_public_id_impl(port, ptr, rust_vec_len, data_len), +37 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_own_promotions_after_version_impl(port, ptr, rust_vec_len, data_len), +38 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_share_for_contact_impl(port, ptr, rust_vec_len, data_len), +39 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_new_user_relation_impl(port, ptr, rust_vec_len, data_len), +40 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_own_promotion_and_clear_old_version_impl(port, ptr, rust_vec_len, data_len), +41 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_contact_version_impl(port, ptr, rust_vec_len, data_len), +42 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_shares_impl(port, ptr, rust_vec_len, data_len), +43 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_store_other_promotion_impl(port, ptr, rust_vec_len, data_len), +44 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_update_config_impl(port, ptr, rust_vec_len, data_len), +45 => wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_sign_data_impl(port, ptr, rust_vec_len, data_len), +46 => wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_signature_impl(port, ptr, rust_vec_len, data_len), +47 => wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_stored_pubkey_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1608,6 +2071,65 @@ impl flutter_rust_bridge::IntoIntoDart flutter_rust_bridge::for_generated::DartAbi { + Vec::::new().into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::bridge::wrapper::RustUtils +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::bridge::wrapper::RustUtils +{ + fn into_into_dart(self) -> crate::bridge::wrapper::RustUtils { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart + for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter +{ + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + Vec::::new().into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter +{ +} +impl + flutter_rust_bridge::IntoIntoDart< + crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter, + > for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter +{ + fn into_into_dart(self) -> crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart + for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter +{ + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + Vec::::new().into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter +{ +} +impl + flutter_rust_bridge::IntoIntoDart< + crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter, + > for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter +{ + fn into_into_dart(self) -> crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter { + self + } +} impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1849,6 +2371,11 @@ impl SseEncode for crate::bridge::wrapper::key_manager::RustKeyManager { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} } +impl SseEncode for crate::bridge::wrapper::RustUtils { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} +} + impl SseEncode for u32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1881,6 +2408,16 @@ impl SseEncode for () { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} } +impl SseEncode for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} +} + +impl SseEncode for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} +} + impl SseEncode for usize { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1906,6 +2443,8 @@ mod io { // Section: imports use super::*; + use crate::user_discovery::traits::UserDiscoveryStore; + use crate::user_discovery::traits::UserDiscoveryUtils; use flutter_rust_bridge::for_generated::byteorder::{ NativeEndian, ReadBytesExt, WriteBytesExt, }; @@ -1928,6 +2467,8 @@ mod web { // Section: imports use super::*; + use crate::user_discovery::traits::UserDiscoveryStore; + use crate::user_discovery::traits::UserDiscoveryUtils; use flutter_rust_bridge::for_generated::byteorder::{ NativeEndian, ReadBytesExt, WriteBytesExt, }; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4792ba90..5f12e840 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -6,7 +6,6 @@ mod error; mod frb_generated; mod keys; mod log; -mod passwordless_recovery; mod secure_storage; mod standalone; mod user_discovery; diff --git a/rust/src/passwordless_recovery/mod.rs b/rust/src/passwordless_recovery/mod.rs deleted file mode 100644 index 1eac912e..00000000 --- a/rust/src/passwordless_recovery/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -include!(concat!(env!("OUT_DIR"), "/passwordless_recovery.rs")); - -struct PasswordLessRecovery {} - -impl PasswordLessRecovery { - pub(crate) fn create_shared_secret_data( - total_shares: i64, - threshold: i64, - pin_unlock_token: Option>, - ) { - } -} diff --git a/scripts/generate_proto.sh b/scripts/generate_proto.sh index e1fafef8..52309111 100755 --- a/scripts/generate_proto.sh +++ b/scripts/generate_proto.sh @@ -16,13 +16,11 @@ protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "messages.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "groups.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "qr.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "data.proto" +protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "passwordless_recovery.proto" mkdir "$GENERATED_DIR/user_discovery/" &>/dev/null protoc --proto_path="./rust/src/user_discovery/" --dart_out="$GENERATED_DIR/user_discovery/" "types.proto" -mkdir "$GENERATED_DIR/passwordless_recovery/" &>/dev/null -protoc --proto_path="./rust/src/passwordless_recovery/" --dart_out="$GENERATED_DIR/passwordless_recovery/" "types.proto" - protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "push_notification.proto" protoc --proto_path="$CLIENT_DIR" --swift_out="./ios/NotificationService/" "push_notification.proto" diff --git a/test/drift/twonly_db/generated/schema.dart b/test/drift/twonly_db/generated/schema.dart index b7c0f66f..51c0ac10 100644 --- a/test/drift/twonly_db/generated/schema.dart +++ b/test/drift/twonly_db/generated/schema.dart @@ -24,6 +24,8 @@ import 'schema_v17.dart' as v17; import 'schema_v18.dart' as v18; import 'schema_v19.dart' as v19; import 'schema_v20.dart' as v20; +import 'schema_v21.dart' as v21; +import 'schema_v22.dart' as v22; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -69,6 +71,10 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v19.DatabaseAtV19(db); case 20: return v20.DatabaseAtV20(db); + case 21: + return v21.DatabaseAtV21(db); + case 22: + return v22.DatabaseAtV22(db); default: throw MissingSchemaException(version, versions); } @@ -95,5 +101,7 @@ class GeneratedHelper implements SchemaInstantiationHelper { 18, 19, 20, + 21, + 22, ]; } diff --git a/test/drift/twonly_db/generated/schema_v21.dart b/test/drift/twonly_db/generated/schema_v21.dart new file mode 100644 index 00000000..6e2e20e6 --- /dev/null +++ b/test/drift/twonly_db/generated/schema_v21.dart @@ -0,0 +1,11000 @@ +// dart format width=80 +import 'dart:typed_data' as i2; +// GENERATED BY drift_dev, DO NOT MODIFY. +// ignore_for_file: type=lint,unused_import +// +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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn displayName = GeneratedColumn( + 'display_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn nickName = GeneratedColumn( + 'nick_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn avatarSvgCompressed = + GeneratedColumn( + 'avatar_svg_compressed', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn senderProfileCounter = GeneratedColumn( + 'sender_profile_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn accepted = GeneratedColumn( + 'accepted', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (accepted IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn deletedByUser = GeneratedColumn( + 'deleted_by_user', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (deleted_by_user IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn requested = GeneratedColumn( + 'requested', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (requested IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn blocked = GeneratedColumn( + 'blocked', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (blocked IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn verified = GeneratedColumn( + 'verified', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (verified IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn accountDeleted = GeneratedColumn( + 'account_deleted', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (account_deleted IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn userDiscoveryVersion = + GeneratedColumn( + 'user_discovery_version', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn userDiscoveryExcluded = GeneratedColumn( + 'user_discovery_excluded', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (user_discovery_excluded IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn userDiscoveryManualApproved = + GeneratedColumn( + 'user_discovery_manual_approved', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NULL DEFAULT 0 CHECK (user_discovery_manual_approved IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn recoveryIsTrustedFriend = + GeneratedColumn( + 'recovery_is_trusted_friend', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (recovery_is_trusted_friend IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn recoveryLastHeartbeat = GeneratedColumn( + 'recovery_last_heartbeat', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn recoverySecretShare = + GeneratedColumn( + 'recovery_secret_share', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn recoveryContactsSecretShare = + GeneratedColumn( + 'recovery_contacts_secret_share', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn recoveryContactsLastHeartbeat = + GeneratedColumn( + 'recovery_contacts_last_heartbeat', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn askForFriendPromotions = GeneratedColumn( + 'ask_for_friend_promotions', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL CHECK (ask_for_friend_promotions IN (0, 1))', + ); + late final GeneratedColumn mediaSendCounter = GeneratedColumn( + 'media_send_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn mediaReceivedCounter = GeneratedColumn( + 'media_received_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + userId, + username, + displayName, + nickName, + avatarSvgCompressed, + senderProfileCounter, + accepted, + deletedByUser, + requested, + blocked, + verified, + accountDeleted, + createdAt, + userDiscoveryVersion, + userDiscoveryExcluded, + userDiscoveryManualApproved, + recoveryIsTrustedFriend, + recoveryLastHeartbeat, + recoverySecretShare, + recoveryContactsSecretShare, + recoveryContactsLastHeartbeat, + askForFriendPromotions, + mediaSendCounter, + mediaReceivedCounter, + ]; + @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'], + ), + avatarSvgCompressed: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}avatar_svg_compressed'], + ), + senderProfileCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}sender_profile_counter'], + )!, + accepted: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}accepted'], + )!, + deletedByUser: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}deleted_by_user'], + )!, + requested: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}requested'], + )!, + blocked: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}blocked'], + )!, + verified: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}verified'], + )!, + accountDeleted: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}account_deleted'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + userDiscoveryVersion: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}user_discovery_version'], + ), + userDiscoveryExcluded: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}user_discovery_excluded'], + )!, + userDiscoveryManualApproved: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}user_discovery_manual_approved'], + ), + recoveryIsTrustedFriend: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_is_trusted_friend'], + )!, + recoveryLastHeartbeat: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_last_heartbeat'], + ), + recoverySecretShare: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}recovery_secret_share'], + ), + recoveryContactsSecretShare: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}recovery_contacts_secret_share'], + ), + recoveryContactsLastHeartbeat: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_contacts_last_heartbeat'], + ), + askForFriendPromotions: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ask_for_friend_promotions'], + ), + mediaSendCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_send_counter'], + )!, + mediaReceivedCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_received_counter'], + )!, + ); + } + + @override + Contacts createAlias(String alias) { + return Contacts(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(user_id)']; + @override + bool get dontWriteConstraints => true; +} + +class ContactsData extends DataClass implements Insertable { + final int userId; + final String username; + final String? displayName; + final String? nickName; + final i2.Uint8List? avatarSvgCompressed; + final int senderProfileCounter; + final int accepted; + final int deletedByUser; + final int requested; + final int blocked; + final int verified; + final int accountDeleted; + final int createdAt; + final i2.Uint8List? userDiscoveryVersion; + final int userDiscoveryExcluded; + final int? userDiscoveryManualApproved; + final int recoveryIsTrustedFriend; + final int? recoveryLastHeartbeat; + final i2.Uint8List? recoverySecretShare; + final i2.Uint8List? recoveryContactsSecretShare; + final int? recoveryContactsLastHeartbeat; + final int? askForFriendPromotions; + final int mediaSendCounter; + final int mediaReceivedCounter; + const ContactsData({ + required this.userId, + required this.username, + this.displayName, + this.nickName, + this.avatarSvgCompressed, + required this.senderProfileCounter, + required this.accepted, + required this.deletedByUser, + required this.requested, + required this.blocked, + required this.verified, + required this.accountDeleted, + required this.createdAt, + this.userDiscoveryVersion, + required this.userDiscoveryExcluded, + this.userDiscoveryManualApproved, + required this.recoveryIsTrustedFriend, + this.recoveryLastHeartbeat, + this.recoverySecretShare, + this.recoveryContactsSecretShare, + this.recoveryContactsLastHeartbeat, + this.askForFriendPromotions, + required this.mediaSendCounter, + required this.mediaReceivedCounter, + }); + @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 || avatarSvgCompressed != null) { + map['avatar_svg_compressed'] = Variable( + avatarSvgCompressed, + ); + } + map['sender_profile_counter'] = Variable(senderProfileCounter); + map['accepted'] = Variable(accepted); + map['deleted_by_user'] = Variable(deletedByUser); + map['requested'] = Variable(requested); + map['blocked'] = Variable(blocked); + map['verified'] = Variable(verified); + map['account_deleted'] = Variable(accountDeleted); + map['created_at'] = Variable(createdAt); + if (!nullToAbsent || userDiscoveryVersion != null) { + map['user_discovery_version'] = Variable( + userDiscoveryVersion, + ); + } + map['user_discovery_excluded'] = Variable(userDiscoveryExcluded); + if (!nullToAbsent || userDiscoveryManualApproved != null) { + map['user_discovery_manual_approved'] = Variable( + userDiscoveryManualApproved, + ); + } + map['recovery_is_trusted_friend'] = Variable(recoveryIsTrustedFriend); + if (!nullToAbsent || recoveryLastHeartbeat != null) { + map['recovery_last_heartbeat'] = Variable(recoveryLastHeartbeat); + } + if (!nullToAbsent || recoverySecretShare != null) { + map['recovery_secret_share'] = Variable( + recoverySecretShare, + ); + } + if (!nullToAbsent || recoveryContactsSecretShare != null) { + map['recovery_contacts_secret_share'] = Variable( + recoveryContactsSecretShare, + ); + } + if (!nullToAbsent || recoveryContactsLastHeartbeat != null) { + map['recovery_contacts_last_heartbeat'] = Variable( + recoveryContactsLastHeartbeat, + ); + } + if (!nullToAbsent || askForFriendPromotions != null) { + map['ask_for_friend_promotions'] = Variable(askForFriendPromotions); + } + map['media_send_counter'] = Variable(mediaSendCounter); + map['media_received_counter'] = Variable(mediaReceivedCounter); + 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), + avatarSvgCompressed: avatarSvgCompressed == null && nullToAbsent + ? const Value.absent() + : Value(avatarSvgCompressed), + senderProfileCounter: Value(senderProfileCounter), + accepted: Value(accepted), + deletedByUser: Value(deletedByUser), + requested: Value(requested), + blocked: Value(blocked), + verified: Value(verified), + accountDeleted: Value(accountDeleted), + createdAt: Value(createdAt), + userDiscoveryVersion: userDiscoveryVersion == null && nullToAbsent + ? const Value.absent() + : Value(userDiscoveryVersion), + userDiscoveryExcluded: Value(userDiscoveryExcluded), + userDiscoveryManualApproved: + userDiscoveryManualApproved == null && nullToAbsent + ? const Value.absent() + : Value(userDiscoveryManualApproved), + recoveryIsTrustedFriend: Value(recoveryIsTrustedFriend), + recoveryLastHeartbeat: recoveryLastHeartbeat == null && nullToAbsent + ? const Value.absent() + : Value(recoveryLastHeartbeat), + recoverySecretShare: recoverySecretShare == null && nullToAbsent + ? const Value.absent() + : Value(recoverySecretShare), + recoveryContactsSecretShare: + recoveryContactsSecretShare == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsSecretShare), + recoveryContactsLastHeartbeat: + recoveryContactsLastHeartbeat == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsLastHeartbeat), + askForFriendPromotions: askForFriendPromotions == null && nullToAbsent + ? const Value.absent() + : Value(askForFriendPromotions), + mediaSendCounter: Value(mediaSendCounter), + mediaReceivedCounter: Value(mediaReceivedCounter), + ); + } + + 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']), + avatarSvgCompressed: serializer.fromJson( + json['avatarSvgCompressed'], + ), + senderProfileCounter: serializer.fromJson( + json['senderProfileCounter'], + ), + accepted: serializer.fromJson(json['accepted']), + deletedByUser: serializer.fromJson(json['deletedByUser']), + requested: serializer.fromJson(json['requested']), + blocked: serializer.fromJson(json['blocked']), + verified: serializer.fromJson(json['verified']), + accountDeleted: serializer.fromJson(json['accountDeleted']), + createdAt: serializer.fromJson(json['createdAt']), + userDiscoveryVersion: serializer.fromJson( + json['userDiscoveryVersion'], + ), + userDiscoveryExcluded: serializer.fromJson( + json['userDiscoveryExcluded'], + ), + userDiscoveryManualApproved: serializer.fromJson( + json['userDiscoveryManualApproved'], + ), + recoveryIsTrustedFriend: serializer.fromJson( + json['recoveryIsTrustedFriend'], + ), + recoveryLastHeartbeat: serializer.fromJson( + json['recoveryLastHeartbeat'], + ), + recoverySecretShare: serializer.fromJson( + json['recoverySecretShare'], + ), + recoveryContactsSecretShare: serializer.fromJson( + json['recoveryContactsSecretShare'], + ), + recoveryContactsLastHeartbeat: serializer.fromJson( + json['recoveryContactsLastHeartbeat'], + ), + askForFriendPromotions: serializer.fromJson( + json['askForFriendPromotions'], + ), + mediaSendCounter: serializer.fromJson(json['mediaSendCounter']), + mediaReceivedCounter: serializer.fromJson( + json['mediaReceivedCounter'], + ), + ); + } + @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), + 'avatarSvgCompressed': serializer.toJson( + avatarSvgCompressed, + ), + 'senderProfileCounter': serializer.toJson(senderProfileCounter), + 'accepted': serializer.toJson(accepted), + 'deletedByUser': serializer.toJson(deletedByUser), + 'requested': serializer.toJson(requested), + 'blocked': serializer.toJson(blocked), + 'verified': serializer.toJson(verified), + 'accountDeleted': serializer.toJson(accountDeleted), + 'createdAt': serializer.toJson(createdAt), + 'userDiscoveryVersion': serializer.toJson( + userDiscoveryVersion, + ), + 'userDiscoveryExcluded': serializer.toJson(userDiscoveryExcluded), + 'userDiscoveryManualApproved': serializer.toJson( + userDiscoveryManualApproved, + ), + 'recoveryIsTrustedFriend': serializer.toJson( + recoveryIsTrustedFriend, + ), + 'recoveryLastHeartbeat': serializer.toJson(recoveryLastHeartbeat), + 'recoverySecretShare': serializer.toJson( + recoverySecretShare, + ), + 'recoveryContactsSecretShare': serializer.toJson( + recoveryContactsSecretShare, + ), + 'recoveryContactsLastHeartbeat': serializer.toJson( + recoveryContactsLastHeartbeat, + ), + 'askForFriendPromotions': serializer.toJson(askForFriendPromotions), + 'mediaSendCounter': serializer.toJson(mediaSendCounter), + 'mediaReceivedCounter': serializer.toJson(mediaReceivedCounter), + }; + } + + ContactsData copyWith({ + int? userId, + String? username, + Value displayName = const Value.absent(), + Value nickName = const Value.absent(), + Value avatarSvgCompressed = const Value.absent(), + int? senderProfileCounter, + int? accepted, + int? deletedByUser, + int? requested, + int? blocked, + int? verified, + int? accountDeleted, + int? createdAt, + Value userDiscoveryVersion = const Value.absent(), + int? userDiscoveryExcluded, + Value userDiscoveryManualApproved = const Value.absent(), + int? recoveryIsTrustedFriend, + Value recoveryLastHeartbeat = const Value.absent(), + Value recoverySecretShare = const Value.absent(), + Value recoveryContactsSecretShare = const Value.absent(), + Value recoveryContactsLastHeartbeat = const Value.absent(), + Value askForFriendPromotions = const Value.absent(), + int? mediaSendCounter, + int? mediaReceivedCounter, + }) => ContactsData( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName.present ? displayName.value : this.displayName, + nickName: nickName.present ? nickName.value : this.nickName, + avatarSvgCompressed: avatarSvgCompressed.present + ? avatarSvgCompressed.value + : this.avatarSvgCompressed, + senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter, + accepted: accepted ?? this.accepted, + deletedByUser: deletedByUser ?? this.deletedByUser, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + accountDeleted: accountDeleted ?? this.accountDeleted, + createdAt: createdAt ?? this.createdAt, + userDiscoveryVersion: userDiscoveryVersion.present + ? userDiscoveryVersion.value + : this.userDiscoveryVersion, + userDiscoveryExcluded: userDiscoveryExcluded ?? this.userDiscoveryExcluded, + userDiscoveryManualApproved: userDiscoveryManualApproved.present + ? userDiscoveryManualApproved.value + : this.userDiscoveryManualApproved, + recoveryIsTrustedFriend: + recoveryIsTrustedFriend ?? this.recoveryIsTrustedFriend, + recoveryLastHeartbeat: recoveryLastHeartbeat.present + ? recoveryLastHeartbeat.value + : this.recoveryLastHeartbeat, + recoverySecretShare: recoverySecretShare.present + ? recoverySecretShare.value + : this.recoverySecretShare, + recoveryContactsSecretShare: recoveryContactsSecretShare.present + ? recoveryContactsSecretShare.value + : this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat.present + ? recoveryContactsLastHeartbeat.value + : this.recoveryContactsLastHeartbeat, + askForFriendPromotions: askForFriendPromotions.present + ? askForFriendPromotions.value + : this.askForFriendPromotions, + mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter, + mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter, + ); + 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, + avatarSvgCompressed: data.avatarSvgCompressed.present + ? data.avatarSvgCompressed.value + : this.avatarSvgCompressed, + senderProfileCounter: data.senderProfileCounter.present + ? data.senderProfileCounter.value + : this.senderProfileCounter, + accepted: data.accepted.present ? data.accepted.value : this.accepted, + deletedByUser: data.deletedByUser.present + ? data.deletedByUser.value + : this.deletedByUser, + 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, + accountDeleted: data.accountDeleted.present + ? data.accountDeleted.value + : this.accountDeleted, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + userDiscoveryVersion: data.userDiscoveryVersion.present + ? data.userDiscoveryVersion.value + : this.userDiscoveryVersion, + userDiscoveryExcluded: data.userDiscoveryExcluded.present + ? data.userDiscoveryExcluded.value + : this.userDiscoveryExcluded, + userDiscoveryManualApproved: data.userDiscoveryManualApproved.present + ? data.userDiscoveryManualApproved.value + : this.userDiscoveryManualApproved, + recoveryIsTrustedFriend: data.recoveryIsTrustedFriend.present + ? data.recoveryIsTrustedFriend.value + : this.recoveryIsTrustedFriend, + recoveryLastHeartbeat: data.recoveryLastHeartbeat.present + ? data.recoveryLastHeartbeat.value + : this.recoveryLastHeartbeat, + recoverySecretShare: data.recoverySecretShare.present + ? data.recoverySecretShare.value + : this.recoverySecretShare, + recoveryContactsSecretShare: data.recoveryContactsSecretShare.present + ? data.recoveryContactsSecretShare.value + : this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: data.recoveryContactsLastHeartbeat.present + ? data.recoveryContactsLastHeartbeat.value + : this.recoveryContactsLastHeartbeat, + askForFriendPromotions: data.askForFriendPromotions.present + ? data.askForFriendPromotions.value + : this.askForFriendPromotions, + mediaSendCounter: data.mediaSendCounter.present + ? data.mediaSendCounter.value + : this.mediaSendCounter, + mediaReceivedCounter: data.mediaReceivedCounter.present + ? data.mediaReceivedCounter.value + : this.mediaReceivedCounter, + ); + } + + @override + String toString() { + return (StringBuffer('ContactsData(') + ..write('userId: $userId, ') + ..write('username: $username, ') + ..write('displayName: $displayName, ') + ..write('nickName: $nickName, ') + ..write('avatarSvgCompressed: $avatarSvgCompressed, ') + ..write('senderProfileCounter: $senderProfileCounter, ') + ..write('accepted: $accepted, ') + ..write('deletedByUser: $deletedByUser, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('accountDeleted: $accountDeleted, ') + ..write('createdAt: $createdAt, ') + ..write('userDiscoveryVersion: $userDiscoveryVersion, ') + ..write('userDiscoveryExcluded: $userDiscoveryExcluded, ') + ..write('userDiscoveryManualApproved: $userDiscoveryManualApproved, ') + ..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ') + ..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ') + ..write('recoverySecretShare: $recoverySecretShare, ') + ..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ') + ..write( + 'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ', + ) + ..write('askForFriendPromotions: $askForFriendPromotions, ') + ..write('mediaSendCounter: $mediaSendCounter, ') + ..write('mediaReceivedCounter: $mediaReceivedCounter') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + userId, + username, + displayName, + nickName, + $driftBlobEquality.hash(avatarSvgCompressed), + senderProfileCounter, + accepted, + deletedByUser, + requested, + blocked, + verified, + accountDeleted, + createdAt, + $driftBlobEquality.hash(userDiscoveryVersion), + userDiscoveryExcluded, + userDiscoveryManualApproved, + recoveryIsTrustedFriend, + recoveryLastHeartbeat, + $driftBlobEquality.hash(recoverySecretShare), + $driftBlobEquality.hash(recoveryContactsSecretShare), + recoveryContactsLastHeartbeat, + askForFriendPromotions, + mediaSendCounter, + mediaReceivedCounter, + ]); + @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 && + $driftBlobEquality.equals( + other.avatarSvgCompressed, + this.avatarSvgCompressed, + ) && + other.senderProfileCounter == this.senderProfileCounter && + other.accepted == this.accepted && + other.deletedByUser == this.deletedByUser && + other.requested == this.requested && + other.blocked == this.blocked && + other.verified == this.verified && + other.accountDeleted == this.accountDeleted && + other.createdAt == this.createdAt && + $driftBlobEquality.equals( + other.userDiscoveryVersion, + this.userDiscoveryVersion, + ) && + other.userDiscoveryExcluded == this.userDiscoveryExcluded && + other.userDiscoveryManualApproved == + this.userDiscoveryManualApproved && + other.recoveryIsTrustedFriend == this.recoveryIsTrustedFriend && + other.recoveryLastHeartbeat == this.recoveryLastHeartbeat && + $driftBlobEquality.equals( + other.recoverySecretShare, + this.recoverySecretShare, + ) && + $driftBlobEquality.equals( + other.recoveryContactsSecretShare, + this.recoveryContactsSecretShare, + ) && + other.recoveryContactsLastHeartbeat == + this.recoveryContactsLastHeartbeat && + other.askForFriendPromotions == this.askForFriendPromotions && + other.mediaSendCounter == this.mediaSendCounter && + other.mediaReceivedCounter == this.mediaReceivedCounter); +} + +class ContactsCompanion extends UpdateCompanion { + final Value userId; + final Value username; + final Value displayName; + final Value nickName; + final Value avatarSvgCompressed; + final Value senderProfileCounter; + final Value accepted; + final Value deletedByUser; + final Value requested; + final Value blocked; + final Value verified; + final Value accountDeleted; + final Value createdAt; + final Value userDiscoveryVersion; + final Value userDiscoveryExcluded; + final Value userDiscoveryManualApproved; + final Value recoveryIsTrustedFriend; + final Value recoveryLastHeartbeat; + final Value recoverySecretShare; + final Value recoveryContactsSecretShare; + final Value recoveryContactsLastHeartbeat; + final Value askForFriendPromotions; + final Value mediaSendCounter; + final Value mediaReceivedCounter; + const ContactsCompanion({ + this.userId = const Value.absent(), + this.username = const Value.absent(), + this.displayName = const Value.absent(), + this.nickName = const Value.absent(), + this.avatarSvgCompressed = const Value.absent(), + this.senderProfileCounter = const Value.absent(), + this.accepted = const Value.absent(), + this.deletedByUser = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.accountDeleted = const Value.absent(), + this.createdAt = const Value.absent(), + this.userDiscoveryVersion = const Value.absent(), + this.userDiscoveryExcluded = const Value.absent(), + this.userDiscoveryManualApproved = const Value.absent(), + this.recoveryIsTrustedFriend = const Value.absent(), + this.recoveryLastHeartbeat = const Value.absent(), + this.recoverySecretShare = const Value.absent(), + this.recoveryContactsSecretShare = const Value.absent(), + this.recoveryContactsLastHeartbeat = const Value.absent(), + this.askForFriendPromotions = const Value.absent(), + this.mediaSendCounter = const Value.absent(), + this.mediaReceivedCounter = const Value.absent(), + }); + ContactsCompanion.insert({ + this.userId = const Value.absent(), + required String username, + this.displayName = const Value.absent(), + this.nickName = const Value.absent(), + this.avatarSvgCompressed = const Value.absent(), + this.senderProfileCounter = const Value.absent(), + this.accepted = const Value.absent(), + this.deletedByUser = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.accountDeleted = const Value.absent(), + this.createdAt = const Value.absent(), + this.userDiscoveryVersion = const Value.absent(), + this.userDiscoveryExcluded = const Value.absent(), + this.userDiscoveryManualApproved = const Value.absent(), + this.recoveryIsTrustedFriend = const Value.absent(), + this.recoveryLastHeartbeat = const Value.absent(), + this.recoverySecretShare = const Value.absent(), + this.recoveryContactsSecretShare = const Value.absent(), + this.recoveryContactsLastHeartbeat = const Value.absent(), + this.askForFriendPromotions = const Value.absent(), + this.mediaSendCounter = const Value.absent(), + this.mediaReceivedCounter = const Value.absent(), + }) : username = Value(username); + static Insertable custom({ + Expression? userId, + Expression? username, + Expression? displayName, + Expression? nickName, + Expression? avatarSvgCompressed, + Expression? senderProfileCounter, + Expression? accepted, + Expression? deletedByUser, + Expression? requested, + Expression? blocked, + Expression? verified, + Expression? accountDeleted, + Expression? createdAt, + Expression? userDiscoveryVersion, + Expression? userDiscoveryExcluded, + Expression? userDiscoveryManualApproved, + Expression? recoveryIsTrustedFriend, + Expression? recoveryLastHeartbeat, + Expression? recoverySecretShare, + Expression? recoveryContactsSecretShare, + Expression? recoveryContactsLastHeartbeat, + Expression? askForFriendPromotions, + Expression? mediaSendCounter, + Expression? mediaReceivedCounter, + }) { + 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 (avatarSvgCompressed != null) + 'avatar_svg_compressed': avatarSvgCompressed, + if (senderProfileCounter != null) + 'sender_profile_counter': senderProfileCounter, + if (accepted != null) 'accepted': accepted, + if (deletedByUser != null) 'deleted_by_user': deletedByUser, + if (requested != null) 'requested': requested, + if (blocked != null) 'blocked': blocked, + if (verified != null) 'verified': verified, + if (accountDeleted != null) 'account_deleted': accountDeleted, + if (createdAt != null) 'created_at': createdAt, + if (userDiscoveryVersion != null) + 'user_discovery_version': userDiscoveryVersion, + if (userDiscoveryExcluded != null) + 'user_discovery_excluded': userDiscoveryExcluded, + if (userDiscoveryManualApproved != null) + 'user_discovery_manual_approved': userDiscoveryManualApproved, + if (recoveryIsTrustedFriend != null) + 'recovery_is_trusted_friend': recoveryIsTrustedFriend, + if (recoveryLastHeartbeat != null) + 'recovery_last_heartbeat': recoveryLastHeartbeat, + if (recoverySecretShare != null) + 'recovery_secret_share': recoverySecretShare, + if (recoveryContactsSecretShare != null) + 'recovery_contacts_secret_share': recoveryContactsSecretShare, + if (recoveryContactsLastHeartbeat != null) + 'recovery_contacts_last_heartbeat': recoveryContactsLastHeartbeat, + if (askForFriendPromotions != null) + 'ask_for_friend_promotions': askForFriendPromotions, + if (mediaSendCounter != null) 'media_send_counter': mediaSendCounter, + if (mediaReceivedCounter != null) + 'media_received_counter': mediaReceivedCounter, + }); + } + + ContactsCompanion copyWith({ + Value? userId, + Value? username, + Value? displayName, + Value? nickName, + Value? avatarSvgCompressed, + Value? senderProfileCounter, + Value? accepted, + Value? deletedByUser, + Value? requested, + Value? blocked, + Value? verified, + Value? accountDeleted, + Value? createdAt, + Value? userDiscoveryVersion, + Value? userDiscoveryExcluded, + Value? userDiscoveryManualApproved, + Value? recoveryIsTrustedFriend, + Value? recoveryLastHeartbeat, + Value? recoverySecretShare, + Value? recoveryContactsSecretShare, + Value? recoveryContactsLastHeartbeat, + Value? askForFriendPromotions, + Value? mediaSendCounter, + Value? mediaReceivedCounter, + }) { + return ContactsCompanion( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName ?? this.displayName, + nickName: nickName ?? this.nickName, + avatarSvgCompressed: avatarSvgCompressed ?? this.avatarSvgCompressed, + senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter, + accepted: accepted ?? this.accepted, + deletedByUser: deletedByUser ?? this.deletedByUser, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + accountDeleted: accountDeleted ?? this.accountDeleted, + createdAt: createdAt ?? this.createdAt, + userDiscoveryVersion: userDiscoveryVersion ?? this.userDiscoveryVersion, + userDiscoveryExcluded: + userDiscoveryExcluded ?? this.userDiscoveryExcluded, + userDiscoveryManualApproved: + userDiscoveryManualApproved ?? this.userDiscoveryManualApproved, + recoveryIsTrustedFriend: + recoveryIsTrustedFriend ?? this.recoveryIsTrustedFriend, + recoveryLastHeartbeat: + recoveryLastHeartbeat ?? this.recoveryLastHeartbeat, + recoverySecretShare: recoverySecretShare ?? this.recoverySecretShare, + recoveryContactsSecretShare: + recoveryContactsSecretShare ?? this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: + recoveryContactsLastHeartbeat ?? this.recoveryContactsLastHeartbeat, + askForFriendPromotions: + askForFriendPromotions ?? this.askForFriendPromotions, + mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter, + mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter, + ); + } + + @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 (avatarSvgCompressed.present) { + map['avatar_svg_compressed'] = Variable( + avatarSvgCompressed.value, + ); + } + if (senderProfileCounter.present) { + map['sender_profile_counter'] = Variable(senderProfileCounter.value); + } + if (accepted.present) { + map['accepted'] = Variable(accepted.value); + } + if (deletedByUser.present) { + map['deleted_by_user'] = Variable(deletedByUser.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 (accountDeleted.present) { + map['account_deleted'] = Variable(accountDeleted.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (userDiscoveryVersion.present) { + map['user_discovery_version'] = Variable( + userDiscoveryVersion.value, + ); + } + if (userDiscoveryExcluded.present) { + map['user_discovery_excluded'] = Variable( + userDiscoveryExcluded.value, + ); + } + if (userDiscoveryManualApproved.present) { + map['user_discovery_manual_approved'] = Variable( + userDiscoveryManualApproved.value, + ); + } + if (recoveryIsTrustedFriend.present) { + map['recovery_is_trusted_friend'] = Variable( + recoveryIsTrustedFriend.value, + ); + } + if (recoveryLastHeartbeat.present) { + map['recovery_last_heartbeat'] = Variable( + recoveryLastHeartbeat.value, + ); + } + if (recoverySecretShare.present) { + map['recovery_secret_share'] = Variable( + recoverySecretShare.value, + ); + } + if (recoveryContactsSecretShare.present) { + map['recovery_contacts_secret_share'] = Variable( + recoveryContactsSecretShare.value, + ); + } + if (recoveryContactsLastHeartbeat.present) { + map['recovery_contacts_last_heartbeat'] = Variable( + recoveryContactsLastHeartbeat.value, + ); + } + if (askForFriendPromotions.present) { + map['ask_for_friend_promotions'] = Variable( + askForFriendPromotions.value, + ); + } + if (mediaSendCounter.present) { + map['media_send_counter'] = Variable(mediaSendCounter.value); + } + if (mediaReceivedCounter.present) { + map['media_received_counter'] = Variable(mediaReceivedCounter.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ContactsCompanion(') + ..write('userId: $userId, ') + ..write('username: $username, ') + ..write('displayName: $displayName, ') + ..write('nickName: $nickName, ') + ..write('avatarSvgCompressed: $avatarSvgCompressed, ') + ..write('senderProfileCounter: $senderProfileCounter, ') + ..write('accepted: $accepted, ') + ..write('deletedByUser: $deletedByUser, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('accountDeleted: $accountDeleted, ') + ..write('createdAt: $createdAt, ') + ..write('userDiscoveryVersion: $userDiscoveryVersion, ') + ..write('userDiscoveryExcluded: $userDiscoveryExcluded, ') + ..write('userDiscoveryManualApproved: $userDiscoveryManualApproved, ') + ..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ') + ..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ') + ..write('recoverySecretShare: $recoverySecretShare, ') + ..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ') + ..write( + 'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ', + ) + ..write('askForFriendPromotions: $askForFriendPromotions, ') + ..write('mediaSendCounter: $mediaSendCounter, ') + ..write('mediaReceivedCounter: $mediaReceivedCounter') + ..write(')')) + .toString(); + } +} + +class Groups extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Groups(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn isGroupAdmin = GeneratedColumn( + 'is_group_admin', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_group_admin IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isDirectChat = GeneratedColumn( + 'is_direct_chat', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_direct_chat IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn pinned = GeneratedColumn( + 'pinned', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (pinned IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn archived = GeneratedColumn( + 'archived', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (archived IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn joinedGroup = GeneratedColumn( + 'joined_group', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (joined_group IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn leftGroup = GeneratedColumn( + 'left_group', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (left_group IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn deletedContent = GeneratedColumn( + 'deleted_content', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (deleted_content IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn stateVersionId = GeneratedColumn( + 'state_version_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn stateEncryptionKey = + GeneratedColumn( + 'state_encryption_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn myGroupPrivateKey = + GeneratedColumn( + 'my_group_private_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn groupName = GeneratedColumn( + 'group_name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn draftMessage = GeneratedColumn( + 'draft_message', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn totalMediaCounter = GeneratedColumn( + 'total_media_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn alsoBestFriend = GeneratedColumn( + 'also_best_friend', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (also_best_friend IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn deleteMessagesAfterMilliseconds = + GeneratedColumn( + 'delete_messages_after_milliseconds', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 86400000', + defaultValue: const CustomExpression('86400000'), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn lastMessageSend = GeneratedColumn( + 'last_message_send', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastMessageReceived = GeneratedColumn( + 'last_message_received', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastFlameCounterChange = GeneratedColumn( + 'last_flame_counter_change', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastFlameSync = GeneratedColumn( + 'last_flame_sync', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn flameCounter = GeneratedColumn( + 'flame_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn maxFlameCounter = GeneratedColumn( + 'max_flame_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn maxFlameCounterFrom = GeneratedColumn( + 'max_flame_counter_from', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastMessageExchange = GeneratedColumn( + 'last_message_exchange', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + groupId, + isGroupAdmin, + isDirectChat, + pinned, + archived, + joinedGroup, + leftGroup, + deletedContent, + stateVersionId, + stateEncryptionKey, + myGroupPrivateKey, + groupName, + draftMessage, + totalMediaCounter, + alsoBestFriend, + deleteMessagesAfterMilliseconds, + createdAt, + lastMessageSend, + lastMessageReceived, + lastFlameCounterChange, + lastFlameSync, + flameCounter, + maxFlameCounter, + maxFlameCounterFrom, + lastMessageExchange, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'groups'; + @override + Set get $primaryKey => {groupId}; + @override + GroupsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GroupsData( + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + isGroupAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_group_admin'], + )!, + isDirectChat: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_direct_chat'], + )!, + pinned: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}pinned'], + )!, + archived: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}archived'], + )!, + joinedGroup: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}joined_group'], + )!, + leftGroup: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}left_group'], + )!, + deletedContent: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}deleted_content'], + )!, + stateVersionId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}state_version_id'], + )!, + stateEncryptionKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}state_encryption_key'], + ), + myGroupPrivateKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}my_group_private_key'], + ), + groupName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_name'], + )!, + draftMessage: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}draft_message'], + ), + totalMediaCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}total_media_counter'], + )!, + alsoBestFriend: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}also_best_friend'], + )!, + deleteMessagesAfterMilliseconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}delete_messages_after_milliseconds'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + lastMessageSend: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message_send'], + ), + lastMessageReceived: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message_received'], + ), + lastFlameCounterChange: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_flame_counter_change'], + ), + lastFlameSync: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_flame_sync'], + ), + flameCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}flame_counter'], + )!, + maxFlameCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}max_flame_counter'], + )!, + maxFlameCounterFrom: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}max_flame_counter_from'], + ), + lastMessageExchange: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message_exchange'], + )!, + ); + } + + @override + Groups createAlias(String alias) { + return Groups(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(group_id)']; + @override + bool get dontWriteConstraints => true; +} + +class GroupsData extends DataClass implements Insertable { + final String groupId; + final int isGroupAdmin; + final int isDirectChat; + final int pinned; + final int archived; + final int joinedGroup; + final int leftGroup; + final int deletedContent; + final int stateVersionId; + final i2.Uint8List? stateEncryptionKey; + final i2.Uint8List? myGroupPrivateKey; + final String groupName; + final String? draftMessage; + final int totalMediaCounter; + final int alsoBestFriend; + final int deleteMessagesAfterMilliseconds; + final int createdAt; + final int? lastMessageSend; + final int? lastMessageReceived; + final int? lastFlameCounterChange; + final int? lastFlameSync; + final int flameCounter; + final int maxFlameCounter; + final int? maxFlameCounterFrom; + final int lastMessageExchange; + const GroupsData({ + required this.groupId, + required this.isGroupAdmin, + required this.isDirectChat, + required this.pinned, + required this.archived, + required this.joinedGroup, + required this.leftGroup, + required this.deletedContent, + required this.stateVersionId, + this.stateEncryptionKey, + this.myGroupPrivateKey, + required this.groupName, + this.draftMessage, + required this.totalMediaCounter, + required this.alsoBestFriend, + required this.deleteMessagesAfterMilliseconds, + required this.createdAt, + this.lastMessageSend, + this.lastMessageReceived, + this.lastFlameCounterChange, + this.lastFlameSync, + required this.flameCounter, + required this.maxFlameCounter, + this.maxFlameCounterFrom, + required this.lastMessageExchange, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_id'] = Variable(groupId); + map['is_group_admin'] = Variable(isGroupAdmin); + map['is_direct_chat'] = Variable(isDirectChat); + map['pinned'] = Variable(pinned); + map['archived'] = Variable(archived); + map['joined_group'] = Variable(joinedGroup); + map['left_group'] = Variable(leftGroup); + map['deleted_content'] = Variable(deletedContent); + map['state_version_id'] = Variable(stateVersionId); + if (!nullToAbsent || stateEncryptionKey != null) { + map['state_encryption_key'] = Variable(stateEncryptionKey); + } + if (!nullToAbsent || myGroupPrivateKey != null) { + map['my_group_private_key'] = Variable(myGroupPrivateKey); + } + map['group_name'] = Variable(groupName); + if (!nullToAbsent || draftMessage != null) { + map['draft_message'] = Variable(draftMessage); + } + map['total_media_counter'] = Variable(totalMediaCounter); + map['also_best_friend'] = Variable(alsoBestFriend); + map['delete_messages_after_milliseconds'] = Variable( + deleteMessagesAfterMilliseconds, + ); + map['created_at'] = Variable(createdAt); + 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['flame_counter'] = Variable(flameCounter); + map['max_flame_counter'] = Variable(maxFlameCounter); + if (!nullToAbsent || maxFlameCounterFrom != null) { + map['max_flame_counter_from'] = Variable(maxFlameCounterFrom); + } + map['last_message_exchange'] = Variable(lastMessageExchange); + return map; + } + + GroupsCompanion toCompanion(bool nullToAbsent) { + return GroupsCompanion( + groupId: Value(groupId), + isGroupAdmin: Value(isGroupAdmin), + isDirectChat: Value(isDirectChat), + pinned: Value(pinned), + archived: Value(archived), + joinedGroup: Value(joinedGroup), + leftGroup: Value(leftGroup), + deletedContent: Value(deletedContent), + stateVersionId: Value(stateVersionId), + stateEncryptionKey: stateEncryptionKey == null && nullToAbsent + ? const Value.absent() + : Value(stateEncryptionKey), + myGroupPrivateKey: myGroupPrivateKey == null && nullToAbsent + ? const Value.absent() + : Value(myGroupPrivateKey), + groupName: Value(groupName), + draftMessage: draftMessage == null && nullToAbsent + ? const Value.absent() + : Value(draftMessage), + totalMediaCounter: Value(totalMediaCounter), + alsoBestFriend: Value(alsoBestFriend), + deleteMessagesAfterMilliseconds: Value(deleteMessagesAfterMilliseconds), + createdAt: Value(createdAt), + 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), + flameCounter: Value(flameCounter), + maxFlameCounter: Value(maxFlameCounter), + maxFlameCounterFrom: maxFlameCounterFrom == null && nullToAbsent + ? const Value.absent() + : Value(maxFlameCounterFrom), + lastMessageExchange: Value(lastMessageExchange), + ); + } + + factory GroupsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GroupsData( + groupId: serializer.fromJson(json['groupId']), + isGroupAdmin: serializer.fromJson(json['isGroupAdmin']), + isDirectChat: serializer.fromJson(json['isDirectChat']), + pinned: serializer.fromJson(json['pinned']), + archived: serializer.fromJson(json['archived']), + joinedGroup: serializer.fromJson(json['joinedGroup']), + leftGroup: serializer.fromJson(json['leftGroup']), + deletedContent: serializer.fromJson(json['deletedContent']), + stateVersionId: serializer.fromJson(json['stateVersionId']), + stateEncryptionKey: serializer.fromJson( + json['stateEncryptionKey'], + ), + myGroupPrivateKey: serializer.fromJson( + json['myGroupPrivateKey'], + ), + groupName: serializer.fromJson(json['groupName']), + draftMessage: serializer.fromJson(json['draftMessage']), + totalMediaCounter: serializer.fromJson(json['totalMediaCounter']), + alsoBestFriend: serializer.fromJson(json['alsoBestFriend']), + deleteMessagesAfterMilliseconds: serializer.fromJson( + json['deleteMessagesAfterMilliseconds'], + ), + createdAt: serializer.fromJson(json['createdAt']), + lastMessageSend: serializer.fromJson(json['lastMessageSend']), + lastMessageReceived: serializer.fromJson( + json['lastMessageReceived'], + ), + lastFlameCounterChange: serializer.fromJson( + json['lastFlameCounterChange'], + ), + lastFlameSync: serializer.fromJson(json['lastFlameSync']), + flameCounter: serializer.fromJson(json['flameCounter']), + maxFlameCounter: serializer.fromJson(json['maxFlameCounter']), + maxFlameCounterFrom: serializer.fromJson( + json['maxFlameCounterFrom'], + ), + lastMessageExchange: serializer.fromJson( + json['lastMessageExchange'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupId': serializer.toJson(groupId), + 'isGroupAdmin': serializer.toJson(isGroupAdmin), + 'isDirectChat': serializer.toJson(isDirectChat), + 'pinned': serializer.toJson(pinned), + 'archived': serializer.toJson(archived), + 'joinedGroup': serializer.toJson(joinedGroup), + 'leftGroup': serializer.toJson(leftGroup), + 'deletedContent': serializer.toJson(deletedContent), + 'stateVersionId': serializer.toJson(stateVersionId), + 'stateEncryptionKey': serializer.toJson( + stateEncryptionKey, + ), + 'myGroupPrivateKey': serializer.toJson(myGroupPrivateKey), + 'groupName': serializer.toJson(groupName), + 'draftMessage': serializer.toJson(draftMessage), + 'totalMediaCounter': serializer.toJson(totalMediaCounter), + 'alsoBestFriend': serializer.toJson(alsoBestFriend), + 'deleteMessagesAfterMilliseconds': serializer.toJson( + deleteMessagesAfterMilliseconds, + ), + 'createdAt': serializer.toJson(createdAt), + 'lastMessageSend': serializer.toJson(lastMessageSend), + 'lastMessageReceived': serializer.toJson(lastMessageReceived), + 'lastFlameCounterChange': serializer.toJson(lastFlameCounterChange), + 'lastFlameSync': serializer.toJson(lastFlameSync), + 'flameCounter': serializer.toJson(flameCounter), + 'maxFlameCounter': serializer.toJson(maxFlameCounter), + 'maxFlameCounterFrom': serializer.toJson(maxFlameCounterFrom), + 'lastMessageExchange': serializer.toJson(lastMessageExchange), + }; + } + + GroupsData copyWith({ + String? groupId, + int? isGroupAdmin, + int? isDirectChat, + int? pinned, + int? archived, + int? joinedGroup, + int? leftGroup, + int? deletedContent, + int? stateVersionId, + Value stateEncryptionKey = const Value.absent(), + Value myGroupPrivateKey = const Value.absent(), + String? groupName, + Value draftMessage = const Value.absent(), + int? totalMediaCounter, + int? alsoBestFriend, + int? deleteMessagesAfterMilliseconds, + int? createdAt, + Value lastMessageSend = const Value.absent(), + Value lastMessageReceived = const Value.absent(), + Value lastFlameCounterChange = const Value.absent(), + Value lastFlameSync = const Value.absent(), + int? flameCounter, + int? maxFlameCounter, + Value maxFlameCounterFrom = const Value.absent(), + int? lastMessageExchange, + }) => GroupsData( + groupId: groupId ?? this.groupId, + isGroupAdmin: isGroupAdmin ?? this.isGroupAdmin, + isDirectChat: isDirectChat ?? this.isDirectChat, + pinned: pinned ?? this.pinned, + archived: archived ?? this.archived, + joinedGroup: joinedGroup ?? this.joinedGroup, + leftGroup: leftGroup ?? this.leftGroup, + deletedContent: deletedContent ?? this.deletedContent, + stateVersionId: stateVersionId ?? this.stateVersionId, + stateEncryptionKey: stateEncryptionKey.present + ? stateEncryptionKey.value + : this.stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey.present + ? myGroupPrivateKey.value + : this.myGroupPrivateKey, + groupName: groupName ?? this.groupName, + draftMessage: draftMessage.present ? draftMessage.value : this.draftMessage, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, + deleteMessagesAfterMilliseconds: + deleteMessagesAfterMilliseconds ?? this.deleteMessagesAfterMilliseconds, + createdAt: createdAt ?? this.createdAt, + 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, + flameCounter: flameCounter ?? this.flameCounter, + maxFlameCounter: maxFlameCounter ?? this.maxFlameCounter, + maxFlameCounterFrom: maxFlameCounterFrom.present + ? maxFlameCounterFrom.value + : this.maxFlameCounterFrom, + lastMessageExchange: lastMessageExchange ?? this.lastMessageExchange, + ); + GroupsData copyWithCompanion(GroupsCompanion data) { + return GroupsData( + groupId: data.groupId.present ? data.groupId.value : this.groupId, + isGroupAdmin: data.isGroupAdmin.present + ? data.isGroupAdmin.value + : this.isGroupAdmin, + isDirectChat: data.isDirectChat.present + ? data.isDirectChat.value + : this.isDirectChat, + pinned: data.pinned.present ? data.pinned.value : this.pinned, + archived: data.archived.present ? data.archived.value : this.archived, + joinedGroup: data.joinedGroup.present + ? data.joinedGroup.value + : this.joinedGroup, + leftGroup: data.leftGroup.present ? data.leftGroup.value : this.leftGroup, + deletedContent: data.deletedContent.present + ? data.deletedContent.value + : this.deletedContent, + stateVersionId: data.stateVersionId.present + ? data.stateVersionId.value + : this.stateVersionId, + stateEncryptionKey: data.stateEncryptionKey.present + ? data.stateEncryptionKey.value + : this.stateEncryptionKey, + myGroupPrivateKey: data.myGroupPrivateKey.present + ? data.myGroupPrivateKey.value + : this.myGroupPrivateKey, + groupName: data.groupName.present ? data.groupName.value : this.groupName, + draftMessage: data.draftMessage.present + ? data.draftMessage.value + : this.draftMessage, + totalMediaCounter: data.totalMediaCounter.present + ? data.totalMediaCounter.value + : this.totalMediaCounter, + alsoBestFriend: data.alsoBestFriend.present + ? data.alsoBestFriend.value + : this.alsoBestFriend, + deleteMessagesAfterMilliseconds: + data.deleteMessagesAfterMilliseconds.present + ? data.deleteMessagesAfterMilliseconds.value + : this.deleteMessagesAfterMilliseconds, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + 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, + flameCounter: data.flameCounter.present + ? data.flameCounter.value + : this.flameCounter, + maxFlameCounter: data.maxFlameCounter.present + ? data.maxFlameCounter.value + : this.maxFlameCounter, + maxFlameCounterFrom: data.maxFlameCounterFrom.present + ? data.maxFlameCounterFrom.value + : this.maxFlameCounterFrom, + lastMessageExchange: data.lastMessageExchange.present + ? data.lastMessageExchange.value + : this.lastMessageExchange, + ); + } + + @override + String toString() { + return (StringBuffer('GroupsData(') + ..write('groupId: $groupId, ') + ..write('isGroupAdmin: $isGroupAdmin, ') + ..write('isDirectChat: $isDirectChat, ') + ..write('pinned: $pinned, ') + ..write('archived: $archived, ') + ..write('joinedGroup: $joinedGroup, ') + ..write('leftGroup: $leftGroup, ') + ..write('deletedContent: $deletedContent, ') + ..write('stateVersionId: $stateVersionId, ') + ..write('stateEncryptionKey: $stateEncryptionKey, ') + ..write('myGroupPrivateKey: $myGroupPrivateKey, ') + ..write('groupName: $groupName, ') + ..write('draftMessage: $draftMessage, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('alsoBestFriend: $alsoBestFriend, ') + ..write( + 'deleteMessagesAfterMilliseconds: $deleteMessagesAfterMilliseconds, ', + ) + ..write('createdAt: $createdAt, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastFlameCounterChange: $lastFlameCounterChange, ') + ..write('lastFlameSync: $lastFlameSync, ') + ..write('flameCounter: $flameCounter, ') + ..write('maxFlameCounter: $maxFlameCounter, ') + ..write('maxFlameCounterFrom: $maxFlameCounterFrom, ') + ..write('lastMessageExchange: $lastMessageExchange') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + groupId, + isGroupAdmin, + isDirectChat, + pinned, + archived, + joinedGroup, + leftGroup, + deletedContent, + stateVersionId, + $driftBlobEquality.hash(stateEncryptionKey), + $driftBlobEquality.hash(myGroupPrivateKey), + groupName, + draftMessage, + totalMediaCounter, + alsoBestFriend, + deleteMessagesAfterMilliseconds, + createdAt, + lastMessageSend, + lastMessageReceived, + lastFlameCounterChange, + lastFlameSync, + flameCounter, + maxFlameCounter, + maxFlameCounterFrom, + lastMessageExchange, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GroupsData && + other.groupId == this.groupId && + other.isGroupAdmin == this.isGroupAdmin && + other.isDirectChat == this.isDirectChat && + other.pinned == this.pinned && + other.archived == this.archived && + other.joinedGroup == this.joinedGroup && + other.leftGroup == this.leftGroup && + other.deletedContent == this.deletedContent && + other.stateVersionId == this.stateVersionId && + $driftBlobEquality.equals( + other.stateEncryptionKey, + this.stateEncryptionKey, + ) && + $driftBlobEquality.equals( + other.myGroupPrivateKey, + this.myGroupPrivateKey, + ) && + other.groupName == this.groupName && + other.draftMessage == this.draftMessage && + other.totalMediaCounter == this.totalMediaCounter && + other.alsoBestFriend == this.alsoBestFriend && + other.deleteMessagesAfterMilliseconds == + this.deleteMessagesAfterMilliseconds && + other.createdAt == this.createdAt && + other.lastMessageSend == this.lastMessageSend && + other.lastMessageReceived == this.lastMessageReceived && + other.lastFlameCounterChange == this.lastFlameCounterChange && + other.lastFlameSync == this.lastFlameSync && + other.flameCounter == this.flameCounter && + other.maxFlameCounter == this.maxFlameCounter && + other.maxFlameCounterFrom == this.maxFlameCounterFrom && + other.lastMessageExchange == this.lastMessageExchange); +} + +class GroupsCompanion extends UpdateCompanion { + final Value groupId; + final Value isGroupAdmin; + final Value isDirectChat; + final Value pinned; + final Value archived; + final Value joinedGroup; + final Value leftGroup; + final Value deletedContent; + final Value stateVersionId; + final Value stateEncryptionKey; + final Value myGroupPrivateKey; + final Value groupName; + final Value draftMessage; + final Value totalMediaCounter; + final Value alsoBestFriend; + final Value deleteMessagesAfterMilliseconds; + final Value createdAt; + final Value lastMessageSend; + final Value lastMessageReceived; + final Value lastFlameCounterChange; + final Value lastFlameSync; + final Value flameCounter; + final Value maxFlameCounter; + final Value maxFlameCounterFrom; + final Value lastMessageExchange; + final Value rowid; + const GroupsCompanion({ + this.groupId = const Value.absent(), + this.isGroupAdmin = const Value.absent(), + this.isDirectChat = const Value.absent(), + this.pinned = const Value.absent(), + this.archived = const Value.absent(), + this.joinedGroup = const Value.absent(), + this.leftGroup = const Value.absent(), + this.deletedContent = const Value.absent(), + this.stateVersionId = const Value.absent(), + this.stateEncryptionKey = const Value.absent(), + this.myGroupPrivateKey = const Value.absent(), + this.groupName = const Value.absent(), + this.draftMessage = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.alsoBestFriend = const Value.absent(), + this.deleteMessagesAfterMilliseconds = const Value.absent(), + this.createdAt = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastFlameCounterChange = const Value.absent(), + this.lastFlameSync = const Value.absent(), + this.flameCounter = const Value.absent(), + this.maxFlameCounter = const Value.absent(), + this.maxFlameCounterFrom = const Value.absent(), + this.lastMessageExchange = const Value.absent(), + this.rowid = const Value.absent(), + }); + GroupsCompanion.insert({ + required String groupId, + this.isGroupAdmin = const Value.absent(), + this.isDirectChat = const Value.absent(), + this.pinned = const Value.absent(), + this.archived = const Value.absent(), + this.joinedGroup = const Value.absent(), + this.leftGroup = const Value.absent(), + this.deletedContent = const Value.absent(), + this.stateVersionId = const Value.absent(), + this.stateEncryptionKey = const Value.absent(), + this.myGroupPrivateKey = const Value.absent(), + required String groupName, + this.draftMessage = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.alsoBestFriend = const Value.absent(), + this.deleteMessagesAfterMilliseconds = const Value.absent(), + this.createdAt = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastFlameCounterChange = const Value.absent(), + this.lastFlameSync = const Value.absent(), + this.flameCounter = const Value.absent(), + this.maxFlameCounter = const Value.absent(), + this.maxFlameCounterFrom = const Value.absent(), + this.lastMessageExchange = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupId = Value(groupId), + groupName = Value(groupName); + static Insertable custom({ + Expression? groupId, + Expression? isGroupAdmin, + Expression? isDirectChat, + Expression? pinned, + Expression? archived, + Expression? joinedGroup, + Expression? leftGroup, + Expression? deletedContent, + Expression? stateVersionId, + Expression? stateEncryptionKey, + Expression? myGroupPrivateKey, + Expression? groupName, + Expression? draftMessage, + Expression? totalMediaCounter, + Expression? alsoBestFriend, + Expression? deleteMessagesAfterMilliseconds, + Expression? createdAt, + Expression? lastMessageSend, + Expression? lastMessageReceived, + Expression? lastFlameCounterChange, + Expression? lastFlameSync, + Expression? flameCounter, + Expression? maxFlameCounter, + Expression? maxFlameCounterFrom, + Expression? lastMessageExchange, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupId != null) 'group_id': groupId, + if (isGroupAdmin != null) 'is_group_admin': isGroupAdmin, + if (isDirectChat != null) 'is_direct_chat': isDirectChat, + if (pinned != null) 'pinned': pinned, + if (archived != null) 'archived': archived, + if (joinedGroup != null) 'joined_group': joinedGroup, + if (leftGroup != null) 'left_group': leftGroup, + if (deletedContent != null) 'deleted_content': deletedContent, + if (stateVersionId != null) 'state_version_id': stateVersionId, + if (stateEncryptionKey != null) + 'state_encryption_key': stateEncryptionKey, + if (myGroupPrivateKey != null) 'my_group_private_key': myGroupPrivateKey, + if (groupName != null) 'group_name': groupName, + if (draftMessage != null) 'draft_message': draftMessage, + if (totalMediaCounter != null) 'total_media_counter': totalMediaCounter, + if (alsoBestFriend != null) 'also_best_friend': alsoBestFriend, + if (deleteMessagesAfterMilliseconds != null) + 'delete_messages_after_milliseconds': deleteMessagesAfterMilliseconds, + if (createdAt != null) 'created_at': createdAt, + 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 (flameCounter != null) 'flame_counter': flameCounter, + if (maxFlameCounter != null) 'max_flame_counter': maxFlameCounter, + if (maxFlameCounterFrom != null) + 'max_flame_counter_from': maxFlameCounterFrom, + if (lastMessageExchange != null) + 'last_message_exchange': lastMessageExchange, + if (rowid != null) 'rowid': rowid, + }); + } + + GroupsCompanion copyWith({ + Value? groupId, + Value? isGroupAdmin, + Value? isDirectChat, + Value? pinned, + Value? archived, + Value? joinedGroup, + Value? leftGroup, + Value? deletedContent, + Value? stateVersionId, + Value? stateEncryptionKey, + Value? myGroupPrivateKey, + Value? groupName, + Value? draftMessage, + Value? totalMediaCounter, + Value? alsoBestFriend, + Value? deleteMessagesAfterMilliseconds, + Value? createdAt, + Value? lastMessageSend, + Value? lastMessageReceived, + Value? lastFlameCounterChange, + Value? lastFlameSync, + Value? flameCounter, + Value? maxFlameCounter, + Value? maxFlameCounterFrom, + Value? lastMessageExchange, + Value? rowid, + }) { + return GroupsCompanion( + groupId: groupId ?? this.groupId, + isGroupAdmin: isGroupAdmin ?? this.isGroupAdmin, + isDirectChat: isDirectChat ?? this.isDirectChat, + pinned: pinned ?? this.pinned, + archived: archived ?? this.archived, + joinedGroup: joinedGroup ?? this.joinedGroup, + leftGroup: leftGroup ?? this.leftGroup, + deletedContent: deletedContent ?? this.deletedContent, + stateVersionId: stateVersionId ?? this.stateVersionId, + stateEncryptionKey: stateEncryptionKey ?? this.stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey ?? this.myGroupPrivateKey, + groupName: groupName ?? this.groupName, + draftMessage: draftMessage ?? this.draftMessage, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, + deleteMessagesAfterMilliseconds: + deleteMessagesAfterMilliseconds ?? + this.deleteMessagesAfterMilliseconds, + createdAt: createdAt ?? this.createdAt, + lastMessageSend: lastMessageSend ?? this.lastMessageSend, + lastMessageReceived: lastMessageReceived ?? this.lastMessageReceived, + lastFlameCounterChange: + lastFlameCounterChange ?? this.lastFlameCounterChange, + lastFlameSync: lastFlameSync ?? this.lastFlameSync, + flameCounter: flameCounter ?? this.flameCounter, + maxFlameCounter: maxFlameCounter ?? this.maxFlameCounter, + maxFlameCounterFrom: maxFlameCounterFrom ?? this.maxFlameCounterFrom, + lastMessageExchange: lastMessageExchange ?? this.lastMessageExchange, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (isGroupAdmin.present) { + map['is_group_admin'] = Variable(isGroupAdmin.value); + } + if (isDirectChat.present) { + map['is_direct_chat'] = Variable(isDirectChat.value); + } + if (pinned.present) { + map['pinned'] = Variable(pinned.value); + } + if (archived.present) { + map['archived'] = Variable(archived.value); + } + if (joinedGroup.present) { + map['joined_group'] = Variable(joinedGroup.value); + } + if (leftGroup.present) { + map['left_group'] = Variable(leftGroup.value); + } + if (deletedContent.present) { + map['deleted_content'] = Variable(deletedContent.value); + } + if (stateVersionId.present) { + map['state_version_id'] = Variable(stateVersionId.value); + } + if (stateEncryptionKey.present) { + map['state_encryption_key'] = Variable( + stateEncryptionKey.value, + ); + } + if (myGroupPrivateKey.present) { + map['my_group_private_key'] = Variable( + myGroupPrivateKey.value, + ); + } + if (groupName.present) { + map['group_name'] = Variable(groupName.value); + } + if (draftMessage.present) { + map['draft_message'] = Variable(draftMessage.value); + } + if (totalMediaCounter.present) { + map['total_media_counter'] = Variable(totalMediaCounter.value); + } + if (alsoBestFriend.present) { + map['also_best_friend'] = Variable(alsoBestFriend.value); + } + if (deleteMessagesAfterMilliseconds.present) { + map['delete_messages_after_milliseconds'] = Variable( + deleteMessagesAfterMilliseconds.value, + ); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.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 (flameCounter.present) { + map['flame_counter'] = Variable(flameCounter.value); + } + if (maxFlameCounter.present) { + map['max_flame_counter'] = Variable(maxFlameCounter.value); + } + if (maxFlameCounterFrom.present) { + map['max_flame_counter_from'] = Variable(maxFlameCounterFrom.value); + } + if (lastMessageExchange.present) { + map['last_message_exchange'] = Variable(lastMessageExchange.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GroupsCompanion(') + ..write('groupId: $groupId, ') + ..write('isGroupAdmin: $isGroupAdmin, ') + ..write('isDirectChat: $isDirectChat, ') + ..write('pinned: $pinned, ') + ..write('archived: $archived, ') + ..write('joinedGroup: $joinedGroup, ') + ..write('leftGroup: $leftGroup, ') + ..write('deletedContent: $deletedContent, ') + ..write('stateVersionId: $stateVersionId, ') + ..write('stateEncryptionKey: $stateEncryptionKey, ') + ..write('myGroupPrivateKey: $myGroupPrivateKey, ') + ..write('groupName: $groupName, ') + ..write('draftMessage: $draftMessage, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('alsoBestFriend: $alsoBestFriend, ') + ..write( + 'deleteMessagesAfterMilliseconds: $deleteMessagesAfterMilliseconds, ', + ) + ..write('createdAt: $createdAt, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastFlameCounterChange: $lastFlameCounterChange, ') + ..write('lastFlameSync: $lastFlameSync, ') + ..write('flameCounter: $flameCounter, ') + ..write('maxFlameCounter: $maxFlameCounter, ') + ..write('maxFlameCounterFrom: $maxFlameCounterFrom, ') + ..write('lastMessageExchange: $lastMessageExchange, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class MediaFiles extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MediaFiles(this.attachedDatabase, [this._alias]); + late final GeneratedColumn mediaId = GeneratedColumn( + 'media_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn uploadState = GeneratedColumn( + 'upload_state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn downloadState = GeneratedColumn( + 'download_state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn requiresAuthentication = GeneratedColumn( + 'requires_authentication', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (requires_authentication IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn stored = GeneratedColumn( + 'stored', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK ("stored" IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isDraftMedia = GeneratedColumn( + 'is_draft_media', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_draft_media IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_favorite IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn hasCropAnalyzed = GeneratedColumn( + 'has_crop_analyzed', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (has_crop_analyzed IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn preProgressingProcess = GeneratedColumn( + 'pre_progressing_process', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn reuploadRequestedBy = + GeneratedColumn( + 'reupload_requested_by', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn displayLimitInMilliseconds = + GeneratedColumn( + 'display_limit_in_milliseconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn removeAudio = GeneratedColumn( + 'remove_audio', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL CHECK (remove_audio IN (0, 1))', + ); + late final GeneratedColumn downloadToken = + GeneratedColumn( + 'download_token', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn encryptionKey = + GeneratedColumn( + 'encryption_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn encryptionMac = + GeneratedColumn( + 'encryption_mac', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn encryptionNonce = + GeneratedColumn( + 'encryption_nonce', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn storedFileHash = + GeneratedColumn( + 'stored_file_hash', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn hasThumbnail = GeneratedColumn( + 'has_thumbnail', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (has_thumbnail IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn sizeInBytes = GeneratedColumn( + 'size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn createdAtMonth = GeneratedColumn( + 'created_at_month', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + mediaId, + type, + uploadState, + downloadState, + requiresAuthentication, + stored, + isDraftMedia, + isFavorite, + hasCropAnalyzed, + preProgressingProcess, + reuploadRequestedBy, + displayLimitInMilliseconds, + removeAudio, + downloadToken, + encryptionKey, + encryptionMac, + encryptionNonce, + storedFileHash, + hasThumbnail, + sizeInBytes, + createdAt, + createdAtMonth, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'media_files'; + @override + Set get $primaryKey => {mediaId}; + @override + MediaFilesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MediaFilesData( + mediaId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}media_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + uploadState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}upload_state'], + ), + downloadState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}download_state'], + ), + requiresAuthentication: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}requires_authentication'], + )!, + stored: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}stored'], + )!, + isDraftMedia: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_draft_media'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_favorite'], + )!, + hasCropAnalyzed: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}has_crop_analyzed'], + )!, + preProgressingProcess: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}pre_progressing_process'], + ), + reuploadRequestedBy: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}reupload_requested_by'], + ), + displayLimitInMilliseconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}display_limit_in_milliseconds'], + ), + removeAudio: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}remove_audio'], + ), + downloadToken: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}download_token'], + ), + encryptionKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}encryption_key'], + ), + encryptionMac: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}encryption_mac'], + ), + encryptionNonce: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}encryption_nonce'], + ), + storedFileHash: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}stored_file_hash'], + ), + hasThumbnail: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}has_thumbnail'], + )!, + sizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}size_in_bytes'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + createdAtMonth: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}created_at_month'], + ), + ); + } + + @override + MediaFiles createAlias(String alias) { + return MediaFiles(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(media_id)']; + @override + bool get dontWriteConstraints => true; +} + +class MediaFilesData extends DataClass implements Insertable { + final String mediaId; + final String type; + final String? uploadState; + final String? downloadState; + final int requiresAuthentication; + final int stored; + final int isDraftMedia; + final int isFavorite; + final int hasCropAnalyzed; + final int? preProgressingProcess; + final String? reuploadRequestedBy; + final int? displayLimitInMilliseconds; + final int? removeAudio; + final i2.Uint8List? downloadToken; + final i2.Uint8List? encryptionKey; + final i2.Uint8List? encryptionMac; + final i2.Uint8List? encryptionNonce; + final i2.Uint8List? storedFileHash; + final int hasThumbnail; + final int? sizeInBytes; + final int createdAt; + final String? createdAtMonth; + const MediaFilesData({ + required this.mediaId, + required this.type, + this.uploadState, + this.downloadState, + required this.requiresAuthentication, + required this.stored, + required this.isDraftMedia, + required this.isFavorite, + required this.hasCropAnalyzed, + this.preProgressingProcess, + this.reuploadRequestedBy, + this.displayLimitInMilliseconds, + this.removeAudio, + this.downloadToken, + this.encryptionKey, + this.encryptionMac, + this.encryptionNonce, + this.storedFileHash, + required this.hasThumbnail, + this.sizeInBytes, + required this.createdAt, + this.createdAtMonth, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['media_id'] = Variable(mediaId); + map['type'] = Variable(type); + if (!nullToAbsent || uploadState != null) { + map['upload_state'] = Variable(uploadState); + } + if (!nullToAbsent || downloadState != null) { + map['download_state'] = Variable(downloadState); + } + map['requires_authentication'] = Variable(requiresAuthentication); + map['stored'] = Variable(stored); + map['is_draft_media'] = Variable(isDraftMedia); + map['is_favorite'] = Variable(isFavorite); + map['has_crop_analyzed'] = Variable(hasCropAnalyzed); + if (!nullToAbsent || preProgressingProcess != null) { + map['pre_progressing_process'] = Variable(preProgressingProcess); + } + if (!nullToAbsent || reuploadRequestedBy != null) { + map['reupload_requested_by'] = Variable(reuploadRequestedBy); + } + if (!nullToAbsent || displayLimitInMilliseconds != null) { + map['display_limit_in_milliseconds'] = Variable( + displayLimitInMilliseconds, + ); + } + if (!nullToAbsent || removeAudio != null) { + map['remove_audio'] = Variable(removeAudio); + } + if (!nullToAbsent || downloadToken != null) { + map['download_token'] = Variable(downloadToken); + } + if (!nullToAbsent || encryptionKey != null) { + map['encryption_key'] = Variable(encryptionKey); + } + if (!nullToAbsent || encryptionMac != null) { + map['encryption_mac'] = Variable(encryptionMac); + } + if (!nullToAbsent || encryptionNonce != null) { + map['encryption_nonce'] = Variable(encryptionNonce); + } + if (!nullToAbsent || storedFileHash != null) { + map['stored_file_hash'] = Variable(storedFileHash); + } + map['has_thumbnail'] = Variable(hasThumbnail); + if (!nullToAbsent || sizeInBytes != null) { + map['size_in_bytes'] = Variable(sizeInBytes); + } + map['created_at'] = Variable(createdAt); + if (!nullToAbsent || createdAtMonth != null) { + map['created_at_month'] = Variable(createdAtMonth); + } + return map; + } + + MediaFilesCompanion toCompanion(bool nullToAbsent) { + return MediaFilesCompanion( + mediaId: Value(mediaId), + type: Value(type), + uploadState: uploadState == null && nullToAbsent + ? const Value.absent() + : Value(uploadState), + downloadState: downloadState == null && nullToAbsent + ? const Value.absent() + : Value(downloadState), + requiresAuthentication: Value(requiresAuthentication), + stored: Value(stored), + isDraftMedia: Value(isDraftMedia), + isFavorite: Value(isFavorite), + hasCropAnalyzed: Value(hasCropAnalyzed), + preProgressingProcess: preProgressingProcess == null && nullToAbsent + ? const Value.absent() + : Value(preProgressingProcess), + reuploadRequestedBy: reuploadRequestedBy == null && nullToAbsent + ? const Value.absent() + : Value(reuploadRequestedBy), + displayLimitInMilliseconds: + displayLimitInMilliseconds == null && nullToAbsent + ? const Value.absent() + : Value(displayLimitInMilliseconds), + removeAudio: removeAudio == null && nullToAbsent + ? const Value.absent() + : Value(removeAudio), + downloadToken: downloadToken == null && nullToAbsent + ? const Value.absent() + : Value(downloadToken), + encryptionKey: encryptionKey == null && nullToAbsent + ? const Value.absent() + : Value(encryptionKey), + encryptionMac: encryptionMac == null && nullToAbsent + ? const Value.absent() + : Value(encryptionMac), + encryptionNonce: encryptionNonce == null && nullToAbsent + ? const Value.absent() + : Value(encryptionNonce), + storedFileHash: storedFileHash == null && nullToAbsent + ? const Value.absent() + : Value(storedFileHash), + hasThumbnail: Value(hasThumbnail), + sizeInBytes: sizeInBytes == null && nullToAbsent + ? const Value.absent() + : Value(sizeInBytes), + createdAt: Value(createdAt), + createdAtMonth: createdAtMonth == null && nullToAbsent + ? const Value.absent() + : Value(createdAtMonth), + ); + } + + factory MediaFilesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MediaFilesData( + mediaId: serializer.fromJson(json['mediaId']), + type: serializer.fromJson(json['type']), + uploadState: serializer.fromJson(json['uploadState']), + downloadState: serializer.fromJson(json['downloadState']), + requiresAuthentication: serializer.fromJson( + json['requiresAuthentication'], + ), + stored: serializer.fromJson(json['stored']), + isDraftMedia: serializer.fromJson(json['isDraftMedia']), + isFavorite: serializer.fromJson(json['isFavorite']), + hasCropAnalyzed: serializer.fromJson(json['hasCropAnalyzed']), + preProgressingProcess: serializer.fromJson( + json['preProgressingProcess'], + ), + reuploadRequestedBy: serializer.fromJson( + json['reuploadRequestedBy'], + ), + displayLimitInMilliseconds: serializer.fromJson( + json['displayLimitInMilliseconds'], + ), + removeAudio: serializer.fromJson(json['removeAudio']), + downloadToken: serializer.fromJson(json['downloadToken']), + encryptionKey: serializer.fromJson(json['encryptionKey']), + encryptionMac: serializer.fromJson(json['encryptionMac']), + encryptionNonce: serializer.fromJson( + json['encryptionNonce'], + ), + storedFileHash: serializer.fromJson( + json['storedFileHash'], + ), + hasThumbnail: serializer.fromJson(json['hasThumbnail']), + sizeInBytes: serializer.fromJson(json['sizeInBytes']), + createdAt: serializer.fromJson(json['createdAt']), + createdAtMonth: serializer.fromJson(json['createdAtMonth']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'mediaId': serializer.toJson(mediaId), + 'type': serializer.toJson(type), + 'uploadState': serializer.toJson(uploadState), + 'downloadState': serializer.toJson(downloadState), + 'requiresAuthentication': serializer.toJson(requiresAuthentication), + 'stored': serializer.toJson(stored), + 'isDraftMedia': serializer.toJson(isDraftMedia), + 'isFavorite': serializer.toJson(isFavorite), + 'hasCropAnalyzed': serializer.toJson(hasCropAnalyzed), + 'preProgressingProcess': serializer.toJson(preProgressingProcess), + 'reuploadRequestedBy': serializer.toJson(reuploadRequestedBy), + 'displayLimitInMilliseconds': serializer.toJson( + displayLimitInMilliseconds, + ), + 'removeAudio': serializer.toJson(removeAudio), + 'downloadToken': serializer.toJson(downloadToken), + 'encryptionKey': serializer.toJson(encryptionKey), + 'encryptionMac': serializer.toJson(encryptionMac), + 'encryptionNonce': serializer.toJson(encryptionNonce), + 'storedFileHash': serializer.toJson(storedFileHash), + 'hasThumbnail': serializer.toJson(hasThumbnail), + 'sizeInBytes': serializer.toJson(sizeInBytes), + 'createdAt': serializer.toJson(createdAt), + 'createdAtMonth': serializer.toJson(createdAtMonth), + }; + } + + MediaFilesData copyWith({ + String? mediaId, + String? type, + Value uploadState = const Value.absent(), + Value downloadState = const Value.absent(), + int? requiresAuthentication, + int? stored, + int? isDraftMedia, + int? isFavorite, + int? hasCropAnalyzed, + Value preProgressingProcess = const Value.absent(), + Value reuploadRequestedBy = const Value.absent(), + Value displayLimitInMilliseconds = const Value.absent(), + Value removeAudio = const Value.absent(), + Value downloadToken = const Value.absent(), + Value encryptionKey = const Value.absent(), + Value encryptionMac = const Value.absent(), + Value encryptionNonce = const Value.absent(), + Value storedFileHash = const Value.absent(), + int? hasThumbnail, + Value sizeInBytes = const Value.absent(), + int? createdAt, + Value createdAtMonth = const Value.absent(), + }) => MediaFilesData( + mediaId: mediaId ?? this.mediaId, + type: type ?? this.type, + uploadState: uploadState.present ? uploadState.value : this.uploadState, + downloadState: downloadState.present + ? downloadState.value + : this.downloadState, + requiresAuthentication: + requiresAuthentication ?? this.requiresAuthentication, + stored: stored ?? this.stored, + isDraftMedia: isDraftMedia ?? this.isDraftMedia, + isFavorite: isFavorite ?? this.isFavorite, + hasCropAnalyzed: hasCropAnalyzed ?? this.hasCropAnalyzed, + preProgressingProcess: preProgressingProcess.present + ? preProgressingProcess.value + : this.preProgressingProcess, + reuploadRequestedBy: reuploadRequestedBy.present + ? reuploadRequestedBy.value + : this.reuploadRequestedBy, + displayLimitInMilliseconds: displayLimitInMilliseconds.present + ? displayLimitInMilliseconds.value + : this.displayLimitInMilliseconds, + removeAudio: removeAudio.present ? removeAudio.value : this.removeAudio, + downloadToken: downloadToken.present + ? downloadToken.value + : this.downloadToken, + encryptionKey: encryptionKey.present + ? encryptionKey.value + : this.encryptionKey, + encryptionMac: encryptionMac.present + ? encryptionMac.value + : this.encryptionMac, + encryptionNonce: encryptionNonce.present + ? encryptionNonce.value + : this.encryptionNonce, + storedFileHash: storedFileHash.present + ? storedFileHash.value + : this.storedFileHash, + hasThumbnail: hasThumbnail ?? this.hasThumbnail, + sizeInBytes: sizeInBytes.present ? sizeInBytes.value : this.sizeInBytes, + createdAt: createdAt ?? this.createdAt, + createdAtMonth: createdAtMonth.present + ? createdAtMonth.value + : this.createdAtMonth, + ); + MediaFilesData copyWithCompanion(MediaFilesCompanion data) { + return MediaFilesData( + mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId, + type: data.type.present ? data.type.value : this.type, + uploadState: data.uploadState.present + ? data.uploadState.value + : this.uploadState, + downloadState: data.downloadState.present + ? data.downloadState.value + : this.downloadState, + requiresAuthentication: data.requiresAuthentication.present + ? data.requiresAuthentication.value + : this.requiresAuthentication, + stored: data.stored.present ? data.stored.value : this.stored, + isDraftMedia: data.isDraftMedia.present + ? data.isDraftMedia.value + : this.isDraftMedia, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + hasCropAnalyzed: data.hasCropAnalyzed.present + ? data.hasCropAnalyzed.value + : this.hasCropAnalyzed, + preProgressingProcess: data.preProgressingProcess.present + ? data.preProgressingProcess.value + : this.preProgressingProcess, + reuploadRequestedBy: data.reuploadRequestedBy.present + ? data.reuploadRequestedBy.value + : this.reuploadRequestedBy, + displayLimitInMilliseconds: data.displayLimitInMilliseconds.present + ? data.displayLimitInMilliseconds.value + : this.displayLimitInMilliseconds, + removeAudio: data.removeAudio.present + ? data.removeAudio.value + : this.removeAudio, + downloadToken: data.downloadToken.present + ? data.downloadToken.value + : this.downloadToken, + encryptionKey: data.encryptionKey.present + ? data.encryptionKey.value + : this.encryptionKey, + encryptionMac: data.encryptionMac.present + ? data.encryptionMac.value + : this.encryptionMac, + encryptionNonce: data.encryptionNonce.present + ? data.encryptionNonce.value + : this.encryptionNonce, + storedFileHash: data.storedFileHash.present + ? data.storedFileHash.value + : this.storedFileHash, + hasThumbnail: data.hasThumbnail.present + ? data.hasThumbnail.value + : this.hasThumbnail, + sizeInBytes: data.sizeInBytes.present + ? data.sizeInBytes.value + : this.sizeInBytes, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + createdAtMonth: data.createdAtMonth.present + ? data.createdAtMonth.value + : this.createdAtMonth, + ); + } + + @override + String toString() { + return (StringBuffer('MediaFilesData(') + ..write('mediaId: $mediaId, ') + ..write('type: $type, ') + ..write('uploadState: $uploadState, ') + ..write('downloadState: $downloadState, ') + ..write('requiresAuthentication: $requiresAuthentication, ') + ..write('stored: $stored, ') + ..write('isDraftMedia: $isDraftMedia, ') + ..write('isFavorite: $isFavorite, ') + ..write('hasCropAnalyzed: $hasCropAnalyzed, ') + ..write('preProgressingProcess: $preProgressingProcess, ') + ..write('reuploadRequestedBy: $reuploadRequestedBy, ') + ..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ') + ..write('removeAudio: $removeAudio, ') + ..write('downloadToken: $downloadToken, ') + ..write('encryptionKey: $encryptionKey, ') + ..write('encryptionMac: $encryptionMac, ') + ..write('encryptionNonce: $encryptionNonce, ') + ..write('storedFileHash: $storedFileHash, ') + ..write('hasThumbnail: $hasThumbnail, ') + ..write('sizeInBytes: $sizeInBytes, ') + ..write('createdAt: $createdAt, ') + ..write('createdAtMonth: $createdAtMonth') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + mediaId, + type, + uploadState, + downloadState, + requiresAuthentication, + stored, + isDraftMedia, + isFavorite, + hasCropAnalyzed, + preProgressingProcess, + reuploadRequestedBy, + displayLimitInMilliseconds, + removeAudio, + $driftBlobEquality.hash(downloadToken), + $driftBlobEquality.hash(encryptionKey), + $driftBlobEquality.hash(encryptionMac), + $driftBlobEquality.hash(encryptionNonce), + $driftBlobEquality.hash(storedFileHash), + hasThumbnail, + sizeInBytes, + createdAt, + createdAtMonth, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MediaFilesData && + other.mediaId == this.mediaId && + other.type == this.type && + other.uploadState == this.uploadState && + other.downloadState == this.downloadState && + other.requiresAuthentication == this.requiresAuthentication && + other.stored == this.stored && + other.isDraftMedia == this.isDraftMedia && + other.isFavorite == this.isFavorite && + other.hasCropAnalyzed == this.hasCropAnalyzed && + other.preProgressingProcess == this.preProgressingProcess && + other.reuploadRequestedBy == this.reuploadRequestedBy && + other.displayLimitInMilliseconds == this.displayLimitInMilliseconds && + other.removeAudio == this.removeAudio && + $driftBlobEquality.equals(other.downloadToken, this.downloadToken) && + $driftBlobEquality.equals(other.encryptionKey, this.encryptionKey) && + $driftBlobEquality.equals(other.encryptionMac, this.encryptionMac) && + $driftBlobEquality.equals( + other.encryptionNonce, + this.encryptionNonce, + ) && + $driftBlobEquality.equals( + other.storedFileHash, + this.storedFileHash, + ) && + other.hasThumbnail == this.hasThumbnail && + other.sizeInBytes == this.sizeInBytes && + other.createdAt == this.createdAt && + other.createdAtMonth == this.createdAtMonth); +} + +class MediaFilesCompanion extends UpdateCompanion { + final Value mediaId; + final Value type; + final Value uploadState; + final Value downloadState; + final Value requiresAuthentication; + final Value stored; + final Value isDraftMedia; + final Value isFavorite; + final Value hasCropAnalyzed; + final Value preProgressingProcess; + final Value reuploadRequestedBy; + final Value displayLimitInMilliseconds; + final Value removeAudio; + final Value downloadToken; + final Value encryptionKey; + final Value encryptionMac; + final Value encryptionNonce; + final Value storedFileHash; + final Value hasThumbnail; + final Value sizeInBytes; + final Value createdAt; + final Value createdAtMonth; + final Value rowid; + const MediaFilesCompanion({ + this.mediaId = const Value.absent(), + this.type = const Value.absent(), + this.uploadState = const Value.absent(), + this.downloadState = const Value.absent(), + this.requiresAuthentication = const Value.absent(), + this.stored = const Value.absent(), + this.isDraftMedia = const Value.absent(), + this.isFavorite = const Value.absent(), + this.hasCropAnalyzed = const Value.absent(), + this.preProgressingProcess = const Value.absent(), + this.reuploadRequestedBy = const Value.absent(), + this.displayLimitInMilliseconds = const Value.absent(), + this.removeAudio = const Value.absent(), + this.downloadToken = const Value.absent(), + this.encryptionKey = const Value.absent(), + this.encryptionMac = const Value.absent(), + this.encryptionNonce = const Value.absent(), + this.storedFileHash = const Value.absent(), + this.hasThumbnail = const Value.absent(), + this.sizeInBytes = const Value.absent(), + this.createdAt = const Value.absent(), + this.createdAtMonth = const Value.absent(), + this.rowid = const Value.absent(), + }); + MediaFilesCompanion.insert({ + required String mediaId, + required String type, + this.uploadState = const Value.absent(), + this.downloadState = const Value.absent(), + this.requiresAuthentication = const Value.absent(), + this.stored = const Value.absent(), + this.isDraftMedia = const Value.absent(), + this.isFavorite = const Value.absent(), + this.hasCropAnalyzed = const Value.absent(), + this.preProgressingProcess = const Value.absent(), + this.reuploadRequestedBy = const Value.absent(), + this.displayLimitInMilliseconds = const Value.absent(), + this.removeAudio = const Value.absent(), + this.downloadToken = const Value.absent(), + this.encryptionKey = const Value.absent(), + this.encryptionMac = const Value.absent(), + this.encryptionNonce = const Value.absent(), + this.storedFileHash = const Value.absent(), + this.hasThumbnail = const Value.absent(), + this.sizeInBytes = const Value.absent(), + this.createdAt = const Value.absent(), + this.createdAtMonth = const Value.absent(), + this.rowid = const Value.absent(), + }) : mediaId = Value(mediaId), + type = Value(type); + static Insertable custom({ + Expression? mediaId, + Expression? type, + Expression? uploadState, + Expression? downloadState, + Expression? requiresAuthentication, + Expression? stored, + Expression? isDraftMedia, + Expression? isFavorite, + Expression? hasCropAnalyzed, + Expression? preProgressingProcess, + Expression? reuploadRequestedBy, + Expression? displayLimitInMilliseconds, + Expression? removeAudio, + Expression? downloadToken, + Expression? encryptionKey, + Expression? encryptionMac, + Expression? encryptionNonce, + Expression? storedFileHash, + Expression? hasThumbnail, + Expression? sizeInBytes, + Expression? createdAt, + Expression? createdAtMonth, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (mediaId != null) 'media_id': mediaId, + if (type != null) 'type': type, + if (uploadState != null) 'upload_state': uploadState, + if (downloadState != null) 'download_state': downloadState, + if (requiresAuthentication != null) + 'requires_authentication': requiresAuthentication, + if (stored != null) 'stored': stored, + if (isDraftMedia != null) 'is_draft_media': isDraftMedia, + if (isFavorite != null) 'is_favorite': isFavorite, + if (hasCropAnalyzed != null) 'has_crop_analyzed': hasCropAnalyzed, + if (preProgressingProcess != null) + 'pre_progressing_process': preProgressingProcess, + if (reuploadRequestedBy != null) + 'reupload_requested_by': reuploadRequestedBy, + if (displayLimitInMilliseconds != null) + 'display_limit_in_milliseconds': displayLimitInMilliseconds, + if (removeAudio != null) 'remove_audio': removeAudio, + if (downloadToken != null) 'download_token': downloadToken, + if (encryptionKey != null) 'encryption_key': encryptionKey, + if (encryptionMac != null) 'encryption_mac': encryptionMac, + if (encryptionNonce != null) 'encryption_nonce': encryptionNonce, + if (storedFileHash != null) 'stored_file_hash': storedFileHash, + if (hasThumbnail != null) 'has_thumbnail': hasThumbnail, + if (sizeInBytes != null) 'size_in_bytes': sizeInBytes, + if (createdAt != null) 'created_at': createdAt, + if (createdAtMonth != null) 'created_at_month': createdAtMonth, + if (rowid != null) 'rowid': rowid, + }); + } + + MediaFilesCompanion copyWith({ + Value? mediaId, + Value? type, + Value? uploadState, + Value? downloadState, + Value? requiresAuthentication, + Value? stored, + Value? isDraftMedia, + Value? isFavorite, + Value? hasCropAnalyzed, + Value? preProgressingProcess, + Value? reuploadRequestedBy, + Value? displayLimitInMilliseconds, + Value? removeAudio, + Value? downloadToken, + Value? encryptionKey, + Value? encryptionMac, + Value? encryptionNonce, + Value? storedFileHash, + Value? hasThumbnail, + Value? sizeInBytes, + Value? createdAt, + Value? createdAtMonth, + Value? rowid, + }) { + return MediaFilesCompanion( + mediaId: mediaId ?? this.mediaId, + type: type ?? this.type, + uploadState: uploadState ?? this.uploadState, + downloadState: downloadState ?? this.downloadState, + requiresAuthentication: + requiresAuthentication ?? this.requiresAuthentication, + stored: stored ?? this.stored, + isDraftMedia: isDraftMedia ?? this.isDraftMedia, + isFavorite: isFavorite ?? this.isFavorite, + hasCropAnalyzed: hasCropAnalyzed ?? this.hasCropAnalyzed, + preProgressingProcess: + preProgressingProcess ?? this.preProgressingProcess, + reuploadRequestedBy: reuploadRequestedBy ?? this.reuploadRequestedBy, + displayLimitInMilliseconds: + displayLimitInMilliseconds ?? this.displayLimitInMilliseconds, + removeAudio: removeAudio ?? this.removeAudio, + downloadToken: downloadToken ?? this.downloadToken, + encryptionKey: encryptionKey ?? this.encryptionKey, + encryptionMac: encryptionMac ?? this.encryptionMac, + encryptionNonce: encryptionNonce ?? this.encryptionNonce, + storedFileHash: storedFileHash ?? this.storedFileHash, + hasThumbnail: hasThumbnail ?? this.hasThumbnail, + sizeInBytes: sizeInBytes ?? this.sizeInBytes, + createdAt: createdAt ?? this.createdAt, + createdAtMonth: createdAtMonth ?? this.createdAtMonth, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (mediaId.present) { + map['media_id'] = Variable(mediaId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (uploadState.present) { + map['upload_state'] = Variable(uploadState.value); + } + if (downloadState.present) { + map['download_state'] = Variable(downloadState.value); + } + if (requiresAuthentication.present) { + map['requires_authentication'] = Variable( + requiresAuthentication.value, + ); + } + if (stored.present) { + map['stored'] = Variable(stored.value); + } + if (isDraftMedia.present) { + map['is_draft_media'] = Variable(isDraftMedia.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (hasCropAnalyzed.present) { + map['has_crop_analyzed'] = Variable(hasCropAnalyzed.value); + } + if (preProgressingProcess.present) { + map['pre_progressing_process'] = Variable( + preProgressingProcess.value, + ); + } + if (reuploadRequestedBy.present) { + map['reupload_requested_by'] = Variable( + reuploadRequestedBy.value, + ); + } + if (displayLimitInMilliseconds.present) { + map['display_limit_in_milliseconds'] = Variable( + displayLimitInMilliseconds.value, + ); + } + if (removeAudio.present) { + map['remove_audio'] = Variable(removeAudio.value); + } + if (downloadToken.present) { + map['download_token'] = Variable(downloadToken.value); + } + if (encryptionKey.present) { + map['encryption_key'] = Variable(encryptionKey.value); + } + if (encryptionMac.present) { + map['encryption_mac'] = Variable(encryptionMac.value); + } + if (encryptionNonce.present) { + map['encryption_nonce'] = Variable(encryptionNonce.value); + } + if (storedFileHash.present) { + map['stored_file_hash'] = Variable(storedFileHash.value); + } + if (hasThumbnail.present) { + map['has_thumbnail'] = Variable(hasThumbnail.value); + } + if (sizeInBytes.present) { + map['size_in_bytes'] = Variable(sizeInBytes.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (createdAtMonth.present) { + map['created_at_month'] = Variable(createdAtMonth.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MediaFilesCompanion(') + ..write('mediaId: $mediaId, ') + ..write('type: $type, ') + ..write('uploadState: $uploadState, ') + ..write('downloadState: $downloadState, ') + ..write('requiresAuthentication: $requiresAuthentication, ') + ..write('stored: $stored, ') + ..write('isDraftMedia: $isDraftMedia, ') + ..write('isFavorite: $isFavorite, ') + ..write('hasCropAnalyzed: $hasCropAnalyzed, ') + ..write('preProgressingProcess: $preProgressingProcess, ') + ..write('reuploadRequestedBy: $reuploadRequestedBy, ') + ..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ') + ..write('removeAudio: $removeAudio, ') + ..write('downloadToken: $downloadToken, ') + ..write('encryptionKey: $encryptionKey, ') + ..write('encryptionMac: $encryptionMac, ') + ..write('encryptionNonce: $encryptionNonce, ') + ..write('storedFileHash: $storedFileHash, ') + ..write('hasThumbnail: $hasThumbnail, ') + ..write('sizeInBytes: $sizeInBytes, ') + ..write('createdAt: $createdAt, ') + ..write('createdAtMonth: $createdAtMonth, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Messages extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Messages(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn senderId = GeneratedColumn( + 'sender_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn content = GeneratedColumn( + 'content', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn mediaId = GeneratedColumn( + 'media_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: + 'NULL REFERENCES media_files(media_id)ON DELETE SET NULL', + ); + late final GeneratedColumn additionalMessageData = + GeneratedColumn( + 'additional_message_data', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn mediaStored = GeneratedColumn( + 'media_stored', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (media_stored IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn mediaReopened = GeneratedColumn( + 'media_reopened', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (media_reopened IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn downloadToken = + GeneratedColumn( + 'download_token', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn quotesMessageId = GeneratedColumn( + 'quotes_message_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn isDeletedFromSender = GeneratedColumn( + 'is_deleted_from_sender', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (is_deleted_from_sender IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn openedAt = GeneratedColumn( + 'opened_at', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn openedByAll = GeneratedColumn( + 'opened_by_all', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn modifiedAt = GeneratedColumn( + 'modified_at', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn ackByUser = GeneratedColumn( + 'ack_by_user', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn ackByServer = GeneratedColumn( + 'ack_by_server', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + groupId, + messageId, + senderId, + type, + content, + mediaId, + additionalMessageData, + mediaStored, + mediaReopened, + downloadToken, + quotesMessageId, + isDeletedFromSender, + openedAt, + openedByAll, + createdAt, + modifiedAt, + ackByUser, + ackByServer, + ]; + @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( + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + senderId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}sender_id'], + ), + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + content: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}content'], + ), + mediaId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}media_id'], + ), + additionalMessageData: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}additional_message_data'], + ), + mediaStored: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_stored'], + )!, + mediaReopened: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_reopened'], + )!, + downloadToken: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}download_token'], + ), + quotesMessageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}quotes_message_id'], + ), + isDeletedFromSender: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_deleted_from_sender'], + )!, + openedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}opened_at'], + ), + openedByAll: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}opened_by_all'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + modifiedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}modified_at'], + ), + ackByUser: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ack_by_user'], + ), + ackByServer: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ack_by_server'], + ), + ); + } + + @override + Messages createAlias(String alias) { + return Messages(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(message_id)']; + @override + bool get dontWriteConstraints => true; +} + +class MessagesData extends DataClass implements Insertable { + final String groupId; + final String messageId; + final int? senderId; + final String type; + final String? content; + final String? mediaId; + final i2.Uint8List? additionalMessageData; + final int mediaStored; + final int mediaReopened; + final i2.Uint8List? downloadToken; + final String? quotesMessageId; + final int isDeletedFromSender; + final int? openedAt; + final int? openedByAll; + final int createdAt; + final int? modifiedAt; + final int? ackByUser; + final int? ackByServer; + const MessagesData({ + required this.groupId, + required this.messageId, + this.senderId, + required this.type, + this.content, + this.mediaId, + this.additionalMessageData, + required this.mediaStored, + required this.mediaReopened, + this.downloadToken, + this.quotesMessageId, + required this.isDeletedFromSender, + this.openedAt, + this.openedByAll, + required this.createdAt, + this.modifiedAt, + this.ackByUser, + this.ackByServer, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_id'] = Variable(groupId); + map['message_id'] = Variable(messageId); + if (!nullToAbsent || senderId != null) { + map['sender_id'] = Variable(senderId); + } + map['type'] = Variable(type); + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + if (!nullToAbsent || mediaId != null) { + map['media_id'] = Variable(mediaId); + } + if (!nullToAbsent || additionalMessageData != null) { + map['additional_message_data'] = Variable( + additionalMessageData, + ); + } + map['media_stored'] = Variable(mediaStored); + map['media_reopened'] = Variable(mediaReopened); + if (!nullToAbsent || downloadToken != null) { + map['download_token'] = Variable(downloadToken); + } + if (!nullToAbsent || quotesMessageId != null) { + map['quotes_message_id'] = Variable(quotesMessageId); + } + map['is_deleted_from_sender'] = Variable(isDeletedFromSender); + if (!nullToAbsent || openedAt != null) { + map['opened_at'] = Variable(openedAt); + } + if (!nullToAbsent || openedByAll != null) { + map['opened_by_all'] = Variable(openedByAll); + } + map['created_at'] = Variable(createdAt); + if (!nullToAbsent || modifiedAt != null) { + map['modified_at'] = Variable(modifiedAt); + } + if (!nullToAbsent || ackByUser != null) { + map['ack_by_user'] = Variable(ackByUser); + } + if (!nullToAbsent || ackByServer != null) { + map['ack_by_server'] = Variable(ackByServer); + } + return map; + } + + MessagesCompanion toCompanion(bool nullToAbsent) { + return MessagesCompanion( + groupId: Value(groupId), + messageId: Value(messageId), + senderId: senderId == null && nullToAbsent + ? const Value.absent() + : Value(senderId), + type: Value(type), + content: content == null && nullToAbsent + ? const Value.absent() + : Value(content), + mediaId: mediaId == null && nullToAbsent + ? const Value.absent() + : Value(mediaId), + additionalMessageData: additionalMessageData == null && nullToAbsent + ? const Value.absent() + : Value(additionalMessageData), + mediaStored: Value(mediaStored), + mediaReopened: Value(mediaReopened), + downloadToken: downloadToken == null && nullToAbsent + ? const Value.absent() + : Value(downloadToken), + quotesMessageId: quotesMessageId == null && nullToAbsent + ? const Value.absent() + : Value(quotesMessageId), + isDeletedFromSender: Value(isDeletedFromSender), + openedAt: openedAt == null && nullToAbsent + ? const Value.absent() + : Value(openedAt), + openedByAll: openedByAll == null && nullToAbsent + ? const Value.absent() + : Value(openedByAll), + createdAt: Value(createdAt), + modifiedAt: modifiedAt == null && nullToAbsent + ? const Value.absent() + : Value(modifiedAt), + ackByUser: ackByUser == null && nullToAbsent + ? const Value.absent() + : Value(ackByUser), + ackByServer: ackByServer == null && nullToAbsent + ? const Value.absent() + : Value(ackByServer), + ); + } + + factory MessagesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessagesData( + groupId: serializer.fromJson(json['groupId']), + messageId: serializer.fromJson(json['messageId']), + senderId: serializer.fromJson(json['senderId']), + type: serializer.fromJson(json['type']), + content: serializer.fromJson(json['content']), + mediaId: serializer.fromJson(json['mediaId']), + additionalMessageData: serializer.fromJson( + json['additionalMessageData'], + ), + mediaStored: serializer.fromJson(json['mediaStored']), + mediaReopened: serializer.fromJson(json['mediaReopened']), + downloadToken: serializer.fromJson(json['downloadToken']), + quotesMessageId: serializer.fromJson(json['quotesMessageId']), + isDeletedFromSender: serializer.fromJson( + json['isDeletedFromSender'], + ), + openedAt: serializer.fromJson(json['openedAt']), + openedByAll: serializer.fromJson(json['openedByAll']), + createdAt: serializer.fromJson(json['createdAt']), + modifiedAt: serializer.fromJson(json['modifiedAt']), + ackByUser: serializer.fromJson(json['ackByUser']), + ackByServer: serializer.fromJson(json['ackByServer']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupId': serializer.toJson(groupId), + 'messageId': serializer.toJson(messageId), + 'senderId': serializer.toJson(senderId), + 'type': serializer.toJson(type), + 'content': serializer.toJson(content), + 'mediaId': serializer.toJson(mediaId), + 'additionalMessageData': serializer.toJson( + additionalMessageData, + ), + 'mediaStored': serializer.toJson(mediaStored), + 'mediaReopened': serializer.toJson(mediaReopened), + 'downloadToken': serializer.toJson(downloadToken), + 'quotesMessageId': serializer.toJson(quotesMessageId), + 'isDeletedFromSender': serializer.toJson(isDeletedFromSender), + 'openedAt': serializer.toJson(openedAt), + 'openedByAll': serializer.toJson(openedByAll), + 'createdAt': serializer.toJson(createdAt), + 'modifiedAt': serializer.toJson(modifiedAt), + 'ackByUser': serializer.toJson(ackByUser), + 'ackByServer': serializer.toJson(ackByServer), + }; + } + + MessagesData copyWith({ + String? groupId, + String? messageId, + Value senderId = const Value.absent(), + String? type, + Value content = const Value.absent(), + Value mediaId = const Value.absent(), + Value additionalMessageData = const Value.absent(), + int? mediaStored, + int? mediaReopened, + Value downloadToken = const Value.absent(), + Value quotesMessageId = const Value.absent(), + int? isDeletedFromSender, + Value openedAt = const Value.absent(), + Value openedByAll = const Value.absent(), + int? createdAt, + Value modifiedAt = const Value.absent(), + Value ackByUser = const Value.absent(), + Value ackByServer = const Value.absent(), + }) => MessagesData( + groupId: groupId ?? this.groupId, + messageId: messageId ?? this.messageId, + senderId: senderId.present ? senderId.value : this.senderId, + type: type ?? this.type, + content: content.present ? content.value : this.content, + mediaId: mediaId.present ? mediaId.value : this.mediaId, + additionalMessageData: additionalMessageData.present + ? additionalMessageData.value + : this.additionalMessageData, + mediaStored: mediaStored ?? this.mediaStored, + mediaReopened: mediaReopened ?? this.mediaReopened, + downloadToken: downloadToken.present + ? downloadToken.value + : this.downloadToken, + quotesMessageId: quotesMessageId.present + ? quotesMessageId.value + : this.quotesMessageId, + isDeletedFromSender: isDeletedFromSender ?? this.isDeletedFromSender, + openedAt: openedAt.present ? openedAt.value : this.openedAt, + openedByAll: openedByAll.present ? openedByAll.value : this.openedByAll, + createdAt: createdAt ?? this.createdAt, + modifiedAt: modifiedAt.present ? modifiedAt.value : this.modifiedAt, + ackByUser: ackByUser.present ? ackByUser.value : this.ackByUser, + ackByServer: ackByServer.present ? ackByServer.value : this.ackByServer, + ); + MessagesData copyWithCompanion(MessagesCompanion data) { + return MessagesData( + groupId: data.groupId.present ? data.groupId.value : this.groupId, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + senderId: data.senderId.present ? data.senderId.value : this.senderId, + type: data.type.present ? data.type.value : this.type, + content: data.content.present ? data.content.value : this.content, + mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId, + additionalMessageData: data.additionalMessageData.present + ? data.additionalMessageData.value + : this.additionalMessageData, + mediaStored: data.mediaStored.present + ? data.mediaStored.value + : this.mediaStored, + mediaReopened: data.mediaReopened.present + ? data.mediaReopened.value + : this.mediaReopened, + downloadToken: data.downloadToken.present + ? data.downloadToken.value + : this.downloadToken, + quotesMessageId: data.quotesMessageId.present + ? data.quotesMessageId.value + : this.quotesMessageId, + isDeletedFromSender: data.isDeletedFromSender.present + ? data.isDeletedFromSender.value + : this.isDeletedFromSender, + openedAt: data.openedAt.present ? data.openedAt.value : this.openedAt, + openedByAll: data.openedByAll.present + ? data.openedByAll.value + : this.openedByAll, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + modifiedAt: data.modifiedAt.present + ? data.modifiedAt.value + : this.modifiedAt, + ackByUser: data.ackByUser.present ? data.ackByUser.value : this.ackByUser, + ackByServer: data.ackByServer.present + ? data.ackByServer.value + : this.ackByServer, + ); + } + + @override + String toString() { + return (StringBuffer('MessagesData(') + ..write('groupId: $groupId, ') + ..write('messageId: $messageId, ') + ..write('senderId: $senderId, ') + ..write('type: $type, ') + ..write('content: $content, ') + ..write('mediaId: $mediaId, ') + ..write('additionalMessageData: $additionalMessageData, ') + ..write('mediaStored: $mediaStored, ') + ..write('mediaReopened: $mediaReopened, ') + ..write('downloadToken: $downloadToken, ') + ..write('quotesMessageId: $quotesMessageId, ') + ..write('isDeletedFromSender: $isDeletedFromSender, ') + ..write('openedAt: $openedAt, ') + ..write('openedByAll: $openedByAll, ') + ..write('createdAt: $createdAt, ') + ..write('modifiedAt: $modifiedAt, ') + ..write('ackByUser: $ackByUser, ') + ..write('ackByServer: $ackByServer') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + groupId, + messageId, + senderId, + type, + content, + mediaId, + $driftBlobEquality.hash(additionalMessageData), + mediaStored, + mediaReopened, + $driftBlobEquality.hash(downloadToken), + quotesMessageId, + isDeletedFromSender, + openedAt, + openedByAll, + createdAt, + modifiedAt, + ackByUser, + ackByServer, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessagesData && + other.groupId == this.groupId && + other.messageId == this.messageId && + other.senderId == this.senderId && + other.type == this.type && + other.content == this.content && + other.mediaId == this.mediaId && + $driftBlobEquality.equals( + other.additionalMessageData, + this.additionalMessageData, + ) && + other.mediaStored == this.mediaStored && + other.mediaReopened == this.mediaReopened && + $driftBlobEquality.equals(other.downloadToken, this.downloadToken) && + other.quotesMessageId == this.quotesMessageId && + other.isDeletedFromSender == this.isDeletedFromSender && + other.openedAt == this.openedAt && + other.openedByAll == this.openedByAll && + other.createdAt == this.createdAt && + other.modifiedAt == this.modifiedAt && + other.ackByUser == this.ackByUser && + other.ackByServer == this.ackByServer); +} + +class MessagesCompanion extends UpdateCompanion { + final Value groupId; + final Value messageId; + final Value senderId; + final Value type; + final Value content; + final Value mediaId; + final Value additionalMessageData; + final Value mediaStored; + final Value mediaReopened; + final Value downloadToken; + final Value quotesMessageId; + final Value isDeletedFromSender; + final Value openedAt; + final Value openedByAll; + final Value createdAt; + final Value modifiedAt; + final Value ackByUser; + final Value ackByServer; + final Value rowid; + const MessagesCompanion({ + this.groupId = const Value.absent(), + this.messageId = const Value.absent(), + this.senderId = const Value.absent(), + this.type = const Value.absent(), + this.content = const Value.absent(), + this.mediaId = const Value.absent(), + this.additionalMessageData = const Value.absent(), + this.mediaStored = const Value.absent(), + this.mediaReopened = const Value.absent(), + this.downloadToken = const Value.absent(), + this.quotesMessageId = const Value.absent(), + this.isDeletedFromSender = const Value.absent(), + this.openedAt = const Value.absent(), + this.openedByAll = const Value.absent(), + this.createdAt = const Value.absent(), + this.modifiedAt = const Value.absent(), + this.ackByUser = const Value.absent(), + this.ackByServer = const Value.absent(), + this.rowid = const Value.absent(), + }); + MessagesCompanion.insert({ + required String groupId, + required String messageId, + this.senderId = const Value.absent(), + required String type, + this.content = const Value.absent(), + this.mediaId = const Value.absent(), + this.additionalMessageData = const Value.absent(), + this.mediaStored = const Value.absent(), + this.mediaReopened = const Value.absent(), + this.downloadToken = const Value.absent(), + this.quotesMessageId = const Value.absent(), + this.isDeletedFromSender = const Value.absent(), + this.openedAt = const Value.absent(), + this.openedByAll = const Value.absent(), + this.createdAt = const Value.absent(), + this.modifiedAt = const Value.absent(), + this.ackByUser = const Value.absent(), + this.ackByServer = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupId = Value(groupId), + messageId = Value(messageId), + type = Value(type); + static Insertable custom({ + Expression? groupId, + Expression? messageId, + Expression? senderId, + Expression? type, + Expression? content, + Expression? mediaId, + Expression? additionalMessageData, + Expression? mediaStored, + Expression? mediaReopened, + Expression? downloadToken, + Expression? quotesMessageId, + Expression? isDeletedFromSender, + Expression? openedAt, + Expression? openedByAll, + Expression? createdAt, + Expression? modifiedAt, + Expression? ackByUser, + Expression? ackByServer, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupId != null) 'group_id': groupId, + if (messageId != null) 'message_id': messageId, + if (senderId != null) 'sender_id': senderId, + if (type != null) 'type': type, + if (content != null) 'content': content, + if (mediaId != null) 'media_id': mediaId, + if (additionalMessageData != null) + 'additional_message_data': additionalMessageData, + if (mediaStored != null) 'media_stored': mediaStored, + if (mediaReopened != null) 'media_reopened': mediaReopened, + if (downloadToken != null) 'download_token': downloadToken, + if (quotesMessageId != null) 'quotes_message_id': quotesMessageId, + if (isDeletedFromSender != null) + 'is_deleted_from_sender': isDeletedFromSender, + if (openedAt != null) 'opened_at': openedAt, + if (openedByAll != null) 'opened_by_all': openedByAll, + if (createdAt != null) 'created_at': createdAt, + if (modifiedAt != null) 'modified_at': modifiedAt, + if (ackByUser != null) 'ack_by_user': ackByUser, + if (ackByServer != null) 'ack_by_server': ackByServer, + if (rowid != null) 'rowid': rowid, + }); + } + + MessagesCompanion copyWith({ + Value? groupId, + Value? messageId, + Value? senderId, + Value? type, + Value? content, + Value? mediaId, + Value? additionalMessageData, + Value? mediaStored, + Value? mediaReopened, + Value? downloadToken, + Value? quotesMessageId, + Value? isDeletedFromSender, + Value? openedAt, + Value? openedByAll, + Value? createdAt, + Value? modifiedAt, + Value? ackByUser, + Value? ackByServer, + Value? rowid, + }) { + return MessagesCompanion( + groupId: groupId ?? this.groupId, + messageId: messageId ?? this.messageId, + senderId: senderId ?? this.senderId, + type: type ?? this.type, + content: content ?? this.content, + mediaId: mediaId ?? this.mediaId, + additionalMessageData: + additionalMessageData ?? this.additionalMessageData, + mediaStored: mediaStored ?? this.mediaStored, + mediaReopened: mediaReopened ?? this.mediaReopened, + downloadToken: downloadToken ?? this.downloadToken, + quotesMessageId: quotesMessageId ?? this.quotesMessageId, + isDeletedFromSender: isDeletedFromSender ?? this.isDeletedFromSender, + openedAt: openedAt ?? this.openedAt, + openedByAll: openedByAll ?? this.openedByAll, + createdAt: createdAt ?? this.createdAt, + modifiedAt: modifiedAt ?? this.modifiedAt, + ackByUser: ackByUser ?? this.ackByUser, + ackByServer: ackByServer ?? this.ackByServer, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (senderId.present) { + map['sender_id'] = Variable(senderId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (mediaId.present) { + map['media_id'] = Variable(mediaId.value); + } + if (additionalMessageData.present) { + map['additional_message_data'] = Variable( + additionalMessageData.value, + ); + } + if (mediaStored.present) { + map['media_stored'] = Variable(mediaStored.value); + } + if (mediaReopened.present) { + map['media_reopened'] = Variable(mediaReopened.value); + } + if (downloadToken.present) { + map['download_token'] = Variable(downloadToken.value); + } + if (quotesMessageId.present) { + map['quotes_message_id'] = Variable(quotesMessageId.value); + } + if (isDeletedFromSender.present) { + map['is_deleted_from_sender'] = Variable(isDeletedFromSender.value); + } + if (openedAt.present) { + map['opened_at'] = Variable(openedAt.value); + } + if (openedByAll.present) { + map['opened_by_all'] = Variable(openedByAll.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (modifiedAt.present) { + map['modified_at'] = Variable(modifiedAt.value); + } + if (ackByUser.present) { + map['ack_by_user'] = Variable(ackByUser.value); + } + if (ackByServer.present) { + map['ack_by_server'] = Variable(ackByServer.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessagesCompanion(') + ..write('groupId: $groupId, ') + ..write('messageId: $messageId, ') + ..write('senderId: $senderId, ') + ..write('type: $type, ') + ..write('content: $content, ') + ..write('mediaId: $mediaId, ') + ..write('additionalMessageData: $additionalMessageData, ') + ..write('mediaStored: $mediaStored, ') + ..write('mediaReopened: $mediaReopened, ') + ..write('downloadToken: $downloadToken, ') + ..write('quotesMessageId: $quotesMessageId, ') + ..write('isDeletedFromSender: $isDeletedFromSender, ') + ..write('openedAt: $openedAt, ') + ..write('openedByAll: $openedByAll, ') + ..write('createdAt: $createdAt, ') + ..write('modifiedAt: $modifiedAt, ') + ..write('ackByUser: $ackByUser, ') + ..write('ackByServer: $ackByServer, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class MessageHistories extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MessageHistories(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn content = GeneratedColumn( + 'content', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + id, + messageId, + contactId, + content, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'message_histories'; + @override + Set get $primaryKey => {id}; + @override + MessageHistoriesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MessageHistoriesData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + ), + content: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}content'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + MessageHistories createAlias(String alias) { + return MessageHistories(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class MessageHistoriesData extends DataClass + implements Insertable { + final int id; + final String messageId; + final int? contactId; + final String? content; + final int createdAt; + const MessageHistoriesData({ + required this.id, + required this.messageId, + this.contactId, + this.content, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['message_id'] = Variable(messageId); + if (!nullToAbsent || contactId != null) { + map['contact_id'] = Variable(contactId); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + map['created_at'] = Variable(createdAt); + return map; + } + + MessageHistoriesCompanion toCompanion(bool nullToAbsent) { + return MessageHistoriesCompanion( + id: Value(id), + messageId: Value(messageId), + contactId: contactId == null && nullToAbsent + ? const Value.absent() + : Value(contactId), + content: content == null && nullToAbsent + ? const Value.absent() + : Value(content), + createdAt: Value(createdAt), + ); + } + + factory MessageHistoriesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessageHistoriesData( + id: serializer.fromJson(json['id']), + messageId: serializer.fromJson(json['messageId']), + contactId: serializer.fromJson(json['contactId']), + content: serializer.fromJson(json['content']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'messageId': serializer.toJson(messageId), + 'contactId': serializer.toJson(contactId), + 'content': serializer.toJson(content), + 'createdAt': serializer.toJson(createdAt), + }; + } + + MessageHistoriesData copyWith({ + int? id, + String? messageId, + Value contactId = const Value.absent(), + Value content = const Value.absent(), + int? createdAt, + }) => MessageHistoriesData( + id: id ?? this.id, + messageId: messageId ?? this.messageId, + contactId: contactId.present ? contactId.value : this.contactId, + content: content.present ? content.value : this.content, + createdAt: createdAt ?? this.createdAt, + ); + MessageHistoriesData copyWithCompanion(MessageHistoriesCompanion data) { + return MessageHistoriesData( + id: data.id.present ? data.id.value : this.id, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + content: data.content.present ? data.content.value : this.content, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('MessageHistoriesData(') + ..write('id: $id, ') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('content: $content, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, messageId, contactId, content, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessageHistoriesData && + other.id == this.id && + other.messageId == this.messageId && + other.contactId == this.contactId && + other.content == this.content && + other.createdAt == this.createdAt); +} + +class MessageHistoriesCompanion extends UpdateCompanion { + final Value id; + final Value messageId; + final Value contactId; + final Value content; + final Value createdAt; + const MessageHistoriesCompanion({ + this.id = const Value.absent(), + this.messageId = const Value.absent(), + this.contactId = const Value.absent(), + this.content = const Value.absent(), + this.createdAt = const Value.absent(), + }); + MessageHistoriesCompanion.insert({ + this.id = const Value.absent(), + required String messageId, + this.contactId = const Value.absent(), + this.content = const Value.absent(), + this.createdAt = const Value.absent(), + }) : messageId = Value(messageId); + static Insertable custom({ + Expression? id, + Expression? messageId, + Expression? contactId, + Expression? content, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (messageId != null) 'message_id': messageId, + if (contactId != null) 'contact_id': contactId, + if (content != null) 'content': content, + if (createdAt != null) 'created_at': createdAt, + }); + } + + MessageHistoriesCompanion copyWith({ + Value? id, + Value? messageId, + Value? contactId, + Value? content, + Value? createdAt, + }) { + return MessageHistoriesCompanion( + id: id ?? this.id, + messageId: messageId ?? this.messageId, + contactId: contactId ?? this.contactId, + content: content ?? this.content, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessageHistoriesCompanion(') + ..write('id: $id, ') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('content: $content, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class Reactions extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Reactions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn emoji = GeneratedColumn( + 'emoji', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn senderId = GeneratedColumn( + 'sender_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [messageId, emoji, senderId, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'reactions'; + @override + Set get $primaryKey => {messageId, senderId, emoji}; + @override + ReactionsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ReactionsData( + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + emoji: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}emoji'], + )!, + senderId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}sender_id'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + Reactions createAlias(String alias) { + return Reactions(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(message_id, sender_id, emoji)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class ReactionsData extends DataClass implements Insertable { + final String messageId; + final String emoji; + final int? senderId; + final int createdAt; + const ReactionsData({ + required this.messageId, + required this.emoji, + this.senderId, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['message_id'] = Variable(messageId); + map['emoji'] = Variable(emoji); + if (!nullToAbsent || senderId != null) { + map['sender_id'] = Variable(senderId); + } + map['created_at'] = Variable(createdAt); + return map; + } + + ReactionsCompanion toCompanion(bool nullToAbsent) { + return ReactionsCompanion( + messageId: Value(messageId), + emoji: Value(emoji), + senderId: senderId == null && nullToAbsent + ? const Value.absent() + : Value(senderId), + createdAt: Value(createdAt), + ); + } + + factory ReactionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ReactionsData( + messageId: serializer.fromJson(json['messageId']), + emoji: serializer.fromJson(json['emoji']), + senderId: serializer.fromJson(json['senderId']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'messageId': serializer.toJson(messageId), + 'emoji': serializer.toJson(emoji), + 'senderId': serializer.toJson(senderId), + 'createdAt': serializer.toJson(createdAt), + }; + } + + ReactionsData copyWith({ + String? messageId, + String? emoji, + Value senderId = const Value.absent(), + int? createdAt, + }) => ReactionsData( + messageId: messageId ?? this.messageId, + emoji: emoji ?? this.emoji, + senderId: senderId.present ? senderId.value : this.senderId, + createdAt: createdAt ?? this.createdAt, + ); + ReactionsData copyWithCompanion(ReactionsCompanion data) { + return ReactionsData( + messageId: data.messageId.present ? data.messageId.value : this.messageId, + emoji: data.emoji.present ? data.emoji.value : this.emoji, + senderId: data.senderId.present ? data.senderId.value : this.senderId, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('ReactionsData(') + ..write('messageId: $messageId, ') + ..write('emoji: $emoji, ') + ..write('senderId: $senderId, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(messageId, emoji, senderId, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ReactionsData && + other.messageId == this.messageId && + other.emoji == this.emoji && + other.senderId == this.senderId && + other.createdAt == this.createdAt); +} + +class ReactionsCompanion extends UpdateCompanion { + final Value messageId; + final Value emoji; + final Value senderId; + final Value createdAt; + final Value rowid; + const ReactionsCompanion({ + this.messageId = const Value.absent(), + this.emoji = const Value.absent(), + this.senderId = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + ReactionsCompanion.insert({ + required String messageId, + required String emoji, + this.senderId = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : messageId = Value(messageId), + emoji = Value(emoji); + static Insertable custom({ + Expression? messageId, + Expression? emoji, + Expression? senderId, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (messageId != null) 'message_id': messageId, + if (emoji != null) 'emoji': emoji, + if (senderId != null) 'sender_id': senderId, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + ReactionsCompanion copyWith({ + Value? messageId, + Value? emoji, + Value? senderId, + Value? createdAt, + Value? rowid, + }) { + return ReactionsCompanion( + messageId: messageId ?? this.messageId, + emoji: emoji ?? this.emoji, + senderId: senderId ?? this.senderId, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (emoji.present) { + map['emoji'] = Variable(emoji.value); + } + if (senderId.present) { + map['sender_id'] = Variable(senderId.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('ReactionsCompanion(') + ..write('messageId: $messageId, ') + ..write('emoji: $emoji, ') + ..write('senderId: $senderId, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class GroupMembers extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + GroupMembers(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES contacts(user_id)', + ); + late final GeneratedColumn memberState = GeneratedColumn( + 'member_state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn groupPublicKey = + GeneratedColumn( + 'group_public_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastChatOpened = GeneratedColumn( + 'last_chat_opened', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastTypeIndicator = GeneratedColumn( + 'last_type_indicator', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastMessage = GeneratedColumn( + 'last_message', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + groupId, + contactId, + memberState, + groupPublicKey, + lastChatOpened, + lastTypeIndicator, + lastMessage, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'group_members'; + @override + Set get $primaryKey => {groupId, contactId}; + @override + GroupMembersData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GroupMembersData( + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + memberState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}member_state'], + ), + groupPublicKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}group_public_key'], + ), + lastChatOpened: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_chat_opened'], + ), + lastTypeIndicator: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_type_indicator'], + ), + lastMessage: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + GroupMembers createAlias(String alias) { + return GroupMembers(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(group_id, contact_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class GroupMembersData extends DataClass + implements Insertable { + final String groupId; + final int contactId; + final String? memberState; + final i2.Uint8List? groupPublicKey; + final int? lastChatOpened; + final int? lastTypeIndicator; + final int? lastMessage; + final int createdAt; + const GroupMembersData({ + required this.groupId, + required this.contactId, + this.memberState, + this.groupPublicKey, + this.lastChatOpened, + this.lastTypeIndicator, + this.lastMessage, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_id'] = Variable(groupId); + map['contact_id'] = Variable(contactId); + if (!nullToAbsent || memberState != null) { + map['member_state'] = Variable(memberState); + } + if (!nullToAbsent || groupPublicKey != null) { + map['group_public_key'] = Variable(groupPublicKey); + } + if (!nullToAbsent || lastChatOpened != null) { + map['last_chat_opened'] = Variable(lastChatOpened); + } + if (!nullToAbsent || lastTypeIndicator != null) { + map['last_type_indicator'] = Variable(lastTypeIndicator); + } + if (!nullToAbsent || lastMessage != null) { + map['last_message'] = Variable(lastMessage); + } + map['created_at'] = Variable(createdAt); + return map; + } + + GroupMembersCompanion toCompanion(bool nullToAbsent) { + return GroupMembersCompanion( + groupId: Value(groupId), + contactId: Value(contactId), + memberState: memberState == null && nullToAbsent + ? const Value.absent() + : Value(memberState), + groupPublicKey: groupPublicKey == null && nullToAbsent + ? const Value.absent() + : Value(groupPublicKey), + lastChatOpened: lastChatOpened == null && nullToAbsent + ? const Value.absent() + : Value(lastChatOpened), + lastTypeIndicator: lastTypeIndicator == null && nullToAbsent + ? const Value.absent() + : Value(lastTypeIndicator), + lastMessage: lastMessage == null && nullToAbsent + ? const Value.absent() + : Value(lastMessage), + createdAt: Value(createdAt), + ); + } + + factory GroupMembersData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GroupMembersData( + groupId: serializer.fromJson(json['groupId']), + contactId: serializer.fromJson(json['contactId']), + memberState: serializer.fromJson(json['memberState']), + groupPublicKey: serializer.fromJson( + json['groupPublicKey'], + ), + lastChatOpened: serializer.fromJson(json['lastChatOpened']), + lastTypeIndicator: serializer.fromJson(json['lastTypeIndicator']), + lastMessage: serializer.fromJson(json['lastMessage']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupId': serializer.toJson(groupId), + 'contactId': serializer.toJson(contactId), + 'memberState': serializer.toJson(memberState), + 'groupPublicKey': serializer.toJson(groupPublicKey), + 'lastChatOpened': serializer.toJson(lastChatOpened), + 'lastTypeIndicator': serializer.toJson(lastTypeIndicator), + 'lastMessage': serializer.toJson(lastMessage), + 'createdAt': serializer.toJson(createdAt), + }; + } + + GroupMembersData copyWith({ + String? groupId, + int? contactId, + Value memberState = const Value.absent(), + Value groupPublicKey = const Value.absent(), + Value lastChatOpened = const Value.absent(), + Value lastTypeIndicator = const Value.absent(), + Value lastMessage = const Value.absent(), + int? createdAt, + }) => GroupMembersData( + groupId: groupId ?? this.groupId, + contactId: contactId ?? this.contactId, + memberState: memberState.present ? memberState.value : this.memberState, + groupPublicKey: groupPublicKey.present + ? groupPublicKey.value + : this.groupPublicKey, + lastChatOpened: lastChatOpened.present + ? lastChatOpened.value + : this.lastChatOpened, + lastTypeIndicator: lastTypeIndicator.present + ? lastTypeIndicator.value + : this.lastTypeIndicator, + lastMessage: lastMessage.present ? lastMessage.value : this.lastMessage, + createdAt: createdAt ?? this.createdAt, + ); + GroupMembersData copyWithCompanion(GroupMembersCompanion data) { + return GroupMembersData( + groupId: data.groupId.present ? data.groupId.value : this.groupId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + memberState: data.memberState.present + ? data.memberState.value + : this.memberState, + groupPublicKey: data.groupPublicKey.present + ? data.groupPublicKey.value + : this.groupPublicKey, + lastChatOpened: data.lastChatOpened.present + ? data.lastChatOpened.value + : this.lastChatOpened, + lastTypeIndicator: data.lastTypeIndicator.present + ? data.lastTypeIndicator.value + : this.lastTypeIndicator, + lastMessage: data.lastMessage.present + ? data.lastMessage.value + : this.lastMessage, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('GroupMembersData(') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('memberState: $memberState, ') + ..write('groupPublicKey: $groupPublicKey, ') + ..write('lastChatOpened: $lastChatOpened, ') + ..write('lastTypeIndicator: $lastTypeIndicator, ') + ..write('lastMessage: $lastMessage, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + groupId, + contactId, + memberState, + $driftBlobEquality.hash(groupPublicKey), + lastChatOpened, + lastTypeIndicator, + lastMessage, + createdAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GroupMembersData && + other.groupId == this.groupId && + other.contactId == this.contactId && + other.memberState == this.memberState && + $driftBlobEquality.equals( + other.groupPublicKey, + this.groupPublicKey, + ) && + other.lastChatOpened == this.lastChatOpened && + other.lastTypeIndicator == this.lastTypeIndicator && + other.lastMessage == this.lastMessage && + other.createdAt == this.createdAt); +} + +class GroupMembersCompanion extends UpdateCompanion { + final Value groupId; + final Value contactId; + final Value memberState; + final Value groupPublicKey; + final Value lastChatOpened; + final Value lastTypeIndicator; + final Value lastMessage; + final Value createdAt; + final Value rowid; + const GroupMembersCompanion({ + this.groupId = const Value.absent(), + this.contactId = const Value.absent(), + this.memberState = const Value.absent(), + this.groupPublicKey = const Value.absent(), + this.lastChatOpened = const Value.absent(), + this.lastTypeIndicator = const Value.absent(), + this.lastMessage = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + GroupMembersCompanion.insert({ + required String groupId, + required int contactId, + this.memberState = const Value.absent(), + this.groupPublicKey = const Value.absent(), + this.lastChatOpened = const Value.absent(), + this.lastTypeIndicator = const Value.absent(), + this.lastMessage = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupId = Value(groupId), + contactId = Value(contactId); + static Insertable custom({ + Expression? groupId, + Expression? contactId, + Expression? memberState, + Expression? groupPublicKey, + Expression? lastChatOpened, + Expression? lastTypeIndicator, + Expression? lastMessage, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupId != null) 'group_id': groupId, + if (contactId != null) 'contact_id': contactId, + if (memberState != null) 'member_state': memberState, + if (groupPublicKey != null) 'group_public_key': groupPublicKey, + if (lastChatOpened != null) 'last_chat_opened': lastChatOpened, + if (lastTypeIndicator != null) 'last_type_indicator': lastTypeIndicator, + if (lastMessage != null) 'last_message': lastMessage, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + GroupMembersCompanion copyWith({ + Value? groupId, + Value? contactId, + Value? memberState, + Value? groupPublicKey, + Value? lastChatOpened, + Value? lastTypeIndicator, + Value? lastMessage, + Value? createdAt, + Value? rowid, + }) { + return GroupMembersCompanion( + groupId: groupId ?? this.groupId, + contactId: contactId ?? this.contactId, + memberState: memberState ?? this.memberState, + groupPublicKey: groupPublicKey ?? this.groupPublicKey, + lastChatOpened: lastChatOpened ?? this.lastChatOpened, + lastTypeIndicator: lastTypeIndicator ?? this.lastTypeIndicator, + lastMessage: lastMessage ?? this.lastMessage, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (memberState.present) { + map['member_state'] = Variable(memberState.value); + } + if (groupPublicKey.present) { + map['group_public_key'] = Variable(groupPublicKey.value); + } + if (lastChatOpened.present) { + map['last_chat_opened'] = Variable(lastChatOpened.value); + } + if (lastTypeIndicator.present) { + map['last_type_indicator'] = Variable(lastTypeIndicator.value); + } + if (lastMessage.present) { + map['last_message'] = Variable(lastMessage.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('GroupMembersCompanion(') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('memberState: $memberState, ') + ..write('groupPublicKey: $groupPublicKey, ') + ..write('lastChatOpened: $lastChatOpened, ') + ..write('lastTypeIndicator: $lastTypeIndicator, ') + ..write('lastMessage: $lastMessage, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Receipts extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Receipts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn receiptId = GeneratedColumn( + 'receipt_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn message = + GeneratedColumn( + 'message', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn contactWillSendsReceipt = + GeneratedColumn( + 'contact_will_sends_receipt', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 1 CHECK (contact_will_sends_receipt IN (0, 1))', + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn + willBeRetriedByMediaUpload = GeneratedColumn( + 'will_be_retried_by_media_upload', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (will_be_retried_by_media_upload IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn markForRetry = GeneratedColumn( + 'mark_for_retry', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn markForRetryAfterAccepted = + GeneratedColumn( + 'mark_for_retry_after_accepted', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn ackByServerAt = GeneratedColumn( + 'ack_by_server_at', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn retryCount = GeneratedColumn( + 'retry_count', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn lastRetry = GeneratedColumn( + 'last_retry', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + receiptId, + contactId, + messageId, + message, + contactWillSendsReceipt, + willBeRetriedByMediaUpload, + markForRetry, + markForRetryAfterAccepted, + ackByServerAt, + retryCount, + lastRetry, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'receipts'; + @override + Set get $primaryKey => {receiptId}; + @override + ReceiptsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ReceiptsData( + receiptId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}receipt_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + ), + message: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}message'], + )!, + contactWillSendsReceipt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_will_sends_receipt'], + )!, + willBeRetriedByMediaUpload: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}will_be_retried_by_media_upload'], + )!, + markForRetry: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mark_for_retry'], + ), + markForRetryAfterAccepted: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mark_for_retry_after_accepted'], + ), + ackByServerAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ack_by_server_at'], + ), + retryCount: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}retry_count'], + )!, + lastRetry: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_retry'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + Receipts createAlias(String alias) { + return Receipts(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(receipt_id)']; + @override + bool get dontWriteConstraints => true; +} + +class ReceiptsData extends DataClass implements Insertable { + final String receiptId; + final int contactId; + final String? messageId; + final i2.Uint8List message; + final int contactWillSendsReceipt; + final int willBeRetriedByMediaUpload; + final int? markForRetry; + final int? markForRetryAfterAccepted; + final int? ackByServerAt; + final int retryCount; + final int? lastRetry; + final int createdAt; + const ReceiptsData({ + required this.receiptId, + required this.contactId, + this.messageId, + required this.message, + required this.contactWillSendsReceipt, + required this.willBeRetriedByMediaUpload, + this.markForRetry, + this.markForRetryAfterAccepted, + this.ackByServerAt, + required this.retryCount, + this.lastRetry, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['receipt_id'] = Variable(receiptId); + map['contact_id'] = Variable(contactId); + if (!nullToAbsent || messageId != null) { + map['message_id'] = Variable(messageId); + } + map['message'] = Variable(message); + map['contact_will_sends_receipt'] = Variable(contactWillSendsReceipt); + map['will_be_retried_by_media_upload'] = Variable( + willBeRetriedByMediaUpload, + ); + if (!nullToAbsent || markForRetry != null) { + map['mark_for_retry'] = Variable(markForRetry); + } + if (!nullToAbsent || markForRetryAfterAccepted != null) { + map['mark_for_retry_after_accepted'] = Variable( + markForRetryAfterAccepted, + ); + } + if (!nullToAbsent || ackByServerAt != null) { + map['ack_by_server_at'] = Variable(ackByServerAt); + } + map['retry_count'] = Variable(retryCount); + if (!nullToAbsent || lastRetry != null) { + map['last_retry'] = Variable(lastRetry); + } + map['created_at'] = Variable(createdAt); + return map; + } + + ReceiptsCompanion toCompanion(bool nullToAbsent) { + return ReceiptsCompanion( + receiptId: Value(receiptId), + contactId: Value(contactId), + messageId: messageId == null && nullToAbsent + ? const Value.absent() + : Value(messageId), + message: Value(message), + contactWillSendsReceipt: Value(contactWillSendsReceipt), + willBeRetriedByMediaUpload: Value(willBeRetriedByMediaUpload), + markForRetry: markForRetry == null && nullToAbsent + ? const Value.absent() + : Value(markForRetry), + markForRetryAfterAccepted: + markForRetryAfterAccepted == null && nullToAbsent + ? const Value.absent() + : Value(markForRetryAfterAccepted), + ackByServerAt: ackByServerAt == null && nullToAbsent + ? const Value.absent() + : Value(ackByServerAt), + retryCount: Value(retryCount), + lastRetry: lastRetry == null && nullToAbsent + ? const Value.absent() + : Value(lastRetry), + createdAt: Value(createdAt), + ); + } + + factory ReceiptsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ReceiptsData( + receiptId: serializer.fromJson(json['receiptId']), + contactId: serializer.fromJson(json['contactId']), + messageId: serializer.fromJson(json['messageId']), + message: serializer.fromJson(json['message']), + contactWillSendsReceipt: serializer.fromJson( + json['contactWillSendsReceipt'], + ), + willBeRetriedByMediaUpload: serializer.fromJson( + json['willBeRetriedByMediaUpload'], + ), + markForRetry: serializer.fromJson(json['markForRetry']), + markForRetryAfterAccepted: serializer.fromJson( + json['markForRetryAfterAccepted'], + ), + ackByServerAt: serializer.fromJson(json['ackByServerAt']), + retryCount: serializer.fromJson(json['retryCount']), + lastRetry: serializer.fromJson(json['lastRetry']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'receiptId': serializer.toJson(receiptId), + 'contactId': serializer.toJson(contactId), + 'messageId': serializer.toJson(messageId), + 'message': serializer.toJson(message), + 'contactWillSendsReceipt': serializer.toJson( + contactWillSendsReceipt, + ), + 'willBeRetriedByMediaUpload': serializer.toJson( + willBeRetriedByMediaUpload, + ), + 'markForRetry': serializer.toJson(markForRetry), + 'markForRetryAfterAccepted': serializer.toJson( + markForRetryAfterAccepted, + ), + 'ackByServerAt': serializer.toJson(ackByServerAt), + 'retryCount': serializer.toJson(retryCount), + 'lastRetry': serializer.toJson(lastRetry), + 'createdAt': serializer.toJson(createdAt), + }; + } + + ReceiptsData copyWith({ + String? receiptId, + int? contactId, + Value messageId = const Value.absent(), + i2.Uint8List? message, + int? contactWillSendsReceipt, + int? willBeRetriedByMediaUpload, + Value markForRetry = const Value.absent(), + Value markForRetryAfterAccepted = const Value.absent(), + Value ackByServerAt = const Value.absent(), + int? retryCount, + Value lastRetry = const Value.absent(), + int? createdAt, + }) => ReceiptsData( + receiptId: receiptId ?? this.receiptId, + contactId: contactId ?? this.contactId, + messageId: messageId.present ? messageId.value : this.messageId, + message: message ?? this.message, + contactWillSendsReceipt: + contactWillSendsReceipt ?? this.contactWillSendsReceipt, + willBeRetriedByMediaUpload: + willBeRetriedByMediaUpload ?? this.willBeRetriedByMediaUpload, + markForRetry: markForRetry.present ? markForRetry.value : this.markForRetry, + markForRetryAfterAccepted: markForRetryAfterAccepted.present + ? markForRetryAfterAccepted.value + : this.markForRetryAfterAccepted, + ackByServerAt: ackByServerAt.present + ? ackByServerAt.value + : this.ackByServerAt, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry.present ? lastRetry.value : this.lastRetry, + createdAt: createdAt ?? this.createdAt, + ); + ReceiptsData copyWithCompanion(ReceiptsCompanion data) { + return ReceiptsData( + receiptId: data.receiptId.present ? data.receiptId.value : this.receiptId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + message: data.message.present ? data.message.value : this.message, + contactWillSendsReceipt: data.contactWillSendsReceipt.present + ? data.contactWillSendsReceipt.value + : this.contactWillSendsReceipt, + willBeRetriedByMediaUpload: data.willBeRetriedByMediaUpload.present + ? data.willBeRetriedByMediaUpload.value + : this.willBeRetriedByMediaUpload, + markForRetry: data.markForRetry.present + ? data.markForRetry.value + : this.markForRetry, + markForRetryAfterAccepted: data.markForRetryAfterAccepted.present + ? data.markForRetryAfterAccepted.value + : this.markForRetryAfterAccepted, + ackByServerAt: data.ackByServerAt.present + ? data.ackByServerAt.value + : this.ackByServerAt, + retryCount: data.retryCount.present + ? data.retryCount.value + : this.retryCount, + lastRetry: data.lastRetry.present ? data.lastRetry.value : this.lastRetry, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('ReceiptsData(') + ..write('receiptId: $receiptId, ') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('message: $message, ') + ..write('contactWillSendsReceipt: $contactWillSendsReceipt, ') + ..write('willBeRetriedByMediaUpload: $willBeRetriedByMediaUpload, ') + ..write('markForRetry: $markForRetry, ') + ..write('markForRetryAfterAccepted: $markForRetryAfterAccepted, ') + ..write('ackByServerAt: $ackByServerAt, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + receiptId, + contactId, + messageId, + $driftBlobEquality.hash(message), + contactWillSendsReceipt, + willBeRetriedByMediaUpload, + markForRetry, + markForRetryAfterAccepted, + ackByServerAt, + retryCount, + lastRetry, + createdAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ReceiptsData && + other.receiptId == this.receiptId && + other.contactId == this.contactId && + other.messageId == this.messageId && + $driftBlobEquality.equals(other.message, this.message) && + other.contactWillSendsReceipt == this.contactWillSendsReceipt && + other.willBeRetriedByMediaUpload == this.willBeRetriedByMediaUpload && + other.markForRetry == this.markForRetry && + other.markForRetryAfterAccepted == this.markForRetryAfterAccepted && + other.ackByServerAt == this.ackByServerAt && + other.retryCount == this.retryCount && + other.lastRetry == this.lastRetry && + other.createdAt == this.createdAt); +} + +class ReceiptsCompanion extends UpdateCompanion { + final Value receiptId; + final Value contactId; + final Value messageId; + final Value message; + final Value contactWillSendsReceipt; + final Value willBeRetriedByMediaUpload; + final Value markForRetry; + final Value markForRetryAfterAccepted; + final Value ackByServerAt; + final Value retryCount; + final Value lastRetry; + final Value createdAt; + final Value rowid; + const ReceiptsCompanion({ + this.receiptId = const Value.absent(), + this.contactId = const Value.absent(), + this.messageId = const Value.absent(), + this.message = const Value.absent(), + this.contactWillSendsReceipt = const Value.absent(), + this.willBeRetriedByMediaUpload = const Value.absent(), + this.markForRetry = const Value.absent(), + this.markForRetryAfterAccepted = const Value.absent(), + this.ackByServerAt = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + ReceiptsCompanion.insert({ + required String receiptId, + required int contactId, + this.messageId = const Value.absent(), + required i2.Uint8List message, + this.contactWillSendsReceipt = const Value.absent(), + this.willBeRetriedByMediaUpload = const Value.absent(), + this.markForRetry = const Value.absent(), + this.markForRetryAfterAccepted = const Value.absent(), + this.ackByServerAt = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : receiptId = Value(receiptId), + contactId = Value(contactId), + message = Value(message); + static Insertable custom({ + Expression? receiptId, + Expression? contactId, + Expression? messageId, + Expression? message, + Expression? contactWillSendsReceipt, + Expression? willBeRetriedByMediaUpload, + Expression? markForRetry, + Expression? markForRetryAfterAccepted, + Expression? ackByServerAt, + Expression? retryCount, + Expression? lastRetry, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (receiptId != null) 'receipt_id': receiptId, + if (contactId != null) 'contact_id': contactId, + if (messageId != null) 'message_id': messageId, + if (message != null) 'message': message, + if (contactWillSendsReceipt != null) + 'contact_will_sends_receipt': contactWillSendsReceipt, + if (willBeRetriedByMediaUpload != null) + 'will_be_retried_by_media_upload': willBeRetriedByMediaUpload, + if (markForRetry != null) 'mark_for_retry': markForRetry, + if (markForRetryAfterAccepted != null) + 'mark_for_retry_after_accepted': markForRetryAfterAccepted, + if (ackByServerAt != null) 'ack_by_server_at': ackByServerAt, + if (retryCount != null) 'retry_count': retryCount, + if (lastRetry != null) 'last_retry': lastRetry, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + ReceiptsCompanion copyWith({ + Value? receiptId, + Value? contactId, + Value? messageId, + Value? message, + Value? contactWillSendsReceipt, + Value? willBeRetriedByMediaUpload, + Value? markForRetry, + Value? markForRetryAfterAccepted, + Value? ackByServerAt, + Value? retryCount, + Value? lastRetry, + Value? createdAt, + Value? rowid, + }) { + return ReceiptsCompanion( + receiptId: receiptId ?? this.receiptId, + contactId: contactId ?? this.contactId, + messageId: messageId ?? this.messageId, + message: message ?? this.message, + contactWillSendsReceipt: + contactWillSendsReceipt ?? this.contactWillSendsReceipt, + willBeRetriedByMediaUpload: + willBeRetriedByMediaUpload ?? this.willBeRetriedByMediaUpload, + markForRetry: markForRetry ?? this.markForRetry, + markForRetryAfterAccepted: + markForRetryAfterAccepted ?? this.markForRetryAfterAccepted, + ackByServerAt: ackByServerAt ?? this.ackByServerAt, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry ?? this.lastRetry, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (receiptId.present) { + map['receipt_id'] = Variable(receiptId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (message.present) { + map['message'] = Variable(message.value); + } + if (contactWillSendsReceipt.present) { + map['contact_will_sends_receipt'] = Variable( + contactWillSendsReceipt.value, + ); + } + if (willBeRetriedByMediaUpload.present) { + map['will_be_retried_by_media_upload'] = Variable( + willBeRetriedByMediaUpload.value, + ); + } + if (markForRetry.present) { + map['mark_for_retry'] = Variable(markForRetry.value); + } + if (markForRetryAfterAccepted.present) { + map['mark_for_retry_after_accepted'] = Variable( + markForRetryAfterAccepted.value, + ); + } + if (ackByServerAt.present) { + map['ack_by_server_at'] = Variable(ackByServerAt.value); + } + if (retryCount.present) { + map['retry_count'] = Variable(retryCount.value); + } + if (lastRetry.present) { + map['last_retry'] = Variable(lastRetry.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('ReceiptsCompanion(') + ..write('receiptId: $receiptId, ') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('message: $message, ') + ..write('contactWillSendsReceipt: $contactWillSendsReceipt, ') + ..write('willBeRetriedByMediaUpload: $willBeRetriedByMediaUpload, ') + ..write('markForRetry: $markForRetry, ') + ..write('markForRetryAfterAccepted: $markForRetryAfterAccepted, ') + ..write('ackByServerAt: $ackByServerAt, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class ReceivedReceipts extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + ReceivedReceipts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn receiptId = GeneratedColumn( + 'receipt_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [receiptId, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'received_receipts'; + @override + Set get $primaryKey => {receiptId}; + @override + ReceivedReceiptsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ReceivedReceiptsData( + receiptId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}receipt_id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + ReceivedReceipts createAlias(String alias) { + return ReceivedReceipts(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(receipt_id)']; + @override + bool get dontWriteConstraints => true; +} + +class ReceivedReceiptsData extends DataClass + implements Insertable { + final String receiptId; + final int createdAt; + const ReceivedReceiptsData({ + required this.receiptId, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['receipt_id'] = Variable(receiptId); + map['created_at'] = Variable(createdAt); + return map; + } + + ReceivedReceiptsCompanion toCompanion(bool nullToAbsent) { + return ReceivedReceiptsCompanion( + receiptId: Value(receiptId), + createdAt: Value(createdAt), + ); + } + + factory ReceivedReceiptsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ReceivedReceiptsData( + receiptId: serializer.fromJson(json['receiptId']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'receiptId': serializer.toJson(receiptId), + 'createdAt': serializer.toJson(createdAt), + }; + } + + ReceivedReceiptsData copyWith({String? receiptId, int? createdAt}) => + ReceivedReceiptsData( + receiptId: receiptId ?? this.receiptId, + createdAt: createdAt ?? this.createdAt, + ); + ReceivedReceiptsData copyWithCompanion(ReceivedReceiptsCompanion data) { + return ReceivedReceiptsData( + receiptId: data.receiptId.present ? data.receiptId.value : this.receiptId, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('ReceivedReceiptsData(') + ..write('receiptId: $receiptId, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(receiptId, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ReceivedReceiptsData && + other.receiptId == this.receiptId && + other.createdAt == this.createdAt); +} + +class ReceivedReceiptsCompanion extends UpdateCompanion { + final Value receiptId; + final Value createdAt; + final Value rowid; + const ReceivedReceiptsCompanion({ + this.receiptId = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + ReceivedReceiptsCompanion.insert({ + required String receiptId, + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : receiptId = Value(receiptId); + static Insertable custom({ + Expression? receiptId, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (receiptId != null) 'receipt_id': receiptId, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + ReceivedReceiptsCompanion copyWith({ + Value? receiptId, + Value? createdAt, + Value? rowid, + }) { + return ReceivedReceiptsCompanion( + receiptId: receiptId ?? this.receiptId, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (receiptId.present) { + map['receipt_id'] = Variable(receiptId.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('ReceivedReceiptsCompanion(') + ..write('receiptId: $receiptId, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn identityKey = + GeneratedColumn( + 'identity_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + 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.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalIdentityKeyStores createAlias(String alias) { + return SignalIdentityKeyStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(device_id, name)']; + @override + bool get dontWriteConstraints => true; +} + +class SignalIdentityKeyStoresData extends DataClass + implements Insertable { + final int deviceId; + final String name; + final i2.Uint8List identityKey; + final int 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, + int? 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn preKey = + GeneratedColumn( + 'pre_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + 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.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalPreKeyStores createAlias(String alias) { + return SignalPreKeyStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(pre_key_id)']; + @override + bool get dontWriteConstraints => true; +} + +class SignalPreKeyStoresData extends DataClass + implements Insertable { + final int preKeyId; + final i2.Uint8List preKey; + final int 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, + int? 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn senderKey = + GeneratedColumn( + 'sender_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @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); + } + + @override + List get customConstraints => const ['PRIMARY KEY(sender_key_name)']; + @override + bool get dontWriteConstraints => true; +} + +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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn sessionRecord = + GeneratedColumn( + 'session_record', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + 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.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalSessionStores createAlias(String alias) { + return SignalSessionStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(device_id, name)']; + @override + bool get dontWriteConstraints => true; +} + +class SignalSessionStoresData extends DataClass + implements Insertable { + final int deviceId; + final String name; + final i2.Uint8List sessionRecord; + final int 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, + int? 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 SignalSignedPreKeyStores extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalSignedPreKeyStores(this.attachedDatabase, [this._alias]); + late final GeneratedColumn signedPreKeyId = GeneratedColumn( + 'signed_pre_key_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn signedPreKey = + GeneratedColumn( + 'signed_pre_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + signedPreKeyId, + signedPreKey, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_signed_pre_key_stores'; + @override + Set get $primaryKey => {signedPreKeyId}; + @override + SignalSignedPreKeyStoresData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalSignedPreKeyStoresData( + signedPreKeyId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}signed_pre_key_id'], + )!, + signedPreKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}signed_pre_key'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalSignedPreKeyStores createAlias(String alias) { + return SignalSignedPreKeyStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(signed_pre_key_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class SignalSignedPreKeyStoresData extends DataClass + implements Insertable { + final int signedPreKeyId; + final i2.Uint8List signedPreKey; + final int createdAt; + const SignalSignedPreKeyStoresData({ + required this.signedPreKeyId, + required this.signedPreKey, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['signed_pre_key_id'] = Variable(signedPreKeyId); + map['signed_pre_key'] = Variable(signedPreKey); + map['created_at'] = Variable(createdAt); + return map; + } + + SignalSignedPreKeyStoresCompanion toCompanion(bool nullToAbsent) { + return SignalSignedPreKeyStoresCompanion( + signedPreKeyId: Value(signedPreKeyId), + signedPreKey: Value(signedPreKey), + createdAt: Value(createdAt), + ); + } + + factory SignalSignedPreKeyStoresData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalSignedPreKeyStoresData( + signedPreKeyId: serializer.fromJson(json['signedPreKeyId']), + signedPreKey: serializer.fromJson(json['signedPreKey']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'signedPreKeyId': serializer.toJson(signedPreKeyId), + 'signedPreKey': serializer.toJson(signedPreKey), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SignalSignedPreKeyStoresData copyWith({ + int? signedPreKeyId, + i2.Uint8List? signedPreKey, + int? createdAt, + }) => SignalSignedPreKeyStoresData( + signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId, + signedPreKey: signedPreKey ?? this.signedPreKey, + createdAt: createdAt ?? this.createdAt, + ); + SignalSignedPreKeyStoresData copyWithCompanion( + SignalSignedPreKeyStoresCompanion data, + ) { + return SignalSignedPreKeyStoresData( + signedPreKeyId: data.signedPreKeyId.present + ? data.signedPreKeyId.value + : this.signedPreKeyId, + signedPreKey: data.signedPreKey.present + ? data.signedPreKey.value + : this.signedPreKey, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SignalSignedPreKeyStoresData(') + ..write('signedPreKeyId: $signedPreKeyId, ') + ..write('signedPreKey: $signedPreKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + signedPreKeyId, + $driftBlobEquality.hash(signedPreKey), + createdAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalSignedPreKeyStoresData && + other.signedPreKeyId == this.signedPreKeyId && + $driftBlobEquality.equals(other.signedPreKey, this.signedPreKey) && + other.createdAt == this.createdAt); +} + +class SignalSignedPreKeyStoresCompanion + extends UpdateCompanion { + final Value signedPreKeyId; + final Value signedPreKey; + final Value createdAt; + const SignalSignedPreKeyStoresCompanion({ + this.signedPreKeyId = const Value.absent(), + this.signedPreKey = const Value.absent(), + this.createdAt = const Value.absent(), + }); + SignalSignedPreKeyStoresCompanion.insert({ + this.signedPreKeyId = const Value.absent(), + required i2.Uint8List signedPreKey, + this.createdAt = const Value.absent(), + }) : signedPreKey = Value(signedPreKey); + static Insertable custom({ + Expression? signedPreKeyId, + Expression? signedPreKey, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (signedPreKeyId != null) 'signed_pre_key_id': signedPreKeyId, + if (signedPreKey != null) 'signed_pre_key': signedPreKey, + if (createdAt != null) 'created_at': createdAt, + }); + } + + SignalSignedPreKeyStoresCompanion copyWith({ + Value? signedPreKeyId, + Value? signedPreKey, + Value? createdAt, + }) { + return SignalSignedPreKeyStoresCompanion( + signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId, + signedPreKey: signedPreKey ?? this.signedPreKey, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (signedPreKeyId.present) { + map['signed_pre_key_id'] = Variable(signedPreKeyId.value); + } + if (signedPreKey.present) { + map['signed_pre_key'] = Variable(signedPreKey.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalSignedPreKeyStoresCompanion(') + ..write('signedPreKeyId: $signedPreKeyId, ') + ..write('signedPreKey: $signedPreKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class MessageActions extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MessageActions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn actionAt = GeneratedColumn( + 'action_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [messageId, contactId, type, actionAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'message_actions'; + @override + Set get $primaryKey => {messageId, contactId, type}; + @override + MessageActionsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MessageActionsData( + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + actionAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}action_at'], + )!, + ); + } + + @override + MessageActions createAlias(String alias) { + return MessageActions(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(message_id, contact_id, type)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class MessageActionsData extends DataClass + implements Insertable { + final String messageId; + final int contactId; + final String type; + final int actionAt; + const MessageActionsData({ + required this.messageId, + required this.contactId, + required this.type, + required this.actionAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['message_id'] = Variable(messageId); + map['contact_id'] = Variable(contactId); + map['type'] = Variable(type); + map['action_at'] = Variable(actionAt); + return map; + } + + MessageActionsCompanion toCompanion(bool nullToAbsent) { + return MessageActionsCompanion( + messageId: Value(messageId), + contactId: Value(contactId), + type: Value(type), + actionAt: Value(actionAt), + ); + } + + factory MessageActionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessageActionsData( + messageId: serializer.fromJson(json['messageId']), + contactId: serializer.fromJson(json['contactId']), + type: serializer.fromJson(json['type']), + actionAt: serializer.fromJson(json['actionAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'messageId': serializer.toJson(messageId), + 'contactId': serializer.toJson(contactId), + 'type': serializer.toJson(type), + 'actionAt': serializer.toJson(actionAt), + }; + } + + MessageActionsData copyWith({ + String? messageId, + int? contactId, + String? type, + int? actionAt, + }) => MessageActionsData( + messageId: messageId ?? this.messageId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + ); + MessageActionsData copyWithCompanion(MessageActionsCompanion data) { + return MessageActionsData( + messageId: data.messageId.present ? data.messageId.value : this.messageId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + type: data.type.present ? data.type.value : this.type, + actionAt: data.actionAt.present ? data.actionAt.value : this.actionAt, + ); + } + + @override + String toString() { + return (StringBuffer('MessageActionsData(') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('actionAt: $actionAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(messageId, contactId, type, actionAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessageActionsData && + other.messageId == this.messageId && + other.contactId == this.contactId && + other.type == this.type && + other.actionAt == this.actionAt); +} + +class MessageActionsCompanion extends UpdateCompanion { + final Value messageId; + final Value contactId; + final Value type; + final Value actionAt; + final Value rowid; + const MessageActionsCompanion({ + this.messageId = const Value.absent(), + this.contactId = const Value.absent(), + this.type = const Value.absent(), + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + MessageActionsCompanion.insert({ + required String messageId, + required int contactId, + required String type, + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : messageId = Value(messageId), + contactId = Value(contactId), + type = Value(type); + static Insertable custom({ + Expression? messageId, + Expression? contactId, + Expression? type, + Expression? actionAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (messageId != null) 'message_id': messageId, + if (contactId != null) 'contact_id': contactId, + if (type != null) 'type': type, + if (actionAt != null) 'action_at': actionAt, + if (rowid != null) 'rowid': rowid, + }); + } + + MessageActionsCompanion copyWith({ + Value? messageId, + Value? contactId, + Value? type, + Value? actionAt, + Value? rowid, + }) { + return MessageActionsCompanion( + messageId: messageId ?? this.messageId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (actionAt.present) { + map['action_at'] = Variable(actionAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessageActionsCompanion(') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('actionAt: $actionAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class GroupHistories extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + GroupHistories(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupHistoryId = GeneratedColumn( + 'group_history_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)', + ); + late final GeneratedColumn affectedContactId = GeneratedColumn( + 'affected_contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn oldGroupName = GeneratedColumn( + 'old_group_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn newGroupName = GeneratedColumn( + 'new_group_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn newDeleteMessagesAfterMilliseconds = + GeneratedColumn( + 'new_delete_messages_after_milliseconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn actionAt = GeneratedColumn( + 'action_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + groupHistoryId, + groupId, + contactId, + affectedContactId, + oldGroupName, + newGroupName, + newDeleteMessagesAfterMilliseconds, + type, + actionAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'group_histories'; + @override + Set get $primaryKey => {groupHistoryId}; + @override + GroupHistoriesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GroupHistoriesData( + groupHistoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_history_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + ), + affectedContactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}affected_contact_id'], + ), + oldGroupName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}old_group_name'], + ), + newGroupName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}new_group_name'], + ), + newDeleteMessagesAfterMilliseconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}new_delete_messages_after_milliseconds'], + ), + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + actionAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}action_at'], + )!, + ); + } + + @override + GroupHistories createAlias(String alias) { + return GroupHistories(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(group_history_id)']; + @override + bool get dontWriteConstraints => true; +} + +class GroupHistoriesData extends DataClass + implements Insertable { + final String groupHistoryId; + final String groupId; + final int? contactId; + final int? affectedContactId; + final String? oldGroupName; + final String? newGroupName; + final int? newDeleteMessagesAfterMilliseconds; + final String type; + final int actionAt; + const GroupHistoriesData({ + required this.groupHistoryId, + required this.groupId, + this.contactId, + this.affectedContactId, + this.oldGroupName, + this.newGroupName, + this.newDeleteMessagesAfterMilliseconds, + required this.type, + required this.actionAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_history_id'] = Variable(groupHistoryId); + map['group_id'] = Variable(groupId); + if (!nullToAbsent || contactId != null) { + map['contact_id'] = Variable(contactId); + } + if (!nullToAbsent || affectedContactId != null) { + map['affected_contact_id'] = Variable(affectedContactId); + } + if (!nullToAbsent || oldGroupName != null) { + map['old_group_name'] = Variable(oldGroupName); + } + if (!nullToAbsent || newGroupName != null) { + map['new_group_name'] = Variable(newGroupName); + } + if (!nullToAbsent || newDeleteMessagesAfterMilliseconds != null) { + map['new_delete_messages_after_milliseconds'] = Variable( + newDeleteMessagesAfterMilliseconds, + ); + } + map['type'] = Variable(type); + map['action_at'] = Variable(actionAt); + return map; + } + + GroupHistoriesCompanion toCompanion(bool nullToAbsent) { + return GroupHistoriesCompanion( + groupHistoryId: Value(groupHistoryId), + groupId: Value(groupId), + contactId: contactId == null && nullToAbsent + ? const Value.absent() + : Value(contactId), + affectedContactId: affectedContactId == null && nullToAbsent + ? const Value.absent() + : Value(affectedContactId), + oldGroupName: oldGroupName == null && nullToAbsent + ? const Value.absent() + : Value(oldGroupName), + newGroupName: newGroupName == null && nullToAbsent + ? const Value.absent() + : Value(newGroupName), + newDeleteMessagesAfterMilliseconds: + newDeleteMessagesAfterMilliseconds == null && nullToAbsent + ? const Value.absent() + : Value(newDeleteMessagesAfterMilliseconds), + type: Value(type), + actionAt: Value(actionAt), + ); + } + + factory GroupHistoriesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GroupHistoriesData( + groupHistoryId: serializer.fromJson(json['groupHistoryId']), + groupId: serializer.fromJson(json['groupId']), + contactId: serializer.fromJson(json['contactId']), + affectedContactId: serializer.fromJson(json['affectedContactId']), + oldGroupName: serializer.fromJson(json['oldGroupName']), + newGroupName: serializer.fromJson(json['newGroupName']), + newDeleteMessagesAfterMilliseconds: serializer.fromJson( + json['newDeleteMessagesAfterMilliseconds'], + ), + type: serializer.fromJson(json['type']), + actionAt: serializer.fromJson(json['actionAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupHistoryId': serializer.toJson(groupHistoryId), + 'groupId': serializer.toJson(groupId), + 'contactId': serializer.toJson(contactId), + 'affectedContactId': serializer.toJson(affectedContactId), + 'oldGroupName': serializer.toJson(oldGroupName), + 'newGroupName': serializer.toJson(newGroupName), + 'newDeleteMessagesAfterMilliseconds': serializer.toJson( + newDeleteMessagesAfterMilliseconds, + ), + 'type': serializer.toJson(type), + 'actionAt': serializer.toJson(actionAt), + }; + } + + GroupHistoriesData copyWith({ + String? groupHistoryId, + String? groupId, + Value contactId = const Value.absent(), + Value affectedContactId = const Value.absent(), + Value oldGroupName = const Value.absent(), + Value newGroupName = const Value.absent(), + Value newDeleteMessagesAfterMilliseconds = const Value.absent(), + String? type, + int? actionAt, + }) => GroupHistoriesData( + groupHistoryId: groupHistoryId ?? this.groupHistoryId, + groupId: groupId ?? this.groupId, + contactId: contactId.present ? contactId.value : this.contactId, + affectedContactId: affectedContactId.present + ? affectedContactId.value + : this.affectedContactId, + oldGroupName: oldGroupName.present ? oldGroupName.value : this.oldGroupName, + newGroupName: newGroupName.present ? newGroupName.value : this.newGroupName, + newDeleteMessagesAfterMilliseconds: + newDeleteMessagesAfterMilliseconds.present + ? newDeleteMessagesAfterMilliseconds.value + : this.newDeleteMessagesAfterMilliseconds, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + ); + GroupHistoriesData copyWithCompanion(GroupHistoriesCompanion data) { + return GroupHistoriesData( + groupHistoryId: data.groupHistoryId.present + ? data.groupHistoryId.value + : this.groupHistoryId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + affectedContactId: data.affectedContactId.present + ? data.affectedContactId.value + : this.affectedContactId, + oldGroupName: data.oldGroupName.present + ? data.oldGroupName.value + : this.oldGroupName, + newGroupName: data.newGroupName.present + ? data.newGroupName.value + : this.newGroupName, + newDeleteMessagesAfterMilliseconds: + data.newDeleteMessagesAfterMilliseconds.present + ? data.newDeleteMessagesAfterMilliseconds.value + : this.newDeleteMessagesAfterMilliseconds, + type: data.type.present ? data.type.value : this.type, + actionAt: data.actionAt.present ? data.actionAt.value : this.actionAt, + ); + } + + @override + String toString() { + return (StringBuffer('GroupHistoriesData(') + ..write('groupHistoryId: $groupHistoryId, ') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('affectedContactId: $affectedContactId, ') + ..write('oldGroupName: $oldGroupName, ') + ..write('newGroupName: $newGroupName, ') + ..write( + 'newDeleteMessagesAfterMilliseconds: $newDeleteMessagesAfterMilliseconds, ', + ) + ..write('type: $type, ') + ..write('actionAt: $actionAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + groupHistoryId, + groupId, + contactId, + affectedContactId, + oldGroupName, + newGroupName, + newDeleteMessagesAfterMilliseconds, + type, + actionAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GroupHistoriesData && + other.groupHistoryId == this.groupHistoryId && + other.groupId == this.groupId && + other.contactId == this.contactId && + other.affectedContactId == this.affectedContactId && + other.oldGroupName == this.oldGroupName && + other.newGroupName == this.newGroupName && + other.newDeleteMessagesAfterMilliseconds == + this.newDeleteMessagesAfterMilliseconds && + other.type == this.type && + other.actionAt == this.actionAt); +} + +class GroupHistoriesCompanion extends UpdateCompanion { + final Value groupHistoryId; + final Value groupId; + final Value contactId; + final Value affectedContactId; + final Value oldGroupName; + final Value newGroupName; + final Value newDeleteMessagesAfterMilliseconds; + final Value type; + final Value actionAt; + final Value rowid; + const GroupHistoriesCompanion({ + this.groupHistoryId = const Value.absent(), + this.groupId = const Value.absent(), + this.contactId = const Value.absent(), + this.affectedContactId = const Value.absent(), + this.oldGroupName = const Value.absent(), + this.newGroupName = const Value.absent(), + this.newDeleteMessagesAfterMilliseconds = const Value.absent(), + this.type = const Value.absent(), + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + GroupHistoriesCompanion.insert({ + required String groupHistoryId, + required String groupId, + this.contactId = const Value.absent(), + this.affectedContactId = const Value.absent(), + this.oldGroupName = const Value.absent(), + this.newGroupName = const Value.absent(), + this.newDeleteMessagesAfterMilliseconds = const Value.absent(), + required String type, + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupHistoryId = Value(groupHistoryId), + groupId = Value(groupId), + type = Value(type); + static Insertable custom({ + Expression? groupHistoryId, + Expression? groupId, + Expression? contactId, + Expression? affectedContactId, + Expression? oldGroupName, + Expression? newGroupName, + Expression? newDeleteMessagesAfterMilliseconds, + Expression? type, + Expression? actionAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupHistoryId != null) 'group_history_id': groupHistoryId, + if (groupId != null) 'group_id': groupId, + if (contactId != null) 'contact_id': contactId, + if (affectedContactId != null) 'affected_contact_id': affectedContactId, + if (oldGroupName != null) 'old_group_name': oldGroupName, + if (newGroupName != null) 'new_group_name': newGroupName, + if (newDeleteMessagesAfterMilliseconds != null) + 'new_delete_messages_after_milliseconds': + newDeleteMessagesAfterMilliseconds, + if (type != null) 'type': type, + if (actionAt != null) 'action_at': actionAt, + if (rowid != null) 'rowid': rowid, + }); + } + + GroupHistoriesCompanion copyWith({ + Value? groupHistoryId, + Value? groupId, + Value? contactId, + Value? affectedContactId, + Value? oldGroupName, + Value? newGroupName, + Value? newDeleteMessagesAfterMilliseconds, + Value? type, + Value? actionAt, + Value? rowid, + }) { + return GroupHistoriesCompanion( + groupHistoryId: groupHistoryId ?? this.groupHistoryId, + groupId: groupId ?? this.groupId, + contactId: contactId ?? this.contactId, + affectedContactId: affectedContactId ?? this.affectedContactId, + oldGroupName: oldGroupName ?? this.oldGroupName, + newGroupName: newGroupName ?? this.newGroupName, + newDeleteMessagesAfterMilliseconds: + newDeleteMessagesAfterMilliseconds ?? + this.newDeleteMessagesAfterMilliseconds, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupHistoryId.present) { + map['group_history_id'] = Variable(groupHistoryId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (affectedContactId.present) { + map['affected_contact_id'] = Variable(affectedContactId.value); + } + if (oldGroupName.present) { + map['old_group_name'] = Variable(oldGroupName.value); + } + if (newGroupName.present) { + map['new_group_name'] = Variable(newGroupName.value); + } + if (newDeleteMessagesAfterMilliseconds.present) { + map['new_delete_messages_after_milliseconds'] = Variable( + newDeleteMessagesAfterMilliseconds.value, + ); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (actionAt.present) { + map['action_at'] = Variable(actionAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GroupHistoriesCompanion(') + ..write('groupHistoryId: $groupHistoryId, ') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('affectedContactId: $affectedContactId, ') + ..write('oldGroupName: $oldGroupName, ') + ..write('newGroupName: $newGroupName, ') + ..write( + 'newDeleteMessagesAfterMilliseconds: $newDeleteMessagesAfterMilliseconds, ', + ) + ..write('type: $type, ') + ..write('actionAt: $actionAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class KeyVerifications extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + KeyVerifications(this.attachedDatabase, [this._alias]); + late final GeneratedColumn verificationId = GeneratedColumn( + 'verification_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn verifiedBy = GeneratedColumn( + 'verified_by', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + verificationId, + contactId, + type, + verifiedBy, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'key_verifications'; + @override + Set get $primaryKey => {verificationId}; + @override + KeyVerificationsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return KeyVerificationsData( + verificationId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}verification_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + verifiedBy: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}verified_by'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + KeyVerifications createAlias(String alias) { + return KeyVerifications(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class KeyVerificationsData extends DataClass + implements Insertable { + final int verificationId; + final int contactId; + final String type; + final int? verifiedBy; + final int createdAt; + const KeyVerificationsData({ + required this.verificationId, + required this.contactId, + required this.type, + this.verifiedBy, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['verification_id'] = Variable(verificationId); + map['contact_id'] = Variable(contactId); + map['type'] = Variable(type); + if (!nullToAbsent || verifiedBy != null) { + map['verified_by'] = Variable(verifiedBy); + } + map['created_at'] = Variable(createdAt); + return map; + } + + KeyVerificationsCompanion toCompanion(bool nullToAbsent) { + return KeyVerificationsCompanion( + verificationId: Value(verificationId), + contactId: Value(contactId), + type: Value(type), + verifiedBy: verifiedBy == null && nullToAbsent + ? const Value.absent() + : Value(verifiedBy), + createdAt: Value(createdAt), + ); + } + + factory KeyVerificationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return KeyVerificationsData( + verificationId: serializer.fromJson(json['verificationId']), + contactId: serializer.fromJson(json['contactId']), + type: serializer.fromJson(json['type']), + verifiedBy: serializer.fromJson(json['verifiedBy']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'verificationId': serializer.toJson(verificationId), + 'contactId': serializer.toJson(contactId), + 'type': serializer.toJson(type), + 'verifiedBy': serializer.toJson(verifiedBy), + 'createdAt': serializer.toJson(createdAt), + }; + } + + KeyVerificationsData copyWith({ + int? verificationId, + int? contactId, + String? type, + Value verifiedBy = const Value.absent(), + int? createdAt, + }) => KeyVerificationsData( + verificationId: verificationId ?? this.verificationId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + verifiedBy: verifiedBy.present ? verifiedBy.value : this.verifiedBy, + createdAt: createdAt ?? this.createdAt, + ); + KeyVerificationsData copyWithCompanion(KeyVerificationsCompanion data) { + return KeyVerificationsData( + verificationId: data.verificationId.present + ? data.verificationId.value + : this.verificationId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + type: data.type.present ? data.type.value : this.type, + verifiedBy: data.verifiedBy.present + ? data.verifiedBy.value + : this.verifiedBy, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('KeyVerificationsData(') + ..write('verificationId: $verificationId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('verifiedBy: $verifiedBy, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(verificationId, contactId, type, verifiedBy, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is KeyVerificationsData && + other.verificationId == this.verificationId && + other.contactId == this.contactId && + other.type == this.type && + other.verifiedBy == this.verifiedBy && + other.createdAt == this.createdAt); +} + +class KeyVerificationsCompanion extends UpdateCompanion { + final Value verificationId; + final Value contactId; + final Value type; + final Value verifiedBy; + final Value createdAt; + const KeyVerificationsCompanion({ + this.verificationId = const Value.absent(), + this.contactId = const Value.absent(), + this.type = const Value.absent(), + this.verifiedBy = const Value.absent(), + this.createdAt = const Value.absent(), + }); + KeyVerificationsCompanion.insert({ + this.verificationId = const Value.absent(), + required int contactId, + required String type, + this.verifiedBy = const Value.absent(), + this.createdAt = const Value.absent(), + }) : contactId = Value(contactId), + type = Value(type); + static Insertable custom({ + Expression? verificationId, + Expression? contactId, + Expression? type, + Expression? verifiedBy, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (verificationId != null) 'verification_id': verificationId, + if (contactId != null) 'contact_id': contactId, + if (type != null) 'type': type, + if (verifiedBy != null) 'verified_by': verifiedBy, + if (createdAt != null) 'created_at': createdAt, + }); + } + + KeyVerificationsCompanion copyWith({ + Value? verificationId, + Value? contactId, + Value? type, + Value? verifiedBy, + Value? createdAt, + }) { + return KeyVerificationsCompanion( + verificationId: verificationId ?? this.verificationId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + verifiedBy: verifiedBy ?? this.verifiedBy, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (verificationId.present) { + map['verification_id'] = Variable(verificationId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (verifiedBy.present) { + map['verified_by'] = Variable(verifiedBy.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('KeyVerificationsCompanion(') + ..write('verificationId: $verificationId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('verifiedBy: $verifiedBy, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class VerificationTokens extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + VerificationTokens(this.attachedDatabase, [this._alias]); + late final GeneratedColumn tokenId = GeneratedColumn( + 'token_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn token = + GeneratedColumn( + 'token', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [tokenId, token, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'verification_tokens'; + @override + Set get $primaryKey => {tokenId}; + @override + VerificationTokensData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return VerificationTokensData( + tokenId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}token_id'], + )!, + token: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}token'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + VerificationTokens createAlias(String alias) { + return VerificationTokens(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class VerificationTokensData extends DataClass + implements Insertable { + final int tokenId; + final i2.Uint8List token; + final int createdAt; + const VerificationTokensData({ + required this.tokenId, + required this.token, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['token_id'] = Variable(tokenId); + map['token'] = Variable(token); + map['created_at'] = Variable(createdAt); + return map; + } + + VerificationTokensCompanion toCompanion(bool nullToAbsent) { + return VerificationTokensCompanion( + tokenId: Value(tokenId), + token: Value(token), + createdAt: Value(createdAt), + ); + } + + factory VerificationTokensData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return VerificationTokensData( + tokenId: serializer.fromJson(json['tokenId']), + token: serializer.fromJson(json['token']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'tokenId': serializer.toJson(tokenId), + 'token': serializer.toJson(token), + 'createdAt': serializer.toJson(createdAt), + }; + } + + VerificationTokensData copyWith({ + int? tokenId, + i2.Uint8List? token, + int? createdAt, + }) => VerificationTokensData( + tokenId: tokenId ?? this.tokenId, + token: token ?? this.token, + createdAt: createdAt ?? this.createdAt, + ); + VerificationTokensData copyWithCompanion(VerificationTokensCompanion data) { + return VerificationTokensData( + tokenId: data.tokenId.present ? data.tokenId.value : this.tokenId, + token: data.token.present ? data.token.value : this.token, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('VerificationTokensData(') + ..write('tokenId: $tokenId, ') + ..write('token: $token, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(tokenId, $driftBlobEquality.hash(token), createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is VerificationTokensData && + other.tokenId == this.tokenId && + $driftBlobEquality.equals(other.token, this.token) && + other.createdAt == this.createdAt); +} + +class VerificationTokensCompanion + extends UpdateCompanion { + final Value tokenId; + final Value token; + final Value createdAt; + const VerificationTokensCompanion({ + this.tokenId = const Value.absent(), + this.token = const Value.absent(), + this.createdAt = const Value.absent(), + }); + VerificationTokensCompanion.insert({ + this.tokenId = const Value.absent(), + required i2.Uint8List token, + this.createdAt = const Value.absent(), + }) : token = Value(token); + static Insertable custom({ + Expression? tokenId, + Expression? token, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (tokenId != null) 'token_id': tokenId, + if (token != null) 'token': token, + if (createdAt != null) 'created_at': createdAt, + }); + } + + VerificationTokensCompanion copyWith({ + Value? tokenId, + Value? token, + Value? createdAt, + }) { + return VerificationTokensCompanion( + tokenId: tokenId ?? this.tokenId, + token: token ?? this.token, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (tokenId.present) { + map['token_id'] = Variable(tokenId.value); + } + if (token.present) { + map['token'] = Variable(token.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('VerificationTokensCompanion(') + ..write('tokenId: $tokenId, ') + ..write('token: $token, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryAnnouncedUsers extends Table + with + TableInfo< + UserDiscoveryAnnouncedUsers, + UserDiscoveryAnnouncedUsersData + > { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryAnnouncedUsers(this.attachedDatabase, [this._alias]); + late final GeneratedColumn announcedUserId = GeneratedColumn( + 'announced_user_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn announcedPublicKey = + GeneratedColumn( + 'announced_public_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn publicId = GeneratedColumn( + 'public_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL UNIQUE', + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn wasShownToTheUser = GeneratedColumn( + 'was_shown_to_the_user', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (was_shown_to_the_user IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_hidden IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn wasAskedFriends = GeneratedColumn( + 'was_asked_friends', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (was_asked_friends IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + announcedUserId, + announcedPublicKey, + publicId, + username, + wasShownToTheUser, + isHidden, + wasAskedFriends, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_announced_users'; + @override + Set get $primaryKey => {announcedUserId}; + @override + UserDiscoveryAnnouncedUsersData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryAnnouncedUsersData( + announcedUserId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}announced_user_id'], + )!, + announcedPublicKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}announced_public_key'], + )!, + publicId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_id'], + )!, + username: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}username'], + ), + wasShownToTheUser: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}was_shown_to_the_user'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_hidden'], + )!, + wasAskedFriends: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}was_asked_friends'], + )!, + ); + } + + @override + UserDiscoveryAnnouncedUsers createAlias(String alias) { + return UserDiscoveryAnnouncedUsers(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(announced_user_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryAnnouncedUsersData extends DataClass + implements Insertable { + final int announcedUserId; + final i2.Uint8List announcedPublicKey; + final int publicId; + final String? username; + final int wasShownToTheUser; + final int isHidden; + final int wasAskedFriends; + const UserDiscoveryAnnouncedUsersData({ + required this.announcedUserId, + required this.announcedPublicKey, + required this.publicId, + this.username, + required this.wasShownToTheUser, + required this.isHidden, + required this.wasAskedFriends, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['announced_user_id'] = Variable(announcedUserId); + map['announced_public_key'] = Variable(announcedPublicKey); + map['public_id'] = Variable(publicId); + if (!nullToAbsent || username != null) { + map['username'] = Variable(username); + } + map['was_shown_to_the_user'] = Variable(wasShownToTheUser); + map['is_hidden'] = Variable(isHidden); + map['was_asked_friends'] = Variable(wasAskedFriends); + return map; + } + + UserDiscoveryAnnouncedUsersCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryAnnouncedUsersCompanion( + announcedUserId: Value(announcedUserId), + announcedPublicKey: Value(announcedPublicKey), + publicId: Value(publicId), + username: username == null && nullToAbsent + ? const Value.absent() + : Value(username), + wasShownToTheUser: Value(wasShownToTheUser), + isHidden: Value(isHidden), + wasAskedFriends: Value(wasAskedFriends), + ); + } + + factory UserDiscoveryAnnouncedUsersData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryAnnouncedUsersData( + announcedUserId: serializer.fromJson(json['announcedUserId']), + announcedPublicKey: serializer.fromJson( + json['announcedPublicKey'], + ), + publicId: serializer.fromJson(json['publicId']), + username: serializer.fromJson(json['username']), + wasShownToTheUser: serializer.fromJson(json['wasShownToTheUser']), + isHidden: serializer.fromJson(json['isHidden']), + wasAskedFriends: serializer.fromJson(json['wasAskedFriends']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'announcedUserId': serializer.toJson(announcedUserId), + 'announcedPublicKey': serializer.toJson(announcedPublicKey), + 'publicId': serializer.toJson(publicId), + 'username': serializer.toJson(username), + 'wasShownToTheUser': serializer.toJson(wasShownToTheUser), + 'isHidden': serializer.toJson(isHidden), + 'wasAskedFriends': serializer.toJson(wasAskedFriends), + }; + } + + UserDiscoveryAnnouncedUsersData copyWith({ + int? announcedUserId, + i2.Uint8List? announcedPublicKey, + int? publicId, + Value username = const Value.absent(), + int? wasShownToTheUser, + int? isHidden, + int? wasAskedFriends, + }) => UserDiscoveryAnnouncedUsersData( + announcedUserId: announcedUserId ?? this.announcedUserId, + announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey, + publicId: publicId ?? this.publicId, + username: username.present ? username.value : this.username, + wasShownToTheUser: wasShownToTheUser ?? this.wasShownToTheUser, + isHidden: isHidden ?? this.isHidden, + wasAskedFriends: wasAskedFriends ?? this.wasAskedFriends, + ); + UserDiscoveryAnnouncedUsersData copyWithCompanion( + UserDiscoveryAnnouncedUsersCompanion data, + ) { + return UserDiscoveryAnnouncedUsersData( + announcedUserId: data.announcedUserId.present + ? data.announcedUserId.value + : this.announcedUserId, + announcedPublicKey: data.announcedPublicKey.present + ? data.announcedPublicKey.value + : this.announcedPublicKey, + publicId: data.publicId.present ? data.publicId.value : this.publicId, + username: data.username.present ? data.username.value : this.username, + wasShownToTheUser: data.wasShownToTheUser.present + ? data.wasShownToTheUser.value + : this.wasShownToTheUser, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + wasAskedFriends: data.wasAskedFriends.present + ? data.wasAskedFriends.value + : this.wasAskedFriends, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryAnnouncedUsersData(') + ..write('announcedUserId: $announcedUserId, ') + ..write('announcedPublicKey: $announcedPublicKey, ') + ..write('publicId: $publicId, ') + ..write('username: $username, ') + ..write('wasShownToTheUser: $wasShownToTheUser, ') + ..write('isHidden: $isHidden, ') + ..write('wasAskedFriends: $wasAskedFriends') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + announcedUserId, + $driftBlobEquality.hash(announcedPublicKey), + publicId, + username, + wasShownToTheUser, + isHidden, + wasAskedFriends, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryAnnouncedUsersData && + other.announcedUserId == this.announcedUserId && + $driftBlobEquality.equals( + other.announcedPublicKey, + this.announcedPublicKey, + ) && + other.publicId == this.publicId && + other.username == this.username && + other.wasShownToTheUser == this.wasShownToTheUser && + other.isHidden == this.isHidden && + other.wasAskedFriends == this.wasAskedFriends); +} + +class UserDiscoveryAnnouncedUsersCompanion + extends UpdateCompanion { + final Value announcedUserId; + final Value announcedPublicKey; + final Value publicId; + final Value username; + final Value wasShownToTheUser; + final Value isHidden; + final Value wasAskedFriends; + const UserDiscoveryAnnouncedUsersCompanion({ + this.announcedUserId = const Value.absent(), + this.announcedPublicKey = const Value.absent(), + this.publicId = const Value.absent(), + this.username = const Value.absent(), + this.wasShownToTheUser = const Value.absent(), + this.isHidden = const Value.absent(), + this.wasAskedFriends = const Value.absent(), + }); + UserDiscoveryAnnouncedUsersCompanion.insert({ + this.announcedUserId = const Value.absent(), + required i2.Uint8List announcedPublicKey, + required int publicId, + this.username = const Value.absent(), + this.wasShownToTheUser = const Value.absent(), + this.isHidden = const Value.absent(), + this.wasAskedFriends = const Value.absent(), + }) : announcedPublicKey = Value(announcedPublicKey), + publicId = Value(publicId); + static Insertable custom({ + Expression? announcedUserId, + Expression? announcedPublicKey, + Expression? publicId, + Expression? username, + Expression? wasShownToTheUser, + Expression? isHidden, + Expression? wasAskedFriends, + }) { + return RawValuesInsertable({ + if (announcedUserId != null) 'announced_user_id': announcedUserId, + if (announcedPublicKey != null) + 'announced_public_key': announcedPublicKey, + if (publicId != null) 'public_id': publicId, + if (username != null) 'username': username, + if (wasShownToTheUser != null) 'was_shown_to_the_user': wasShownToTheUser, + if (isHidden != null) 'is_hidden': isHidden, + if (wasAskedFriends != null) 'was_asked_friends': wasAskedFriends, + }); + } + + UserDiscoveryAnnouncedUsersCompanion copyWith({ + Value? announcedUserId, + Value? announcedPublicKey, + Value? publicId, + Value? username, + Value? wasShownToTheUser, + Value? isHidden, + Value? wasAskedFriends, + }) { + return UserDiscoveryAnnouncedUsersCompanion( + announcedUserId: announcedUserId ?? this.announcedUserId, + announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey, + publicId: publicId ?? this.publicId, + username: username ?? this.username, + wasShownToTheUser: wasShownToTheUser ?? this.wasShownToTheUser, + isHidden: isHidden ?? this.isHidden, + wasAskedFriends: wasAskedFriends ?? this.wasAskedFriends, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (announcedUserId.present) { + map['announced_user_id'] = Variable(announcedUserId.value); + } + if (announcedPublicKey.present) { + map['announced_public_key'] = Variable( + announcedPublicKey.value, + ); + } + if (publicId.present) { + map['public_id'] = Variable(publicId.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (wasShownToTheUser.present) { + map['was_shown_to_the_user'] = Variable(wasShownToTheUser.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (wasAskedFriends.present) { + map['was_asked_friends'] = Variable(wasAskedFriends.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryAnnouncedUsersCompanion(') + ..write('announcedUserId: $announcedUserId, ') + ..write('announcedPublicKey: $announcedPublicKey, ') + ..write('publicId: $publicId, ') + ..write('username: $username, ') + ..write('wasShownToTheUser: $wasShownToTheUser, ') + ..write('isHidden: $isHidden, ') + ..write('wasAskedFriends: $wasAskedFriends') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryUserRelations extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryUserRelations(this.attachedDatabase, [this._alias]); + late final GeneratedColumn announcedUserId = GeneratedColumn( + 'announced_user_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES user_discovery_announced_users(announced_user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn fromContactId = GeneratedColumn( + 'from_contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn publicKeyVerifiedTimestamp = + GeneratedColumn( + 'public_key_verified_timestamp', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + announcedUserId, + fromContactId, + publicKeyVerifiedTimestamp, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_user_relations'; + @override + Set get $primaryKey => {announcedUserId, fromContactId}; + @override + UserDiscoveryUserRelationsData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryUserRelationsData( + announcedUserId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}announced_user_id'], + )!, + fromContactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}from_contact_id'], + )!, + publicKeyVerifiedTimestamp: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_key_verified_timestamp'], + ), + ); + } + + @override + UserDiscoveryUserRelations createAlias(String alias) { + return UserDiscoveryUserRelations(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(announced_user_id, from_contact_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryUserRelationsData extends DataClass + implements Insertable { + final int announcedUserId; + final int fromContactId; + final int? publicKeyVerifiedTimestamp; + const UserDiscoveryUserRelationsData({ + required this.announcedUserId, + required this.fromContactId, + this.publicKeyVerifiedTimestamp, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['announced_user_id'] = Variable(announcedUserId); + map['from_contact_id'] = Variable(fromContactId); + if (!nullToAbsent || publicKeyVerifiedTimestamp != null) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp, + ); + } + return map; + } + + UserDiscoveryUserRelationsCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryUserRelationsCompanion( + announcedUserId: Value(announcedUserId), + fromContactId: Value(fromContactId), + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp == null && nullToAbsent + ? const Value.absent() + : Value(publicKeyVerifiedTimestamp), + ); + } + + factory UserDiscoveryUserRelationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryUserRelationsData( + announcedUserId: serializer.fromJson(json['announcedUserId']), + fromContactId: serializer.fromJson(json['fromContactId']), + publicKeyVerifiedTimestamp: serializer.fromJson( + json['publicKeyVerifiedTimestamp'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'announcedUserId': serializer.toJson(announcedUserId), + 'fromContactId': serializer.toJson(fromContactId), + 'publicKeyVerifiedTimestamp': serializer.toJson( + publicKeyVerifiedTimestamp, + ), + }; + } + + UserDiscoveryUserRelationsData copyWith({ + int? announcedUserId, + int? fromContactId, + Value publicKeyVerifiedTimestamp = const Value.absent(), + }) => UserDiscoveryUserRelationsData( + announcedUserId: announcedUserId ?? this.announcedUserId, + fromContactId: fromContactId ?? this.fromContactId, + publicKeyVerifiedTimestamp: publicKeyVerifiedTimestamp.present + ? publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + UserDiscoveryUserRelationsData copyWithCompanion( + UserDiscoveryUserRelationsCompanion data, + ) { + return UserDiscoveryUserRelationsData( + announcedUserId: data.announcedUserId.present + ? data.announcedUserId.value + : this.announcedUserId, + fromContactId: data.fromContactId.present + ? data.fromContactId.value + : this.fromContactId, + publicKeyVerifiedTimestamp: data.publicKeyVerifiedTimestamp.present + ? data.publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryUserRelationsData(') + ..write('announcedUserId: $announcedUserId, ') + ..write('fromContactId: $fromContactId, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(announcedUserId, fromContactId, publicKeyVerifiedTimestamp); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryUserRelationsData && + other.announcedUserId == this.announcedUserId && + other.fromContactId == this.fromContactId && + other.publicKeyVerifiedTimestamp == this.publicKeyVerifiedTimestamp); +} + +class UserDiscoveryUserRelationsCompanion + extends UpdateCompanion { + final Value announcedUserId; + final Value fromContactId; + final Value publicKeyVerifiedTimestamp; + final Value rowid; + const UserDiscoveryUserRelationsCompanion({ + this.announcedUserId = const Value.absent(), + this.fromContactId = const Value.absent(), + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }); + UserDiscoveryUserRelationsCompanion.insert({ + required int announcedUserId, + required int fromContactId, + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }) : announcedUserId = Value(announcedUserId), + fromContactId = Value(fromContactId); + static Insertable custom({ + Expression? announcedUserId, + Expression? fromContactId, + Expression? publicKeyVerifiedTimestamp, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (announcedUserId != null) 'announced_user_id': announcedUserId, + if (fromContactId != null) 'from_contact_id': fromContactId, + if (publicKeyVerifiedTimestamp != null) + 'public_key_verified_timestamp': publicKeyVerifiedTimestamp, + if (rowid != null) 'rowid': rowid, + }); + } + + UserDiscoveryUserRelationsCompanion copyWith({ + Value? announcedUserId, + Value? fromContactId, + Value? publicKeyVerifiedTimestamp, + Value? rowid, + }) { + return UserDiscoveryUserRelationsCompanion( + announcedUserId: announcedUserId ?? this.announcedUserId, + fromContactId: fromContactId ?? this.fromContactId, + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp ?? this.publicKeyVerifiedTimestamp, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (announcedUserId.present) { + map['announced_user_id'] = Variable(announcedUserId.value); + } + if (fromContactId.present) { + map['from_contact_id'] = Variable(fromContactId.value); + } + if (publicKeyVerifiedTimestamp.present) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp.value, + ); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryUserRelationsCompanion(') + ..write('announcedUserId: $announcedUserId, ') + ..write('fromContactId: $fromContactId, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryOtherPromotions extends Table + with + TableInfo< + UserDiscoveryOtherPromotions, + UserDiscoveryOtherPromotionsData + > { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryOtherPromotions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn fromContactId = GeneratedColumn( + 'from_contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn promotionId = GeneratedColumn( + 'promotion_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn publicId = GeneratedColumn( + 'public_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn threshold = GeneratedColumn( + 'threshold', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn announcementShare = + GeneratedColumn( + 'announcement_share', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn publicKeyVerifiedTimestamp = + GeneratedColumn( + 'public_key_verified_timestamp', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + fromContactId, + promotionId, + publicId, + threshold, + announcementShare, + publicKeyVerifiedTimestamp, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_other_promotions'; + @override + Set get $primaryKey => {fromContactId, publicId}; + @override + UserDiscoveryOtherPromotionsData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryOtherPromotionsData( + fromContactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}from_contact_id'], + )!, + promotionId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}promotion_id'], + )!, + publicId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_id'], + )!, + threshold: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}threshold'], + )!, + announcementShare: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}announcement_share'], + )!, + publicKeyVerifiedTimestamp: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_key_verified_timestamp'], + ), + ); + } + + @override + UserDiscoveryOtherPromotions createAlias(String alias) { + return UserDiscoveryOtherPromotions(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(from_contact_id, public_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryOtherPromotionsData extends DataClass + implements Insertable { + final int fromContactId; + final int promotionId; + final int publicId; + final int threshold; + final i2.Uint8List announcementShare; + final int? publicKeyVerifiedTimestamp; + const UserDiscoveryOtherPromotionsData({ + required this.fromContactId, + required this.promotionId, + required this.publicId, + required this.threshold, + required this.announcementShare, + this.publicKeyVerifiedTimestamp, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['from_contact_id'] = Variable(fromContactId); + map['promotion_id'] = Variable(promotionId); + map['public_id'] = Variable(publicId); + map['threshold'] = Variable(threshold); + map['announcement_share'] = Variable(announcementShare); + if (!nullToAbsent || publicKeyVerifiedTimestamp != null) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp, + ); + } + return map; + } + + UserDiscoveryOtherPromotionsCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryOtherPromotionsCompanion( + fromContactId: Value(fromContactId), + promotionId: Value(promotionId), + publicId: Value(publicId), + threshold: Value(threshold), + announcementShare: Value(announcementShare), + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp == null && nullToAbsent + ? const Value.absent() + : Value(publicKeyVerifiedTimestamp), + ); + } + + factory UserDiscoveryOtherPromotionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryOtherPromotionsData( + fromContactId: serializer.fromJson(json['fromContactId']), + promotionId: serializer.fromJson(json['promotionId']), + publicId: serializer.fromJson(json['publicId']), + threshold: serializer.fromJson(json['threshold']), + announcementShare: serializer.fromJson( + json['announcementShare'], + ), + publicKeyVerifiedTimestamp: serializer.fromJson( + json['publicKeyVerifiedTimestamp'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'fromContactId': serializer.toJson(fromContactId), + 'promotionId': serializer.toJson(promotionId), + 'publicId': serializer.toJson(publicId), + 'threshold': serializer.toJson(threshold), + 'announcementShare': serializer.toJson(announcementShare), + 'publicKeyVerifiedTimestamp': serializer.toJson( + publicKeyVerifiedTimestamp, + ), + }; + } + + UserDiscoveryOtherPromotionsData copyWith({ + int? fromContactId, + int? promotionId, + int? publicId, + int? threshold, + i2.Uint8List? announcementShare, + Value publicKeyVerifiedTimestamp = const Value.absent(), + }) => UserDiscoveryOtherPromotionsData( + fromContactId: fromContactId ?? this.fromContactId, + promotionId: promotionId ?? this.promotionId, + publicId: publicId ?? this.publicId, + threshold: threshold ?? this.threshold, + announcementShare: announcementShare ?? this.announcementShare, + publicKeyVerifiedTimestamp: publicKeyVerifiedTimestamp.present + ? publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + UserDiscoveryOtherPromotionsData copyWithCompanion( + UserDiscoveryOtherPromotionsCompanion data, + ) { + return UserDiscoveryOtherPromotionsData( + fromContactId: data.fromContactId.present + ? data.fromContactId.value + : this.fromContactId, + promotionId: data.promotionId.present + ? data.promotionId.value + : this.promotionId, + publicId: data.publicId.present ? data.publicId.value : this.publicId, + threshold: data.threshold.present ? data.threshold.value : this.threshold, + announcementShare: data.announcementShare.present + ? data.announcementShare.value + : this.announcementShare, + publicKeyVerifiedTimestamp: data.publicKeyVerifiedTimestamp.present + ? data.publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOtherPromotionsData(') + ..write('fromContactId: $fromContactId, ') + ..write('promotionId: $promotionId, ') + ..write('publicId: $publicId, ') + ..write('threshold: $threshold, ') + ..write('announcementShare: $announcementShare, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + fromContactId, + promotionId, + publicId, + threshold, + $driftBlobEquality.hash(announcementShare), + publicKeyVerifiedTimestamp, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryOtherPromotionsData && + other.fromContactId == this.fromContactId && + other.promotionId == this.promotionId && + other.publicId == this.publicId && + other.threshold == this.threshold && + $driftBlobEquality.equals( + other.announcementShare, + this.announcementShare, + ) && + other.publicKeyVerifiedTimestamp == this.publicKeyVerifiedTimestamp); +} + +class UserDiscoveryOtherPromotionsCompanion + extends UpdateCompanion { + final Value fromContactId; + final Value promotionId; + final Value publicId; + final Value threshold; + final Value announcementShare; + final Value publicKeyVerifiedTimestamp; + final Value rowid; + const UserDiscoveryOtherPromotionsCompanion({ + this.fromContactId = const Value.absent(), + this.promotionId = const Value.absent(), + this.publicId = const Value.absent(), + this.threshold = const Value.absent(), + this.announcementShare = const Value.absent(), + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }); + UserDiscoveryOtherPromotionsCompanion.insert({ + required int fromContactId, + required int promotionId, + required int publicId, + required int threshold, + required i2.Uint8List announcementShare, + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }) : fromContactId = Value(fromContactId), + promotionId = Value(promotionId), + publicId = Value(publicId), + threshold = Value(threshold), + announcementShare = Value(announcementShare); + static Insertable custom({ + Expression? fromContactId, + Expression? promotionId, + Expression? publicId, + Expression? threshold, + Expression? announcementShare, + Expression? publicKeyVerifiedTimestamp, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (fromContactId != null) 'from_contact_id': fromContactId, + if (promotionId != null) 'promotion_id': promotionId, + if (publicId != null) 'public_id': publicId, + if (threshold != null) 'threshold': threshold, + if (announcementShare != null) 'announcement_share': announcementShare, + if (publicKeyVerifiedTimestamp != null) + 'public_key_verified_timestamp': publicKeyVerifiedTimestamp, + if (rowid != null) 'rowid': rowid, + }); + } + + UserDiscoveryOtherPromotionsCompanion copyWith({ + Value? fromContactId, + Value? promotionId, + Value? publicId, + Value? threshold, + Value? announcementShare, + Value? publicKeyVerifiedTimestamp, + Value? rowid, + }) { + return UserDiscoveryOtherPromotionsCompanion( + fromContactId: fromContactId ?? this.fromContactId, + promotionId: promotionId ?? this.promotionId, + publicId: publicId ?? this.publicId, + threshold: threshold ?? this.threshold, + announcementShare: announcementShare ?? this.announcementShare, + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp ?? this.publicKeyVerifiedTimestamp, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (fromContactId.present) { + map['from_contact_id'] = Variable(fromContactId.value); + } + if (promotionId.present) { + map['promotion_id'] = Variable(promotionId.value); + } + if (publicId.present) { + map['public_id'] = Variable(publicId.value); + } + if (threshold.present) { + map['threshold'] = Variable(threshold.value); + } + if (announcementShare.present) { + map['announcement_share'] = Variable( + announcementShare.value, + ); + } + if (publicKeyVerifiedTimestamp.present) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp.value, + ); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOtherPromotionsCompanion(') + ..write('fromContactId: $fromContactId, ') + ..write('promotionId: $promotionId, ') + ..write('publicId: $publicId, ') + ..write('threshold: $threshold, ') + ..write('announcementShare: $announcementShare, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryOwnPromotions extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryOwnPromotions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn versionId = GeneratedColumn( + 'version_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn promotion = + GeneratedColumn( + 'promotion', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @override + List get $columns => [versionId, contactId, promotion]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_own_promotions'; + @override + Set get $primaryKey => {versionId}; + @override + UserDiscoveryOwnPromotionsData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryOwnPromotionsData( + versionId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}version_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + promotion: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}promotion'], + )!, + ); + } + + @override + UserDiscoveryOwnPromotions createAlias(String alias) { + return UserDiscoveryOwnPromotions(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryOwnPromotionsData extends DataClass + implements Insertable { + final int versionId; + final int contactId; + final i2.Uint8List promotion; + const UserDiscoveryOwnPromotionsData({ + required this.versionId, + required this.contactId, + required this.promotion, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['version_id'] = Variable(versionId); + map['contact_id'] = Variable(contactId); + map['promotion'] = Variable(promotion); + return map; + } + + UserDiscoveryOwnPromotionsCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryOwnPromotionsCompanion( + versionId: Value(versionId), + contactId: Value(contactId), + promotion: Value(promotion), + ); + } + + factory UserDiscoveryOwnPromotionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryOwnPromotionsData( + versionId: serializer.fromJson(json['versionId']), + contactId: serializer.fromJson(json['contactId']), + promotion: serializer.fromJson(json['promotion']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'versionId': serializer.toJson(versionId), + 'contactId': serializer.toJson(contactId), + 'promotion': serializer.toJson(promotion), + }; + } + + UserDiscoveryOwnPromotionsData copyWith({ + int? versionId, + int? contactId, + i2.Uint8List? promotion, + }) => UserDiscoveryOwnPromotionsData( + versionId: versionId ?? this.versionId, + contactId: contactId ?? this.contactId, + promotion: promotion ?? this.promotion, + ); + UserDiscoveryOwnPromotionsData copyWithCompanion( + UserDiscoveryOwnPromotionsCompanion data, + ) { + return UserDiscoveryOwnPromotionsData( + versionId: data.versionId.present ? data.versionId.value : this.versionId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + promotion: data.promotion.present ? data.promotion.value : this.promotion, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOwnPromotionsData(') + ..write('versionId: $versionId, ') + ..write('contactId: $contactId, ') + ..write('promotion: $promotion') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(versionId, contactId, $driftBlobEquality.hash(promotion)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryOwnPromotionsData && + other.versionId == this.versionId && + other.contactId == this.contactId && + $driftBlobEquality.equals(other.promotion, this.promotion)); +} + +class UserDiscoveryOwnPromotionsCompanion + extends UpdateCompanion { + final Value versionId; + final Value contactId; + final Value promotion; + const UserDiscoveryOwnPromotionsCompanion({ + this.versionId = const Value.absent(), + this.contactId = const Value.absent(), + this.promotion = const Value.absent(), + }); + UserDiscoveryOwnPromotionsCompanion.insert({ + this.versionId = const Value.absent(), + required int contactId, + required i2.Uint8List promotion, + }) : contactId = Value(contactId), + promotion = Value(promotion); + static Insertable custom({ + Expression? versionId, + Expression? contactId, + Expression? promotion, + }) { + return RawValuesInsertable({ + if (versionId != null) 'version_id': versionId, + if (contactId != null) 'contact_id': contactId, + if (promotion != null) 'promotion': promotion, + }); + } + + UserDiscoveryOwnPromotionsCompanion copyWith({ + Value? versionId, + Value? contactId, + Value? promotion, + }) { + return UserDiscoveryOwnPromotionsCompanion( + versionId: versionId ?? this.versionId, + contactId: contactId ?? this.contactId, + promotion: promotion ?? this.promotion, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (versionId.present) { + map['version_id'] = Variable(versionId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (promotion.present) { + map['promotion'] = Variable(promotion.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOwnPromotionsCompanion(') + ..write('versionId: $versionId, ') + ..write('contactId: $contactId, ') + ..write('promotion: $promotion') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryShares extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryShares(this.attachedDatabase, [this._alias]); + late final GeneratedColumn shareId = GeneratedColumn( + 'share_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn share = + GeneratedColumn( + 'share', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + @override + List get $columns => [shareId, share, contactId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_shares'; + @override + Set get $primaryKey => {shareId}; + @override + UserDiscoverySharesData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoverySharesData( + shareId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}share_id'], + )!, + share: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}share'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + ), + ); + } + + @override + UserDiscoveryShares createAlias(String alias) { + return UserDiscoveryShares(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoverySharesData extends DataClass + implements Insertable { + final int shareId; + final i2.Uint8List share; + final int? contactId; + const UserDiscoverySharesData({ + required this.shareId, + required this.share, + this.contactId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['share_id'] = Variable(shareId); + map['share'] = Variable(share); + if (!nullToAbsent || contactId != null) { + map['contact_id'] = Variable(contactId); + } + return map; + } + + UserDiscoverySharesCompanion toCompanion(bool nullToAbsent) { + return UserDiscoverySharesCompanion( + shareId: Value(shareId), + share: Value(share), + contactId: contactId == null && nullToAbsent + ? const Value.absent() + : Value(contactId), + ); + } + + factory UserDiscoverySharesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoverySharesData( + shareId: serializer.fromJson(json['shareId']), + share: serializer.fromJson(json['share']), + contactId: serializer.fromJson(json['contactId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'shareId': serializer.toJson(shareId), + 'share': serializer.toJson(share), + 'contactId': serializer.toJson(contactId), + }; + } + + UserDiscoverySharesData copyWith({ + int? shareId, + i2.Uint8List? share, + Value contactId = const Value.absent(), + }) => UserDiscoverySharesData( + shareId: shareId ?? this.shareId, + share: share ?? this.share, + contactId: contactId.present ? contactId.value : this.contactId, + ); + UserDiscoverySharesData copyWithCompanion(UserDiscoverySharesCompanion data) { + return UserDiscoverySharesData( + shareId: data.shareId.present ? data.shareId.value : this.shareId, + share: data.share.present ? data.share.value : this.share, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoverySharesData(') + ..write('shareId: $shareId, ') + ..write('share: $share, ') + ..write('contactId: $contactId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(shareId, $driftBlobEquality.hash(share), contactId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoverySharesData && + other.shareId == this.shareId && + $driftBlobEquality.equals(other.share, this.share) && + other.contactId == this.contactId); +} + +class UserDiscoverySharesCompanion + extends UpdateCompanion { + final Value shareId; + final Value share; + final Value contactId; + const UserDiscoverySharesCompanion({ + this.shareId = const Value.absent(), + this.share = const Value.absent(), + this.contactId = const Value.absent(), + }); + UserDiscoverySharesCompanion.insert({ + this.shareId = const Value.absent(), + required i2.Uint8List share, + this.contactId = const Value.absent(), + }) : share = Value(share); + static Insertable custom({ + Expression? shareId, + Expression? share, + Expression? contactId, + }) { + return RawValuesInsertable({ + if (shareId != null) 'share_id': shareId, + if (share != null) 'share': share, + if (contactId != null) 'contact_id': contactId, + }); + } + + UserDiscoverySharesCompanion copyWith({ + Value? shareId, + Value? share, + Value? contactId, + }) { + return UserDiscoverySharesCompanion( + shareId: shareId ?? this.shareId, + share: share ?? this.share, + contactId: contactId ?? this.contactId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (shareId.present) { + map['share_id'] = Variable(shareId.value); + } + if (share.present) { + map['share'] = Variable(share.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoverySharesCompanion(') + ..write('shareId: $shareId, ') + ..write('share: $share, ') + ..write('contactId: $contactId') + ..write(')')) + .toString(); + } +} + +class Shortcuts extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Shortcuts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn emoji = GeneratedColumn( + 'emoji', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL UNIQUE', + ); + late final GeneratedColumn usageCounter = GeneratedColumn( + 'usage_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [id, emoji, usageCounter]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'shortcuts'; + @override + Set get $primaryKey => {id}; + @override + ShortcutsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ShortcutsData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + emoji: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}emoji'], + )!, + usageCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}usage_counter'], + )!, + ); + } + + @override + Shortcuts createAlias(String alias) { + return Shortcuts(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class ShortcutsData extends DataClass implements Insertable { + final int id; + final String emoji; + final int usageCounter; + const ShortcutsData({ + required this.id, + required this.emoji, + required this.usageCounter, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['emoji'] = Variable(emoji); + map['usage_counter'] = Variable(usageCounter); + return map; + } + + ShortcutsCompanion toCompanion(bool nullToAbsent) { + return ShortcutsCompanion( + id: Value(id), + emoji: Value(emoji), + usageCounter: Value(usageCounter), + ); + } + + factory ShortcutsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ShortcutsData( + id: serializer.fromJson(json['id']), + emoji: serializer.fromJson(json['emoji']), + usageCounter: serializer.fromJson(json['usageCounter']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'emoji': serializer.toJson(emoji), + 'usageCounter': serializer.toJson(usageCounter), + }; + } + + ShortcutsData copyWith({int? id, String? emoji, int? usageCounter}) => + ShortcutsData( + id: id ?? this.id, + emoji: emoji ?? this.emoji, + usageCounter: usageCounter ?? this.usageCounter, + ); + ShortcutsData copyWithCompanion(ShortcutsCompanion data) { + return ShortcutsData( + id: data.id.present ? data.id.value : this.id, + emoji: data.emoji.present ? data.emoji.value : this.emoji, + usageCounter: data.usageCounter.present + ? data.usageCounter.value + : this.usageCounter, + ); + } + + @override + String toString() { + return (StringBuffer('ShortcutsData(') + ..write('id: $id, ') + ..write('emoji: $emoji, ') + ..write('usageCounter: $usageCounter') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, emoji, usageCounter); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ShortcutsData && + other.id == this.id && + other.emoji == this.emoji && + other.usageCounter == this.usageCounter); +} + +class ShortcutsCompanion extends UpdateCompanion { + final Value id; + final Value emoji; + final Value usageCounter; + const ShortcutsCompanion({ + this.id = const Value.absent(), + this.emoji = const Value.absent(), + this.usageCounter = const Value.absent(), + }); + ShortcutsCompanion.insert({ + this.id = const Value.absent(), + required String emoji, + this.usageCounter = const Value.absent(), + }) : emoji = Value(emoji); + static Insertable custom({ + Expression? id, + Expression? emoji, + Expression? usageCounter, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (emoji != null) 'emoji': emoji, + if (usageCounter != null) 'usage_counter': usageCounter, + }); + } + + ShortcutsCompanion copyWith({ + Value? id, + Value? emoji, + Value? usageCounter, + }) { + return ShortcutsCompanion( + id: id ?? this.id, + emoji: emoji ?? this.emoji, + usageCounter: usageCounter ?? this.usageCounter, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (emoji.present) { + map['emoji'] = Variable(emoji.value); + } + if (usageCounter.present) { + map['usage_counter'] = Variable(usageCounter.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ShortcutsCompanion(') + ..write('id: $id, ') + ..write('emoji: $emoji, ') + ..write('usageCounter: $usageCounter') + ..write(')')) + .toString(); + } +} + +class ShortcutMembers extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + ShortcutMembers(this.attachedDatabase, [this._alias]); + late final GeneratedColumn shortcutId = GeneratedColumn( + 'shortcut_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES shortcuts(id)ON DELETE CASCADE', + ); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + @override + List get $columns => [shortcutId, groupId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'shortcut_members'; + @override + Set get $primaryKey => {shortcutId, groupId}; + @override + ShortcutMembersData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ShortcutMembersData( + shortcutId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}shortcut_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + ); + } + + @override + ShortcutMembers createAlias(String alias) { + return ShortcutMembers(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(shortcut_id, group_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class ShortcutMembersData extends DataClass + implements Insertable { + final int shortcutId; + final String groupId; + const ShortcutMembersData({required this.shortcutId, required this.groupId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shortcut_id'] = Variable(shortcutId); + map['group_id'] = Variable(groupId); + return map; + } + + ShortcutMembersCompanion toCompanion(bool nullToAbsent) { + return ShortcutMembersCompanion( + shortcutId: Value(shortcutId), + groupId: Value(groupId), + ); + } + + factory ShortcutMembersData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ShortcutMembersData( + shortcutId: serializer.fromJson(json['shortcutId']), + groupId: serializer.fromJson(json['groupId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'shortcutId': serializer.toJson(shortcutId), + 'groupId': serializer.toJson(groupId), + }; + } + + ShortcutMembersData copyWith({int? shortcutId, String? groupId}) => + ShortcutMembersData( + shortcutId: shortcutId ?? this.shortcutId, + groupId: groupId ?? this.groupId, + ); + ShortcutMembersData copyWithCompanion(ShortcutMembersCompanion data) { + return ShortcutMembersData( + shortcutId: data.shortcutId.present + ? data.shortcutId.value + : this.shortcutId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + ); + } + + @override + String toString() { + return (StringBuffer('ShortcutMembersData(') + ..write('shortcutId: $shortcutId, ') + ..write('groupId: $groupId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(shortcutId, groupId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ShortcutMembersData && + other.shortcutId == this.shortcutId && + other.groupId == this.groupId); +} + +class ShortcutMembersCompanion extends UpdateCompanion { + final Value shortcutId; + final Value groupId; + final Value rowid; + const ShortcutMembersCompanion({ + this.shortcutId = const Value.absent(), + this.groupId = const Value.absent(), + this.rowid = const Value.absent(), + }); + ShortcutMembersCompanion.insert({ + required int shortcutId, + required String groupId, + this.rowid = const Value.absent(), + }) : shortcutId = Value(shortcutId), + groupId = Value(groupId); + static Insertable custom({ + Expression? shortcutId, + Expression? groupId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (shortcutId != null) 'shortcut_id': shortcutId, + if (groupId != null) 'group_id': groupId, + if (rowid != null) 'rowid': rowid, + }); + } + + ShortcutMembersCompanion copyWith({ + Value? shortcutId, + Value? groupId, + Value? rowid, + }) { + return ShortcutMembersCompanion( + shortcutId: shortcutId ?? this.shortcutId, + groupId: groupId ?? this.groupId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (shortcutId.present) { + map['shortcut_id'] = Variable(shortcutId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ShortcutMembersCompanion(') + ..write('shortcutId: $shortcutId, ') + ..write('groupId: $groupId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV21 extends GeneratedDatabase { + DatabaseAtV21(QueryExecutor e) : super(e); + late final Contacts contacts = Contacts(this); + late final Groups groups = Groups(this); + late final MediaFiles mediaFiles = MediaFiles(this); + late final Messages messages = Messages(this); + late final MessageHistories messageHistories = MessageHistories(this); + late final Reactions reactions = Reactions(this); + late final GroupMembers groupMembers = GroupMembers(this); + late final Receipts receipts = Receipts(this); + late final ReceivedReceipts receivedReceipts = ReceivedReceipts(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 SignalSignedPreKeyStores signalSignedPreKeyStores = + SignalSignedPreKeyStores(this); + late final MessageActions messageActions = MessageActions(this); + late final GroupHistories groupHistories = GroupHistories(this); + late final KeyVerifications keyVerifications = KeyVerifications(this); + late final VerificationTokens verificationTokens = VerificationTokens(this); + late final UserDiscoveryAnnouncedUsers userDiscoveryAnnouncedUsers = + UserDiscoveryAnnouncedUsers(this); + late final UserDiscoveryUserRelations userDiscoveryUserRelations = + UserDiscoveryUserRelations(this); + late final UserDiscoveryOtherPromotions userDiscoveryOtherPromotions = + UserDiscoveryOtherPromotions(this); + late final UserDiscoveryOwnPromotions userDiscoveryOwnPromotions = + UserDiscoveryOwnPromotions(this); + late final UserDiscoveryShares userDiscoveryShares = UserDiscoveryShares( + this, + ); + late final Shortcuts shortcuts = Shortcuts(this); + late final ShortcutMembers shortcutMembers = ShortcutMembers(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + contacts, + groups, + mediaFiles, + messages, + messageHistories, + reactions, + groupMembers, + receipts, + receivedReceipts, + signalIdentityKeyStores, + signalPreKeyStores, + signalSenderKeyStores, + signalSessionStores, + signalSignedPreKeyStores, + messageActions, + groupHistories, + keyVerifications, + verificationTokens, + userDiscoveryAnnouncedUsers, + userDiscoveryUserRelations, + userDiscoveryOtherPromotions, + userDiscoveryOwnPromotions, + userDiscoveryShares, + shortcuts, + shortcutMembers, + ]; + @override + StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('messages', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'media_files', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('messages', kind: UpdateKind.update)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_histories', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_histories', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('reactions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('reactions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('group_members', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('receipts', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('receipts', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_actions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_actions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('group_histories', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('key_verifications', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('key_verifications', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_discovery_announced_users', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_user_relations', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_user_relations', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_other_promotions', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_own_promotions', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('user_discovery_shares', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'shortcuts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('shortcut_members', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('shortcut_members', kind: UpdateKind.delete)], + ), + ]); + @override + int get schemaVersion => 21; +} diff --git a/test/drift/twonly_db/generated/schema_v22.dart b/test/drift/twonly_db/generated/schema_v22.dart new file mode 100644 index 00000000..8eda920e --- /dev/null +++ b/test/drift/twonly_db/generated/schema_v22.dart @@ -0,0 +1,11056 @@ +// dart format width=80 +import 'dart:typed_data' as i2; +// GENERATED BY drift_dev, DO NOT MODIFY. +// ignore_for_file: type=lint,unused_import +// +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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn displayName = GeneratedColumn( + 'display_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn nickName = GeneratedColumn( + 'nick_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn avatarSvgCompressed = + GeneratedColumn( + 'avatar_svg_compressed', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn senderProfileCounter = GeneratedColumn( + 'sender_profile_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn accepted = GeneratedColumn( + 'accepted', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (accepted IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn deletedByUser = GeneratedColumn( + 'deleted_by_user', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (deleted_by_user IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn requested = GeneratedColumn( + 'requested', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (requested IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn blocked = GeneratedColumn( + 'blocked', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (blocked IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn verified = GeneratedColumn( + 'verified', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (verified IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn accountDeleted = GeneratedColumn( + 'account_deleted', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (account_deleted IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn userDiscoveryVersion = + GeneratedColumn( + 'user_discovery_version', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn userDiscoveryExcluded = GeneratedColumn( + 'user_discovery_excluded', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (user_discovery_excluded IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn userDiscoveryManualApproved = + GeneratedColumn( + 'user_discovery_manual_approved', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NULL DEFAULT 0 CHECK (user_discovery_manual_approved IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn recoveryIsTrustedFriend = + GeneratedColumn( + 'recovery_is_trusted_friend', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (recovery_is_trusted_friend IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn recoveryLastHeartbeat = GeneratedColumn( + 'recovery_last_heartbeat', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn recoverySecretShare = + GeneratedColumn( + 'recovery_secret_share', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn recoveryContactsSecretShare = + GeneratedColumn( + 'recovery_contacts_secret_share', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn recoveryContactsLastHeartbeat = + GeneratedColumn( + 'recovery_contacts_last_heartbeat', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn recoveryContactsThreshold = + GeneratedColumn( + 'recovery_contacts_threshold', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn askForFriendPromotions = GeneratedColumn( + 'ask_for_friend_promotions', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL CHECK (ask_for_friend_promotions IN (0, 1))', + ); + late final GeneratedColumn mediaSendCounter = GeneratedColumn( + 'media_send_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn mediaReceivedCounter = GeneratedColumn( + 'media_received_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + userId, + username, + displayName, + nickName, + avatarSvgCompressed, + senderProfileCounter, + accepted, + deletedByUser, + requested, + blocked, + verified, + accountDeleted, + createdAt, + userDiscoveryVersion, + userDiscoveryExcluded, + userDiscoveryManualApproved, + recoveryIsTrustedFriend, + recoveryLastHeartbeat, + recoverySecretShare, + recoveryContactsSecretShare, + recoveryContactsLastHeartbeat, + recoveryContactsThreshold, + askForFriendPromotions, + mediaSendCounter, + mediaReceivedCounter, + ]; + @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'], + ), + avatarSvgCompressed: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}avatar_svg_compressed'], + ), + senderProfileCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}sender_profile_counter'], + )!, + accepted: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}accepted'], + )!, + deletedByUser: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}deleted_by_user'], + )!, + requested: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}requested'], + )!, + blocked: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}blocked'], + )!, + verified: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}verified'], + )!, + accountDeleted: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}account_deleted'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + userDiscoveryVersion: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}user_discovery_version'], + ), + userDiscoveryExcluded: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}user_discovery_excluded'], + )!, + userDiscoveryManualApproved: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}user_discovery_manual_approved'], + ), + recoveryIsTrustedFriend: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_is_trusted_friend'], + )!, + recoveryLastHeartbeat: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_last_heartbeat'], + ), + recoverySecretShare: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}recovery_secret_share'], + ), + recoveryContactsSecretShare: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}recovery_contacts_secret_share'], + ), + recoveryContactsLastHeartbeat: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_contacts_last_heartbeat'], + ), + recoveryContactsThreshold: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recovery_contacts_threshold'], + ), + askForFriendPromotions: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ask_for_friend_promotions'], + ), + mediaSendCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_send_counter'], + )!, + mediaReceivedCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_received_counter'], + )!, + ); + } + + @override + Contacts createAlias(String alias) { + return Contacts(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(user_id)']; + @override + bool get dontWriteConstraints => true; +} + +class ContactsData extends DataClass implements Insertable { + final int userId; + final String username; + final String? displayName; + final String? nickName; + final i2.Uint8List? avatarSvgCompressed; + final int senderProfileCounter; + final int accepted; + final int deletedByUser; + final int requested; + final int blocked; + final int verified; + final int accountDeleted; + final int createdAt; + final i2.Uint8List? userDiscoveryVersion; + final int userDiscoveryExcluded; + final int? userDiscoveryManualApproved; + final int recoveryIsTrustedFriend; + final int? recoveryLastHeartbeat; + final i2.Uint8List? recoverySecretShare; + final i2.Uint8List? recoveryContactsSecretShare; + final int? recoveryContactsLastHeartbeat; + final int? recoveryContactsThreshold; + final int? askForFriendPromotions; + final int mediaSendCounter; + final int mediaReceivedCounter; + const ContactsData({ + required this.userId, + required this.username, + this.displayName, + this.nickName, + this.avatarSvgCompressed, + required this.senderProfileCounter, + required this.accepted, + required this.deletedByUser, + required this.requested, + required this.blocked, + required this.verified, + required this.accountDeleted, + required this.createdAt, + this.userDiscoveryVersion, + required this.userDiscoveryExcluded, + this.userDiscoveryManualApproved, + required this.recoveryIsTrustedFriend, + this.recoveryLastHeartbeat, + this.recoverySecretShare, + this.recoveryContactsSecretShare, + this.recoveryContactsLastHeartbeat, + this.recoveryContactsThreshold, + this.askForFriendPromotions, + required this.mediaSendCounter, + required this.mediaReceivedCounter, + }); + @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 || avatarSvgCompressed != null) { + map['avatar_svg_compressed'] = Variable( + avatarSvgCompressed, + ); + } + map['sender_profile_counter'] = Variable(senderProfileCounter); + map['accepted'] = Variable(accepted); + map['deleted_by_user'] = Variable(deletedByUser); + map['requested'] = Variable(requested); + map['blocked'] = Variable(blocked); + map['verified'] = Variable(verified); + map['account_deleted'] = Variable(accountDeleted); + map['created_at'] = Variable(createdAt); + if (!nullToAbsent || userDiscoveryVersion != null) { + map['user_discovery_version'] = Variable( + userDiscoveryVersion, + ); + } + map['user_discovery_excluded'] = Variable(userDiscoveryExcluded); + if (!nullToAbsent || userDiscoveryManualApproved != null) { + map['user_discovery_manual_approved'] = Variable( + userDiscoveryManualApproved, + ); + } + map['recovery_is_trusted_friend'] = Variable(recoveryIsTrustedFriend); + if (!nullToAbsent || recoveryLastHeartbeat != null) { + map['recovery_last_heartbeat'] = Variable(recoveryLastHeartbeat); + } + if (!nullToAbsent || recoverySecretShare != null) { + map['recovery_secret_share'] = Variable( + recoverySecretShare, + ); + } + if (!nullToAbsent || recoveryContactsSecretShare != null) { + map['recovery_contacts_secret_share'] = Variable( + recoveryContactsSecretShare, + ); + } + if (!nullToAbsent || recoveryContactsLastHeartbeat != null) { + map['recovery_contacts_last_heartbeat'] = Variable( + recoveryContactsLastHeartbeat, + ); + } + if (!nullToAbsent || recoveryContactsThreshold != null) { + map['recovery_contacts_threshold'] = Variable( + recoveryContactsThreshold, + ); + } + if (!nullToAbsent || askForFriendPromotions != null) { + map['ask_for_friend_promotions'] = Variable(askForFriendPromotions); + } + map['media_send_counter'] = Variable(mediaSendCounter); + map['media_received_counter'] = Variable(mediaReceivedCounter); + 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), + avatarSvgCompressed: avatarSvgCompressed == null && nullToAbsent + ? const Value.absent() + : Value(avatarSvgCompressed), + senderProfileCounter: Value(senderProfileCounter), + accepted: Value(accepted), + deletedByUser: Value(deletedByUser), + requested: Value(requested), + blocked: Value(blocked), + verified: Value(verified), + accountDeleted: Value(accountDeleted), + createdAt: Value(createdAt), + userDiscoveryVersion: userDiscoveryVersion == null && nullToAbsent + ? const Value.absent() + : Value(userDiscoveryVersion), + userDiscoveryExcluded: Value(userDiscoveryExcluded), + userDiscoveryManualApproved: + userDiscoveryManualApproved == null && nullToAbsent + ? const Value.absent() + : Value(userDiscoveryManualApproved), + recoveryIsTrustedFriend: Value(recoveryIsTrustedFriend), + recoveryLastHeartbeat: recoveryLastHeartbeat == null && nullToAbsent + ? const Value.absent() + : Value(recoveryLastHeartbeat), + recoverySecretShare: recoverySecretShare == null && nullToAbsent + ? const Value.absent() + : Value(recoverySecretShare), + recoveryContactsSecretShare: + recoveryContactsSecretShare == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsSecretShare), + recoveryContactsLastHeartbeat: + recoveryContactsLastHeartbeat == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsLastHeartbeat), + recoveryContactsThreshold: + recoveryContactsThreshold == null && nullToAbsent + ? const Value.absent() + : Value(recoveryContactsThreshold), + askForFriendPromotions: askForFriendPromotions == null && nullToAbsent + ? const Value.absent() + : Value(askForFriendPromotions), + mediaSendCounter: Value(mediaSendCounter), + mediaReceivedCounter: Value(mediaReceivedCounter), + ); + } + + 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']), + avatarSvgCompressed: serializer.fromJson( + json['avatarSvgCompressed'], + ), + senderProfileCounter: serializer.fromJson( + json['senderProfileCounter'], + ), + accepted: serializer.fromJson(json['accepted']), + deletedByUser: serializer.fromJson(json['deletedByUser']), + requested: serializer.fromJson(json['requested']), + blocked: serializer.fromJson(json['blocked']), + verified: serializer.fromJson(json['verified']), + accountDeleted: serializer.fromJson(json['accountDeleted']), + createdAt: serializer.fromJson(json['createdAt']), + userDiscoveryVersion: serializer.fromJson( + json['userDiscoveryVersion'], + ), + userDiscoveryExcluded: serializer.fromJson( + json['userDiscoveryExcluded'], + ), + userDiscoveryManualApproved: serializer.fromJson( + json['userDiscoveryManualApproved'], + ), + recoveryIsTrustedFriend: serializer.fromJson( + json['recoveryIsTrustedFriend'], + ), + recoveryLastHeartbeat: serializer.fromJson( + json['recoveryLastHeartbeat'], + ), + recoverySecretShare: serializer.fromJson( + json['recoverySecretShare'], + ), + recoveryContactsSecretShare: serializer.fromJson( + json['recoveryContactsSecretShare'], + ), + recoveryContactsLastHeartbeat: serializer.fromJson( + json['recoveryContactsLastHeartbeat'], + ), + recoveryContactsThreshold: serializer.fromJson( + json['recoveryContactsThreshold'], + ), + askForFriendPromotions: serializer.fromJson( + json['askForFriendPromotions'], + ), + mediaSendCounter: serializer.fromJson(json['mediaSendCounter']), + mediaReceivedCounter: serializer.fromJson( + json['mediaReceivedCounter'], + ), + ); + } + @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), + 'avatarSvgCompressed': serializer.toJson( + avatarSvgCompressed, + ), + 'senderProfileCounter': serializer.toJson(senderProfileCounter), + 'accepted': serializer.toJson(accepted), + 'deletedByUser': serializer.toJson(deletedByUser), + 'requested': serializer.toJson(requested), + 'blocked': serializer.toJson(blocked), + 'verified': serializer.toJson(verified), + 'accountDeleted': serializer.toJson(accountDeleted), + 'createdAt': serializer.toJson(createdAt), + 'userDiscoveryVersion': serializer.toJson( + userDiscoveryVersion, + ), + 'userDiscoveryExcluded': serializer.toJson(userDiscoveryExcluded), + 'userDiscoveryManualApproved': serializer.toJson( + userDiscoveryManualApproved, + ), + 'recoveryIsTrustedFriend': serializer.toJson( + recoveryIsTrustedFriend, + ), + 'recoveryLastHeartbeat': serializer.toJson(recoveryLastHeartbeat), + 'recoverySecretShare': serializer.toJson( + recoverySecretShare, + ), + 'recoveryContactsSecretShare': serializer.toJson( + recoveryContactsSecretShare, + ), + 'recoveryContactsLastHeartbeat': serializer.toJson( + recoveryContactsLastHeartbeat, + ), + 'recoveryContactsThreshold': serializer.toJson( + recoveryContactsThreshold, + ), + 'askForFriendPromotions': serializer.toJson(askForFriendPromotions), + 'mediaSendCounter': serializer.toJson(mediaSendCounter), + 'mediaReceivedCounter': serializer.toJson(mediaReceivedCounter), + }; + } + + ContactsData copyWith({ + int? userId, + String? username, + Value displayName = const Value.absent(), + Value nickName = const Value.absent(), + Value avatarSvgCompressed = const Value.absent(), + int? senderProfileCounter, + int? accepted, + int? deletedByUser, + int? requested, + int? blocked, + int? verified, + int? accountDeleted, + int? createdAt, + Value userDiscoveryVersion = const Value.absent(), + int? userDiscoveryExcluded, + Value userDiscoveryManualApproved = const Value.absent(), + int? recoveryIsTrustedFriend, + Value recoveryLastHeartbeat = const Value.absent(), + Value recoverySecretShare = const Value.absent(), + Value recoveryContactsSecretShare = const Value.absent(), + Value recoveryContactsLastHeartbeat = const Value.absent(), + Value recoveryContactsThreshold = const Value.absent(), + Value askForFriendPromotions = const Value.absent(), + int? mediaSendCounter, + int? mediaReceivedCounter, + }) => ContactsData( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName.present ? displayName.value : this.displayName, + nickName: nickName.present ? nickName.value : this.nickName, + avatarSvgCompressed: avatarSvgCompressed.present + ? avatarSvgCompressed.value + : this.avatarSvgCompressed, + senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter, + accepted: accepted ?? this.accepted, + deletedByUser: deletedByUser ?? this.deletedByUser, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + accountDeleted: accountDeleted ?? this.accountDeleted, + createdAt: createdAt ?? this.createdAt, + userDiscoveryVersion: userDiscoveryVersion.present + ? userDiscoveryVersion.value + : this.userDiscoveryVersion, + userDiscoveryExcluded: userDiscoveryExcluded ?? this.userDiscoveryExcluded, + userDiscoveryManualApproved: userDiscoveryManualApproved.present + ? userDiscoveryManualApproved.value + : this.userDiscoveryManualApproved, + recoveryIsTrustedFriend: + recoveryIsTrustedFriend ?? this.recoveryIsTrustedFriend, + recoveryLastHeartbeat: recoveryLastHeartbeat.present + ? recoveryLastHeartbeat.value + : this.recoveryLastHeartbeat, + recoverySecretShare: recoverySecretShare.present + ? recoverySecretShare.value + : this.recoverySecretShare, + recoveryContactsSecretShare: recoveryContactsSecretShare.present + ? recoveryContactsSecretShare.value + : this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat.present + ? recoveryContactsLastHeartbeat.value + : this.recoveryContactsLastHeartbeat, + recoveryContactsThreshold: recoveryContactsThreshold.present + ? recoveryContactsThreshold.value + : this.recoveryContactsThreshold, + askForFriendPromotions: askForFriendPromotions.present + ? askForFriendPromotions.value + : this.askForFriendPromotions, + mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter, + mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter, + ); + 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, + avatarSvgCompressed: data.avatarSvgCompressed.present + ? data.avatarSvgCompressed.value + : this.avatarSvgCompressed, + senderProfileCounter: data.senderProfileCounter.present + ? data.senderProfileCounter.value + : this.senderProfileCounter, + accepted: data.accepted.present ? data.accepted.value : this.accepted, + deletedByUser: data.deletedByUser.present + ? data.deletedByUser.value + : this.deletedByUser, + 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, + accountDeleted: data.accountDeleted.present + ? data.accountDeleted.value + : this.accountDeleted, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + userDiscoveryVersion: data.userDiscoveryVersion.present + ? data.userDiscoveryVersion.value + : this.userDiscoveryVersion, + userDiscoveryExcluded: data.userDiscoveryExcluded.present + ? data.userDiscoveryExcluded.value + : this.userDiscoveryExcluded, + userDiscoveryManualApproved: data.userDiscoveryManualApproved.present + ? data.userDiscoveryManualApproved.value + : this.userDiscoveryManualApproved, + recoveryIsTrustedFriend: data.recoveryIsTrustedFriend.present + ? data.recoveryIsTrustedFriend.value + : this.recoveryIsTrustedFriend, + recoveryLastHeartbeat: data.recoveryLastHeartbeat.present + ? data.recoveryLastHeartbeat.value + : this.recoveryLastHeartbeat, + recoverySecretShare: data.recoverySecretShare.present + ? data.recoverySecretShare.value + : this.recoverySecretShare, + recoveryContactsSecretShare: data.recoveryContactsSecretShare.present + ? data.recoveryContactsSecretShare.value + : this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: data.recoveryContactsLastHeartbeat.present + ? data.recoveryContactsLastHeartbeat.value + : this.recoveryContactsLastHeartbeat, + recoveryContactsThreshold: data.recoveryContactsThreshold.present + ? data.recoveryContactsThreshold.value + : this.recoveryContactsThreshold, + askForFriendPromotions: data.askForFriendPromotions.present + ? data.askForFriendPromotions.value + : this.askForFriendPromotions, + mediaSendCounter: data.mediaSendCounter.present + ? data.mediaSendCounter.value + : this.mediaSendCounter, + mediaReceivedCounter: data.mediaReceivedCounter.present + ? data.mediaReceivedCounter.value + : this.mediaReceivedCounter, + ); + } + + @override + String toString() { + return (StringBuffer('ContactsData(') + ..write('userId: $userId, ') + ..write('username: $username, ') + ..write('displayName: $displayName, ') + ..write('nickName: $nickName, ') + ..write('avatarSvgCompressed: $avatarSvgCompressed, ') + ..write('senderProfileCounter: $senderProfileCounter, ') + ..write('accepted: $accepted, ') + ..write('deletedByUser: $deletedByUser, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('accountDeleted: $accountDeleted, ') + ..write('createdAt: $createdAt, ') + ..write('userDiscoveryVersion: $userDiscoveryVersion, ') + ..write('userDiscoveryExcluded: $userDiscoveryExcluded, ') + ..write('userDiscoveryManualApproved: $userDiscoveryManualApproved, ') + ..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ') + ..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ') + ..write('recoverySecretShare: $recoverySecretShare, ') + ..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ') + ..write( + 'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ', + ) + ..write('recoveryContactsThreshold: $recoveryContactsThreshold, ') + ..write('askForFriendPromotions: $askForFriendPromotions, ') + ..write('mediaSendCounter: $mediaSendCounter, ') + ..write('mediaReceivedCounter: $mediaReceivedCounter') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + userId, + username, + displayName, + nickName, + $driftBlobEquality.hash(avatarSvgCompressed), + senderProfileCounter, + accepted, + deletedByUser, + requested, + blocked, + verified, + accountDeleted, + createdAt, + $driftBlobEquality.hash(userDiscoveryVersion), + userDiscoveryExcluded, + userDiscoveryManualApproved, + recoveryIsTrustedFriend, + recoveryLastHeartbeat, + $driftBlobEquality.hash(recoverySecretShare), + $driftBlobEquality.hash(recoveryContactsSecretShare), + recoveryContactsLastHeartbeat, + recoveryContactsThreshold, + askForFriendPromotions, + mediaSendCounter, + mediaReceivedCounter, + ]); + @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 && + $driftBlobEquality.equals( + other.avatarSvgCompressed, + this.avatarSvgCompressed, + ) && + other.senderProfileCounter == this.senderProfileCounter && + other.accepted == this.accepted && + other.deletedByUser == this.deletedByUser && + other.requested == this.requested && + other.blocked == this.blocked && + other.verified == this.verified && + other.accountDeleted == this.accountDeleted && + other.createdAt == this.createdAt && + $driftBlobEquality.equals( + other.userDiscoveryVersion, + this.userDiscoveryVersion, + ) && + other.userDiscoveryExcluded == this.userDiscoveryExcluded && + other.userDiscoveryManualApproved == + this.userDiscoveryManualApproved && + other.recoveryIsTrustedFriend == this.recoveryIsTrustedFriend && + other.recoveryLastHeartbeat == this.recoveryLastHeartbeat && + $driftBlobEquality.equals( + other.recoverySecretShare, + this.recoverySecretShare, + ) && + $driftBlobEquality.equals( + other.recoveryContactsSecretShare, + this.recoveryContactsSecretShare, + ) && + other.recoveryContactsLastHeartbeat == + this.recoveryContactsLastHeartbeat && + other.recoveryContactsThreshold == this.recoveryContactsThreshold && + other.askForFriendPromotions == this.askForFriendPromotions && + other.mediaSendCounter == this.mediaSendCounter && + other.mediaReceivedCounter == this.mediaReceivedCounter); +} + +class ContactsCompanion extends UpdateCompanion { + final Value userId; + final Value username; + final Value displayName; + final Value nickName; + final Value avatarSvgCompressed; + final Value senderProfileCounter; + final Value accepted; + final Value deletedByUser; + final Value requested; + final Value blocked; + final Value verified; + final Value accountDeleted; + final Value createdAt; + final Value userDiscoveryVersion; + final Value userDiscoveryExcluded; + final Value userDiscoveryManualApproved; + final Value recoveryIsTrustedFriend; + final Value recoveryLastHeartbeat; + final Value recoverySecretShare; + final Value recoveryContactsSecretShare; + final Value recoveryContactsLastHeartbeat; + final Value recoveryContactsThreshold; + final Value askForFriendPromotions; + final Value mediaSendCounter; + final Value mediaReceivedCounter; + const ContactsCompanion({ + this.userId = const Value.absent(), + this.username = const Value.absent(), + this.displayName = const Value.absent(), + this.nickName = const Value.absent(), + this.avatarSvgCompressed = const Value.absent(), + this.senderProfileCounter = const Value.absent(), + this.accepted = const Value.absent(), + this.deletedByUser = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.accountDeleted = const Value.absent(), + this.createdAt = const Value.absent(), + this.userDiscoveryVersion = const Value.absent(), + this.userDiscoveryExcluded = const Value.absent(), + this.userDiscoveryManualApproved = const Value.absent(), + this.recoveryIsTrustedFriend = const Value.absent(), + this.recoveryLastHeartbeat = const Value.absent(), + this.recoverySecretShare = const Value.absent(), + this.recoveryContactsSecretShare = const Value.absent(), + this.recoveryContactsLastHeartbeat = const Value.absent(), + this.recoveryContactsThreshold = const Value.absent(), + this.askForFriendPromotions = const Value.absent(), + this.mediaSendCounter = const Value.absent(), + this.mediaReceivedCounter = const Value.absent(), + }); + ContactsCompanion.insert({ + this.userId = const Value.absent(), + required String username, + this.displayName = const Value.absent(), + this.nickName = const Value.absent(), + this.avatarSvgCompressed = const Value.absent(), + this.senderProfileCounter = const Value.absent(), + this.accepted = const Value.absent(), + this.deletedByUser = const Value.absent(), + this.requested = const Value.absent(), + this.blocked = const Value.absent(), + this.verified = const Value.absent(), + this.accountDeleted = const Value.absent(), + this.createdAt = const Value.absent(), + this.userDiscoveryVersion = const Value.absent(), + this.userDiscoveryExcluded = const Value.absent(), + this.userDiscoveryManualApproved = const Value.absent(), + this.recoveryIsTrustedFriend = const Value.absent(), + this.recoveryLastHeartbeat = const Value.absent(), + this.recoverySecretShare = const Value.absent(), + this.recoveryContactsSecretShare = const Value.absent(), + this.recoveryContactsLastHeartbeat = const Value.absent(), + this.recoveryContactsThreshold = const Value.absent(), + this.askForFriendPromotions = const Value.absent(), + this.mediaSendCounter = const Value.absent(), + this.mediaReceivedCounter = const Value.absent(), + }) : username = Value(username); + static Insertable custom({ + Expression? userId, + Expression? username, + Expression? displayName, + Expression? nickName, + Expression? avatarSvgCompressed, + Expression? senderProfileCounter, + Expression? accepted, + Expression? deletedByUser, + Expression? requested, + Expression? blocked, + Expression? verified, + Expression? accountDeleted, + Expression? createdAt, + Expression? userDiscoveryVersion, + Expression? userDiscoveryExcluded, + Expression? userDiscoveryManualApproved, + Expression? recoveryIsTrustedFriend, + Expression? recoveryLastHeartbeat, + Expression? recoverySecretShare, + Expression? recoveryContactsSecretShare, + Expression? recoveryContactsLastHeartbeat, + Expression? recoveryContactsThreshold, + Expression? askForFriendPromotions, + Expression? mediaSendCounter, + Expression? mediaReceivedCounter, + }) { + 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 (avatarSvgCompressed != null) + 'avatar_svg_compressed': avatarSvgCompressed, + if (senderProfileCounter != null) + 'sender_profile_counter': senderProfileCounter, + if (accepted != null) 'accepted': accepted, + if (deletedByUser != null) 'deleted_by_user': deletedByUser, + if (requested != null) 'requested': requested, + if (blocked != null) 'blocked': blocked, + if (verified != null) 'verified': verified, + if (accountDeleted != null) 'account_deleted': accountDeleted, + if (createdAt != null) 'created_at': createdAt, + if (userDiscoveryVersion != null) + 'user_discovery_version': userDiscoveryVersion, + if (userDiscoveryExcluded != null) + 'user_discovery_excluded': userDiscoveryExcluded, + if (userDiscoveryManualApproved != null) + 'user_discovery_manual_approved': userDiscoveryManualApproved, + if (recoveryIsTrustedFriend != null) + 'recovery_is_trusted_friend': recoveryIsTrustedFriend, + if (recoveryLastHeartbeat != null) + 'recovery_last_heartbeat': recoveryLastHeartbeat, + if (recoverySecretShare != null) + 'recovery_secret_share': recoverySecretShare, + if (recoveryContactsSecretShare != null) + 'recovery_contacts_secret_share': recoveryContactsSecretShare, + if (recoveryContactsLastHeartbeat != null) + 'recovery_contacts_last_heartbeat': recoveryContactsLastHeartbeat, + if (recoveryContactsThreshold != null) + 'recovery_contacts_threshold': recoveryContactsThreshold, + if (askForFriendPromotions != null) + 'ask_for_friend_promotions': askForFriendPromotions, + if (mediaSendCounter != null) 'media_send_counter': mediaSendCounter, + if (mediaReceivedCounter != null) + 'media_received_counter': mediaReceivedCounter, + }); + } + + ContactsCompanion copyWith({ + Value? userId, + Value? username, + Value? displayName, + Value? nickName, + Value? avatarSvgCompressed, + Value? senderProfileCounter, + Value? accepted, + Value? deletedByUser, + Value? requested, + Value? blocked, + Value? verified, + Value? accountDeleted, + Value? createdAt, + Value? userDiscoveryVersion, + Value? userDiscoveryExcluded, + Value? userDiscoveryManualApproved, + Value? recoveryIsTrustedFriend, + Value? recoveryLastHeartbeat, + Value? recoverySecretShare, + Value? recoveryContactsSecretShare, + Value? recoveryContactsLastHeartbeat, + Value? recoveryContactsThreshold, + Value? askForFriendPromotions, + Value? mediaSendCounter, + Value? mediaReceivedCounter, + }) { + return ContactsCompanion( + userId: userId ?? this.userId, + username: username ?? this.username, + displayName: displayName ?? this.displayName, + nickName: nickName ?? this.nickName, + avatarSvgCompressed: avatarSvgCompressed ?? this.avatarSvgCompressed, + senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter, + accepted: accepted ?? this.accepted, + deletedByUser: deletedByUser ?? this.deletedByUser, + requested: requested ?? this.requested, + blocked: blocked ?? this.blocked, + verified: verified ?? this.verified, + accountDeleted: accountDeleted ?? this.accountDeleted, + createdAt: createdAt ?? this.createdAt, + userDiscoveryVersion: userDiscoveryVersion ?? this.userDiscoveryVersion, + userDiscoveryExcluded: + userDiscoveryExcluded ?? this.userDiscoveryExcluded, + userDiscoveryManualApproved: + userDiscoveryManualApproved ?? this.userDiscoveryManualApproved, + recoveryIsTrustedFriend: + recoveryIsTrustedFriend ?? this.recoveryIsTrustedFriend, + recoveryLastHeartbeat: + recoveryLastHeartbeat ?? this.recoveryLastHeartbeat, + recoverySecretShare: recoverySecretShare ?? this.recoverySecretShare, + recoveryContactsSecretShare: + recoveryContactsSecretShare ?? this.recoveryContactsSecretShare, + recoveryContactsLastHeartbeat: + recoveryContactsLastHeartbeat ?? this.recoveryContactsLastHeartbeat, + recoveryContactsThreshold: + recoveryContactsThreshold ?? this.recoveryContactsThreshold, + askForFriendPromotions: + askForFriendPromotions ?? this.askForFriendPromotions, + mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter, + mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter, + ); + } + + @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 (avatarSvgCompressed.present) { + map['avatar_svg_compressed'] = Variable( + avatarSvgCompressed.value, + ); + } + if (senderProfileCounter.present) { + map['sender_profile_counter'] = Variable(senderProfileCounter.value); + } + if (accepted.present) { + map['accepted'] = Variable(accepted.value); + } + if (deletedByUser.present) { + map['deleted_by_user'] = Variable(deletedByUser.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 (accountDeleted.present) { + map['account_deleted'] = Variable(accountDeleted.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (userDiscoveryVersion.present) { + map['user_discovery_version'] = Variable( + userDiscoveryVersion.value, + ); + } + if (userDiscoveryExcluded.present) { + map['user_discovery_excluded'] = Variable( + userDiscoveryExcluded.value, + ); + } + if (userDiscoveryManualApproved.present) { + map['user_discovery_manual_approved'] = Variable( + userDiscoveryManualApproved.value, + ); + } + if (recoveryIsTrustedFriend.present) { + map['recovery_is_trusted_friend'] = Variable( + recoveryIsTrustedFriend.value, + ); + } + if (recoveryLastHeartbeat.present) { + map['recovery_last_heartbeat'] = Variable( + recoveryLastHeartbeat.value, + ); + } + if (recoverySecretShare.present) { + map['recovery_secret_share'] = Variable( + recoverySecretShare.value, + ); + } + if (recoveryContactsSecretShare.present) { + map['recovery_contacts_secret_share'] = Variable( + recoveryContactsSecretShare.value, + ); + } + if (recoveryContactsLastHeartbeat.present) { + map['recovery_contacts_last_heartbeat'] = Variable( + recoveryContactsLastHeartbeat.value, + ); + } + if (recoveryContactsThreshold.present) { + map['recovery_contacts_threshold'] = Variable( + recoveryContactsThreshold.value, + ); + } + if (askForFriendPromotions.present) { + map['ask_for_friend_promotions'] = Variable( + askForFriendPromotions.value, + ); + } + if (mediaSendCounter.present) { + map['media_send_counter'] = Variable(mediaSendCounter.value); + } + if (mediaReceivedCounter.present) { + map['media_received_counter'] = Variable(mediaReceivedCounter.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ContactsCompanion(') + ..write('userId: $userId, ') + ..write('username: $username, ') + ..write('displayName: $displayName, ') + ..write('nickName: $nickName, ') + ..write('avatarSvgCompressed: $avatarSvgCompressed, ') + ..write('senderProfileCounter: $senderProfileCounter, ') + ..write('accepted: $accepted, ') + ..write('deletedByUser: $deletedByUser, ') + ..write('requested: $requested, ') + ..write('blocked: $blocked, ') + ..write('verified: $verified, ') + ..write('accountDeleted: $accountDeleted, ') + ..write('createdAt: $createdAt, ') + ..write('userDiscoveryVersion: $userDiscoveryVersion, ') + ..write('userDiscoveryExcluded: $userDiscoveryExcluded, ') + ..write('userDiscoveryManualApproved: $userDiscoveryManualApproved, ') + ..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ') + ..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ') + ..write('recoverySecretShare: $recoverySecretShare, ') + ..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ') + ..write( + 'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ', + ) + ..write('recoveryContactsThreshold: $recoveryContactsThreshold, ') + ..write('askForFriendPromotions: $askForFriendPromotions, ') + ..write('mediaSendCounter: $mediaSendCounter, ') + ..write('mediaReceivedCounter: $mediaReceivedCounter') + ..write(')')) + .toString(); + } +} + +class Groups extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Groups(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn isGroupAdmin = GeneratedColumn( + 'is_group_admin', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_group_admin IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isDirectChat = GeneratedColumn( + 'is_direct_chat', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_direct_chat IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn pinned = GeneratedColumn( + 'pinned', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (pinned IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn archived = GeneratedColumn( + 'archived', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (archived IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn joinedGroup = GeneratedColumn( + 'joined_group', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (joined_group IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn leftGroup = GeneratedColumn( + 'left_group', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (left_group IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn deletedContent = GeneratedColumn( + 'deleted_content', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (deleted_content IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn stateVersionId = GeneratedColumn( + 'state_version_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn stateEncryptionKey = + GeneratedColumn( + 'state_encryption_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn myGroupPrivateKey = + GeneratedColumn( + 'my_group_private_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn groupName = GeneratedColumn( + 'group_name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn draftMessage = GeneratedColumn( + 'draft_message', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn totalMediaCounter = GeneratedColumn( + 'total_media_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn alsoBestFriend = GeneratedColumn( + 'also_best_friend', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (also_best_friend IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn deleteMessagesAfterMilliseconds = + GeneratedColumn( + 'delete_messages_after_milliseconds', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 86400000', + defaultValue: const CustomExpression('86400000'), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn lastMessageSend = GeneratedColumn( + 'last_message_send', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastMessageReceived = GeneratedColumn( + 'last_message_received', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastFlameCounterChange = GeneratedColumn( + 'last_flame_counter_change', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastFlameSync = GeneratedColumn( + 'last_flame_sync', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn flameCounter = GeneratedColumn( + 'flame_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn maxFlameCounter = GeneratedColumn( + 'max_flame_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn maxFlameCounterFrom = GeneratedColumn( + 'max_flame_counter_from', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastMessageExchange = GeneratedColumn( + 'last_message_exchange', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + groupId, + isGroupAdmin, + isDirectChat, + pinned, + archived, + joinedGroup, + leftGroup, + deletedContent, + stateVersionId, + stateEncryptionKey, + myGroupPrivateKey, + groupName, + draftMessage, + totalMediaCounter, + alsoBestFriend, + deleteMessagesAfterMilliseconds, + createdAt, + lastMessageSend, + lastMessageReceived, + lastFlameCounterChange, + lastFlameSync, + flameCounter, + maxFlameCounter, + maxFlameCounterFrom, + lastMessageExchange, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'groups'; + @override + Set get $primaryKey => {groupId}; + @override + GroupsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GroupsData( + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + isGroupAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_group_admin'], + )!, + isDirectChat: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_direct_chat'], + )!, + pinned: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}pinned'], + )!, + archived: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}archived'], + )!, + joinedGroup: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}joined_group'], + )!, + leftGroup: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}left_group'], + )!, + deletedContent: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}deleted_content'], + )!, + stateVersionId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}state_version_id'], + )!, + stateEncryptionKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}state_encryption_key'], + ), + myGroupPrivateKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}my_group_private_key'], + ), + groupName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_name'], + )!, + draftMessage: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}draft_message'], + ), + totalMediaCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}total_media_counter'], + )!, + alsoBestFriend: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}also_best_friend'], + )!, + deleteMessagesAfterMilliseconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}delete_messages_after_milliseconds'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + lastMessageSend: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message_send'], + ), + lastMessageReceived: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message_received'], + ), + lastFlameCounterChange: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_flame_counter_change'], + ), + lastFlameSync: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_flame_sync'], + ), + flameCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}flame_counter'], + )!, + maxFlameCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}max_flame_counter'], + )!, + maxFlameCounterFrom: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}max_flame_counter_from'], + ), + lastMessageExchange: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message_exchange'], + )!, + ); + } + + @override + Groups createAlias(String alias) { + return Groups(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(group_id)']; + @override + bool get dontWriteConstraints => true; +} + +class GroupsData extends DataClass implements Insertable { + final String groupId; + final int isGroupAdmin; + final int isDirectChat; + final int pinned; + final int archived; + final int joinedGroup; + final int leftGroup; + final int deletedContent; + final int stateVersionId; + final i2.Uint8List? stateEncryptionKey; + final i2.Uint8List? myGroupPrivateKey; + final String groupName; + final String? draftMessage; + final int totalMediaCounter; + final int alsoBestFriend; + final int deleteMessagesAfterMilliseconds; + final int createdAt; + final int? lastMessageSend; + final int? lastMessageReceived; + final int? lastFlameCounterChange; + final int? lastFlameSync; + final int flameCounter; + final int maxFlameCounter; + final int? maxFlameCounterFrom; + final int lastMessageExchange; + const GroupsData({ + required this.groupId, + required this.isGroupAdmin, + required this.isDirectChat, + required this.pinned, + required this.archived, + required this.joinedGroup, + required this.leftGroup, + required this.deletedContent, + required this.stateVersionId, + this.stateEncryptionKey, + this.myGroupPrivateKey, + required this.groupName, + this.draftMessage, + required this.totalMediaCounter, + required this.alsoBestFriend, + required this.deleteMessagesAfterMilliseconds, + required this.createdAt, + this.lastMessageSend, + this.lastMessageReceived, + this.lastFlameCounterChange, + this.lastFlameSync, + required this.flameCounter, + required this.maxFlameCounter, + this.maxFlameCounterFrom, + required this.lastMessageExchange, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_id'] = Variable(groupId); + map['is_group_admin'] = Variable(isGroupAdmin); + map['is_direct_chat'] = Variable(isDirectChat); + map['pinned'] = Variable(pinned); + map['archived'] = Variable(archived); + map['joined_group'] = Variable(joinedGroup); + map['left_group'] = Variable(leftGroup); + map['deleted_content'] = Variable(deletedContent); + map['state_version_id'] = Variable(stateVersionId); + if (!nullToAbsent || stateEncryptionKey != null) { + map['state_encryption_key'] = Variable(stateEncryptionKey); + } + if (!nullToAbsent || myGroupPrivateKey != null) { + map['my_group_private_key'] = Variable(myGroupPrivateKey); + } + map['group_name'] = Variable(groupName); + if (!nullToAbsent || draftMessage != null) { + map['draft_message'] = Variable(draftMessage); + } + map['total_media_counter'] = Variable(totalMediaCounter); + map['also_best_friend'] = Variable(alsoBestFriend); + map['delete_messages_after_milliseconds'] = Variable( + deleteMessagesAfterMilliseconds, + ); + map['created_at'] = Variable(createdAt); + 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['flame_counter'] = Variable(flameCounter); + map['max_flame_counter'] = Variable(maxFlameCounter); + if (!nullToAbsent || maxFlameCounterFrom != null) { + map['max_flame_counter_from'] = Variable(maxFlameCounterFrom); + } + map['last_message_exchange'] = Variable(lastMessageExchange); + return map; + } + + GroupsCompanion toCompanion(bool nullToAbsent) { + return GroupsCompanion( + groupId: Value(groupId), + isGroupAdmin: Value(isGroupAdmin), + isDirectChat: Value(isDirectChat), + pinned: Value(pinned), + archived: Value(archived), + joinedGroup: Value(joinedGroup), + leftGroup: Value(leftGroup), + deletedContent: Value(deletedContent), + stateVersionId: Value(stateVersionId), + stateEncryptionKey: stateEncryptionKey == null && nullToAbsent + ? const Value.absent() + : Value(stateEncryptionKey), + myGroupPrivateKey: myGroupPrivateKey == null && nullToAbsent + ? const Value.absent() + : Value(myGroupPrivateKey), + groupName: Value(groupName), + draftMessage: draftMessage == null && nullToAbsent + ? const Value.absent() + : Value(draftMessage), + totalMediaCounter: Value(totalMediaCounter), + alsoBestFriend: Value(alsoBestFriend), + deleteMessagesAfterMilliseconds: Value(deleteMessagesAfterMilliseconds), + createdAt: Value(createdAt), + 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), + flameCounter: Value(flameCounter), + maxFlameCounter: Value(maxFlameCounter), + maxFlameCounterFrom: maxFlameCounterFrom == null && nullToAbsent + ? const Value.absent() + : Value(maxFlameCounterFrom), + lastMessageExchange: Value(lastMessageExchange), + ); + } + + factory GroupsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GroupsData( + groupId: serializer.fromJson(json['groupId']), + isGroupAdmin: serializer.fromJson(json['isGroupAdmin']), + isDirectChat: serializer.fromJson(json['isDirectChat']), + pinned: serializer.fromJson(json['pinned']), + archived: serializer.fromJson(json['archived']), + joinedGroup: serializer.fromJson(json['joinedGroup']), + leftGroup: serializer.fromJson(json['leftGroup']), + deletedContent: serializer.fromJson(json['deletedContent']), + stateVersionId: serializer.fromJson(json['stateVersionId']), + stateEncryptionKey: serializer.fromJson( + json['stateEncryptionKey'], + ), + myGroupPrivateKey: serializer.fromJson( + json['myGroupPrivateKey'], + ), + groupName: serializer.fromJson(json['groupName']), + draftMessage: serializer.fromJson(json['draftMessage']), + totalMediaCounter: serializer.fromJson(json['totalMediaCounter']), + alsoBestFriend: serializer.fromJson(json['alsoBestFriend']), + deleteMessagesAfterMilliseconds: serializer.fromJson( + json['deleteMessagesAfterMilliseconds'], + ), + createdAt: serializer.fromJson(json['createdAt']), + lastMessageSend: serializer.fromJson(json['lastMessageSend']), + lastMessageReceived: serializer.fromJson( + json['lastMessageReceived'], + ), + lastFlameCounterChange: serializer.fromJson( + json['lastFlameCounterChange'], + ), + lastFlameSync: serializer.fromJson(json['lastFlameSync']), + flameCounter: serializer.fromJson(json['flameCounter']), + maxFlameCounter: serializer.fromJson(json['maxFlameCounter']), + maxFlameCounterFrom: serializer.fromJson( + json['maxFlameCounterFrom'], + ), + lastMessageExchange: serializer.fromJson( + json['lastMessageExchange'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupId': serializer.toJson(groupId), + 'isGroupAdmin': serializer.toJson(isGroupAdmin), + 'isDirectChat': serializer.toJson(isDirectChat), + 'pinned': serializer.toJson(pinned), + 'archived': serializer.toJson(archived), + 'joinedGroup': serializer.toJson(joinedGroup), + 'leftGroup': serializer.toJson(leftGroup), + 'deletedContent': serializer.toJson(deletedContent), + 'stateVersionId': serializer.toJson(stateVersionId), + 'stateEncryptionKey': serializer.toJson( + stateEncryptionKey, + ), + 'myGroupPrivateKey': serializer.toJson(myGroupPrivateKey), + 'groupName': serializer.toJson(groupName), + 'draftMessage': serializer.toJson(draftMessage), + 'totalMediaCounter': serializer.toJson(totalMediaCounter), + 'alsoBestFriend': serializer.toJson(alsoBestFriend), + 'deleteMessagesAfterMilliseconds': serializer.toJson( + deleteMessagesAfterMilliseconds, + ), + 'createdAt': serializer.toJson(createdAt), + 'lastMessageSend': serializer.toJson(lastMessageSend), + 'lastMessageReceived': serializer.toJson(lastMessageReceived), + 'lastFlameCounterChange': serializer.toJson(lastFlameCounterChange), + 'lastFlameSync': serializer.toJson(lastFlameSync), + 'flameCounter': serializer.toJson(flameCounter), + 'maxFlameCounter': serializer.toJson(maxFlameCounter), + 'maxFlameCounterFrom': serializer.toJson(maxFlameCounterFrom), + 'lastMessageExchange': serializer.toJson(lastMessageExchange), + }; + } + + GroupsData copyWith({ + String? groupId, + int? isGroupAdmin, + int? isDirectChat, + int? pinned, + int? archived, + int? joinedGroup, + int? leftGroup, + int? deletedContent, + int? stateVersionId, + Value stateEncryptionKey = const Value.absent(), + Value myGroupPrivateKey = const Value.absent(), + String? groupName, + Value draftMessage = const Value.absent(), + int? totalMediaCounter, + int? alsoBestFriend, + int? deleteMessagesAfterMilliseconds, + int? createdAt, + Value lastMessageSend = const Value.absent(), + Value lastMessageReceived = const Value.absent(), + Value lastFlameCounterChange = const Value.absent(), + Value lastFlameSync = const Value.absent(), + int? flameCounter, + int? maxFlameCounter, + Value maxFlameCounterFrom = const Value.absent(), + int? lastMessageExchange, + }) => GroupsData( + groupId: groupId ?? this.groupId, + isGroupAdmin: isGroupAdmin ?? this.isGroupAdmin, + isDirectChat: isDirectChat ?? this.isDirectChat, + pinned: pinned ?? this.pinned, + archived: archived ?? this.archived, + joinedGroup: joinedGroup ?? this.joinedGroup, + leftGroup: leftGroup ?? this.leftGroup, + deletedContent: deletedContent ?? this.deletedContent, + stateVersionId: stateVersionId ?? this.stateVersionId, + stateEncryptionKey: stateEncryptionKey.present + ? stateEncryptionKey.value + : this.stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey.present + ? myGroupPrivateKey.value + : this.myGroupPrivateKey, + groupName: groupName ?? this.groupName, + draftMessage: draftMessage.present ? draftMessage.value : this.draftMessage, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, + deleteMessagesAfterMilliseconds: + deleteMessagesAfterMilliseconds ?? this.deleteMessagesAfterMilliseconds, + createdAt: createdAt ?? this.createdAt, + 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, + flameCounter: flameCounter ?? this.flameCounter, + maxFlameCounter: maxFlameCounter ?? this.maxFlameCounter, + maxFlameCounterFrom: maxFlameCounterFrom.present + ? maxFlameCounterFrom.value + : this.maxFlameCounterFrom, + lastMessageExchange: lastMessageExchange ?? this.lastMessageExchange, + ); + GroupsData copyWithCompanion(GroupsCompanion data) { + return GroupsData( + groupId: data.groupId.present ? data.groupId.value : this.groupId, + isGroupAdmin: data.isGroupAdmin.present + ? data.isGroupAdmin.value + : this.isGroupAdmin, + isDirectChat: data.isDirectChat.present + ? data.isDirectChat.value + : this.isDirectChat, + pinned: data.pinned.present ? data.pinned.value : this.pinned, + archived: data.archived.present ? data.archived.value : this.archived, + joinedGroup: data.joinedGroup.present + ? data.joinedGroup.value + : this.joinedGroup, + leftGroup: data.leftGroup.present ? data.leftGroup.value : this.leftGroup, + deletedContent: data.deletedContent.present + ? data.deletedContent.value + : this.deletedContent, + stateVersionId: data.stateVersionId.present + ? data.stateVersionId.value + : this.stateVersionId, + stateEncryptionKey: data.stateEncryptionKey.present + ? data.stateEncryptionKey.value + : this.stateEncryptionKey, + myGroupPrivateKey: data.myGroupPrivateKey.present + ? data.myGroupPrivateKey.value + : this.myGroupPrivateKey, + groupName: data.groupName.present ? data.groupName.value : this.groupName, + draftMessage: data.draftMessage.present + ? data.draftMessage.value + : this.draftMessage, + totalMediaCounter: data.totalMediaCounter.present + ? data.totalMediaCounter.value + : this.totalMediaCounter, + alsoBestFriend: data.alsoBestFriend.present + ? data.alsoBestFriend.value + : this.alsoBestFriend, + deleteMessagesAfterMilliseconds: + data.deleteMessagesAfterMilliseconds.present + ? data.deleteMessagesAfterMilliseconds.value + : this.deleteMessagesAfterMilliseconds, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + 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, + flameCounter: data.flameCounter.present + ? data.flameCounter.value + : this.flameCounter, + maxFlameCounter: data.maxFlameCounter.present + ? data.maxFlameCounter.value + : this.maxFlameCounter, + maxFlameCounterFrom: data.maxFlameCounterFrom.present + ? data.maxFlameCounterFrom.value + : this.maxFlameCounterFrom, + lastMessageExchange: data.lastMessageExchange.present + ? data.lastMessageExchange.value + : this.lastMessageExchange, + ); + } + + @override + String toString() { + return (StringBuffer('GroupsData(') + ..write('groupId: $groupId, ') + ..write('isGroupAdmin: $isGroupAdmin, ') + ..write('isDirectChat: $isDirectChat, ') + ..write('pinned: $pinned, ') + ..write('archived: $archived, ') + ..write('joinedGroup: $joinedGroup, ') + ..write('leftGroup: $leftGroup, ') + ..write('deletedContent: $deletedContent, ') + ..write('stateVersionId: $stateVersionId, ') + ..write('stateEncryptionKey: $stateEncryptionKey, ') + ..write('myGroupPrivateKey: $myGroupPrivateKey, ') + ..write('groupName: $groupName, ') + ..write('draftMessage: $draftMessage, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('alsoBestFriend: $alsoBestFriend, ') + ..write( + 'deleteMessagesAfterMilliseconds: $deleteMessagesAfterMilliseconds, ', + ) + ..write('createdAt: $createdAt, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastFlameCounterChange: $lastFlameCounterChange, ') + ..write('lastFlameSync: $lastFlameSync, ') + ..write('flameCounter: $flameCounter, ') + ..write('maxFlameCounter: $maxFlameCounter, ') + ..write('maxFlameCounterFrom: $maxFlameCounterFrom, ') + ..write('lastMessageExchange: $lastMessageExchange') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + groupId, + isGroupAdmin, + isDirectChat, + pinned, + archived, + joinedGroup, + leftGroup, + deletedContent, + stateVersionId, + $driftBlobEquality.hash(stateEncryptionKey), + $driftBlobEquality.hash(myGroupPrivateKey), + groupName, + draftMessage, + totalMediaCounter, + alsoBestFriend, + deleteMessagesAfterMilliseconds, + createdAt, + lastMessageSend, + lastMessageReceived, + lastFlameCounterChange, + lastFlameSync, + flameCounter, + maxFlameCounter, + maxFlameCounterFrom, + lastMessageExchange, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GroupsData && + other.groupId == this.groupId && + other.isGroupAdmin == this.isGroupAdmin && + other.isDirectChat == this.isDirectChat && + other.pinned == this.pinned && + other.archived == this.archived && + other.joinedGroup == this.joinedGroup && + other.leftGroup == this.leftGroup && + other.deletedContent == this.deletedContent && + other.stateVersionId == this.stateVersionId && + $driftBlobEquality.equals( + other.stateEncryptionKey, + this.stateEncryptionKey, + ) && + $driftBlobEquality.equals( + other.myGroupPrivateKey, + this.myGroupPrivateKey, + ) && + other.groupName == this.groupName && + other.draftMessage == this.draftMessage && + other.totalMediaCounter == this.totalMediaCounter && + other.alsoBestFriend == this.alsoBestFriend && + other.deleteMessagesAfterMilliseconds == + this.deleteMessagesAfterMilliseconds && + other.createdAt == this.createdAt && + other.lastMessageSend == this.lastMessageSend && + other.lastMessageReceived == this.lastMessageReceived && + other.lastFlameCounterChange == this.lastFlameCounterChange && + other.lastFlameSync == this.lastFlameSync && + other.flameCounter == this.flameCounter && + other.maxFlameCounter == this.maxFlameCounter && + other.maxFlameCounterFrom == this.maxFlameCounterFrom && + other.lastMessageExchange == this.lastMessageExchange); +} + +class GroupsCompanion extends UpdateCompanion { + final Value groupId; + final Value isGroupAdmin; + final Value isDirectChat; + final Value pinned; + final Value archived; + final Value joinedGroup; + final Value leftGroup; + final Value deletedContent; + final Value stateVersionId; + final Value stateEncryptionKey; + final Value myGroupPrivateKey; + final Value groupName; + final Value draftMessage; + final Value totalMediaCounter; + final Value alsoBestFriend; + final Value deleteMessagesAfterMilliseconds; + final Value createdAt; + final Value lastMessageSend; + final Value lastMessageReceived; + final Value lastFlameCounterChange; + final Value lastFlameSync; + final Value flameCounter; + final Value maxFlameCounter; + final Value maxFlameCounterFrom; + final Value lastMessageExchange; + final Value rowid; + const GroupsCompanion({ + this.groupId = const Value.absent(), + this.isGroupAdmin = const Value.absent(), + this.isDirectChat = const Value.absent(), + this.pinned = const Value.absent(), + this.archived = const Value.absent(), + this.joinedGroup = const Value.absent(), + this.leftGroup = const Value.absent(), + this.deletedContent = const Value.absent(), + this.stateVersionId = const Value.absent(), + this.stateEncryptionKey = const Value.absent(), + this.myGroupPrivateKey = const Value.absent(), + this.groupName = const Value.absent(), + this.draftMessage = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.alsoBestFriend = const Value.absent(), + this.deleteMessagesAfterMilliseconds = const Value.absent(), + this.createdAt = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastFlameCounterChange = const Value.absent(), + this.lastFlameSync = const Value.absent(), + this.flameCounter = const Value.absent(), + this.maxFlameCounter = const Value.absent(), + this.maxFlameCounterFrom = const Value.absent(), + this.lastMessageExchange = const Value.absent(), + this.rowid = const Value.absent(), + }); + GroupsCompanion.insert({ + required String groupId, + this.isGroupAdmin = const Value.absent(), + this.isDirectChat = const Value.absent(), + this.pinned = const Value.absent(), + this.archived = const Value.absent(), + this.joinedGroup = const Value.absent(), + this.leftGroup = const Value.absent(), + this.deletedContent = const Value.absent(), + this.stateVersionId = const Value.absent(), + this.stateEncryptionKey = const Value.absent(), + this.myGroupPrivateKey = const Value.absent(), + required String groupName, + this.draftMessage = const Value.absent(), + this.totalMediaCounter = const Value.absent(), + this.alsoBestFriend = const Value.absent(), + this.deleteMessagesAfterMilliseconds = const Value.absent(), + this.createdAt = const Value.absent(), + this.lastMessageSend = const Value.absent(), + this.lastMessageReceived = const Value.absent(), + this.lastFlameCounterChange = const Value.absent(), + this.lastFlameSync = const Value.absent(), + this.flameCounter = const Value.absent(), + this.maxFlameCounter = const Value.absent(), + this.maxFlameCounterFrom = const Value.absent(), + this.lastMessageExchange = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupId = Value(groupId), + groupName = Value(groupName); + static Insertable custom({ + Expression? groupId, + Expression? isGroupAdmin, + Expression? isDirectChat, + Expression? pinned, + Expression? archived, + Expression? joinedGroup, + Expression? leftGroup, + Expression? deletedContent, + Expression? stateVersionId, + Expression? stateEncryptionKey, + Expression? myGroupPrivateKey, + Expression? groupName, + Expression? draftMessage, + Expression? totalMediaCounter, + Expression? alsoBestFriend, + Expression? deleteMessagesAfterMilliseconds, + Expression? createdAt, + Expression? lastMessageSend, + Expression? lastMessageReceived, + Expression? lastFlameCounterChange, + Expression? lastFlameSync, + Expression? flameCounter, + Expression? maxFlameCounter, + Expression? maxFlameCounterFrom, + Expression? lastMessageExchange, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupId != null) 'group_id': groupId, + if (isGroupAdmin != null) 'is_group_admin': isGroupAdmin, + if (isDirectChat != null) 'is_direct_chat': isDirectChat, + if (pinned != null) 'pinned': pinned, + if (archived != null) 'archived': archived, + if (joinedGroup != null) 'joined_group': joinedGroup, + if (leftGroup != null) 'left_group': leftGroup, + if (deletedContent != null) 'deleted_content': deletedContent, + if (stateVersionId != null) 'state_version_id': stateVersionId, + if (stateEncryptionKey != null) + 'state_encryption_key': stateEncryptionKey, + if (myGroupPrivateKey != null) 'my_group_private_key': myGroupPrivateKey, + if (groupName != null) 'group_name': groupName, + if (draftMessage != null) 'draft_message': draftMessage, + if (totalMediaCounter != null) 'total_media_counter': totalMediaCounter, + if (alsoBestFriend != null) 'also_best_friend': alsoBestFriend, + if (deleteMessagesAfterMilliseconds != null) + 'delete_messages_after_milliseconds': deleteMessagesAfterMilliseconds, + if (createdAt != null) 'created_at': createdAt, + 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 (flameCounter != null) 'flame_counter': flameCounter, + if (maxFlameCounter != null) 'max_flame_counter': maxFlameCounter, + if (maxFlameCounterFrom != null) + 'max_flame_counter_from': maxFlameCounterFrom, + if (lastMessageExchange != null) + 'last_message_exchange': lastMessageExchange, + if (rowid != null) 'rowid': rowid, + }); + } + + GroupsCompanion copyWith({ + Value? groupId, + Value? isGroupAdmin, + Value? isDirectChat, + Value? pinned, + Value? archived, + Value? joinedGroup, + Value? leftGroup, + Value? deletedContent, + Value? stateVersionId, + Value? stateEncryptionKey, + Value? myGroupPrivateKey, + Value? groupName, + Value? draftMessage, + Value? totalMediaCounter, + Value? alsoBestFriend, + Value? deleteMessagesAfterMilliseconds, + Value? createdAt, + Value? lastMessageSend, + Value? lastMessageReceived, + Value? lastFlameCounterChange, + Value? lastFlameSync, + Value? flameCounter, + Value? maxFlameCounter, + Value? maxFlameCounterFrom, + Value? lastMessageExchange, + Value? rowid, + }) { + return GroupsCompanion( + groupId: groupId ?? this.groupId, + isGroupAdmin: isGroupAdmin ?? this.isGroupAdmin, + isDirectChat: isDirectChat ?? this.isDirectChat, + pinned: pinned ?? this.pinned, + archived: archived ?? this.archived, + joinedGroup: joinedGroup ?? this.joinedGroup, + leftGroup: leftGroup ?? this.leftGroup, + deletedContent: deletedContent ?? this.deletedContent, + stateVersionId: stateVersionId ?? this.stateVersionId, + stateEncryptionKey: stateEncryptionKey ?? this.stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey ?? this.myGroupPrivateKey, + groupName: groupName ?? this.groupName, + draftMessage: draftMessage ?? this.draftMessage, + totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, + alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, + deleteMessagesAfterMilliseconds: + deleteMessagesAfterMilliseconds ?? + this.deleteMessagesAfterMilliseconds, + createdAt: createdAt ?? this.createdAt, + lastMessageSend: lastMessageSend ?? this.lastMessageSend, + lastMessageReceived: lastMessageReceived ?? this.lastMessageReceived, + lastFlameCounterChange: + lastFlameCounterChange ?? this.lastFlameCounterChange, + lastFlameSync: lastFlameSync ?? this.lastFlameSync, + flameCounter: flameCounter ?? this.flameCounter, + maxFlameCounter: maxFlameCounter ?? this.maxFlameCounter, + maxFlameCounterFrom: maxFlameCounterFrom ?? this.maxFlameCounterFrom, + lastMessageExchange: lastMessageExchange ?? this.lastMessageExchange, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (isGroupAdmin.present) { + map['is_group_admin'] = Variable(isGroupAdmin.value); + } + if (isDirectChat.present) { + map['is_direct_chat'] = Variable(isDirectChat.value); + } + if (pinned.present) { + map['pinned'] = Variable(pinned.value); + } + if (archived.present) { + map['archived'] = Variable(archived.value); + } + if (joinedGroup.present) { + map['joined_group'] = Variable(joinedGroup.value); + } + if (leftGroup.present) { + map['left_group'] = Variable(leftGroup.value); + } + if (deletedContent.present) { + map['deleted_content'] = Variable(deletedContent.value); + } + if (stateVersionId.present) { + map['state_version_id'] = Variable(stateVersionId.value); + } + if (stateEncryptionKey.present) { + map['state_encryption_key'] = Variable( + stateEncryptionKey.value, + ); + } + if (myGroupPrivateKey.present) { + map['my_group_private_key'] = Variable( + myGroupPrivateKey.value, + ); + } + if (groupName.present) { + map['group_name'] = Variable(groupName.value); + } + if (draftMessage.present) { + map['draft_message'] = Variable(draftMessage.value); + } + if (totalMediaCounter.present) { + map['total_media_counter'] = Variable(totalMediaCounter.value); + } + if (alsoBestFriend.present) { + map['also_best_friend'] = Variable(alsoBestFriend.value); + } + if (deleteMessagesAfterMilliseconds.present) { + map['delete_messages_after_milliseconds'] = Variable( + deleteMessagesAfterMilliseconds.value, + ); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.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 (flameCounter.present) { + map['flame_counter'] = Variable(flameCounter.value); + } + if (maxFlameCounter.present) { + map['max_flame_counter'] = Variable(maxFlameCounter.value); + } + if (maxFlameCounterFrom.present) { + map['max_flame_counter_from'] = Variable(maxFlameCounterFrom.value); + } + if (lastMessageExchange.present) { + map['last_message_exchange'] = Variable(lastMessageExchange.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GroupsCompanion(') + ..write('groupId: $groupId, ') + ..write('isGroupAdmin: $isGroupAdmin, ') + ..write('isDirectChat: $isDirectChat, ') + ..write('pinned: $pinned, ') + ..write('archived: $archived, ') + ..write('joinedGroup: $joinedGroup, ') + ..write('leftGroup: $leftGroup, ') + ..write('deletedContent: $deletedContent, ') + ..write('stateVersionId: $stateVersionId, ') + ..write('stateEncryptionKey: $stateEncryptionKey, ') + ..write('myGroupPrivateKey: $myGroupPrivateKey, ') + ..write('groupName: $groupName, ') + ..write('draftMessage: $draftMessage, ') + ..write('totalMediaCounter: $totalMediaCounter, ') + ..write('alsoBestFriend: $alsoBestFriend, ') + ..write( + 'deleteMessagesAfterMilliseconds: $deleteMessagesAfterMilliseconds, ', + ) + ..write('createdAt: $createdAt, ') + ..write('lastMessageSend: $lastMessageSend, ') + ..write('lastMessageReceived: $lastMessageReceived, ') + ..write('lastFlameCounterChange: $lastFlameCounterChange, ') + ..write('lastFlameSync: $lastFlameSync, ') + ..write('flameCounter: $flameCounter, ') + ..write('maxFlameCounter: $maxFlameCounter, ') + ..write('maxFlameCounterFrom: $maxFlameCounterFrom, ') + ..write('lastMessageExchange: $lastMessageExchange, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class MediaFiles extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MediaFiles(this.attachedDatabase, [this._alias]); + late final GeneratedColumn mediaId = GeneratedColumn( + 'media_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn uploadState = GeneratedColumn( + 'upload_state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn downloadState = GeneratedColumn( + 'download_state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn requiresAuthentication = GeneratedColumn( + 'requires_authentication', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (requires_authentication IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn stored = GeneratedColumn( + 'stored', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK ("stored" IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isDraftMedia = GeneratedColumn( + 'is_draft_media', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_draft_media IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_favorite IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn hasCropAnalyzed = GeneratedColumn( + 'has_crop_analyzed', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (has_crop_analyzed IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn preProgressingProcess = GeneratedColumn( + 'pre_progressing_process', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn reuploadRequestedBy = + GeneratedColumn( + 'reupload_requested_by', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn displayLimitInMilliseconds = + GeneratedColumn( + 'display_limit_in_milliseconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn removeAudio = GeneratedColumn( + 'remove_audio', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL CHECK (remove_audio IN (0, 1))', + ); + late final GeneratedColumn downloadToken = + GeneratedColumn( + 'download_token', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn encryptionKey = + GeneratedColumn( + 'encryption_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn encryptionMac = + GeneratedColumn( + 'encryption_mac', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn encryptionNonce = + GeneratedColumn( + 'encryption_nonce', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn storedFileHash = + GeneratedColumn( + 'stored_file_hash', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn hasThumbnail = GeneratedColumn( + 'has_thumbnail', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (has_thumbnail IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn sizeInBytes = GeneratedColumn( + 'size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn createdAtMonth = GeneratedColumn( + 'created_at_month', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + mediaId, + type, + uploadState, + downloadState, + requiresAuthentication, + stored, + isDraftMedia, + isFavorite, + hasCropAnalyzed, + preProgressingProcess, + reuploadRequestedBy, + displayLimitInMilliseconds, + removeAudio, + downloadToken, + encryptionKey, + encryptionMac, + encryptionNonce, + storedFileHash, + hasThumbnail, + sizeInBytes, + createdAt, + createdAtMonth, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'media_files'; + @override + Set get $primaryKey => {mediaId}; + @override + MediaFilesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MediaFilesData( + mediaId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}media_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + uploadState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}upload_state'], + ), + downloadState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}download_state'], + ), + requiresAuthentication: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}requires_authentication'], + )!, + stored: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}stored'], + )!, + isDraftMedia: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_draft_media'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_favorite'], + )!, + hasCropAnalyzed: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}has_crop_analyzed'], + )!, + preProgressingProcess: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}pre_progressing_process'], + ), + reuploadRequestedBy: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}reupload_requested_by'], + ), + displayLimitInMilliseconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}display_limit_in_milliseconds'], + ), + removeAudio: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}remove_audio'], + ), + downloadToken: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}download_token'], + ), + encryptionKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}encryption_key'], + ), + encryptionMac: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}encryption_mac'], + ), + encryptionNonce: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}encryption_nonce'], + ), + storedFileHash: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}stored_file_hash'], + ), + hasThumbnail: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}has_thumbnail'], + )!, + sizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}size_in_bytes'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + createdAtMonth: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}created_at_month'], + ), + ); + } + + @override + MediaFiles createAlias(String alias) { + return MediaFiles(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(media_id)']; + @override + bool get dontWriteConstraints => true; +} + +class MediaFilesData extends DataClass implements Insertable { + final String mediaId; + final String type; + final String? uploadState; + final String? downloadState; + final int requiresAuthentication; + final int stored; + final int isDraftMedia; + final int isFavorite; + final int hasCropAnalyzed; + final int? preProgressingProcess; + final String? reuploadRequestedBy; + final int? displayLimitInMilliseconds; + final int? removeAudio; + final i2.Uint8List? downloadToken; + final i2.Uint8List? encryptionKey; + final i2.Uint8List? encryptionMac; + final i2.Uint8List? encryptionNonce; + final i2.Uint8List? storedFileHash; + final int hasThumbnail; + final int? sizeInBytes; + final int createdAt; + final String? createdAtMonth; + const MediaFilesData({ + required this.mediaId, + required this.type, + this.uploadState, + this.downloadState, + required this.requiresAuthentication, + required this.stored, + required this.isDraftMedia, + required this.isFavorite, + required this.hasCropAnalyzed, + this.preProgressingProcess, + this.reuploadRequestedBy, + this.displayLimitInMilliseconds, + this.removeAudio, + this.downloadToken, + this.encryptionKey, + this.encryptionMac, + this.encryptionNonce, + this.storedFileHash, + required this.hasThumbnail, + this.sizeInBytes, + required this.createdAt, + this.createdAtMonth, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['media_id'] = Variable(mediaId); + map['type'] = Variable(type); + if (!nullToAbsent || uploadState != null) { + map['upload_state'] = Variable(uploadState); + } + if (!nullToAbsent || downloadState != null) { + map['download_state'] = Variable(downloadState); + } + map['requires_authentication'] = Variable(requiresAuthentication); + map['stored'] = Variable(stored); + map['is_draft_media'] = Variable(isDraftMedia); + map['is_favorite'] = Variable(isFavorite); + map['has_crop_analyzed'] = Variable(hasCropAnalyzed); + if (!nullToAbsent || preProgressingProcess != null) { + map['pre_progressing_process'] = Variable(preProgressingProcess); + } + if (!nullToAbsent || reuploadRequestedBy != null) { + map['reupload_requested_by'] = Variable(reuploadRequestedBy); + } + if (!nullToAbsent || displayLimitInMilliseconds != null) { + map['display_limit_in_milliseconds'] = Variable( + displayLimitInMilliseconds, + ); + } + if (!nullToAbsent || removeAudio != null) { + map['remove_audio'] = Variable(removeAudio); + } + if (!nullToAbsent || downloadToken != null) { + map['download_token'] = Variable(downloadToken); + } + if (!nullToAbsent || encryptionKey != null) { + map['encryption_key'] = Variable(encryptionKey); + } + if (!nullToAbsent || encryptionMac != null) { + map['encryption_mac'] = Variable(encryptionMac); + } + if (!nullToAbsent || encryptionNonce != null) { + map['encryption_nonce'] = Variable(encryptionNonce); + } + if (!nullToAbsent || storedFileHash != null) { + map['stored_file_hash'] = Variable(storedFileHash); + } + map['has_thumbnail'] = Variable(hasThumbnail); + if (!nullToAbsent || sizeInBytes != null) { + map['size_in_bytes'] = Variable(sizeInBytes); + } + map['created_at'] = Variable(createdAt); + if (!nullToAbsent || createdAtMonth != null) { + map['created_at_month'] = Variable(createdAtMonth); + } + return map; + } + + MediaFilesCompanion toCompanion(bool nullToAbsent) { + return MediaFilesCompanion( + mediaId: Value(mediaId), + type: Value(type), + uploadState: uploadState == null && nullToAbsent + ? const Value.absent() + : Value(uploadState), + downloadState: downloadState == null && nullToAbsent + ? const Value.absent() + : Value(downloadState), + requiresAuthentication: Value(requiresAuthentication), + stored: Value(stored), + isDraftMedia: Value(isDraftMedia), + isFavorite: Value(isFavorite), + hasCropAnalyzed: Value(hasCropAnalyzed), + preProgressingProcess: preProgressingProcess == null && nullToAbsent + ? const Value.absent() + : Value(preProgressingProcess), + reuploadRequestedBy: reuploadRequestedBy == null && nullToAbsent + ? const Value.absent() + : Value(reuploadRequestedBy), + displayLimitInMilliseconds: + displayLimitInMilliseconds == null && nullToAbsent + ? const Value.absent() + : Value(displayLimitInMilliseconds), + removeAudio: removeAudio == null && nullToAbsent + ? const Value.absent() + : Value(removeAudio), + downloadToken: downloadToken == null && nullToAbsent + ? const Value.absent() + : Value(downloadToken), + encryptionKey: encryptionKey == null && nullToAbsent + ? const Value.absent() + : Value(encryptionKey), + encryptionMac: encryptionMac == null && nullToAbsent + ? const Value.absent() + : Value(encryptionMac), + encryptionNonce: encryptionNonce == null && nullToAbsent + ? const Value.absent() + : Value(encryptionNonce), + storedFileHash: storedFileHash == null && nullToAbsent + ? const Value.absent() + : Value(storedFileHash), + hasThumbnail: Value(hasThumbnail), + sizeInBytes: sizeInBytes == null && nullToAbsent + ? const Value.absent() + : Value(sizeInBytes), + createdAt: Value(createdAt), + createdAtMonth: createdAtMonth == null && nullToAbsent + ? const Value.absent() + : Value(createdAtMonth), + ); + } + + factory MediaFilesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MediaFilesData( + mediaId: serializer.fromJson(json['mediaId']), + type: serializer.fromJson(json['type']), + uploadState: serializer.fromJson(json['uploadState']), + downloadState: serializer.fromJson(json['downloadState']), + requiresAuthentication: serializer.fromJson( + json['requiresAuthentication'], + ), + stored: serializer.fromJson(json['stored']), + isDraftMedia: serializer.fromJson(json['isDraftMedia']), + isFavorite: serializer.fromJson(json['isFavorite']), + hasCropAnalyzed: serializer.fromJson(json['hasCropAnalyzed']), + preProgressingProcess: serializer.fromJson( + json['preProgressingProcess'], + ), + reuploadRequestedBy: serializer.fromJson( + json['reuploadRequestedBy'], + ), + displayLimitInMilliseconds: serializer.fromJson( + json['displayLimitInMilliseconds'], + ), + removeAudio: serializer.fromJson(json['removeAudio']), + downloadToken: serializer.fromJson(json['downloadToken']), + encryptionKey: serializer.fromJson(json['encryptionKey']), + encryptionMac: serializer.fromJson(json['encryptionMac']), + encryptionNonce: serializer.fromJson( + json['encryptionNonce'], + ), + storedFileHash: serializer.fromJson( + json['storedFileHash'], + ), + hasThumbnail: serializer.fromJson(json['hasThumbnail']), + sizeInBytes: serializer.fromJson(json['sizeInBytes']), + createdAt: serializer.fromJson(json['createdAt']), + createdAtMonth: serializer.fromJson(json['createdAtMonth']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'mediaId': serializer.toJson(mediaId), + 'type': serializer.toJson(type), + 'uploadState': serializer.toJson(uploadState), + 'downloadState': serializer.toJson(downloadState), + 'requiresAuthentication': serializer.toJson(requiresAuthentication), + 'stored': serializer.toJson(stored), + 'isDraftMedia': serializer.toJson(isDraftMedia), + 'isFavorite': serializer.toJson(isFavorite), + 'hasCropAnalyzed': serializer.toJson(hasCropAnalyzed), + 'preProgressingProcess': serializer.toJson(preProgressingProcess), + 'reuploadRequestedBy': serializer.toJson(reuploadRequestedBy), + 'displayLimitInMilliseconds': serializer.toJson( + displayLimitInMilliseconds, + ), + 'removeAudio': serializer.toJson(removeAudio), + 'downloadToken': serializer.toJson(downloadToken), + 'encryptionKey': serializer.toJson(encryptionKey), + 'encryptionMac': serializer.toJson(encryptionMac), + 'encryptionNonce': serializer.toJson(encryptionNonce), + 'storedFileHash': serializer.toJson(storedFileHash), + 'hasThumbnail': serializer.toJson(hasThumbnail), + 'sizeInBytes': serializer.toJson(sizeInBytes), + 'createdAt': serializer.toJson(createdAt), + 'createdAtMonth': serializer.toJson(createdAtMonth), + }; + } + + MediaFilesData copyWith({ + String? mediaId, + String? type, + Value uploadState = const Value.absent(), + Value downloadState = const Value.absent(), + int? requiresAuthentication, + int? stored, + int? isDraftMedia, + int? isFavorite, + int? hasCropAnalyzed, + Value preProgressingProcess = const Value.absent(), + Value reuploadRequestedBy = const Value.absent(), + Value displayLimitInMilliseconds = const Value.absent(), + Value removeAudio = const Value.absent(), + Value downloadToken = const Value.absent(), + Value encryptionKey = const Value.absent(), + Value encryptionMac = const Value.absent(), + Value encryptionNonce = const Value.absent(), + Value storedFileHash = const Value.absent(), + int? hasThumbnail, + Value sizeInBytes = const Value.absent(), + int? createdAt, + Value createdAtMonth = const Value.absent(), + }) => MediaFilesData( + mediaId: mediaId ?? this.mediaId, + type: type ?? this.type, + uploadState: uploadState.present ? uploadState.value : this.uploadState, + downloadState: downloadState.present + ? downloadState.value + : this.downloadState, + requiresAuthentication: + requiresAuthentication ?? this.requiresAuthentication, + stored: stored ?? this.stored, + isDraftMedia: isDraftMedia ?? this.isDraftMedia, + isFavorite: isFavorite ?? this.isFavorite, + hasCropAnalyzed: hasCropAnalyzed ?? this.hasCropAnalyzed, + preProgressingProcess: preProgressingProcess.present + ? preProgressingProcess.value + : this.preProgressingProcess, + reuploadRequestedBy: reuploadRequestedBy.present + ? reuploadRequestedBy.value + : this.reuploadRequestedBy, + displayLimitInMilliseconds: displayLimitInMilliseconds.present + ? displayLimitInMilliseconds.value + : this.displayLimitInMilliseconds, + removeAudio: removeAudio.present ? removeAudio.value : this.removeAudio, + downloadToken: downloadToken.present + ? downloadToken.value + : this.downloadToken, + encryptionKey: encryptionKey.present + ? encryptionKey.value + : this.encryptionKey, + encryptionMac: encryptionMac.present + ? encryptionMac.value + : this.encryptionMac, + encryptionNonce: encryptionNonce.present + ? encryptionNonce.value + : this.encryptionNonce, + storedFileHash: storedFileHash.present + ? storedFileHash.value + : this.storedFileHash, + hasThumbnail: hasThumbnail ?? this.hasThumbnail, + sizeInBytes: sizeInBytes.present ? sizeInBytes.value : this.sizeInBytes, + createdAt: createdAt ?? this.createdAt, + createdAtMonth: createdAtMonth.present + ? createdAtMonth.value + : this.createdAtMonth, + ); + MediaFilesData copyWithCompanion(MediaFilesCompanion data) { + return MediaFilesData( + mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId, + type: data.type.present ? data.type.value : this.type, + uploadState: data.uploadState.present + ? data.uploadState.value + : this.uploadState, + downloadState: data.downloadState.present + ? data.downloadState.value + : this.downloadState, + requiresAuthentication: data.requiresAuthentication.present + ? data.requiresAuthentication.value + : this.requiresAuthentication, + stored: data.stored.present ? data.stored.value : this.stored, + isDraftMedia: data.isDraftMedia.present + ? data.isDraftMedia.value + : this.isDraftMedia, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + hasCropAnalyzed: data.hasCropAnalyzed.present + ? data.hasCropAnalyzed.value + : this.hasCropAnalyzed, + preProgressingProcess: data.preProgressingProcess.present + ? data.preProgressingProcess.value + : this.preProgressingProcess, + reuploadRequestedBy: data.reuploadRequestedBy.present + ? data.reuploadRequestedBy.value + : this.reuploadRequestedBy, + displayLimitInMilliseconds: data.displayLimitInMilliseconds.present + ? data.displayLimitInMilliseconds.value + : this.displayLimitInMilliseconds, + removeAudio: data.removeAudio.present + ? data.removeAudio.value + : this.removeAudio, + downloadToken: data.downloadToken.present + ? data.downloadToken.value + : this.downloadToken, + encryptionKey: data.encryptionKey.present + ? data.encryptionKey.value + : this.encryptionKey, + encryptionMac: data.encryptionMac.present + ? data.encryptionMac.value + : this.encryptionMac, + encryptionNonce: data.encryptionNonce.present + ? data.encryptionNonce.value + : this.encryptionNonce, + storedFileHash: data.storedFileHash.present + ? data.storedFileHash.value + : this.storedFileHash, + hasThumbnail: data.hasThumbnail.present + ? data.hasThumbnail.value + : this.hasThumbnail, + sizeInBytes: data.sizeInBytes.present + ? data.sizeInBytes.value + : this.sizeInBytes, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + createdAtMonth: data.createdAtMonth.present + ? data.createdAtMonth.value + : this.createdAtMonth, + ); + } + + @override + String toString() { + return (StringBuffer('MediaFilesData(') + ..write('mediaId: $mediaId, ') + ..write('type: $type, ') + ..write('uploadState: $uploadState, ') + ..write('downloadState: $downloadState, ') + ..write('requiresAuthentication: $requiresAuthentication, ') + ..write('stored: $stored, ') + ..write('isDraftMedia: $isDraftMedia, ') + ..write('isFavorite: $isFavorite, ') + ..write('hasCropAnalyzed: $hasCropAnalyzed, ') + ..write('preProgressingProcess: $preProgressingProcess, ') + ..write('reuploadRequestedBy: $reuploadRequestedBy, ') + ..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ') + ..write('removeAudio: $removeAudio, ') + ..write('downloadToken: $downloadToken, ') + ..write('encryptionKey: $encryptionKey, ') + ..write('encryptionMac: $encryptionMac, ') + ..write('encryptionNonce: $encryptionNonce, ') + ..write('storedFileHash: $storedFileHash, ') + ..write('hasThumbnail: $hasThumbnail, ') + ..write('sizeInBytes: $sizeInBytes, ') + ..write('createdAt: $createdAt, ') + ..write('createdAtMonth: $createdAtMonth') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + mediaId, + type, + uploadState, + downloadState, + requiresAuthentication, + stored, + isDraftMedia, + isFavorite, + hasCropAnalyzed, + preProgressingProcess, + reuploadRequestedBy, + displayLimitInMilliseconds, + removeAudio, + $driftBlobEquality.hash(downloadToken), + $driftBlobEquality.hash(encryptionKey), + $driftBlobEquality.hash(encryptionMac), + $driftBlobEquality.hash(encryptionNonce), + $driftBlobEquality.hash(storedFileHash), + hasThumbnail, + sizeInBytes, + createdAt, + createdAtMonth, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MediaFilesData && + other.mediaId == this.mediaId && + other.type == this.type && + other.uploadState == this.uploadState && + other.downloadState == this.downloadState && + other.requiresAuthentication == this.requiresAuthentication && + other.stored == this.stored && + other.isDraftMedia == this.isDraftMedia && + other.isFavorite == this.isFavorite && + other.hasCropAnalyzed == this.hasCropAnalyzed && + other.preProgressingProcess == this.preProgressingProcess && + other.reuploadRequestedBy == this.reuploadRequestedBy && + other.displayLimitInMilliseconds == this.displayLimitInMilliseconds && + other.removeAudio == this.removeAudio && + $driftBlobEquality.equals(other.downloadToken, this.downloadToken) && + $driftBlobEquality.equals(other.encryptionKey, this.encryptionKey) && + $driftBlobEquality.equals(other.encryptionMac, this.encryptionMac) && + $driftBlobEquality.equals( + other.encryptionNonce, + this.encryptionNonce, + ) && + $driftBlobEquality.equals( + other.storedFileHash, + this.storedFileHash, + ) && + other.hasThumbnail == this.hasThumbnail && + other.sizeInBytes == this.sizeInBytes && + other.createdAt == this.createdAt && + other.createdAtMonth == this.createdAtMonth); +} + +class MediaFilesCompanion extends UpdateCompanion { + final Value mediaId; + final Value type; + final Value uploadState; + final Value downloadState; + final Value requiresAuthentication; + final Value stored; + final Value isDraftMedia; + final Value isFavorite; + final Value hasCropAnalyzed; + final Value preProgressingProcess; + final Value reuploadRequestedBy; + final Value displayLimitInMilliseconds; + final Value removeAudio; + final Value downloadToken; + final Value encryptionKey; + final Value encryptionMac; + final Value encryptionNonce; + final Value storedFileHash; + final Value hasThumbnail; + final Value sizeInBytes; + final Value createdAt; + final Value createdAtMonth; + final Value rowid; + const MediaFilesCompanion({ + this.mediaId = const Value.absent(), + this.type = const Value.absent(), + this.uploadState = const Value.absent(), + this.downloadState = const Value.absent(), + this.requiresAuthentication = const Value.absent(), + this.stored = const Value.absent(), + this.isDraftMedia = const Value.absent(), + this.isFavorite = const Value.absent(), + this.hasCropAnalyzed = const Value.absent(), + this.preProgressingProcess = const Value.absent(), + this.reuploadRequestedBy = const Value.absent(), + this.displayLimitInMilliseconds = const Value.absent(), + this.removeAudio = const Value.absent(), + this.downloadToken = const Value.absent(), + this.encryptionKey = const Value.absent(), + this.encryptionMac = const Value.absent(), + this.encryptionNonce = const Value.absent(), + this.storedFileHash = const Value.absent(), + this.hasThumbnail = const Value.absent(), + this.sizeInBytes = const Value.absent(), + this.createdAt = const Value.absent(), + this.createdAtMonth = const Value.absent(), + this.rowid = const Value.absent(), + }); + MediaFilesCompanion.insert({ + required String mediaId, + required String type, + this.uploadState = const Value.absent(), + this.downloadState = const Value.absent(), + this.requiresAuthentication = const Value.absent(), + this.stored = const Value.absent(), + this.isDraftMedia = const Value.absent(), + this.isFavorite = const Value.absent(), + this.hasCropAnalyzed = const Value.absent(), + this.preProgressingProcess = const Value.absent(), + this.reuploadRequestedBy = const Value.absent(), + this.displayLimitInMilliseconds = const Value.absent(), + this.removeAudio = const Value.absent(), + this.downloadToken = const Value.absent(), + this.encryptionKey = const Value.absent(), + this.encryptionMac = const Value.absent(), + this.encryptionNonce = const Value.absent(), + this.storedFileHash = const Value.absent(), + this.hasThumbnail = const Value.absent(), + this.sizeInBytes = const Value.absent(), + this.createdAt = const Value.absent(), + this.createdAtMonth = const Value.absent(), + this.rowid = const Value.absent(), + }) : mediaId = Value(mediaId), + type = Value(type); + static Insertable custom({ + Expression? mediaId, + Expression? type, + Expression? uploadState, + Expression? downloadState, + Expression? requiresAuthentication, + Expression? stored, + Expression? isDraftMedia, + Expression? isFavorite, + Expression? hasCropAnalyzed, + Expression? preProgressingProcess, + Expression? reuploadRequestedBy, + Expression? displayLimitInMilliseconds, + Expression? removeAudio, + Expression? downloadToken, + Expression? encryptionKey, + Expression? encryptionMac, + Expression? encryptionNonce, + Expression? storedFileHash, + Expression? hasThumbnail, + Expression? sizeInBytes, + Expression? createdAt, + Expression? createdAtMonth, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (mediaId != null) 'media_id': mediaId, + if (type != null) 'type': type, + if (uploadState != null) 'upload_state': uploadState, + if (downloadState != null) 'download_state': downloadState, + if (requiresAuthentication != null) + 'requires_authentication': requiresAuthentication, + if (stored != null) 'stored': stored, + if (isDraftMedia != null) 'is_draft_media': isDraftMedia, + if (isFavorite != null) 'is_favorite': isFavorite, + if (hasCropAnalyzed != null) 'has_crop_analyzed': hasCropAnalyzed, + if (preProgressingProcess != null) + 'pre_progressing_process': preProgressingProcess, + if (reuploadRequestedBy != null) + 'reupload_requested_by': reuploadRequestedBy, + if (displayLimitInMilliseconds != null) + 'display_limit_in_milliseconds': displayLimitInMilliseconds, + if (removeAudio != null) 'remove_audio': removeAudio, + if (downloadToken != null) 'download_token': downloadToken, + if (encryptionKey != null) 'encryption_key': encryptionKey, + if (encryptionMac != null) 'encryption_mac': encryptionMac, + if (encryptionNonce != null) 'encryption_nonce': encryptionNonce, + if (storedFileHash != null) 'stored_file_hash': storedFileHash, + if (hasThumbnail != null) 'has_thumbnail': hasThumbnail, + if (sizeInBytes != null) 'size_in_bytes': sizeInBytes, + if (createdAt != null) 'created_at': createdAt, + if (createdAtMonth != null) 'created_at_month': createdAtMonth, + if (rowid != null) 'rowid': rowid, + }); + } + + MediaFilesCompanion copyWith({ + Value? mediaId, + Value? type, + Value? uploadState, + Value? downloadState, + Value? requiresAuthentication, + Value? stored, + Value? isDraftMedia, + Value? isFavorite, + Value? hasCropAnalyzed, + Value? preProgressingProcess, + Value? reuploadRequestedBy, + Value? displayLimitInMilliseconds, + Value? removeAudio, + Value? downloadToken, + Value? encryptionKey, + Value? encryptionMac, + Value? encryptionNonce, + Value? storedFileHash, + Value? hasThumbnail, + Value? sizeInBytes, + Value? createdAt, + Value? createdAtMonth, + Value? rowid, + }) { + return MediaFilesCompanion( + mediaId: mediaId ?? this.mediaId, + type: type ?? this.type, + uploadState: uploadState ?? this.uploadState, + downloadState: downloadState ?? this.downloadState, + requiresAuthentication: + requiresAuthentication ?? this.requiresAuthentication, + stored: stored ?? this.stored, + isDraftMedia: isDraftMedia ?? this.isDraftMedia, + isFavorite: isFavorite ?? this.isFavorite, + hasCropAnalyzed: hasCropAnalyzed ?? this.hasCropAnalyzed, + preProgressingProcess: + preProgressingProcess ?? this.preProgressingProcess, + reuploadRequestedBy: reuploadRequestedBy ?? this.reuploadRequestedBy, + displayLimitInMilliseconds: + displayLimitInMilliseconds ?? this.displayLimitInMilliseconds, + removeAudio: removeAudio ?? this.removeAudio, + downloadToken: downloadToken ?? this.downloadToken, + encryptionKey: encryptionKey ?? this.encryptionKey, + encryptionMac: encryptionMac ?? this.encryptionMac, + encryptionNonce: encryptionNonce ?? this.encryptionNonce, + storedFileHash: storedFileHash ?? this.storedFileHash, + hasThumbnail: hasThumbnail ?? this.hasThumbnail, + sizeInBytes: sizeInBytes ?? this.sizeInBytes, + createdAt: createdAt ?? this.createdAt, + createdAtMonth: createdAtMonth ?? this.createdAtMonth, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (mediaId.present) { + map['media_id'] = Variable(mediaId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (uploadState.present) { + map['upload_state'] = Variable(uploadState.value); + } + if (downloadState.present) { + map['download_state'] = Variable(downloadState.value); + } + if (requiresAuthentication.present) { + map['requires_authentication'] = Variable( + requiresAuthentication.value, + ); + } + if (stored.present) { + map['stored'] = Variable(stored.value); + } + if (isDraftMedia.present) { + map['is_draft_media'] = Variable(isDraftMedia.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (hasCropAnalyzed.present) { + map['has_crop_analyzed'] = Variable(hasCropAnalyzed.value); + } + if (preProgressingProcess.present) { + map['pre_progressing_process'] = Variable( + preProgressingProcess.value, + ); + } + if (reuploadRequestedBy.present) { + map['reupload_requested_by'] = Variable( + reuploadRequestedBy.value, + ); + } + if (displayLimitInMilliseconds.present) { + map['display_limit_in_milliseconds'] = Variable( + displayLimitInMilliseconds.value, + ); + } + if (removeAudio.present) { + map['remove_audio'] = Variable(removeAudio.value); + } + if (downloadToken.present) { + map['download_token'] = Variable(downloadToken.value); + } + if (encryptionKey.present) { + map['encryption_key'] = Variable(encryptionKey.value); + } + if (encryptionMac.present) { + map['encryption_mac'] = Variable(encryptionMac.value); + } + if (encryptionNonce.present) { + map['encryption_nonce'] = Variable(encryptionNonce.value); + } + if (storedFileHash.present) { + map['stored_file_hash'] = Variable(storedFileHash.value); + } + if (hasThumbnail.present) { + map['has_thumbnail'] = Variable(hasThumbnail.value); + } + if (sizeInBytes.present) { + map['size_in_bytes'] = Variable(sizeInBytes.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (createdAtMonth.present) { + map['created_at_month'] = Variable(createdAtMonth.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MediaFilesCompanion(') + ..write('mediaId: $mediaId, ') + ..write('type: $type, ') + ..write('uploadState: $uploadState, ') + ..write('downloadState: $downloadState, ') + ..write('requiresAuthentication: $requiresAuthentication, ') + ..write('stored: $stored, ') + ..write('isDraftMedia: $isDraftMedia, ') + ..write('isFavorite: $isFavorite, ') + ..write('hasCropAnalyzed: $hasCropAnalyzed, ') + ..write('preProgressingProcess: $preProgressingProcess, ') + ..write('reuploadRequestedBy: $reuploadRequestedBy, ') + ..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ') + ..write('removeAudio: $removeAudio, ') + ..write('downloadToken: $downloadToken, ') + ..write('encryptionKey: $encryptionKey, ') + ..write('encryptionMac: $encryptionMac, ') + ..write('encryptionNonce: $encryptionNonce, ') + ..write('storedFileHash: $storedFileHash, ') + ..write('hasThumbnail: $hasThumbnail, ') + ..write('sizeInBytes: $sizeInBytes, ') + ..write('createdAt: $createdAt, ') + ..write('createdAtMonth: $createdAtMonth, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Messages extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Messages(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn senderId = GeneratedColumn( + 'sender_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn content = GeneratedColumn( + 'content', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn mediaId = GeneratedColumn( + 'media_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: + 'NULL REFERENCES media_files(media_id)ON DELETE SET NULL', + ); + late final GeneratedColumn additionalMessageData = + GeneratedColumn( + 'additional_message_data', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn mediaStored = GeneratedColumn( + 'media_stored', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (media_stored IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn mediaReopened = GeneratedColumn( + 'media_reopened', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (media_reopened IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn downloadToken = + GeneratedColumn( + 'download_token', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn quotesMessageId = GeneratedColumn( + 'quotes_message_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn isDeletedFromSender = GeneratedColumn( + 'is_deleted_from_sender', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (is_deleted_from_sender IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn openedAt = GeneratedColumn( + 'opened_at', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn openedByAll = GeneratedColumn( + 'opened_by_all', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + late final GeneratedColumn modifiedAt = GeneratedColumn( + 'modified_at', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn ackByUser = GeneratedColumn( + 'ack_by_user', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn ackByServer = GeneratedColumn( + 'ack_by_server', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + groupId, + messageId, + senderId, + type, + content, + mediaId, + additionalMessageData, + mediaStored, + mediaReopened, + downloadToken, + quotesMessageId, + isDeletedFromSender, + openedAt, + openedByAll, + createdAt, + modifiedAt, + ackByUser, + ackByServer, + ]; + @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( + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + senderId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}sender_id'], + ), + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + content: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}content'], + ), + mediaId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}media_id'], + ), + additionalMessageData: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}additional_message_data'], + ), + mediaStored: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_stored'], + )!, + mediaReopened: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}media_reopened'], + )!, + downloadToken: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}download_token'], + ), + quotesMessageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}quotes_message_id'], + ), + isDeletedFromSender: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_deleted_from_sender'], + )!, + openedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}opened_at'], + ), + openedByAll: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}opened_by_all'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + modifiedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}modified_at'], + ), + ackByUser: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ack_by_user'], + ), + ackByServer: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ack_by_server'], + ), + ); + } + + @override + Messages createAlias(String alias) { + return Messages(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(message_id)']; + @override + bool get dontWriteConstraints => true; +} + +class MessagesData extends DataClass implements Insertable { + final String groupId; + final String messageId; + final int? senderId; + final String type; + final String? content; + final String? mediaId; + final i2.Uint8List? additionalMessageData; + final int mediaStored; + final int mediaReopened; + final i2.Uint8List? downloadToken; + final String? quotesMessageId; + final int isDeletedFromSender; + final int? openedAt; + final int? openedByAll; + final int createdAt; + final int? modifiedAt; + final int? ackByUser; + final int? ackByServer; + const MessagesData({ + required this.groupId, + required this.messageId, + this.senderId, + required this.type, + this.content, + this.mediaId, + this.additionalMessageData, + required this.mediaStored, + required this.mediaReopened, + this.downloadToken, + this.quotesMessageId, + required this.isDeletedFromSender, + this.openedAt, + this.openedByAll, + required this.createdAt, + this.modifiedAt, + this.ackByUser, + this.ackByServer, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_id'] = Variable(groupId); + map['message_id'] = Variable(messageId); + if (!nullToAbsent || senderId != null) { + map['sender_id'] = Variable(senderId); + } + map['type'] = Variable(type); + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + if (!nullToAbsent || mediaId != null) { + map['media_id'] = Variable(mediaId); + } + if (!nullToAbsent || additionalMessageData != null) { + map['additional_message_data'] = Variable( + additionalMessageData, + ); + } + map['media_stored'] = Variable(mediaStored); + map['media_reopened'] = Variable(mediaReopened); + if (!nullToAbsent || downloadToken != null) { + map['download_token'] = Variable(downloadToken); + } + if (!nullToAbsent || quotesMessageId != null) { + map['quotes_message_id'] = Variable(quotesMessageId); + } + map['is_deleted_from_sender'] = Variable(isDeletedFromSender); + if (!nullToAbsent || openedAt != null) { + map['opened_at'] = Variable(openedAt); + } + if (!nullToAbsent || openedByAll != null) { + map['opened_by_all'] = Variable(openedByAll); + } + map['created_at'] = Variable(createdAt); + if (!nullToAbsent || modifiedAt != null) { + map['modified_at'] = Variable(modifiedAt); + } + if (!nullToAbsent || ackByUser != null) { + map['ack_by_user'] = Variable(ackByUser); + } + if (!nullToAbsent || ackByServer != null) { + map['ack_by_server'] = Variable(ackByServer); + } + return map; + } + + MessagesCompanion toCompanion(bool nullToAbsent) { + return MessagesCompanion( + groupId: Value(groupId), + messageId: Value(messageId), + senderId: senderId == null && nullToAbsent + ? const Value.absent() + : Value(senderId), + type: Value(type), + content: content == null && nullToAbsent + ? const Value.absent() + : Value(content), + mediaId: mediaId == null && nullToAbsent + ? const Value.absent() + : Value(mediaId), + additionalMessageData: additionalMessageData == null && nullToAbsent + ? const Value.absent() + : Value(additionalMessageData), + mediaStored: Value(mediaStored), + mediaReopened: Value(mediaReopened), + downloadToken: downloadToken == null && nullToAbsent + ? const Value.absent() + : Value(downloadToken), + quotesMessageId: quotesMessageId == null && nullToAbsent + ? const Value.absent() + : Value(quotesMessageId), + isDeletedFromSender: Value(isDeletedFromSender), + openedAt: openedAt == null && nullToAbsent + ? const Value.absent() + : Value(openedAt), + openedByAll: openedByAll == null && nullToAbsent + ? const Value.absent() + : Value(openedByAll), + createdAt: Value(createdAt), + modifiedAt: modifiedAt == null && nullToAbsent + ? const Value.absent() + : Value(modifiedAt), + ackByUser: ackByUser == null && nullToAbsent + ? const Value.absent() + : Value(ackByUser), + ackByServer: ackByServer == null && nullToAbsent + ? const Value.absent() + : Value(ackByServer), + ); + } + + factory MessagesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessagesData( + groupId: serializer.fromJson(json['groupId']), + messageId: serializer.fromJson(json['messageId']), + senderId: serializer.fromJson(json['senderId']), + type: serializer.fromJson(json['type']), + content: serializer.fromJson(json['content']), + mediaId: serializer.fromJson(json['mediaId']), + additionalMessageData: serializer.fromJson( + json['additionalMessageData'], + ), + mediaStored: serializer.fromJson(json['mediaStored']), + mediaReopened: serializer.fromJson(json['mediaReopened']), + downloadToken: serializer.fromJson(json['downloadToken']), + quotesMessageId: serializer.fromJson(json['quotesMessageId']), + isDeletedFromSender: serializer.fromJson( + json['isDeletedFromSender'], + ), + openedAt: serializer.fromJson(json['openedAt']), + openedByAll: serializer.fromJson(json['openedByAll']), + createdAt: serializer.fromJson(json['createdAt']), + modifiedAt: serializer.fromJson(json['modifiedAt']), + ackByUser: serializer.fromJson(json['ackByUser']), + ackByServer: serializer.fromJson(json['ackByServer']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupId': serializer.toJson(groupId), + 'messageId': serializer.toJson(messageId), + 'senderId': serializer.toJson(senderId), + 'type': serializer.toJson(type), + 'content': serializer.toJson(content), + 'mediaId': serializer.toJson(mediaId), + 'additionalMessageData': serializer.toJson( + additionalMessageData, + ), + 'mediaStored': serializer.toJson(mediaStored), + 'mediaReopened': serializer.toJson(mediaReopened), + 'downloadToken': serializer.toJson(downloadToken), + 'quotesMessageId': serializer.toJson(quotesMessageId), + 'isDeletedFromSender': serializer.toJson(isDeletedFromSender), + 'openedAt': serializer.toJson(openedAt), + 'openedByAll': serializer.toJson(openedByAll), + 'createdAt': serializer.toJson(createdAt), + 'modifiedAt': serializer.toJson(modifiedAt), + 'ackByUser': serializer.toJson(ackByUser), + 'ackByServer': serializer.toJson(ackByServer), + }; + } + + MessagesData copyWith({ + String? groupId, + String? messageId, + Value senderId = const Value.absent(), + String? type, + Value content = const Value.absent(), + Value mediaId = const Value.absent(), + Value additionalMessageData = const Value.absent(), + int? mediaStored, + int? mediaReopened, + Value downloadToken = const Value.absent(), + Value quotesMessageId = const Value.absent(), + int? isDeletedFromSender, + Value openedAt = const Value.absent(), + Value openedByAll = const Value.absent(), + int? createdAt, + Value modifiedAt = const Value.absent(), + Value ackByUser = const Value.absent(), + Value ackByServer = const Value.absent(), + }) => MessagesData( + groupId: groupId ?? this.groupId, + messageId: messageId ?? this.messageId, + senderId: senderId.present ? senderId.value : this.senderId, + type: type ?? this.type, + content: content.present ? content.value : this.content, + mediaId: mediaId.present ? mediaId.value : this.mediaId, + additionalMessageData: additionalMessageData.present + ? additionalMessageData.value + : this.additionalMessageData, + mediaStored: mediaStored ?? this.mediaStored, + mediaReopened: mediaReopened ?? this.mediaReopened, + downloadToken: downloadToken.present + ? downloadToken.value + : this.downloadToken, + quotesMessageId: quotesMessageId.present + ? quotesMessageId.value + : this.quotesMessageId, + isDeletedFromSender: isDeletedFromSender ?? this.isDeletedFromSender, + openedAt: openedAt.present ? openedAt.value : this.openedAt, + openedByAll: openedByAll.present ? openedByAll.value : this.openedByAll, + createdAt: createdAt ?? this.createdAt, + modifiedAt: modifiedAt.present ? modifiedAt.value : this.modifiedAt, + ackByUser: ackByUser.present ? ackByUser.value : this.ackByUser, + ackByServer: ackByServer.present ? ackByServer.value : this.ackByServer, + ); + MessagesData copyWithCompanion(MessagesCompanion data) { + return MessagesData( + groupId: data.groupId.present ? data.groupId.value : this.groupId, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + senderId: data.senderId.present ? data.senderId.value : this.senderId, + type: data.type.present ? data.type.value : this.type, + content: data.content.present ? data.content.value : this.content, + mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId, + additionalMessageData: data.additionalMessageData.present + ? data.additionalMessageData.value + : this.additionalMessageData, + mediaStored: data.mediaStored.present + ? data.mediaStored.value + : this.mediaStored, + mediaReopened: data.mediaReopened.present + ? data.mediaReopened.value + : this.mediaReopened, + downloadToken: data.downloadToken.present + ? data.downloadToken.value + : this.downloadToken, + quotesMessageId: data.quotesMessageId.present + ? data.quotesMessageId.value + : this.quotesMessageId, + isDeletedFromSender: data.isDeletedFromSender.present + ? data.isDeletedFromSender.value + : this.isDeletedFromSender, + openedAt: data.openedAt.present ? data.openedAt.value : this.openedAt, + openedByAll: data.openedByAll.present + ? data.openedByAll.value + : this.openedByAll, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + modifiedAt: data.modifiedAt.present + ? data.modifiedAt.value + : this.modifiedAt, + ackByUser: data.ackByUser.present ? data.ackByUser.value : this.ackByUser, + ackByServer: data.ackByServer.present + ? data.ackByServer.value + : this.ackByServer, + ); + } + + @override + String toString() { + return (StringBuffer('MessagesData(') + ..write('groupId: $groupId, ') + ..write('messageId: $messageId, ') + ..write('senderId: $senderId, ') + ..write('type: $type, ') + ..write('content: $content, ') + ..write('mediaId: $mediaId, ') + ..write('additionalMessageData: $additionalMessageData, ') + ..write('mediaStored: $mediaStored, ') + ..write('mediaReopened: $mediaReopened, ') + ..write('downloadToken: $downloadToken, ') + ..write('quotesMessageId: $quotesMessageId, ') + ..write('isDeletedFromSender: $isDeletedFromSender, ') + ..write('openedAt: $openedAt, ') + ..write('openedByAll: $openedByAll, ') + ..write('createdAt: $createdAt, ') + ..write('modifiedAt: $modifiedAt, ') + ..write('ackByUser: $ackByUser, ') + ..write('ackByServer: $ackByServer') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + groupId, + messageId, + senderId, + type, + content, + mediaId, + $driftBlobEquality.hash(additionalMessageData), + mediaStored, + mediaReopened, + $driftBlobEquality.hash(downloadToken), + quotesMessageId, + isDeletedFromSender, + openedAt, + openedByAll, + createdAt, + modifiedAt, + ackByUser, + ackByServer, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessagesData && + other.groupId == this.groupId && + other.messageId == this.messageId && + other.senderId == this.senderId && + other.type == this.type && + other.content == this.content && + other.mediaId == this.mediaId && + $driftBlobEquality.equals( + other.additionalMessageData, + this.additionalMessageData, + ) && + other.mediaStored == this.mediaStored && + other.mediaReopened == this.mediaReopened && + $driftBlobEquality.equals(other.downloadToken, this.downloadToken) && + other.quotesMessageId == this.quotesMessageId && + other.isDeletedFromSender == this.isDeletedFromSender && + other.openedAt == this.openedAt && + other.openedByAll == this.openedByAll && + other.createdAt == this.createdAt && + other.modifiedAt == this.modifiedAt && + other.ackByUser == this.ackByUser && + other.ackByServer == this.ackByServer); +} + +class MessagesCompanion extends UpdateCompanion { + final Value groupId; + final Value messageId; + final Value senderId; + final Value type; + final Value content; + final Value mediaId; + final Value additionalMessageData; + final Value mediaStored; + final Value mediaReopened; + final Value downloadToken; + final Value quotesMessageId; + final Value isDeletedFromSender; + final Value openedAt; + final Value openedByAll; + final Value createdAt; + final Value modifiedAt; + final Value ackByUser; + final Value ackByServer; + final Value rowid; + const MessagesCompanion({ + this.groupId = const Value.absent(), + this.messageId = const Value.absent(), + this.senderId = const Value.absent(), + this.type = const Value.absent(), + this.content = const Value.absent(), + this.mediaId = const Value.absent(), + this.additionalMessageData = const Value.absent(), + this.mediaStored = const Value.absent(), + this.mediaReopened = const Value.absent(), + this.downloadToken = const Value.absent(), + this.quotesMessageId = const Value.absent(), + this.isDeletedFromSender = const Value.absent(), + this.openedAt = const Value.absent(), + this.openedByAll = const Value.absent(), + this.createdAt = const Value.absent(), + this.modifiedAt = const Value.absent(), + this.ackByUser = const Value.absent(), + this.ackByServer = const Value.absent(), + this.rowid = const Value.absent(), + }); + MessagesCompanion.insert({ + required String groupId, + required String messageId, + this.senderId = const Value.absent(), + required String type, + this.content = const Value.absent(), + this.mediaId = const Value.absent(), + this.additionalMessageData = const Value.absent(), + this.mediaStored = const Value.absent(), + this.mediaReopened = const Value.absent(), + this.downloadToken = const Value.absent(), + this.quotesMessageId = const Value.absent(), + this.isDeletedFromSender = const Value.absent(), + this.openedAt = const Value.absent(), + this.openedByAll = const Value.absent(), + this.createdAt = const Value.absent(), + this.modifiedAt = const Value.absent(), + this.ackByUser = const Value.absent(), + this.ackByServer = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupId = Value(groupId), + messageId = Value(messageId), + type = Value(type); + static Insertable custom({ + Expression? groupId, + Expression? messageId, + Expression? senderId, + Expression? type, + Expression? content, + Expression? mediaId, + Expression? additionalMessageData, + Expression? mediaStored, + Expression? mediaReopened, + Expression? downloadToken, + Expression? quotesMessageId, + Expression? isDeletedFromSender, + Expression? openedAt, + Expression? openedByAll, + Expression? createdAt, + Expression? modifiedAt, + Expression? ackByUser, + Expression? ackByServer, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupId != null) 'group_id': groupId, + if (messageId != null) 'message_id': messageId, + if (senderId != null) 'sender_id': senderId, + if (type != null) 'type': type, + if (content != null) 'content': content, + if (mediaId != null) 'media_id': mediaId, + if (additionalMessageData != null) + 'additional_message_data': additionalMessageData, + if (mediaStored != null) 'media_stored': mediaStored, + if (mediaReopened != null) 'media_reopened': mediaReopened, + if (downloadToken != null) 'download_token': downloadToken, + if (quotesMessageId != null) 'quotes_message_id': quotesMessageId, + if (isDeletedFromSender != null) + 'is_deleted_from_sender': isDeletedFromSender, + if (openedAt != null) 'opened_at': openedAt, + if (openedByAll != null) 'opened_by_all': openedByAll, + if (createdAt != null) 'created_at': createdAt, + if (modifiedAt != null) 'modified_at': modifiedAt, + if (ackByUser != null) 'ack_by_user': ackByUser, + if (ackByServer != null) 'ack_by_server': ackByServer, + if (rowid != null) 'rowid': rowid, + }); + } + + MessagesCompanion copyWith({ + Value? groupId, + Value? messageId, + Value? senderId, + Value? type, + Value? content, + Value? mediaId, + Value? additionalMessageData, + Value? mediaStored, + Value? mediaReopened, + Value? downloadToken, + Value? quotesMessageId, + Value? isDeletedFromSender, + Value? openedAt, + Value? openedByAll, + Value? createdAt, + Value? modifiedAt, + Value? ackByUser, + Value? ackByServer, + Value? rowid, + }) { + return MessagesCompanion( + groupId: groupId ?? this.groupId, + messageId: messageId ?? this.messageId, + senderId: senderId ?? this.senderId, + type: type ?? this.type, + content: content ?? this.content, + mediaId: mediaId ?? this.mediaId, + additionalMessageData: + additionalMessageData ?? this.additionalMessageData, + mediaStored: mediaStored ?? this.mediaStored, + mediaReopened: mediaReopened ?? this.mediaReopened, + downloadToken: downloadToken ?? this.downloadToken, + quotesMessageId: quotesMessageId ?? this.quotesMessageId, + isDeletedFromSender: isDeletedFromSender ?? this.isDeletedFromSender, + openedAt: openedAt ?? this.openedAt, + openedByAll: openedByAll ?? this.openedByAll, + createdAt: createdAt ?? this.createdAt, + modifiedAt: modifiedAt ?? this.modifiedAt, + ackByUser: ackByUser ?? this.ackByUser, + ackByServer: ackByServer ?? this.ackByServer, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (senderId.present) { + map['sender_id'] = Variable(senderId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (mediaId.present) { + map['media_id'] = Variable(mediaId.value); + } + if (additionalMessageData.present) { + map['additional_message_data'] = Variable( + additionalMessageData.value, + ); + } + if (mediaStored.present) { + map['media_stored'] = Variable(mediaStored.value); + } + if (mediaReopened.present) { + map['media_reopened'] = Variable(mediaReopened.value); + } + if (downloadToken.present) { + map['download_token'] = Variable(downloadToken.value); + } + if (quotesMessageId.present) { + map['quotes_message_id'] = Variable(quotesMessageId.value); + } + if (isDeletedFromSender.present) { + map['is_deleted_from_sender'] = Variable(isDeletedFromSender.value); + } + if (openedAt.present) { + map['opened_at'] = Variable(openedAt.value); + } + if (openedByAll.present) { + map['opened_by_all'] = Variable(openedByAll.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (modifiedAt.present) { + map['modified_at'] = Variable(modifiedAt.value); + } + if (ackByUser.present) { + map['ack_by_user'] = Variable(ackByUser.value); + } + if (ackByServer.present) { + map['ack_by_server'] = Variable(ackByServer.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessagesCompanion(') + ..write('groupId: $groupId, ') + ..write('messageId: $messageId, ') + ..write('senderId: $senderId, ') + ..write('type: $type, ') + ..write('content: $content, ') + ..write('mediaId: $mediaId, ') + ..write('additionalMessageData: $additionalMessageData, ') + ..write('mediaStored: $mediaStored, ') + ..write('mediaReopened: $mediaReopened, ') + ..write('downloadToken: $downloadToken, ') + ..write('quotesMessageId: $quotesMessageId, ') + ..write('isDeletedFromSender: $isDeletedFromSender, ') + ..write('openedAt: $openedAt, ') + ..write('openedByAll: $openedByAll, ') + ..write('createdAt: $createdAt, ') + ..write('modifiedAt: $modifiedAt, ') + ..write('ackByUser: $ackByUser, ') + ..write('ackByServer: $ackByServer, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class MessageHistories extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MessageHistories(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn content = GeneratedColumn( + 'content', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + id, + messageId, + contactId, + content, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'message_histories'; + @override + Set get $primaryKey => {id}; + @override + MessageHistoriesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MessageHistoriesData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + ), + content: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}content'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + MessageHistories createAlias(String alias) { + return MessageHistories(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class MessageHistoriesData extends DataClass + implements Insertable { + final int id; + final String messageId; + final int? contactId; + final String? content; + final int createdAt; + const MessageHistoriesData({ + required this.id, + required this.messageId, + this.contactId, + this.content, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['message_id'] = Variable(messageId); + if (!nullToAbsent || contactId != null) { + map['contact_id'] = Variable(contactId); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + map['created_at'] = Variable(createdAt); + return map; + } + + MessageHistoriesCompanion toCompanion(bool nullToAbsent) { + return MessageHistoriesCompanion( + id: Value(id), + messageId: Value(messageId), + contactId: contactId == null && nullToAbsent + ? const Value.absent() + : Value(contactId), + content: content == null && nullToAbsent + ? const Value.absent() + : Value(content), + createdAt: Value(createdAt), + ); + } + + factory MessageHistoriesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessageHistoriesData( + id: serializer.fromJson(json['id']), + messageId: serializer.fromJson(json['messageId']), + contactId: serializer.fromJson(json['contactId']), + content: serializer.fromJson(json['content']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'messageId': serializer.toJson(messageId), + 'contactId': serializer.toJson(contactId), + 'content': serializer.toJson(content), + 'createdAt': serializer.toJson(createdAt), + }; + } + + MessageHistoriesData copyWith({ + int? id, + String? messageId, + Value contactId = const Value.absent(), + Value content = const Value.absent(), + int? createdAt, + }) => MessageHistoriesData( + id: id ?? this.id, + messageId: messageId ?? this.messageId, + contactId: contactId.present ? contactId.value : this.contactId, + content: content.present ? content.value : this.content, + createdAt: createdAt ?? this.createdAt, + ); + MessageHistoriesData copyWithCompanion(MessageHistoriesCompanion data) { + return MessageHistoriesData( + id: data.id.present ? data.id.value : this.id, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + content: data.content.present ? data.content.value : this.content, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('MessageHistoriesData(') + ..write('id: $id, ') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('content: $content, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, messageId, contactId, content, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessageHistoriesData && + other.id == this.id && + other.messageId == this.messageId && + other.contactId == this.contactId && + other.content == this.content && + other.createdAt == this.createdAt); +} + +class MessageHistoriesCompanion extends UpdateCompanion { + final Value id; + final Value messageId; + final Value contactId; + final Value content; + final Value createdAt; + const MessageHistoriesCompanion({ + this.id = const Value.absent(), + this.messageId = const Value.absent(), + this.contactId = const Value.absent(), + this.content = const Value.absent(), + this.createdAt = const Value.absent(), + }); + MessageHistoriesCompanion.insert({ + this.id = const Value.absent(), + required String messageId, + this.contactId = const Value.absent(), + this.content = const Value.absent(), + this.createdAt = const Value.absent(), + }) : messageId = Value(messageId); + static Insertable custom({ + Expression? id, + Expression? messageId, + Expression? contactId, + Expression? content, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (messageId != null) 'message_id': messageId, + if (contactId != null) 'contact_id': contactId, + if (content != null) 'content': content, + if (createdAt != null) 'created_at': createdAt, + }); + } + + MessageHistoriesCompanion copyWith({ + Value? id, + Value? messageId, + Value? contactId, + Value? content, + Value? createdAt, + }) { + return MessageHistoriesCompanion( + id: id ?? this.id, + messageId: messageId ?? this.messageId, + contactId: contactId ?? this.contactId, + content: content ?? this.content, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessageHistoriesCompanion(') + ..write('id: $id, ') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('content: $content, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class Reactions extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Reactions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn emoji = GeneratedColumn( + 'emoji', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn senderId = GeneratedColumn( + 'sender_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [messageId, emoji, senderId, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'reactions'; + @override + Set get $primaryKey => {messageId, senderId, emoji}; + @override + ReactionsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ReactionsData( + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + emoji: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}emoji'], + )!, + senderId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}sender_id'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + Reactions createAlias(String alias) { + return Reactions(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(message_id, sender_id, emoji)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class ReactionsData extends DataClass implements Insertable { + final String messageId; + final String emoji; + final int? senderId; + final int createdAt; + const ReactionsData({ + required this.messageId, + required this.emoji, + this.senderId, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['message_id'] = Variable(messageId); + map['emoji'] = Variable(emoji); + if (!nullToAbsent || senderId != null) { + map['sender_id'] = Variable(senderId); + } + map['created_at'] = Variable(createdAt); + return map; + } + + ReactionsCompanion toCompanion(bool nullToAbsent) { + return ReactionsCompanion( + messageId: Value(messageId), + emoji: Value(emoji), + senderId: senderId == null && nullToAbsent + ? const Value.absent() + : Value(senderId), + createdAt: Value(createdAt), + ); + } + + factory ReactionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ReactionsData( + messageId: serializer.fromJson(json['messageId']), + emoji: serializer.fromJson(json['emoji']), + senderId: serializer.fromJson(json['senderId']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'messageId': serializer.toJson(messageId), + 'emoji': serializer.toJson(emoji), + 'senderId': serializer.toJson(senderId), + 'createdAt': serializer.toJson(createdAt), + }; + } + + ReactionsData copyWith({ + String? messageId, + String? emoji, + Value senderId = const Value.absent(), + int? createdAt, + }) => ReactionsData( + messageId: messageId ?? this.messageId, + emoji: emoji ?? this.emoji, + senderId: senderId.present ? senderId.value : this.senderId, + createdAt: createdAt ?? this.createdAt, + ); + ReactionsData copyWithCompanion(ReactionsCompanion data) { + return ReactionsData( + messageId: data.messageId.present ? data.messageId.value : this.messageId, + emoji: data.emoji.present ? data.emoji.value : this.emoji, + senderId: data.senderId.present ? data.senderId.value : this.senderId, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('ReactionsData(') + ..write('messageId: $messageId, ') + ..write('emoji: $emoji, ') + ..write('senderId: $senderId, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(messageId, emoji, senderId, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ReactionsData && + other.messageId == this.messageId && + other.emoji == this.emoji && + other.senderId == this.senderId && + other.createdAt == this.createdAt); +} + +class ReactionsCompanion extends UpdateCompanion { + final Value messageId; + final Value emoji; + final Value senderId; + final Value createdAt; + final Value rowid; + const ReactionsCompanion({ + this.messageId = const Value.absent(), + this.emoji = const Value.absent(), + this.senderId = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + ReactionsCompanion.insert({ + required String messageId, + required String emoji, + this.senderId = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : messageId = Value(messageId), + emoji = Value(emoji); + static Insertable custom({ + Expression? messageId, + Expression? emoji, + Expression? senderId, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (messageId != null) 'message_id': messageId, + if (emoji != null) 'emoji': emoji, + if (senderId != null) 'sender_id': senderId, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + ReactionsCompanion copyWith({ + Value? messageId, + Value? emoji, + Value? senderId, + Value? createdAt, + Value? rowid, + }) { + return ReactionsCompanion( + messageId: messageId ?? this.messageId, + emoji: emoji ?? this.emoji, + senderId: senderId ?? this.senderId, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (emoji.present) { + map['emoji'] = Variable(emoji.value); + } + if (senderId.present) { + map['sender_id'] = Variable(senderId.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('ReactionsCompanion(') + ..write('messageId: $messageId, ') + ..write('emoji: $emoji, ') + ..write('senderId: $senderId, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class GroupMembers extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + GroupMembers(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES contacts(user_id)', + ); + late final GeneratedColumn memberState = GeneratedColumn( + 'member_state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn groupPublicKey = + GeneratedColumn( + 'group_public_key', + aliasedName, + true, + type: DriftSqlType.blob, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastChatOpened = GeneratedColumn( + 'last_chat_opened', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastTypeIndicator = GeneratedColumn( + 'last_type_indicator', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lastMessage = GeneratedColumn( + 'last_message', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + groupId, + contactId, + memberState, + groupPublicKey, + lastChatOpened, + lastTypeIndicator, + lastMessage, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'group_members'; + @override + Set get $primaryKey => {groupId, contactId}; + @override + GroupMembersData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GroupMembersData( + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + memberState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}member_state'], + ), + groupPublicKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}group_public_key'], + ), + lastChatOpened: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_chat_opened'], + ), + lastTypeIndicator: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_type_indicator'], + ), + lastMessage: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_message'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + GroupMembers createAlias(String alias) { + return GroupMembers(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(group_id, contact_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class GroupMembersData extends DataClass + implements Insertable { + final String groupId; + final int contactId; + final String? memberState; + final i2.Uint8List? groupPublicKey; + final int? lastChatOpened; + final int? lastTypeIndicator; + final int? lastMessage; + final int createdAt; + const GroupMembersData({ + required this.groupId, + required this.contactId, + this.memberState, + this.groupPublicKey, + this.lastChatOpened, + this.lastTypeIndicator, + this.lastMessage, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_id'] = Variable(groupId); + map['contact_id'] = Variable(contactId); + if (!nullToAbsent || memberState != null) { + map['member_state'] = Variable(memberState); + } + if (!nullToAbsent || groupPublicKey != null) { + map['group_public_key'] = Variable(groupPublicKey); + } + if (!nullToAbsent || lastChatOpened != null) { + map['last_chat_opened'] = Variable(lastChatOpened); + } + if (!nullToAbsent || lastTypeIndicator != null) { + map['last_type_indicator'] = Variable(lastTypeIndicator); + } + if (!nullToAbsent || lastMessage != null) { + map['last_message'] = Variable(lastMessage); + } + map['created_at'] = Variable(createdAt); + return map; + } + + GroupMembersCompanion toCompanion(bool nullToAbsent) { + return GroupMembersCompanion( + groupId: Value(groupId), + contactId: Value(contactId), + memberState: memberState == null && nullToAbsent + ? const Value.absent() + : Value(memberState), + groupPublicKey: groupPublicKey == null && nullToAbsent + ? const Value.absent() + : Value(groupPublicKey), + lastChatOpened: lastChatOpened == null && nullToAbsent + ? const Value.absent() + : Value(lastChatOpened), + lastTypeIndicator: lastTypeIndicator == null && nullToAbsent + ? const Value.absent() + : Value(lastTypeIndicator), + lastMessage: lastMessage == null && nullToAbsent + ? const Value.absent() + : Value(lastMessage), + createdAt: Value(createdAt), + ); + } + + factory GroupMembersData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GroupMembersData( + groupId: serializer.fromJson(json['groupId']), + contactId: serializer.fromJson(json['contactId']), + memberState: serializer.fromJson(json['memberState']), + groupPublicKey: serializer.fromJson( + json['groupPublicKey'], + ), + lastChatOpened: serializer.fromJson(json['lastChatOpened']), + lastTypeIndicator: serializer.fromJson(json['lastTypeIndicator']), + lastMessage: serializer.fromJson(json['lastMessage']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupId': serializer.toJson(groupId), + 'contactId': serializer.toJson(contactId), + 'memberState': serializer.toJson(memberState), + 'groupPublicKey': serializer.toJson(groupPublicKey), + 'lastChatOpened': serializer.toJson(lastChatOpened), + 'lastTypeIndicator': serializer.toJson(lastTypeIndicator), + 'lastMessage': serializer.toJson(lastMessage), + 'createdAt': serializer.toJson(createdAt), + }; + } + + GroupMembersData copyWith({ + String? groupId, + int? contactId, + Value memberState = const Value.absent(), + Value groupPublicKey = const Value.absent(), + Value lastChatOpened = const Value.absent(), + Value lastTypeIndicator = const Value.absent(), + Value lastMessage = const Value.absent(), + int? createdAt, + }) => GroupMembersData( + groupId: groupId ?? this.groupId, + contactId: contactId ?? this.contactId, + memberState: memberState.present ? memberState.value : this.memberState, + groupPublicKey: groupPublicKey.present + ? groupPublicKey.value + : this.groupPublicKey, + lastChatOpened: lastChatOpened.present + ? lastChatOpened.value + : this.lastChatOpened, + lastTypeIndicator: lastTypeIndicator.present + ? lastTypeIndicator.value + : this.lastTypeIndicator, + lastMessage: lastMessage.present ? lastMessage.value : this.lastMessage, + createdAt: createdAt ?? this.createdAt, + ); + GroupMembersData copyWithCompanion(GroupMembersCompanion data) { + return GroupMembersData( + groupId: data.groupId.present ? data.groupId.value : this.groupId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + memberState: data.memberState.present + ? data.memberState.value + : this.memberState, + groupPublicKey: data.groupPublicKey.present + ? data.groupPublicKey.value + : this.groupPublicKey, + lastChatOpened: data.lastChatOpened.present + ? data.lastChatOpened.value + : this.lastChatOpened, + lastTypeIndicator: data.lastTypeIndicator.present + ? data.lastTypeIndicator.value + : this.lastTypeIndicator, + lastMessage: data.lastMessage.present + ? data.lastMessage.value + : this.lastMessage, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('GroupMembersData(') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('memberState: $memberState, ') + ..write('groupPublicKey: $groupPublicKey, ') + ..write('lastChatOpened: $lastChatOpened, ') + ..write('lastTypeIndicator: $lastTypeIndicator, ') + ..write('lastMessage: $lastMessage, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + groupId, + contactId, + memberState, + $driftBlobEquality.hash(groupPublicKey), + lastChatOpened, + lastTypeIndicator, + lastMessage, + createdAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GroupMembersData && + other.groupId == this.groupId && + other.contactId == this.contactId && + other.memberState == this.memberState && + $driftBlobEquality.equals( + other.groupPublicKey, + this.groupPublicKey, + ) && + other.lastChatOpened == this.lastChatOpened && + other.lastTypeIndicator == this.lastTypeIndicator && + other.lastMessage == this.lastMessage && + other.createdAt == this.createdAt); +} + +class GroupMembersCompanion extends UpdateCompanion { + final Value groupId; + final Value contactId; + final Value memberState; + final Value groupPublicKey; + final Value lastChatOpened; + final Value lastTypeIndicator; + final Value lastMessage; + final Value createdAt; + final Value rowid; + const GroupMembersCompanion({ + this.groupId = const Value.absent(), + this.contactId = const Value.absent(), + this.memberState = const Value.absent(), + this.groupPublicKey = const Value.absent(), + this.lastChatOpened = const Value.absent(), + this.lastTypeIndicator = const Value.absent(), + this.lastMessage = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + GroupMembersCompanion.insert({ + required String groupId, + required int contactId, + this.memberState = const Value.absent(), + this.groupPublicKey = const Value.absent(), + this.lastChatOpened = const Value.absent(), + this.lastTypeIndicator = const Value.absent(), + this.lastMessage = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupId = Value(groupId), + contactId = Value(contactId); + static Insertable custom({ + Expression? groupId, + Expression? contactId, + Expression? memberState, + Expression? groupPublicKey, + Expression? lastChatOpened, + Expression? lastTypeIndicator, + Expression? lastMessage, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupId != null) 'group_id': groupId, + if (contactId != null) 'contact_id': contactId, + if (memberState != null) 'member_state': memberState, + if (groupPublicKey != null) 'group_public_key': groupPublicKey, + if (lastChatOpened != null) 'last_chat_opened': lastChatOpened, + if (lastTypeIndicator != null) 'last_type_indicator': lastTypeIndicator, + if (lastMessage != null) 'last_message': lastMessage, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + GroupMembersCompanion copyWith({ + Value? groupId, + Value? contactId, + Value? memberState, + Value? groupPublicKey, + Value? lastChatOpened, + Value? lastTypeIndicator, + Value? lastMessage, + Value? createdAt, + Value? rowid, + }) { + return GroupMembersCompanion( + groupId: groupId ?? this.groupId, + contactId: contactId ?? this.contactId, + memberState: memberState ?? this.memberState, + groupPublicKey: groupPublicKey ?? this.groupPublicKey, + lastChatOpened: lastChatOpened ?? this.lastChatOpened, + lastTypeIndicator: lastTypeIndicator ?? this.lastTypeIndicator, + lastMessage: lastMessage ?? this.lastMessage, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (memberState.present) { + map['member_state'] = Variable(memberState.value); + } + if (groupPublicKey.present) { + map['group_public_key'] = Variable(groupPublicKey.value); + } + if (lastChatOpened.present) { + map['last_chat_opened'] = Variable(lastChatOpened.value); + } + if (lastTypeIndicator.present) { + map['last_type_indicator'] = Variable(lastTypeIndicator.value); + } + if (lastMessage.present) { + map['last_message'] = Variable(lastMessage.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('GroupMembersCompanion(') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('memberState: $memberState, ') + ..write('groupPublicKey: $groupPublicKey, ') + ..write('lastChatOpened: $lastChatOpened, ') + ..write('lastTypeIndicator: $lastTypeIndicator, ') + ..write('lastMessage: $lastMessage, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Receipts extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Receipts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn receiptId = GeneratedColumn( + 'receipt_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn message = + GeneratedColumn( + 'message', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn contactWillSendsReceipt = + GeneratedColumn( + 'contact_will_sends_receipt', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 1 CHECK (contact_will_sends_receipt IN (0, 1))', + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn + willBeRetriedByMediaUpload = GeneratedColumn( + 'will_be_retried_by_media_upload', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (will_be_retried_by_media_upload IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn markForRetry = GeneratedColumn( + 'mark_for_retry', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn markForRetryAfterAccepted = + GeneratedColumn( + 'mark_for_retry_after_accepted', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn ackByServerAt = GeneratedColumn( + 'ack_by_server_at', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn retryCount = GeneratedColumn( + 'retry_count', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn lastRetry = GeneratedColumn( + 'last_retry', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + receiptId, + contactId, + messageId, + message, + contactWillSendsReceipt, + willBeRetriedByMediaUpload, + markForRetry, + markForRetryAfterAccepted, + ackByServerAt, + retryCount, + lastRetry, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'receipts'; + @override + Set get $primaryKey => {receiptId}; + @override + ReceiptsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ReceiptsData( + receiptId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}receipt_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + ), + message: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}message'], + )!, + contactWillSendsReceipt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_will_sends_receipt'], + )!, + willBeRetriedByMediaUpload: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}will_be_retried_by_media_upload'], + )!, + markForRetry: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mark_for_retry'], + ), + markForRetryAfterAccepted: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mark_for_retry_after_accepted'], + ), + ackByServerAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}ack_by_server_at'], + ), + retryCount: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}retry_count'], + )!, + lastRetry: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}last_retry'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + Receipts createAlias(String alias) { + return Receipts(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(receipt_id)']; + @override + bool get dontWriteConstraints => true; +} + +class ReceiptsData extends DataClass implements Insertable { + final String receiptId; + final int contactId; + final String? messageId; + final i2.Uint8List message; + final int contactWillSendsReceipt; + final int willBeRetriedByMediaUpload; + final int? markForRetry; + final int? markForRetryAfterAccepted; + final int? ackByServerAt; + final int retryCount; + final int? lastRetry; + final int createdAt; + const ReceiptsData({ + required this.receiptId, + required this.contactId, + this.messageId, + required this.message, + required this.contactWillSendsReceipt, + required this.willBeRetriedByMediaUpload, + this.markForRetry, + this.markForRetryAfterAccepted, + this.ackByServerAt, + required this.retryCount, + this.lastRetry, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['receipt_id'] = Variable(receiptId); + map['contact_id'] = Variable(contactId); + if (!nullToAbsent || messageId != null) { + map['message_id'] = Variable(messageId); + } + map['message'] = Variable(message); + map['contact_will_sends_receipt'] = Variable(contactWillSendsReceipt); + map['will_be_retried_by_media_upload'] = Variable( + willBeRetriedByMediaUpload, + ); + if (!nullToAbsent || markForRetry != null) { + map['mark_for_retry'] = Variable(markForRetry); + } + if (!nullToAbsent || markForRetryAfterAccepted != null) { + map['mark_for_retry_after_accepted'] = Variable( + markForRetryAfterAccepted, + ); + } + if (!nullToAbsent || ackByServerAt != null) { + map['ack_by_server_at'] = Variable(ackByServerAt); + } + map['retry_count'] = Variable(retryCount); + if (!nullToAbsent || lastRetry != null) { + map['last_retry'] = Variable(lastRetry); + } + map['created_at'] = Variable(createdAt); + return map; + } + + ReceiptsCompanion toCompanion(bool nullToAbsent) { + return ReceiptsCompanion( + receiptId: Value(receiptId), + contactId: Value(contactId), + messageId: messageId == null && nullToAbsent + ? const Value.absent() + : Value(messageId), + message: Value(message), + contactWillSendsReceipt: Value(contactWillSendsReceipt), + willBeRetriedByMediaUpload: Value(willBeRetriedByMediaUpload), + markForRetry: markForRetry == null && nullToAbsent + ? const Value.absent() + : Value(markForRetry), + markForRetryAfterAccepted: + markForRetryAfterAccepted == null && nullToAbsent + ? const Value.absent() + : Value(markForRetryAfterAccepted), + ackByServerAt: ackByServerAt == null && nullToAbsent + ? const Value.absent() + : Value(ackByServerAt), + retryCount: Value(retryCount), + lastRetry: lastRetry == null && nullToAbsent + ? const Value.absent() + : Value(lastRetry), + createdAt: Value(createdAt), + ); + } + + factory ReceiptsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ReceiptsData( + receiptId: serializer.fromJson(json['receiptId']), + contactId: serializer.fromJson(json['contactId']), + messageId: serializer.fromJson(json['messageId']), + message: serializer.fromJson(json['message']), + contactWillSendsReceipt: serializer.fromJson( + json['contactWillSendsReceipt'], + ), + willBeRetriedByMediaUpload: serializer.fromJson( + json['willBeRetriedByMediaUpload'], + ), + markForRetry: serializer.fromJson(json['markForRetry']), + markForRetryAfterAccepted: serializer.fromJson( + json['markForRetryAfterAccepted'], + ), + ackByServerAt: serializer.fromJson(json['ackByServerAt']), + retryCount: serializer.fromJson(json['retryCount']), + lastRetry: serializer.fromJson(json['lastRetry']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'receiptId': serializer.toJson(receiptId), + 'contactId': serializer.toJson(contactId), + 'messageId': serializer.toJson(messageId), + 'message': serializer.toJson(message), + 'contactWillSendsReceipt': serializer.toJson( + contactWillSendsReceipt, + ), + 'willBeRetriedByMediaUpload': serializer.toJson( + willBeRetriedByMediaUpload, + ), + 'markForRetry': serializer.toJson(markForRetry), + 'markForRetryAfterAccepted': serializer.toJson( + markForRetryAfterAccepted, + ), + 'ackByServerAt': serializer.toJson(ackByServerAt), + 'retryCount': serializer.toJson(retryCount), + 'lastRetry': serializer.toJson(lastRetry), + 'createdAt': serializer.toJson(createdAt), + }; + } + + ReceiptsData copyWith({ + String? receiptId, + int? contactId, + Value messageId = const Value.absent(), + i2.Uint8List? message, + int? contactWillSendsReceipt, + int? willBeRetriedByMediaUpload, + Value markForRetry = const Value.absent(), + Value markForRetryAfterAccepted = const Value.absent(), + Value ackByServerAt = const Value.absent(), + int? retryCount, + Value lastRetry = const Value.absent(), + int? createdAt, + }) => ReceiptsData( + receiptId: receiptId ?? this.receiptId, + contactId: contactId ?? this.contactId, + messageId: messageId.present ? messageId.value : this.messageId, + message: message ?? this.message, + contactWillSendsReceipt: + contactWillSendsReceipt ?? this.contactWillSendsReceipt, + willBeRetriedByMediaUpload: + willBeRetriedByMediaUpload ?? this.willBeRetriedByMediaUpload, + markForRetry: markForRetry.present ? markForRetry.value : this.markForRetry, + markForRetryAfterAccepted: markForRetryAfterAccepted.present + ? markForRetryAfterAccepted.value + : this.markForRetryAfterAccepted, + ackByServerAt: ackByServerAt.present + ? ackByServerAt.value + : this.ackByServerAt, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry.present ? lastRetry.value : this.lastRetry, + createdAt: createdAt ?? this.createdAt, + ); + ReceiptsData copyWithCompanion(ReceiptsCompanion data) { + return ReceiptsData( + receiptId: data.receiptId.present ? data.receiptId.value : this.receiptId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + messageId: data.messageId.present ? data.messageId.value : this.messageId, + message: data.message.present ? data.message.value : this.message, + contactWillSendsReceipt: data.contactWillSendsReceipt.present + ? data.contactWillSendsReceipt.value + : this.contactWillSendsReceipt, + willBeRetriedByMediaUpload: data.willBeRetriedByMediaUpload.present + ? data.willBeRetriedByMediaUpload.value + : this.willBeRetriedByMediaUpload, + markForRetry: data.markForRetry.present + ? data.markForRetry.value + : this.markForRetry, + markForRetryAfterAccepted: data.markForRetryAfterAccepted.present + ? data.markForRetryAfterAccepted.value + : this.markForRetryAfterAccepted, + ackByServerAt: data.ackByServerAt.present + ? data.ackByServerAt.value + : this.ackByServerAt, + retryCount: data.retryCount.present + ? data.retryCount.value + : this.retryCount, + lastRetry: data.lastRetry.present ? data.lastRetry.value : this.lastRetry, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('ReceiptsData(') + ..write('receiptId: $receiptId, ') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('message: $message, ') + ..write('contactWillSendsReceipt: $contactWillSendsReceipt, ') + ..write('willBeRetriedByMediaUpload: $willBeRetriedByMediaUpload, ') + ..write('markForRetry: $markForRetry, ') + ..write('markForRetryAfterAccepted: $markForRetryAfterAccepted, ') + ..write('ackByServerAt: $ackByServerAt, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + receiptId, + contactId, + messageId, + $driftBlobEquality.hash(message), + contactWillSendsReceipt, + willBeRetriedByMediaUpload, + markForRetry, + markForRetryAfterAccepted, + ackByServerAt, + retryCount, + lastRetry, + createdAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ReceiptsData && + other.receiptId == this.receiptId && + other.contactId == this.contactId && + other.messageId == this.messageId && + $driftBlobEquality.equals(other.message, this.message) && + other.contactWillSendsReceipt == this.contactWillSendsReceipt && + other.willBeRetriedByMediaUpload == this.willBeRetriedByMediaUpload && + other.markForRetry == this.markForRetry && + other.markForRetryAfterAccepted == this.markForRetryAfterAccepted && + other.ackByServerAt == this.ackByServerAt && + other.retryCount == this.retryCount && + other.lastRetry == this.lastRetry && + other.createdAt == this.createdAt); +} + +class ReceiptsCompanion extends UpdateCompanion { + final Value receiptId; + final Value contactId; + final Value messageId; + final Value message; + final Value contactWillSendsReceipt; + final Value willBeRetriedByMediaUpload; + final Value markForRetry; + final Value markForRetryAfterAccepted; + final Value ackByServerAt; + final Value retryCount; + final Value lastRetry; + final Value createdAt; + final Value rowid; + const ReceiptsCompanion({ + this.receiptId = const Value.absent(), + this.contactId = const Value.absent(), + this.messageId = const Value.absent(), + this.message = const Value.absent(), + this.contactWillSendsReceipt = const Value.absent(), + this.willBeRetriedByMediaUpload = const Value.absent(), + this.markForRetry = const Value.absent(), + this.markForRetryAfterAccepted = const Value.absent(), + this.ackByServerAt = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + ReceiptsCompanion.insert({ + required String receiptId, + required int contactId, + this.messageId = const Value.absent(), + required i2.Uint8List message, + this.contactWillSendsReceipt = const Value.absent(), + this.willBeRetriedByMediaUpload = const Value.absent(), + this.markForRetry = const Value.absent(), + this.markForRetryAfterAccepted = const Value.absent(), + this.ackByServerAt = const Value.absent(), + this.retryCount = const Value.absent(), + this.lastRetry = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : receiptId = Value(receiptId), + contactId = Value(contactId), + message = Value(message); + static Insertable custom({ + Expression? receiptId, + Expression? contactId, + Expression? messageId, + Expression? message, + Expression? contactWillSendsReceipt, + Expression? willBeRetriedByMediaUpload, + Expression? markForRetry, + Expression? markForRetryAfterAccepted, + Expression? ackByServerAt, + Expression? retryCount, + Expression? lastRetry, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (receiptId != null) 'receipt_id': receiptId, + if (contactId != null) 'contact_id': contactId, + if (messageId != null) 'message_id': messageId, + if (message != null) 'message': message, + if (contactWillSendsReceipt != null) + 'contact_will_sends_receipt': contactWillSendsReceipt, + if (willBeRetriedByMediaUpload != null) + 'will_be_retried_by_media_upload': willBeRetriedByMediaUpload, + if (markForRetry != null) 'mark_for_retry': markForRetry, + if (markForRetryAfterAccepted != null) + 'mark_for_retry_after_accepted': markForRetryAfterAccepted, + if (ackByServerAt != null) 'ack_by_server_at': ackByServerAt, + if (retryCount != null) 'retry_count': retryCount, + if (lastRetry != null) 'last_retry': lastRetry, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + ReceiptsCompanion copyWith({ + Value? receiptId, + Value? contactId, + Value? messageId, + Value? message, + Value? contactWillSendsReceipt, + Value? willBeRetriedByMediaUpload, + Value? markForRetry, + Value? markForRetryAfterAccepted, + Value? ackByServerAt, + Value? retryCount, + Value? lastRetry, + Value? createdAt, + Value? rowid, + }) { + return ReceiptsCompanion( + receiptId: receiptId ?? this.receiptId, + contactId: contactId ?? this.contactId, + messageId: messageId ?? this.messageId, + message: message ?? this.message, + contactWillSendsReceipt: + contactWillSendsReceipt ?? this.contactWillSendsReceipt, + willBeRetriedByMediaUpload: + willBeRetriedByMediaUpload ?? this.willBeRetriedByMediaUpload, + markForRetry: markForRetry ?? this.markForRetry, + markForRetryAfterAccepted: + markForRetryAfterAccepted ?? this.markForRetryAfterAccepted, + ackByServerAt: ackByServerAt ?? this.ackByServerAt, + retryCount: retryCount ?? this.retryCount, + lastRetry: lastRetry ?? this.lastRetry, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (receiptId.present) { + map['receipt_id'] = Variable(receiptId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (message.present) { + map['message'] = Variable(message.value); + } + if (contactWillSendsReceipt.present) { + map['contact_will_sends_receipt'] = Variable( + contactWillSendsReceipt.value, + ); + } + if (willBeRetriedByMediaUpload.present) { + map['will_be_retried_by_media_upload'] = Variable( + willBeRetriedByMediaUpload.value, + ); + } + if (markForRetry.present) { + map['mark_for_retry'] = Variable(markForRetry.value); + } + if (markForRetryAfterAccepted.present) { + map['mark_for_retry_after_accepted'] = Variable( + markForRetryAfterAccepted.value, + ); + } + if (ackByServerAt.present) { + map['ack_by_server_at'] = Variable(ackByServerAt.value); + } + if (retryCount.present) { + map['retry_count'] = Variable(retryCount.value); + } + if (lastRetry.present) { + map['last_retry'] = Variable(lastRetry.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('ReceiptsCompanion(') + ..write('receiptId: $receiptId, ') + ..write('contactId: $contactId, ') + ..write('messageId: $messageId, ') + ..write('message: $message, ') + ..write('contactWillSendsReceipt: $contactWillSendsReceipt, ') + ..write('willBeRetriedByMediaUpload: $willBeRetriedByMediaUpload, ') + ..write('markForRetry: $markForRetry, ') + ..write('markForRetryAfterAccepted: $markForRetryAfterAccepted, ') + ..write('ackByServerAt: $ackByServerAt, ') + ..write('retryCount: $retryCount, ') + ..write('lastRetry: $lastRetry, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class ReceivedReceipts extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + ReceivedReceipts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn receiptId = GeneratedColumn( + 'receipt_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [receiptId, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'received_receipts'; + @override + Set get $primaryKey => {receiptId}; + @override + ReceivedReceiptsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ReceivedReceiptsData( + receiptId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}receipt_id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + ReceivedReceipts createAlias(String alias) { + return ReceivedReceipts(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(receipt_id)']; + @override + bool get dontWriteConstraints => true; +} + +class ReceivedReceiptsData extends DataClass + implements Insertable { + final String receiptId; + final int createdAt; + const ReceivedReceiptsData({ + required this.receiptId, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['receipt_id'] = Variable(receiptId); + map['created_at'] = Variable(createdAt); + return map; + } + + ReceivedReceiptsCompanion toCompanion(bool nullToAbsent) { + return ReceivedReceiptsCompanion( + receiptId: Value(receiptId), + createdAt: Value(createdAt), + ); + } + + factory ReceivedReceiptsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ReceivedReceiptsData( + receiptId: serializer.fromJson(json['receiptId']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'receiptId': serializer.toJson(receiptId), + 'createdAt': serializer.toJson(createdAt), + }; + } + + ReceivedReceiptsData copyWith({String? receiptId, int? createdAt}) => + ReceivedReceiptsData( + receiptId: receiptId ?? this.receiptId, + createdAt: createdAt ?? this.createdAt, + ); + ReceivedReceiptsData copyWithCompanion(ReceivedReceiptsCompanion data) { + return ReceivedReceiptsData( + receiptId: data.receiptId.present ? data.receiptId.value : this.receiptId, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('ReceivedReceiptsData(') + ..write('receiptId: $receiptId, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(receiptId, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ReceivedReceiptsData && + other.receiptId == this.receiptId && + other.createdAt == this.createdAt); +} + +class ReceivedReceiptsCompanion extends UpdateCompanion { + final Value receiptId; + final Value createdAt; + final Value rowid; + const ReceivedReceiptsCompanion({ + this.receiptId = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + ReceivedReceiptsCompanion.insert({ + required String receiptId, + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : receiptId = Value(receiptId); + static Insertable custom({ + Expression? receiptId, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (receiptId != null) 'receipt_id': receiptId, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + ReceivedReceiptsCompanion copyWith({ + Value? receiptId, + Value? createdAt, + Value? rowid, + }) { + return ReceivedReceiptsCompanion( + receiptId: receiptId ?? this.receiptId, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (receiptId.present) { + map['receipt_id'] = Variable(receiptId.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('ReceivedReceiptsCompanion(') + ..write('receiptId: $receiptId, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn identityKey = + GeneratedColumn( + 'identity_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + 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.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalIdentityKeyStores createAlias(String alias) { + return SignalIdentityKeyStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(device_id, name)']; + @override + bool get dontWriteConstraints => true; +} + +class SignalIdentityKeyStoresData extends DataClass + implements Insertable { + final int deviceId; + final String name; + final i2.Uint8List identityKey; + final int 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, + int? 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn preKey = + GeneratedColumn( + 'pre_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + 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.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalPreKeyStores createAlias(String alias) { + return SignalPreKeyStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(pre_key_id)']; + @override + bool get dontWriteConstraints => true; +} + +class SignalPreKeyStoresData extends DataClass + implements Insertable { + final int preKeyId; + final i2.Uint8List preKey; + final int 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, + int? 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn senderKey = + GeneratedColumn( + 'sender_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @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); + } + + @override + List get customConstraints => const ['PRIMARY KEY(sender_key_name)']; + @override + bool get dontWriteConstraints => true; +} + +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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn sessionRecord = + GeneratedColumn( + 'session_record', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + 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.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalSessionStores createAlias(String alias) { + return SignalSessionStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(device_id, name)']; + @override + bool get dontWriteConstraints => true; +} + +class SignalSessionStoresData extends DataClass + implements Insertable { + final int deviceId; + final String name; + final i2.Uint8List sessionRecord; + final int 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, + int? 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 SignalSignedPreKeyStores extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SignalSignedPreKeyStores(this.attachedDatabase, [this._alias]); + late final GeneratedColumn signedPreKeyId = GeneratedColumn( + 'signed_pre_key_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn signedPreKey = + GeneratedColumn( + 'signed_pre_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + signedPreKeyId, + signedPreKey, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'signal_signed_pre_key_stores'; + @override + Set get $primaryKey => {signedPreKeyId}; + @override + SignalSignedPreKeyStoresData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SignalSignedPreKeyStoresData( + signedPreKeyId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}signed_pre_key_id'], + )!, + signedPreKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}signed_pre_key'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + SignalSignedPreKeyStores createAlias(String alias) { + return SignalSignedPreKeyStores(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(signed_pre_key_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class SignalSignedPreKeyStoresData extends DataClass + implements Insertable { + final int signedPreKeyId; + final i2.Uint8List signedPreKey; + final int createdAt; + const SignalSignedPreKeyStoresData({ + required this.signedPreKeyId, + required this.signedPreKey, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['signed_pre_key_id'] = Variable(signedPreKeyId); + map['signed_pre_key'] = Variable(signedPreKey); + map['created_at'] = Variable(createdAt); + return map; + } + + SignalSignedPreKeyStoresCompanion toCompanion(bool nullToAbsent) { + return SignalSignedPreKeyStoresCompanion( + signedPreKeyId: Value(signedPreKeyId), + signedPreKey: Value(signedPreKey), + createdAt: Value(createdAt), + ); + } + + factory SignalSignedPreKeyStoresData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SignalSignedPreKeyStoresData( + signedPreKeyId: serializer.fromJson(json['signedPreKeyId']), + signedPreKey: serializer.fromJson(json['signedPreKey']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'signedPreKeyId': serializer.toJson(signedPreKeyId), + 'signedPreKey': serializer.toJson(signedPreKey), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SignalSignedPreKeyStoresData copyWith({ + int? signedPreKeyId, + i2.Uint8List? signedPreKey, + int? createdAt, + }) => SignalSignedPreKeyStoresData( + signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId, + signedPreKey: signedPreKey ?? this.signedPreKey, + createdAt: createdAt ?? this.createdAt, + ); + SignalSignedPreKeyStoresData copyWithCompanion( + SignalSignedPreKeyStoresCompanion data, + ) { + return SignalSignedPreKeyStoresData( + signedPreKeyId: data.signedPreKeyId.present + ? data.signedPreKeyId.value + : this.signedPreKeyId, + signedPreKey: data.signedPreKey.present + ? data.signedPreKey.value + : this.signedPreKey, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SignalSignedPreKeyStoresData(') + ..write('signedPreKeyId: $signedPreKeyId, ') + ..write('signedPreKey: $signedPreKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + signedPreKeyId, + $driftBlobEquality.hash(signedPreKey), + createdAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SignalSignedPreKeyStoresData && + other.signedPreKeyId == this.signedPreKeyId && + $driftBlobEquality.equals(other.signedPreKey, this.signedPreKey) && + other.createdAt == this.createdAt); +} + +class SignalSignedPreKeyStoresCompanion + extends UpdateCompanion { + final Value signedPreKeyId; + final Value signedPreKey; + final Value createdAt; + const SignalSignedPreKeyStoresCompanion({ + this.signedPreKeyId = const Value.absent(), + this.signedPreKey = const Value.absent(), + this.createdAt = const Value.absent(), + }); + SignalSignedPreKeyStoresCompanion.insert({ + this.signedPreKeyId = const Value.absent(), + required i2.Uint8List signedPreKey, + this.createdAt = const Value.absent(), + }) : signedPreKey = Value(signedPreKey); + static Insertable custom({ + Expression? signedPreKeyId, + Expression? signedPreKey, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (signedPreKeyId != null) 'signed_pre_key_id': signedPreKeyId, + if (signedPreKey != null) 'signed_pre_key': signedPreKey, + if (createdAt != null) 'created_at': createdAt, + }); + } + + SignalSignedPreKeyStoresCompanion copyWith({ + Value? signedPreKeyId, + Value? signedPreKey, + Value? createdAt, + }) { + return SignalSignedPreKeyStoresCompanion( + signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId, + signedPreKey: signedPreKey ?? this.signedPreKey, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (signedPreKeyId.present) { + map['signed_pre_key_id'] = Variable(signedPreKeyId.value); + } + if (signedPreKey.present) { + map['signed_pre_key'] = Variable(signedPreKey.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SignalSignedPreKeyStoresCompanion(') + ..write('signedPreKeyId: $signedPreKeyId, ') + ..write('signedPreKey: $signedPreKey, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class MessageActions extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MessageActions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn messageId = GeneratedColumn( + 'message_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES messages(message_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn actionAt = GeneratedColumn( + 'action_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [messageId, contactId, type, actionAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'message_actions'; + @override + Set get $primaryKey => {messageId, contactId, type}; + @override + MessageActionsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MessageActionsData( + messageId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}message_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + actionAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}action_at'], + )!, + ); + } + + @override + MessageActions createAlias(String alias) { + return MessageActions(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(message_id, contact_id, type)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class MessageActionsData extends DataClass + implements Insertable { + final String messageId; + final int contactId; + final String type; + final int actionAt; + const MessageActionsData({ + required this.messageId, + required this.contactId, + required this.type, + required this.actionAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['message_id'] = Variable(messageId); + map['contact_id'] = Variable(contactId); + map['type'] = Variable(type); + map['action_at'] = Variable(actionAt); + return map; + } + + MessageActionsCompanion toCompanion(bool nullToAbsent) { + return MessageActionsCompanion( + messageId: Value(messageId), + contactId: Value(contactId), + type: Value(type), + actionAt: Value(actionAt), + ); + } + + factory MessageActionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MessageActionsData( + messageId: serializer.fromJson(json['messageId']), + contactId: serializer.fromJson(json['contactId']), + type: serializer.fromJson(json['type']), + actionAt: serializer.fromJson(json['actionAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'messageId': serializer.toJson(messageId), + 'contactId': serializer.toJson(contactId), + 'type': serializer.toJson(type), + 'actionAt': serializer.toJson(actionAt), + }; + } + + MessageActionsData copyWith({ + String? messageId, + int? contactId, + String? type, + int? actionAt, + }) => MessageActionsData( + messageId: messageId ?? this.messageId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + ); + MessageActionsData copyWithCompanion(MessageActionsCompanion data) { + return MessageActionsData( + messageId: data.messageId.present ? data.messageId.value : this.messageId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + type: data.type.present ? data.type.value : this.type, + actionAt: data.actionAt.present ? data.actionAt.value : this.actionAt, + ); + } + + @override + String toString() { + return (StringBuffer('MessageActionsData(') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('actionAt: $actionAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(messageId, contactId, type, actionAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MessageActionsData && + other.messageId == this.messageId && + other.contactId == this.contactId && + other.type == this.type && + other.actionAt == this.actionAt); +} + +class MessageActionsCompanion extends UpdateCompanion { + final Value messageId; + final Value contactId; + final Value type; + final Value actionAt; + final Value rowid; + const MessageActionsCompanion({ + this.messageId = const Value.absent(), + this.contactId = const Value.absent(), + this.type = const Value.absent(), + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + MessageActionsCompanion.insert({ + required String messageId, + required int contactId, + required String type, + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : messageId = Value(messageId), + contactId = Value(contactId), + type = Value(type); + static Insertable custom({ + Expression? messageId, + Expression? contactId, + Expression? type, + Expression? actionAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (messageId != null) 'message_id': messageId, + if (contactId != null) 'contact_id': contactId, + if (type != null) 'type': type, + if (actionAt != null) 'action_at': actionAt, + if (rowid != null) 'rowid': rowid, + }); + } + + MessageActionsCompanion copyWith({ + Value? messageId, + Value? contactId, + Value? type, + Value? actionAt, + Value? rowid, + }) { + return MessageActionsCompanion( + messageId: messageId ?? this.messageId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (messageId.present) { + map['message_id'] = Variable(messageId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (actionAt.present) { + map['action_at'] = Variable(actionAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MessageActionsCompanion(') + ..write('messageId: $messageId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('actionAt: $actionAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class GroupHistories extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + GroupHistories(this.attachedDatabase, [this._alias]); + late final GeneratedColumn groupHistoryId = GeneratedColumn( + 'group_history_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)', + ); + late final GeneratedColumn affectedContactId = GeneratedColumn( + 'affected_contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn oldGroupName = GeneratedColumn( + 'old_group_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn newGroupName = GeneratedColumn( + 'new_group_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn newDeleteMessagesAfterMilliseconds = + GeneratedColumn( + 'new_delete_messages_after_milliseconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn actionAt = GeneratedColumn( + 'action_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + groupHistoryId, + groupId, + contactId, + affectedContactId, + oldGroupName, + newGroupName, + newDeleteMessagesAfterMilliseconds, + type, + actionAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'group_histories'; + @override + Set get $primaryKey => {groupHistoryId}; + @override + GroupHistoriesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GroupHistoriesData( + groupHistoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_history_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + ), + affectedContactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}affected_contact_id'], + ), + oldGroupName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}old_group_name'], + ), + newGroupName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}new_group_name'], + ), + newDeleteMessagesAfterMilliseconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}new_delete_messages_after_milliseconds'], + ), + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + actionAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}action_at'], + )!, + ); + } + + @override + GroupHistories createAlias(String alias) { + return GroupHistories(attachedDatabase, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY(group_history_id)']; + @override + bool get dontWriteConstraints => true; +} + +class GroupHistoriesData extends DataClass + implements Insertable { + final String groupHistoryId; + final String groupId; + final int? contactId; + final int? affectedContactId; + final String? oldGroupName; + final String? newGroupName; + final int? newDeleteMessagesAfterMilliseconds; + final String type; + final int actionAt; + const GroupHistoriesData({ + required this.groupHistoryId, + required this.groupId, + this.contactId, + this.affectedContactId, + this.oldGroupName, + this.newGroupName, + this.newDeleteMessagesAfterMilliseconds, + required this.type, + required this.actionAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_history_id'] = Variable(groupHistoryId); + map['group_id'] = Variable(groupId); + if (!nullToAbsent || contactId != null) { + map['contact_id'] = Variable(contactId); + } + if (!nullToAbsent || affectedContactId != null) { + map['affected_contact_id'] = Variable(affectedContactId); + } + if (!nullToAbsent || oldGroupName != null) { + map['old_group_name'] = Variable(oldGroupName); + } + if (!nullToAbsent || newGroupName != null) { + map['new_group_name'] = Variable(newGroupName); + } + if (!nullToAbsent || newDeleteMessagesAfterMilliseconds != null) { + map['new_delete_messages_after_milliseconds'] = Variable( + newDeleteMessagesAfterMilliseconds, + ); + } + map['type'] = Variable(type); + map['action_at'] = Variable(actionAt); + return map; + } + + GroupHistoriesCompanion toCompanion(bool nullToAbsent) { + return GroupHistoriesCompanion( + groupHistoryId: Value(groupHistoryId), + groupId: Value(groupId), + contactId: contactId == null && nullToAbsent + ? const Value.absent() + : Value(contactId), + affectedContactId: affectedContactId == null && nullToAbsent + ? const Value.absent() + : Value(affectedContactId), + oldGroupName: oldGroupName == null && nullToAbsent + ? const Value.absent() + : Value(oldGroupName), + newGroupName: newGroupName == null && nullToAbsent + ? const Value.absent() + : Value(newGroupName), + newDeleteMessagesAfterMilliseconds: + newDeleteMessagesAfterMilliseconds == null && nullToAbsent + ? const Value.absent() + : Value(newDeleteMessagesAfterMilliseconds), + type: Value(type), + actionAt: Value(actionAt), + ); + } + + factory GroupHistoriesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GroupHistoriesData( + groupHistoryId: serializer.fromJson(json['groupHistoryId']), + groupId: serializer.fromJson(json['groupId']), + contactId: serializer.fromJson(json['contactId']), + affectedContactId: serializer.fromJson(json['affectedContactId']), + oldGroupName: serializer.fromJson(json['oldGroupName']), + newGroupName: serializer.fromJson(json['newGroupName']), + newDeleteMessagesAfterMilliseconds: serializer.fromJson( + json['newDeleteMessagesAfterMilliseconds'], + ), + type: serializer.fromJson(json['type']), + actionAt: serializer.fromJson(json['actionAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupHistoryId': serializer.toJson(groupHistoryId), + 'groupId': serializer.toJson(groupId), + 'contactId': serializer.toJson(contactId), + 'affectedContactId': serializer.toJson(affectedContactId), + 'oldGroupName': serializer.toJson(oldGroupName), + 'newGroupName': serializer.toJson(newGroupName), + 'newDeleteMessagesAfterMilliseconds': serializer.toJson( + newDeleteMessagesAfterMilliseconds, + ), + 'type': serializer.toJson(type), + 'actionAt': serializer.toJson(actionAt), + }; + } + + GroupHistoriesData copyWith({ + String? groupHistoryId, + String? groupId, + Value contactId = const Value.absent(), + Value affectedContactId = const Value.absent(), + Value oldGroupName = const Value.absent(), + Value newGroupName = const Value.absent(), + Value newDeleteMessagesAfterMilliseconds = const Value.absent(), + String? type, + int? actionAt, + }) => GroupHistoriesData( + groupHistoryId: groupHistoryId ?? this.groupHistoryId, + groupId: groupId ?? this.groupId, + contactId: contactId.present ? contactId.value : this.contactId, + affectedContactId: affectedContactId.present + ? affectedContactId.value + : this.affectedContactId, + oldGroupName: oldGroupName.present ? oldGroupName.value : this.oldGroupName, + newGroupName: newGroupName.present ? newGroupName.value : this.newGroupName, + newDeleteMessagesAfterMilliseconds: + newDeleteMessagesAfterMilliseconds.present + ? newDeleteMessagesAfterMilliseconds.value + : this.newDeleteMessagesAfterMilliseconds, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + ); + GroupHistoriesData copyWithCompanion(GroupHistoriesCompanion data) { + return GroupHistoriesData( + groupHistoryId: data.groupHistoryId.present + ? data.groupHistoryId.value + : this.groupHistoryId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + affectedContactId: data.affectedContactId.present + ? data.affectedContactId.value + : this.affectedContactId, + oldGroupName: data.oldGroupName.present + ? data.oldGroupName.value + : this.oldGroupName, + newGroupName: data.newGroupName.present + ? data.newGroupName.value + : this.newGroupName, + newDeleteMessagesAfterMilliseconds: + data.newDeleteMessagesAfterMilliseconds.present + ? data.newDeleteMessagesAfterMilliseconds.value + : this.newDeleteMessagesAfterMilliseconds, + type: data.type.present ? data.type.value : this.type, + actionAt: data.actionAt.present ? data.actionAt.value : this.actionAt, + ); + } + + @override + String toString() { + return (StringBuffer('GroupHistoriesData(') + ..write('groupHistoryId: $groupHistoryId, ') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('affectedContactId: $affectedContactId, ') + ..write('oldGroupName: $oldGroupName, ') + ..write('newGroupName: $newGroupName, ') + ..write( + 'newDeleteMessagesAfterMilliseconds: $newDeleteMessagesAfterMilliseconds, ', + ) + ..write('type: $type, ') + ..write('actionAt: $actionAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + groupHistoryId, + groupId, + contactId, + affectedContactId, + oldGroupName, + newGroupName, + newDeleteMessagesAfterMilliseconds, + type, + actionAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GroupHistoriesData && + other.groupHistoryId == this.groupHistoryId && + other.groupId == this.groupId && + other.contactId == this.contactId && + other.affectedContactId == this.affectedContactId && + other.oldGroupName == this.oldGroupName && + other.newGroupName == this.newGroupName && + other.newDeleteMessagesAfterMilliseconds == + this.newDeleteMessagesAfterMilliseconds && + other.type == this.type && + other.actionAt == this.actionAt); +} + +class GroupHistoriesCompanion extends UpdateCompanion { + final Value groupHistoryId; + final Value groupId; + final Value contactId; + final Value affectedContactId; + final Value oldGroupName; + final Value newGroupName; + final Value newDeleteMessagesAfterMilliseconds; + final Value type; + final Value actionAt; + final Value rowid; + const GroupHistoriesCompanion({ + this.groupHistoryId = const Value.absent(), + this.groupId = const Value.absent(), + this.contactId = const Value.absent(), + this.affectedContactId = const Value.absent(), + this.oldGroupName = const Value.absent(), + this.newGroupName = const Value.absent(), + this.newDeleteMessagesAfterMilliseconds = const Value.absent(), + this.type = const Value.absent(), + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + GroupHistoriesCompanion.insert({ + required String groupHistoryId, + required String groupId, + this.contactId = const Value.absent(), + this.affectedContactId = const Value.absent(), + this.oldGroupName = const Value.absent(), + this.newGroupName = const Value.absent(), + this.newDeleteMessagesAfterMilliseconds = const Value.absent(), + required String type, + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupHistoryId = Value(groupHistoryId), + groupId = Value(groupId), + type = Value(type); + static Insertable custom({ + Expression? groupHistoryId, + Expression? groupId, + Expression? contactId, + Expression? affectedContactId, + Expression? oldGroupName, + Expression? newGroupName, + Expression? newDeleteMessagesAfterMilliseconds, + Expression? type, + Expression? actionAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupHistoryId != null) 'group_history_id': groupHistoryId, + if (groupId != null) 'group_id': groupId, + if (contactId != null) 'contact_id': contactId, + if (affectedContactId != null) 'affected_contact_id': affectedContactId, + if (oldGroupName != null) 'old_group_name': oldGroupName, + if (newGroupName != null) 'new_group_name': newGroupName, + if (newDeleteMessagesAfterMilliseconds != null) + 'new_delete_messages_after_milliseconds': + newDeleteMessagesAfterMilliseconds, + if (type != null) 'type': type, + if (actionAt != null) 'action_at': actionAt, + if (rowid != null) 'rowid': rowid, + }); + } + + GroupHistoriesCompanion copyWith({ + Value? groupHistoryId, + Value? groupId, + Value? contactId, + Value? affectedContactId, + Value? oldGroupName, + Value? newGroupName, + Value? newDeleteMessagesAfterMilliseconds, + Value? type, + Value? actionAt, + Value? rowid, + }) { + return GroupHistoriesCompanion( + groupHistoryId: groupHistoryId ?? this.groupHistoryId, + groupId: groupId ?? this.groupId, + contactId: contactId ?? this.contactId, + affectedContactId: affectedContactId ?? this.affectedContactId, + oldGroupName: oldGroupName ?? this.oldGroupName, + newGroupName: newGroupName ?? this.newGroupName, + newDeleteMessagesAfterMilliseconds: + newDeleteMessagesAfterMilliseconds ?? + this.newDeleteMessagesAfterMilliseconds, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupHistoryId.present) { + map['group_history_id'] = Variable(groupHistoryId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (affectedContactId.present) { + map['affected_contact_id'] = Variable(affectedContactId.value); + } + if (oldGroupName.present) { + map['old_group_name'] = Variable(oldGroupName.value); + } + if (newGroupName.present) { + map['new_group_name'] = Variable(newGroupName.value); + } + if (newDeleteMessagesAfterMilliseconds.present) { + map['new_delete_messages_after_milliseconds'] = Variable( + newDeleteMessagesAfterMilliseconds.value, + ); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (actionAt.present) { + map['action_at'] = Variable(actionAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GroupHistoriesCompanion(') + ..write('groupHistoryId: $groupHistoryId, ') + ..write('groupId: $groupId, ') + ..write('contactId: $contactId, ') + ..write('affectedContactId: $affectedContactId, ') + ..write('oldGroupName: $oldGroupName, ') + ..write('newGroupName: $newGroupName, ') + ..write( + 'newDeleteMessagesAfterMilliseconds: $newDeleteMessagesAfterMilliseconds, ', + ) + ..write('type: $type, ') + ..write('actionAt: $actionAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class KeyVerifications extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + KeyVerifications(this.attachedDatabase, [this._alias]); + late final GeneratedColumn verificationId = GeneratedColumn( + 'verification_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn verifiedBy = GeneratedColumn( + 'verified_by', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [ + verificationId, + contactId, + type, + verifiedBy, + createdAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'key_verifications'; + @override + Set get $primaryKey => {verificationId}; + @override + KeyVerificationsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return KeyVerificationsData( + verificationId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}verification_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + verifiedBy: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}verified_by'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + KeyVerifications createAlias(String alias) { + return KeyVerifications(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class KeyVerificationsData extends DataClass + implements Insertable { + final int verificationId; + final int contactId; + final String type; + final int? verifiedBy; + final int createdAt; + const KeyVerificationsData({ + required this.verificationId, + required this.contactId, + required this.type, + this.verifiedBy, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['verification_id'] = Variable(verificationId); + map['contact_id'] = Variable(contactId); + map['type'] = Variable(type); + if (!nullToAbsent || verifiedBy != null) { + map['verified_by'] = Variable(verifiedBy); + } + map['created_at'] = Variable(createdAt); + return map; + } + + KeyVerificationsCompanion toCompanion(bool nullToAbsent) { + return KeyVerificationsCompanion( + verificationId: Value(verificationId), + contactId: Value(contactId), + type: Value(type), + verifiedBy: verifiedBy == null && nullToAbsent + ? const Value.absent() + : Value(verifiedBy), + createdAt: Value(createdAt), + ); + } + + factory KeyVerificationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return KeyVerificationsData( + verificationId: serializer.fromJson(json['verificationId']), + contactId: serializer.fromJson(json['contactId']), + type: serializer.fromJson(json['type']), + verifiedBy: serializer.fromJson(json['verifiedBy']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'verificationId': serializer.toJson(verificationId), + 'contactId': serializer.toJson(contactId), + 'type': serializer.toJson(type), + 'verifiedBy': serializer.toJson(verifiedBy), + 'createdAt': serializer.toJson(createdAt), + }; + } + + KeyVerificationsData copyWith({ + int? verificationId, + int? contactId, + String? type, + Value verifiedBy = const Value.absent(), + int? createdAt, + }) => KeyVerificationsData( + verificationId: verificationId ?? this.verificationId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + verifiedBy: verifiedBy.present ? verifiedBy.value : this.verifiedBy, + createdAt: createdAt ?? this.createdAt, + ); + KeyVerificationsData copyWithCompanion(KeyVerificationsCompanion data) { + return KeyVerificationsData( + verificationId: data.verificationId.present + ? data.verificationId.value + : this.verificationId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + type: data.type.present ? data.type.value : this.type, + verifiedBy: data.verifiedBy.present + ? data.verifiedBy.value + : this.verifiedBy, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('KeyVerificationsData(') + ..write('verificationId: $verificationId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('verifiedBy: $verifiedBy, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(verificationId, contactId, type, verifiedBy, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is KeyVerificationsData && + other.verificationId == this.verificationId && + other.contactId == this.contactId && + other.type == this.type && + other.verifiedBy == this.verifiedBy && + other.createdAt == this.createdAt); +} + +class KeyVerificationsCompanion extends UpdateCompanion { + final Value verificationId; + final Value contactId; + final Value type; + final Value verifiedBy; + final Value createdAt; + const KeyVerificationsCompanion({ + this.verificationId = const Value.absent(), + this.contactId = const Value.absent(), + this.type = const Value.absent(), + this.verifiedBy = const Value.absent(), + this.createdAt = const Value.absent(), + }); + KeyVerificationsCompanion.insert({ + this.verificationId = const Value.absent(), + required int contactId, + required String type, + this.verifiedBy = const Value.absent(), + this.createdAt = const Value.absent(), + }) : contactId = Value(contactId), + type = Value(type); + static Insertable custom({ + Expression? verificationId, + Expression? contactId, + Expression? type, + Expression? verifiedBy, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (verificationId != null) 'verification_id': verificationId, + if (contactId != null) 'contact_id': contactId, + if (type != null) 'type': type, + if (verifiedBy != null) 'verified_by': verifiedBy, + if (createdAt != null) 'created_at': createdAt, + }); + } + + KeyVerificationsCompanion copyWith({ + Value? verificationId, + Value? contactId, + Value? type, + Value? verifiedBy, + Value? createdAt, + }) { + return KeyVerificationsCompanion( + verificationId: verificationId ?? this.verificationId, + contactId: contactId ?? this.contactId, + type: type ?? this.type, + verifiedBy: verifiedBy ?? this.verifiedBy, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (verificationId.present) { + map['verification_id'] = Variable(verificationId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (verifiedBy.present) { + map['verified_by'] = Variable(verifiedBy.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('KeyVerificationsCompanion(') + ..write('verificationId: $verificationId, ') + ..write('contactId: $contactId, ') + ..write('type: $type, ') + ..write('verifiedBy: $verifiedBy, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class VerificationTokens extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + VerificationTokens(this.attachedDatabase, [this._alias]); + late final GeneratedColumn tokenId = GeneratedColumn( + 'token_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn token = + GeneratedColumn( + 'token', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER))', + defaultValue: const CustomExpression( + 'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)', + ), + ); + @override + List get $columns => [tokenId, token, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'verification_tokens'; + @override + Set get $primaryKey => {tokenId}; + @override + VerificationTokensData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return VerificationTokensData( + tokenId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}token_id'], + )!, + token: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}token'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + VerificationTokens createAlias(String alias) { + return VerificationTokens(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class VerificationTokensData extends DataClass + implements Insertable { + final int tokenId; + final i2.Uint8List token; + final int createdAt; + const VerificationTokensData({ + required this.tokenId, + required this.token, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['token_id'] = Variable(tokenId); + map['token'] = Variable(token); + map['created_at'] = Variable(createdAt); + return map; + } + + VerificationTokensCompanion toCompanion(bool nullToAbsent) { + return VerificationTokensCompanion( + tokenId: Value(tokenId), + token: Value(token), + createdAt: Value(createdAt), + ); + } + + factory VerificationTokensData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return VerificationTokensData( + tokenId: serializer.fromJson(json['tokenId']), + token: serializer.fromJson(json['token']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'tokenId': serializer.toJson(tokenId), + 'token': serializer.toJson(token), + 'createdAt': serializer.toJson(createdAt), + }; + } + + VerificationTokensData copyWith({ + int? tokenId, + i2.Uint8List? token, + int? createdAt, + }) => VerificationTokensData( + tokenId: tokenId ?? this.tokenId, + token: token ?? this.token, + createdAt: createdAt ?? this.createdAt, + ); + VerificationTokensData copyWithCompanion(VerificationTokensCompanion data) { + return VerificationTokensData( + tokenId: data.tokenId.present ? data.tokenId.value : this.tokenId, + token: data.token.present ? data.token.value : this.token, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('VerificationTokensData(') + ..write('tokenId: $tokenId, ') + ..write('token: $token, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(tokenId, $driftBlobEquality.hash(token), createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is VerificationTokensData && + other.tokenId == this.tokenId && + $driftBlobEquality.equals(other.token, this.token) && + other.createdAt == this.createdAt); +} + +class VerificationTokensCompanion + extends UpdateCompanion { + final Value tokenId; + final Value token; + final Value createdAt; + const VerificationTokensCompanion({ + this.tokenId = const Value.absent(), + this.token = const Value.absent(), + this.createdAt = const Value.absent(), + }); + VerificationTokensCompanion.insert({ + this.tokenId = const Value.absent(), + required i2.Uint8List token, + this.createdAt = const Value.absent(), + }) : token = Value(token); + static Insertable custom({ + Expression? tokenId, + Expression? token, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (tokenId != null) 'token_id': tokenId, + if (token != null) 'token': token, + if (createdAt != null) 'created_at': createdAt, + }); + } + + VerificationTokensCompanion copyWith({ + Value? tokenId, + Value? token, + Value? createdAt, + }) { + return VerificationTokensCompanion( + tokenId: tokenId ?? this.tokenId, + token: token ?? this.token, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (tokenId.present) { + map['token_id'] = Variable(tokenId.value); + } + if (token.present) { + map['token'] = Variable(token.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('VerificationTokensCompanion(') + ..write('tokenId: $tokenId, ') + ..write('token: $token, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryAnnouncedUsers extends Table + with + TableInfo< + UserDiscoveryAnnouncedUsers, + UserDiscoveryAnnouncedUsersData + > { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryAnnouncedUsers(this.attachedDatabase, [this._alias]); + late final GeneratedColumn announcedUserId = GeneratedColumn( + 'announced_user_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn announcedPublicKey = + GeneratedColumn( + 'announced_public_key', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn publicId = GeneratedColumn( + 'public_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL UNIQUE', + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn wasShownToTheUser = GeneratedColumn( + 'was_shown_to_the_user', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (was_shown_to_the_user IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_hidden IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn wasAskedFriends = GeneratedColumn( + 'was_asked_friends', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (was_asked_friends IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + announcedUserId, + announcedPublicKey, + publicId, + username, + wasShownToTheUser, + isHidden, + wasAskedFriends, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_announced_users'; + @override + Set get $primaryKey => {announcedUserId}; + @override + UserDiscoveryAnnouncedUsersData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryAnnouncedUsersData( + announcedUserId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}announced_user_id'], + )!, + announcedPublicKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}announced_public_key'], + )!, + publicId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_id'], + )!, + username: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}username'], + ), + wasShownToTheUser: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}was_shown_to_the_user'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_hidden'], + )!, + wasAskedFriends: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}was_asked_friends'], + )!, + ); + } + + @override + UserDiscoveryAnnouncedUsers createAlias(String alias) { + return UserDiscoveryAnnouncedUsers(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(announced_user_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryAnnouncedUsersData extends DataClass + implements Insertable { + final int announcedUserId; + final i2.Uint8List announcedPublicKey; + final int publicId; + final String? username; + final int wasShownToTheUser; + final int isHidden; + final int wasAskedFriends; + const UserDiscoveryAnnouncedUsersData({ + required this.announcedUserId, + required this.announcedPublicKey, + required this.publicId, + this.username, + required this.wasShownToTheUser, + required this.isHidden, + required this.wasAskedFriends, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['announced_user_id'] = Variable(announcedUserId); + map['announced_public_key'] = Variable(announcedPublicKey); + map['public_id'] = Variable(publicId); + if (!nullToAbsent || username != null) { + map['username'] = Variable(username); + } + map['was_shown_to_the_user'] = Variable(wasShownToTheUser); + map['is_hidden'] = Variable(isHidden); + map['was_asked_friends'] = Variable(wasAskedFriends); + return map; + } + + UserDiscoveryAnnouncedUsersCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryAnnouncedUsersCompanion( + announcedUserId: Value(announcedUserId), + announcedPublicKey: Value(announcedPublicKey), + publicId: Value(publicId), + username: username == null && nullToAbsent + ? const Value.absent() + : Value(username), + wasShownToTheUser: Value(wasShownToTheUser), + isHidden: Value(isHidden), + wasAskedFriends: Value(wasAskedFriends), + ); + } + + factory UserDiscoveryAnnouncedUsersData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryAnnouncedUsersData( + announcedUserId: serializer.fromJson(json['announcedUserId']), + announcedPublicKey: serializer.fromJson( + json['announcedPublicKey'], + ), + publicId: serializer.fromJson(json['publicId']), + username: serializer.fromJson(json['username']), + wasShownToTheUser: serializer.fromJson(json['wasShownToTheUser']), + isHidden: serializer.fromJson(json['isHidden']), + wasAskedFriends: serializer.fromJson(json['wasAskedFriends']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'announcedUserId': serializer.toJson(announcedUserId), + 'announcedPublicKey': serializer.toJson(announcedPublicKey), + 'publicId': serializer.toJson(publicId), + 'username': serializer.toJson(username), + 'wasShownToTheUser': serializer.toJson(wasShownToTheUser), + 'isHidden': serializer.toJson(isHidden), + 'wasAskedFriends': serializer.toJson(wasAskedFriends), + }; + } + + UserDiscoveryAnnouncedUsersData copyWith({ + int? announcedUserId, + i2.Uint8List? announcedPublicKey, + int? publicId, + Value username = const Value.absent(), + int? wasShownToTheUser, + int? isHidden, + int? wasAskedFriends, + }) => UserDiscoveryAnnouncedUsersData( + announcedUserId: announcedUserId ?? this.announcedUserId, + announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey, + publicId: publicId ?? this.publicId, + username: username.present ? username.value : this.username, + wasShownToTheUser: wasShownToTheUser ?? this.wasShownToTheUser, + isHidden: isHidden ?? this.isHidden, + wasAskedFriends: wasAskedFriends ?? this.wasAskedFriends, + ); + UserDiscoveryAnnouncedUsersData copyWithCompanion( + UserDiscoveryAnnouncedUsersCompanion data, + ) { + return UserDiscoveryAnnouncedUsersData( + announcedUserId: data.announcedUserId.present + ? data.announcedUserId.value + : this.announcedUserId, + announcedPublicKey: data.announcedPublicKey.present + ? data.announcedPublicKey.value + : this.announcedPublicKey, + publicId: data.publicId.present ? data.publicId.value : this.publicId, + username: data.username.present ? data.username.value : this.username, + wasShownToTheUser: data.wasShownToTheUser.present + ? data.wasShownToTheUser.value + : this.wasShownToTheUser, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + wasAskedFriends: data.wasAskedFriends.present + ? data.wasAskedFriends.value + : this.wasAskedFriends, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryAnnouncedUsersData(') + ..write('announcedUserId: $announcedUserId, ') + ..write('announcedPublicKey: $announcedPublicKey, ') + ..write('publicId: $publicId, ') + ..write('username: $username, ') + ..write('wasShownToTheUser: $wasShownToTheUser, ') + ..write('isHidden: $isHidden, ') + ..write('wasAskedFriends: $wasAskedFriends') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + announcedUserId, + $driftBlobEquality.hash(announcedPublicKey), + publicId, + username, + wasShownToTheUser, + isHidden, + wasAskedFriends, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryAnnouncedUsersData && + other.announcedUserId == this.announcedUserId && + $driftBlobEquality.equals( + other.announcedPublicKey, + this.announcedPublicKey, + ) && + other.publicId == this.publicId && + other.username == this.username && + other.wasShownToTheUser == this.wasShownToTheUser && + other.isHidden == this.isHidden && + other.wasAskedFriends == this.wasAskedFriends); +} + +class UserDiscoveryAnnouncedUsersCompanion + extends UpdateCompanion { + final Value announcedUserId; + final Value announcedPublicKey; + final Value publicId; + final Value username; + final Value wasShownToTheUser; + final Value isHidden; + final Value wasAskedFriends; + const UserDiscoveryAnnouncedUsersCompanion({ + this.announcedUserId = const Value.absent(), + this.announcedPublicKey = const Value.absent(), + this.publicId = const Value.absent(), + this.username = const Value.absent(), + this.wasShownToTheUser = const Value.absent(), + this.isHidden = const Value.absent(), + this.wasAskedFriends = const Value.absent(), + }); + UserDiscoveryAnnouncedUsersCompanion.insert({ + this.announcedUserId = const Value.absent(), + required i2.Uint8List announcedPublicKey, + required int publicId, + this.username = const Value.absent(), + this.wasShownToTheUser = const Value.absent(), + this.isHidden = const Value.absent(), + this.wasAskedFriends = const Value.absent(), + }) : announcedPublicKey = Value(announcedPublicKey), + publicId = Value(publicId); + static Insertable custom({ + Expression? announcedUserId, + Expression? announcedPublicKey, + Expression? publicId, + Expression? username, + Expression? wasShownToTheUser, + Expression? isHidden, + Expression? wasAskedFriends, + }) { + return RawValuesInsertable({ + if (announcedUserId != null) 'announced_user_id': announcedUserId, + if (announcedPublicKey != null) + 'announced_public_key': announcedPublicKey, + if (publicId != null) 'public_id': publicId, + if (username != null) 'username': username, + if (wasShownToTheUser != null) 'was_shown_to_the_user': wasShownToTheUser, + if (isHidden != null) 'is_hidden': isHidden, + if (wasAskedFriends != null) 'was_asked_friends': wasAskedFriends, + }); + } + + UserDiscoveryAnnouncedUsersCompanion copyWith({ + Value? announcedUserId, + Value? announcedPublicKey, + Value? publicId, + Value? username, + Value? wasShownToTheUser, + Value? isHidden, + Value? wasAskedFriends, + }) { + return UserDiscoveryAnnouncedUsersCompanion( + announcedUserId: announcedUserId ?? this.announcedUserId, + announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey, + publicId: publicId ?? this.publicId, + username: username ?? this.username, + wasShownToTheUser: wasShownToTheUser ?? this.wasShownToTheUser, + isHidden: isHidden ?? this.isHidden, + wasAskedFriends: wasAskedFriends ?? this.wasAskedFriends, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (announcedUserId.present) { + map['announced_user_id'] = Variable(announcedUserId.value); + } + if (announcedPublicKey.present) { + map['announced_public_key'] = Variable( + announcedPublicKey.value, + ); + } + if (publicId.present) { + map['public_id'] = Variable(publicId.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (wasShownToTheUser.present) { + map['was_shown_to_the_user'] = Variable(wasShownToTheUser.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (wasAskedFriends.present) { + map['was_asked_friends'] = Variable(wasAskedFriends.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryAnnouncedUsersCompanion(') + ..write('announcedUserId: $announcedUserId, ') + ..write('announcedPublicKey: $announcedPublicKey, ') + ..write('publicId: $publicId, ') + ..write('username: $username, ') + ..write('wasShownToTheUser: $wasShownToTheUser, ') + ..write('isHidden: $isHidden, ') + ..write('wasAskedFriends: $wasAskedFriends') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryUserRelations extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryUserRelations(this.attachedDatabase, [this._alias]); + late final GeneratedColumn announcedUserId = GeneratedColumn( + 'announced_user_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES user_discovery_announced_users(announced_user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn fromContactId = GeneratedColumn( + 'from_contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn publicKeyVerifiedTimestamp = + GeneratedColumn( + 'public_key_verified_timestamp', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + announcedUserId, + fromContactId, + publicKeyVerifiedTimestamp, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_user_relations'; + @override + Set get $primaryKey => {announcedUserId, fromContactId}; + @override + UserDiscoveryUserRelationsData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryUserRelationsData( + announcedUserId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}announced_user_id'], + )!, + fromContactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}from_contact_id'], + )!, + publicKeyVerifiedTimestamp: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_key_verified_timestamp'], + ), + ); + } + + @override + UserDiscoveryUserRelations createAlias(String alias) { + return UserDiscoveryUserRelations(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(announced_user_id, from_contact_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryUserRelationsData extends DataClass + implements Insertable { + final int announcedUserId; + final int fromContactId; + final int? publicKeyVerifiedTimestamp; + const UserDiscoveryUserRelationsData({ + required this.announcedUserId, + required this.fromContactId, + this.publicKeyVerifiedTimestamp, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['announced_user_id'] = Variable(announcedUserId); + map['from_contact_id'] = Variable(fromContactId); + if (!nullToAbsent || publicKeyVerifiedTimestamp != null) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp, + ); + } + return map; + } + + UserDiscoveryUserRelationsCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryUserRelationsCompanion( + announcedUserId: Value(announcedUserId), + fromContactId: Value(fromContactId), + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp == null && nullToAbsent + ? const Value.absent() + : Value(publicKeyVerifiedTimestamp), + ); + } + + factory UserDiscoveryUserRelationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryUserRelationsData( + announcedUserId: serializer.fromJson(json['announcedUserId']), + fromContactId: serializer.fromJson(json['fromContactId']), + publicKeyVerifiedTimestamp: serializer.fromJson( + json['publicKeyVerifiedTimestamp'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'announcedUserId': serializer.toJson(announcedUserId), + 'fromContactId': serializer.toJson(fromContactId), + 'publicKeyVerifiedTimestamp': serializer.toJson( + publicKeyVerifiedTimestamp, + ), + }; + } + + UserDiscoveryUserRelationsData copyWith({ + int? announcedUserId, + int? fromContactId, + Value publicKeyVerifiedTimestamp = const Value.absent(), + }) => UserDiscoveryUserRelationsData( + announcedUserId: announcedUserId ?? this.announcedUserId, + fromContactId: fromContactId ?? this.fromContactId, + publicKeyVerifiedTimestamp: publicKeyVerifiedTimestamp.present + ? publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + UserDiscoveryUserRelationsData copyWithCompanion( + UserDiscoveryUserRelationsCompanion data, + ) { + return UserDiscoveryUserRelationsData( + announcedUserId: data.announcedUserId.present + ? data.announcedUserId.value + : this.announcedUserId, + fromContactId: data.fromContactId.present + ? data.fromContactId.value + : this.fromContactId, + publicKeyVerifiedTimestamp: data.publicKeyVerifiedTimestamp.present + ? data.publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryUserRelationsData(') + ..write('announcedUserId: $announcedUserId, ') + ..write('fromContactId: $fromContactId, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(announcedUserId, fromContactId, publicKeyVerifiedTimestamp); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryUserRelationsData && + other.announcedUserId == this.announcedUserId && + other.fromContactId == this.fromContactId && + other.publicKeyVerifiedTimestamp == this.publicKeyVerifiedTimestamp); +} + +class UserDiscoveryUserRelationsCompanion + extends UpdateCompanion { + final Value announcedUserId; + final Value fromContactId; + final Value publicKeyVerifiedTimestamp; + final Value rowid; + const UserDiscoveryUserRelationsCompanion({ + this.announcedUserId = const Value.absent(), + this.fromContactId = const Value.absent(), + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }); + UserDiscoveryUserRelationsCompanion.insert({ + required int announcedUserId, + required int fromContactId, + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }) : announcedUserId = Value(announcedUserId), + fromContactId = Value(fromContactId); + static Insertable custom({ + Expression? announcedUserId, + Expression? fromContactId, + Expression? publicKeyVerifiedTimestamp, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (announcedUserId != null) 'announced_user_id': announcedUserId, + if (fromContactId != null) 'from_contact_id': fromContactId, + if (publicKeyVerifiedTimestamp != null) + 'public_key_verified_timestamp': publicKeyVerifiedTimestamp, + if (rowid != null) 'rowid': rowid, + }); + } + + UserDiscoveryUserRelationsCompanion copyWith({ + Value? announcedUserId, + Value? fromContactId, + Value? publicKeyVerifiedTimestamp, + Value? rowid, + }) { + return UserDiscoveryUserRelationsCompanion( + announcedUserId: announcedUserId ?? this.announcedUserId, + fromContactId: fromContactId ?? this.fromContactId, + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp ?? this.publicKeyVerifiedTimestamp, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (announcedUserId.present) { + map['announced_user_id'] = Variable(announcedUserId.value); + } + if (fromContactId.present) { + map['from_contact_id'] = Variable(fromContactId.value); + } + if (publicKeyVerifiedTimestamp.present) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp.value, + ); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryUserRelationsCompanion(') + ..write('announcedUserId: $announcedUserId, ') + ..write('fromContactId: $fromContactId, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryOtherPromotions extends Table + with + TableInfo< + UserDiscoveryOtherPromotions, + UserDiscoveryOtherPromotionsData + > { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryOtherPromotions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn fromContactId = GeneratedColumn( + 'from_contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn promotionId = GeneratedColumn( + 'promotion_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn publicId = GeneratedColumn( + 'public_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn threshold = GeneratedColumn( + 'threshold', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn announcementShare = + GeneratedColumn( + 'announcement_share', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn publicKeyVerifiedTimestamp = + GeneratedColumn( + 'public_key_verified_timestamp', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + fromContactId, + promotionId, + publicId, + threshold, + announcementShare, + publicKeyVerifiedTimestamp, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_other_promotions'; + @override + Set get $primaryKey => {fromContactId, publicId}; + @override + UserDiscoveryOtherPromotionsData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryOtherPromotionsData( + fromContactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}from_contact_id'], + )!, + promotionId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}promotion_id'], + )!, + publicId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_id'], + )!, + threshold: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}threshold'], + )!, + announcementShare: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}announcement_share'], + )!, + publicKeyVerifiedTimestamp: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}public_key_verified_timestamp'], + ), + ); + } + + @override + UserDiscoveryOtherPromotions createAlias(String alias) { + return UserDiscoveryOtherPromotions(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(from_contact_id, public_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryOtherPromotionsData extends DataClass + implements Insertable { + final int fromContactId; + final int promotionId; + final int publicId; + final int threshold; + final i2.Uint8List announcementShare; + final int? publicKeyVerifiedTimestamp; + const UserDiscoveryOtherPromotionsData({ + required this.fromContactId, + required this.promotionId, + required this.publicId, + required this.threshold, + required this.announcementShare, + this.publicKeyVerifiedTimestamp, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['from_contact_id'] = Variable(fromContactId); + map['promotion_id'] = Variable(promotionId); + map['public_id'] = Variable(publicId); + map['threshold'] = Variable(threshold); + map['announcement_share'] = Variable(announcementShare); + if (!nullToAbsent || publicKeyVerifiedTimestamp != null) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp, + ); + } + return map; + } + + UserDiscoveryOtherPromotionsCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryOtherPromotionsCompanion( + fromContactId: Value(fromContactId), + promotionId: Value(promotionId), + publicId: Value(publicId), + threshold: Value(threshold), + announcementShare: Value(announcementShare), + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp == null && nullToAbsent + ? const Value.absent() + : Value(publicKeyVerifiedTimestamp), + ); + } + + factory UserDiscoveryOtherPromotionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryOtherPromotionsData( + fromContactId: serializer.fromJson(json['fromContactId']), + promotionId: serializer.fromJson(json['promotionId']), + publicId: serializer.fromJson(json['publicId']), + threshold: serializer.fromJson(json['threshold']), + announcementShare: serializer.fromJson( + json['announcementShare'], + ), + publicKeyVerifiedTimestamp: serializer.fromJson( + json['publicKeyVerifiedTimestamp'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'fromContactId': serializer.toJson(fromContactId), + 'promotionId': serializer.toJson(promotionId), + 'publicId': serializer.toJson(publicId), + 'threshold': serializer.toJson(threshold), + 'announcementShare': serializer.toJson(announcementShare), + 'publicKeyVerifiedTimestamp': serializer.toJson( + publicKeyVerifiedTimestamp, + ), + }; + } + + UserDiscoveryOtherPromotionsData copyWith({ + int? fromContactId, + int? promotionId, + int? publicId, + int? threshold, + i2.Uint8List? announcementShare, + Value publicKeyVerifiedTimestamp = const Value.absent(), + }) => UserDiscoveryOtherPromotionsData( + fromContactId: fromContactId ?? this.fromContactId, + promotionId: promotionId ?? this.promotionId, + publicId: publicId ?? this.publicId, + threshold: threshold ?? this.threshold, + announcementShare: announcementShare ?? this.announcementShare, + publicKeyVerifiedTimestamp: publicKeyVerifiedTimestamp.present + ? publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + UserDiscoveryOtherPromotionsData copyWithCompanion( + UserDiscoveryOtherPromotionsCompanion data, + ) { + return UserDiscoveryOtherPromotionsData( + fromContactId: data.fromContactId.present + ? data.fromContactId.value + : this.fromContactId, + promotionId: data.promotionId.present + ? data.promotionId.value + : this.promotionId, + publicId: data.publicId.present ? data.publicId.value : this.publicId, + threshold: data.threshold.present ? data.threshold.value : this.threshold, + announcementShare: data.announcementShare.present + ? data.announcementShare.value + : this.announcementShare, + publicKeyVerifiedTimestamp: data.publicKeyVerifiedTimestamp.present + ? data.publicKeyVerifiedTimestamp.value + : this.publicKeyVerifiedTimestamp, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOtherPromotionsData(') + ..write('fromContactId: $fromContactId, ') + ..write('promotionId: $promotionId, ') + ..write('publicId: $publicId, ') + ..write('threshold: $threshold, ') + ..write('announcementShare: $announcementShare, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + fromContactId, + promotionId, + publicId, + threshold, + $driftBlobEquality.hash(announcementShare), + publicKeyVerifiedTimestamp, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryOtherPromotionsData && + other.fromContactId == this.fromContactId && + other.promotionId == this.promotionId && + other.publicId == this.publicId && + other.threshold == this.threshold && + $driftBlobEquality.equals( + other.announcementShare, + this.announcementShare, + ) && + other.publicKeyVerifiedTimestamp == this.publicKeyVerifiedTimestamp); +} + +class UserDiscoveryOtherPromotionsCompanion + extends UpdateCompanion { + final Value fromContactId; + final Value promotionId; + final Value publicId; + final Value threshold; + final Value announcementShare; + final Value publicKeyVerifiedTimestamp; + final Value rowid; + const UserDiscoveryOtherPromotionsCompanion({ + this.fromContactId = const Value.absent(), + this.promotionId = const Value.absent(), + this.publicId = const Value.absent(), + this.threshold = const Value.absent(), + this.announcementShare = const Value.absent(), + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }); + UserDiscoveryOtherPromotionsCompanion.insert({ + required int fromContactId, + required int promotionId, + required int publicId, + required int threshold, + required i2.Uint8List announcementShare, + this.publicKeyVerifiedTimestamp = const Value.absent(), + this.rowid = const Value.absent(), + }) : fromContactId = Value(fromContactId), + promotionId = Value(promotionId), + publicId = Value(publicId), + threshold = Value(threshold), + announcementShare = Value(announcementShare); + static Insertable custom({ + Expression? fromContactId, + Expression? promotionId, + Expression? publicId, + Expression? threshold, + Expression? announcementShare, + Expression? publicKeyVerifiedTimestamp, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (fromContactId != null) 'from_contact_id': fromContactId, + if (promotionId != null) 'promotion_id': promotionId, + if (publicId != null) 'public_id': publicId, + if (threshold != null) 'threshold': threshold, + if (announcementShare != null) 'announcement_share': announcementShare, + if (publicKeyVerifiedTimestamp != null) + 'public_key_verified_timestamp': publicKeyVerifiedTimestamp, + if (rowid != null) 'rowid': rowid, + }); + } + + UserDiscoveryOtherPromotionsCompanion copyWith({ + Value? fromContactId, + Value? promotionId, + Value? publicId, + Value? threshold, + Value? announcementShare, + Value? publicKeyVerifiedTimestamp, + Value? rowid, + }) { + return UserDiscoveryOtherPromotionsCompanion( + fromContactId: fromContactId ?? this.fromContactId, + promotionId: promotionId ?? this.promotionId, + publicId: publicId ?? this.publicId, + threshold: threshold ?? this.threshold, + announcementShare: announcementShare ?? this.announcementShare, + publicKeyVerifiedTimestamp: + publicKeyVerifiedTimestamp ?? this.publicKeyVerifiedTimestamp, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (fromContactId.present) { + map['from_contact_id'] = Variable(fromContactId.value); + } + if (promotionId.present) { + map['promotion_id'] = Variable(promotionId.value); + } + if (publicId.present) { + map['public_id'] = Variable(publicId.value); + } + if (threshold.present) { + map['threshold'] = Variable(threshold.value); + } + if (announcementShare.present) { + map['announcement_share'] = Variable( + announcementShare.value, + ); + } + if (publicKeyVerifiedTimestamp.present) { + map['public_key_verified_timestamp'] = Variable( + publicKeyVerifiedTimestamp.value, + ); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOtherPromotionsCompanion(') + ..write('fromContactId: $fromContactId, ') + ..write('promotionId: $promotionId, ') + ..write('publicId: $publicId, ') + ..write('threshold: $threshold, ') + ..write('announcementShare: $announcementShare, ') + ..write('publicKeyVerifiedTimestamp: $publicKeyVerifiedTimestamp, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryOwnPromotions extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryOwnPromotions(this.attachedDatabase, [this._alias]); + late final GeneratedColumn versionId = GeneratedColumn( + 'version_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + late final GeneratedColumn promotion = + GeneratedColumn( + 'promotion', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @override + List get $columns => [versionId, contactId, promotion]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_own_promotions'; + @override + Set get $primaryKey => {versionId}; + @override + UserDiscoveryOwnPromotionsData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoveryOwnPromotionsData( + versionId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}version_id'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + )!, + promotion: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}promotion'], + )!, + ); + } + + @override + UserDiscoveryOwnPromotions createAlias(String alias) { + return UserDiscoveryOwnPromotions(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoveryOwnPromotionsData extends DataClass + implements Insertable { + final int versionId; + final int contactId; + final i2.Uint8List promotion; + const UserDiscoveryOwnPromotionsData({ + required this.versionId, + required this.contactId, + required this.promotion, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['version_id'] = Variable(versionId); + map['contact_id'] = Variable(contactId); + map['promotion'] = Variable(promotion); + return map; + } + + UserDiscoveryOwnPromotionsCompanion toCompanion(bool nullToAbsent) { + return UserDiscoveryOwnPromotionsCompanion( + versionId: Value(versionId), + contactId: Value(contactId), + promotion: Value(promotion), + ); + } + + factory UserDiscoveryOwnPromotionsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoveryOwnPromotionsData( + versionId: serializer.fromJson(json['versionId']), + contactId: serializer.fromJson(json['contactId']), + promotion: serializer.fromJson(json['promotion']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'versionId': serializer.toJson(versionId), + 'contactId': serializer.toJson(contactId), + 'promotion': serializer.toJson(promotion), + }; + } + + UserDiscoveryOwnPromotionsData copyWith({ + int? versionId, + int? contactId, + i2.Uint8List? promotion, + }) => UserDiscoveryOwnPromotionsData( + versionId: versionId ?? this.versionId, + contactId: contactId ?? this.contactId, + promotion: promotion ?? this.promotion, + ); + UserDiscoveryOwnPromotionsData copyWithCompanion( + UserDiscoveryOwnPromotionsCompanion data, + ) { + return UserDiscoveryOwnPromotionsData( + versionId: data.versionId.present ? data.versionId.value : this.versionId, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + promotion: data.promotion.present ? data.promotion.value : this.promotion, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOwnPromotionsData(') + ..write('versionId: $versionId, ') + ..write('contactId: $contactId, ') + ..write('promotion: $promotion') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(versionId, contactId, $driftBlobEquality.hash(promotion)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoveryOwnPromotionsData && + other.versionId == this.versionId && + other.contactId == this.contactId && + $driftBlobEquality.equals(other.promotion, this.promotion)); +} + +class UserDiscoveryOwnPromotionsCompanion + extends UpdateCompanion { + final Value versionId; + final Value contactId; + final Value promotion; + const UserDiscoveryOwnPromotionsCompanion({ + this.versionId = const Value.absent(), + this.contactId = const Value.absent(), + this.promotion = const Value.absent(), + }); + UserDiscoveryOwnPromotionsCompanion.insert({ + this.versionId = const Value.absent(), + required int contactId, + required i2.Uint8List promotion, + }) : contactId = Value(contactId), + promotion = Value(promotion); + static Insertable custom({ + Expression? versionId, + Expression? contactId, + Expression? promotion, + }) { + return RawValuesInsertable({ + if (versionId != null) 'version_id': versionId, + if (contactId != null) 'contact_id': contactId, + if (promotion != null) 'promotion': promotion, + }); + } + + UserDiscoveryOwnPromotionsCompanion copyWith({ + Value? versionId, + Value? contactId, + Value? promotion, + }) { + return UserDiscoveryOwnPromotionsCompanion( + versionId: versionId ?? this.versionId, + contactId: contactId ?? this.contactId, + promotion: promotion ?? this.promotion, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (versionId.present) { + map['version_id'] = Variable(versionId.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + if (promotion.present) { + map['promotion'] = Variable(promotion.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoveryOwnPromotionsCompanion(') + ..write('versionId: $versionId, ') + ..write('contactId: $contactId, ') + ..write('promotion: $promotion') + ..write(')')) + .toString(); + } +} + +class UserDiscoveryShares extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserDiscoveryShares(this.attachedDatabase, [this._alias]); + late final GeneratedColumn shareId = GeneratedColumn( + 'share_id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn share = + GeneratedColumn( + 'share', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn contactId = GeneratedColumn( + 'contact_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES contacts(user_id)ON DELETE CASCADE', + ); + @override + List get $columns => [shareId, share, contactId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_discovery_shares'; + @override + Set get $primaryKey => {shareId}; + @override + UserDiscoverySharesData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserDiscoverySharesData( + shareId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}share_id'], + )!, + share: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}share'], + )!, + contactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}contact_id'], + ), + ); + } + + @override + UserDiscoveryShares createAlias(String alias) { + return UserDiscoveryShares(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class UserDiscoverySharesData extends DataClass + implements Insertable { + final int shareId; + final i2.Uint8List share; + final int? contactId; + const UserDiscoverySharesData({ + required this.shareId, + required this.share, + this.contactId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['share_id'] = Variable(shareId); + map['share'] = Variable(share); + if (!nullToAbsent || contactId != null) { + map['contact_id'] = Variable(contactId); + } + return map; + } + + UserDiscoverySharesCompanion toCompanion(bool nullToAbsent) { + return UserDiscoverySharesCompanion( + shareId: Value(shareId), + share: Value(share), + contactId: contactId == null && nullToAbsent + ? const Value.absent() + : Value(contactId), + ); + } + + factory UserDiscoverySharesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserDiscoverySharesData( + shareId: serializer.fromJson(json['shareId']), + share: serializer.fromJson(json['share']), + contactId: serializer.fromJson(json['contactId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'shareId': serializer.toJson(shareId), + 'share': serializer.toJson(share), + 'contactId': serializer.toJson(contactId), + }; + } + + UserDiscoverySharesData copyWith({ + int? shareId, + i2.Uint8List? share, + Value contactId = const Value.absent(), + }) => UserDiscoverySharesData( + shareId: shareId ?? this.shareId, + share: share ?? this.share, + contactId: contactId.present ? contactId.value : this.contactId, + ); + UserDiscoverySharesData copyWithCompanion(UserDiscoverySharesCompanion data) { + return UserDiscoverySharesData( + shareId: data.shareId.present ? data.shareId.value : this.shareId, + share: data.share.present ? data.share.value : this.share, + contactId: data.contactId.present ? data.contactId.value : this.contactId, + ); + } + + @override + String toString() { + return (StringBuffer('UserDiscoverySharesData(') + ..write('shareId: $shareId, ') + ..write('share: $share, ') + ..write('contactId: $contactId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(shareId, $driftBlobEquality.hash(share), contactId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserDiscoverySharesData && + other.shareId == this.shareId && + $driftBlobEquality.equals(other.share, this.share) && + other.contactId == this.contactId); +} + +class UserDiscoverySharesCompanion + extends UpdateCompanion { + final Value shareId; + final Value share; + final Value contactId; + const UserDiscoverySharesCompanion({ + this.shareId = const Value.absent(), + this.share = const Value.absent(), + this.contactId = const Value.absent(), + }); + UserDiscoverySharesCompanion.insert({ + this.shareId = const Value.absent(), + required i2.Uint8List share, + this.contactId = const Value.absent(), + }) : share = Value(share); + static Insertable custom({ + Expression? shareId, + Expression? share, + Expression? contactId, + }) { + return RawValuesInsertable({ + if (shareId != null) 'share_id': shareId, + if (share != null) 'share': share, + if (contactId != null) 'contact_id': contactId, + }); + } + + UserDiscoverySharesCompanion copyWith({ + Value? shareId, + Value? share, + Value? contactId, + }) { + return UserDiscoverySharesCompanion( + shareId: shareId ?? this.shareId, + share: share ?? this.share, + contactId: contactId ?? this.contactId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (shareId.present) { + map['share_id'] = Variable(shareId.value); + } + if (share.present) { + map['share'] = Variable(share.value); + } + if (contactId.present) { + map['contact_id'] = Variable(contactId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserDiscoverySharesCompanion(') + ..write('shareId: $shareId, ') + ..write('share: $share, ') + ..write('contactId: $contactId') + ..write(')')) + .toString(); + } +} + +class Shortcuts extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Shortcuts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + late final GeneratedColumn emoji = GeneratedColumn( + 'emoji', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL UNIQUE', + ); + late final GeneratedColumn usageCounter = GeneratedColumn( + 'usage_counter', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [id, emoji, usageCounter]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'shortcuts'; + @override + Set get $primaryKey => {id}; + @override + ShortcutsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ShortcutsData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + emoji: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}emoji'], + )!, + usageCounter: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}usage_counter'], + )!, + ); + } + + @override + Shortcuts createAlias(String alias) { + return Shortcuts(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class ShortcutsData extends DataClass implements Insertable { + final int id; + final String emoji; + final int usageCounter; + const ShortcutsData({ + required this.id, + required this.emoji, + required this.usageCounter, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['emoji'] = Variable(emoji); + map['usage_counter'] = Variable(usageCounter); + return map; + } + + ShortcutsCompanion toCompanion(bool nullToAbsent) { + return ShortcutsCompanion( + id: Value(id), + emoji: Value(emoji), + usageCounter: Value(usageCounter), + ); + } + + factory ShortcutsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ShortcutsData( + id: serializer.fromJson(json['id']), + emoji: serializer.fromJson(json['emoji']), + usageCounter: serializer.fromJson(json['usageCounter']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'emoji': serializer.toJson(emoji), + 'usageCounter': serializer.toJson(usageCounter), + }; + } + + ShortcutsData copyWith({int? id, String? emoji, int? usageCounter}) => + ShortcutsData( + id: id ?? this.id, + emoji: emoji ?? this.emoji, + usageCounter: usageCounter ?? this.usageCounter, + ); + ShortcutsData copyWithCompanion(ShortcutsCompanion data) { + return ShortcutsData( + id: data.id.present ? data.id.value : this.id, + emoji: data.emoji.present ? data.emoji.value : this.emoji, + usageCounter: data.usageCounter.present + ? data.usageCounter.value + : this.usageCounter, + ); + } + + @override + String toString() { + return (StringBuffer('ShortcutsData(') + ..write('id: $id, ') + ..write('emoji: $emoji, ') + ..write('usageCounter: $usageCounter') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, emoji, usageCounter); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ShortcutsData && + other.id == this.id && + other.emoji == this.emoji && + other.usageCounter == this.usageCounter); +} + +class ShortcutsCompanion extends UpdateCompanion { + final Value id; + final Value emoji; + final Value usageCounter; + const ShortcutsCompanion({ + this.id = const Value.absent(), + this.emoji = const Value.absent(), + this.usageCounter = const Value.absent(), + }); + ShortcutsCompanion.insert({ + this.id = const Value.absent(), + required String emoji, + this.usageCounter = const Value.absent(), + }) : emoji = Value(emoji); + static Insertable custom({ + Expression? id, + Expression? emoji, + Expression? usageCounter, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (emoji != null) 'emoji': emoji, + if (usageCounter != null) 'usage_counter': usageCounter, + }); + } + + ShortcutsCompanion copyWith({ + Value? id, + Value? emoji, + Value? usageCounter, + }) { + return ShortcutsCompanion( + id: id ?? this.id, + emoji: emoji ?? this.emoji, + usageCounter: usageCounter ?? this.usageCounter, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (emoji.present) { + map['emoji'] = Variable(emoji.value); + } + if (usageCounter.present) { + map['usage_counter'] = Variable(usageCounter.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ShortcutsCompanion(') + ..write('id: $id, ') + ..write('emoji: $emoji, ') + ..write('usageCounter: $usageCounter') + ..write(')')) + .toString(); + } +} + +class ShortcutMembers extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + ShortcutMembers(this.attachedDatabase, [this._alias]); + late final GeneratedColumn shortcutId = GeneratedColumn( + 'shortcut_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES shortcuts(id)ON DELETE CASCADE', + ); + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES "groups"(group_id)ON DELETE CASCADE', + ); + @override + List get $columns => [shortcutId, groupId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'shortcut_members'; + @override + Set get $primaryKey => {shortcutId, groupId}; + @override + ShortcutMembersData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ShortcutMembersData( + shortcutId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}shortcut_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + ); + } + + @override + ShortcutMembers createAlias(String alias) { + return ShortcutMembers(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(shortcut_id, group_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class ShortcutMembersData extends DataClass + implements Insertable { + final int shortcutId; + final String groupId; + const ShortcutMembersData({required this.shortcutId, required this.groupId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shortcut_id'] = Variable(shortcutId); + map['group_id'] = Variable(groupId); + return map; + } + + ShortcutMembersCompanion toCompanion(bool nullToAbsent) { + return ShortcutMembersCompanion( + shortcutId: Value(shortcutId), + groupId: Value(groupId), + ); + } + + factory ShortcutMembersData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ShortcutMembersData( + shortcutId: serializer.fromJson(json['shortcutId']), + groupId: serializer.fromJson(json['groupId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'shortcutId': serializer.toJson(shortcutId), + 'groupId': serializer.toJson(groupId), + }; + } + + ShortcutMembersData copyWith({int? shortcutId, String? groupId}) => + ShortcutMembersData( + shortcutId: shortcutId ?? this.shortcutId, + groupId: groupId ?? this.groupId, + ); + ShortcutMembersData copyWithCompanion(ShortcutMembersCompanion data) { + return ShortcutMembersData( + shortcutId: data.shortcutId.present + ? data.shortcutId.value + : this.shortcutId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + ); + } + + @override + String toString() { + return (StringBuffer('ShortcutMembersData(') + ..write('shortcutId: $shortcutId, ') + ..write('groupId: $groupId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(shortcutId, groupId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ShortcutMembersData && + other.shortcutId == this.shortcutId && + other.groupId == this.groupId); +} + +class ShortcutMembersCompanion extends UpdateCompanion { + final Value shortcutId; + final Value groupId; + final Value rowid; + const ShortcutMembersCompanion({ + this.shortcutId = const Value.absent(), + this.groupId = const Value.absent(), + this.rowid = const Value.absent(), + }); + ShortcutMembersCompanion.insert({ + required int shortcutId, + required String groupId, + this.rowid = const Value.absent(), + }) : shortcutId = Value(shortcutId), + groupId = Value(groupId); + static Insertable custom({ + Expression? shortcutId, + Expression? groupId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (shortcutId != null) 'shortcut_id': shortcutId, + if (groupId != null) 'group_id': groupId, + if (rowid != null) 'rowid': rowid, + }); + } + + ShortcutMembersCompanion copyWith({ + Value? shortcutId, + Value? groupId, + Value? rowid, + }) { + return ShortcutMembersCompanion( + shortcutId: shortcutId ?? this.shortcutId, + groupId: groupId ?? this.groupId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (shortcutId.present) { + map['shortcut_id'] = Variable(shortcutId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ShortcutMembersCompanion(') + ..write('shortcutId: $shortcutId, ') + ..write('groupId: $groupId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV22 extends GeneratedDatabase { + DatabaseAtV22(QueryExecutor e) : super(e); + late final Contacts contacts = Contacts(this); + late final Groups groups = Groups(this); + late final MediaFiles mediaFiles = MediaFiles(this); + late final Messages messages = Messages(this); + late final MessageHistories messageHistories = MessageHistories(this); + late final Reactions reactions = Reactions(this); + late final GroupMembers groupMembers = GroupMembers(this); + late final Receipts receipts = Receipts(this); + late final ReceivedReceipts receivedReceipts = ReceivedReceipts(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 SignalSignedPreKeyStores signalSignedPreKeyStores = + SignalSignedPreKeyStores(this); + late final MessageActions messageActions = MessageActions(this); + late final GroupHistories groupHistories = GroupHistories(this); + late final KeyVerifications keyVerifications = KeyVerifications(this); + late final VerificationTokens verificationTokens = VerificationTokens(this); + late final UserDiscoveryAnnouncedUsers userDiscoveryAnnouncedUsers = + UserDiscoveryAnnouncedUsers(this); + late final UserDiscoveryUserRelations userDiscoveryUserRelations = + UserDiscoveryUserRelations(this); + late final UserDiscoveryOtherPromotions userDiscoveryOtherPromotions = + UserDiscoveryOtherPromotions(this); + late final UserDiscoveryOwnPromotions userDiscoveryOwnPromotions = + UserDiscoveryOwnPromotions(this); + late final UserDiscoveryShares userDiscoveryShares = UserDiscoveryShares( + this, + ); + late final Shortcuts shortcuts = Shortcuts(this); + late final ShortcutMembers shortcutMembers = ShortcutMembers(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + contacts, + groups, + mediaFiles, + messages, + messageHistories, + reactions, + groupMembers, + receipts, + receivedReceipts, + signalIdentityKeyStores, + signalPreKeyStores, + signalSenderKeyStores, + signalSessionStores, + signalSignedPreKeyStores, + messageActions, + groupHistories, + keyVerifications, + verificationTokens, + userDiscoveryAnnouncedUsers, + userDiscoveryUserRelations, + userDiscoveryOtherPromotions, + userDiscoveryOwnPromotions, + userDiscoveryShares, + shortcuts, + shortcutMembers, + ]; + @override + StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('messages', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'media_files', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('messages', kind: UpdateKind.update)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_histories', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_histories', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('reactions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('reactions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('group_members', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('receipts', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('receipts', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'messages', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_actions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('message_actions', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('group_histories', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('key_verifications', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('key_verifications', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_discovery_announced_users', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_user_relations', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_user_relations', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_other_promotions', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('user_discovery_own_promotions', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'contacts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('user_discovery_shares', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'shortcuts', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('shortcut_members', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'groups', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('shortcut_members', kind: UpdateKind.delete)], + ), + ]); + @override + int get schemaVersion => 22; +} diff --git a/test/features/link_parser_test.dart b/test/features/link_parser_test.dart index 03e206cd..37d09e63 100644 --- a/test/features/link_parser_test.dart +++ b/test/features/link_parser_test.dart @@ -57,20 +57,6 @@ void main() { shareAction: 70, likeAction: 90, ), - LinkParserTest( - title: 'Kuketz-Blog 🛡 (@kuketzblog@social.tchncs.de)', - url: 'https://social.tchncs.de/@kuketzblog/115898752560771936', - siteName: 'Mastodon', - desc: - 'AWS verspricht jetzt »Souveränität« mit einem »europäischen« Cloud-Angebot – Standort Deutschland, großes Vertrauens-Theater.\n' - '\n' - 'Nur: Souveränität ist keine Postleitzahl. Wenn der Anbieter Amazon heißt, bleibt es dasselbe Märchen mit neuem Umschlag: Der Cloud Act, FISA etc. gilt trotzdem. US-Recht schlägt Geografie. Das Gerede von »Souveränität« ist kein Konzept, sondern Marketing.\n' - '\n' - 'https://www.heise.de/news/AWS-verspricht-Souveraenitaet-mit-europaeischem-Cloudangebot-11141800.html', - vendor: Vendor.mastodonSocialMediaPosting, - shareAction: 15, - likeAction: 190, - ), LinkParserTest( title: 'David Kriesel: Traue keinem Scan, den du nicht selbst gefälscht hast', diff --git a/test/services/backup_service_test.dart b/test/services/backup_service_test.dart index 331ccf4a..6f3bc45a 100644 --- a/test/services/backup_service_test.dart +++ b/test/services/backup_service_test.dart @@ -7,6 +7,7 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:twonly/core/bridge.dart' as bridge; import 'package:twonly/core/bridge/wrapper/backup.dart'; +import 'package:twonly/core/bridge/wrapper/key_manager.dart'; import 'package:twonly/core/frb_generated.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; @@ -20,6 +21,8 @@ import 'package:twonly/src/services/backup.service.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/keyvalue.dart'; +import '../mocks/platform_channels.dart'; + void main() { if (!Platform.isMacOS) { return; @@ -40,6 +43,17 @@ void main() { return null; }); + const pathProviderChannel = MethodChannel( + 'plugins.flutter.io/path_provider', + ); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(pathProviderChannel, (methodCall) async { + if (methodCall.method == 'getApplicationSupportDirectory') { + return tempDir.path; + } + return null; + }); + final dylibPath = '${Directory.current.path}/rust/target/debug/librust_lib_twonly.dylib'; if (File(dylibPath).existsSync()) { @@ -66,6 +80,7 @@ void main() { }); setUp(() async { + setupPlatformChannelMocks(); await locator.reset(); final dbFile = File('${tempDir.path}/twonly.sqlite'); locator @@ -230,5 +245,23 @@ void main() { ); }, ); + + test( + 'startPasswordlessBackupRecovery restores identity and returns tryAgainLater on download', + () async { + final keyManagerBytes = await RustKeyManager.serialize(); + + final error = await BackupService.startPasswordlessBackupRecovery( + 1, + 'test_user', + keyManagerBytes, + ); + expect(error, RecoveryError.tryAgainLater); + + // Verify key manager was imported successfully + final recoveredUserId = await RustKeyManager.getUserId(); + expect(recoveredUserId, 1); + }, + ); }); } diff --git a/test/services/messages_stream_test.dart b/test/services/messages_stream_test.dart new file mode 100644 index 00000000..3fefeeca --- /dev/null +++ b/test/services/messages_stream_test.dart @@ -0,0 +1,148 @@ +import 'package:drift/drift.dart' hide isNotNull, isNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/database/tables/messages.table.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/model/json/userdata.model.dart'; +import 'package:twonly/src/services/user.service.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() async { + await locator.reset(); + locator + ..registerSingleton( + TwonlyDB.forTesting( + DatabaseConnection( + NativeDatabase.memory(), + closeStreamsSynchronously: true, + ), + ), + ) + ..registerSingleton(UserService()); + + userService.currentUser = UserData( + userId: 1, + username: 'test_user', + displayName: 'Test User', + subscriptionPlan: 'Free', + currentSetupPage: null, + appVersion: 100, + ); + userService.isUserCreated = true; + }); + + tearDown(() async { + await twonlyDB.close(); + }); + + test( + 'watchByGroupId stream emits updates when a new text message is inserted', + () async { + // 1. Create a group + await twonlyDB.groupsDao.createNewGroup( + GroupsCompanion.insert( + groupId: 'test_group_stream', + groupName: 'Test Group Stream', + ), + ); + + // 2. Start watching + final stream = await twonlyDB.messagesDao.watchByGroupId( + 'test_group_stream', + ); + + final emissions = >[]; + final subscription = stream.listen(emissions.add); + + // Wait briefly for initial empty emission + await Future.delayed(const Duration(milliseconds: 100)); + + expect(emissions, hasLength(1)); + expect(emissions.first, isEmpty); + + // 3. Insert a message + await twonlyDB.messagesDao.insertMessage( + MessagesCompanion.insert( + messageId: 'msg_1', + groupId: 'test_group_stream', + type: 'text', + content: const Value('Hello world'), + ), + ); + + // Wait for the stream to propagate the event + await Future.delayed(const Duration(milliseconds: 100)); + + // Verify it emitted the new list containing our message + expect(emissions, hasLength(2)); + expect(emissions[1], hasLength(1)); + expect(emissions[1].first.messageId, equals('msg_1')); + + await subscription.cancel(); + }, + ); + + test( + 'simulate sending flow with group updates and verify messages stream emits', + () async { + await twonlyDB.groupsDao.createNewGroup( + GroupsCompanion.insert( + groupId: 'test_group_2', + groupName: 'Test Group 2', + ), + ); + + final msgStream = await twonlyDB.messagesDao.watchByGroupId( + 'test_group_2', + ); + final messagesEmissions = >[]; + final msgSub = msgStream.listen(messagesEmissions.add); + + final groupStream = twonlyDB.groupsDao.watchGroup('test_group_2'); + final groupEmissions = []; + final groupSub = groupStream.listen(groupEmissions.add); + + await Future.delayed(const Duration(milliseconds: 100)); + expect(messagesEmissions, hasLength(1)); + expect(groupEmissions, hasLength(1)); + + // Simulate sending: + // 1. Update group draftMessage to null (as done in insertAndSendTextMessage) + await twonlyDB.groupsDao.updateGroup( + 'test_group_2', + const GroupsCompanion( + draftMessage: Value(null), + ), + ); + + // 2. Insert message + final message = await twonlyDB.messagesDao.insertMessage( + MessagesCompanion( + groupId: const Value('test_group_2'), + content: const Value('Hello from simulation'), + type: Value(MessageType.text.name), + ), + ); + + expect(message, isNotNull); + + await Future.delayed(const Duration(milliseconds: 100)); + + // Verify group stream received updates + expect(groupEmissions.length, greaterThan(1)); + + // Verify message stream received updates containing the new message + expect(messagesEmissions.length, greaterThan(1)); + expect( + messagesEmissions.last.any((m) => m.messageId == message!.messageId), + isTrue, + ); + + await msgSub.cancel(); + await groupSub.cancel(); + }, + ); +} diff --git a/test/services/passwordless_recovery_service_test.dart b/test/services/passwordless_recovery_service_test.dart new file mode 100644 index 00000000..43d6d81c --- /dev/null +++ b/test/services/passwordless_recovery_service_test.dart @@ -0,0 +1,612 @@ +// ignore_for_file: strict_raw_type + +import 'dart:convert' show utf8; +import 'dart:io'; + +import 'package:clock/clock.dart'; +import 'package:crypto/crypto.dart'; +import 'package:drift/drift.dart' hide isNotNull, isNull; +import 'package:drift/native.dart'; +import 'package:fixnum/fixnum.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; +import 'package:twonly/core/bridge.dart' as bridge; +import 'package:twonly/core/frb_generated.dart'; +import 'package:twonly/globals.dart'; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/callbacks/callbacks.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/model/json/userdata.model.dart'; +import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart' + as api_pb; +import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart' + as pb; +import 'package:twonly/src/services/api.service.dart'; +import 'package:twonly/src/services/api/utils.api.dart'; +import 'package:twonly/src/services/passwordless_recovery.service.dart'; +import 'package:twonly/src/services/signal/identity.signal.dart'; +import 'package:twonly/src/services/signal/session.signal.dart'; +import 'package:twonly/src/services/user.service.dart'; +import 'package:twonly/src/utils/log.dart'; + +import '../mocks/platform_channels.dart'; + +class MockApiService extends ApiService { + final List<(List, List?)> registeredPasswordLessRecoveries = []; + final List<(int, Uint8List, List?)> sentTextMessages = []; + + @override + Future registerPasswordLessRecovery( + List encryptedServerKey, + List? pinUnlockToken, + ) async { + registeredPasswordLessRecoveries.add((encryptedServerKey, pinUnlockToken)); + return Result.success(api_pb.Response_Ok()); + } + + @override + Future sendTextMessage( + int target, + Uint8List msg, + List? pushData, + ) async { + sentTextMessages.add((target, msg, pushData)); + return Result.success(api_pb.Response_Ok()); + } + + @override + Future updateSignedPreKey( + int id, + List key, + List signature, + ) async { + return Result.success(api_pb.Response_Ok()); + } +} + +void main() { + if (!Platform.isMacOS) { + return; + } + + TestWidgetsFlutterBinding.ensureInitialized(); + + late Directory tempDir; + + setUpAll(() async { + Log.init(); + driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; + final dylibPath = + '${Directory.current.path}/rust/target/debug/librust_lib_twonly.dylib'; + if (File(dylibPath).existsSync()) { + await RustLib.init( + externalLibrary: ExternalLibrary.open(dylibPath), + ); + } else { + await RustLib.init(); + } + await initFlutterCallbacksForRust(); + + tempDir = Directory.systemTemp.createTempSync('twonly_passwordless_test_'); + AppEnvironment.initTesting( + customCacheDir: tempDir.path, + customSupportDir: tempDir.path, + ); + + await bridge.initializeTwonlyFlutter( + config: bridge.InitConfig( + databaseDir: tempDir.path, + dataDir: tempDir.path, + ), + ); + }); + + setUp(() async { + setupPlatformChannelMocks(); + await locator.reset(); + final dbFile = File('${tempDir.path}/twonly.sqlite'); + if (dbFile.existsSync()) { + dbFile.deleteSync(); + } + + locator + ..registerSingleton( + TwonlyDB(NativeDatabase(dbFile)), + ) + ..registerSingleton(UserService()) + ..registerSingleton(MockApiService()); + + userService.currentUser = UserData( + userId: 1, + username: 'me', + displayName: 'Me', + subscriptionPlan: 'Free', + currentSetupPage: null, + appVersion: 100, + ); + userService.isUserCreated = true; + await UserService.save(userService.currentUser); + + await createIfNotExistsSignalIdentity(); + }); + + tearDown(() async { + try { + await twonlyDB.close(); + } catch (_) {} + }); + + tearDownAll(() async { + if (tempDir.existsSync()) { + try { + tempDir.deleteSync(recursive: true); + } catch (_) {} + } + }); + + Future setupSignalSession(int contactId) async { + final identityKeyPair = generateIdentityKeyPair(); + final registrationId = generateRegistrationId(true); + final signedPreKey = generateSignedPreKey(identityKeyPair, 1); + final preKey = generatePreKeys(1, 1).first; + + final responseUserData = api_pb.Response_UserData() + ..userId = Int64(contactId) + ..username = utf8.encode('user_$contactId') + ..registrationId = Int64(registrationId) + ..publicIdentityKey = identityKeyPair.getPublicKey().serialize() + ..signedPrekey = signedPreKey.getKeyPair().publicKey.serialize() + ..signedPrekeyId = Int64(signedPreKey.id) + ..signedPrekeySignature = signedPreKey.signature; + + responseUserData.prekeys.add( + api_pb.Response_PreKey() + ..id = Int64(preKey.id) + ..prekey = preKey.getKeyPair().publicKey.serialize(), + ); + + final success = await processSignalUserData(responseUserData); + expect(success, isTrue); + } + + group('PasswordlessRecoveryService - enablePasswordlessRecovery', () { + test('works with SecondFactorType.none', () async { + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert(userId: const Value(2), username: 'friend_2'), + ); + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert(userId: const Value(3), username: 'friend_3'), + ); + + final success = + await PasswordlessRecoveryService.enablePasswordlessRecovery( + trustedFriendIds: [2, 3], + secondFactorType: SecondFactorType.none, + secondFactorValue: '', + threshold: 2, + ); + + expect(success, isTrue); + + final c2 = await twonlyDB.contactsDao.getContactById(2); + final c3 = await twonlyDB.contactsDao.getContactById(3); + + expect(c2?.recoveryIsTrustedFriend, isTrue); + expect(c3?.recoveryIsTrustedFriend, isTrue); + expect(c2?.recoverySecretShare, isNotNull); + expect(c3?.recoverySecretShare, isNotNull); + expect(c2?.recoveryLastHeartbeat, isNull); + expect(c3?.recoveryLastHeartbeat, isNull); + }); + + test('works with SecondFactorType.email', () async { + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert(userId: const Value(2), username: 'friend_2'), + ); + + final success = + await PasswordlessRecoveryService.enablePasswordlessRecovery( + trustedFriendIds: [2], + secondFactorType: SecondFactorType.email, + secondFactorValue: 'a_very_long_email_addr@gmail.com', + threshold: 1, + ); + + expect(success, isTrue); + + final c2 = await twonlyDB.contactsDao.getContactById(2); + expect(c2?.recoveryIsTrustedFriend, isTrue); + expect(c2?.recoverySecretShare, isNotNull); + }); + + test('works with SecondFactorType.pin', () async { + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert(userId: const Value(2), username: 'friend_2'), + ); + + final success = + await PasswordlessRecoveryService.enablePasswordlessRecovery( + trustedFriendIds: [2], + secondFactorType: SecondFactorType.pin, + secondFactorValue: '123456', + threshold: 1, + ); + + expect(success, isTrue); + + final c2 = await twonlyDB.contactsDao.getContactById(2); + expect(c2?.recoveryIsTrustedFriend, isTrue); + expect(c2?.recoverySecretShare, isNotNull); + }); + + test('sends delete messages to old trusted friends', () async { + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(2), + username: 'friend_2', + recoveryIsTrustedFriend: const Value(true), + ), + ); + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert(userId: const Value(3), username: 'friend_3'), + ); + + await setupSignalSession(2); + await setupSignalSession(3); + + final mockApi = apiService as MockApiService; + mockApi.sentTextMessages.clear(); + + final success = + await PasswordlessRecoveryService.enablePasswordlessRecovery( + trustedFriendIds: [3], + secondFactorType: SecondFactorType.none, + secondFactorValue: '', + threshold: 1, + ); + + expect(success, isTrue); + + // Verify old friend got a delete message + expect(mockApi.sentTextMessages.isNotEmpty, isTrue); + final deleteMsg = mockApi.sentTextMessages.firstWhere((t) => t.$1 == 2); + expect(deleteMsg, isNotNull); + + final oldFriend = await twonlyDB.contactsDao.getContactById(2); + expect(oldFriend?.recoveryIsTrustedFriend, isFalse); + }); + }); + + group('PasswordlessRecoveryService - performHeartbeat', () { + test( + 'triggers server registration based on lastServerHeartbeat time', + () async { + final config = PasswordLessRecovery(3) + ..encryptedServerKey = Uint8List.fromList([1, 2, 3]) + ..pinUnlockToken = Uint8List.fromList([4, 5, 6]); + + await UserService.update((u) => u.passwordLessRecovery = config); + + final mockApi = apiService as MockApiService; + mockApi.registeredPasswordLessRecoveries.clear(); + + // Case 1: lastServerHeartbeat is null -> should trigger registration + await PasswordlessRecoveryService.performHeartbeat(); + expect(mockApi.registeredPasswordLessRecoveries.length, 1); + expect( + userService.currentUser.passwordLessRecovery?.lastServerHeartbeat, + isNotNull, + ); + + // Case 2: lastServerHeartbeat is < 30 days old -> should not trigger + mockApi.registeredPasswordLessRecoveries.clear(); + final now = clock.now(); + await UserService.update( + (u) => u.passwordLessRecovery?.lastServerHeartbeat = now.subtract( + const Duration(days: 15), + ), + ); + await PasswordlessRecoveryService.performHeartbeat(); + expect(mockApi.registeredPasswordLessRecoveries.length, 0); + + // Case 3: lastServerHeartbeat is > 30 days old -> should trigger + await withClock(Clock.fixed(now), () async { + await UserService.update( + (u) => u.passwordLessRecovery?.lastServerHeartbeat = now.subtract( + const Duration(days: 31), + ), + ); + await PasswordlessRecoveryService.performHeartbeat(); + expect(mockApi.registeredPasswordLessRecoveries.length, 1); + expect( + userService.currentUser.passwordLessRecovery?.lastServerHeartbeat, + now, + ); + }); + }, + ); + + test( + 'sends secret shares to trusted friends with null recoveryLastHeartbeat', + () async { + final config = PasswordLessRecovery(3) + ..encryptedServerKey = Uint8List.fromList([1, 2, 3]) + ..pinUnlockToken = Uint8List.fromList([4, 5, 6]); + await UserService.update((u) => u.passwordLessRecovery = config); + + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(2), + username: 'friend_2', + recoveryIsTrustedFriend: const Value(true), + recoverySecretShare: Value(Uint8List.fromList([1, 2, 3])), + recoveryLastHeartbeat: const Value(null), + ), + ); + + await setupSignalSession(2); + + final mockApi = apiService as MockApiService; + mockApi.sentTextMessages.clear(); + + final baseTime = DateTime(2026, 6, 20, 12); + + await withClock(Clock.fixed(baseTime), () async { + await PasswordlessRecoveryService.performHeartbeat(); + }); + + // Verify message sent + expect(mockApi.sentTextMessages.length, 1); + expect(mockApi.sentTextMessages.first.$1, 2); + + // Verify db update + final c2 = await twonlyDB.contactsDao.getContactById(2); + expect(c2?.recoveryLastHeartbeat, isNull); + + // Verify calling again does not resend + mockApi.sentTextMessages.clear(); + await withClock(Clock.fixed(baseTime), () async { + await PasswordlessRecoveryService.performHeartbeat(); + }); + expect(mockApi.sentTextMessages.length, 0); + }, + ); + + test( + 'sends heartbeat to friends who chose us as a trusted friend based on time', + () async { + final config = PasswordLessRecovery(3) + ..encryptedServerKey = Uint8List.fromList([1, 2, 3]) + ..pinUnlockToken = Uint8List.fromList([4, 5, 6]); + await UserService.update((u) => u.passwordLessRecovery = config); + + final shareBytes = Uint8List.fromList([10, 20, 30]); + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(3), + username: 'friend_3', + recoveryContactsSecretShare: Value(shareBytes), + recoveryContactsLastHeartbeat: const Value(null), + ), + ); + + await setupSignalSession(3); + + final mockApi = apiService as MockApiService; + mockApi.sentTextMessages.clear(); + + final baseTime = DateTime(2026, 6, 20, 12); + + // Case 1: recoveryContactsLastHeartbeat is null -> should send + await withClock(Clock.fixed(baseTime), () async { + await PasswordlessRecoveryService.performHeartbeat(); + }); + expect(mockApi.sentTextMessages.length, 1); + expect(mockApi.sentTextMessages.first.$1, 3); + + final c3 = await twonlyDB.contactsDao.getContactById(3); + expect(c3?.recoveryContactsLastHeartbeat, baseTime); + + // Case 2: last heartbeat is < 7 days -> should not send + mockApi.sentTextMessages.clear(); + await withClock( + Clock.fixed(baseTime.add(const Duration(days: 5))), + () async { + await PasswordlessRecoveryService.performHeartbeat(); + }, + ); + expect(mockApi.sentTextMessages.length, 0); + + // Case 3: last heartbeat is > 7 days -> should send + mockApi.sentTextMessages.clear(); + final targetTime = baseTime.add(const Duration(days: 8)); + await withClock(Clock.fixed(targetTime), () async { + await PasswordlessRecoveryService.performHeartbeat(); + }); + expect(mockApi.sentTextMessages.length, 1); + + final c3Updated = await twonlyDB.contactsDao.getContactById(3); + expect(c3Updated?.recoveryContactsLastHeartbeat, targetTime); + }, + ); + + test( + 'sends heartbeat to friends who chose us as a trusted friend even if our config is null', + () async { + await UserService.update((u) => u.passwordLessRecovery = null); + + final shareBytes = Uint8List.fromList([10, 20, 30]); + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(4), + username: 'friend_4', + recoveryContactsSecretShare: Value(shareBytes), + recoveryContactsLastHeartbeat: const Value(null), + ), + ); + + await setupSignalSession(4); + + final mockApi = apiService as MockApiService; + mockApi.sentTextMessages.clear(); + + final baseTime = DateTime(2026, 6, 20, 12); + + await withClock(Clock.fixed(baseTime), () async { + await PasswordlessRecoveryService.performHeartbeat(); + }); + + expect(mockApi.sentTextMessages.length, 1); + expect(mockApi.sentTextMessages.first.$1, 4); + }, + ); + }); + + group('PasswordlessRecoveryService - handlePasswordlessRecovery', () { + test( + 'handlePasswordlessRecovery deletes share when delete is true', + () async { + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(4), + username: 'friend_4', + recoveryContactsSecretShare: Value(Uint8List.fromList([7, 8, 9])), + recoveryContactsLastHeartbeat: Value(DateTime.now()), + ), + ); + + final msg = pb.EncryptedContent_PasswordLessRecovery()..delete = true; + + await PasswordlessRecoveryService.handlePasswordlessRecovery( + 4, + msg, + 'receipt_id_1', + ); + + final c4 = await twonlyDB.contactsDao.getContactById(4); + expect(c4?.recoveryContactsSecretShare, isNull); + expect(c4?.recoveryContactsLastHeartbeat, isNull); + }, + ); + + test( + 'handlePasswordlessRecovery saves share when delete is false', + () async { + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(4), + username: 'friend_4', + ), + ); + + final msg = pb.EncryptedContent_PasswordLessRecovery() + ..delete = false + ..recoverySecretShare = [11, 22, 33]; + + await PasswordlessRecoveryService.handlePasswordlessRecovery( + 4, + msg, + 'receipt_id_2', + ); + + final c4 = await twonlyDB.contactsDao.getContactById(4); + expect( + c4?.recoveryContactsSecretShare, + Uint8List.fromList([11, 22, 33]), + ); + expect(c4?.recoveryContactsLastHeartbeat, isNull); + }, + ); + }); + + group('PasswordlessRecoveryService - handlePasswordlessRecoveryHeartbeat', () { + test('sends delete message back if stored share is null', () async { + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert(userId: const Value(5), username: 'friend_5'), + ); + + await setupSignalSession(5); + + final mockApi = apiService as MockApiService; + mockApi.sentTextMessages.clear(); + + final msg = pb.EncryptedContent_PasswordLessRecoveryHeartbeat() + ..hash = [1, 2, 3]; + + await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat( + 5, + msg, + 'receipt_id_3', + ); + + // Await unawaited sendCipherText call + await Future.delayed(const Duration(milliseconds: 100)); + + // We expect a delete message sent back + expect(mockApi.sentTextMessages.isNotEmpty, isTrue); + expect(mockApi.sentTextMessages.first.$1, 5); + }); + + test( + 'updates recoveryLastHeartbeat to clock.now() when hash matches', + () async { + final shareBytes = Uint8List.fromList([1, 2, 3]); + final hashBytes = sha256.convert(shareBytes).bytes; + + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(5), + username: 'friend_5', + recoverySecretShare: Value(shareBytes), + ), + ); + + final msg = pb.EncryptedContent_PasswordLessRecoveryHeartbeat() + ..hash = hashBytes; + + final now = DateTime(2026, 6, 20, 12); + + await withClock(Clock.fixed(now), () async { + await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat( + 5, + msg, + 'receipt_id_4', + ); + }); + + final c5 = await twonlyDB.contactsDao.getContactById(5); + expect(c5?.recoveryLastHeartbeat, now); + }, + ); + + test( + 'sets recoveryLastHeartbeat to null when hash does not match', + () async { + final shareBytes = Uint8List.fromList([1, 2, 3]); + final wrongHashBytes = sha256.convert([4, 5, 6]).bytes; + + await twonlyDB.contactsDao.insertContact( + ContactsCompanion.insert( + userId: const Value(5), + username: 'friend_5', + recoverySecretShare: Value(shareBytes), + recoveryLastHeartbeat: Value(DateTime.now()), + ), + ); + + final msg = pb.EncryptedContent_PasswordLessRecoveryHeartbeat() + ..hash = wrongHashBytes; + + await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat( + 5, + msg, + 'receipt_id_5', + ); + + final c5 = await twonlyDB.contactsDao.getContactById(5); + expect(c5?.recoveryLastHeartbeat, isNull); + }, + ); + }); +}