Repository: status-im/status-mobile
Pull request #22252: Improve Error Handling Logic in Enter Seed Phrase Screen
Author: login: Parveshdhull
Labels: name: Tested - OK
name: request-manual-qa
Created: 2025-03-06T12:02:28Z | Merged: 2025-03-18T10:51:45Z

fixes https://github.com/status-im/status-mobile/issues/22013
fixes issue 6 in https://github.com/status-im/status-mobile/issues/21970

Originally reported here - https://github.com/status-im/status-mobile/pull/21947#pullrequestreview-2563056380

### Video:

https://github.com/user-attachments/assets/e6bc8a3a-aa5d-4e8a-b3d5-ac903364736e

### Affected Area:

All screens that take seed phrase as input
- On-boarding account creation without keycard
- On-boarding account creation with keycard
- Keycard migration
- Keycard backup flow
- Wallet create account


status: ready


Diff:
diff --git a/src/quo/components/buttons/predictive_keyboard/view.cljs b/src/quo/components/buttons/predictive_keyboard/view.cljs
index 7a89064e00e..284ed0dc11a 100644
--- a/src/quo/components/buttons/predictive_keyboard/view.cljs
+++ b/src/quo/components/buttons/predictive_keyboard/view.cljs
@@ -34,10 +34,10 @@
    - `words` List of words to display in the keyboard.
    - `on-press` Callback called when a word is pressed `(fn [word])`
    - `theme` :light or :dark, received from with-theme HOC."
-  [{:keys [type blur? text words on-press]}]
+  [{:keys [type blur? text words on-press container-style] :or {container-style {}}}]
   (let [theme (quo.theme/use-theme)]
     [linear-gradient/linear-gradient
-     {:style               {:flex-direction :row}
+     {:style               (assoc container-style :flex-direction :row)
       :accessibility-label :predictive-keyboard
       :colors              (if blur?
                              (gradients :blur)
diff --git a/src/status_im/common/enter_seed_phrase/events.cljs b/src/status_im/common/enter_seed_phrase/events.cljs
new file mode 100644
index 00000000000..c5974487323
--- /dev/null
+++ b/src/status_im/common/enter_seed_phrase/events.cljs
@@ -0,0 +1,11 @@
+(ns status-im.common.enter-seed-phrase.events
+  (:require
+    [utils.re-frame :as rf]))
+
+(rf/reg-event-fx :enter-seed-phrase/set-error
+ (fn [{:keys [db]} [error]]
+   {:db (assoc db :enter-seed-phrase/error error)}))
+
+(rf/reg-event-fx :enter-seed-phrase/clear-error
+ (fn [{:keys [db]}]
+   {:db (dissoc db :enter-seed-phrase/error)}))
diff --git a/src/status_im/common/enter_seed_phrase/view.cljs b/src/status_im/common/enter_seed_phrase/view.cljs
index 0de0ed64d2c..b968df5c401 100644
--- a/src/status_im/common/enter_seed_phrase/view.cljs
+++ b/src/status_im/common/enter_seed_phrase/view.cljs
@@ -110,30 +110,20 @@
                      focus-input               #(some-> @input-ref
                                                         (oops/ocall "focus"))
                      set-incorrect-seed-phrase #(reset! incorrect-seed-phrase? true)
-                     set-invalid-seed-phrase   #(reset! invalid-seed-phrase? true)
+                     on-error                  #(reset! invalid-seed-phrase? true)
                      seed-phrase               (reagent/atom "")
-                     on-change-seed-phrase     (fn [new-phrase]
-                                                 (when @invalid-seed-phrase?
-                                                   (reset! invalid-seed-phrase? false))
-                                                 (when @incorrect-seed-phrase?
-                                                   (reset! incorrect-seed-phrase? false))
-                                                 (reset! seed-phrase new-phrase))
                      on-submit                 (fn []
                                                  (swap! seed-phrase clean-seed-phrase)
                                                  (rf/dispatch
                                                   [:profile.recover/validate-recovery-phrase
                                                    (security/mask-data @seed-phrase)
                                                    {:on-success (fn [mnemonic key-uid]
-                                                                  (if on-success
+                                                                  (when on-success
                                                                     (on-success
-                                                                     {:key-uid key-uid
-                                                                      :phrase mnemonic
-                                                                      :on-error
-                                                                      set-invalid-seed-phrase})
-                                                                    (rf/dispatch
-                                                                     [:onboarding/seed-phrase-validated
-                                                                      mnemonic key-uid])))
-                                                    :on-error   set-invalid-seed-phrase}]))]
+                                                                     {:key-uid     key-uid
+                                                                      :seed-phrase mnemonic
+                                                                      :on-error    on-error})))
+                                                    :on-error   on-error}]))]
     (let [words-coll               (mnemonic/passphrase->words @seed-phrase)
           last-word                (peek words-coll)
           pick-suggested-word      (fn [pressed-word]
@@ -149,8 +139,18 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          global-error             (rf/sub [:enter-seed-phrase/error])
+          on-change-seed-phrase    (fn [new-phrase]
+                                     (when global-error
+                                       (rf/dispatch [:enter-seed-phrase/clear-error]))
+                                     (when @invalid-seed-phrase?
+                                       (reset! invalid-seed-phrase? false))
+                                     (when @incorrect-seed-phrase?
+                                       (reset! incorrect-seed-phrase? false))
+                                     (reset! seed-phrase new-phrase))
           suggestions-state        (cond
-                                     (or error-in-words?
+                                     (or global-error
+                                         error-in-words?
                                          words-exceeded?
                                          @invalid-seed-phrase?
                                          @incorrect-seed-phrase?)         :error
@@ -158,16 +158,26 @@
                                      (string/ends-with? @seed-phrase " ") :empty
                                      :else                                :words)
           suggestions-text         (cond
-                                     upper-case?             (i18n/label :t/seed-phrase-words-uppercase)
-                                     words-exceeded?         (i18n/label :t/seed-phrase-words-exceeded)
+                                     global-error            global-error
+                                     upper-case?             (i18n/label
+                                                              :t/seed-phrase-words-uppercase)
+                                     words-exceeded?         (i18n/label
+                                                              :t/seed-phrase-words-exceeded)
                                      error-in-words?         (i18n/label :t/seed-phrase-error)
                                      @invalid-seed-phrase?   (i18n/label :t/seed-phrase-invalid)
                                      @incorrect-seed-phrase? (i18n/label :t/seed-phrase-incorrect)
                                      :else                   (i18n/label :t/seed-phrase-info))
           error-state?             (= suggestions-state :error)
-          button-disabled?         (or error-state?
+          button-disabled?         (or (and (not global-error) error-state?)
                                        (not (constants/seed-phrase-valid-length word-count))
-                                       (not all-words-valid?))]
+                                       (not all-words-valid?))
+          button-label             (if global-error
+                                     (i18n/label :t/enter-another-recovery-phrase)
+                                     (i18n/label :t/continue))
+          reset-seed-phrase        (fn []
+                                     (reset! seed-phrase "")
+                                     (on-change-seed-phrase ""))
+          button-on-press          (if global-error reset-seed-phrase on-submit)]
       [rn/view
        {:style (style/recovery-phrase-container {:insets          initial-insets
                                                  :banner-offset   banner-offset
@@ -191,16 +201,17 @@
            {:container-style style/continue-button
             :type            :primary
             :disabled?       button-disabled?
-            :on-press        on-submit}
-           (i18n/label :t/continue)])]
+            :on-press        button-on-press}
+           button-label])]
        (when (or @keyboard-shown? error-state?)
          [rn/view {:style style/keyboard-container}
           [quo/predictive-keyboard
-           {:type     suggestions-state
-            :blur?    (not on-success)
-            :text     suggestions-text
-            :words    (keyboard-suggestions last-word)
-            :on-press pick-suggested-word}]])])
+           {:type            suggestions-state
+            :blur?           (not on-success)
+            :text            suggestions-text
+            :container-style {:padding-right 20}
+            :words           (keyboard-suggestions last-word)
+            :on-press        pick-suggested-word}]])])
     (finally
      (.remove keyboard-show-listener)
      (.remove keyboard-hide-listener))))
@@ -227,10 +238,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success onboarding-flow?]} (rf/sub [:get-screen-params])]
     (rn/use-unmount
-     (when-not on-success
-       #(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen])))
+     (fn []
+       (rf/dispatch [:enter-seed-phrase/clear-error])
+       (when onboarding-flow?
+         (rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen]))))
     [screen
      {:title      (i18n/label :t/use-recovery-phrase)
       :on-success on-success}]))
diff --git a/src/status_im/contexts/keycard/backup/events.cljs b/src/status_im/contexts/keycard/backup/events.cljs
index d2d2c53d201..9dd01c30b7c 100644
--- a/src/status_im/contexts/keycard/backup/events.cljs
+++ b/src/status_im/contexts/keycard/backup/events.cljs
@@ -1,5 +1,6 @@
 (ns status-im.contexts.keycard.backup.events
-  (:require [utils.re-frame :as rf]
+  (:require [utils.i18n :as i18n]
+            [utils.re-frame :as rf]
             [utils.security.core :as security]))
 
 (rf/reg-event-fx :keycard/backup.generate-and-load-key
@@ -24,12 +25,14 @@
             :on-success   #(rf/dispatch [:keycard/backup.generate-and-load-key])}]]]}))
 
 (rf/reg-event-fx :keycard/backup.phrase-entered
- (fn [{:keys [db]} [{:keys [phrase]}]]
-   {:db (assoc-in db [:keycard :backup :masked-phrase] phrase)
-    :fx [[:dispatch [:navigate-back]]
-         [:dispatch
-          [:open-modal :screen/keycard.backup.ready-to-add
-           {:on-press #(rf/dispatch [:keycard/backup.ready-to-add-connect])}]]]}))
+ (fn [{:keys [db]} [{:keys [key-uid seed-phrase]}]]
+   (if (not= key-uid (get-in db [:profile/profile :key-uid]))
+     {:fx [[:dispatch [:enter-seed-phrase/set-error (i18n/label :t/seed-phrase-dont-match)]]]}
+     {:db (assoc-in db [:keycard :backup :masked-phrase] seed-phrase)
+      :fx [[:dispatch [:navigate-back]]
+           [:dispatch
+            [:open-modal :screen/keycard.backup.ready-to-add
+             {:on-press #(rf/dispatch [:keycard/backup.ready-to-add-connect])}]]]})))
 
 (rf/reg-event-fx :keycard/backup.save-instance-uid-and-pin
  (fn [{:keys [db]} [masked-pin]]
diff --git a/src/status_im/contexts/keycard/create/events.cljs b/src/status_im/contexts/keycard/create/events.cljs
index 13198b886ba..ffa58f8fb33 100644
--- a/src/status_im/contexts/keycard/create/events.cljs
+++ b/src/status_im/contexts/keycard/create/events.cljs
@@ -1,6 +1,7 @@
 (ns status-im.contexts.keycard.create.events
   (:require [clojure.string :as string]
             [legacy.status-im.ethereum.mnemonic :as mnemonic]
+            [utils.i18n :as i18n]
             [utils.re-frame :as rf]
             [utils.security.core :as security]))
 
@@ -38,12 +39,14 @@
               #(rf/dispatch [:keycard/create.generate-and-load-keys %])}]]]})))
 
 (rf/reg-event-fx :keycard/create.seed-phrase-entered
- (fn [{:keys [db]} [masked-seed-phrase]]
-   {:db (assoc-in db [:keycard :create :masked-phrase] masked-seed-phrase)
-    :fx [[:dispatch [:navigate-back]]
-         [:dispatch
-          [:open-modal :screen/keycard.create.ready-to-add
-           {:on-continue #(rf/dispatch [:keycard/create.connect-and-load-keys])}]]]}))
+ (fn [{:keys [db]} [{:keys [key-uid seed-phrase]}]]
+   (if (contains? (:profile/profiles-overview db) key-uid)
+     {:fx [[:dispatch [:enter-seed-phrase/set-error (i18n/label :t/account-already-exist-error)]]]}
+     {:db (assoc-in db [:keycard :create :masked-phrase] seed-phrase)
+      :fx [[:dispatch [:navigate-back]]
+           [:dispatch
+            [:open-modal :screen/keycard.create.ready-to-add
+             {:on-continue #(rf/dispatch [:keycard/create.connect-and-load-keys])}]]]})))
 
 (rf/reg-event-fx :keycard/create.phrase-backed-up
  (fn [{:keys [db]} [masked-phrase-vector]]
@@ -113,8 +116,7 @@
       {:on-continue #(rf/dispatch [:keycard/create.connect-and-generate-phrase])}])
     (rf/dispatch
      [:open-modal :screen/use-recovery-phrase-dark
-      {:on-success (fn [{:keys [phrase]}]
-                     (rf/dispatch [:keycard/create.seed-phrase-entered phrase]))}])))
+      {:on-success #(rf/dispatch [:keycard/create.seed-phrase-entered %])}])))
 
 (rf/reg-event-fx :keycard/create.open-empty
  (fn []
diff --git a/src/status_im/contexts/keycard/migrate/events.cljs b/src/status_im/contexts/keycard/migrate/events.cljs
index 16e49cfd1c5..41561f964fd 100644
--- a/src/status_im/contexts/keycard/migrate/events.cljs
+++ b/src/status_im/contexts/keycard/migrate/events.cljs
@@ -1,5 +1,6 @@
 (ns status-im.contexts.keycard.migrate.events
   (:require [clojure.string :as string]
+            [utils.i18n :as i18n]
             [utils.re-frame :as rf]
             [utils.security.core :as security]))
 
@@ -87,12 +88,15 @@
                  #(rf/dispatch [:keycard/migration.phrase-backed-up])}]))
 
 (rf/reg-event-fx :keycard/migration.phrase-entered
- (fn [{:keys [db]} [{:keys [phrase]}]]
-   {:db (assoc-in db [:keycard :migration :masked-phrase] phrase)
-    :fx [[:dispatch [:navigate-back]]
-         [:dispatch
-          [:open-modal :screen/keycard.authorise
-           {:on-success #(rf/dispatch [:keycard/migration.authorisation-success %])}]]]}))
+ (fn [{:keys [db]} [{:keys [seed-phrase key-uid]}]]
+   (if (not= key-uid (get-in db [:profile/profile :key-uid]))
+     {:fx [[:dispatch
+            [:enter-seed-phrase/set-error (i18n/label :t/seed-phrase-dont-match-migrate-flow)]]]}
+     {:db (assoc-in db [:keycard :migration :masked-phrase] seed-phrase)
+      :fx [[:dispatch [:navigate-back]]
+           [:dispatch
+            [:open-modal :screen/keycard.authorise
+             {:on-success #(rf/dispatch [:keycard/migration.authorisation-success %])}]]]})))
 
 (rf/reg-event-fx :keycard/migration.get-phrase
  (fn [{:keys [db]}]
diff --git a/src/status_im/contexts/keycard/unblock/events.cljs b/src/status_im/contexts/keycard/unblock/events.cljs
index 248649ac114..04203d12a1e 100644
--- a/src/status_im/contexts/keycard/unblock/events.cljs
+++ b/src/status_im/contexts/keycard/unblock/events.cljs
@@ -24,8 +24,8 @@
          [:dispatch [:open-modal :screen/keycard.unblock.ready-to-unblock]]]}))
 
 (rf/reg-event-fx :keycard/unblock.phrase-entered
- (fn [{:keys [db]} [{:keys [phrase]}]]
-   {:db (assoc-in db [:keycard :unblock :masked-phrase] phrase)
+ (fn [{:keys [db]} [{:keys [seed-phrase]}]]
+   {:db (assoc-in db [:keycard :unblock :masked-phrase] seed-phrase)
     :fx [[:dispatch [:navigate-back]]
          [:dispatch
           [:open-modal :screen/keycard.pin.create
diff --git a/src/status_im/contexts/onboarding/events.cljs b/src/status_im/contexts/onboarding/events.cljs
index 89419dade4d..b6b25739267 100644
--- a/src/status_im/contexts/onboarding/events.cljs
+++ b/src/status_im/contexts/onboarding/events.cljs
@@ -1,7 +1,6 @@
 (ns status-im.contexts.onboarding.events
   (:require
     [quo.foundations.colors :as colors]
-    [re-frame.core :as re-frame]
     status-im.common.biometric.events
     [status-im.constants :as constants]
     [status-im.contexts.shell.constants :as shell.constants]
@@ -31,7 +30,10 @@
 (rf/reg-event-fx :onboarding/navigate-to-sign-in-by-seed-phrase
  (fn [{:keys [db]} [from-screen]]
    {:db (assoc db :onboarding/navigated-to-enter-seed-phrase-from-screen from-screen)
-    :fx [[:dispatch [:navigate-to-within-stack [:screen/onboarding.enter-seed-phrase from-screen]]]]}))
+    :fx [[:dispatch
+          [:navigate-to-within-stack [:screen/onboarding.enter-seed-phrase from-screen]
+           {:on-success       #(rf/dispatch [:onboarding/seed-phrase-validated %])
+            :onboarding-flow? true}]]]}))
 
 (rf/reg-event-fx
  :onboarding/clear-navigated-to-enter-seed-phrase-from-screen
@@ -107,28 +109,15 @@
               [:navigate-to-within-stack [:screen/onboarding.enable-biometrics from-screen]]
               [:onboarding/create-account-and-login])]]})))
 
-(rf/reg-event-fx
- :onboarding/multiaccount-already-exists
- (fn [_ [key-uid]]
-   {:fx [[:effects.utils/show-confirmation
-          {:title               (i18n/label :t/multiaccount-exists-title)
-           :content             (i18n/label :t/multiaccount-exists-content)
-           :confirm-button-text (i18n/label :t/unlock)
-           :on-accept           (fn []
-                                  (re-frame/dispatch [:pop-to-root :screen/profile.profiles])
-                                  ;; Note - Profile selection is not working
-                                  (re-frame/dispatch [:profile/profile-selected key-uid]))
-           :on-cancel           #(re-frame/dispatch [:pop-to-root :screen/profile.profiles])}]]}))
-
 (rf/reg-event-fx
  :onboarding/seed-phrase-validated
- (fn [{:keys [db]} [seed-phrase key-uid]]
+ (fn [{:keys [db]} [{:keys [seed-phrase key-uid]}]]
    (let [next-screen :screen/onboarding.create-profile-password
          from-screen (get db
                           :onboarding/navigated-to-enter-seed-phrase-from-screen
                           :screen/onboarding.create-profile)]
      (if (contains? (:profile/profiles-overview db) key-uid)
-       {:fx [[:dispatch [:onboarding/multiaccount-already-exists key-uid]]]}
+       {:fx [[:dispatch [:enter-seed-phrase/set-error (i18n/label :t/account-already-exist-error)]]]}
        {:db (-> db
                 (assoc-in [:onboarding/profile :seed-phrase] seed-phrase)
                 (assoc-in [:onboarding/profile :key-uid] key-uid)
diff --git a/src/status_im/contexts/wallet/add_account/create_account/events.cljs b/src/status_im/contexts/wallet/add_account/create_account/events.cljs
index 52dce3c6b44..c56653b181a 100644
--- a/src/status_im/contexts/wallet/add_account/create_account/events.cljs
+++ b/src/status_im/contexts/wallet/add_account/create_account/events.cljs
@@ -56,7 +56,7 @@
 (rf/reg-event-fx :wallet/store-new-seed-phrase store-new-seed-phrase)
 
 (defn seed-phrase-validated
-  [{:keys [db]} [seed-phrase key-uid on-error]]
+  [{:keys [db]} [{:keys [seed-phrase key-uid on-error]}]]
   (let [keypair-already-added? (-> db
                                    :wallet
                                    :keypairs
diff --git a/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs
index aee7ee986a3..c65231e6f76 100644
--- a/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs
+++ b/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs
@@ -35,9 +35,8 @@
       :add-divider?        true
       :on-press            (fn []
                              (rf/dispatch [:navigate-to :screen/use-recovery-phrase
-                                           {:on-success (fn [{:keys [key-uid phrase on-error]}]
-                                                          (rf/dispatch [:wallet/seed-phrase-validated
-                                                                        phrase key-uid on-error]))}]))}
+                                           {:on-success #(rf/dispatch [:wallet/seed-phrase-validated
+                                                                       %])}]))}
      (when (ff/enabled? ::ff/wallet.import-private-key)
        {:icon                :i/key
         :accessibility-label :import-private-key
diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs
index 332bb1e77da..4c1949b7773 100644
--- a/src/status_im/events.cljs
+++ b/src/status_im/events.cljs
@@ -5,6 +5,7 @@
     status-im.common.app-monitoring.events
     status-im.common.async-storage.effects
     status-im.common.emoji-picker.events
+    status-im.common.enter-seed-phrase.events
     status-im.common.font.events
     status-im.common.image-crop-picker.events
     [status-im.common.json-rpc.events]
diff --git a/src/status_im/subs/root.cljs b/src/status_im/subs/root.cljs
index 12704e1a40d..e64933ac38c 100644
--- a/src/status_im/subs/root.cljs
+++ b/src/status_im/subs/root.cljs
@@ -77,6 +77,7 @@
 (reg-root-key-sub :alert-banners :alert-banners)
 (reg-root-key-sub :alert-banners/hide? :alert-banners/hide?)
 (reg-root-key-sub :currencies :currencies)
+(reg-root-key-sub :enter-seed-phrase/error :enter-seed-phrase/error)
 
 ;;onboarding
 (reg-root-key-sub :onboarding/generated-keys? :onboarding/generated-keys?)
diff --git a/translations/en.json b/translations/en.json
index db83070394f..fbf1b161be9 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -24,6 +24,7 @@
   "access-key": "Access key",
   "account": "Account",
   "account-added": "Account added",
+  "account-already-exist-error": "The recovery phrase you entered is linked to a key pair already added to this device.",
   "account-color": "Account color",
   "account-content": "You can compare accounts in Status to bank accounts. Like a bank account, an account typically has an address and a balance; You use this account to transact on Ethereum. You can have multiple accounts in your wallet. All accessed by unlocking Status.",
   "account-created": "{{name}} created",
@@ -999,6 +1000,7 @@
   "enter-a-private-key": "Enter a private key",
   "enter-a-seed-phrase": "Enter a seed phrase",
   "enter-address": "Enter address",
+  "enter-another-recovery-phrase": "Enter another recovery phrase",
   "enter-channel": "Enter channel",
   "enter-chat-key": "Enter chat key or scan a QR",
   "enter-contact-code": "ENS (vitalik94) or chat key (0x04…)",
@@ -2333,6 +2335,8 @@
   "seed-key-uid-mismatch-desc-1": "The seed phrase you entered does not match {{multiaccount-name}}",
   "seed-key-uid-mismatch-desc-2": "To manage keys for this account verify your seed phrase and try again.",
   "seed-phrase-content": "A set of friendly-to-read words, randomly selected from the BIP39 standard list and used to recover or access your Ethereum account on other wallets and devices. Also referred to as a “mnemonic phrase,” “recovery phrase” or “wallet backup” across the crypto ecosystem. Most crypto apps use this same standard to generate accounts.",
+  "seed-phrase-dont-match": "The phrase you’ve entered not matching key pair that you want to back up",
+  "seed-phrase-dont-match-migrate-flow": "The phrase you’ve entered does not match your Status profile key pair",
   "seed-phrase-error": "Recovery phrase contains invalid words",
   "seed-phrase-incorrect": "Recovery phrase does not match key pair",
   "seed-phrase-info": "Enter 12, 18 or 24 words separated by spaces",


Reviews:
user: login: flexsurfer
body: thank you , left a few comments
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T09:37:05Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:03:27Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:03:30Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:03:32Z
user: login: flexsurfer
body: thank you, good job
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:07:50Z
user: login: ilmotta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T03:40:28Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T05:44:40Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T05:49:36Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T05:53:21Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T05:55:37Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T05:58:17Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T08:49:00Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T10:20:33Z
user: login: ulisesmac
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T16:17:53Z
user: login: ilmotta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T17:15:05Z
user: login: ilmotta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T17:16:02Z
user: login: ilmotta
body: Thank you for engaging in my review feedback @Parveshdhull. Code looks good 💯 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T17:18:29Z
user: login: Parveshdhull
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T04:06:58Z
user: login: ilmotta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T10:32:42Z

Review comments:
diff_hunk: @@ -229,8 +232,9 @@
   []
   (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
     (rn/use-unmount
-     (when-not on-success
-       #(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen])))
+     (fn []
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: flexsurfer
body: could please elaborate on why `(when-not on-success` was removed
created_at: 2025-03-10T09:31:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,7 +132,9 @@
                {:on-complete init-card-with-pin-and-continue}]])]})))
 
 (rf/reg-event-fx :keycard/backup.phrase-entered
- (fn [{:keys [db]} [{:keys [phrase]}]]
-   {:db (assoc-in db [:keycard :backup :masked-phrase] phrase)
-    :fx [[:dispatch [:navigate-back]]
-         [:dispatch [:open-modal :screen/keycard.backup.ready-to-add]]]}))
+ (fn [{:keys [db]} [{:keys [key-uid seed-phrase]}]]
+   (if (not= key-uid (get-in db [:profile/profile :key-uid]))
+     {:fx [[:dispatch [:enter-seed-phrase/set-error (i18n/label :t/seed-phrase-dont-match)]]]}
path: src/status_im/contexts/keycard/backup/events.cljs
user: login: flexsurfer
body: wouldn't it better to pass :profile/profile :key-uid into enter-phrase screen instead?
created_at: 2025-03-10T09:34:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,28 +108,15 @@
               [:navigate-to-within-stack [:screen/onboarding.enable-biometrics from-screen]]
               [:onboarding/create-account-and-login])]]})))
 
-(rf/reg-event-fx
- :onboarding/multiaccount-already-exists
- (fn [_ [key-uid]]
-   {:fx [[:effects.utils/show-confirmation
-          {:title               (i18n/label :t/multiaccount-exists-title)
-           :content             (i18n/label :t/multiaccount-exists-content)
-           :confirm-button-text (i18n/label :t/unlock)
-           :on-accept           (fn []
-                                  (re-frame/dispatch [:pop-to-root :screen/profile.profiles])
-                                  ;; Note - Profile selection is not working
-                                  (re-frame/dispatch [:profile/profile-selected key-uid]))
-           :on-cancel           #(re-frame/dispatch [:pop-to-root :screen/profile.profiles])}]]}))
-
 (rf/reg-event-fx
  :onboarding/seed-phrase-validated
- (fn [{:keys [db]} [seed-phrase key-uid]]
+ (fn [{:keys [db]} [{:keys [seed-phrase key-uid]}]]
    (let [next-screen :screen/onboarding.create-profile-password
          from-screen (get db
                           :onboarding/navigated-to-enter-seed-phrase-from-screen
                           :screen/onboarding.create-profile)]
      (if (contains? (:profile/profiles-overview db) key-uid)
-       {:fx [[:dispatch [:onboarding/multiaccount-already-exists key-uid]]]}
+       {:fx [[:dispatch [:enter-seed-phrase/set-error (i18n/label :t/account-already-exist-error)]]]}
path: src/status_im/contexts/onboarding/events.cljs
user: login: flexsurfer
body: i believe here we should keep multiaccount-already-exists because we want user be able to login
created_at: 2025-03-10T09:36:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,28 +108,15 @@
               [:navigate-to-within-stack [:screen/onboarding.enable-biometrics from-screen]]
               [:onboarding/create-account-and-login])]]})))
 
-(rf/reg-event-fx
- :onboarding/multiaccount-already-exists
- (fn [_ [key-uid]]
-   {:fx [[:effects.utils/show-confirmation
-          {:title               (i18n/label :t/multiaccount-exists-title)
-           :content             (i18n/label :t/multiaccount-exists-content)
-           :confirm-button-text (i18n/label :t/unlock)
-           :on-accept           (fn []
-                                  (re-frame/dispatch [:pop-to-root :screen/profile.profiles])
-                                  ;; Note - Profile selection is not working
-                                  (re-frame/dispatch [:profile/profile-selected key-uid]))
-           :on-cancel           #(re-frame/dispatch [:pop-to-root :screen/profile.profiles])}]]}))
-
 (rf/reg-event-fx
  :onboarding/seed-phrase-validated
- (fn [{:keys [db]} [seed-phrase key-uid]]
+ (fn [{:keys [db]} [{:keys [seed-phrase key-uid]}]]
    (let [next-screen :screen/onboarding.create-profile-password
          from-screen (get db
                           :onboarding/navigated-to-enter-seed-phrase-from-screen
                           :screen/onboarding.create-profile)]
      (if (contains? (:profile/profiles-overview db) key-uid)
-       {:fx [[:dispatch [:onboarding/multiaccount-already-exists key-uid]]]}
+       {:fx [[:dispatch [:enter-seed-phrase/set-error (i18n/label :t/account-already-exist-error)]]]}
path: src/status_im/contexts/onboarding/events.cljs
user: login: Parveshdhull
body: Hi, user will be still able to login by going back manually, we are just removing popup.

Original discussion - https://github.com/status-im/status-mobile/pull/21947#pullrequestreview-2563056380

> I don't have any strong preference regarding this point, but I find the current solution showing a pop-up slightly worse than what's in Figma because the user loses context by being redirected to the profile selection. The solution in Figma is consistent with other error messages that can occur in the recovery phrase screen, so that's mainly why I prefer it. If the effort is small seems worth improving to match the UX in Figma. Not a blocker to this PR IMO.
created_at: 2025-03-10T10:03:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,7 +132,9 @@
                {:on-complete init-card-with-pin-and-continue}]])]})))
 
 (rf/reg-event-fx :keycard/backup.phrase-entered
- (fn [{:keys [db]} [{:keys [phrase]}]]
-   {:db (assoc-in db [:keycard :backup :masked-phrase] phrase)
-    :fx [[:dispatch [:navigate-back]]
-         [:dispatch [:open-modal :screen/keycard.backup.ready-to-add]]]}))
+ (fn [{:keys [db]} [{:keys [key-uid seed-phrase]}]]
+   (if (not= key-uid (get-in db [:profile/profile :key-uid]))
+     {:fx [[:dispatch [:enter-seed-phrase/set-error (i18n/label :t/seed-phrase-dont-match)]]]}
path: src/status_im/contexts/keycard/backup/events.cljs
user: login: Parveshdhull
body: Actually, I tried that first, but the code became very complicated since the error logic changes depending on the flow. So, I think it's better to keep the enter-seed-phrase logic focused solely on entering the seed phrase. The validation logic should be handled within their respective namespaces, as we were already doing for the 'account already exists' scenario.
created_at: 2025-03-10T10:03:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,8 +232,9 @@
   []
   (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
     (rn/use-unmount
-     (when-not on-success
-       #(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen])))
+     (fn []
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: Hi @flexsurfer, Thank you for reviewing the PR.

> could please elaborate on why (when-not on-success was removed

Now, we are also passing on-success for onboarding instead of hardcoding it, which is why I removed it. It's a small, harmless cleanup event, so it's not problematic if it's being called every time. However, I have removed it and am now passing it as an on-unmount parameter [here](https://github.com/status-im/status-mobile/pull/22252/commits/c266132bd9a7428d983bc499fbba6e7f8ed8cc71). Thank you
created_at: 2025-03-10T10:03:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,21 +139,34 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          error                    (rf/sub [:enter-seed-phrase/error])
+          on-change-seed-phrase    (fn [new-phrase]
+                                     (when error
+                                       (rf/dispatch [:enter-seed-phrase/clear-error]))
+                                     (when @invalid-seed-phrase?
+                                       (reset! invalid-seed-phrase? false))
+                                     (when @incorrect-seed-phrase?
+                                       (reset! incorrect-seed-phrase? false))
+                                     (reset! seed-phrase new-phrase))
           suggestions-state        (cond
-                                     (or error-in-words?
+                                     (or error
+                                         error-in-words?
                                          words-exceeded?
                                          @invalid-seed-phrase?
                                          @incorrect-seed-phrase?)         :error
                                      (string/blank? @seed-phrase)         :info
                                      (string/ends-with? @seed-phrase " ") :empty
                                      :else                                :words)
-          suggestions-text         (cond
-                                     upper-case?             (i18n/label :t/seed-phrase-words-uppercase)
-                                     words-exceeded?         (i18n/label :t/seed-phrase-words-exceeded)
-                                     error-in-words?         (i18n/label :t/seed-phrase-error)
-                                     @invalid-seed-phrase?   (i18n/label :t/seed-phrase-invalid)
-                                     @incorrect-seed-phrase? (i18n/label :t/seed-phrase-incorrect)
-                                     :else                   (i18n/label :t/seed-phrase-info))
+          suggestions-text         (or error
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: ilmotta
body: Reads a little strange to see `cond` nested inside `or` because the `cond` macro is also meant to unnest conditionals if possible. Just a nitpick, but I would make the first condition:

```clojure
(cond
  error       error
  upper-case? ...
  ...)
```
created_at: 2025-03-11T03:16:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: ilmotta
body: This looks like a smell @Parveshdhull. Fundamentally we shouldn't couple the event and subscription layers with the components lifecycles directly. The subs are a bit more tied, but not really, because re-frame does the heavy lifting based on reactions.

I don't know if what I'm going to suggest is a good fit, but sometimes, instead of passing opaque callbacks all around, we can pass open/transparent data, which the consumer can contextualize and decide what to do. The tradeoff of this choice might be that the code may not be as general (since a callback can do absolutely anything) but the code can end up being easier to inspect/debug.

What are your thoughts on this pattern @ulisesmac @clauxx? Do you see a better way?
created_at: 2025-03-11T03:25:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,21 +139,34 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          error                    (rf/sub [:enter-seed-phrase/error])
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: ilmotta
body: What is the rationale to prefer the state in the app-db instead of a local state? The local state wouldn't need to cater for unmounting situations. If the error is mostly limited to this component, and also prop drilling wouldn't be an issue, a local state can be better.
created_at: 2025-03-11T03:35:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,21 +139,34 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          error                    (rf/sub [:enter-seed-phrase/error])
+          on-change-seed-phrase    (fn [new-phrase]
+                                     (when error
+                                       (rf/dispatch [:enter-seed-phrase/clear-error]))
+                                     (when @invalid-seed-phrase?
+                                       (reset! invalid-seed-phrase? false))
+                                     (when @incorrect-seed-phrase?
+                                       (reset! incorrect-seed-phrase? false))
+                                     (reset! seed-phrase new-phrase))
           suggestions-state        (cond
-                                     (or error-in-words?
+                                     (or error
+                                         error-in-words?
                                          words-exceeded?
                                          @invalid-seed-phrase?
                                          @incorrect-seed-phrase?)         :error
                                      (string/blank? @seed-phrase)         :info
                                      (string/ends-with? @seed-phrase " ") :empty
                                      :else                                :words)
-          suggestions-text         (cond
-                                     upper-case?             (i18n/label :t/seed-phrase-words-uppercase)
-                                     words-exceeded?         (i18n/label :t/seed-phrase-words-exceeded)
-                                     error-in-words?         (i18n/label :t/seed-phrase-error)
-                                     @invalid-seed-phrase?   (i18n/label :t/seed-phrase-invalid)
-                                     @incorrect-seed-phrase? (i18n/label :t/seed-phrase-incorrect)
-                                     :else                   (i18n/label :t/seed-phrase-info))
+          suggestions-text         (or error
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: Thank you, fixed
created_at: 2025-03-11T05:44:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,21 +139,34 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          error                    (rf/sub [:enter-seed-phrase/error])
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: Hi @ilmotta,

Once the seed phrase is validated, we dispatch the on-success function. This function further validates based on the flow. For example:

- In the account creation flow, the key-uid of the entered seed phrase should not be present in the profiles.
- In the backup keycard flow, the key-uid of the entered seed phrase should match the key-uid of the current profile.

Since the validations are happening outside of this screen, it seems simpler to store the error state in the app-db. I could alternatively pass the set-error function, but we’re not supposed to call functions from events. Therefore, I need to create an effect and pass this function to that effect.
created_at: 2025-03-11T05:49:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: Hi, Are you suggesting that, instead of passing `on-unmount`, I pass the `onboarding-flow?` parameter and then call `(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen])` on `on-unmount` when it is true?
created_at: 2025-03-11T05:53:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: Or alternatively, I can remove cleanup call `on-unmount` and cleanup this state while opening this screen?
created_at: 2025-03-11T05:55:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,21 +139,34 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          error                    (rf/sub [:enter-seed-phrase/error])
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: > Since the validations are happening outside of this screen,

By the way, I also tried moving the flow-based validation into the screen itself, but it seemed more noisy to me (passing unnecessary data based on the flow, if-else statements, etc.). This approach seems cleaner to me.

https://github.com/status-im/status-mobile/pull/22252#discussion_r1986971590
created_at: 2025-03-11T05:58:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: Another solution, instead of passing on-unmount or onboarding-flow?, is to create an additional wrapper view specifically for onboarding. This approach aligns with the DX task - https://www.notion.so/Explicit-is-better-than-implicit-1a78f96fb65c80e4967bfb43f2e09280
created_at: 2025-03-11T08:49:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,21 +139,34 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          error                    (rf/sub [:enter-seed-phrase/error])
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: UPD: Renamed error to global-error
created_at: 2025-03-11T10:20:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: ulisesmac
body: @ilmotta I think your suggestion is great, and also what @Parveshdhull said about passing `onboarding-flow?` as param solves it.

I also don't like to pass callbacks because of their opaqueness, although sometimes it's needed.
created_at: 2025-03-11T16:17:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: ilmotta
body: @Parveshdhull your suggestion to cleanup on mount if it works is usually my preferred choice of cleanup strategy because it's guaranteed any state would converge to the correct state at all times. Cleaning up on exit points tends to be fragile.

As @ulisesmac said above, I also prefer a more data-driven approach or what you suggest with a wrapper.

> Another solution, instead of passing on-unmount or onboarding-flow?, is to create an additional wrapper view specifically for onboarding.

This can work well too. If I understood the Notion reference, basically general parts should receive general inputs and the conditional logic should be handled by a more concrete parent/wrapper function or component. If that's the case, makes sense 👍🏼 
created_at: 2025-03-11T17:15:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,21 +139,34 @@
           error-in-words?          (or (not last-partial-word-valid?)
                                        (not butlast-words-valid?))
           upper-case?              (boolean (re-find #"[A-Z]" @seed-phrase))
+          error                    (rf/sub [:enter-seed-phrase/error])
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: ilmotta
body: If you tried and you prefer this way I trust your decision! Thanks @Parveshdhull 
created_at: 2025-03-11T17:16:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: Parveshdhull
body: Thank you, @ilmotta and @ulisesmac for the discussion. 

For now, I have migrated to the `onboarding-flow?` parameter. The wrapper approach is still a bit immature, and we need to agree on and create guidelines for naming wrappers, or perhaps consider using a different namespace, etc (added details in [document](https://www.notion.so/Explicit-is-better-than-implicit-1a78f96fb65c80e4967bfb43f2e09280)). I think it would be worth revisiting this later; otherwise, our codebase may end up looking inconsistent with a few wrappers.
created_at: 2025-03-12T04:06:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -227,10 +230,12 @@
 
 (defn view
   []
-  (let [{:keys [on-success]} (rf/sub [:get-screen-params])]
+  (let [{:keys [on-success on-unmount]} (rf/sub [:get-screen-params])]
path: src/status_im/common/enter_seed_phrase/view.cljs
user: login: ilmotta
body: Nice discussion in this thread, thank you @Parveshdhull. In terms of inconsistency in the repo, there's a benefit in experimenting ideas in develop, learning from developer feedback if it's good, and only later on devising the full guidelines. We did that iteration style a lot with testing utilities. Otherwise we may get in a paralysis situation because the repository is very large and thus reaching full consistency can be challenging. Perhaps in the beginning a light guideline can be defined (?) and the experiment could kick in?
created_at: 2025-03-12T10:32:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: status-im-auto
created_at: 2025-03-06T12:30:31Z
body: 
### Jenkins Builds
<details>
<summary>Click to see older builds (49)</summary>

| :grey_question: | Commit | :hash: | Finished (UTC) | Duration | Platform | Result |
|-|-|-|-|-|-|-|
| :heavy_check_mark: | 6c9dcee6 | [#1](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/1/) | 2025-03-06 12:30:30 | ~27 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250306-120242-6c9dce-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250306-120242-6c9dce-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 6c9dcee6 | [#1](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/1/) | 2025-03-06 12:30:40 | ~27 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250306-120247-6c9dce-pr22252-tests.log) |
| :heavy_check_mark: | 6c9dcee6 | [#1](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/1/) | 2025-03-06 12:35:49 | ~33 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250306-120247-6c9dce-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250306-120247-6c9dce-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 6c9dcee6 | [#1](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/1/) | 2025-03-06 13:08:32 | ~1 hr 5 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/pbSqdm) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FpbSqdm&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :x: | da58d9aa | [#2](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/2/) | 2025-03-10 08:43:04 | ~2 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/2/consoleText) |
| :heavy_check_mark: | da58d9aa | [#2](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/2/) | 2025-03-10 08:48:20 | ~8 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-084009-da58d9-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-084009-da58d9-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | da58d9aa | [#2](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/2/) | 2025-03-10 08:48:51 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-084010-da58d9-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-084010-da58d9-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :x: | 6ed4fdfe | [#3](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/3/) | 2025-03-10 08:53:25 | ~4 min | `ios` | [:page_facing_up:`log`](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/3/consoleText) |
| :heavy_check_mark: | 6ed4fdfe | [#3](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/3/) | 2025-03-10 08:54:13 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-084917-6ed4fd-pr22252-tests.log) |
| :heavy_check_mark: | 6ed4fdfe | [#3](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/3/) | 2025-03-10 08:56:05 | ~6 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-084913-6ed4fd-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-084913-6ed4fd-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 6ed4fdfe | [#3](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/3/) | 2025-03-10 08:57:34 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-084913-6ed4fd-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-084913-6ed4fd-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :x: | 0ac6316e | [#4](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/4/) | 2025-03-10 09:00:17 | ~2 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/4/consoleText) |
| :heavy_check_mark: | 0ac6316e | [#4](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/4/) | 2025-03-10 09:05:47 | ~7 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-085753-0ac631-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-085753-0ac631-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 0ac6316e | [#4](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/4/) | 2025-03-10 09:06:19 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-085753-0ac631-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-085753-0ac631-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 0ac6316e | [#4](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/4/) | 2025-03-10 09:08:35 | ~10 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/rsN2yx) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FrsN2yx&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | e0c0ae63 | [#6](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/6/) | 2025-03-10 09:22:14 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-091743-e0c0ae-pr22252-tests.log) |
| :heavy_check_mark: | e0c0ae63 | [#6](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/6/) | 2025-03-10 09:24:32 | ~6 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-091739-e0c0ae-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-091739-e0c0ae-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | e0c0ae63 | [#6](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/6/) | 2025-03-10 09:25:03 | ~7 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-091739-e0c0ae-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-091739-e0c0ae-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | e0c0ae63 | [#6](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/6/) | 2025-03-10 09:28:17 | ~10 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/9o8G1Y) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2F9o8G1Y&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | c266132b | [#7](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/7/) | 2025-03-10 10:09:11 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-100424-c26613-pr22252-tests.log) |
| :heavy_check_mark: | c266132b | [#7](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/7/) | 2025-03-10 10:11:08 | ~6 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-100424-c26613-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-100424-c26613-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | c266132b | [#7](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/7/) | 2025-03-10 10:12:49 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250310-100424-c26613-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250310-100424-c26613-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | c266132b | [#7](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/7/) | 2025-03-10 10:14:10 | ~9 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/G52sE8) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FG52sE8&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | 4146ee83 | [#8](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/8/) | 2025-03-11 05:49:50 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-054458-4146ee-pr22252-tests.log) |
| :heavy_check_mark: | 4146ee83 | [#8](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/8/) | 2025-03-11 05:51:40 | ~6 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-054458-4146ee-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-054458-4146ee-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 4146ee83 | [#8](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/8/) | 2025-03-11 05:53:19 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-054458-4146ee-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-054458-4146ee-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 4146ee83 | [#8](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/8/) | 2025-03-11 05:55:37 | ~10 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/Zi19cv) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FZi19cv&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :x: | 8a6cfa82 | [#9](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/9/) | 2025-03-11 10:04:55 | ~2 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/9/consoleText) |
| :heavy_check_mark: | 8a6cfa82 | [#9](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/9/) | 2025-03-11 10:09:44 | ~7 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-100157-8a6cfa-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-100157-8a6cfa-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 8a6cfa82 | [#9](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/9/) | 2025-03-11 10:10:18 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-100200-8a6cfa-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-100200-8a6cfa-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 8a6cfa82 | [#9](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/9/) | 2025-03-11 10:13:17 | ~11 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/pdLpfA) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FpdLpfA&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | 21cd6a68 | [#10](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/10/) | 2025-03-11 10:21:26 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-101707-21cd6a-pr22252-tests.log) |
| :heavy_check_mark: | 21cd6a68 | [#10](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/10/) | 2025-03-11 10:25:13 | ~8 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-101702-21cd6a-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-101702-21cd6a-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 21cd6a68 | [#10](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/10/) | 2025-03-11 10:25:48 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-101707-21cd6a-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-101707-21cd6a-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 21cd6a68 | [#10](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/10/) | 2025-03-11 10:28:29 | ~11 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/D7r8jz) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FD7r8jz&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | 55d22987 | [#11](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/11/) | 2025-03-11 16:05:36 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-160117-55d229-pr22252-tests.log) |
| :heavy_check_mark: | 55d22987 | [#11](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/11/) | 2025-03-11 16:09:06 | ~7 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-160113-55d229-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-160113-55d229-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 55d22987 | [#11](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/11/) | 2025-03-11 16:09:37 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250311-160113-55d229-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250311-160113-55d229-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 55d22987 | [#11](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/11/) | 2025-03-11 16:11:02 | ~9 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/vbrHMZ) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FvbrHMZ&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | 5f680234 | [#12](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/12/) | 2025-03-12 04:10:56 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250312-040639-5f6802-pr22252-tests.log) |
| :heavy_check_mark: | 5f680234 | [#12](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/12/) | 2025-03-12 04:14:20 | ~7 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250312-040635-5f6802-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250312-040635-5f6802-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 5f680234 | [#12](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/12/) | 2025-03-12 04:14:56 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250312-040639-5f6802-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250312-040639-5f6802-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 5f680234 | [#12](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/12/) | 2025-03-12 04:18:15 | ~11 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/YUgFxH) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FYUgFxH&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | c84b82d0 | [#13](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/13/) | 2025-03-17 09:29:04 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250317-092420-c84b82-pr22252-tests.log) |
| | | | | | | |
| :x: | 27b6ac23 | [#14](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/14/) | 2025-03-17 09:34:37 | ~2 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/14/consoleText) |
| | | | | | | |
| :heavy_check_mark: | a23fd419 | [#15](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/15/) | 2025-03-17 09:40:59 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250317-093635-a23fd4-pr22252-tests.log) |
| :heavy_check_mark: | a23fd419 | [#15](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/15/) | 2025-03-17 09:43:25 | ~6 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250317-093630-a23fd4-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250317-093630-a23fd4-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | a23fd419 | [#15](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/15/) | 2025-03-17 09:44:05 | ~7 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250317-093635-a23fd4-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250317-093635-a23fd4-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | a23fd419 | [#15](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/15/) | 2025-03-17 09:47:47 | ~11 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/XcXfph) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FXcXfph&size=400x400&errorCorrectionLevel=L)|
</details>

| :grey_question: | Commit | :hash: | Finished (UTC) | Duration | Platform | Result |
|-|-|-|-|-|-|-|
| :heavy_check_mark: | 92ca0f18 | [#16](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/16/) | 2025-03-17 13:32:20 | ~7 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250317-132516-92ca0f-pr22252-tests.log) |
| :heavy_check_mark: | 92ca0f18 | [#16](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/16/) | 2025-03-17 13:34:28 | ~9 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250317-132513-92ca0f-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250317-132513-92ca0f-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 92ca0f18 | [#16](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/16/) | 2025-03-17 13:34:29 | ~9 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250317-132513-92ca0f-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250317-132513-92ca0f-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 92ca0f18 | [#16](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/16/) | 2025-03-17 13:42:29 | ~17 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/PmsjrD) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2FPmsjrD&size=400x400&errorCorrectionLevel=L)|
| | | | | | | |
| :heavy_check_mark: | 25c84627 | [#17](https://ci.status.im/job/status-mobile/job/prs/job/tests/job/PR-22252/17/) | 2025-03-18 10:44:54 | ~4 min | `tests` | [:page_facing_up:`log`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250318-104004-25c846-pr22252-tests.log) |
| :heavy_check_mark: | 25c84627 | [#17](https://ci.status.im/job/status-mobile/job/prs/job/android-e2e/job/PR-22252/17/) | 2025-03-18 10:47:01 | ~6 min | `android-e2e` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250318-104004-25c846-pr22252-x86_64.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250318-104004-25c846-pr22252-x86_64.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 25c84627 | [#17](https://ci.status.im/job/status-mobile/job/prs/job/android/job/PR-22252/17/) | 2025-03-18 10:48:29 | ~8 min | `android` | [:robot:`apk`](https://status-im-mobile-prs.ams3.cdn.digitaloceanspaces.com/StatusIm-Mobile-250318-104004-25c846-pr22252-arm64-v8a.apk) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fstatus-im-mobile-prs.ams3.cdn.digitaloceanspaces.com%2FStatusIm-Mobile-250318-104004-25c846-pr22252-arm64-v8a.apk&size=400x400&errorCorrectionLevel=L)|
| :heavy_check_mark: | 25c84627 | [#17](https://ci.status.im/job/status-mobile/job/prs/job/ios/job/PR-22252/17/) | 2025-03-18 10:51:35 | ~11 min | `ios` | [:iphone:`ipa`](https://i.diawi.com/imkyby) [:calling:](https://quickchart.io/qr?text=https%3A%2F%2Fi.diawi.com%2Fimkyby&size=400x400&errorCorrectionLevel=L)|

author_association: MEMBER
user: login: flexsurfer
created_at: 2025-03-06T12:58:02Z
body: hey @Parveshdhull im not sure if it relevant but in create profile (keycard) flow there is a button 
<img width="335" alt="image" src="https://github.com/user-attachments/assets/9bc7a94c-1ff4-4f49-9fe8-3b420e94d44c" />

also i think this check should be conditional, in some flows we expect phrase from profile on device
<img width="331" alt="image" src="https://github.com/user-attachments/assets/e83a18a2-4d93-4e68-8aab-f31d6fdb8254" />

author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-06T13:04:07Z
body: Thank you very much @flexsurfer for checking the PR.

 > in create profile (keycard) flow there is a button
I will fix it.

> also i think this check should be conditional, in some flows we expect phrase from profile on device

Thank you, I will make it conditional 
author_association: CONTRIBUTOR
user: login: flexsurfer
created_at: 2025-03-06T13:04:24Z
body: thank you . this is the issue https://github.com/status-im/status-mobile/issues/22013
author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-10T09:17:44Z
body: @flexsurfer @ilmotta PR is ready for review.
author_association: CONTRIBUTOR
user: login: status-im-auto
created_at: 2025-03-10T16:43:09Z
body: ## 71% of end-end tests have passed
```
Total executed tests: 14
Failed tests: 4
Expected to fail tests: 0
Passed tests: 10
```
```
IDs of failed tests: 741840,727231,741839,741841 
```
<h3>Failed tests (4)</h3><details><summary>Click to expand</summary><br/><li><a href="https://ci.status.im/job/status-mobile/job/e2e/job/status-app-prs-rerun/parambuild/?BRANCH_NAME=fix/recovery-phrase-error&PR_ID=22252&APK_NAME=22252.apk&TR_CASE_IDS=741840,727231,741839,741841">Rerun failed tests</a></li><br/><h4>Class TestWalletCollectibles:</h4><table style="width: 100%"><colgroup><col span="1" style="width: 20%;"><col span="1" style="width: 80%;"></colgroup><tbody><tr></tr><tr><td><b>1. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809538">test_wallet_send_collectible</a>, id: 741840 </b></td></tr><tr><td><p><blockquote><div>Device 1: Tap on found: Button </div><div>Device 1: Find `CollectibleItemElement` by `xpath`: `//*[@content-desc='collectible-list-item']//*[contains(@text,'BVL')]/../..` </div></blockquote></p>

```
critical/wallet/test_collectibles.py:104: in test_wallet_send_collectible
    self.wallet_view.get_collectible_element('BVL').click()
../views/base_element.py:89: in click
    element = self.find_element()
../views/base_element.py:78: in find_element
    raise NoSuchElementException(
 Device 1: CollectibleItemElement by xpath: `//*[@content-desc='collectible-list-item']//*[contains(@text,'BVL')]/../..` is not found on the screen; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
```

<br/><br/></td></tr><tr><td><b>2. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809537">test_wallet_collectibles_balance</a>, id: 741839 </b></td></tr><tr><td><p><blockquote><div># STEP: Check BVL collectible info and image</div><div># STEP: Check Glitch Punks collectible info and image</div></blockquote></p>

```
critical/wallet/test_collectibles.py:94: in test_wallet_collectibles_balance
    self.errors.verify_no_errors()
base_test_case.py:179: in verify_no_errors
    pytest.fail('\n '.join([self.errors.pop(0) for _ in range(len(self.errors))]))
 Device 1: Collectible 'BVL' is not displayed
E    Device 1: Collectible 'Glitch Punks' is not displayed
```

<br/><br/></td></tr><tr><td><b>3. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809539">test_wallet_collectible_send_from_expanded_info_view</a>, id: 741841 </b></td></tr><tr><td><p><blockquote><div>Device 1: Find `Button` by `accessibility id`: `collectibles-tab` </div><div>Device 1: Tap on found: Button </div></blockquote></p>

```
critical/wallet/test_collectibles.py:150: in test_wallet_collectible_send_from_expanded_info_view
    self.wallet_view.get_collectible_element('Glitch Punks').wait_for_element().click()
../views/base_element.py:120: in wait_for_element
    raise TimeoutException(
 Device `1`: `CollectibleItemElement` by` xpath`: `//*[@content-desc='collectible-list-item']//*[contains(@text,'Glitch Punks')]/../..` is not found on the screen after wait_for_element
```

<br/><br/></td></tr></tbody></table><h4>Class TestWalletOneDevice:</h4><table style="width: 100%"><colgroup><col span="1" style="width: 20%;"><col span="1" style="width: 80%;"></colgroup><tbody><tr></tr><tr><td><b>1. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809532">test_wallet_add_remove_regular_account</a>, id: 727231 </b></td></tr><tr><td><p><blockquote><div># STEP: Adding new regular account</div><div>Device 1: Find `Button` by `accessibility id`: `add-account` </div></blockquote></p>

```
critical/wallet/test_wallet_mainnet.py:350: in test_wallet_add_remove_regular_account
    self.wallet_view.add_regular_account(account_name=new_account_name)
../views/wallet_view.py:245: in add_regular_account
    self.add_account_button.click()
../views/base_element.py:91: in click
    element.click()
../../../../status-app-prs@tmp/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webelement.py:94: in click
    self._execute(Command.CLICK_ELEMENT)
../../../../status-app-prs@tmp/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webelement.py:395: in _execute
    return self._parent.execute(command, params)
../../../../status-app-prs@tmp/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py:345: in execute
    self.error_handler.check_response(response)
../../../../status-app-prs@tmp/venv/lib/python3.10/site-packages/appium/webdriver/errorhandler.py:122: in check_response
    raise exception_class(msg=message, stacktrace=format_stacktrace(stacktrace))
 The element 'By.accessibilityId: add-account' is not linked to the same object in DOM anymore; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#stale-element-reference-exception
E   Stacktrace:
E   io.appium.uiautomator2.common.exceptions.StaleElementReferenceException: The element 'By.accessibilityId: add-account' is not linked to the same object in DOM anymore
E   	at io.appium.uiautomator2.model.ElementsCache.restore(ElementsCache.java:122)
E   	at io.appium.uiautomator2.model.ElementsCache.get(ElementsCache.java:153)
E   	at io.appium.uiautomator2.handler.Click.safeHandle(Click.java:36)
E   	at io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:59)
E   	at io.appium.uiautomator2.server.AppiumServlet.handleRequest(AppiumServlet.java:277)
E   	at io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:271)
E   	at io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:77)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E   	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E   	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E   	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E   	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:435)
E   	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
E   	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
E   	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:250)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E   	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E   	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E   	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E   	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1294)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E   	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E   	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:911)
E   	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
E   	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:611)
E   	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:552)
E   	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:466)
E   	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:438)
E   	at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:140)
E   	at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
E   	at java.lang.Thread.run(Thread.java:1012)
```

<br/><br/></td></tr></tbody></table></details><h3>Passed tests (10)</h3><details><summary>Click to expand</summary><br/><br/><h4>Class TestCommunityMultipleDeviceMerged:</h4><table style="width: 100%"><colgroup><col span="1" style="width: 20%;"><col span="1" style="width: 80%;"></colgroup><tbody><tr></tr><tr><td><b>1. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809528">test_community_message_edit</a>, id: 702843 </b></td></tr><tr><td><ins>Device sessions</ins><p><ul><p>Device 1:<ul><li><a href="https://appautomation.lambdatest.com/test?testID=APPTES6lmEyS8AZ63hw6nh">Steps, video, logs</a></li></ul></p><p>Device 2:<ul><li><a href="https://appautomation.lambdatest.com/test?testID=APPTESVqmJuwMSpvDrOjFv">Steps, video, logs</a></li></ul></p></ul></p></td></tr></tbody></table><h4>Class TestCommunityOneDeviceMerged:</h4><table style="width: 100%"><colgroup><col span="1" style="width: 20%;"><col span="1" style="width: 80%;"></colgroup><tbody><tr></tr><tr><td><b>1. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809526">test_community_copy_and_paste_message_in_chat_input</a>, id: 702742 </b></td></tr><tr><td><ins>Device sessions</ins><p><ul><p>Device 1:<ul><li><a href="https://appautomation.lambdatest.com/test?testID=APPTESgeZdrJ7vVNAR3Omz">Steps, video, logs</a></li></ul></p></ul></p></td></tr><tr><td><b>2. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809529">test_restore_multiaccount_with_waku_backup_remove_profile_switch</a>, id: 703133 </b></td></tr><tr><td><ins>Device sessions</ins><p><ul><p>Device 1:<ul><li><a href="https://appautomation.lambdatest.com/test?testID=APPTESgeZdrJ7vVNAR3Omz">Steps, video, logs</a></li></ul></p></ul></p></td></tr></tbody></table><h4>Class TestWalletMultipleDevice:</h4><table style="width: 100%"><colgroup><col span="1" style="width: 20%;"><col span="1" style="width: 80%;"></colgroup><tbody><tr></tr><tr><td><b>1. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809531">test_wallet_send_asset_from_drawer</a>, id: 727230 </b></td></tr><tr><td></td></tr><tr><td><b>2. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809530">test_wallet_send_eth</a>, id: 727229 </b></td></tr><tr><td></td></tr></tbody></table><h4>Class TestOneToOneChatMultipleSharedDevicesNewUi:</h4><table style="width: 100%"><colgroup><col span="1" style="width: 20%;"><col span="1" style="width: 80%;"></colgroup><tbody><tr></tr><tr><td><b>1. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809527">test_1_1_chat_non_latin_messages_stack_update_profile_photo</a>, id: 702745 </b></td></tr><tr><td><ins>Device sessions</ins><p><ul><p>Device 1:<ul><li><a href="https://appautomation.lambdatest.com/test?testID=APPTESoeboXYr5tCUH2eKG">Steps, video, logs</a></li></ul></p><p>Device 2:<ul><li><a href="https://appautomation.lambdatest.com/test?testID=APPTESzSJWzQEYIqYGKWBG">Steps, video, logs</a></li></ul></p></ul></p></td></tr></tbody></table><h4>Class TestWalletOneDevice:</h4><table style="width: 100%"><colgroup><col span="1" style="width: 20%;"><col span="1" style="width: 80%;"></colgroup><tbody><tr></tr><tr><td><b>1. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809535">test_wallet_swap_flow_mainnet</a>, id: 741555 </b></td></tr><tr><td></td></tr><tr><td><b>2. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809533">test_wallet_balance_mainnet</a>, id: 740490 </b></td></tr><tr><td></td></tr><tr><td><b>3. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809536">test_wallet_bridge_flow_mainnet</a>, id: 741612 </b></td></tr><tr><td></td></tr><tr><td><b>4. <a href="https://ethstatus.testrail.net/index.php?/tests/view/3809534">test_wallet_send_flow_mainnet</a>, id: 741554 </b></td></tr><tr><td></td></tr></tbody></table></details>
author_association: MEMBER
user: login: pavloburykh
created_at: 2025-03-11T07:23:44Z
body: Hey @Parveshdhull! Thanks for the PR. I see there are couple unresolved review comments above. Should I wait for approval or PR is ready for testing? 
author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-11T08:46:59Z
body: Hi @pavloburykh, Thank you very much for taking the PR for testing

> Should I wait for approval or PR is ready for testing?

Yes, lets wait for @ilmotta's approval.

author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-11T09:31:48Z
body: > Yes, lets wait for @ilmotta's approval.

Sounds good @Parveshdhull 

Meanwhile, I have a questions and small UI issues:

### QUEStTION: 

According to [designs](https://www.figma.com/design/YGm3igIOAcwMqUVJWCJ6f1/Keycard?node-id=5045-49281&m=dev) in case of validation error instead of `Continue` button we should show `Enter another recovery phrase` button which clears recovery phrase so user can enter it from scratch. Should we implement it in scope of this PR or there is a sepatate issue on this? 

**Actual result:** 

![photo_2025-03-11 11 25 59](https://github.com/user-attachments/assets/3e982c49-2621-4b4c-8978-6872dcf720f2)
![photo_2025-03-11 11 26 01](https://github.com/user-attachments/assets/b69506c4-53bc-4d8e-a998-85c3518ecde3)


**Expected result:** 

<img width="453" alt="Keycard – Figma 2025-03-11 11-25-29" src="https://github.com/user-attachments/assets/573eea6b-5ca4-45ff-a54c-8227c4656fd2" />


 

author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-11T09:31:53Z
body: ### ISSUE 1 Validation error has wrong padding in some places 
 
**Actual result:** 

**Android 12, Samsung Galaxy A 52** 

![photo_2025-03-11 11 28 20](https://github.com/user-attachments/assets/f5eb1c0e-2086-408c-8cd4-a491c06c8258)

IOS 17.6.1, iPhone 15

![photo_2025-03-11 11 29 34](https://github.com/user-attachments/assets/e24e29d8-42d2-4405-8460-1d2206b1a37e)


author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-11T10:02:57Z
body: Thank you @pavloburykh for reporting these issues. Should be fixed now.

I also updated the validation error message for the keycard migration flow. It was similar to the backup flow, but not exactly the same.

UPD: Oops I forget to change on-press for "Enter another recovery phrase" (fixed)
author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-11T15:17:38Z
body: Thank you @Parveshdhull! Both issues are fixed. 
There is a minor problem of validation error background is cut on the right side (see screenshots below). But it can be hadled separately as low priority. It's up to you. 

Same on IOS and Android. 

![photo_2025-03-11 17 13 34](https://github.com/user-attachments/assets/73781830-4343-450a-b2e0-5127b859fc64)


author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-11T16:03:09Z
body: Thank you @pavloburykh should be fixed now.

> Same on IOS and Android.

It's weird, I don't see the blurred background (with or without cutoff) on my Android & Emulator 🤔

![image](https://github.com/user-attachments/assets/27852eb6-dd9c-4bac-a895-3cdd85e01ead)

author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-12T04:08:06Z
body: Thank you @pavloburykh for finding these bugs. All fixed and PR is reviewed, ready for retesting.
author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-12T14:27:02Z
body: @Parveshdhull thanks for your fixes!

I am facing one more issue, which is not reproducible in develop, could you please check if it can be PR related? 

### ISSUE 2 Fail screen after Finalising migration

Bug is related to Finalising migration flow, when user migrates Profile to the keycard which already holds keypair of this Profile. 

**Steps:** 

1. Recover User A 
2. Go to Profile - Keycard
3. Scan Keycard which already holds current Profile keys
4. Go through Finalise migration flow
5. See if migration is successfull

**Actual result:** Error screen is displayed after final keycard scanning. At the same time, migration is successful, user is able to login Profile via keycard. 

![photo_2025-03-12 16 25 57](https://github.com/user-attachments/assets/fa867f1f-e619-4804-934f-61758acae07a)

https://github.com/user-attachments/assets/b59ec1f1-ed1f-4a88-ad1c-fd5334a3f0c3


author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-17T09:28:20Z
body: Hi @pavloburykh, Thank you very much for reporting the issue.

I have rebased my PR onto the develop branch, and I am no longer able to reproduce the issue in the debug build. I'm not sure if the issue was addressed by [this PR](https://github.com/status-im/status-mobile/pull/22214). Could you please recheck?

ps: had to squash commits, there were many conflicts with rebase
author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-17T12:27:20Z
body: @Parveshdhull thank you! ISSUE 2 is no longer reproducible for me. 

There is one more issue, I wanted to ask you to check. I am not sure if it could be PR related, but I have faced it several times while testing your PR although I failed to reproduce it in develop. However there are no reproduction steps unfortunately, it happens randomly. 

If you prefer to investigate this issue separately, I can log a follow up ticket after merging current PR. 

### ISSUE 3 Keycard scanner randomly stops working (Android) 

On the video below you can see me trying to scan Keycard on my Android but scanner does not trigger connection with keycard. After putting Status app to foreground and back - scanner starts working. 

**Steps:**

There are no reproduction steps unfortunately, it happens randomly. 

[Status-debug-logs (11).zip](https://github.com/user-attachments/files/19286051/Status-debug-logs.11.zip)

https://github.com/user-attachments/assets/95bc5861-7d53-4e07-9013-3c0312fd4a8d


Device: Samsung Galaxy A52
OS: Android 12
author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-17T12:41:18Z
body: > @Parveshdhull thank you! ISSUE 2 is no longer reproducible for me.

Awesome, thank you.

> ISSUE 3 Keycard scanner randomly stops working 

Thank you for reporting this issue. I'm not sure why it is reproducible only in this PR, but it doesn't seem to be related to my changes. This PR only makes modifications to the enter-seed-phrase flow/screen. Keycard scanning etc are after this flow, once the seed phrase input has been provided.

author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-17T12:46:49Z
body: ```
03-17 14:09:36.683 29434 29629 I ReactNativeJS: Running "nfc-sheet
03-17 14:13:24.505 29434 29629 D ReactNativeJS: 2025-03-17T12:13:24.504Z DEBUG [keycard.keycard:25] - stop-nfc
03-17 14:13:24.535 29434 29629 I ReactNativeJS: Running "keycard.different-card
```
From logs it seems fine, nfc was running and only stoped while showing navigating to different card
author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-17T12:47:49Z
body: Maybe a build realted issue (nightly vs pr) :thinking: 
author_association: CONTRIBUTOR
user: login: Parveshdhull
created_at: 2025-03-17T12:54:58Z
body: Or might be card position.

> Samsung Galaxy A52

Please can you try pressing keycard chip part at this position, next time you face this issue.
![image](https://github.com/user-attachments/assets/1d4723ee-a01d-4f95-a24d-f638fc2ac99b)

author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-17T13:03:01Z
body: > Maybe a build realted issue (nightly vs pr)

Thanks for looking at it. I will log the issue separately and we will keep an eye on this and provide an update. In this PR I have faced the issue approx 6 times. 

@Parveshdhull looks like we have regression after rebasing. Please take a look. 

### ISSUE 4 No validation is shown when importing already added recovery phrase to Keycard during onboarding

**Steps:**

1. Recover Profile A
2. Logout Profile A and proceed with new onboarding: Create new profile > Use an empty keycard > Scan keycard > Import recovery phrase to Keycard
3. Enter seed phrase that belongs to already recovered Profile A
4. See if validation error appears

**Actual result:** no validation error. User can proceed with importing which will eventually stuck after final scan. 


https://github.com/user-attachments/assets/78ffb9ce-3dbd-4d1d-abed-d44d9a645a19


author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-17T13:13:47Z
body: > Or might be card position.
> 
> > Samsung Galaxy A52
> 
> Please can you try pressing keycard chip part at this position, next time you face this issue. ![image](https://private-user-images.githubusercontent.com/17097240/423479512-1d4723ee-a01d-4f95-a24d-f638fc2ac99b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDIyMTcyMjEsIm5iZiI6MTc0MjIxNjkyMSwicGF0aCI6Ii8xNzA5NzI0MC80MjM0Nzk1MTItMWQ0NzIzZWUtYTAxZC00Zjk1LWEyNGQtZjYzOGZjMmFjOTliLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMTclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzE3VDEzMDg0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTlkZWZlOTk1NzU2ZTQ0ZDliYjc3MDVjNzRkYzNjYzU3NjUxZGQ2Y2RhNDQ5ZjFiMmVmZjA4ZDYzMjBiMTBhMjcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.v3Zq2Za9Vsq46-egYsra9yhjzIvfGevHfLAW13MSsuY)

Thanks @Parveshdhull I will try, but I doubt it is related to card position. On the video below you can see that scanner does not work but once I reopen the app it starts working with the same keycard position. Scanner usually works for me when I put the chip in the area of camera. 


https://github.com/user-attachments/assets/f9572c2b-234c-4fd0-84a6-da41bdad2061




author_association: CONTRIBUTOR
user: login: pavloburykh
created_at: 2025-03-18T10:30:21Z
body: @Parveshdhull thank you for all the fixes. PR is ready for merge. 

**What has been tested:**

1. Seedphrase validation errors in the following flows: KK migration, Import keypair to Keycard (during onboarding), Import keypair from Profile settings,  Recover account via seedphrase, Importing new keypair, Keycard backup flow
2. Regression testing of keycard flows
author_association: CONTRIBUTOR