improve qr code verifications
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run

This commit is contained in:
otsmr 2026-05-19 15:27:44 +02:00
parent 65d188c4f2
commit 304190387d
16 changed files with 336 additions and 125 deletions

View file

@ -27,7 +27,8 @@ class KeyVerificationDao extends DatabaseAccessor<TwonlyDB>
KeyVerificationDao(super.db);
Future<List<VerificationToken>> getRecentVerificationTokens() {
final cutoff = DateTime.now().subtract(const Duration(hours: 24));
// Tokens are only valid for one hour, so if the users are currently offline, the verification notification will still work later.
final cutoff = DateTime.now().subtract(const Duration(hours: 1));
return (select(
verificationTokens,
)..where((t) => t.createdAt.isBiggerOrEqualValue(cutoff))).get();
@ -223,4 +224,31 @@ class KeyVerificationDao extends DatabaseAccessor<TwonlyDB>
Log.error(e);
}
}
Future<void> deleteKeyVerification(int contactId) async {
try {
await (delete(keyVerifications)..where((kv) => kv.contactId.equals(contactId))).go();
if (userService.currentUser.isUserDiscoveryEnabled) {
await FlutterUserDiscovery.updateVerificationStateForUser(
contactId: contactId,
);
}
} catch (e) {
Log.error(e);
}
}
Future<void> deleteKeyVerificationById(int verificationId, int contactId) async {
try {
await (delete(keyVerifications)..where((kv) => kv.verificationId.equals(verificationId))).go();
final remaining = await getContactVerification(contactId);
if (remaining.isEmpty && userService.currentUser.isUserDiscoveryEnabled) {
await FlutterUserDiscovery.updateVerificationStateForUser(
contactId: contactId,
);
}
} catch (e) {
Log.error(e);
}
}
}

View file

@ -911,8 +911,8 @@ abstract class AppLocalizations {
/// No description provided for @verificationTypeSecretQrToken.
///
/// In en, this message translates to:
/// **'The other person scanned your QR code.'**
String get verificationTypeSecretQrToken;
/// **'{username} has scanned your QR code.'**
String verificationTypeSecretQrToken(Object username);
/// No description provided for @verificationTypeLink.
///
@ -2699,7 +2699,7 @@ abstract class AppLocalizations {
/// No description provided for @verificationBadgeGeneralDesc.
///
/// In en, this message translates to:
/// **'The checkmark gives you the certainty that you are messaging the right person. Scan the contact\'s QR code to verify it.'**
/// **'The checkmark gives you the certainty that you are messaging the right person. You can verify contacts at any time by scanning their QR code.'**
String get verificationBadgeGeneralDesc;
/// No description provided for @verificationBadgeGreenDesc.
@ -2720,6 +2720,24 @@ abstract class AppLocalizations {
/// **'A contact whose identity has *not* yet been verified.'**
String get verificationBadgeRedDesc;
/// No description provided for @deleteVerificationTitle.
///
/// In en, this message translates to:
/// **'Delete verification?'**
String get deleteVerificationTitle;
/// No description provided for @deleteVerificationBody.
///
/// In en, this message translates to:
/// **'Are you sure you want to delete this verification?'**
String get deleteVerificationBody;
/// No description provided for @secretQrTokenVerifiedSnackbar.
///
/// In en, this message translates to:
/// **'{username} has scanned your QR code and is now verified.'**
String secretQrTokenVerifiedSnackbar(Object username);
/// No description provided for @chatEntryFlameRestored.
///
/// In en, this message translates to:

View file

@ -447,8 +447,9 @@ class AppLocalizationsDe extends AppLocalizations {
String get verificationTypeQrScanned => 'Du hast den QR-Code gescannt.';
@override
String get verificationTypeSecretQrToken =>
'Die andere Person hat deinen QR-Code gescannt.';
String verificationTypeSecretQrToken(Object username) {
return '$username hat deinen QR-Code gescannt.';
}
@override
String get verificationTypeLink => 'Per Link verifiziert.';
@ -1501,7 +1502,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get verificationBadgeGeneralDesc =>
'Der Haken gibt dir die Sicherheit, dass du mit der richtigen Person schreibst. Scanne einen Kontakt, um diesen zu verifizieren.';
'Der Haken gibt dir die Sicherheit, dass du mit der richtigen Person schreibst. Du kannst Kontakte jederzeit verifizieren, indem du deren QR-Code scannst.';
@override
String get verificationBadgeGreenDesc =>
@ -1515,6 +1516,18 @@ class AppLocalizationsDe extends AppLocalizations {
String get verificationBadgeRedDesc =>
'Ein Kontakt, dessen Identität noch *nicht überprüft* wurde.';
@override
String get deleteVerificationTitle => 'Verifizierung löschen?';
@override
String get deleteVerificationBody =>
'Möchtest du diese Verifizierung wirklich löschen?';
@override
String secretQrTokenVerifiedSnackbar(Object username) {
return '$username hat deinen QR-Code gescannt und ist nun verifiziert.';
}
@override
String chatEntryFlameRestored(Object count) {
return '$count Flammen wiederhergestellt';

View file

@ -442,8 +442,9 @@ class AppLocalizationsEn extends AppLocalizations {
String get verificationTypeQrScanned => 'You scanned their QR code.';
@override
String get verificationTypeSecretQrToken =>
'The other person scanned your QR code.';
String verificationTypeSecretQrToken(Object username) {
return '$username has scanned your QR code.';
}
@override
String get verificationTypeLink => 'Verified via link.';
@ -1486,7 +1487,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get verificationBadgeGeneralDesc =>
'The checkmark gives you the certainty that you are messaging the right person. Scan the contact\'s QR code to verify it.';
'The checkmark gives you the certainty that you are messaging the right person. You can verify contacts at any time by scanning their QR code.';
@override
String get verificationBadgeGreenDesc =>
@ -1500,6 +1501,18 @@ class AppLocalizationsEn extends AppLocalizations {
String get verificationBadgeRedDesc =>
'A contact whose identity has *not* yet been verified.';
@override
String get deleteVerificationTitle => 'Delete verification?';
@override
String get deleteVerificationBody =>
'Are you sure you want to delete this verification?';
@override
String secretQrTokenVerifiedSnackbar(Object username) {
return '$username has scanned your QR code and is now verified.';
}
@override
String chatEntryFlameRestored(Object count) {
return '$count flames restored';

@ -1 +1 @@
Subproject commit a8c5a355abf95578f1bdbf6a71077c5078b9dd93
Subproject commit 0675e74501d6610a84273232517652db25965e3f

View file

@ -97,6 +97,7 @@ class PublicProfile extends $pb.GeneratedMessage {
$core.List<$core.int>? signedPrekeySignature,
$fixnum.Int64? signedPrekeyId,
$core.List<$core.int>? secretVerificationToken,
$fixnum.Int64? timestamp,
}) {
final result = create();
if (userId != null) result.userId = userId;
@ -109,6 +110,7 @@ class PublicProfile extends $pb.GeneratedMessage {
if (signedPrekeyId != null) result.signedPrekeyId = signedPrekeyId;
if (secretVerificationToken != null)
result.secretVerificationToken = secretVerificationToken;
if (timestamp != null) result.timestamp = timestamp;
return result;
}
@ -136,6 +138,7 @@ class PublicProfile extends $pb.GeneratedMessage {
..aInt64(7, _omitFieldNames ? '' : 'signedPrekeyId')
..a<$core.List<$core.int>>(
8, _omitFieldNames ? '' : 'secretVerificationToken', $pb.PbFieldType.OY)
..aInt64(9, _omitFieldNames ? '' : 'timestamp')
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -230,6 +233,15 @@ class PublicProfile extends $pb.GeneratedMessage {
$core.bool hasSecretVerificationToken() => $_has(7);
@$pb.TagNumber(8)
void clearSecretVerificationToken() => $_clearField(8);
@$pb.TagNumber(9)
$fixnum.Int64 get timestamp => $_getI64(8);
@$pb.TagNumber(9)
set timestamp($fixnum.Int64 value) => $_setInt64(8, value);
@$pb.TagNumber(9)
$core.bool hasTimestamp() => $_has(8);
@$pb.TagNumber(9)
void clearTimestamp() => $_clearField(9);
}
const $core.bool _omitFieldNames =

View file

@ -77,9 +77,19 @@ const PublicProfile$json = {
'10': 'secretVerificationToken',
'17': true
},
{
'1': 'timestamp',
'3': 9,
'4': 1,
'5': 3,
'9': 1,
'10': 'timestamp',
'17': true
},
],
'8': [
{'1': '_secret_verification_token'},
{'1': '_timestamp'},
],
};
@ -91,4 +101,5 @@ final $typed_data.Uint8List publicProfileDescriptor = $convert.base64Decode(
'lvbl9pZBgFIAEoA1IOcmVnaXN0cmF0aW9uSWQSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1cmUY'
'BiABKAxSFXNpZ25lZFByZWtleVNpZ25hdHVyZRIoChBzaWduZWRfcHJla2V5X2lkGAcgASgDUg'
'5zaWduZWRQcmVrZXlJZBI/ChlzZWNyZXRfdmVyaWZpY2F0aW9uX3Rva2VuGAggASgMSABSF3Nl'
'Y3JldFZlcmlmaWNhdGlvblRva2VuiAEBQhwKGl9zZWNyZXRfdmVyaWZpY2F0aW9uX3Rva2Vu');
'Y3JldFZlcmlmaWNhdGlvblRva2VuiAEBEiEKCXRpbWVzdGFtcBgJIAEoA0gBUgl0aW1lc3RhbX'
'CIAQFCHAoaX3NlY3JldF92ZXJpZmljYXRpb25fdG9rZW5CDAoKX3RpbWVzdGFtcA==');

View file

@ -17,4 +17,5 @@ message PublicProfile {
bytes signed_prekey_signature = 6;
int64 signed_prekey_id = 7;
optional bytes secret_verification_token = 8;
optional int64 timestamp = 9;
}

View file

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:twonly/app.dart';
import 'package:twonly/src/constants/routes.keys.dart';
@ -47,7 +48,10 @@ import 'package:twonly/src/visual/views/settings/subscription/subscription.view.
import 'package:twonly/src/visual/views/user_study/user_study_questionnaire.view.dart';
import 'package:twonly/src/visual/views/user_study/user_study_welcome.view.dart';
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
final routerProvider = GoRouter(
navigatorKey: rootNavigatorKey,
routes: [
GoRoute(
path: Routes.home,

View file

@ -3,14 +3,17 @@ import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:cryptography_plus/cryptography_plus.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
as pb;
import 'package:twonly/src/providers/routing.provider.dart';
import 'package:twonly/src/services/api/messages.api.dart';
import 'package:twonly/src/services/signal/identity.signal.dart';
import 'package:twonly/src/services/signal/session.signal.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/components/snackbar.dart';
class KeyVerificationService {
static Future<List<int>> getNewSecretVerificationToken() async {
@ -70,6 +73,18 @@ class KeyVerificationService {
VerificationType.secretQrToken,
);
Log.info('Contact was verified via secretQrToken');
final contact = await twonlyDB.contactsDao.getContactById(fromUserId);
final context = rootNavigatorKey.currentContext;
if (context != null && context.mounted && contact != null) {
showSnackbar(
context,
context.lang.secretQrTokenVerifiedSnackbar(
getContactDisplayName(contact),
),
level: SnackbarLevel.success,
);
}
return;
}
}

View file

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'package:clock/clock.dart';
import 'package:collection/collection.dart' show ListExtensions;
import 'package:drift/drift.dart' show Value;
import 'package:fixnum/fixnum.dart';
@ -41,6 +42,7 @@ class QrCodeUtils {
signedPrekeySignature: signedPreKey.signature,
signedPrekeyId: Int64(signedPreKey.id),
secretVerificationToken: secretVerificationToken,
timestamp: Int64(clock.now().millisecondsSinceEpoch),
);
final data = publicProfile.writeToBuffer();
@ -94,7 +96,18 @@ class QrCodeUtils {
);
if (verificationOk) {
if (profile.hasSecretVerificationToken()) {
var useSecretVerificationToken = profile.hasSecretVerificationToken();
if (profile.hasTimestamp()) {
// Only notify the scanned user if the QR code was generated within the last 10 minutes.
final timestamp = DateTime.fromMillisecondsSinceEpoch(
profile.timestamp.toInt(),
);
final tenMinutesAgo = clock.now().subtract(const Duration(minutes: 10));
if (timestamp.isBefore(tenMinutesAgo)) {
useSecretVerificationToken = false;
}
}
if (useSecretVerificationToken) {
unawaited(
KeyVerificationService.handleScannedVerificationToken(
contact.userId,

View file

@ -62,7 +62,12 @@ void _showOverlay({
required Duration displayDuration,
required void Function(AnimationController) onAnimationControllerInit,
}) {
final overlayState = Overlay.maybeOf(context);
var overlayState = Overlay.maybeOf(context);
if (overlayState == null) {
if (context is StatefulElement && context.state is NavigatorState) {
overlayState = (context.state as NavigatorState).overlay;
}
}
if (overlayState == null) return;
late OverlayEntry overlayEntry;

View file

@ -113,7 +113,7 @@ class CameraScannedOverlay extends StatelessWidget {
),
const SizedBox(width: 10),
Text(
getContactDisplayName(c.contact, maxLength: 13),
getContactDisplayName(c.contact, maxLength: 9),
),
Expanded(child: Container()),
ColoredBox(

View file

@ -4,11 +4,9 @@ import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/user_discovery.service.dart';
import 'package:twonly/src/utils/misc.dart';
@ -20,6 +18,7 @@ import 'package:twonly/src/visual/components/snackbar.dart';
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart';
import 'package:twonly/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart';
import 'package:twonly/src/visual/views/groups/group.view.dart';
import 'package:twonly/src/visual/views/settings/privacy/user_discovery.view.dart';
@ -35,13 +34,9 @@ class ContactView extends StatefulWidget {
class _ContactViewState extends State<ContactView> {
Contact? _contact;
List<GroupMember> _memberOfGroups = [];
List<KeyVerification> _keyVerifications = [];
List<(Contact, DateTime)> _transferredTrust = [];
late StreamSubscription<Contact?> _streamContact;
late StreamSubscription<List<GroupMember>> _streamMemberOfGroups;
late StreamSubscription<List<KeyVerification>> _streamKeyVerifications;
late StreamSubscription<List<(Contact, DateTime)>> _streamTransferredTrust;
@override
void initState() {
@ -63,30 +58,12 @@ class _ContactViewState extends State<ContactView> {
_memberOfGroups = groups;
});
});
_streamKeyVerifications = twonlyDB.keyVerificationDao
.watchContactVerification(widget.userId)
.listen((update) {
if (!mounted) return;
setState(() {
_keyVerifications = update;
});
});
_streamTransferredTrust = twonlyDB.keyVerificationDao
.watchTransferredTrustVerifications(widget.userId)
.listen((update) {
if (!mounted) return;
setState(() {
_transferredTrust = update;
});
});
}
@override
void dispose() {
_streamContact.cancel();
_streamMemberOfGroups.cancel();
_streamKeyVerifications.cancel();
_streamTransferredTrust.cancel();
super.dispose();
}
@ -260,74 +237,8 @@ class _ContactViewState extends State<ContactView> {
RestoreFlameComp(
contactId: widget.userId,
),
if (_keyVerifications.isEmpty && _transferredTrust.isEmpty)
BetterListTile(
leading: VerificationBadgeComp(
VerificationExpansionTileComp(
contact: contact,
size: 20,
),
text: context.lang.contactVerifyNumberTitle,
onTap: () async {
await context.push(Routes.settingsHelpFaqVerifyBadge);
setState(() {});
},
),
if (_keyVerifications.isNotEmpty || _transferredTrust.isNotEmpty)
ExpansionTile(
shape: const RoundedRectangleBorder(),
backgroundColor: context.color.surfaceContainer,
collapsedShape: const RoundedRectangleBorder(),
leading: Padding(
padding: const EdgeInsetsGeometry.only(left: 12, right: 12),
child: VerificationBadgeComp(
contact: contact,
size: 20,
),
),
title: Text(context.lang.userVerifiedTitle),
children: [
..._keyVerifications.map(
(kv) => ListTile(
dense: true,
title: Text(_verificationTypeLabel(context, kv.type)),
trailing: Text(
DateFormat.yMd(
Localizations.localeOf(context).toString(),
).format(kv.createdAt),
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 13,
),
),
),
),
..._transferredTrust.map(
(tt) => ListTile(
dense: true,
title: Row(
children: [
Text(
context.lang.contactVerifiedBy(
getContactDisplayName(tt.$1),
),
),
VerificationBadgeComp(
contact: tt.$1,
),
],
),
trailing: Text(
DateFormat.yMd(
Localizations.localeOf(context).toString(),
).format(tt.$2),
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 13,
),
),
),
),
],
),
if (userService.currentUser.isUserDiscoveryEnabled)
if (userService.currentUser.userDiscoveryRequiresManualApproval &&
@ -408,19 +319,6 @@ class _ContactViewState extends State<ContactView> {
}
}
String _verificationTypeLabel(BuildContext context, VerificationType type) {
return switch (type) {
VerificationType.qrScanned => context.lang.verificationTypeQrScanned,
VerificationType.secretQrToken =>
context.lang.verificationTypeSecretQrToken,
VerificationType.link => context.lang.verificationTypeLink,
VerificationType.contactSharedByVerified =>
context.lang.verificationTypeContactSharedByVerified,
VerificationType.migratedFromOldVersion =>
context.lang.verificationTypeMigratedFromOldVersion,
};
}
Future<String?> showNicknameChangeDialog(
BuildContext context,
Contact contact,

View file

@ -0,0 +1,184 @@
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:intl/intl.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/components/alert.dialog.dart';
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
class VerificationExpansionTileComp extends StatefulWidget {
const VerificationExpansionTileComp({
required this.contact,
super.key,
});
final Contact contact;
@override
State<VerificationExpansionTileComp> createState() =>
_VerificationExpansionTileCompState();
}
class _VerificationExpansionTileCompState
extends State<VerificationExpansionTileComp> {
List<KeyVerification> _keyVerifications = [];
List<(Contact, DateTime)> _transferredTrust = [];
late StreamSubscription<List<KeyVerification>> _streamKeyVerifications;
late StreamSubscription<List<(Contact, DateTime)>> _streamTransferredTrust;
@override
void initState() {
super.initState();
_streamKeyVerifications = twonlyDB.keyVerificationDao
.watchContactVerification(widget.contact.userId)
.listen((update) {
if (!mounted) return;
setState(() {
_keyVerifications = update;
});
});
_streamTransferredTrust = twonlyDB.keyVerificationDao
.watchTransferredTrustVerifications(widget.contact.userId)
.listen((update) {
if (!mounted) return;
setState(() {
_transferredTrust = update;
});
});
}
@override
void dispose() {
_streamKeyVerifications.cancel();
_streamTransferredTrust.cancel();
super.dispose();
}
String _verificationTypeLabel(BuildContext context, VerificationType type) {
return switch (type) {
VerificationType.qrScanned => context.lang.verificationTypeQrScanned,
VerificationType.secretQrToken =>
context.lang.verificationTypeSecretQrToken(
getContactDisplayName(widget.contact),
),
VerificationType.link => context.lang.verificationTypeLink,
VerificationType.contactSharedByVerified =>
context.lang.verificationTypeContactSharedByVerified,
VerificationType.migratedFromOldVersion =>
context.lang.verificationTypeMigratedFromOldVersion,
};
}
@override
Widget build(BuildContext context) {
if (_keyVerifications.isEmpty && _transferredTrust.isEmpty) {
return BetterListTile(
leading: VerificationBadgeComp(
contact: widget.contact,
size: 20,
),
text: context.lang.contactVerifyNumberTitle,
onTap: () async {
await context.push(Routes.settingsHelpFaqVerifyBadge);
if (mounted) setState(() {});
},
);
}
return ExpansionTile(
shape: const RoundedRectangleBorder(),
backgroundColor: context.color.surfaceContainer,
collapsedShape: const RoundedRectangleBorder(),
leading: Padding(
padding: const EdgeInsetsGeometry.only(left: 12, right: 12),
child: VerificationBadgeComp(
contact: widget.contact,
size: 20,
),
),
title: Text(context.lang.userVerifiedTitle),
children: [
..._keyVerifications.map(
(kv) => ListTile(
dense: true,
contentPadding: const EdgeInsets.only(left: 16),
title: Text(_verificationTypeLabel(context, kv.type)),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
DateFormat.yMd(
Localizations.localeOf(context).toString(),
).format(kv.createdAt),
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 13,
),
),
IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
iconSize: 8,
icon: Icon(
FontAwesomeIcons.trash,
size: 8,
color: context.color.onSurfaceVariant,
),
onPressed: () async {
final confirm = await showAlertDialog(
context,
context.lang.deleteVerificationTitle,
context.lang.deleteVerificationBody,
);
if (confirm) {
await twonlyDB.keyVerificationDao
.deleteKeyVerificationById(
kv.verificationId,
widget.contact.userId,
);
}
},
),
],
),
),
),
..._transferredTrust.map(
(tt) => ListTile(
dense: true,
title: Row(
children: [
Text(
context.lang.contactVerifiedBy(
getContactDisplayName(tt.$1),
),
),
VerificationBadgeComp(
contact: tt.$1,
),
],
),
trailing: Text(
DateFormat.yMd(
Localizations.localeOf(context).toString(),
).format(tt.$2),
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 13,
),
),
),
),
],
);
}
}

View file

@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/views/onboarding/setup/add_new_contacts.setup.dart';
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';
@ -15,7 +14,6 @@ import 'package:twonly/src/visual/views/settings/privacy/user_discovery/componen
enum SetupPages {
profile,
backup,
addNewContact,
verificationBadge,
shareYourFriends,
letYourFriendsFindYou,
@ -185,8 +183,6 @@ class _SetupViewState extends State<SetupView> {
return const ProfileSetupPage();
case SetupPages.backup:
return const BackupSetupPage();
case SetupPages.addNewContact:
return const AddNewContactsPage();
case SetupPages.verificationBadge:
return const VerificationBadgeSetupPage();
case SetupPages.shareYourFriends: