From 379e0e8f2ea4411b637517d0aff93237bf5a2d45 Mon Sep 17 00:00:00 2001 From: Bibash Shrestha Date: Mon, 16 Dec 2024 16:30:12 +0545 Subject: [PATCH] feat: Update recovery phrase verification at initialisation #3190 --- .../response_string/response_string.dart | 1 + .../response_string_extension.dart | 4 + .../shared/enum/status/mnemonic_status.dart | 15 +-- .../message_handler/global_message.dart | 3 + .../message_handler/response_message.dart | 6 + lib/app/shared/widget/phrase_word.dart | 8 +- lib/l10n/arb/app_en.arb | 3 +- lib/l10n/untranslated.json | 10 +- .../cubit/onboarding_verify_phrase_cubit.dart | 122 ++++++++++++------ .../onboarding_verify_phrase_cubit.g.dart | 3 +- .../cubit/onboarding_verify_phrase_state.dart | 12 +- .../view/onboarding_verify_phrase.dart | 12 +- .../view/onboarding_verify_phrase_test.dart | 6 - 13 files changed, 128 insertions(+), 77 deletions(-) diff --git a/lib/app/shared/enum/message/response_string/response_string.dart b/lib/app/shared/enum/message/response_string/response_string.dart index f2a313b1c..acae3068b 100644 --- a/lib/app/shared/enum/message/response_string/response_string.dart +++ b/lib/app/shared/enum/message/response_string/response_string.dart @@ -162,4 +162,5 @@ enum ResponseString { RESPONSE_STRING_invalidClientErrorDescription, RESPONSE_STRING_vpFormatsNotSupportedErrorDescription, RESPONSE_STRING_invalidPresentationDefinitionUriErrorDescription, + RESPONSE_STRING_recoveryPhraseIncorrectErrorMessage, } diff --git a/lib/app/shared/enum/message/response_string/response_string_extension.dart b/lib/app/shared/enum/message/response_string/response_string_extension.dart index e9dfb1865..b4c43abe2 100644 --- a/lib/app/shared/enum/message/response_string/response_string_extension.dart +++ b/lib/app/shared/enum/message/response_string/response_string_extension.dart @@ -518,6 +518,10 @@ extension ResponseStringX on ResponseString { .RESPONSE_STRING_invalidPresentationDefinitionUriErrorDescription: return globalMessage .RESPONSE_STRING_invalidPresentationDefinitionUriErrorDescription; + + case ResponseString.RESPONSE_STRING_recoveryPhraseIncorrectErrorMessage: + return globalMessage + .RESPONSE_STRING_recoveryPhraseIncorrectErrorMessage; } } } diff --git a/lib/app/shared/enum/status/mnemonic_status.dart b/lib/app/shared/enum/status/mnemonic_status.dart index 587b63ed3..3d3d2282f 100644 --- a/lib/app/shared/enum/status/mnemonic_status.dart +++ b/lib/app/shared/enum/status/mnemonic_status.dart @@ -1,29 +1,16 @@ - import 'package:flutter/material.dart'; enum MnemonicStatus { unselected, selected, - wrongSelection, } extension MnemonicStatusX on MnemonicStatus { - bool get showOrder { - switch (this) { - case MnemonicStatus.unselected: - case MnemonicStatus.wrongSelection: - return false; - case MnemonicStatus.selected: - return true; - } - } - Color color(BuildContext context) { switch (this) { case MnemonicStatus.unselected: return Theme.of(context).colorScheme.secondaryContainer; - case MnemonicStatus.wrongSelection: - return Theme.of(context).colorScheme.error; + case MnemonicStatus.selected: return Theme.of(context).colorScheme.primary; } diff --git a/lib/app/shared/message_handler/global_message.dart b/lib/app/shared/message_handler/global_message.dart index c058f0513..b9d6d1df2 100644 --- a/lib/app/shared/message_handler/global_message.dart +++ b/lib/app/shared/message_handler/global_message.dart @@ -398,4 +398,7 @@ class GlobalMessage { String get RESPONSE_STRING_invalidPresentationDefinitionUriErrorDescription => l10n.invalidPresentationDefinitionUriErrorDescription; + + String get RESPONSE_STRING_recoveryPhraseIncorrectErrorMessage => + l10n.recoveryPhraseIncorrectErrorMessage; } diff --git a/lib/app/shared/message_handler/response_message.dart b/lib/app/shared/message_handler/response_message.dart index 8a81007ee..73e911557 100644 --- a/lib/app/shared/message_handler/response_message.dart +++ b/lib/app/shared/message_handler/response_message.dart @@ -797,6 +797,12 @@ class ResponseMessage with MessageHandler { .localise( context, ); + + case ResponseString.RESPONSE_STRING_recoveryPhraseIncorrectErrorMessage: + return ResponseString + .RESPONSE_STRING_recoveryPhraseIncorrectErrorMessage.localise( + context, + ); } } return ''; diff --git a/lib/app/shared/widget/phrase_word.dart b/lib/app/shared/widget/phrase_word.dart index 4780b9d97..e809e4431 100644 --- a/lib/app/shared/widget/phrase_word.dart +++ b/lib/app/shared/widget/phrase_word.dart @@ -5,17 +5,15 @@ import 'package:flutter/material.dart'; class PhraseWord extends StatelessWidget { const PhraseWord({ super.key, - required this.order, + this.order, required this.word, this.color, - this.showOrder = true, this.onTap, }); - final int order; + final int? order; final String word; final Color? color; - final bool showOrder; final void Function()? onTap; @override @@ -40,7 +38,7 @@ class PhraseWord extends StatelessWidget { height: 25, child: Center( child: MyText( - showOrder ? '$order. $word' : word, + order == null ? word : '$order. $word', textAlign: TextAlign.center, ), ), diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 7c057b119..97b4c7fe8 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1172,5 +1172,6 @@ }, "reject": "Reject", "operation": "Operation", - "chooseYourSSIProfileOrCustomizeYourOwn": "Choose your wallet profile or customize your own" + "chooseYourSSIProfileOrCustomizeYourOwn": "Choose your wallet profile or customize your own", + "recoveryPhraseIncorrectErrorMessage": " Please try again with correct order." } diff --git a/lib/l10n/untranslated.json b/lib/l10n/untranslated.json index 508325a69..bfc550ae5 100644 --- a/lib/l10n/untranslated.json +++ b/lib/l10n/untranslated.json @@ -6,7 +6,8 @@ "ebsiV4DecentralizedId", "reject", "operation", - "chooseYourSSIProfileOrCustomizeYourOwn" + "chooseYourSSIProfileOrCustomizeYourOwn", + "recoveryPhraseIncorrectErrorMessage" ], "es": [ @@ -16,6 +17,11 @@ "ebsiV4DecentralizedId", "reject", "operation", - "chooseYourSSIProfileOrCustomizeYourOwn" + "chooseYourSSIProfileOrCustomizeYourOwn", + "recoveryPhraseIncorrectErrorMessage" + ], + + "fr": [ + "recoveryPhraseIncorrectErrorMessage" ] } diff --git a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart index b36809c07..0cb85e3a6 100644 --- a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart +++ b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart @@ -66,55 +66,65 @@ class OnBoardingVerifyPhraseCubit extends Cubit { emit(state.copyWith(mnemonicStates: oldState, status: AppStatus.idle)); } - List tempMnemonics = []; + List selectionorder = []; Future verify({ required List mnemonic, required int index, }) async { final mnemonicState = state.mnemonicStates[index]; - final int clickCount = tempMnemonics.length; - - if (mnemonicState.order <= clickCount) return; - - if (mnemonicState.mnemonicStatus != MnemonicStatus.wrongSelection) { - for (final mnemonicState in state.mnemonicStates) { - if (mnemonicState.mnemonicStatus == MnemonicStatus.wrongSelection) { - return; - } - } - } final updatedList = List.from(state.mnemonicStates); - if (mnemonicState.order == clickCount + 1) { - tempMnemonics.add(mnemonic[mnemonicState.order - 1]); - updatedList[index] = - mnemonicState.copyWith(mnemonicStatus: MnemonicStatus.selected); - } else { - if (mnemonicState.mnemonicStatus == MnemonicStatus.unselected) { + + switch (mnemonicState.mnemonicStatus) { + case MnemonicStatus.unselected: + // new selection updatedList[index] = mnemonicState.copyWith( - mnemonicStatus: MnemonicStatus.wrongSelection, + mnemonicStatus: MnemonicStatus.selected, + userSelectedOrder: selectionorder.length + 1, + ); + selectionorder.add(mnemonicState.order); + case MnemonicStatus.selected: + // remove selection + final order = mnemonicState.order; + updatedList[index] = mnemonicState.copyWith( + mnemonicStatus: MnemonicStatus.unselected, + userSelectedOrder: null, ); - } else { - updatedList[index] = - mnemonicState.copyWith(mnemonicStatus: MnemonicStatus.unselected); - } - } - emit(state.copyWith(status: AppStatus.idle, mnemonicStates: updatedList)); - - if (tempMnemonics.length >= 6) { - if (mnemonic[0] == tempMnemonics[0] && - mnemonic[1] == tempMnemonics[1] && - mnemonic[2] == tempMnemonics[2] && - mnemonic[3] == tempMnemonics[3] && - mnemonic[4] == tempMnemonics[4] && - mnemonic[5] == tempMnemonics[5]) { - emit(state.copyWith(isVerified: true, status: AppStatus.idle)); - } else { - emit(state.copyWith(isVerified: false, status: AppStatus.idle)); - } + if (selectionorder.last != order) { + // if first or middle elements are removed then we need to reorder + // the selection + + selectionorder.remove(order); + + var count = 0; + + for (int i = 0; i < updatedList.length; i++) { + if (index == i) continue; // already operated in this index + + final element = updatedList[i]; + if (selectionorder.contains(element.order)) { + // reorder remaining list + updatedList[i] = updatedList[i].copyWith( + mnemonicStatus: MnemonicStatus.selected, + userSelectedOrder: count + 1, + ); + count++; + } + } + } else { + selectionorder.remove(order); + } } + + emit( + state.copyWith( + status: AppStatus.idle, + mnemonicStates: updatedList, + isVerified: selectionorder.length == 12, + ), + ); } Future generateSSIAndCryptoAccount({ @@ -122,7 +132,45 @@ class OnBoardingVerifyPhraseCubit extends Cubit { required bool isFromOnboarding, }) async { emit(state.loading()); + try { + if (selectionorder.length == 12) { + var verified = true; + final updatedList = List.from(state.mnemonicStates); + for (var i = 0; i < 12; i++) { + if (updatedList[i].order == updatedList[i].userSelectedOrder) { + verified = true; + } else { + verified = false; + break; + } + } + + if (!verified) { + selectionorder = []; + for (var i = 0; i < 12; i++) { + updatedList[i] = updatedList[i].copyWith( + mnemonicStatus: MnemonicStatus.unselected, + userSelectedOrder: null, + ); + } + + emit( + state.copyWith( + status: AppStatus.error, + mnemonicStates: updatedList, + message: StateMessage.error( + showDialog: true, + messageHandler: ResponseMessage( + message: ResponseString + .RESPONSE_STRING_recoveryPhraseIncorrectErrorMessage, + ), + ), + ), + ); + return; + } + } if (isFromOnboarding) { await generateAccount( mnemonic: mnemonic, diff --git a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.g.dart b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.g.dart index 5abbb5f9d..57fb9ccda 100644 --- a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.g.dart +++ b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.g.dart @@ -54,16 +54,17 @@ MnemonicState _$MnemonicStateFromJson(Map json) => _$MnemonicStatusEnumMap, json['mnemonicStatus']) ?? MnemonicStatus.unselected, order: (json['order'] as num).toInt(), + userSelectedOrder: (json['userSelectedOrder'] as num?)?.toInt(), ); Map _$MnemonicStateToJson(MnemonicState instance) => { 'mnemonicStatus': _$MnemonicStatusEnumMap[instance.mnemonicStatus]!, 'order': instance.order, + 'userSelectedOrder': instance.userSelectedOrder, }; const _$MnemonicStatusEnumMap = { MnemonicStatus.unselected: 'unselected', MnemonicStatus.selected: 'selected', - MnemonicStatus.wrongSelection: 'wrongSelection', }; diff --git a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_state.dart b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_state.dart index 9b8a77e07..89a281623 100644 --- a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_state.dart +++ b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_state.dart @@ -58,7 +58,7 @@ class OnBoardingVerifyPhraseState extends Equatable { }) { return OnBoardingVerifyPhraseState( status: status, - message: message ?? this.message, + message: message, isVerified: isVerified ?? this.isVerified, mnemonicStates: mnemonicStates ?? this.mnemonicStates, ); @@ -75,6 +75,7 @@ class MnemonicState extends Equatable { const MnemonicState({ this.mnemonicStatus = MnemonicStatus.unselected, required this.order, + this.userSelectedOrder, }); factory MnemonicState.fromJson(Map json) => @@ -82,18 +83,25 @@ class MnemonicState extends Equatable { final MnemonicStatus mnemonicStatus; final int order; + final int? userSelectedOrder; MnemonicState copyWith({ required MnemonicStatus mnemonicStatus, + required int? userSelectedOrder, }) { return MnemonicState( order: order, mnemonicStatus: mnemonicStatus, + userSelectedOrder: userSelectedOrder, ); } Map toJson() => _$MnemonicStateToJson(this); @override - List get props => [mnemonicStatus, order]; + List get props => [ + mnemonicStatus, + order, + userSelectedOrder, + ]; } diff --git a/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart b/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart index 7a9cc841b..6ef9de254 100644 --- a/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart +++ b/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart @@ -189,11 +189,9 @@ class _OnBoardingVerifyPhraseViewState Expanded( child: PhraseWord( key: Key(col1Mnemonics.order.toString()), - order: col1Mnemonics.order, + order: col1Mnemonics.userSelectedOrder, word: widget.mnemonic[col1Mnemonics.order - 1], - showOrder: - col1Mnemonics.mnemonicStatus.showOrder, color: col1Mnemonics.mnemonicStatus .color(context), onTap: () { @@ -208,11 +206,9 @@ class _OnBoardingVerifyPhraseViewState Expanded( child: PhraseWord( key: Key(col2Mnemonics.order.toString()), - order: col2Mnemonics.order, + order: col2Mnemonics.userSelectedOrder, word: widget.mnemonic[col2Mnemonics.order - 1], - showOrder: - col2Mnemonics.mnemonicStatus.showOrder, color: col2Mnemonics.mnemonicStatus .color(context), onTap: () { @@ -227,11 +223,9 @@ class _OnBoardingVerifyPhraseViewState Expanded( child: PhraseWord( key: Key(col3Mnemonics.order.toString()), - order: col3Mnemonics.order, + order: col3Mnemonics.userSelectedOrder, word: widget.mnemonic[col3Mnemonics.order - 1], - showOrder: - col3Mnemonics.mnemonicStatus.showOrder, color: col3Mnemonics.mnemonicStatus .color(context), onTap: () { diff --git a/test/onboarding/verify_phrase/view/onboarding_verify_phrase_test.dart b/test/onboarding/verify_phrase/view/onboarding_verify_phrase_test.dart index b1709c6cb..1ec085068 100644 --- a/test/onboarding/verify_phrase/view/onboarding_verify_phrase_test.dart +++ b/test/onboarding/verify_phrase/view/onboarding_verify_phrase_test.dart @@ -583,12 +583,6 @@ void main() { await tester.tap(find.byKey(Key(mnemonicState.order.toString()))); - await tester.pumpAndSettle(); - expect( - onBoardingVerifyPhraseCubit.state.mnemonicStates[index].mnemonicStatus, - MnemonicStatus.wrongSelection, - ); - await tester.tap(find.byKey(Key(mnemonicState.order.toString()))); await tester.pumpAndSettle();