import 'dart:math'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_base.dart'; part 'wallet_seed_view_model.g.dart'; class WalletSeedViewModel = WalletSeedViewModelBase with _$WalletSeedViewModel; abstract class WalletSeedViewModelBase with Store { WalletSeedViewModelBase(WalletBase wallet) : name = wallet.name, seed = wallet.seed!, currentOptions = ObservableList(), verificationIndices = ObservableList() { setupSeedVerification(); } @observable String name; @observable String seed; /// The Regex split the words based on any whitespace character. /// /// Either standard ASCII space (U+0020) or the full-width space character (U+3000) used by the Japanese. List get seedSplit => seed.split(RegExp(r'\s+')); int get columnCount => seedSplit.length <= 16 ? 2 : 3; double get columnAspectRatio => seedSplit.length <= 16 ? 1.8 : 2.8; /// The indices of the seed to be verified. ObservableList verificationIndices; /// The index of the word in verificationIndices being verified. @observable int currentStepIndex = 0; /// The options to be displayed on the page for the current seed step. /// /// The user has to choose from these. ObservableList currentOptions; /// The number of words to be verified, linked to a Feature Flag so we can easily modify it. int get verificationWordCount => FeatureFlag.verificationWordsCount; /// Then number of wrong entries the user has selected; /// /// Routes the view to the seed screen if it's up to two. @observable int wrongEntries = 0; int get currentWordIndex => verificationIndices[currentStepIndex]; String get currentCorrectWord => seedSplit[currentWordIndex]; @observable bool isVerificationComplete = false; void setupSeedVerification() { generateRandomIndices(); generateOptions(); } /// Generate the indices of the seeds to be verified. /// /// Structured to be as random as possible. @action void generateRandomIndices() { verificationIndices.clear(); final random = Random(); final indices = []; while (indices.length < verificationWordCount) { final i = random.nextInt(seedSplit.length); if (!indices.contains(i)) { indices.add(i); } } verificationIndices.addAll(indices); } /// Generates the options for each index being verified. @action void generateOptions() { currentOptions.clear(); final correctWord = currentCorrectWord; final incorrectWords = seedSplit.where((word) => word != correctWord).toList(); incorrectWords.shuffle(); final options = [correctWord, ...incorrectWords.take(5)]; options.shuffle(); currentOptions.addAll(options); } bool isChosenWordCorrect(String chosenWord) { if (chosenWord == currentCorrectWord) { wrongEntries = 0; if (currentStepIndex + 1 < verificationWordCount) { currentStepIndex++; generateOptions(); } else { // All verification steps completed isVerificationComplete = true; } return true; } else { wrongEntries++; return false; } } }