pop until contact view after sucessfull verification
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run

This commit is contained in:
otsmr 2026-07-18 20:25:28 +02:00
parent bb8f33e9d6
commit 6284345b57
4 changed files with 99 additions and 58 deletions

View file

@ -3,6 +3,7 @@ import 'dart:typed_data';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:cryptography_plus/cryptography_plus.dart'; import 'package:cryptography_plus/cryptography_plus.dart';
import 'package:flutter/material.dart';
import 'package:twonly/locator.dart'; import 'package:twonly/locator.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/contacts.table.dart'; import 'package:twonly/src/database/tables/contacts.table.dart';
@ -17,6 +18,24 @@ import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/components/verification_success_dialog.comp.dart'; import 'package:twonly/src/visual/components/verification_success_dialog.comp.dart';
class KeyVerificationService { class KeyVerificationService {
static final StreamController<void> _verificationSuccessCloseController =
StreamController<void>.broadcast();
static Stream<void> get onVerificationSuccessClose =>
_verificationSuccessCloseController.stream;
static void notifyVerificationSuccessClose() {
_verificationSuccessCloseController.add(null);
}
static void closeVerificationFlows(BuildContext context) {
Navigator.popUntil(context, (route) {
final name = route.settings.name;
if (name == null) return false;
return !name.contains('qr_scanner') && !name.contains('verifybadge');
});
}
static Future<List<int>> getNewSecretVerificationToken() async { static Future<List<int>> getNewSecretVerificationToken() async {
final token = getRandomUint8List(16); final token = getRandomUint8List(16);
await twonlyDB.keyVerificationDao.insertVerificationToken(token); await twonlyDB.keyVerificationDao.insertVerificationToken(token);

View file

@ -1,9 +1,11 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/key_verification.service.dart';
import 'package:twonly/src/utils/avatars.dart'; import 'package:twonly/src/utils/avatars.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/qr.utils.dart'; import 'package:twonly/src/utils/qr.utils.dart';
@ -22,59 +24,73 @@ class ProfileQrCodeComp extends StatefulWidget {
BuildContext context, { BuildContext context, {
Contact? contact, Contact? contact,
bool openToVerify = false, bool openToVerify = false,
}) { }) async {
return showModalBottomSheet( StreamSubscription<void>? subscription;
context: context,
isScrollControlled: true, if (openToVerify) {
backgroundColor: Colors.transparent, subscription = KeyVerificationService.onVerificationSuccessClose.listen((_) {
builder: (context) { if (context.mounted) {
return Container( KeyVerificationService.closeVerificationFlows(context);
decoration: BoxDecoration( }
color: context.color.surface, });
borderRadius: const BorderRadius.vertical( }
top: Radius.circular(24),
try {
await showModalBottomSheet<void>(
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),
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20), child: Column(
child: Column( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ const SizedBox(width: double.infinity),
const SizedBox(width: double.infinity), Container(
Container( width: 40,
width: 40, height: 4,
height: 4, decoration: BoxDecoration(
decoration: BoxDecoration( color: context.color.onSurface.withValues(alpha: 0.2),
color: context.color.onSurface.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(2),
borderRadius: BorderRadius.circular(2), ),
), ),
), const SizedBox(height: 24),
const SizedBox(height: 24), Text(
Text( openToVerify
openToVerify ? (contact != null
? (contact != null ? context.lang.letUserScanQrCode(
? context.lang.letUserScanQrCode( getContactDisplayName(contact),
getContactDisplayName(contact), )
) : context.lang.letFriendScanQrToVerify)
: context.lang.letFriendScanQrToVerify) : (contact != null
: (contact != null ? context.lang.letUserScanQrCode(
? context.lang.letUserScanQrCode( getContactDisplayName(contact),
getContactDisplayName(contact), )
) : context.lang.addContactQrSheetSubtext),
: context.lang.addContactQrSheetSubtext), style: TextStyle(
style: TextStyle( fontSize: 14,
fontSize: 14, color: context.color.onSurface.withValues(alpha: 0.6),
color: context.color.onSurface.withValues(alpha: 0.6), ),
textAlign: TextAlign.center,
), ),
textAlign: TextAlign.center, const SizedBox(height: 24),
), const ProfileQrCodeComp(),
const SizedBox(height: 24), const SizedBox(height: 34),
const ProfileQrCodeComp(), ],
const SizedBox(height: 34), ),
], );
), },
); );
}, } finally {
); await subscription?.cancel();
}
} }
@override @override

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/key_verification.service.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
import 'package:twonly/src/visual/components/verification_success_animation.comp.dart'; import 'package:twonly/src/visual/components/verification_success_animation.comp.dart';
@ -18,11 +19,16 @@ class VerificationSuccessDialog extends StatelessWidget {
final String? message; final String? message;
/// Utility method to easily present this dialog. /// Utility method to easily present this dialog.
static Future<void> show(BuildContext context, Contact contact, {String? message}) { static Future<void> show(
BuildContext context,
Contact contact, {
String? message,
}) {
return showDialog<void>( return showDialog<void>(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (context) => VerificationSuccessDialog(contact: contact, message: message), builder: (context) =>
VerificationSuccessDialog(contact: contact, message: message),
); );
} }
@ -75,7 +81,10 @@ class VerificationSuccessDialog extends StatelessWidget {
), ),
const SizedBox(height: 28), const SizedBox(height: 28),
MyButton( MyButton(
onPressed: () => Navigator.pop(context), onPressed: () {
KeyVerificationService.notifyVerificationSuccessClose();
Navigator.pop(context);
},
child: Text(context.lang.close), child: Text(context.lang.close),
), ),
], ],

View file

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/key_verification.service.dart';
import 'package:twonly/src/utils/misc.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.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/camera_preview_controller_view.dart';
@ -34,11 +35,7 @@ class QrCodeScannerViewState extends State<QrCodeScannerView> {
if (widget.openToVerify && widget.contact != null) { if (widget.openToVerify && widget.contact != null) {
_mainCameraController.onVerificationSuccessDismissed = (contact) { _mainCameraController.onVerificationSuccessDismissed = (contact) {
if (mounted) { if (mounted) {
Navigator.popUntil(context, (route) { KeyVerificationService.closeVerificationFlows(context);
final name = route.settings.name;
if (name == null) return true;
return !name.contains('qr_scanner') && !name.contains('verifybadge');
});
} }
}; };
} }