diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index e5e8056..fdf6e86 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -759,5 +759,7 @@ "accountKeyPairsDescription": "Manage the key pairs which used to encrypt messages.", "enrollNewKeyPair": "Enroll New One", "enrollNewKeyPairDescription": "Generate a new key pair.", - "keyPairHasPrivateKey": "With private key" + "keyPairHasPrivateKey": "With private key", + "decrypting": "Decrypting……", + "decryptingKeyNotFound": "Key not found or exchange failed, the other party may not be online" } diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json index dfa4c75..b1160ad 100644 --- a/assets/translations/zh-CN.json +++ b/assets/translations/zh-CN.json @@ -757,5 +757,7 @@ "accountKeyPairsDescription": "管理用于加密信息的密钥对。", "enrollNewKeyPair": "新建密钥对", "enrollNewKeyPairDescription": "生成一对新密钥对。", - "keyPairHasPrivateKey": "有私钥" + "keyPairHasPrivateKey": "有私钥", + "decrypting": "解密中……", + "decryptingKeyNotFound": "未找到密钥对或交换失败,对方可能不在线" } diff --git a/assets/translations/zh-HK.json b/assets/translations/zh-HK.json index ee3f771..d1ce236 100644 --- a/assets/translations/zh-HK.json +++ b/assets/translations/zh-HK.json @@ -756,6 +756,8 @@ "accountKeyPairs": "密鑰對", "accountKeyPairsDescription": "管理用於加密信息的密鑰對。", "enrollNewKeyPair": "新建密鑰對", - "enrollNewKeyPairDescription": "生成一對新密鑰對,覆蓋當前的;如果已有一個密鑰將會丟棄舊密鑰的私鑰。", - "keyPairHasPrivateKey": "有私鑰" + "enrollNewKeyPairDescription": "生成一對新密鑰對。", + "keyPairHasPrivateKey": "有私鑰", + "decrypting": "解密中……", + "decryptingKeyNotFound": "未找到密鑰對或交換失敗,對方可能不在線" } diff --git a/assets/translations/zh-TW.json b/assets/translations/zh-TW.json index 356b069..de46854 100644 --- a/assets/translations/zh-TW.json +++ b/assets/translations/zh-TW.json @@ -756,6 +756,8 @@ "accountKeyPairs": "密鑰對", "accountKeyPairsDescription": "管理用於加密信息的密鑰對。", "enrollNewKeyPair": "新建密鑰對", - "enrollNewKeyPairDescription": "生成一對新密鑰對,覆蓋當前的;如果已有一個密鑰將會丟棄舊密鑰的私鑰。", - "keyPairHasPrivateKey": "有私鑰" + "enrollNewKeyPairDescription": "生成一對新密鑰對。", + "keyPairHasPrivateKey": "有私鑰", + "decrypting": "解密中……", + "decryptingKeyNotFound": "未找到密鑰對或交換失敗,對方可能不在線" } diff --git a/drift_schemas/my_database/drift_schema_v2.json b/drift_schemas/my_database/drift_schema_v2.json index 74978a3..ef6ca3e 100644 --- a/drift_schemas/my_database/drift_schema_v2.json +++ b/drift_schemas/my_database/drift_schema_v2.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"sn_local_chat_channel","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":"alias","getter_name":"alias","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":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnChannelConverter()","dart_type_name":"SnChannel"}},{"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":1,"references":[],"type":"table","data":{"name":"sn_local_chat_message","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":"channel_id","getter_name":"channelId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnMessageConverter()","dart_type_name":"SnChatMessage"}},{"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":2,"references":[],"type":"table","data":{"name":"sn_local_key_pair","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"account_id","getter_name":"accountId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"public_key","getter_name":"publicKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"private_key","getter_name":"privateKey","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_active","getter_name":"isActive","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_active\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_active\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"sn_local_chat_channel","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":"alias","getter_name":"alias","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":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnChannelConverter()","dart_type_name":"SnChannel"}},{"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":1,"references":[],"type":"table","data":{"name":"sn_local_chat_message","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":"channel_id","getter_name":"channelId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnMessageConverter()","dart_type_name":"SnChatMessage"}},{"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":2,"references":[],"type":"table","data":{"name":"sn_local_key_pair","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"account_id","getter_name":"accountId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"public_key","getter_name":"publicKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"private_key","getter_name":"privateKey","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_active","getter_name":"isActive","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_active\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_active\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart index cd6c0a1..57c1e76 100644 --- a/lib/database/database.g.dart +++ b/lib/database/database.g.dart @@ -621,7 +621,7 @@ class $SnLocalKeyPairTable extends SnLocalKeyPair } @override - Set get $primaryKey => const {}; + Set get $primaryKey => {id}; @override SnLocalKeyPairData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; diff --git a/lib/database/database.steps.dart b/lib/database/database.steps.dart index e5972e9..a3e8e26 100644 --- a/lib/database/database.steps.dart +++ b/lib/database/database.steps.dart @@ -47,7 +47,9 @@ final class Schema2 extends i0.VersionedSchema { entityName: 'sn_local_key_pair', withoutRowId: false, isStrict: false, - tableConstraints: [], + tableConstraints: [ + 'PRIMARY KEY(id)', + ], columns: [ _column_5, _column_6, diff --git a/lib/database/keypair.dart b/lib/database/keypair.dart index 05c4b5e..0e4c5fd 100644 --- a/lib/database/keypair.dart +++ b/lib/database/keypair.dart @@ -10,4 +10,7 @@ class SnLocalKeyPair extends Table { TextColumn get privateKey => text().nullable()(); BoolColumn get isActive => boolean().withDefault(Constant(false))(); + + @override + Set> get primaryKey => {id}; } diff --git a/lib/providers/keypair.dart b/lib/providers/keypair.dart index b1df38f..ce51709 100644 --- a/lib/providers/keypair.dart +++ b/lib/providers/keypair.dart @@ -35,24 +35,35 @@ class KeyPairProvider { case 'kex.ack': ackKeyExchange(event); break; - case 'key.ask': + case 'kex.ask': replyAskKeyExchange(event); break; } }); } - Future decryptText(String text, String kpId) async { + Future decryptText(String text, String kpId, {int? kpOwner}) async { + String? publicKey; final kp = await (_dt.db.snLocalKeyPair.select() ..where((e) => e.id.equals(kpId))) .getSingleOrNull(); - if (kp == null) throw Exception('Key pair not found'); - return await RSA.decryptPKCS1v15(text, kp.privateKey!); + if (kp == null) { + if (kpOwner != null) { + final out = await askKeyExchange(kpOwner, kpId); + publicKey = out.publicKey; + } + } else { + publicKey = kp.publicKey; + } + if (publicKey == null) { + throw Exception('Key pair not found'); + } + return await RSA.decryptPKCS1v15(text, publicKey); } Future encryptText(String text) async { if (activeKp == null) throw Exception('No active key pair'); - return await RSA.encryptPKCS1v15(text, activeKp!.publicKey); + return await RSA.encryptPKCS1v15(text, activeKp!.privateKey!); } final Map> _requests = {}; @@ -65,7 +76,7 @@ class KeyPairProvider { _ws.conn?.sink.add( jsonEncode(WebSocketPackage( - method: 'key.ask', + method: 'kex.ask', endpoint: 'id', payload: { 'keypair_id': kpId, @@ -105,12 +116,7 @@ class KeyPairProvider { publicKey: kpMeta.publicKey, privateKey: Value(kpMeta.privateKey), ), - onConflict: DoUpdate( - (_) => SnLocalKeyPairCompanion.custom( - publicKey: Constant(kpMeta.publicKey), - privateKey: Constant(kpMeta.privateKey), - ), - ), + onConflict: DoNothing(), ); } @@ -208,8 +214,10 @@ class KeyPairProvider { final kpMeta = SnKeyPair( id: id, accountId: _ua.user!.id, - publicKey: kp.publicKey, - privateKey: kp.privateKey, + // This is work as expected + // We need to share private key to let everyone can decode the message + publicKey: kp.privateKey, + privateKey: kp.publicKey, ); // Save the keypair to the local database diff --git a/lib/providers/websocket.dart b/lib/providers/websocket.dart index c66a59b..758cb16 100644 --- a/lib/providers/websocket.dart +++ b/lib/providers/websocket.dart @@ -53,12 +53,11 @@ class WebSocketProvider extends ChangeNotifier { try { _connectCompleter = Completer(); - final clientId = await FlutterUdid.consistentUdid; final atk = await _sn.getFreshAtk(); final uri = Uri.parse( kIsWeb ? '${_sn.client.options.baseUrl.replaceFirst('http', 'ws')}/ws?tk=$atk' - : '${_sn.client.options.baseUrl.replaceFirst('http', 'ws')}/ws?clientId=${clientId}tk=$atk', + : '${_sn.client.options.baseUrl.replaceFirst('http', 'ws')}/ws?clientId=${await FlutterUdid.consistentUdid}tk=$atk', ); isBusy = true; diff --git a/lib/widgets/chat/chat_message.dart b/lib/widgets/chat/chat_message.dart index 539fbc6..76ce065 100644 --- a/lib/widgets/chat/chat_message.dart +++ b/lib/widgets/chat/chat_message.dart @@ -9,6 +9,7 @@ import 'package:popover/popover.dart'; import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/config.dart'; +import 'package:surface/providers/keypair.dart'; import 'package:surface/providers/user_directory.dart'; import 'package:surface/providers/userinfo.dart'; import 'package:surface/screens/account/profile_page.dart'; @@ -106,30 +107,34 @@ class ChatMessage extends StatelessWidget { GestureDetector( child: AccountImage( content: user?.avatar, - badge: - (user?.badges.isNotEmpty ?? false) - ? Icon( - kBadgesMeta[user!.badges.first.type]?.$2 ?? Symbols.question_mark, - color: kBadgesMeta[user.badges.first.type]?.$3, - fill: 1, - size: 18, - shadows: [ - Shadow(offset: Offset(1, 1), blurRadius: 5.0, color: Color.fromARGB(200, 0, 0, 0)), - ], - ) - : null, + badge: (user?.badges.isNotEmpty ?? false) + ? Icon( + kBadgesMeta[user!.badges.first.type]?.$2 ?? + Symbols.question_mark, + color: kBadgesMeta[user.badges.first.type]?.$3, + fill: 1, + size: 18, + shadows: [ + Shadow( + offset: Offset(1, 1), + blurRadius: 5.0, + color: Color.fromARGB(200, 0, 0, 0)), + ], + ) + : null, ), onTap: () { if (user == null) return; showPopover( - backgroundColor: Theme.of(context).colorScheme.surface, + backgroundColor: + Theme.of(context).colorScheme.surface, context: context, transition: PopoverTransition.other, - bodyBuilder: - (context) => SizedBox( - width: math.min(400, MediaQuery.of(context).size.width - 10), - child: AccountPopoverCard(data: user), - ), + bodyBuilder: (context) => SizedBox( + width: math.min( + 400, MediaQuery.of(context).size.width - 10), + child: AccountPopoverCard(data: user), + ), direction: PopoverDirection.bottom, arrowHeight: 5, arrowWidth: 15, @@ -150,12 +155,19 @@ class ChatMessage extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (isCompact) AccountImage(content: user?.avatar, radius: 12).padding(right: 8), + if (isCompact) + AccountImage( + content: user?.avatar, radius: 12) + .padding(right: 8), Text( - (data.sender.nick?.isNotEmpty ?? false) ? data.sender.nick! : user?.nick ?? 'unknown', + (data.sender.nick?.isNotEmpty ?? false) + ? data.sender.nick! + : user?.nick ?? 'unknown', ).bold(), const Gap(8), - Text(dateFormatter.format(data.createdAt.toLocal())).fontSize(13), + Text(dateFormatter + .format(data.createdAt.toLocal())) + .fontSize(13), ], ).height(21), if (isCompact) const Gap(8), @@ -164,10 +176,14 @@ class ChatMessage extends StatelessWidget { Container( constraints: BoxConstraints(maxWidth: 360), decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(8)), - border: Border.all(color: Theme.of(context).dividerColor, width: 1), + borderRadius: const BorderRadius.all( + Radius.circular(8)), + border: Border.all( + color: Theme.of(context).dividerColor, + width: 1), ), - padding: const EdgeInsets.only(left: 4, right: 4, top: 8, bottom: 6), + padding: const EdgeInsets.only( + left: 4, right: 4, top: 8, bottom: 6), child: ChatMessage( data: data.preload!.quoteEvent!, isCompact: true, @@ -179,11 +195,11 @@ class ChatMessage extends StatelessWidget { ).padding(bottom: 4, top: 4), switch (data.type) { 'messages.new' => _ChatMessageText( - data: data, - onReply: onReply, - onEdit: onEdit, - onDelete: onDelete, - ), + data: data, + onReply: onReply, + onEdit: onEdit, + onDelete: onDelete, + ), _ => _ChatMessageSystemNotify(data: data), }, ], @@ -204,9 +220,13 @@ class ChatMessage extends StatelessWidget { bordered: true, maxHeight: 360, maxWidth: 480 - 48 - padding.left, - padding: padding.copyWith(top: 8, left: isCompact ? padding.left : 48 + padding.left), + padding: padding.copyWith( + top: 8, left: isCompact ? padding.left : 48 + padding.left), ), - if (!hasMerged && !isCompact) const Gap(12) else if (!isCompact) const Gap(8), + if (!hasMerged && !isCompact) + const Gap(12) + else if (!isCompact) + const Gap(8), ], ), ), @@ -220,7 +240,8 @@ class _ChatMessageText extends StatelessWidget { final Function(SnChatMessage)? onEdit; final Function(SnChatMessage)? onDelete; - const _ChatMessageText({required this.data, this.onReply, this.onEdit, this.onDelete}); + const _ChatMessageText( + {required this.data, this.onReply, this.onEdit, this.onDelete}); @override Widget build(BuildContext context) { @@ -234,7 +255,8 @@ class _ChatMessageText extends StatelessWidget { children: [ SelectionArea( contextMenuBuilder: (context, editableTextState) { - final List items = editableTextState.contextMenuButtonItems; + final List items = + editableTextState.contextMenuButtonItems; if (onReply != null) { items.insert( @@ -278,13 +300,18 @@ class _ChatMessageText extends StatelessWidget { buttonItems: items, ); }, - child: MarkdownTextContent( - content: data.body['text'], - isAutoWarp: true, - isEnlargeSticker: RegExp(r"^:([-\w]+):$").hasMatch(data.body['text'] ?? ''), - ), + child: switch (data.body['algorithm']) { + 'rsa' => _ChatDecryptMessage(message: data), + _ => MarkdownTextContent( + content: data.body['text'], + isAutoWarp: true, + isEnlargeSticker: + RegExp(r"^:([-\w]+):$").hasMatch(data.body['text'] ?? ''), + ), + }, ), - if (data.updatedAt != data.createdAt) Text('messageEditedHint'.tr()).fontSize(13).opacity(0.75), + if (data.updatedAt != data.createdAt) + Text('messageEditedHint'.tr()).fontSize(13).opacity(0.75), ], ); } else if (data.body['attachments']?.isNotEmpty) { @@ -335,14 +362,19 @@ class _ChatMessageSystemNotify extends StatelessWidget { ).opacity(0.75); case 'calls.start': return Row( - children: [const Icon(Symbols.call, size: 20), const Gap(4), Text('callMessageStarted'.tr())], + children: [ + const Icon(Symbols.call, size: 20), + const Gap(4), + Text('callMessageStarted'.tr()) + ], ).opacity(0.75); case 'calls.end': return Row( children: [ const Icon(Symbols.call_end, size: 20), const Gap(4), - Text('callMessageEnded'.tr(args: [_formatDuration(Duration(seconds: data.body['last']))])), + Text('callMessageEnded'.tr( + args: [_formatDuration(Duration(seconds: data.body['last']))])), ], ).opacity(0.75); default: @@ -356,3 +388,33 @@ class _ChatMessageSystemNotify extends StatelessWidget { } } } + +class _ChatDecryptMessage extends StatelessWidget { + final SnChatMessage message; + const _ChatDecryptMessage({required this.message}); + + @override + Widget build(BuildContext context) { + final kp = context.read(); + return FutureBuilder( + future: kp.decryptText( + message.body['text'], + message.body['keypair_id'], + kpOwner: message.sender.accountId, + ), + builder: (context, snapshot) { + if (snapshot.hasError) { + return Text('decryptingKeyNotFound'.tr()); + } + if (!snapshot.hasData) { + return Text('decrypting'.tr()); + } + return MarkdownTextContent( + content: snapshot.data!, + isAutoWarp: true, + isEnlargeSticker: RegExp(r"^:([-\w]+):$").hasMatch(snapshot.data!), + ); + }, + ); + } +} diff --git a/macos/Podfile.lock b/macos/Podfile.lock index a78a874..52c38fa 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -7,6 +7,8 @@ PODS: - FlutterMacOS - device_info_plus (0.0.1): - FlutterMacOS + - fast_rsa (0.6.0): + - FlutterMacOS - file_picker (0.0.1): - FlutterMacOS - file_saver (0.0.1): @@ -23,14 +25,14 @@ PODS: - Firebase/Messaging (11.8.0): - Firebase/CoreOnly - FirebaseMessaging (~> 11.8.0) - - firebase_analytics (11.4.3): + - firebase_analytics (11.4.4): - Firebase/Analytics (= 11.8.0) - firebase_core - FlutterMacOS - - firebase_core (3.12.0): + - firebase_core (3.12.1): - Firebase/CoreOnly (~> 11.8.0) - FlutterMacOS - - firebase_messaging (15.2.3): + - firebase_messaging (15.2.4): - Firebase/CoreOnly (~> 11.8.0) - Firebase/Messaging (~> 11.8.0) - firebase_core @@ -76,6 +78,8 @@ PODS: - flutter_inappwebview_macos (0.0.1): - FlutterMacOS - OrderedSet (~> 6.0.3) + - flutter_timezone (0.1.0): + - FlutterMacOS - flutter_udid (0.0.1): - FlutterMacOS - SAMKeychain @@ -86,6 +90,8 @@ PODS: - gal (1.0.0): - Flutter - FlutterMacOS + - geolocator_apple (1.2.0): + - FlutterMacOS - GoogleAppMeasurement (11.8.0): - GoogleAppMeasurement/AdIdSupport (= 11.8.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) @@ -213,6 +219,7 @@ DEPENDENCIES: - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - fast_rsa (from `Flutter/ephemeral/.symlinks/plugins/fast_rsa/macos`) - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`) - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) @@ -220,10 +227,12 @@ DEPENDENCIES: - firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`) - firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`) - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) + - flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`) - flutter_udid (from `Flutter/ephemeral/.symlinks/plugins/flutter_udid/macos`) - flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`) + - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos`) - hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`) - in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`) - livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`) @@ -272,6 +281,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/croppy/macos device_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + fast_rsa: + :path: Flutter/ephemeral/.symlinks/plugins/fast_rsa/macos file_picker: :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos file_saver: @@ -286,6 +297,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos flutter_inappwebview_macos: :path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos + flutter_timezone: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos flutter_udid: :path: Flutter/ephemeral/.symlinks/plugins/flutter_udid/macos flutter_webrtc: @@ -294,6 +307,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral gal: :path: Flutter/ephemeral/.symlinks/plugins/gal/darwin + geolocator_apple: + :path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos hotkey_manager_macos: :path: Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos in_app_review: @@ -338,23 +353,26 @@ SPEC CHECKSUMS: connectivity_plus: 0a976dfd033b59192912fa3c6c7b54aab5093802 croppy: 25a638bd7d05411d8c697f481568f261037694fc device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215 + fast_rsa: 47a50bec1042c8c01726007dc0590a078418f997 file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af file_saver: 44e6fbf666677faf097302460e214e977fdd977b file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf - firebase_analytics: 1a71372a9735d7046d2c69db848a8d178f9fb587 - firebase_core: 68e1d27035b096239f147a041643e14e156f1481 - firebase_messaging: 89b5e0e28413dd878a58d2f286cdc03887b5d467 + firebase_analytics: 75b9d9ea8b21ce77898a3a46910e2051e93db8e1 + firebase_core: 1b573eac37729348cdc472516991dd7e269ae37e + firebase_messaging: 0620038ea399ceae2218c9087fca00a28f576209 FirebaseAnalytics: 4fd42def128146e24e480e89f310e3d8534ea42b FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629 FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917 FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8 flutter_inappwebview_macos: bdf207b8f4ebd58e86ae06cd96b147de99a67c9b + flutter_timezone: 62400baa441155f2a4144188648f2ff861649747 flutter_udid: 2e7b3da4b5fdfba86a396b97898f5fe8f4ec1a52 flutter_webrtc: d55fd3f5c75b42940b6b4b2cf376a5797398d1f8 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5 + geolocator_apple: 72a78ae3f3e4ec0db62117bd93e34523f5011d58 GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d diff --git a/test/drift/my_database/generated/schema_v2.dart b/test/drift/my_database/generated/schema_v2.dart index 2edd76e..86329b6 100644 --- a/test/drift/my_database/generated/schema_v2.dart +++ b/test/drift/my_database/generated/schema_v2.dart @@ -481,7 +481,7 @@ class SnLocalKeyPair extends Table String get actualTableName => $name; static const String $name = 'sn_local_key_pair'; @override - Set get $primaryKey => const {}; + Set get $primaryKey => {id}; @override SnLocalKeyPairData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';