Compare commits

...
Author SHA1 Message Date
Mohsen 21ebd4d930 feat_: add support for xcode 16.1 2024-12-10 13:23:26 -03:00
Brian Sztamfater 40c36c30e6 feat(wallet): simplify send flow ux
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-12-09 23:43:44 -03:00
Ulises ManuelandYevheniia Berdnyk e85bc539d3 feat(onboarding): Remove color and username steps (#21715)
* Remove password disclaimer, update copy and simplify implementation

* Remove non-existing wallet event

* Skip profile configuration for the "create profile" onboarding flow

* Include flow for new profiles when another profile already exists

* Add explanation about temp username

* Point 20 to a constant

* Simplify create profile password screen

* Fix password creation screen jump on mount

* e2e: updated tests

* Completely remove the "create profile" onboarding screen

* e2e: updated tests with recovering user

---------

Co-authored-by: Yevheniia Berdnyk <ie.berdnyk@gmail.com>
2024-12-09 13:55:47 -06:00
Mohamed Javid c26d298573 fix_: slider reset in standard authentication (#21779)
This commit resets the slider-button after the completion of slider in standard authentication.


Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-12-10 00:56:57 +05:30
Mohamed Javid cdde36ca28 fix(wallet)_: logic for calculating zero balance (#21777)
This commit fixes logic to calculate whether the user has zero balance in all non-watched accounts.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-12-10 00:21:00 +05:30
Mohamed Javid 3c3ef20769 fix(wallet)_: tx data cleanup after send or bridge tx (#21770)
This commit fixes the token/networks going to zero/disabled in the "Bridge to" screen when the user navigates back from the input screen.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-12-09 20:02:48 +05:30
Alexander 2150f6db48 Bump release to 2.32.0 (#21773) 2024-12-09 12:26:51 +01:00
Sean Hagstrom 10101a23e6 Feature: add "close community" action to opened/spectated communities (#21687)
This change adds a new action to the action-drawer for opened communities. Now opened communities can be closed by users by pressing on the "Close community" action. Note, that this action will only be available to communities displayed in communities list under the opened tab.
2024-12-06 09:50:40 -08:00
Brian Sztamfater f75fff9dd8 fix_: sometimes balances are not updated until relogin (#21748)
https://github.com/status-im/status-go/compare/a61f28bf...5371520f

Signed-off-by: Brian Sztamfater <brian@status.im>
2024-12-06 13:32:05 -03:00
56 changed files with 1067 additions and 1277 deletions
+1 -1
View File
@@ -1 +1 @@
2.30.0
2.32.0
+15 -12
View File
@@ -23,15 +23,18 @@ PODS:
- secp256k1
- SSZipArchive
- libevent (2.1.12)
- libwebp (1.2.4):
- libwebp/demux (= 1.2.4)
- libwebp/mux (= 1.2.4)
- libwebp/webp (= 1.2.4)
- libwebp/demux (1.2.4):
- libwebp (1.3.2):
- libwebp/demux (= 1.3.2)
- libwebp/mux (= 1.3.2)
- libwebp/sharpyuv (= 1.3.2)
- libwebp/webp (= 1.3.2)
- libwebp/demux (1.3.2):
- libwebp/webp
- libwebp/mux (1.2.4):
- libwebp/mux (1.3.2):
- libwebp/demux
- libwebp/webp (1.2.4)
- libwebp/sharpyuv (1.3.2)
- libwebp/webp (1.3.2):
- libwebp/sharpyuv
- RCT-Folly (2022.05.16.00):
- boost
- DoubleConversion
@@ -1189,7 +1192,7 @@ PODS:
- secp256k1 (0.1.6)
- SocketRocket (0.6.1)
- SSZipArchive (2.4.3)
- TOCropViewController (2.6.1)
- TOCropViewController (2.7.4)
- Yoga (1.14.0)
DEPENDENCIES:
@@ -1489,7 +1492,7 @@ CHECKOUT OPTIONS:
:git: https://github.com/status-im/Keycard.swift.git
:tag: 3.1.1
secp256k1:
:commit: 46a1fa30d9b8babeae85ff519050f42394ab5fcc
:commit: 4ab977cc2b2d7319be858bcb30a5d189bb149884
:git: https://github.com/status-im/secp256k1.swift.git
:submodules: true
@@ -1506,7 +1509,7 @@ SPEC CHECKSUMS:
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
Keycard: 3bd24405b9a875b3b3cf1ab1be69b5c30c5f28bc
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: 2544c0f1081a5fa12e108bb8cb40e5f4581ccd87
RCTTypeSafety: 50efabe2b115c11ed03fbf3fd79e2f163ddb5d7c
@@ -1590,9 +1593,9 @@ SPEC CHECKSUMS:
secp256k1: f61d67e6fdcb85fd727acf1bf35ace6036db540c
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
Yoga: a716eea57d0d3430219c0a5a233e1e93ee931eb7
PODFILE CHECKSUM: 81a66b45adddb083171ade882c343deb4faf2a8b
PODFILE CHECKSUM: 56d9f695324306d6db368d1172d211dfab55b7cb
COCOAPODS: 1.15.2
+1 -1
View File
@@ -51,7 +51,7 @@ in {
yarn = super.yarn.override { nodejs = super.nodejs_20; };
openjdk = super.openjdk17_headless;
xcodeWrapper = callPackage ./pkgs/xcodeenv/compose-xcodewrapper.nix { } {
versions = ["15.1" "15.2" "15.3" "15.4"];
versions = ["15.1" "15.2" "15.3" "15.4" "16.0" "16.1"];
};
go = super.go_1_21;
clang = super.clang_15;
@@ -61,14 +61,17 @@
(defn view
[{:keys [header footer customization-color footer-container-padding header-container-style
content-container-style gradient-cover? keyboard-should-persist-taps shell-overlay?
blur-options content-avoid-keyboard? automatically-adjust-keyboard-insets]
blur-options content-avoid-keyboard? automatically-adjust-keyboard-insets
;; Note: Provide `initial-header-height` to avoid a jump due to the on-layout 1-frame
;; delay. Revisit this on RN 0.76 since the on-layout delay has been fixed.
initial-header-height]
:or {footer-container-padding (safe-area/get-top)}}
& children]
(reagent/with-let [scroll-view-ref (atom nil)
set-scroll-ref #(reset! scroll-view-ref %)
window-height (:height (rn/get-window))
footer-container-height (reagent/atom 0)
header-height (reagent/atom 0)
header-height (reagent/atom nil)
content-container-height (reagent/atom 0)
content-scroll-y (reagent/atom 0)
keyboard-height (reagent/atom 0)
@@ -112,7 +115,7 @@
{:style
(if content-avoid-keyboard?
(style/content-keyboard-avoiding-view
{:top @header-height
{:top (or @header-height initial-header-height)
:bottom (if keyboard-shown?
@footer-container-height
(+ bottom-safe-area @footer-container-height))})
@@ -15,18 +15,18 @@
auth-method (rf/sub [:auth-method])
biometric-auth? (= auth-method constants/auth-method-biometric)
on-complete (rn/use-callback
(fn [reset]
(rf/dispatch [:standard-auth/authorize
{:on-close (fn [success?]
(js/setTimeout #(reset success?) 200))
:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
:keycard-supported? keycard-supported?
:biometric-auth? biometric-auth?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label}]))
(fn [reset-slider-fn]
(js/setTimeout #(reset-slider-fn false) 500)
(rf/dispatch
[:standard-auth/authorize
{:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
:keycard-supported? keycard-supported?
:biometric-auth? biometric-auth?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label}]))
(vec (conj dependencies on-auth-success on-auth-fail)))
biometric-type (rf/sub [:biometrics/supported-type])]
[quo/slide-button
@@ -28,9 +28,10 @@
:long-enough?
:short-enough?)))))
(def password-tip? (set constants/password-tips))
(defn strength
[validations]
(->> (select-keys validations constants/password-tips)
(vals)
(filter true?)
count))
(->> validations
(filter #(and (password-tip? (key %)) (val %)))
(count)))
@@ -52,8 +52,8 @@
{:eventName "navigation"
:platform platform-os
:appVersion app-version
:eventValue {:viewId "onboarding.create-profile-info"}}}]
(tracking/track-view-id-event :screen/onboarding.create-profile)))
:eventValue {:viewId "onboarding.create-profile-password"}}}]
(tracking/track-view-id-event :screen/onboarding.create-profile-password)))
(is (= [] (tracking/track-view-id-event :unknown-stack)))))
(deftest tracked-event-test
@@ -130,4 +130,12 @@
:role-permissions? true}
:profile/test-networks-enabled? false})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(h/is-truthy (h/get-by-translation-text :t/unmute-community))))
(h/is-truthy (h/get-by-translation-text :t/unmute-community)))
(h/test "opened community"
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined false
:spectated true}
:profile/test-networks-enabled? false})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(h/is-truthy (h/get-by-translation-text :t/close-community))))
@@ -124,6 +124,14 @@
:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [leave-menu/leave-sheet id color])}])})
(defn close-community
[id]
{:icon :i/close-circle
:label (i18n/label :t/close-community)
:accessibility-label :close-community
:danger? true
:on-press #(rf/dispatch [:communities/leave id])})
(defn cancel-request-to-join
[id request-id]
{:icon :i/block
@@ -135,7 +143,7 @@
request-id])}])})
(defn not-joined-options
[id token-gated? intro-message test-networks-enabled?]
[{:keys [id join-pending? spectated? token-gated? intro-message test-networks-enabled?]}]
(let [common [(show-qr id) (share-community id)]
specific (cond
(and token-gated? (not test-networks-enabled?))
@@ -146,19 +154,22 @@
(not token-gated?)
[(view-members id) (view-rules id intro-message) (invite-contacts id)])]
[(concat specific common)]))
[(concat specific
common
(when (and spectated? (not join-pending?))
[(assoc (close-community id) :add-divider? true)]))]))
(defn join-request-sent-options
[id token-gated? request-id intro-message test-networks-enabled?]
[(conj (first (not-joined-options id token-gated? intro-message test-networks-enabled?))
[{:keys [id request-id] :as config}]
[(conj (first (not-joined-options config))
(assoc (cancel-request-to-join id request-id) :add-divider? true))])
(defn banned-options
[id token-gated? intro-message test-networks-enabled?]
(not-joined-options id token-gated? intro-message test-networks-enabled?))
[config]
(not-joined-options config))
(defn joined-options
[id token-gated? muted? muted-till color intro-message]
[{:keys [id token-gated? muted? muted-till color intro-message]}]
[[(view-members id)
(view-rules id intro-message)
(when token-gated? (view-token-gating id))
@@ -173,7 +184,7 @@
[(assoc (leave-community id color) :add-divider? true)]])
(defn owner-options
[id token-gated? muted? muted-till intro-message]
[{:keys [id token-gated? muted? muted-till intro-message]}]
[[(view-members id)
(view-rules id intro-message)
(when token-gated? (view-token-gating id))
@@ -186,22 +197,29 @@
(defn get-context-drawers
[{:keys [id]}]
(let [{:keys [role-permissions? admin joined
muted banList muted-till color
intro-message]} (rf/sub [:communities/community id])
request-id (rf/sub [:communities/my-pending-request-to-join id])
test-networks? (rf/sub [:profile/test-networks-enabled?])]
(let [{:keys
[id admin joined
banList color muted muted-till
spectated role-permissions?
intro-message]} (rf/sub [:communities/community id])
request-id (rf/sub [:communities/my-pending-request-to-join id])
test-networks? (rf/sub [:profile/test-networks-enabled?])
config {:id id
:color color
:muted-till muted-till
:muted? muted
:spectated? spectated
:token-gated? role-permissions?
:intro-message intro-message
:test-networks? test-networks?
:join-pending? (boolean request-id)
:request-id request-id}]
(cond
admin
(owner-options id role-permissions? muted muted-till intro-message)
joined
(joined-options id role-permissions? muted muted-till color intro-message)
request-id
(join-request-sent-options id role-permissions? request-id intro-message test-networks?)
banList
(banned-options id role-permissions? intro-message test-networks?)
:else
(not-joined-options id role-permissions? intro-message test-networks?))))
admin (owner-options config)
joined (joined-options config)
request-id (join-request-sent-options config)
banList (banned-options config)
:else (not-joined-options config))))
(defn community-options-bottom-sheet
[id]
@@ -1,6 +1,7 @@
(ns status-im.contexts.onboarding.create-password.style
(:require
[quo.foundations.colors :as colors]))
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
(def heading {:margin-bottom 20})
(def heading-subtitle {:color colors/white})
@@ -21,6 +22,18 @@
:padding-top 12
:padding-horizontal 20})
(def disclaimer-container {:padding-horizontal 20})
(def footer-container {:padding-bottom 12})
(def footer-button-container {:margin-top 20 :padding-horizontal 20})
(def blur-options
{:blur-amount 34
:blur-radius 20
:blur-type :transparent
:overlay-color :transparent
:background-color (if platform/android?
colors/neutral-100
colors/neutral-80-opa-1-blur)
:padding-vertical 0
:padding-horizontal 0})
(def page-nav-height 56)
@@ -1,9 +1,7 @@
(ns status-im.contexts.onboarding.create-password.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[status-im.common.floating-button-page.view :as floating-button]
[status-im.common.password-with-hint.view :as password-with-hint]
@@ -29,22 +27,37 @@
(i18n/label :t/password-creation-subtitle)]])
(defn password-inputs
[{:keys [passwords-match? on-change-password on-change-repeat-password on-input-focus
password-long-enough? password-short-enough? empty-password? show-password-validation?
on-blur-repeat-password]}]
(let [hint-1-status (if password-long-enough? :success :default)
hint-2-status (if passwords-match? :success :error)
hint-2-text (if passwords-match?
(i18n/label :t/password-creation-match)
(i18n/label :t/password-creation-dont-match))
error? (and show-password-validation?
(not passwords-match?)
(not empty-password?))]
[{:keys [set-password set-repeat-password same-password-length? same-passwords?
password-long-enough? password-short-enough? non-empty-password?]}]
(let [[show-validation?
set-show-validation?] (rn/use-state false)
on-change-password (rn/use-callback
(fn [new-value]
(set-password new-value)
(when same-password-length?
(set-show-validation? true)))
[same-password-length?])
on-change-repeat-password (rn/use-callback
(fn [new-value]
(set-repeat-password new-value)
(when same-password-length?
(set-show-validation? true)))
[same-password-length?])
on-blur-repeat-password (rn/use-callback
#(set-show-validation? non-empty-password?)
[non-empty-password?])
hint-1-status (if password-long-enough? :success :default)
hint-2-status (if same-passwords? :success :error)
hint-2-text (if same-passwords?
(i18n/label :t/password-creation-match)
(i18n/label :t/password-creation-dont-match))
error? (and show-validation?
(not same-passwords?)
non-empty-password?)]
[:<>
[password-with-hint/view
{:hint (if (not password-short-enough?)
{:text (i18n/label
:t/password-creation-max-length-hint)
{:text (i18n/label :t/password-creation-max-length-hint)
:status :error
:shown? true}
{:text (i18n/label :t/password-creation-hint)
@@ -52,55 +65,50 @@
:shown? true})
:placeholder (i18n/label :t/password-creation-placeholder-1)
:on-change-text on-change-password
:on-focus on-input-focus
:auto-focus true}]
[rn/view {:style style/space-between-inputs}]
[password-with-hint/view
{:hint {:text hint-2-text
:status hint-2-status
:shown? (and (not empty-password?)
show-password-validation?)}
:shown? (and non-empty-password? show-validation?)}
:error? error?
:placeholder (i18n/label :t/password-creation-placeholder-2)
:on-change-text on-change-repeat-password
:on-focus on-input-focus
:on-blur on-blur-repeat-password}]]))
(defn help
[{{:keys [lower-case? upper-case? numbers? symbols?]} :validations
password-strength :password-strength}]
[rn/view
[quo/strength-divider {:type (constants/strength-status password-strength :info)}
(i18n/label :t/password-creation-tips-title)]
[rn/view {:style style/password-tips}
[quo/tips {:completed? lower-case?}
(i18n/label :t/password-creation-tips-1)]
[quo/tips {:completed? upper-case?}
(i18n/label :t/password-creation-tips-2)]
[quo/tips {:completed? numbers?}
(i18n/label :t/password-creation-tips-3)]
[quo/tips {:completed? symbols?}
(i18n/label :t/password-creation-tips-4)]]])
[{:keys [lower-case? upper-case? numbers? symbols?] :as validations}]
(let [password-strength (constants/strength-status (password/strength validations) :info)]
[rn/view
[quo/strength-divider {:type password-strength}
(i18n/label :t/password-creation-tips-title)]
[rn/view {:style style/password-tips}
[quo/tips {:completed? lower-case?}
(i18n/label :t/password-creation-tips-1)]
[quo/tips {:completed? upper-case?}
(i18n/label :t/password-creation-tips-2)]
[quo/tips {:completed? numbers?}
(i18n/label :t/password-creation-tips-3)]
[quo/tips {:completed? symbols?}
(i18n/label :t/password-creation-tips-4)]]]))
(defn- use-password-checks
[password]
(rn/use-memo
(fn []
(let [{:keys [long-enough? short-enough?]
:as validations} (password/validate password)]
{:password-long-enough? long-enough?
:password-short-enough? short-enough?
:password-validations validations
:password-strength (password/strength validations)
:empty-password? (empty? password)}))
(-> password
(password/validate)
(assoc :non-empty? (seq password))))
[password]))
(defn- use-repeat-password-checks
[password repeat-password]
(rn/use-memo
(fn []
{:same-password-length? (and (seq password) (= (count password) (count repeat-password)))
:same-passwords? (and (seq password) (= password repeat-password))})
{:same-password-length? (and (seq password)
(= (count password) (count repeat-password)))
:same-passwords? (and (seq password)
(= password repeat-password))})
[password repeat-password]))
(defn create-password-doc
@@ -119,102 +127,70 @@
{:content create-password-doc
:shell? true}]))
(defn- navigate-back
[]
(rf/dispatch [:navigate-back]))
(defn- navigate-back [] (rf/dispatch [:navigate-back]))
(defn- page-nav
[]
(let [{:keys [top]} (safe-area/get-insets)]
[quo/page-nav
{:margin-top top
:background :blur
:icon-name :i/arrow-left
:on-press navigate-back
:right-side [{:icon-name :i/info
:on-press on-press-info}]}]))
[top]
[quo/page-nav
{:margin-top top
:background :blur
:icon-name :i/arrow-left
:on-press navigate-back
:right-side [{:icon-name :i/info
:on-press on-press-info}]}])
(defn- help-and-confirm-button
[{:keys [password-validations same-passwords? on-submit]}]
(let [{customization-color :color} (rf/sub [:onboarding/profile])
all-requirements-met? (and (:non-empty? password-validations)
(:long-enough? password-validations)
(:short-enough? password-validations)
same-passwords?)]
[rn/view {:style style/footer-container}
[help password-validations]
[quo/button
{:container-style style/footer-button-container
:disabled? (not all-requirements-met?)
:customization-color customization-color
:on-press on-submit}
(i18n/label :t/password-creation-confirm)]]))
(defn- on-confirm-password
[password]
(rf/dispatch [:onboarding/password-set (security/mask-data password)]))
(defn create-password
[]
(let [[password set-password] (rn/use-state "")
[repeat-password set-repeat-password] (rn/use-state "")
[accepts-disclaimer? set-accepts-disclaimer?] (rn/use-state false)
[focused-input set-focused-input] (rn/use-state nil)
[show-password-validation?
set-show-password-validation?] (rn/use-state false)
{user-color :color} (rf/sub [:onboarding/profile])
{:keys [password-long-enough?
password-short-enough?
password-validations password-strength
empty-password?]} (use-password-checks password)
{:keys [same-password-length? same-passwords?]} (use-repeat-password-checks password
repeat-password)
meet-requirements? (and (not empty-password?)
password-long-enough?
password-short-enough?
same-passwords?
accepts-disclaimer?)]
(let [[password set-password] (rn/use-state "")
[repeat-password
set-repeat-password] (rn/use-state "")
{:keys [long-enough? short-enough? non-empty?]
:as password-validations} (use-password-checks password)
{:keys [same-password-length?
same-passwords?]} (use-repeat-password-checks password repeat-password)
on-submit (rn/use-callback
#(on-confirm-password password)
[password])
top (safe-area/get-top)]
[floating-button/view
{:header [page-nav]
:keyboard-should-persist-taps :handled
:content-avoid-keyboard? true
{:header [page-nav top]
:initial-header-height (+ style/page-nav-height top)
:keyboard-should-persist-taps :handled
:content-avoid-keyboard? true
:automatically-adjust-keyboard-insets true
:blur-options
{:blur-amount 34
:blur-radius 20
:blur-type :transparent
:overlay-color :transparent
:background-color (if platform/android? colors/neutral-100 colors/neutral-80-opa-1-blur)
:padding-vertical 0
:padding-horizontal 0}
:footer-container-padding 0
:footer
[rn/view
{:style style/footer-container}
(when same-passwords?
[rn/view {:style style/disclaimer-container}
[quo/disclaimer
{:blur? true
:on-change (partial set-accepts-disclaimer? not)
:checked? accepts-disclaimer?
:customization-color user-color}
(i18n/label :t/password-creation-disclaimer)]])
(when (and (= focused-input :password) (not same-passwords?))
[help
{:validations password-validations
:password-strength password-strength}])
[quo/button
{:container-style style/footer-button-container
:disabled? (not meet-requirements?)
:customization-color user-color
:on-press #(rf/dispatch
[:onboarding/password-set
(security/mask-data password)])}
(i18n/label :t/password-creation-confirm)]]}
:blur-options style/blur-options
:footer-container-padding 0
:footer [help-and-confirm-button
{:password-validations password-validations
:same-passwords? same-passwords?
:on-submit on-submit}]}
[rn/view {:style style/form-container}
[header]
[password-inputs
{:password-long-enough? password-long-enough?
:password-short-enough? password-short-enough?
:passwords-match? same-passwords?
:empty-password? empty-password?
:show-password-validation? show-password-validation?
:on-input-focus #(set-focused-input :password)
:on-change-password (fn [new-value]
(set-password new-value)
(when same-password-length?
(set-show-password-validation? true)))
:on-change-repeat-password (fn [new-value]
(set-repeat-password new-value)
(when same-password-length?
(set-show-password-validation? true)))
:on-blur-repeat-password #(if empty-password?
(set-show-password-validation? false)
(set-show-password-validation? true))}]]]))
{:password-long-enough? long-enough?
:password-short-enough? short-enough?
:non-empty-password? non-empty?
:same-passwords? same-passwords?
:same-password-length? same-password-length?
:set-password set-password
:set-repeat-password set-repeat-password}]]]))
@@ -1,60 +0,0 @@
(ns status-im.contexts.onboarding.create-profile.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
(def continue-button
{:width "100%"
:margin-left :auto
:margin-top (if platform/android? :auto 0)
:margin-right :auto})
(def button-container
{:width "100%"
:padding-left 20
:padding-right 20
:padding-top 12
:align-self :flex-end
:height 64})
(defn view-button-container
[keyboard-shown?]
(merge button-container
(if platform/ios?
{:margin-bottom (if keyboard-shown? 0 34)}
{:margin-bottom (if keyboard-shown? 12 34)})))
(def blur-button-container
(merge button-container
(when platform/android? {:padding-bottom 12})))
(def page-container
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0
:z-index 100})
(def info-message
{:margin-top 8})
(def title
{:color colors/white
:margin-top 12
:margin-bottom 18})
(def color-title
{:color colors/white-70-blur
:margin-top 20
:margin-bottom 16})
(def content-container
{:padding-horizontal 20})
(def input-container
{:align-items :flex-start})
(def profile-input-container
{:flex-direction :row
:justify-content :center})
@@ -1,210 +0,0 @@
(ns status-im.contexts.onboarding.create-profile.view
(:require
[clojure.string :as string]
[oops.core :as oops]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.hooks :as hooks]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.common.avatar-picture-picker.view :as profile-picture-picker]
[status-im.common.validation.profile :as profile-validator]
[status-im.constants :as c]
[status-im.contexts.onboarding.create-profile.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.responsiveness :as responsiveness]))
(def scroll-view-height (reagent/atom 0))
(def content-container-height (reagent/atom 0))
(defn show-button-background
[keyboard-height keyboard-shown content-scroll-y]
(let [button-container-height 64
keyboard-view-height (+ keyboard-height button-container-height)]
(when keyboard-shown
(cond
platform/android?
(< (- @scroll-view-height button-container-height) @content-container-height)
platform/ios?
(< (- @scroll-view-height keyboard-view-height) (- @content-container-height content-scroll-y))
:else
false))))
(defn button-container
[show-keyboard? keyboard-shown show-background? keyboard-height children]
(let [height (reagent/atom 0)]
(reset! height (if show-keyboard? (if keyboard-shown keyboard-height 0) 0))
[rn/view {:style {:margin-top :auto}}
(cond
(and (> @height 0) show-background?)
[quo/blur
(when keyboard-shown
{:blur-amount 34
:blur-type :transparent
:overlay-color :transparent
:background-color (if platform/android? colors/neutral-100 colors/neutral-80-opa-1-blur)
:style style/blur-button-container})
children]
(and (> @height 0) (not show-background?))
[rn/view {:style (style/view-button-container true)}
children]
(not show-keyboard?)
[rn/view {:style (style/view-button-container false)}
children])]))
(defn- page
[{:keys [onboarding-profile-data navigation-bar-top]}]
(reagent/with-let [show-keyboard? (reagent/atom false)
content-scroll-y (reagent/atom 0)
show-listener (oops/ocall rn/keyboard
"addListener"
(if platform/android?
"keyboardDidShow"
"keyboardWillShow")
#(reset! show-keyboard? true))
hide-listener (oops/ocall rn/keyboard
"addListener"
(if platform/android?
"keyboardDidHide"
"keyboardWillHide")
#(reset! show-keyboard? false))
{:keys [image-path display-name color]} onboarding-profile-data
full-name (reagent/atom display-name)
validation-msg (reagent/atom
(profile-validator/validation-name
@full-name))
on-change-text (fn [s]
(reset! validation-msg
(profile-validator/validation-name
s))
(reset! full-name (string/trim s)))
custom-color (reagent/atom (or color
c/profile-default-color))
profile-pic (reagent/atom image-path)
on-change-profile-pic #(reset! profile-pic %)
on-change #(reset! custom-color %)]
(let [name-too-short? (profile-validator/name-too-short? @full-name)
valid-name? (and (not @validation-msg) (not name-too-short?))
info-message (if @validation-msg
@validation-msg
(i18n/label :t/minimum-characters
{:min-chars
profile-validator/min-length}))
info-type (cond @validation-msg :error
name-too-short? :default
:else :success)
{:keys [keyboard-shown keyboard-height]} (hooks/use-keyboard)
show-background? (show-button-background keyboard-height
keyboard-shown
@content-scroll-y)
{window-width :width} (rn/get-window)]
[rn/view {:style style/page-container}
[quo/page-nav
{:margin-top navigation-bar-top
:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])}]
[rn/scroll-view
{:on-layout (fn [event]
(let [height (oops/oget event "nativeEvent.layout.height")]
(reset! scroll-view-height height)
(reset! content-scroll-y 0)))
:on-scroll (fn [event]
(let [y (oops/oget event "nativeEvent.contentOffset.y")]
(reset! content-scroll-y y)))
:scroll-event-throttle 64
:content-container-style {:flexGrow 1}}
[rn/view
{:on-layout (fn [event]
(let [height (oops/oget event "nativeEvent.layout.height")]
(reset! content-container-height height)))}
[rn/view
{:style style/content-container}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title} (i18n/label :t/create-profile)]
[rn/view
{:style style/input-container}
[rn/view
{:style style/profile-input-container}
[quo/profile-input
{:customization-color @custom-color
:placeholder (i18n/label :t/your-name)
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[profile-picture-picker/view
{:on-result on-change-profile-pic
:has-picture? false}])
:shell? true}]))
:image-picker-props {:profile-picture @profile-pic
:full-name (if (seq @full-name)
@full-name
(i18n/label :t/your-name))
:customization-color @custom-color}
:title-input-props {:default-value display-name
:auto-focus true
:max-length c/profile-name-max-length
:on-change-text on-change-text}}]]
[quo/info-message
{:status info-type
:size :default
:icon (if valid-name? :i/positive-state :i/info)
:color (when (= :default info-type) colors/white-70-blur)
:container-style style/info-message}
info-message]
[quo/text
{:size :paragraph-2
:weight :medium
:style style/color-title}
(i18n/label :t/accent-colour)]]]
[quo/color-picker
{:blur? true
:default-selected :blue
:on-change on-change
:window-width window-width
:container-style {:padding-left (responsiveness/iphone-11-Pro-20-pixel-from-width
window-width)}}]]]
[rn/keyboard-avoiding-view
{:style {:position :absolute
:top 0
:bottom 0
:left 0
:right 0}
:pointer-events :box-none}
[button-container @show-keyboard? keyboard-shown show-background? keyboard-height
[quo/button
{:accessibility-label :submit-create-profile-button
:type :primary
:customization-color @custom-color
:on-press (fn []
(rf/dispatch [:onboarding/profile-data-set
{:image-path @profile-pic
:display-name @full-name
:color @custom-color}]))
:container-style style/continue-button
:disabled? (or (not valid-name?) (not (seq @full-name)))}
(i18n/label :t/continue)]]]])
(finally
(oops/ocall show-listener "remove")
(oops/ocall hide-listener "remove"))))
(defn create-profile
[]
(let [{:keys [top]} (safe-area/get-insets)
onboarding-profile-data (rf/sub [:onboarding/profile])]
[page
{:navigation-bar-top top
:onboarding-profile-data onboarding-profile-data}]))
+83 -71
View File
@@ -1,5 +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]
@@ -111,63 +112,57 @@
(rf/reg-event-fx
:onboarding/password-set
(fn [{:keys [db]} [masked-password]]
(let [biometric-supported-type (get-in db [:biometrics :supported-type])]
(let [biometric-supported-type (get-in db [:biometrics :supported-type])
from-screen (get db
:onboarding/navigated-to-enter-seed-phrase-from-screen
:screen/onboarding.new-to-status)]
{:db (-> db
(assoc-in [:onboarding/profile :password] masked-password)
(assoc-in [:onboarding/profile :auth-method] constants/auth-method-password))
:fx [[:dispatch
(if biometric-supported-type
[:navigate-to-within-stack
[:screen/onboarding.enable-biometrics
(get db
:onboarding/navigated-to-enter-seed-phrase-from-screen
:screen/onboarding.new-to-status)]]
[:navigate-to-within-stack [:screen/onboarding.enable-biometrics from-screen]]
[:onboarding/create-account-and-login])]]})))
(rf/defn navigate-to-enable-biometrics
{:events [:onboarding/navigate-to-enable-biometrics]}
[{:keys [db]}]
(let [supported-type (get-in db [:biometrics :supported-type])]
{:dispatch (if supported-type
[:open-modal :screen/onboarding.enable-biometrics]
[:open-modal :screen/onboarding.enable-notifications])}))
(rf/reg-event-fx
:onboarding/navigate-to-enable-biometrics
(fn [{:keys [db]}]
(let [supported-type (get-in db [:biometrics :supported-type])]
{:dispatch (if supported-type
[:open-modal :screen/onboarding.enable-biometrics]
[:open-modal :screen/onboarding.enable-notifications])})))
(rf/defn seed-phrase-validated
{:events [:onboarding/seed-phrase-validated]}
[{:keys [db]} seed-phrase key-uid]
(let [syncing-account-recovered? (and (seq (:syncing/key-uid db))
(= (:syncing/key-uid db) key-uid))
next-screen (if syncing-account-recovered?
:screen/onboarding.create-profile-password
:screen/onboarding.create-profile)]
(if (contains? (:profile/profiles-overview db) key-uid)
{: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])
(re-frame/dispatch
[:profile/profile-selected key-uid]))
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}}
{:db (-> db
(assoc-in [:onboarding/profile :seed-phrase] seed-phrase)
(assoc-in [:onboarding/profile :key-uid] key-uid)
(assoc-in [:onboarding/profile :color] constants/profile-default-color))
:fx [[:dispatch
[:navigate-to-within-stack
[next-screen
(get db
:onboarding/navigated-to-enter-seed-phrase-from-screen
:screen/onboarding.new-to-status)]]]]})))
(rf/reg-event-fx
:onboarding/seed-phrase-validated
(fn [{:keys [db]} [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.new-to-status)]
(if (contains? (:profile/profiles-overview db) 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])
(re-frame/dispatch [:profile/profile-selected key-uid]))
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}]]}
{:db (-> db
(assoc-in [:onboarding/profile :seed-phrase] seed-phrase)
(assoc-in [:onboarding/profile :key-uid] key-uid)
(assoc-in [:onboarding/profile :color] constants/profile-default-color))
:fx [[:dispatch [:navigate-to-within-stack [next-screen from-screen]]]]}))))
(rf/defn navigate-to-create-profile
{:events [:onboarding/navigate-to-create-profile]}
[{:keys [db]}]
;; Restart the flow
{:db (dissoc db :onboarding/profile)
:dispatch [:navigate-to-within-stack
[:screen/onboarding.create-profile :screen/onboarding.new-to-status]]})
(rf/reg-event-fx
:onboarding/navigate-to-create-profile
(fn [{:keys [db]}]
{:db (-> db
(assoc-in [:onboarding/profile :color] (rand-nth colors/account-colors))
(update :onboarding/profile dissoc :image-path))
:fx [[:dispatch
[:navigate-to-within-stack
[:screen/onboarding.create-profile-password :screen/onboarding.new-to-status]]]]}))
(rf/reg-event-fx :onboarding/navigate-to-sign-in-by-syncing
(fn [{:keys [db]}]
@@ -180,26 +175,43 @@
(fn [{:keys [db]} [auth-method]]
{:db (assoc db :auth-method auth-method)}))
(rf/defn onboarding-new-account-finalize-setup
{:events [:onboarding/finalize-setup]}
[{:keys [db]}]
(let [masked-password (get-in db [:onboarding/profile :password])
key-uid (get-in db [:profile/profile :key-uid])
syncing? (get-in db [:onboarding/profile :syncing?])
auth-method (get-in db [:onboarding/profile :auth-method])
biometric-enabled? (= auth-method constants/auth-method-biometric)]
(cond-> {:db (assoc db :onboarding/generated-keys? true)}
biometric-enabled?
(assoc :keychain/save-password-and-auth-method
{:key-uid key-uid
:masked-password (if syncing?
masked-password
(security/hash-masked-password masked-password))
:on-success (fn []
(rf/dispatch [:onboarding/set-auth-method auth-method])
(when syncing?
(rf/dispatch
[:onboarding/navigate-to-enable-notifications-from-syncing])))
:on-error #(log/error "failed to save biometrics"
{:key-uid key-uid
:error %})}))))
(def ^:const temp-display-name
"While creating a profile, we cannot use an empty string; this value works as a
placeholder that will be updated later once the compressed key exists. See
`status-im.contexts.profile.edit.name.events/get-default-display-name` for more details."
"temporal username")
(rf/reg-event-fx
:onboarding/use-temporary-display-name
(fn [{:keys [db]} [temporary-display-name?]]
{:db (assoc db
:onboarding/profile
{:temporary-display-name? temporary-display-name?
:display-name (if temporary-display-name?
temp-display-name
"")})}))
(rf/reg-event-fx
:onboarding/finalize-setup
(fn [{db :db}]
(let [{:keys [password syncing? auth-method
temporary-display-name?]} (:onboarding/profile db)
{:keys [key-uid] :as profile} (:profile/profile db)
biometric-enabled? (= auth-method constants/auth-method-biometric)]
{:db (assoc db :onboarding/generated-keys? true)
:fx [(when temporary-display-name?
[:dispatch [:profile/set-default-profile-name profile]])
(when biometric-enabled?
[:keychain/save-password-and-auth-method
{:key-uid key-uid
:masked-password (if syncing?
password
(security/hash-masked-password password))
:on-success (fn []
(rf/dispatch [:onboarding/set-auth-method auth-method])
(when syncing?
(rf/dispatch
[:onboarding/navigate-to-enable-notifications-from-syncing])))
:on-error #(log/error "failed to save biometrics"
{:key-uid key-uid
:error %})}])]})))
@@ -52,6 +52,7 @@
[]
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
(blur-show-fn))
(rf/dispatch [:onboarding/use-temporary-display-name false])
(rf/dispatch [:open-modal
:screen/onboarding.share-usage
{:next-screen :screen/onboarding.sync-or-recover-profile}]))
@@ -60,6 +61,7 @@
[]
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
(blur-show-fn))
(rf/dispatch [:onboarding/use-temporary-display-name true])
(rf/dispatch [:open-modal :screen/onboarding.share-usage
{:next-screen :screen/onboarding.new-to-status}]))
@@ -1,5 +1,7 @@
(ns status-im.contexts.profile.edit.name.events
(:require [utils.i18n :as i18n]
(:require [clojure.string :as string]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/reg-event-fx :profile/edit-profile-name-success
@@ -20,3 +22,16 @@
:on-success [:profile/edit-profile-name-success]}]]]})
(rf/reg-event-fx :profile/edit-name edit-profile-name)
(defn display-name-from-compressed-key
[profile]
(-> profile :compressed-key (string/split #"zQ3") second (subs 0 constants/profile-name-max-length)))
(rf/reg-event-fx
:profile/set-default-profile-name
(fn [{db :db} [profile]]
(let [default-display-name (display-name-from-compressed-key profile)]
{:db (assoc-in db [:profile/profile :display-name] default-display-name)
:fx [[:json-rpc/call
[{:method "wakuext_setDisplayName"
:params [default-display-name]}]]]})))
@@ -152,15 +152,12 @@
(rf/reg-event-fx
:profile.login/login-node-signal
(fn [{{:onboarding/keys [recovered-account? new-account?] :as db} :db}
[{:keys [settings account ensUsernames error]}]]
(fn [{db :db} [{:keys [settings account ensUsernames error]}]]
(log/debug "[signals] node.login" "error" error)
(if error
{:db (update db :profile/login #(-> % (dissoc :processing) (assoc :error error)))}
{:db (dissoc db :profile/login)
:fx [(when (and new-account? (not recovered-account?))
[:dispatch-later [{:ms 1000 :dispatch [:wallet-legacy/set-initial-blocks-range]}]])
[:dispatch-later [{:ms 2000 :dispatch [:ens/update-usernames ensUsernames]}]]
:fx [[:dispatch-later [{:ms 2000 :dispatch [:ens/update-usernames ensUsernames]}]]
[:dispatch [:profile.login/login-existing-profile settings account]]]})))
(rf/reg-event-fx
@@ -47,6 +47,7 @@
[]
(when @push-animation-fn-atom
(@push-animation-fn-atom))
(rf/dispatch [:onboarding/use-temporary-display-name true])
(debounce/throttle-and-dispatch
[:open-modal :screen/onboarding.new-to-status]
1000))
@@ -55,6 +56,7 @@
[]
(when @push-animation-fn-atom
(@push-animation-fn-atom))
(rf/dispatch [:onboarding/use-temporary-display-name false])
(debounce/throttle-and-dispatch
[:open-modal :screen/onboarding.sync-or-recover-profile]
1000))
@@ -1,6 +1,5 @@
(ns status-im.contexts.wallet.bridge.bridge-to.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[quo.theme]
@@ -15,28 +14,34 @@
(defn- bridge-token-component
[]
(fn [{:keys [chain-id network-name]} {:keys [supported-networks] :as token}]
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])
balance (utils/calculate-total-token-balance token [chain-id])
crypto-value (utils/get-standard-crypto-format token balance prices-per-token)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
fiat-value)
token-available-on-network? (network-utils/token-available-on-network? supported-networks
chain-id)]
(fn [{:keys [chain-id network-name]} {:keys [supported-networks] :as token} selected-network]
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])
{selected-network-chain-id :chain-id} selected-network
balance (utils/calculate-total-token-balance token [chain-id])
crypto-value (utils/get-standard-crypto-format token
balance
prices-per-token)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
fiat-value)
token-available-on-network? (network-utils/token-available-on-network?
supported-networks
chain-id)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network (:network-name network))
:token-value (str crypto-value " " (:symbol token))
:fiat-value fiat-formatted
:state (if token-available-on-network? :default :disabled)
:state (if (and token-available-on-network? (not= chain-id selected-network-chain-id))
:default
:disabled)
:on-press #(rf/dispatch [:wallet/select-bridge-network
{:network-chain-id chain-id
:stack-id :screen/wallet.bridge-to}])}])))
@@ -46,6 +51,7 @@
(let [network-details (rf/sub [:wallet/network-details])
account (rf/sub [:wallet/current-viewing-account])
token (rf/sub [:wallet/wallet-send-token])
selected-network (rf/sub [:wallet/send-network])
token-symbol (:symbol token)
tokens (:tokens account)
mainnet (first network-details)
@@ -55,8 +61,7 @@
(assoc account-token
:networks (:networks token)
:supported-networks (:supported-networks token)))
bridge-to-title (i18n/label :t/bridge-to
{:name (string/upper-case (str token-symbol))})]
bridge-to-title (i18n/label :t/select-network-to-receive)]
(hot-reload/use-safe-unmount #(rf/dispatch [:wallet/clean-bridge-to-selection]))
@@ -68,11 +73,11 @@
:switcher-type :select-account}]
[quo/page-top {:title bridge-to-title}]
[rn/view style/content-container
[bridge-token-component (assoc mainnet :network-name :t/mainnet) account-token]]
[bridge-token-component (assoc mainnet :network-name :t/mainnet) account-token selected-network]]
[quo/divider-label (i18n/label :t/layer-2)]
[rn/flat-list
{:data layer-2-networks
:render-fn (fn [network]
[bridge-token-component network account-token])
[bridge-token-component network account-token selected-network])
:content-container-style style/content-container}]]))
@@ -1,9 +1,7 @@
(ns status-im.contexts.wallet.bridge.flow-config)
(def steps
[{:screen-id :screen/wallet.select-from
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
{:screen-id :screen/wallet.bridge-select-asset
[{:screen-id :screen/wallet.bridge-select-asset
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :token])))}
{:screen-id :screen/wallet.bridge-to
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :bridge-to-chain-id])))}
@@ -82,13 +82,13 @@
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
account-owns-token? (rf/sub [:wallet/current-account-owns-token token-symbol])
network-details (rf/sub [:wallet/network-details])
receive-token-symbol (if (= token-symbol "SNT") "ETH" "SNT")
token-owned? (if selected-account account-owns-token? (seq token-owners))
asset-to-receive (rf/sub [:wallet/token-by-symbol receive-token-symbol])
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?
:asset-to-receive asset-to-receive}
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
@@ -108,9 +108,13 @@
(action-send params entry-point))
(action-receive selected-account)
(when token-owned?
(action-swap params))
(action-swap (assoc params
:asset-to-receive
asset-to-receive)))
(when token-owned?
(action-bridge (assoc params
:network-details
network-details
:bridge-disabled?
(send-utils/bridge-disabled? token-symbol))))]))]]))
@@ -1,23 +1,11 @@
(ns status-im.contexts.wallet.common.wizard.events
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]
(:require [status-im.contexts.wallet.common.wizard.utils :as wizard-utils]
[utils.re-frame :as rf]))
(defn- wizard-find-next-screen
[db flow-id current-screen]
(let [flow-config (case flow-id
:wallet-send-flow wallet-send-flow/steps
:wallet-bridge-flow wallet-bridge-flow/steps
nil)]
(first (filter (fn [{:keys [skip-step? screen-id]}]
(and (not= screen-id current-screen)
(not (and (fn? skip-step?) (skip-step? db)))))
flow-config))))
(rf/reg-event-fx
:wallet/wizard-navigate-forward
(fn [{:keys [db]} [{:keys [current-screen flow-id start-flow?]}]]
(let [next-screen (wizard-find-next-screen db flow-id current-screen)]
(let [next-screen (wizard-utils/wizard-find-next-screen db flow-id current-screen)]
{:fx [[:dispatch
(if start-flow?
[:open-modal (:screen-id next-screen)]
@@ -0,0 +1,14 @@
(ns status-im.contexts.wallet.common.wizard.utils
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]))
(defn wizard-find-next-screen
[db flow-id current-screen]
(let [flow-config (case flow-id
:wallet-send-flow wallet-send-flow/steps
:wallet-bridge-flow wallet-bridge-flow/steps
nil)]
(first (filter (fn [{:keys [skip-step? screen-id]}]
(and (not= screen-id current-screen)
(not (and (fn? skip-step?) (skip-step? db)))))
flow-config))))
+53 -9
View File
@@ -10,11 +10,13 @@
[status-im.contexts.settings.wallet.effects]
[status-im.contexts.settings.wallet.events]
[status-im.contexts.wallet.common.activity-tab.events]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.external-links :as external-links]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.db :as db]
[status-im.contexts.wallet.item-types :as item-types]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[status-im.contexts.wallet.tokens.events]
[status-im.feature-flags :as ff]
[taoensso.timbre :as log]
@@ -215,7 +217,7 @@
{:db (assoc-in db [:wallet :ui :tokens-loading address] true)
:fx [[:json-rpc/call
[{:method "wallet_fetchOrGetCachedWalletBalances"
:params [[address]]
:params [[address] true]
:on-success [:wallet/store-wallet-token address]
:on-error [:wallet/get-wallet-token-for-account-failed address]}]]]}))
@@ -343,19 +345,61 @@
(rf/reg-event-fx :wallet/get-keypairs get-keypairs)
(rf/reg-event-fx :wallet/bridge-select-token
(fn [{:keys [db]} [{:keys [token token-symbol stack-id start-flow?]}]]
(let [missing-recipient? (-> db :wallet :ui :send :to-address nil?)
to-address (-> db :wallet :current-viewing-account-address)]
(fn [{:keys [db]}
[{:keys [token token-symbol stack-id network owners network-details start-flow?] :as params}]]
(let [{:keys [wallet]} db
unique-owner (when (= (count owners) 1)
(first owners))
token (if (and unique-owner (nil? token))
(let [token (utils/get-token-from-account db
token-symbol
unique-owner)]
(assoc token
:networks (network-utils/network-list-with-positive-balance
token
network-details)
:supported-networks (network-utils/network-list
token
network-details)
:total-balance (utils/calculate-total-token-balance token)))
token)
missing-recipient? (-> db :wallet :ui :send :to-address nil?)
to-address (or unique-owner
(-> db :wallet :current-viewing-account-address)
(utils/get-default-account (-> db :wallet :accounts)))
view-id (:view-id db)
root-screen? (or (= view-id :wallet-stack) (nil? view-id))
multi-account-balance? (-> (utils/get-accounts-with-token-balance (:accounts wallet)
token)
(count)
(> 1))]
{:db (cond-> db
:always (assoc-in [:wallet :ui :send :tx-type] :tx/bridge)
token (assoc-in [:wallet :ui :send :token] token)
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
network (assoc-in [:wallet :ui :send :network] network)
missing-recipient? (assoc-in [:wallet :ui :send :to-address] to-address))
:fx [[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]})))
:fx (if (and (not unique-owner) multi-account-balance? root-screen?)
[[:dispatch [:open-modal :screen/wallet.select-from]]]
(if (some? network)
[[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]
[[:dispatch [:wallet/switch-current-viewing-account to-address]]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:title (i18n/label :t/select-network-to-bridge-from)
:token-symbol (or token-symbol (:symbol token))
:source :bridge
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/bridge-select-token
(assoc params :network network)]))}])}]]]))})))
(rf/reg-event-fx :wallet/start-bridge
(fn [{:keys [db]}]
@@ -125,7 +125,7 @@
(let [expected-effects {:db {:wallet {:ui {:tokens-loading {address true}}}}
:fx [[:json-rpc/call
[{:method "wallet_fetchOrGetCachedWalletBalances"
:params [[address]]
:params [[address] true]
:on-success [:wallet/store-wallet-token address]
:on-error [:wallet/get-wallet-token-for-account-failed
address]}]]]}]
+138 -131
View File
@@ -5,8 +5,10 @@
[status-im.contexts.wallet.collectible.utils :as collectible.utils]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.common.wizard.utils :as wizard-utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[taoensso.timbre :as log]
[utils.money :as utils.money]
[utils.number]
@@ -148,23 +150,16 @@
(rf/reg-event-fx
:wallet/select-send-address
(fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]]
(let [[prefix to-address] (utils/split-prefix-and-address address)
testnet-enabled? (get-in db [:profile/profile :test-networks-enabled?])
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
collectible-tx? (send-utils/tx-type-collectible?
(-> db :wallet :ui :send :tx-type))
collectible (when collectible-tx?
(-> db :wallet :ui :send :collectible))
one-collectible? (when collectible-tx?
(= (collectible.utils/collectible-balance collectible) 1))]
(let [[_ to-address] (utils/split-prefix-and-address address)
collectible-tx? (send-utils/tx-type-collectible?
(-> db :wallet :ui :send :tx-type))
collectible (when collectible-tx?
(-> db :wallet :ui :send :collectible))
one-collectible? (when collectible-tx?
(= (collectible.utils/collectible-balance collectible) 1))]
{:db (-> db
(assoc-in [:wallet :ui :send :recipient] (or recipient address))
(assoc-in [:wallet :ui :send :to-address] to-address)
(assoc-in [:wallet :ui :send :address-prefix] prefix)
(assoc-in [:wallet :ui :send :receiver-preferred-networks] receiver-networks)
(assoc-in [:wallet :ui :send :receiver-networks] receiver-networks))
(assoc-in [:wallet :ui :send :to-address] to-address))
:fx [(when (and collectible-tx? one-collectible?)
[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]])
[:dispatch
@@ -182,14 +177,12 @@
(rf/reg-event-fx
:wallet/set-token-to-send
(fn [{:keys [db]} [{:keys [token-symbol token stack-id start-flow? owners]} entry-point]]
(fn [{:keys [db]}
[{:keys [token-symbol token network stack-id start-flow? owners] :as params} entry-point]]
;; `token` is a map extracted from the sender, but in the wallet home page we don't know the
;; sender yet, so we only provide the `token-symbol`, later in
;; `:wallet/select-from-account` the `token` key will be set.
(let [{:keys [networks]} token
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
token-networks-ids (map :chain-id networks)
unsupported-token? (not-any? (set receiver-networks) token-networks-ids)
(let [{:keys [wallet]} db
unique-owner (when (= (count owners) 1)
(first owners))
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
@@ -197,17 +190,25 @@
(when (and token-symbol unique-owner)
(some #(when (= (:symbol %) token-symbol) %)
unique-owner-tokens)))
view-id (:view-id db)
root-screen? (or (= view-id :wallet-stack) (nil? view-id))
multi-account-balance? (-> (utils/get-accounts-with-token-balance (:accounts wallet)
token)
(count)
(> 1))
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
network-details (-> (get-in db
[:wallet :networks (if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))]
(network-utils/sorted-networks-with-details))
next-screen (:screen-id (wizard-utils/wizard-find-next-screen db
:wallet-send-flow
stack-id))]
(when (or token-data token-symbol)
{:db (cond-> db
:always (update-in [:wallet :ui :send]
network (update-in [:wallet :ui :send]
#(-> %
(dissoc :collectible :tx-type)
(assoc :token-not-supported-in-receiver-networks?
unsupported-token?)))
(assoc :network network)))
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
token-data (update-in [:wallet :ui :send]
#(assoc %
@@ -216,20 +217,35 @@
(network-utils/network-list
token-data
network-details))
:token-display-name (:symbol token-data)))
:token-display-name (:symbol token-data)
:token-symbol (:symbol token-data)))
unique-owner (assoc-in [:wallet :current-viewing-account-address] unique-owner)
entry-point (assoc-in [:wallet :ui :send :entry-point] entry-point))
:fx [[:dispatch [:wallet/stop-and-clean-suggested-routes]]
[:dispatch
;; ^:flush-dom allows us to make sure the re-frame DB state is always synced
;; before the navigation occurs, so the new screen is always rendered with
;; the DB state set by this event. By adding the metadata we are omitting
;; a 1-frame blink when the screen is mounted.
^:flush-dom
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}))))
:fx (if (and (not unique-owner) multi-account-balance? root-screen?)
[[:dispatch [:open-modal :screen/wallet.select-from]]]
(if (or (= next-screen :screen/wallet.select-from) (some? network))
[[:dispatch [:wallet/stop-and-clean-suggested-routes]]
[:dispatch
;; ^:flush-dom allows us to make sure the re-frame DB state is always synced
;; before the navigation occurs, so the new screen is always rendered with
;; the DB state set by this event. By adding the metadata we are omitting
;; a 1-frame blink when the screen is mounted.
^:flush-dom
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]
[[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (or token-symbol (:symbol token-data))
:source :send
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/set-token-to-send
(assoc params :network network)]))}])}]]]))}))))
(rf/reg-event-fx
:wallet/edit-token-to-send
@@ -288,7 +304,7 @@
collectible
(str (:name collection-data) " #" collectible-id))
owner-address (-> db :wallet :current-viewing-account-address)
owner-address (-> collectible :ownership first :address)
collectible-tx (-> db
(update-in [:wallet :ui :send] dissoc :token)
(assoc-in [:wallet :ui :send :collectible] collectible)
@@ -404,89 +420,62 @@
(rf/reg-event-fx :wallet/start-get-suggested-routes
(fn [{:keys [db]} [{:keys [amount amount-out updated-token] :as args :or {amount-out "0"}}]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
{:keys [token tx-type collectible to-address
receiver-networks disabled-from-chain-ids
from-locked-amounts bridge-to-chain-id]
network bridge-to-chain-id]
:or {token updated-token}} (get-in db [:wallet :ui :send])
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db [:wallet :networks (if test-networks-enabled? :test :prod)])
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
to-token-id ""
gas-rates constants/gas-rate-medium
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
amount-in (to-hex amount)
amount-out (to-hex amount-out)
from-address wallet-address
disabled-to-chain-ids (if (= tx-type :tx/bridge)
(filter #(not= % bridge-to-chain-id) network-chain-ids)
(filter (fn [chain-id]
(not (some #(= chain-id %)
receiver-networks)))
network-chain-ids))
from-locked-amount (update-vals from-locked-amounts to-hex)
send-type (case tx-type
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
:tx/bridge constants/send-type-bridge
constants/send-type-transfer)
balances-per-chain (when token (:balances-per-chain token))
sender-token-available-networks-for-suggested-routes
(when token
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain balances-per-chain
:disabled-chain-ids disabled-from-chain-ids
:only-with-balance? true}))
receiver-token-available-networks-for-suggested-routes
(when token
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain balances-per-chain
:disabled-chain-ids disabled-from-chain-ids
:only-with-balance? false}))
token-networks-ids (when token (map #(:chain-id %) (:supported-networks token)))
sender-network-values (when sender-token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
{:valid-networks
(if (= tx-type :tx/bridge)
(remove #(= bridge-to-chain-id %)
sender-token-available-networks-for-suggested-routes)
sender-token-available-networks-for-suggested-routes)
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? false}))
receiver-network-values (when receiver-token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
{:valid-networks
(if (= tx-type :tx/bridge)
(filter
#(= bridge-to-chain-id %)
receiver-token-available-networks-for-suggested-routes)
receiver-token-available-networks-for-suggested-routes)
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true}))
request-uuid (str (random-uuid))
params [{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
:amountIn amount-in
:amountOut amount-out
:tokenID token-id
:toTokenID to-token-id
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount from-locked-amount
:username (:username args)
:publicKey (:publicKey args)
:packID (:packID args)}]]
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db
[:wallet :networks
(if test-networks-enabled? :test :prod)])
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
to-token-id ""
gas-rates constants/gas-rate-medium
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
amount-in (to-hex amount)
amount-out (to-hex amount-out)
from-address wallet-address
network-chain-id (if collectible
(get-in collectible [:id :contract-id :chain-id])
(:chain-id network))
disabled-from-chain-ids (filter #(not= % network-chain-id) network-chain-ids)
disabled-to-chain-ids (filter #(not= %
(if (= tx-type :tx/bridge)
bridge-to-chain-id
network-chain-id))
network-chain-ids)
send-type (case tx-type
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
:tx/bridge constants/send-type-bridge
constants/send-type-transfer)
sender-network-values (when (= tx-type :tx/bridge)
(send-utils/loading-network-amounts
{:networks [network-chain-id]
:values {network-chain-id amount}
:receiver? false}))
receiver-network-values (when (= tx-type :tx/bridge)
(send-utils/loading-network-amounts
{:networks [bridge-to-chain-id]
:receiver? true}))
request-uuid (str (random-uuid))
params [{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
:amountIn amount-in
:amountOut amount-out
:tokenID token-id
:toTokenID to-token-id
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount {}
:username (:username args)
:publicKey (:publicKey args)
:packID (:packID args)}]]
(when (and to-address from-address amount-in token-id)
{:db (update-in db
[:wallet :ui :send]
@@ -619,7 +608,7 @@
(rf/reg-event-fx :wallet/clean-up-transaction-flow
(fn [_]
{:fx [[:dispatch [:wallet/clean-send-data]]
{:fx [[:dispatch [:dismiss-modal :screen/wallet.transaction-confirmation]]
[:dispatch [:wallet/clean-scanned-address]]
[:dispatch [:wallet/clean-local-suggestions]]
[:dispatch [:wallet/clean-send-address]]
@@ -629,9 +618,11 @@
(rf/reg-event-fx :wallet/end-transaction-flow
(fn [{:keys [db]}]
(let [address (get-in db [:wallet :current-viewing-account-address])]
{:fx [[:dispatch [:dismiss-modal :screen/wallet.transaction-confirmation]]
[:dispatch [:wallet/navigate-to-account-within-stack address]]
[:dispatch [:wallet/select-account-tab :activity]]]})))
{:fx [[:dispatch [:wallet/navigate-to-account-within-stack address]]
[:dispatch [:wallet/select-account-tab :activity]]
[:dispatch-later
[{:ms 20
:dispatch [:wallet/clean-up-transaction-flow]}]]]})))
(rf/reg-event-fx
:wallet/build-transactions-from-route
@@ -710,7 +701,7 @@
(rf/reg-event-fx
:wallet/select-from-account
(fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]]
(fn [{db :db} [{:keys [address stack-id network-details network start-flow?] :as params}]]
(let [{:keys [token-symbol
tx-type]} (-> db :wallet :ui :send)
token (when token-symbol
@@ -722,22 +713,38 @@
:networks (network-utils/network-list-with-positive-balance
token
network-details)
:supported-networks (network-utils/network-list token
network-details)
:supported-networks (network-utils/network-list
token
network-details)
:total-balance (utils/calculate-total-token-balance token))))
bridge-tx? (= tx-type :tx/bridge)
flow-id (if bridge-tx?
:wallet-bridge-flow
:wallet-send-flow)]
{:db (cond-> db
network (assoc-in [:wallet :ui :send :network] network)
token-symbol (assoc-in [:wallet :ui :send :token] token)
bridge-tx? (assoc-in [:wallet :ui :send :to-address] address))
:fx [[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id flow-id}]]]})))
:fx (if (some? network)
[[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id flow-id}]]]
[[:dispatch [:dismiss-modal :screen/wallet.select-from]]
[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (or token-symbol (:symbol token))
:source :send
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/select-from-account
(assoc params :network network)]))}])}]]])})))
(rf/reg-event-fx
:wallet/transaction-confirmation-navigate-back
@@ -1,6 +1,7 @@
(ns status-im.contexts.wallet.send.events-test
(:require
[cljs.test :refer-macros [is testing]]
[matcher-combinators.matchers :as m]
matcher-combinators.test
[re-frame.db :as rf-db]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
@@ -579,8 +580,23 @@
[event-id dispatch]
(let [stack-id :screen/stack
start-flow? false
address "0x01"]
address "0x01"
network {:chain-id 1}]
(testing "when tx-type is :tx/bridge and token-symbol is nil"
(let [flow-id :wallet-bridge-flow
tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address
:tx-type tx-type}}}}
:fx [[:dispatch [:dismiss-modal :screen/wallet.select-from]]
[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch [:show-bottom-sheet {:content (m/pred fn?)}]]]}]
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type}}}})
(is (match? expected-result
(dispatch [event-id
{:address address
:stack-id stack-id
:start-flow? start-flow?}])))))
(testing "when tx-type is :tx/bridge, network is selected and token-symbol is nil"
(let [flow-id :wallet-bridge-flow
tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address
@@ -596,6 +612,7 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?}])))))
(testing "when tx-type is not :tx/bridge and token-symbol is nil"
(let [flow-id :wallet-send-flow
@@ -612,6 +629,7 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?}])))))
(testing "when tx-type is :tx/bridge and token-symbol is not nil"
(let [flow-id :wallet-bridge-flow
@@ -680,5 +698,6 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?
:netork-details network-details}])))))))
@@ -14,9 +14,7 @@
(-> db :wallet :ui :send :token some?))
(def steps
[{:screen-id :screen/wallet.select-from
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
{:screen-id :screen/wallet.select-address
[{:screen-id :screen/wallet.select-address
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :recipient])))}
{:screen-id :screen/wallet.select-asset
:skip-step? (fn [db] (or (token-selected? db) (collectible-selected? db)))}
@@ -6,8 +6,8 @@
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.send.from.style :as style]
[status-im.setup.hot-reload :as hot-reload]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- on-account-press
@@ -15,27 +15,28 @@
(rf/dispatch [:wallet/select-from-account
{:address address
:network-details network-details
:stack-id :screen/wallet.select-from}]))
(defn- on-close
[]
(rf/dispatch [:wallet/clean-current-viewing-account]))
:stack-id :screen/wallet.select-from
:start-flow? true}]))
(defn- render-fn
[item _ _ {:keys [network-details]}]
(let [transformed-address (rf/sub [:wallet/account-address (:address item)
(:network-preferences-names item)])]
(let [has-balance (money/above-zero? (:asset-pay-balance item))]
[quo/account-item
{:on-press #(on-account-press (:address item) network-details)
{:type (if has-balance :tag :default)
:on-press #(on-account-press (:address item) network-details)
:state (if has-balance :default :disabled)
:token-props {:symbol (:asset-pay-symbol item)
:value (:asset-pay-balance item)}
:account-props (assoc item
:address transformed-address
:address (:formatted-address item)
:full-address? true)}]))
(defn view
[]
(let [accounts (rf/sub [:wallet/accounts-with-current-asset])
(let [token-symbol (rf/sub [:wallet/send-token-symbol])
token (rf/sub [:wallet/token-by-symbol token-symbol])
accounts (rf/sub [:wallet/accounts-with-balances token])
network-details (rf/sub [:wallet/network-details])]
(hot-reload/use-safe-unmount on-close)
[floating-button-page/view
{:footer-container-padding 0
:header [account-switcher/view
@@ -30,19 +30,16 @@
(let [amount (rf/sub [:wallet/send-total-amount-formatted])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
{:keys [full-name]} (rf/sub [:wallet/bridge-to-network-details])]
[quo/data-item
(cond-> {:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/recipient-gets)
:subtitle amount}
(= tx-type :tx/bridge)
(assoc
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:on-press show-bonder-fee-info))]))
(when (= tx-type :tx/bridge)
[quo/data-item
{:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:subtitle amount
:on-press show-bonder-fee-info}])))
(defn view
[{:keys [loading-routes? fees]}]
@@ -62,6 +59,6 @@
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/fees)
:title (i18n/label :t/max-fees)
:subtitle fees}]
[received-amount {:loading-routes? loading-routes?}]])
@@ -25,10 +25,9 @@
:height 40})
(def fees-data-item
{:flex 1
:height 40
:margin-horizontal 16
:background-color :transparent})
{:flex 1
:height 40
:background-color :transparent})
(def amount-data-item
{:flex 1
@@ -2,7 +2,6 @@
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
@@ -15,29 +14,18 @@
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.contexts.wallet.send.routes.view :as routes]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
[status-im.setup.hot-reload :as hot-reload]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- every-network-value-is-zero?
[sender-network-values]
(every? (fn [{:keys [total-amount]}]
(and
total-amount
(money/bignumber? total-amount)
(money/equal-to total-amount
(money/bignumber "0"))))
sender-network-values))
(defn select-asset-bottom-sheet
[clear-input!]
(let [{preselected-token-symbol :symbol} (rf/sub [:wallet/wallet-send-token])]
[:<> ;; Need to be a `:<>` to keep `asset-list` scrollable.
[quo/drawer-top
{:title (i18n/label :t/select-asset)
{:title (i18n/label :t/select-token)
:container-style {:padding-bottom 8}}]
[asset-list/view
{:content-container-style {:padding-horizontal 8
@@ -47,37 +35,6 @@
(rf/dispatch [:wallet/edit-token-to-send token])
(clear-input!))}]]))
(defn- token-not-available
[token-symbol receiver-networks token-networks]
(let [theme (quo.theme/use-theme)
add-token-networks (fn []
(let [chain-ids (concat receiver-networks
(mapv #(:chain-id %) token-networks))]
(rf/dispatch [:wallet/update-receiver-networks chain-ids])))]
[rn/view {:style (style/token-not-available-container theme)}
[rn/view
[quo/icon :i/alert
{:size 16
:color (colors/resolve-color :danger theme)}]]
[rn/view {:style style/token-not-available-content-container}
[quo/text
{:style (style/token-not-available-text theme)
:size :paragraph-2}
(i18n/label :t/token-not-available-on-receiver-networks {:token-symbol token-symbol})]
[quo/button
{:size 24
:customization-color (colors/resolve-color :danger theme)
:on-press add-token-networks}
(i18n/label :t/add-networks-token-can-be-sent-to {:token-symbol token-symbol})]]]))
(defn- show-unpreferred-networks-alert
[on-confirm]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[unpreferred-networks-alert/view
{:on-confirm on-confirm}])}]))
(defn- no-routes-found
[]
[rn/view {:style style/no-routes-found-container}
@@ -119,7 +76,7 @@
(defn- insufficient-asset-amount?
[{:keys [token-symbol owned-eth-token input-state no-routes-found? limit-exceeded?
sender-network-values enough-assets?]}]
enough-assets?]}]
(let [eth-selected? (= token-symbol (string/upper-case constants/mainnet-short-name))
zero-owned-eth? (money/equal-to (:total-balance owned-eth-token) 0)
input-at-max-owned-amount? (money/equal-to
@@ -129,7 +86,6 @@
input-at-max-owned-amount?
zero-owned-eth?)]
(and (or no-routes-found? limit-exceeded?)
(seq sender-network-values)
(or exceeded-input? (not enough-assets?)))))
(defn view
@@ -142,124 +98,104 @@
button-one-props :button-one-props
current-screen-id :current-screen-id
initial-crypto-currency? :initial-crypto-currency?
enabled-from-chain-ids :enabled-from-chain-ids
from-enabled-networks :from-enabled-networks
:or {initial-crypto-currency? true}}]
(let [view-id (rf/sub [:view-id])
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
[crypto-currency?
set-crypto-currency] (rn/use-state initial-crypto-currency?)
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
token-networks :networks
:as token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
(let [view-id (rf/sub [:view-id])
{fiat-currency :currency} (rf/sub [:profile/profile])
currency (rf/sub [:profile/currency])
{token-symbol :symbol
:as token} (rf/sub [:wallet/wallet-send-token])
network (rf/sub [:wallet/send-network])
{:keys [total-balance]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
prices-per-token (rf/sub [:wallet/prices-per-token])
usd-conversion-rate (utils/token-usd-price token prices-per-token)
currency (rf/sub [:profile/currency])
token-decimals (rf/sub [:wallet/send-display-token-decimals])
conversion-rate (utils/token-price-by-symbol prices-per-token
token-symbol
currency)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(rf/sub [:wallet/send-amount-fixed
(/ input-value conversion-rate)]))
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
unsupported-token-in-receiver? (and (not= tx-type :tx/bridge)
(->> receiver-network-values
(remove #(= (:type %) :add))
(every? #(= (:type %) :not-available))))
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
routes (when suggested-routes
(or (:best suggested-routes) []))
no-routes-found? (and
(every-network-value-is-zero? sender-network-values)
(some? routes)
(not loading-routes?)
(not unsupported-token-in-receiver?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub [:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-network? (set receiver-preferred-networks)
sending-to-unpreferred-networks? (some (comp not receiver-preferred-network?)
receiver-networks)
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:no-routes-found? no-routes-found?
:limit-exceeded? limit-exceeded?
:sender-network-values sender-network-values})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? routes))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
(rn/use-unmount
#(rf/dispatch [:wallet/clean-up-transaction-flow]))
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
[(:chain-id network)]])
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
[(:chain-id network)]])
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
[crypto-currency?
set-crypto-currency] (rn/use-state initial-crypto-currency?)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
token-balance (or default-limit-crypto total-balance)
prices-per-token (rf/sub [:wallet/prices-per-token])
usd-conversion-rate (utils/token-usd-price token prices-per-token)
conversion-rate (utils/token-price-by-symbol prices-per-token
token-symbol
currency)
token-decimals (rf/sub [:wallet/send-display-token-decimals])
clear-input! #(set-input-state controlled-input/delete-all)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(rf/sub [:wallet/send-amount-fixed
(/ input-value conversion-rate)]))
routes-request-uuid (when suggested-routes
(:uuid suggested-routes))
no-routes-found? (and
routes-request-uuid
(empty? route)
(not loading-routes?))
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:no-routes-found? no-routes-found?
:limit-exceeded? limit-exceeded?})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
on-confirm (or default-on-confirm handle-on-confirm)
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? route))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
(rn/use-effect
(fn []
(when active-screen?
@@ -289,8 +225,9 @@
[current-address])
(rn/use-effect
(fn []
(request-fetch-routes 0))
[send-from-locked-amounts])
(when active-screen?
(request-fetch-routes 2000)))
[amount-in-crypto valid-input?])
[rn/view
{:style style/screen
:accessibility-label (str "container"
@@ -318,7 +255,7 @@
conversion-rate)
conversion-rate))
:hint-component [quo/network-tags
{:networks (seq from-enabled-networks)
{:networks (seq [network])
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
@@ -330,17 +267,9 @@
(controlled-input/upper-limit-bn
input-state)))})
:status (when (controlled-input/input-error input-state) :error)}]}]
[routes/view
{:token token-by-symbol
:input-value input-value
:valid-input? valid-input?
:token-not-supported-in-receiver-networks? unsupported-token-in-receiver?
:current-screen-id current-screen-id
:request-fetch-routes request-fetch-routes}]
(when (and (not loading-routes?)
sender-network-values
unsupported-token-in-receiver?)
[token-not-available token-symbol receiver-networks token-networks])
(if (= tx-type :tx/bridge)
[routes/view {:token token-by-symbol}]
[rn/view {:style {:flex 1}}])
(when not-enough-asset?
[not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route))
@@ -361,14 +290,10 @@
loading-routes?
(and (not should-try-again?)
confirm-disabled?))
:on-press (cond
should-try-again?
:on-press (if should-try-again?
#(rf/dispatch [:wallet/start-get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount-in-crypto))
:customization-color current-color}
(when should-try-again?
@@ -34,19 +34,3 @@
:top margin-top}
inverted?
(assoc :transform [{:scaleY -1}])))
(def disclaimer
{:margin-horizontal 20
:margin-top 20
:margin-bottom 8})
(def input-container
{:margin-top 8
:margin-bottom 12})
(defn keyboard-container
[bottom]
{:padding-bottom bottom})
(def error-box
{:margin-horizontal 20})
@@ -1,87 +1,23 @@
(ns status-im.contexts.wallet.send.routes.view
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.routes.style :as style]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def row-height 44)
(def space-between-rows 11)
(def network-link-linear-height 10)
(def network-link-1x-height 56)
(def network-link-2x-height 111)
(defn- open-preferences
[]
(rf/dispatch
[:show-bottom-sheet
{:content
(fn []
(let [receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub
[:wallet/wallet-send-receiver-preferred-networks])
{token-symbol :symbol
token-networks :supported-networks} (rf/sub [:wallet/wallet-send-token])
token-chain-ids-set (set (mapv #(:chain-id %) token-networks))
[selected-receiver-networks
set-selected-receiver-networks] (rn/use-state receiver-networks)
receiver-preferred-networks-set (set receiver-preferred-networks)
receiver-selected-preferred-networks (filter #(contains?
receiver-preferred-networks-set
%)
selected-receiver-networks)
receiver-selected-non-preferred-networks (filter #(not (contains?
receiver-preferred-networks-set
%))
selected-receiver-networks)
not-available-preferred-networks (filter (fn [preferred-chain-id]
(not (contains? token-chain-ids-set
preferred-chain-id)))
receiver-selected-preferred-networks)
not-available-non-preferred-networks (filter (fn [preferred-chain-id]
(not (contains?
token-chain-ids-set
preferred-chain-id)))
receiver-selected-non-preferred-networks)
first-section-warning-label (when (not-empty not-available-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-preferred-networks)}))
second-section-warning-label (when (not-empty
not-available-non-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-non-preferred-networks)}))]
[network-preferences/view
{:title (i18n/label :t/edit-receiver-networks)
:first-section-label (i18n/label :t/preferred-by-receiver)
:second-section-label (i18n/label :t/not-preferred-by-receiver)
:selected-networks (set (map network-utils/id->network
receiver-networks))
:receiver-preferred-networks receiver-preferred-networks
:button-label (i18n/label :t/apply-changes)
:first-section-warning-label first-section-warning-label
:second-section-warning-label second-section-warning-label
:on-change #(set-selected-receiver-networks %)
:on-save (fn [chain-ids]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}]))
(defn render-network-values
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
[{:keys [network-values token-symbol receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[rn/view
(doall
@@ -96,27 +32,19 @@
(= network-value-type :not-available)
:disabled
:else network-value-type)
amount-formatted (-> (rf/sub [:wallet/send-amount-fixed total-amount])
amount-formatted (-> total-amount
(common-utils/sanitized-token-amount-to-display
constants/min-token-decimals-to-display)
(str " " token-symbol))]
[rn/view
{:key (str (if receiver? "to" "from") "-" chain-id)
:style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge
{:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available)
amount-formatted)
:network (network-utils/id->network chain-id)
:status status
:on-press #(when (not loading-routes?)
(cond
(= network-value-type :edit)
(open-preferences)
on-press (on-press chain-id total-amount)))
:on-long-press #(when (and (not loading-routes?) (not= status :disabled))
(cond
(= network-value-type :add)
(open-preferences)
on-long-press (on-long-press chain-id total-amount)))}]]))
{:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available)
amount-formatted)
:network (network-utils/id->network chain-id)
:status status}]]))
network-values))])
(defn render-network-links
@@ -156,59 +84,15 @@
:destination destination}]]]))
network-links)])
(defn disable-chain
[chain-id disabled-from-chain-ids token-available-networks-for-suggested-routes]
(let [disabled-chain-ids
(if (contains? (set
disabled-from-chain-ids)
chain-id)
(vec (remove #(= % chain-id)
disabled-from-chain-ids))
(conj disabled-from-chain-ids
chain-id))
re-enabling-chain?
(< (count disabled-chain-ids)
(count disabled-from-chain-ids))]
(if (or re-enabling-chain?
(> (count token-available-networks-for-suggested-routes) 1))
(rf/dispatch [:wallet/disable-from-networks
disabled-chain-ids])
(rf/dispatch [:toasts/upsert
{:id :disable-chain-error
:type :negative
:text (i18n/label :t/at-least-one-network-must-be-activated)}]))))
(defn view
[{:keys [token theme valid-input? request-fetch-routes on-press-to-network current-screen-id
token-not-supported-in-receiver-networks? input-value]}]
(let [token-symbol (:symbol token)
nav-current-screen-id (rf/sub [:view-id])
active-screen? (= nav-current-screen-id current-screen-id)
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub
[:wallet/wallet-send-receiver-network-values])
network-links (rf/sub [:wallet/wallet-send-network-links])
disabled-from-chain-ids (rf/sub
[:wallet/wallet-send-disabled-from-chain-ids])
{token-balances-per-chain :balances-per-chain} (rf/sub [:wallet/wallet-send-token])
token-available-networks-for-suggested-routes
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain token-balances-per-chain
:disabled-chain-ids disabled-from-chain-ids})
show-routes? (not-empty sender-network-values)]
(rn/use-effect
(fn []
(when (and active-screen?
(> (count token-available-networks-for-suggested-routes) 0))
(request-fetch-routes 2000)))
[input-value valid-input?])
(rn/use-effect
#(when (and active-screen? (> (count token-available-networks-for-suggested-routes) 0))
(request-fetch-routes 0))
[disabled-from-chain-ids])
[{:keys [token]}]
(let [theme (quo.theme/use-theme)
token-symbol (:symbol token)
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
network-links (rf/sub [:wallet/wallet-send-network-links])
show-routes? (not-empty sender-network-values)]
[rn/scroll-view {:content-container-style style/routes-container}
(when show-routes?
[rn/view {:style style/routes-header-container}
@@ -220,27 +104,18 @@
:container-style style/section-label-right}]])
[rn/view {:style style/routes-inner-container}
[render-network-values
{:token-symbol token-symbol
:network-values sender-network-values
:on-press (fn [chain-id-to-disable]
(disable-chain
chain-id-to-disable
disabled-from-chain-ids
token-available-networks-for-suggested-routes))
:receiver? false
:theme theme
:loading-routes? loading-routes?
:token-not-supported-in-receiver-networks? false}]
{:token-symbol token-symbol
:network-values sender-network-values
:receiver? false
:theme theme
:loading-routes? loading-routes?}]
[render-network-links
{:network-links network-links
:sender-network-values sender-network-values}]
[render-network-values
{:token-symbol token-symbol
:network-values receiver-network-values
:on-press on-press-to-network
:receiver? true
:loading-routes? loading-routes?
:theme theme
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:on-save #(request-fetch-routes 0)}]]]))
{:token-symbol token-symbol
:network-values receiver-network-values
:receiver? true
:loading-routes? loading-routes?
:theme theme}]]]))
@@ -78,7 +78,7 @@
:on-press on-close
:switcher-type :select-account}]
[quo/page-top
{:title (i18n/label :t/select-asset)
{:title (i18n/label :t/select-token)
:title-accessibility-label :title-label}]
[quo/segmented-control
{:size 32
+11 -38
View File
@@ -229,44 +229,17 @@
safe-add-type-edit)))
(defn loading-network-amounts
[{:keys [valid-networks disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?]}]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
token-networks-ids-set (set token-networks-ids)
valid-networks-set (set valid-networks)
not-available-networks (if receiver?
(filter #(not (token-networks-ids-set %)) receiver-networks)
[])
not-available-networks-set (set not-available-networks)
valid-networks (-> (concat valid-networks
(when (not (and receiver? (= tx-type :tx/bridge)))
disabled-chain-ids)
(when receiver?
(filter #(not (valid-networks-set %))
not-available-networks)))
(distinct))]
(->> valid-networks
(map
(fn [chain-id]
(cond->
{:chain-id chain-id
:type (cond
(contains? not-available-networks-set chain-id) :not-available
(or receiver?
(not (contains? disabled-set chain-id))) :loading
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}
(and (not receiver?) (contains? disabled-set chain-id))
(assoc :total-amount (money/bignumber "0")))))
(filter
(fn [network-amount]
(or (and receiver?
(or (= tx-type :tx/bridge)
(contains? receiver-networks-set (:chain-id network-amount))))
(not receiver?))))
(sort-by (fn [network-amount]
(get network-priority-score
(network-utils/id->network (:chain-id network-amount)))))
(vec))))
[{:keys [networks values receiver?]}]
(->> networks
(map
(fn [chain-id]
(let [network-value (when values (get values chain-id))]
(cond-> {:chain-id chain-id
:type (if network-value :default :loading)}
network-value (assoc :total-amount
(money/bignumber network-value))
(and (not network-value) (not receiver?)) (assoc :total-amount (money/bignumber "0"))))))
(vec)))
(defn network-links
[route from-values-by-chain to-values-by-chain]
@@ -8,12 +8,16 @@
[utils.re-frame :as rf]))
(defn- network-item
[{:keys [network on-select-network]}]
[{:keys [network on-select-network source]}]
(let [{:keys [network-name
chain-id]} network
chain-id]} network
{balance-in-crypto :crypto
balance-in-fiat :fiat} (rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
mainnet? (= network-name constants/mainnet-network-name)]
balance-in-fiat :fiat} (case source
:swap
(rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
(rf/sub [:wallet/send-token-network-balance chain-id]))
mainnet?
(= network-name constants/mainnet-network-name)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network network-name)
@@ -23,21 +27,28 @@
:container-style (style/network-list-container mainnet?)}]))
(defn view
[{:keys [on-select-network]}]
(let [token-symbol (rf/sub [:wallet/swap-asset-to-pay-token-symbol])
{mainnet-network :mainnet-network
layer-2-networks :layer-2-networks} (rf/sub [:wallet/swap-asset-to-pay-networks])
[{:keys [token-symbol on-select-network source title]
:or {source :swap
title (i18n/label :t/select-network)}}]
(let [{mainnet-network :mainnet-network
layer-2-networks :layer-2-networks} (case source
:swap
(rf/sub [:wallet/swap-asset-to-pay-networks])
:send
(rf/sub [:wallet/send-token-grouped-networks])
:bridge
(rf/sub [:wallet/send-token-grouped-networks]))
render-fn (rn/use-callback (fn [network]
[network-item
{:network network
:on-select-network
on-select-network}]))]
{:network network
:on-select-network on-select-network
:source source}]))]
[:<>
[rn/view {:style style/header-container}
[quo/text
{:size :heading-2
:weight :semi-bold}
(i18n/label :t/select-network)]
title]
[quo/context-tag
{:type :token
:size 24
@@ -46,7 +57,8 @@
(when mainnet-network
[network-item
{:network mainnet-network
:on-select-network on-select-network}])
:on-select-network on-select-network
:source source}])
[quo/divider-label {:container-style style/divider-label}
(i18n/label :t/layer-2)]
[rn/flat-list
@@ -34,7 +34,8 @@
(defn view
[]
(let [accounts (rf/sub [:wallet/accounts-with-balances])]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
accounts (rf/sub [:wallet/accounts-with-balances asset-to-pay])]
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav
-12
View File
@@ -43,7 +43,6 @@
[status-im.contexts.keycard.pin.enter.view :as keycard.pin.enter]
[status-im.contexts.onboarding.create-or-sync-profile.view :as create-or-sync-profile]
[status-im.contexts.onboarding.create-password.view :as create-password]
[status-im.contexts.onboarding.create-profile.view :as create-profile]
[status-im.contexts.onboarding.enable-biometrics.view :as enable-biometrics]
[status-im.contexts.onboarding.enable-notifications.view :as enable-notifications]
[status-im.contexts.onboarding.identifiers.view :as identifiers]
@@ -728,16 +727,6 @@
:modalPresentationStyle :overCurrentContext}
:component create-or-sync-profile/sync-or-recover-profile})
(def onboarding-create-profile
{:name :screen/onboarding.create-profile
:metrics {:track? true
:alias-id :onboarding.create-profile-info}
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations transitions/push-animations-for-transparent-background
:popGesture false}
:component create-profile/create-profile})
(def onboarding-create-profile-password
{:name :screen/onboarding.create-profile-password
:metrics {:track? true
@@ -872,7 +861,6 @@
[onboarding-intro
onboarding-new-to-status
onboarding-sync-or-recover-profile
onboarding-create-profile
onboarding-create-profile-password
onboarding-enable-biometrics
onboarding-preparing-status
+3 -3
View File
@@ -56,12 +56,12 @@
(re-frame/reg-sub
:wallet/network-values
:<- [:wallet/wallet-send]
:<- [:wallet/send-display-token-decimals]
(fn [[{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} token-decimals]
(fn [{:keys [from-values-by-chain to-values-by-chain token-display-name token] :as send-data}
[_ to-values?]]
(let [network-values (if to-values? to-values-by-chain from-values-by-chain)
token-symbol (or token-display-name
(-> send-data :token :symbol))]
(-> send-data :token :symbol))
token-decimals (:decimals token)]
(reduce-kv
(fn [acc chain-id amount]
(let [network-name (network-utils/id->network chain-id)
+48 -2
View File
@@ -2,7 +2,7 @@
(:require
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-tab-constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money]
@@ -34,6 +34,11 @@
:<- [:wallet/wallet-send]
:-> :token)
(rf/reg-sub
:wallet/send-token-symbol
:<- [:wallet/wallet-send]
:-> :token-symbol)
(rf/reg-sub
:wallet/send-transaction-ids
:<- [:wallet/wallet-send]
@@ -54,6 +59,11 @@
:<- [:wallet/wallet-send]
:-> :tx-type)
(rf/reg-sub
:wallet/send-network
:<- [:wallet/wallet-send]
:-> :network)
(rf/reg-sub
:wallet/sending-collectible?
:<- [:wallet/send-tx-type]
@@ -77,7 +87,7 @@
(->> address-activity
(sort :timestamp)
(keep (fn [{:keys [activity-type recipient]}]
(when (= activity-constants/wallet-activity-type-send activity-type)
(when (= activity-tab-constants/wallet-activity-type-send activity-type)
recipient)))
(distinct)))))
@@ -173,3 +183,39 @@
(= (:chain-id network) bridge-to-chain-id)
network))
networks))))
(rf/reg-sub
:wallet/send-token-grouped-networks
:<- [:wallet/wallet-send-token]
(fn [token]
(let [{token-networks :networks} token
grouped-networks (group-by :layer
token-networks)
mainnet-network (first (get grouped-networks constants/layer-1-network))
layer-2-networks (get grouped-networks constants/layer-2-network)]
{:mainnet-network mainnet-network
:layer-2-networks layer-2-networks})))
(rf/reg-sub
:wallet/send-token-network-balance
:<- [:wallet/wallet-send-token]
:<- [:profile/currency]
:<- [:profile/currency-symbol]
:<- [:wallet/prices-per-token]
(fn [[token currency currency-symbol prices-per-token] [_ chain-id]]
(let [{:keys [balances-per-chain
decimals]} token
balance-for-chain (get balances-per-chain chain-id)
total-balance (money/token->unit (:raw-balance balance-for-chain) decimals)
fiat-value (common-utils/calculate-token-fiat-value
{:currency currency
:balance total-balance
:token token
:prices-per-token prices-per-token})
crypto-formatted (common-utils/get-standard-crypto-format token
total-balance
prices-per-token)
fiat-formatted (common-utils/fiat-formatted-for-ui currency-symbol
fiat-value)]
{:crypto (str crypto-formatted " " (:symbol token))
:fiat fiat-formatted})))
-21
View File
@@ -334,24 +334,3 @@
:token token-for-fees
:prices-per-token prices-per-token})]
fee-in-fiat)))
(rf/reg-sub
:wallet/accounts-with-balances
:<- [:wallet/operable-accounts]
:<- [:wallet/swap-asset-to-pay]
(fn [[accounts asset-to-pay]]
(let [token-symbol (:symbol asset-to-pay)]
(map
(fn [account]
(let [tokens (:tokens account)
filtered-tokens (filter #(= (:symbol %) token-symbol) tokens)
asset-pay-balance (utils/calculate-total-token-balance filtered-tokens)
formatted-address (network-utils/format-address (:address account)
(:network-preferences-names account))]
(assoc account
:formatted-address formatted-address
:asset-pay-balance (utils/sanitized-token-amount-to-display
asset-pay-balance
constants/min-token-decimals-to-display)
:asset-pay-symbol token-symbol)))
accounts))))
+25 -7
View File
@@ -711,13 +711,11 @@
(rf/reg-sub
:wallet/zero-balance-in-all-non-watched-accounts?
:<- [:wallet/aggregated-tokens]
:<- [:profile/currency]
:<- [:wallet/prices-per-token]
(fn [[aggregated-tokens currency prices-per-token]]
(let [balance (utils/calculate-balance-from-tokens {:currency currency
:tokens aggregated-tokens
:prices-per-token prices-per-token})]
(and (not-empty aggregated-tokens) (money/equal-to balance 0)))))
(fn [aggregated-tokens]
(let [zero-balance? (->> aggregated-tokens
(mapcat (comp vals :balances-per-chain))
(every? #(money/equal-to (:raw-balance %) 0)))]
(and (not-empty aggregated-tokens) zero-balance?))))
(rf/reg-sub
:wallet/network-preference-details
@@ -903,3 +901,23 @@
:<- [:wallet/accounts-without-current-viewing-account]
(fn [accounts]
(get-emoji-and-colors-from-accounts accounts)))
(rf/reg-sub
:wallet/accounts-with-balances
:<- [:wallet/operable-accounts]
(fn [accounts [_ token]]
(let [token-symbol (:symbol token)]
(map
(fn [account]
(let [tokens (:tokens account)
filtered-tokens (filter #(= (:symbol %) token-symbol) tokens)
asset-pay-balance (utils/calculate-total-token-balance filtered-tokens)
formatted-address (network-utils/format-address (:address account)
(:network-preferences-names account))]
(assoc account
:formatted-address formatted-address
:asset-pay-balance (utils/sanitized-token-amount-to-display
asset-pay-balance
constants/min-token-decimals-to-display)
:asset-pay-symbol token-symbol)))
accounts))))
+125
View File
@@ -1008,3 +1008,128 @@
(update accounts "0x2" assoc :operable :partially)))
(is (true? (rf/sub [sub-name])))))
(h/deftest-sub :wallet/zero-balance-in-all-non-watched-accounts?
[sub-name]
(testing "returns true if the balance is zero in all non-watched accounts"
(swap! rf-db/app-db
#(assoc-in %
[:wallet :accounts]
{"0x1" {:address "0x1"
:watch-only? false
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}
"0x2" {:address "0x2"
:watch-only? false
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}
"0x3"
{:address "0x3"
:watch-only? true
:tokens [{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance
(money/bignumber
"2")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance
(money/bignumber
"1")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance
(money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance
(money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance
(money/bignumber
"2")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance
(money/bignumber
"0")
:has-error
false}}}]}}))
(is (true? (rf/sub [sub-name]))))
(testing "returns false if the balance is not zero in all non-watched accounts"
(swap! rf-db/app-db
#(assoc-in %
[:wallet :accounts]
{"0x1" {:address "0x1"
:watch-only? false
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"1")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}
"0x2" {:address "0x2"
:watch-only? true
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"1")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}}))
(is (false? (rf/sub [sub-name])))))
+5 -1
View File
@@ -285,10 +285,14 @@
[bn1 bn2]
(.round (.mul ^js bn1 bn2) 0))
(defn div
(defn- div*
[bn1 bn2]
(.dividedBy ^js bn1 bn2))
(def div
"Divides with defaults, this version is able to receive `nil` and takes them as 0."
(fnil div* (bignumber 0) (bignumber 1)))
(defn div-and-round
[bn1 bn2]
(.round (.dividedBy ^js bn1 bn2) 0))
@@ -17,11 +17,10 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = 'sender', 'receiver'
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user,))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = self.home_1.get_profile_view(), self.home_2.get_profile_view()
self.public_key_1 = self.home_1.get_public_key()
self.profile_link_2 = self.home_2.get_link_to_profile()
@@ -98,8 +97,8 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
def test_activity_center_contact_request_accept_swipe_mark_all_as_read(self):
self.device_2.just_fyi('Creating a new user on Device2')
self.home_2.reopen_app(sign_in=False)
new_username = "new user"
self.device_2.create_user(username=new_username, first_user=False)
self.device_2.create_user(first_user=False)
new_username = self.home_2.get_username()
self.device_2.just_fyi('Device2 sends a contact request to Device1 via Paste button and check user details')
self.home_2.chats_tab.click()
@@ -159,20 +158,19 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
self.username_2, self.profile_link_2, decoded_username))
public_key_2 = self.profile_link_2.split("#")[-1]
new_username_1 = "test user 123"
def _device_1_creates_user():
self.home_1.just_fyi("Device 1 creates a new user")
self.home_1.reopen_app(sign_in=False)
self.device_1.create_user(username=new_username_1, first_user=False)
self.device_1.create_user(first_user=False)
return self.home_1.get_username()
def _device_2_sign_in():
self.home_2.just_fyi("Device 2 sign in, user name is " + self.username_2)
self.home_2.reopen_app(sign_in=False)
self.device_2.sign_in(user_name=self.username_2)
self.loop.run_until_complete(run_in_parallel(((_device_1_creates_user, {}),
(_device_2_sign_in, {}))))
new_username_1, _ = self.loop.run_until_complete(run_in_parallel(((_device_1_creates_user, {}),
(_device_2_sign_in, {}))))
self.device_1.just_fyi('Device1 sends a contact request to Device2 using his profile link')
self.home_1.chats_tab.click()
@@ -242,11 +240,11 @@ class TestActivityMultipleDevicePR(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = 'user1', 'user2'
self.loop.run_until_complete(
run_in_parallel(((self.device_1.create_user, {'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
run_in_parallel(((self.device_1.create_user,),
(self.device_2.create_user,))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = self.home_1.get_profile_view(), self.home_2.get_profile_view()
self.public_key_2 = self.home_2.get_public_key()
self.home_2.navigate_back_to_home_view()
@@ -382,11 +380,11 @@ class TestActivityMultipleDevicePRTwo(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = 'user1', 'user2'
self.loop.run_until_complete(
run_in_parallel(((self.device_1.create_user, {'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
run_in_parallel(((self.device_1.create_user,),
(self.device_2.create_user,))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = self.home_1.get_profile_view(), self.home_2.get_profile_view()
self.public_key_2 = self.home_2.get_public_key()
self.home_2.navigate_back_to_home_view()
@@ -20,13 +20,11 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = 'sender', 'receiver'
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'enable_notifications': True,
'username': self.username_2}))))
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user, {'enable_notifications': True}))))
self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.homes = (self.home_1, self.home_2)
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = (home.get_profile_view() for home in self.homes)
self.public_key_2 = self.home_2.get_public_key()
@@ -543,13 +541,11 @@ class TestOneToOneChatMultipleSharedDevicesNewUiTwo(MultipleSharedDeviceTestCase
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = 'sender', 'receiver'
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'enable_notifications': True,
'username': self.username_2}))))
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user, {'enable_notifications': True}))))
self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.homes = (self.home_1, self.home_2)
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = (home.get_profile_view() for home in self.homes)
self.public_key_2 = self.home_2.get_public_key()
@@ -21,17 +21,25 @@ class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
self.message_to_admin = 'Hey, admin!'
self.public_keys, self.usernames, self.chats = {}, {}, {}
self.sign_in_views = [SignInView(self.drivers[key]) for key in self.drivers]
self.usernames = ('user admin', 'member_1', 'member_2')
self.loop.run_until_complete(
run_in_parallel(
(
(self.sign_in_views[0].create_user, {'enable_notifications': True, 'username': self.usernames[0]}),
(self.sign_in_views[1].create_user, {'enable_notifications': True, 'username': self.usernames[1]}),
(self.sign_in_views[2].create_user, {'enable_notifications': True, 'username': self.usernames[2]})
(self.sign_in_views[0].create_user, {'enable_notifications': True}),
(self.sign_in_views[1].create_user, {'enable_notifications': True}),
(self.sign_in_views[2].create_user, {'enable_notifications': True})
)
)
)
self.homes = [sign_in.get_home_view() for sign_in in self.sign_in_views]
self.usernames = self.loop.run_until_complete(
run_in_parallel(
(
(self.homes[0].get_username,),
(self.homes[1].get_username,),
(self.homes[2].get_username,)
)
)
)
self.public_keys = self.loop.run_until_complete(
run_in_parallel(
(
@@ -21,9 +21,9 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(1)
self.sign_in = SignInView(self.drivers[0])
self.username = 'first user'
self.home = self.sign_in.create_user(username=self.username)
self.home = self.sign_in.create_user()
self.username = self.home.get_username()
self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button)
self.community_name = "closed community"
self.channel_name = "cats"
@@ -158,8 +158,8 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
def test_restore_multiaccount_with_waku_backup_remove_profile_switch(self):
self.home.reopen_app(sign_in=False)
self.home.just_fyi("Restore user with predefined communities and contacts")
recover_user_name = 'Recover user'
self.sign_in.recover_access(passphrase=waku_user.seed, second_user=True, username=recover_user_name)
self.sign_in.recover_access(passphrase=waku_user.seed, second_user=True)
recover_user_name = self.home.get_username()
self.home.just_fyi("Check contacts/blocked users")
self.home.chats_tab.click()
@@ -306,11 +306,10 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = "user_1", "user_2"
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user,))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.public_key_2 = self.home_2.get_public_key()
self.profile_1 = self.home_1.get_profile_view()
[home.navigate_back_to_home_view() for home in self.homes]
@@ -836,12 +835,10 @@ class TestCommunityMultipleDeviceMergedTwo(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = "user_1", "user_2"
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'enable_notifications': True,
'username': self.username_2}))))
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user, {'enable_notifications': True}))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.public_key_2 = self.home_2.get_public_key()
self.profile_1 = self.home_1.get_profile_view()
[home.navigate_back_to_home_view() for home in self.homes]
@@ -13,9 +13,9 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
self.drivers, self.loop = create_shared_drivers(1)
self.driver = self.drivers[0]
self.sign_in = SignInView(self.driver)
self.username = 'test user'
self.home = self.sign_in.create_user(username=self.username)
self.home = self.sign_in.create_user()
self.username = self.home.get_username()
self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button)
self.open_community_name = "open community"
self.channel_name = "general"
+3 -3
View File
@@ -20,10 +20,10 @@ class TestFallbackMultipleDevice(MultipleSharedDeviceTestCase):
self.sign_in_3.get_home_view()
self.sign_in_1.just_fyi("Device 1: create a new user")
self.sign_in_3.just_fyi("Device 3: create a new user")
self.user_name_1, self.user_name_3 = 'first user', 'third user'
self.loop.run_until_complete(
run_in_parallel(((self.sign_in_1.create_user, {'username': self.user_name_1}),
(self.sign_in_3.create_user, {'username': self.user_name_3}))))
run_in_parallel(((self.sign_in_1.create_user,),
(self.sign_in_3.create_user,))))
self.user_name_1, self.user_name_3 = self.home_1.get_username(), self.home_3.get_username()
self.profile_1 = self.home_1.profile_button.click()
self.profile_2 = self.home_2.get_profile_view()
self.sign_in_3.just_fyi("Device 3: get public key")
+5 -7
View File
@@ -24,13 +24,11 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
self.sender, self.receiver = transaction_senders['ETH_1'], transaction_senders['ETH_2']
self.sender['wallet_address'] = '0x' + self.sender['address']
self.receiver['wallet_address'] = '0x' + self.receiver['address']
self.sender_username, self.receiver_username = 'sender', 'receiver'
self.loop.run_until_complete(
run_in_parallel(((self.sign_in_1.recover_access, {'passphrase': self.sender['passphrase'],
'username': self.sender_username}),
(self.sign_in_2.recover_access, {'passphrase': self.receiver['passphrase'],
'username': self.receiver_username}))))
run_in_parallel(((self.sign_in_1.recover_access, {'passphrase': self.sender['passphrase']}),
(self.sign_in_2.recover_access, {'passphrase': self.receiver['passphrase']}))))
self.home_1, self.home_2 = self.sign_in_1.get_home_view(), self.sign_in_2.get_home_view()
self.sender_username, self.receiver_username = self.home_1.get_username(), self.home_2.get_username()
self.wallet_1, self.wallet_2 = self.sign_in_1.get_wallet_view(), self.sign_in_2.get_wallet_view()
self.wallet_1.wallet_tab.click()
self.wallet_2.wallet_tab.click()
@@ -206,10 +204,10 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
self.arb_balance = {'Ether': 0.0001, 'USDCoin': 0.0, 'Status': 0.0, 'Uniswap': 0.5, 'Dai Stablecoin': 0.0}
self.sender['wallet_address'] = '0x' + self.sender['address']
self.receiver['wallet_address'] = '0x' + self.receiver['address']
self.sender_username, self.receiver_username = 'sender', 'receiver'
self.sign_in_view.recover_access(passphrase=self.sender['passphrase'], username=self.sender_username)
self.sign_in_view.recover_access(passphrase=self.sender['passphrase'])
self.home_view = self.sign_in_view.get_home_view()
self.sender_username = self.home_view.get_username()
self.wallet_view = self.home_view.wallet_tab.click()
@marks.testrail_id(740490)
+8
View File
@@ -624,3 +624,11 @@ class HomeView(BaseView):
element = Text(self.driver, accessibility_id='new-device-installation-id')
element.wait_for_visibility_of_element()
return element.text
def get_username(self):
profile_view = self.get_profile_view()
profile_view = self.profile_button.click_until_presence_of_element(profile_view.default_username_text)
profile_view.default_username_text.wait_for_element(3)
username = profile_view.default_username_text.text
profile_view.click_system_back_button()
return username
+1 -1
View File
@@ -179,7 +179,7 @@ class ProfileView(BaseView):
# Header
self.public_key_text = Text(self.driver, accessibility_id="chat-key")
self.default_username_text = Text(self.driver, accessibility_id="default-username")
self.default_username_text = Text(self.driver, accessibility_id="username")
self.contact_name_text = Text(self.driver, accessibility_id="contact-name")
self.share_my_profile_button = Button(self.driver, accessibility_id="share-header-button")
self.profile_picture = ProfilePictureElement(self.driver)
+3 -8
View File
@@ -211,7 +211,7 @@ class SignInView(BaseView):
self.profile_continue_button = Button(self.driver, accessibility_id="submit-create-profile-button")
self.profile_password_edit_box = EditBox(self.driver, translation_id="password-creation-placeholder-1")
self.profile_repeat_password_edit_box = EditBox(self.driver, translation_id="password-creation-placeholder-2")
self.profile_confirm_password_button = Button(self.driver, translation_id="password-creation-confirm")
self.profile_confirm_password_button = Button(self.driver, accessibility_id="Confirm password")
self.enable_biometric_maybe_later_button = Button(self.driver, translation_id="maybe-later")
self.identifiers_button = Button(self.driver, accessibility_id="skip-identifiers")
self.start_button = Button(self.driver, accessibility_id="welcome-button")
@@ -228,8 +228,6 @@ class SignInView(BaseView):
input_elements[0].send_keys(password)
input_elements[1].click()
input_elements[1].send_keys(password)
self.checkbox_button.scroll_to_element()
self.checkbox_button.click()
self.profile_confirm_password_button.click()
def set_profile(self, username: str, set_image=False):
@@ -238,7 +236,7 @@ class SignInView(BaseView):
if set_image:
pass
def create_user(self, password=common_password, username="test user", first_user=True, enable_notifications=False):
def create_user(self, password=common_password, first_user=True, enable_notifications=False):
self.driver.info("## Creating new multiaccount with password:'%s'" % password, device=False)
if first_user:
self.create_profile_button.click_until_presence_of_element(self.start_fresh_lets_go_button)
@@ -249,7 +247,6 @@ class SignInView(BaseView):
self.plus_profiles_button.click()
self.create_new_profile_button.click()
self.start_fresh_lets_go_button.click_until_presence_of_element(self.profile_title_input)
self.set_profile(username)
self.set_password(password)
self.chats_tab.wait_for_visibility_of_element(30)
self.driver.info("## New multiaccount is created successfully!", device=False)
@@ -261,7 +258,7 @@ class SignInView(BaseView):
return home_view
def recover_access(self, passphrase: str, password: str = common_password, enable_notifications=False,
second_user=False, username='Restore user', set_image=False, after_sync_code=False):
second_user=False, after_sync_code=False):
self.driver.info("## Recover access (password:%s)" % password, device=False)
if not after_sync_code:
@@ -274,8 +271,6 @@ class SignInView(BaseView):
self.use_recovery_phrase_button.click()
self.passphrase_edit_box.send_keys(passphrase)
self.continue_button.click_until_presence_of_element(self.profile_title_input)
if not after_sync_code:
self.set_profile(username, set_image)
self.set_password(password)
self.chats_tab.wait_for_visibility_of_element(30)
self.driver.info("## Multiaccount is recovered successfully!", device=False)
+9 -3
View File
@@ -395,6 +395,7 @@
"close-app-title": "Warning!",
"close-chat": "Close chat",
"close-chat-confirmation": "This conversation will disappear from the list.\nIt will appear again if the contact sends a new message.",
"close-community": "Close community",
"close-contact-search": "Close contact search",
"code-snippet": "Code snippet",
"collectibles": "Collectibles",
@@ -798,7 +799,6 @@
"do-not-cheat": "Don't try to cheat",
"do-not-cheat-description": "These 12 words give access to all of your funds so it is important that you write them in the correct order, take it seriously.",
"do-not-quit": "Do not quit the application or turn off your device. Doing so will lead to data corruption, loss of your Status profile and the inability to use Status.",
"do-not-share": "Do not share",
"done": "Done",
"dont-ask": "Don't ask me again",
@@ -1214,7 +1214,10 @@
"include": "Include",
"incoming": "Incoming",
"incoming-transaction": "Incoming transaction",
"incorrect-code": ["str", "Sorry the code was incorrect, please enter it again"],
"incorrect-code": [
"str",
"Sorry the code was incorrect, please enter it again"
],
"incorrect-private-key": "This is not the private key for this key pair",
"increase-gas": "Increase Gas",
"information-you-input-and-send": "Information you input and send",
@@ -1929,7 +1932,7 @@
"password-creation-max-length-hint": "Maximum 100 characters",
"password-creation-placeholder-1": "Type password",
"password-creation-placeholder-2": "Repeat password",
"password-creation-subtitle": "To log in to Status and sign transactions",
"password-creation-subtitle": "This password can't be recovered",
"password-creation-tips-1": "Lower case",
"password-creation-tips-2": "Upper case",
"password-creation-tips-3": "Numbers",
@@ -2279,8 +2282,11 @@
"select-chat": "Select chat to start messaging",
"select-network": "Select network",
"select-network-for-buying": "Select network for buying",
"select-network-to-bridge-from": "Select network to bridge from",
"select-network-to-receive": "Select network to receive",
"select-networks": "Select networks",
"select-new-location-for-keys": "Select a new location to save your private key(s)",
"select-token": "Select token",
"select-token-to-receive": "Select token to receive",
"select-token-to-swap": "Select token to Swap",
"selected": "Selected",