Compare commits

..
Author SHA1 Message Date
Prem Chaitanya Prathi f399b7887c fix: filter bad peer removal and missing message verifier fixes 2024-12-19 16:10:52 +05:30
Mohamed Javid 0f036ca9c4 fix_: slider reset in standard authentication (#21831)
This commit fixes the slider-button doesn't reset after the completion of slider in standard authentication with keycard keypairs.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-12-17 17:46:16 +05:30
flexsurfer c19128e8b1 [#21828] Close the flows for adding accounts in keycard accounts (#21829) 2024-12-16 17:49:59 +01:00
Brian Sztamfater 8d615801d1 feat_: optimize endpoint calls when fetching balances (#21802)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-12-16 12:11:09 -03:00
Mohsen 4f6e1897fe [#21135] fix: handle unsupported wallet address (#21820) 2024-12-16 17:54:16 +03:00
Ulises ManuelandShivek Khurana 93b5f8d180 Hide viewing community token requirements under a feature flag (#21814)
Co-authored-by: Shivek Khurana <khuranashivek@outlook.com>
2024-12-16 18:17:06 +05:30
Parvesh Monu 0e607dc293 fix Privacy policy button in learn more sheet open terms-of-use sheet (#21823) 2024-12-16 15:42:17 +05:30
flexsurfer 9ced0e4131 [#21708] [Android] Login with Keycard stucks on Preparing screen from time to time (#21811) 2024-12-13 19:57:26 +01:00
Sean Hagstrom 55ee800e18 feature: add keycard metric events (#21794)
This change adds some metrics events for tracking when keycard users have reached an unavailable feature, when keycard users have successfully logged in, and when keycard users have succeeded or failed to migrate to keycard.
2024-12-12 09:21:09 -08:00
flexsurfer 676998b83b [#21803] Cannot perform transactions/create derived account via keycard using NON default keypairs which existed before keycard migration (#21808) 2024-12-12 16:31:54 +01:00
Ulises Manuel 755ef19315 feat(profile): Make terms of use and privacy policy easily accessible (#21759)
* Fix `:true` keyword being used in navigation screens.

* Add about screen, terms of use and privacy policy links

* Remove old about app screen
2024-12-11 13:15:26 -06:00
Ulises Manuel bc9b0f8c87 feat(oboarding): Update ilustrations (#21781)
* Share usage data screen image done

* Update login by syncing and login using recovery phrase images

*  Small refactor: remove `merge` and add a `let` binding

* Fix modal entering expected animation
2024-12-11 11:05:23 -06:00
Yevheniia Berdnyk c8b063493b e2e: False failures fixes 2024-12-11 00:38:34 +02:00
flexsurfer 949e78a17e [#21577] Keycard [Wallet] - Derives next account for profile key pair (#21753) 2024-12-10 18:07:45 +01:00
Mohsen a821ec5a14 [#21763] feat: add feature unavailable for dapps interaction (#21771) 2024-12-10 11:35:44 +01:00
cuiyourong 4648066ec7 chore: fix some typos in comment (#21775) 2024-12-10 09:27:32 +01:00
102 changed files with 1646 additions and 1098 deletions
+1 -1
View File
@@ -150,7 +150,7 @@ This one is straightforward, just evaluate a navigation dispatch form from anywh
#### REPL-ing a component
This is also straighforward, but there are some small differences. Just like above, we only have to evaluate a dispatch form, as follows:
This is also straightforward, but there are some small differences. Just like above, we only have to evaluate a dispatch form, as follows:
```cljs
;; example debugging a quo component, although it could be any other component e.g. from the `status-im2` ns.
+12 -15
View File
@@ -23,18 +23,15 @@ PODS:
- secp256k1
- SSZipArchive
- libevent (2.1.12)
- 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 (1.2.4):
- libwebp/demux (= 1.2.4)
- libwebp/mux (= 1.2.4)
- libwebp/webp (= 1.2.4)
- libwebp/demux (1.2.4):
- libwebp/webp
- libwebp/mux (1.3.2):
- libwebp/mux (1.2.4):
- libwebp/demux
- libwebp/sharpyuv (1.3.2)
- libwebp/webp (1.3.2):
- libwebp/sharpyuv
- libwebp/webp (1.2.4)
- RCT-Folly (2022.05.16.00):
- boost
- DoubleConversion
@@ -1192,7 +1189,7 @@ PODS:
- secp256k1 (0.1.6)
- SocketRocket (0.6.1)
- SSZipArchive (2.4.3)
- TOCropViewController (2.7.4)
- TOCropViewController (2.6.1)
- Yoga (1.14.0)
DEPENDENCIES:
@@ -1492,7 +1489,7 @@ CHECKOUT OPTIONS:
:git: https://github.com/status-im/Keycard.swift.git
:tag: 3.1.1
secp256k1:
:commit: 4ab977cc2b2d7319be858bcb30a5d189bb149884
:commit: 46a1fa30d9b8babeae85ff519050f42394ab5fcc
:git: https://github.com/status-im/secp256k1.swift.git
:submodules: true
@@ -1509,7 +1506,7 @@ SPEC CHECKSUMS:
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
Keycard: 3bd24405b9a875b3b3cf1ab1be69b5c30c5f28bc
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: 2544c0f1081a5fa12e108bb8cb40e5f4581ccd87
RCTTypeSafety: 50efabe2b115c11ed03fbf3fd79e2f163ddb5d7c
@@ -1593,9 +1590,9 @@ SPEC CHECKSUMS:
secp256k1: f61d67e6fdcb85fd727acf1bf35ace6036db540c
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
Yoga: a716eea57d0d3430219c0a5a233e1e93ee931eb7
PODFILE CHECKSUM: 56d9f695324306d6db368d1172d211dfab55b7cb
PODFILE CHECKSUM: 81a66b45adddb083171ade882c343deb4faf2a8b
COCOAPODS: 1.15.2
+1 -1
View File
@@ -26,4 +26,4 @@ These log files are uploaded to Jenkins as CI job artifacts on failure using `ar
* `set_xcode_version.log` - Output from `node_modules/react-native/scripts/react-native-xcode.sh`.
- Created by redirecting output of `shellScript` in `ios/StatusIm.xcodeproj/project.pbxproj` for "Run Script" build Phase.
* `Status PR-StatusImPR.log - Created by [Fastlane Gym](https://docs.fastlane.tools/actions/gym/).
- Configred via [`Fastfile`](../Fastfile) using `buildlog_path` argument for `build_ios_app`.
- Configured via [`Fastfile`](../Fastfile) using `buildlog_path` argument for `build_ios_app`.
+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" "16.0" "16.1"];
versions = ["15.1" "15.2" "15.3" "15.4"];
};
go = super.go_1_21;
clang = super.clang_15;
+1 -1
View File
@@ -66,7 +66,7 @@
"react-native-shake": "^3.3.1",
"react-native-share": "10.0.2",
"react-native-static-safe-area-insets": "^2.2.0",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.6.3",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.6.4",
"react-native-svg": "13.10.0",
"react-native-webview": "13.6.3",
"react-syntax-highlighter": "^15.5.0"
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

+4
View File
@@ -71,6 +71,10 @@
[callback]
(.addListener ^js event-emitter "keyCardOnDisconnected" callback))
(defn on-card-new-pairing
[callback]
(.addListener ^js event-emitter "keyCardNewPairing" callback))
(defn on-nfc-user-cancelled
[callback]
(.addListener ^js event-emitter "keyCardOnNFCUserCancelled" callback))
@@ -1,67 +0,0 @@
(ns legacy.status-im.ui.screens.about-app.views
(:require
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.copyable-text :as copyable-text]
[legacy.status-im.ui.components.icons.icons :as icons]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.react :as react]
[quo.core :as quo]
[utils.i18n :as i18n]
[utils.re-frame :as rf])
(:require-macros [legacy.status-im.utils.views :as views]))
(views/defview about-app
[]
(views/letsubs [app-version [:get-app-short-version]
commit-hash [:get-commit-hash]
node-version [:get-app-node-version]]
[:<>
[quo/page-nav
{:type :title
:title (i18n/label :t/about-app)
:background :blur
:icon-name :i/close
:on-press #(rf/dispatch [:navigate-back])}]
[react/scroll-view
[copyable-text/copyable-text-view
{:copied-text app-version}
[list.item/list-item
{:size :small
:accessibility-label :app-version
:title (i18n/label :t/version)
:accessory :text
:accessory-text app-version}]]
[copyable-text/copyable-text-view
{:copied-text commit-hash}
[list.item/list-item
{:size :small
:accessibility-label :commit-hash
:title (i18n/label :t/app-commit)
:accessory :text
:accessory-text commit-hash}]]
[copyable-text/copyable-text-view
{:copied-text node-version}
[list.item/list-item
{:size :small
:accessibility-label :node-version
:title (i18n/label :t/node-version)
:accessory :text
:accessory-text node-version}]]]]))
(views/defview learn-more-sheet
[]
(views/letsubs [{:keys [title content]} [:bottom-sheet/options]]
[react/view
{:style {:padding-left 16
:padding-top 16
:padding-right 34
:padding-bottom 0}}
[react/view {:style {:align-items :center :flex-direction :row :margin-bottom 16}}
[icons/icon :main-icons/info
{:color colors/blue
:container-style {:margin-right 13}}]
[react/text {:style {:typography :title-bold}} title]]
[react/text {:style {:color colors/gray}} content]]))
(def learn-more
{:content learn-more-sheet})
@@ -1,7 +1,6 @@
(ns legacy.status-im.ui.screens.screens
(:require
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.screens.about-app.views :as about-app]
[legacy.status-im.ui.screens.advanced-settings.views :as advanced-settings]
[legacy.status-im.ui.screens.appearance.views :as appearance]
[legacy.status-im.ui.screens.backup-settings.view :as backup-settings]
@@ -93,12 +92,6 @@
:insets {:top? platform/android?}}
:component sync-settings/sync-settings}
;; ABOUT
{:name :about-app
:options {:topBar {:visible false}
:insets {:top? platform/android?}}
:component about-app/about-app}
;; STATUS HELP
{:name :help-center
:options {:topBar {:visible false}
@@ -16,7 +16,8 @@
[rn/view {:style (style/loading-container size blur? theme)}]))
(defn- left-subtitle
[{:keys [size subtitle-type subtitle-color icon icon-color blur? subtitle customization-color emoji
[{:keys [size subtitle-text-props subtitle-type subtitle-color icon icon-color blur? subtitle
customization-color emoji
network-image]
:or {subtitle-type :default}}]
(let [theme (quo.theme/use-theme)]
@@ -39,9 +40,10 @@
:style style/image}]
nil)])
[text/text
{:weight :medium
:size :paragraph-2
:style (style/description subtitle-color blur? theme)}
(merge {:weight :medium
:size :paragraph-2
:style (style/description subtitle-color blur? theme)}
subtitle-text-props)
subtitle]
(when (= subtitle-type :editable)
[icons/icon :i/edit
@@ -75,7 +77,8 @@
(defn- left-side
"The description can either be given as a string `subtitle-type` or a component `custom-subtitle`"
[{:keys [title status size blur? custom-subtitle icon subtitle subtitle-type subtitle-color icon-color
[{:keys [title subtitle-text-props status size blur? custom-subtitle icon subtitle subtitle-type
subtitle-color icon-color
customization-color network-image emoji title-icon]
:as props}]
(let [theme (quo.theme/use-theme)]
@@ -94,6 +97,7 @@
[custom-subtitle props]
[left-subtitle
{:theme theme
:subtitle-text-props subtitle-text-props
:size size
:subtitle-type subtitle-type
:subtitle-color subtitle-color
@@ -140,6 +144,7 @@
[:size {:optional true} [:maybe [:enum :default :small :large]]]
[:title :string]
[:subtitle {:optional true} [:maybe [:or :string :double]]]
[:subtitle-text-props {:optional true} [:maybe :map]]
[:custom-subtitle {:optional true} [:maybe fn?]]
[:icon {:optional true} [:maybe :keyword]]
[:title-icon {:optional true} [:maybe :keyword]]
@@ -79,11 +79,11 @@
[rn/view {:style style/community-logo-ring}]])
(defn- description-container
[{:keys [description description-text collection-text community-text
collection-image community-image blur?]
[{:keys [description description-text collection-text community-text blur?
collection-image community-image description-accessibility-label]
context-tag-props :context-tag
summary-props :summary}]
[rn/view
[rn/view {:accessibility-label description-accessibility-label}
(cond
(and (= description :text) (not (string/blank? description-text)))
[text/text
+11 -2
View File
@@ -1,11 +1,20 @@
(ns status-im.common.privacy.view
(:require-macros [legacy.status-im.utils.slurp :refer [slurp]])
(:require [quo.core :as quo]
[react-native.gesture :as gesture]))
[react-native.gesture :as gesture]
[status-im.contexts.settings.common.header :as header]
[utils.i18n :as i18n]))
(def privacy-statement-text (slurp "resources/privacy.mdwn"))
(defn privacy-statement
[]
[gesture/scroll-view {:margin 20}
[gesture/scroll-view {:style {:margin 20}}
[quo/text privacy-statement-text]])
(defn view
[]
[quo/overlay {:type :shell}
[header/view {:title (i18n/label :t/privacy-policy)}]
[gesture/scroll-view {:style {:padding-horizontal 20}}
[quo/text privacy-statement-text]]])
+2
View File
@@ -3,6 +3,8 @@
(def ui
{:add-new-contact (js/require "../resources/images/ui2/add-contact.png")
:biometrics (js/require "../resources/images/ui2/biometrics.png")
:usage-data (js/require "../resources/images/ui2/usage-data.png")
:login-syncing (js/require "../resources/images/ui2/login-syncing.png")
:chat-privately-with-friends (js/require "../resources/images/ui2/chat-privately-with-friends.png")
:desktop-how-to-pair-sign-in (js/require "../resources/images/ui2/desktop-how-to-pair-sign-in.png")
:desktop-how-to-pair-logged-in (js/require
@@ -10,24 +10,43 @@
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-fx :effects.keycard/call-on-auth-success
(fn [on-auth-success]
(when on-auth-success (on-auth-success ""))))
(defn- handle-password-success
[has-partially-operable-accounts? on-auth-success masked-password]
(let [on-auth-success-callback #(on-auth-success masked-password)]
(rf/dispatch [:standard-auth/set-success true])
(rf/dispatch [:standard-auth/reset-login-password])
(if has-partially-operable-accounts?
(rf/dispatch [:wallet/make-partially-operable-accounts-fully-operable
{:password masked-password
:on-success on-auth-success-callback
:on-error on-auth-success-callback}])
(on-auth-success-callback))))
(defn authorize
[{:keys [db]} [{:keys [on-auth-success keycard-supported?] :as args}]]
(let [key-uid (get-in db [:profile/profile :key-uid])
keycard? (get-in db [:profile/profile :keycard-pairing])]
[{:keys [db]} [{:keys [on-auth-success] :as args}]]
(let [key-uid (get-in db [:profile/profile :key-uid])
keycard-profile? (get-in db [:profile/profile :keycard-pairing])]
{:fx
[(if keycard?
(if keycard-supported?
[:effects.keycard/call-on-auth-success on-auth-success]
[:effects.utils/show-popup
{:title "This feature is not supported yet "
:content
"Keycard support is limited to logging in
and signing the sending transaction.
Use Status Desktop to access all functions."}])
[(if keycard-profile?
[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete
(fn [pin]
(rf/dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/get-keys
{:pin pin
:on-success (fn [{:keys [encryption-public-key]}]
(rf/dispatch [:keycard/disconnect])
(handle-password-success false
on-auth-success
(security/mask-data encryption-public-key)))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]))}]))}]]
[:effects.biometric/check-if-available
{:key-uid key-uid
:on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args])
@@ -89,21 +108,12 @@
(defn- bottom-sheet-password-view
[{:keys [on-press-biometric on-auth-success auth-button-icon-left auth-button-label]}]
(fn []
(let [has-partially-operable-accounts? (rf/sub [:wallet/has-partially-operable-accounts?])
handle-password-success
(fn [password]
(let [sha3-pwd (security/hash-masked-password password)
on-auth-success-callback #(on-auth-success sha3-pwd)]
(rf/dispatch [:standard-auth/set-success true])
(rf/dispatch [:standard-auth/reset-login-password])
(if has-partially-operable-accounts?
(rf/dispatch [:wallet/make-partially-operable-accounts-fully-operable
{:password sha3-pwd
:on-success on-auth-success-callback
:on-error on-auth-success-callback}])
(on-auth-success-callback))))]
(let [has-partially-operable-accounts? (rf/sub [:wallet/has-partially-operable-accounts?])]
[enter-password/view
{:on-enter-password handle-password-success
{:on-enter-password #(handle-password-success
has-partially-operable-accounts?
on-auth-success
(security/hash-masked-password %))
:on-press-biometrics on-press-biometric
:button-icon-left auth-button-icon-left
:button-label auth-button-label}])))
@@ -9,31 +9,35 @@
(defn view
[{:keys [track-text customization-color auth-button-label on-auth-success on-auth-fail
auth-button-icon-left size blur? container-style disabled? dependencies keycard-supported?]
auth-button-icon-left size blur? container-style disabled? dependencies on-complete]
:or {container-style {:flex 1}}}]
(let [theme (quo.theme/use-theme)
auth-method (rf/sub [:auth-method])
biometric-auth? (= auth-method constants/auth-method-biometric)
on-complete (rn/use-callback
(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])]
(let [theme (quo.theme/use-theme)
auth-method (rf/sub [:auth-method])
biometric-auth? (= auth-method constants/auth-method-biometric)
on-complete-callback (rn/use-callback
(fn []
(rf/dispatch [:standard-auth/authorize
{:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
: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)))
on-slider-complete (rn/use-callback
(fn [reset-slider-fn]
(js/setTimeout #(reset-slider-fn false) 500)
(if (fn? on-complete)
(on-complete)
(on-complete-callback)))
[on-complete on-complete-callback])
biometric-type (rf/sub [:biometrics/supported-type])]
[quo/slide-button
{:container-style container-style
:size size
:customization-color customization-color
:on-complete on-complete
:on-complete on-slider-complete
:track-icon (if biometric-auth?
(biometric/get-icon-by-type biometric-type)
:password)
+11 -2
View File
@@ -1,11 +1,20 @@
(ns status-im.common.terms.view
(:require-macros [legacy.status-im.utils.slurp :refer [slurp]])
(:require [quo.core :as quo]
[react-native.gesture :as gesture]))
[react-native.gesture :as gesture]
[status-im.contexts.settings.common.header :as header]
[utils.i18n :as i18n]))
(def terms-of-use-text (slurp "resources/terms-of-use.mdwn"))
(defn terms-of-use
[]
[gesture/scroll-view {:margin 20}
[gesture/scroll-view {:style {:margin 20}}
[quo/text terms-of-use-text]])
(defn view
[]
[quo/overlay {:type :shell}
[header/view {:title (i18n/label :t/terms-of-service)}]
[gesture/scroll-view {:style {:padding-horizontal 20}}
[quo/text terms-of-use-text]]])
+7
View File
@@ -334,6 +334,7 @@
(def ^:const privacy-policy-link "https://status.im/privacy-policy/")
(def ^:const terms-of-service-link "https://status.im/terms-of-use")
(def ^:const create-account-link "https://status.app/help/wallet/create-wallet-accounts")
(def ^:const mobile-upvote-link "https://status-mobile.featureupvote.com")
(def ^:const visibility-status-unknown 0)
(def ^:const visibility-status-automatic 1)
@@ -641,3 +642,9 @@
(def ^:const wallet-connect-transaction-refresh-interval-ms 10000)
(def ^:const native-token-symbol "ETH")
;; About app links
(def ^:const status-app-url "http://status.app")
(def ^:const go-waku-url "https://github.com/waku-org/go-waku")
(def ^:const status-go-url "https://github.com/status-im/status-go")
(def ^:const status-mobile-url "https://github.com/status-im/status-mobile")
@@ -53,7 +53,13 @@
(conj (navigation-event (name view-id)))
(= :screen/onboarding.syncing-results view-id)
(conj (key-value-event "onboarding-completed")))))
(conj (key-value-event "onboarding-completed"))
(= :screen/keycard.migrate.success view-id)
(conj (key-value-event "keycard-migration-succeeded"))
(= :screen/keycard.migrate.fail view-id)
(conj (key-value-event "keycard-migration-failed")))))
(defn navigated-to-collectibles-tab-event
[location]
@@ -11,30 +11,35 @@
[{:keys [contact-id]}]
(let [{:keys [contact-request-state
community-id
chat-name]} (rf/sub [:chats/current-chat-chat-view])
chat-type (rf/sub [:chats/chat-type])
[primary-name _] (if (= chat-type :public-chat)
[(str "#" chat-name) ""]
(rf/sub [:contacts/contact-two-names-by-identity contact-id]))
community-chat? (= chat-type :community-chat)
joined (when community-chat?
(rf/sub [:communities/community-joined community-id]))
pending? (when community-chat?
(rf/sub [:communities/my-pending-request-to-join community-id]))
contact-request-send? (or (not contact-request-state)
(= contact-request-state
constants/contact-request-state-none))
contact-request-received? (= contact-request-state
constants/contact-request-state-received)
contact-request-pending? (= contact-request-state
constants/contact-request-state-sent)]
chat-name]} (rf/sub [:chats/current-chat-chat-view])
chat-type (rf/sub [:chats/chat-type])
[primary-name _] (if (= chat-type :public-chat)
[(str "#" chat-name) ""]
(rf/sub [:contacts/contact-two-names-by-identity contact-id]))
community-chat? (= chat-type :community-chat)
joined (when community-chat?
(rf/sub [:communities/community-joined community-id]))
pending? (when community-chat?
(rf/sub [:communities/my-pending-request-to-join community-id]))
contact-request-send? (or (not contact-request-state)
(= contact-request-state
constants/contact-request-state-none))
contact-request-received? (= contact-request-state
constants/contact-request-state-received)
contact-request-pending? (= contact-request-state
constants/contact-request-state-sent)
keycard? (rf/sub [:keycard/keycard-profile?])
keycard-feature-unavailable (rn/use-callback
#(rf/dispatch [:keycard/feature-unavailable-show]))]
[rn/view {:style style/container}
[quo/permission-context
{:blur? true
:on-press (cond
(and community-chat? (not pending?) (not joined))
#(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id community-id}])
(if keycard?
keycard-feature-unavailable
#(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id community-id}]))
(not community-chat?)
#(rf/dispatch [:chat.ui/show-profile contact-id]))
@@ -10,72 +10,73 @@
[status-im.contexts.communities.actions.community-rules.view :as community-rules]
[status-im.contexts.communities.actions.permissions-sheet.view :as permissions-sheet]
[status-im.contexts.communities.utils :as communities.utils]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [theme (quo.theme/use-theme)
{id :community-id} (rf/sub [:get-screen-params])
(let [theme (quo.theme/use-theme)
{id :community-id} (rf/sub [:get-screen-params])
{:keys [name color images joined]} (rf/sub [:communities/community id])
has-permissions? (rf/sub [:communities/has-permissions? id])
airdrop-account (rf/sub [:communities/airdrop-account id])
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
revealed-accounts-count (count revealed-accounts)
wallet-accounts-count (count (rf/sub [:wallet/operable-accounts]))
addresses-shared-text (if (= revealed-accounts-count wallet-accounts-count)
(i18n/label :t/all-addresses)
(i18n/label-pluralize
revealed-accounts-count
:t/address-count))
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label (communities.utils/role->translation-key
highest-permission-role
:t/member))
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
navigate-back (rn/use-callback #(rf/dispatch [:navigate-back]))
show-addresses-for-permissions
(rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :addresses-for-permissions {:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content (fn [] [addresses-for-permissions/view])}])))
[can-edit-addresses?])
show-airdrop-addresses
(rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :address-for-airdrop {:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content (fn [] [airdrop-addresses/view])}])))
[can-edit-addresses?])
confirm-choices
(rn/use-callback
(fn []
(rf/dispatch
[:standard-auth/authorize
{:auth-button-label (if can-edit-addresses?
(i18n/label :t/edit-shared-addresses)
(i18n/label :t/request-to-join))
:on-auth-success (fn [password]
(rf/dispatch
[:communities/request-to-join-with-addresses
{:community-id id
:password password}]))}])
(navigate-back))
[can-edit-addresses?])
open-permission-sheet
(rn/use-callback (fn []
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [permissions-sheet/view id])}]))
[id])]
has-permissions? (rf/sub [:communities/has-permissions? id])
airdrop-account (rf/sub [:communities/airdrop-account id])
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
revealed-accounts-count (count revealed-accounts)
wallet-accounts-count (count (rf/sub [:wallet/operable-accounts]))
addresses-shared-text (if (= revealed-accounts-count wallet-accounts-count)
(i18n/label :t/all-addresses)
(i18n/label-pluralize
revealed-accounts-count
:t/address-count))
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label (communities.utils/role->translation-key
highest-permission-role
:t/member))
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
navigate-back (rn/use-callback #(rf/dispatch [:navigate-back]))
show-addresses-for-permissions (rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :addresses-for-permissions
{:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content
addresses-for-permissions/view}])))
[can-edit-addresses?])
show-airdrop-addresses (rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :address-for-airdrop
{:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content airdrop-addresses/view}])))
[can-edit-addresses?])
confirm-choices (rn/use-callback
(fn []
(rf/dispatch
[:standard-auth/authorize
{:auth-button-label (if can-edit-addresses?
(i18n/label
:t/edit-shared-addresses)
(i18n/label :t/request-to-join))
:on-auth-success
(fn [password]
(rf/dispatch
[:communities/request-to-join-with-addresses
{:community-id id
:password password}]))}])
(navigate-back))
[can-edit-addresses?])
open-permission-sheet (rn/use-callback
(fn []
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[permissions-sheet/view id])}]))
[id])]
(rn/use-mount
(fn []
(rf/dispatch [:communities/initialize-permission-addresses id])))
@@ -86,7 +87,7 @@
:icon-name :i/close
:on-press navigate-back
:accessibility-label :back-button}
has-permissions?
(and has-permissions? (ff/enabled? ::ff/community.view-token-requirements))
(assoc :right-side
[{:icon-left :i/unlocked
:on-press open-permission-sheet
@@ -7,6 +7,7 @@
[status-im.constants :as constants]
[status-im.contexts.communities.actions.addresses-for-permissions.style :as style]
[status-im.contexts.communities.actions.permissions-sheet.view :as permissions-sheet]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
@@ -122,23 +123,23 @@
(defn- page-top
[{:keys [community-id identical-choices? can-edit-addresses?]}]
(let [{:keys [name logo color]} (rf/sub [:communities/for-context-tag community-id])
has-permissions? (rf/sub [:communities/has-permissions? community-id])
confirm-discard-changes
(rn/use-callback
(fn []
(if identical-choices?
(rf/dispatch [:dismiss-modal :addresses-for-permissions])
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [confirm-discard-drawer
community-id])}])))
[identical-choices? community-id])
open-permission-sheet
(rn/use-callback (fn []
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [permissions-sheet/view
community-id])}]))
[community-id])]
has-permissions? (rf/sub [:communities/has-permissions? community-id])
confirm-discard-changes (rn/use-callback
(fn []
(if identical-choices?
(rf/dispatch [:dismiss-modal :addresses-for-permissions])
(rf/dispatch [:show-bottom-sheet
{:content (fn []
[confirm-discard-drawer
community-id])}])))
[identical-choices? community-id])
open-permission-sheet (rn/use-callback
(fn []
(rf/dispatch [:show-bottom-sheet
{:content (fn []
[permissions-sheet/view
community-id])}]))
[community-id])]
[:<>
(when can-edit-addresses?
[quo/page-nav
@@ -161,7 +162,7 @@
:community-name name
:community-logo logo
:customization-color color}
has-permissions?
(and has-permissions? (ff/enabled? ::ff/community.view-token-requirements))
(assoc :button-icon :i/info
:button-type :grey
:on-button-press open-permission-sheet))])]))
@@ -40,7 +40,8 @@
:right-icon :i/chevron-right
:accessibility-label :view-token-gating
:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [permissions-sheet/view id])
{:content (fn []
[permissions-sheet/view id])
:padding-bottom-override 16}])
:label (i18n/label :t/view-token-gating)})
@@ -146,7 +147,9 @@
[{: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?))
(and token-gated?
(not test-networks-enabled?)
(ff/enabled? ::ff/community.view-token-requirements))
[(invite-contacts id) (view-token-gating id)]
token-gated?
@@ -172,7 +175,8 @@
[{: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))
(when (and token-gated? (ff/enabled? ::ff/community.view-token-requirements))
(view-token-gating id))
(when (ff/enabled? ::ff/community.edit-account-selection)
(edit-shared-addresses id))
(mark-as-read id)
@@ -27,7 +27,11 @@
(fn []
(let [theme (quo.theme/use-theme)
{:keys [id]} (rf/sub [:get-screen-params])
{:keys [color name images]} (rf/sub [:communities/community id])]
{:keys [color name images]} (rf/sub [:communities/community id])
keycard? (rf/sub [:keycard/keycard-profile?])
keycard-feature-unavailable (rn/use-callback
#(rf/dispatch [:keycard/feature-unavailable-show
{:feature-name :community.request-to-join}]))]
[rn/safe-area-view {:flex 1}
[gesture/scroll-view {:style style/container}
[rn/view style/page-container
@@ -59,7 +63,9 @@
(i18n/label :t/cancel)]
[quo/button
{:accessibility-label :join-community-button
:on-press #(join-community-and-navigate-back id)
:on-press (if keycard?
keycard-feature-unavailable
#(join-community-and-navigate-back id))
:container-style {:flex 1}
:inner-style {:background-color (colors/resolve-color color theme)}}
(i18n/label :t/request-to-join)]]
@@ -106,12 +106,14 @@
[quo/text (i18n/label :t/network-not-supported)])
(defn- request-access-button
[id color]
[id color keycard? keycard-feature-unavailable]
[quo/button
{:on-press (if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
{:on-press (if keycard?
keycard-feature-unavailable
(if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}])))
:accessibility-label :show-request-to-join-screen-button
:customization-color color
:container-style {:margin-bottom 12}
@@ -126,27 +128,34 @@
(defn- token-requirements
[{:keys [id color role-permissions?]}]
(let [{:keys [can-request-access? no-member-permission? networks-not-supported?
highest-permission-role
tokens]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label
(communities.utils/role->translation-key highest-permission-role :t/member))
on-press (rn/use-callback
(fn []
(if config/community-accounts-selection-enabled?
(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
(rf/dispatch [:open-modal :community-requests-to-join
{:id id}])))
[id])
on-press-info #(rf/dispatch
[:show-bottom-sheet {:content token-gated-communities-info}])]
(let
[{:keys [can-request-access? no-member-permission? networks-not-supported?
highest-permission-role
tokens]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label
(communities.utils/role->translation-key highest-permission-role
:t/member))
on-press (rn/use-callback
(fn []
(if config/community-accounts-selection-enabled?
(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
(rf/dispatch [:open-modal :community-requests-to-join
{:id id}])))
[id])
on-press-info #(rf/dispatch
[:show-bottom-sheet {:content token-gated-communities-info}])
keycard? (rf/sub [:keycard/keycard-profile?])
keycard-feature-unavailable (rn/use-callback
#(rf/dispatch [:keycard/feature-unavailable-show
{:feature-name :community.request-to-join}]))]
(cond
networks-not-supported?
[network-not-supported]
(or (not role-permissions?) no-member-permission?)
[request-access-button id color]
[request-access-button id color keycard? keycard-feature-unavailable]
:else
[quo/community-token-gating
@@ -154,7 +163,7 @@
:tokens tokens
:community-color color
:satisfied? can-request-access?
:on-press on-press
:on-press (if keycard? keycard-feature-unavailable on-press)
:on-press-info on-press-info}])))
(defn- join-community
+3 -1
View File
@@ -8,7 +8,8 @@
[status-im.contexts.keycard.utils :as keycard.utils]
[status-im.contexts.profile.config :as profile.config]
[utils.hex :as hex]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
(defonce ^:private active-listeners (atom []))
@@ -24,6 +25,7 @@
(reset! active-listeners
[(keycard/on-card-connected #(rf/dispatch [:keycard/on-card-connected]))
(keycard/on-card-disconnected #(rf/dispatch [:keycard/on-card-disconnected]))
(keycard/on-card-new-pairing #(rf/dispatch [:keycard/on-card-new-pairing (transforms/js->clj %)]))
(when platform/ios?
(keycard/on-nfc-user-cancelled #(rf/dispatch [:keycard.ios/on-nfc-user-cancelled])))
(when platform/ios?
+39 -14
View File
@@ -9,7 +9,9 @@
status-im.contexts.keycard.pin.events
status-im.contexts.keycard.sign.events
[status-im.contexts.keycard.utils :as keycard.utils]
utils.datetime))
[utils.address :as address]
utils.datetime
[utils.ethereum.eip.eip55 :as eip55]))
(rf/reg-event-fx :keycard/on-card-connected
(fn [{:keys [db]} _]
@@ -23,21 +25,22 @@
:fx [(when-let [event (get-in db [:keycard :on-card-disconnected-event-vector])]
[:dispatch event])]}))
(rf/reg-event-fx :keycard/on-card-new-pairing
(fn [{:keys [db]} [{:keys [pairing instanceUID]}]]
(when (and instanceUID pairing)
(let [pairings (get-in db [:keycard :pairings])
new-pairings (assoc pairings
instanceUID
{:pairing pairing
:paired-on (utils.datetime/timestamp)})]
{:db (assoc-in db [:keycard :pairings] new-pairings)
:keycard/persist-pairings new-pairings}))))
(rf/reg-event-fx :keycard/on-retrieve-pairings-success
(fn [{:keys [db]} [pairings]]
{:db (assoc-in db [:keycard :pairings] pairings)
:fx [[:effects.keycard/set-pairing-to-keycard pairings]]}))
(rf/reg-event-fx :keycard/update-pairings
(fn [{:keys [db]} [instance-uid pairing]]
(let [pairings (get-in db [:keycard :pairings])
new-pairings (assoc pairings
instance-uid
{:pairing pairing
:paired-on (utils.datetime/timestamp)})]
{:db (assoc-in db [:keycard :pairings] new-pairings)
:keycard/persist-pairings new-pairings})))
(rf/reg-event-fx :keycard/on-action-with-pin-error
(fn [{:keys [db]} [error]]
(let [tag-was-lost? (keycard.utils/tag-lost? (:error error))
@@ -65,6 +68,30 @@
(fn [_ [data]]
{:effects.keycard/export-key data}))
(rf/reg-event-fx :keycard/connect-derive-address-and-add-account
(fn [_ [{:keys [pin derivation-path key-uid account-preferences]}]]
{:fx [[:dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/export-key
{:pin pin
:path derivation-path
:on-success (fn [public-key]
(let [derived-account {:public-key (str "0x" public-key)
:address (eip55/address->checksum
(str "0x"
(address/public-key->address
(subs public-key 2))))
:path derivation-path}]
(rf/dispatch [:keycard/disconnect])
(rf/dispatch [:wallet/add-account
(assoc account-preferences :key-uid key-uid)
derived-account])))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]))}]]]}))
(rf/reg-event-fx :keycard/cancel-connection
(fn [{:keys [db]}]
{:db (update db
@@ -100,10 +127,8 @@
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [key-uid on-success on-error]}]]
{:effects.keycard/get-application-info
{:on-success (fn [{:keys [instance-uid new-pairing] :as app-info}]
{:on-success (fn [app-info]
(rf/dispatch [:keycard/update-application-info app-info])
(when (and instance-uid new-pairing)
(rf/dispatch [:keycard/update-pairings instance-uid new-pairing]))
(if-let [error (keycard.utils/validate-application-info key-uid app-info)]
(if on-error
(on-error error)
@@ -0,0 +1,27 @@
(ns status-im.contexts.keycard.feature-unavailable.events
(:require
[status-im.constants :as constants]
[status-im.contexts.keycard.feature-unavailable.view :as feature-unavailable]
[utils.re-frame :as rf]))
(rf/reg-event-fx
:keycard/feature-unavailable-upvote
(fn [_ [{:keys [feature-name]}]]
{:fx [[:dispatch [:open-url constants/mobile-upvote-link]]
[:dispatch
[:centralized-metrics/track
:metric/feature-unavailable-upvote
{:feature-name feature-name}]]]}))
(rf/reg-event-fx
:keycard/feature-unavailable-show
(fn [_ [{:keys [feature-name theme] :as options}]]
{:fx [[:dispatch
[:show-bottom-sheet
{:theme theme
:content (fn []
(feature-unavailable/view options))}]]
[:dispatch
[:centralized-metrics/track
:metric/feature-unavailable
{:feature-name feature-name}]]]}))
@@ -0,0 +1,27 @@
(ns status-im.contexts.keycard.feature-unavailable.view
(:require
[quo.core :as quo]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn on-upvote
[feature-name]
(rf/dispatch [:keycard/feature-unavailable-upvote {:feature-name feature-name}]))
(defn view
[{:keys [feature-name]}]
[:<>
[quo/drawer-top
{:title (i18n/label :t/feature-unavailable)
:description (i18n/label :t/feature-unavailable-for-keycard-description)}]
[quo/information-box
{:type :default
:icon :i/info
:style {:margin-top 8 :margin-horizontal 20}}
[:<>
(i18n/label :t/feature-unavailable-info)
[quo/text
{:style {:text-decoration-line :underline}
:size :paragraph-2
:on-press #(on-upvote feature-name)}
(i18n/label :t/upvote-it)]]]])
@@ -91,7 +91,7 @@
:title (i18n/label :t/log-in-by-syncing)
:subtitle (i18n/label :t/log-in-by-syncing-subtitle)
:accessibility-label :log-in-by-syncing-icon-card
:image (resources/get-image :ethereum-address)
:image (resources/get-image :login-syncing)
:on-press show-check-before-syncing}])
(defn- use-empty-keycard-icon-card
@@ -4,7 +4,7 @@
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[status-im.common.terms.view :as terms]
[status-im.common.privacy.view :as privacy]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -26,7 +26,7 @@
[quo/text
{:size :paragraph-2
:weight :bold
:on-press #(rf/dispatch [:show-bottom-sheet {:content terms/terms-of-use :shell? true}])}
:on-press #(rf/dispatch [:show-bottom-sheet {:content privacy/privacy-statement :shell? true}])}
(i18n/label :t/more-details-in-privacy-policy-2)]]])
(defn- bullet-points
@@ -1,4 +1,5 @@
(ns status-im.contexts.onboarding.share-usage.style)
(ns status-im.contexts.onboarding.share-usage.style
(:require [quo.foundations.colors :as colors]))
(def title-container
{:margin-horizontal 20
@@ -16,3 +17,7 @@
[insets]
{:margin 20
:margin-bottom (+ 12 (:bottom insets))})
(def page
{:flex 1
:background-color colors/neutral-100})
@@ -17,35 +17,38 @@
(rf/dispatch [:navigate-to-within-stack [next-screen :screen/onboarding.share-usage]]) ;; Onboarding
(rf/dispatch [:navigate-back]))) ;; Login Screen
(defn- learn-more
[]
(rf/dispatch [:show-bottom-sheet
{:content learn-more-sheet/view
:shell? true}]))
(defn view
[]
(let [insets (safe-area/get-insets)
next-screen (:next-screen (rf/sub [:get-screen-params :screen/onboarding.share-usage]))
share-usage-data (rn/use-callback #(share-usage-data-fn true next-screen))
maybe-later (rn/use-callback #(share-usage-data-fn false next-screen))
learn-more (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content learn-more-sheet/view
:shell? true}]))]
[:<>
maybe-later (rn/use-callback #(share-usage-data-fn false next-screen))]
[rn/view {:style style/page}
[quo/page-nav
{:margin-top (:top insets)
:background :blur
:icon-name :i/arrow-left
:icon-name :i/close
:on-press events-helper/navigate-back
:right-side [{:icon-left :i/info
:accessibility-label :learn-more
:label (i18n/label :t/learn-more)
:on-press learn-more}]}]
[quo/text-combinations
{:container-style style/title-container
:title (i18n/label :t/help-us-improve-status)
[quo/page-top
{:title (i18n/label :t/help-us-improve-status)
:title-accessibility-label :share-usage-title
:description (i18n/label :t/collecting-usage-data)
:description :text
:description-text (i18n/label :t/collecting-usage-data)
:description-accessibility-label :share-usage-subtitle}]
[rn/image
{:resize-mode :contain
:style (style/page-illustration (:width (rn/get-window)))
:source (resources/get-image :biometrics)}]
:source (resources/get-image :usage-data)}]
[rn/view {:style (style/buttons insets)}
[quo/button
{:size 40
@@ -47,7 +47,8 @@
settings
{:log-level log-level}))
(assoc-in [:activity-center :loading?] true)
(dissoc :centralized-metrics/onboarding-enabled?))]
(dissoc :centralized-metrics/onboarding-enabled?))
keycard? (get-in new-db [:profile/profile :keycard-pairing])]
{:db (cond-> new-db
pairing-completed? (dissoc :syncing))
:fx (into [[:json-rpc/call
@@ -75,8 +76,10 @@
[:effects.chat/open-last-chat (:key-uid profile-overview)])
(when (:centralized-metrics/onboarding-enabled? db)
[:dispatch [:profile.settings/toggle-telemetry true]])]
[:dispatch [:profile.settings/toggle-telemetry true]])
(when keycard?
[:dispatch [:centralized-metrics/track :metric/keycard-login]])]
(cond
pairing-completed?
[[:dispatch [:update-theme-and-init-root :screen/onboarding.syncing-results]]]
@@ -125,7 +125,7 @@
:blur? true
:image-props :i/light})]
[{:title (i18n/label :t/about)
:on-press #(rf/dispatch [:open-modal :about-app])
:on-press #(rf/dispatch [:open-modal :screen/settings.about])
:action :arrow
:blur? true}
{:title (i18n/label :t/status-help)
@@ -7,33 +7,39 @@
[utils.re-frame :as rf]))
(defn- on-press-biometric-enable
[button-label theme]
[button-label theme keycard-profile?]
(fn []
(rf/dispatch
[:standard-auth/authorize-with-password
{:blur? true
:theme theme
:auth-button-label (i18n/label :t/biometric-enable-button {:bio-type-label button-label})
:on-auth-success (fn [password]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:biometric/authenticate
{:on-success #(rf/dispatch [:biometric/enable password])
:on-fail #(rf/dispatch [:biometric/show-message (ex-cause %)])}]))}])))
(if keycard-profile?
(rf/dispatch [:keycard/feature-unavailable-show
{:theme :dark
:feature-name :settings.enable-biometrics}])
(rf/dispatch
[:standard-auth/authorize-with-password
{:blur? true
:theme theme
:auth-button-label (i18n/label :t/biometric-enable-button {:bio-type-label button-label})
:on-auth-success (fn [password]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:biometric/authenticate
{:on-success #(rf/dispatch [:biometric/enable password])
:on-fail #(rf/dispatch [:biometric/show-message
(ex-cause %)])}]))}]))))
(defn- get-biometric-item
[theme]
(let [auth-method (rf/sub [:auth-method])
biometric-type (rf/sub [:biometrics/supported-type])
label (biometric/get-label-by-type biometric-type)
icon (biometric/get-icon-by-type biometric-type)
supported? (boolean biometric-type)
enabled? (= auth-method constants/auth-method-biometric)
biometric-on? (and supported? enabled?)
press-handler (when supported?
(if biometric-on?
(fn [] (rf/dispatch [:biometric/disable]))
(on-press-biometric-enable label theme)))]
(let [auth-method (rf/sub [:auth-method])
biometric-type (rf/sub [:biometrics/supported-type])
keycard-profile? (rf/sub [:keycard/keycard-profile?])
label (biometric/get-label-by-type biometric-type)
icon (biometric/get-icon-by-type biometric-type)
supported? (boolean biometric-type)
enabled? (= auth-method constants/auth-method-biometric)
biometric-on? (and supported? enabled?)
press-handler (when supported?
(if biometric-on?
(fn [] (rf/dispatch [:biometric/disable]))
(on-press-biometric-enable label theme keycard-profile?)))]
{:title label
:image-props icon
:image :icon
@@ -58,7 +64,8 @@
(defn view
[]
(let [theme (quo.theme/use-theme)]
(let [theme (quo.theme/use-theme)
keycard-profile? (rf/sub [:keycard/keycard-profile?])]
[quo/overlay {:type :shell :top-inset? true}
[quo/page-nav
{:background :blur
@@ -68,6 +75,7 @@
[quo/category
{:key :category
:data [(get-biometric-item theme)
(get-change-password-item)]
(when-not keycard-profile?
(get-change-password-item))]
:blur? true
:list-type :settings}]]))
@@ -0,0 +1,9 @@
(ns status-im.contexts.settings.about.style)
(def category-spacing {:padding-bottom 12})
(def app-info-container
{:padding-horizontal 20
:padding-top 8
:padding-bottom 16
:row-gap 16})
@@ -0,0 +1,101 @@
(ns status-im.contexts.settings.about.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.settings.about.style :as style]
[status-im.contexts.settings.common.header :as header]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- open-link
[url]
(rf/dispatch [:browser.ui/open-url url]))
(defn- copy
[data item-name]
(rf/dispatch [:share/copy-text-and-show-toast
{:text-to-copy data
:post-copy-message (str item-name
" "
(i18n/label :t/sharing-copied-to-clipboard))}]))
(def about-data
[{:app-info? true}
{:category-label (i18n/label :t/website)
:items [{:title "status.app"
:on-press #(open-link constants/status-app-url)
:blur? true
:action :arrow
:action-props {:icon :i/external}}]}
{:category-label (i18n/label :t/github-repos)
:items [{:title "status-mobile"
:on-press #(open-link constants/status-mobile-url)
:blur? true
:action :arrow
:action-props {:icon :i/external}}
{:title "status-go"
:on-press #(open-link constants/status-go-url)
:blur? true
:action :arrow
:action-props {:icon :i/external}}
{:title "go-waku"
:on-press #(open-link constants/go-waku-url)
:blur? true
:action :arrow
:action-props {:icon :i/external}}]}
{:category-label (i18n/label :t/documents)
:items [{:title (i18n/label :t/privacy-policy)
:on-press #(rf/dispatch [:open-modal :screen/settings.privacy-policy])
:blur? true
:action :arrow}
{:title (i18n/label :t/terms-of-service)
:on-press #(rf/dispatch [:open-modal :screen/settings.terms-of-use])
:blur? true
:action :arrow}]}])
(defn info-item
[{:keys [title info]}]
[quo/data-item
{:size :default
:status :default
:right-icon :i/copy
:card? true
:blur? true
:title title
:on-press #(copy info title)
:subtitle info
:subtitle-type :default
:subtitle-text-props {:number-of-lines 1
:ellipsize-mode :middle}}])
(defn- app-info
[]
(let [app-version (rf/sub [:get-app-short-version])
commit-hash (rf/sub [:get-commit-hash])
node-version (rf/sub [:get-app-node-version])]
[rn/view {:style style/app-info-container}
[info-item {:title (i18n/label :t/version) :info app-version}]
[info-item {:title (i18n/label :t/app-commit) :info commit-hash}]
[info-item {:title (i18n/label :t/node-version) :info node-version}]]))
(defn category
[{:keys [app-info? category-label items]}]
(if app-info?
[app-info]
[quo/category
{:label category-label
:list-type :settings
:container-style style/category-spacing
:blur? true
:data items}]))
(defn view
[]
[quo/overlay {:type :shell}
[header/view {:title (i18n/label :t/about)}]
[rn/flat-list
{:data about-data
:shows-vertical-scroll-indicator false
:render-fn category
:bounces false
:over-scroll-mode :never}]])
@@ -0,0 +1,14 @@
(ns status-im.contexts.settings.common.header
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]))
(defn view
[{:keys [title]}]
[rn/view {:style {:padding-top (safe-area/get-top)}}
[quo/page-nav
{:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top {:title title}]])
@@ -41,7 +41,7 @@
(defn- keypair
[{keypair-type :type
:keys [accounts name]
:keys [accounts name keycard?]
:as item}
_ _
{:keys [profile-picture compressed-key customization-color]}]
@@ -59,7 +59,7 @@
:type (if default-keypair?
:default-keypair
:keypair)
:stored :on-device
:stored (if keycard? :on-keycard :on-device)
:shortened-key shortened-key
:customization-color customization-color
:profile-picture profile-picture})}))
@@ -68,7 +68,7 @@
[quo/keypair
{:blur? true
:status-indicator false
:stored :on-device
:stored (if keycard? :on-keycard :on-device)
:action (if default-keypair? :none :options)
:accounts accounts
:customization-color customization-color
@@ -10,7 +10,6 @@
[status-im.feature-flags :as ff]
[utils.address :as utils-address]
[utils.debounce :as debounce]
[utils.ethereum.eip.eip681 :as eip681]
[utils.i18n :as i18n]
[utils.url :as url]))
@@ -28,13 +27,6 @@
(let [index (string/index-of scanned-text "#")]
(subs scanned-text (inc index))))
(defn eip681-address?
[scanned-text]
(-> scanned-text
eip681/parse-uri
:address
boolean))
(defn pairing-qr-code?
[_]
false)
@@ -88,10 +80,10 @@
(debounce/debounce-and-dispatch [:generic-scanner/scan-success address] 300)
(debounce/debounce-and-dispatch [:shell/change-tab :wallet-stack] 300))
(eip681-address? scanned-text)
(utils-address/eip-681-address? scanned-text)
(do
(debounce/debounce-and-dispatch [:wallet-legacy/request-uri-parsed
(eip681/parse-uri scanned-text)]
(debounce/debounce-and-dispatch [:generic-scanner/scan-success
(utils-address/eip-681-address->eth-address scanned-text)]
300)
(debounce/debounce-and-dispatch [:shell/change-tab :wallet-stack] 300))
@@ -38,7 +38,13 @@
#(if (:enabled? %)
:paired-devices
:unpaired-devices)
other-devices)]
other-devices)
keycard? (rf/sub [:keycard/keycard-profile?])
keycard-feature-unavailable (rn/use-callback
#(rf/dispatch
[:keycard/feature-unavailable-show
{:theme :dark
:feature-name :settings.setup-syncing}]))]
[quo/overlay {:type :shell :top-inset? true}
[quo/page-nav
{:type :no-title
@@ -60,7 +66,9 @@
:type :primary
:customization-color profile-color
:icon-only? true
:on-press open-setup-syncing-with-customization-color}
:on-press (if keycard?
keycard-feature-unavailable
open-setup-syncing-with-customization-color)}
:i/add]]
[device/view (merge user-device {:this-device? true})]
(when (seq paired-devices)
@@ -69,7 +69,9 @@
(let [{:keys [customization-color] :as profile} (rf/sub [:profile/profile-with-image])
{:keys [address path watch-only?]} (rf/sub [:wallet/current-viewing-account])
{keypair-name :name
keypair-type :type} (rf/sub [:wallet/current-viewing-account-keypair])
keypair-type :type
keycards :keycards} (rf/sub [:wallet/current-viewing-account-keypair])
keypair-keycard? (boolean (seq keycards))
networks (rf/sub [:wallet/network-preference-details])
origin-type (case keypair-type
:seed
@@ -98,7 +100,7 @@
(when (not watch-only?)
[quo/account-origin
{:type origin-type
:stored :on-device
:stored (if keypair-keycard? :on-keycard :on-device)
:profile-picture (profile.utils/photo profile)
:customization-color customization-color
:derivation-path path
@@ -58,13 +58,14 @@
[item _ _
{:keys [profile-picture compressed-key selected-key-uid set-selected-key-uid customization-color]}]
(let [profile-keypair? (= (:type item) :profile)
keycard? (boolean (seq (:keycards item)))
accounts (parse-accounts (:accounts item))]
[quo/keypair
{:customization-color customization-color
:profile-picture (when profile-keypair? profile-picture)
:status-indicator false
:type (if profile-keypair? :default-keypair :other)
:stored :on-device
:stored (if keycard? :on-keycard :on-device)
:on-options-press #(js/alert "Options pressed")
:action :selector
:blur? false
@@ -22,7 +22,7 @@
[utils.string]))
(defn- get-keypair-data
[{:keys [title primary-keypair? new-keypair? derivation-path customization-color]}]
[{:keys [title primary-keypair? new-keypair? derivation-path customization-color keycard?]}]
[{:title title
:image (if primary-keypair? :avatar :icon)
:image-props (if primary-keypair?
@@ -35,7 +35,7 @@
:button-text (i18n/label :t/edit)
:alignment :flex-start}
:description :text
:description-props {:text (i18n/label :t/on-device)}}
:description-props {:text (i18n/label (if keycard? :on-keycard :on-device))}}
(when-not (string/blank? derivation-path)
(let [formatted-path (string/replace derivation-path #"/" " / ")
on-auth-success (fn [password]
@@ -123,7 +123,7 @@
:container-style color-picker-style}]])])))
(defn- new-account-origin
[{:keys [keypair-title derivation-path customization-color]}]
[{:keys [keypair-title derivation-path customization-color keycard?]}]
(let [{keypair-name :name} (rf/sub [:wallet/selected-keypair])
primary? (rf/sub [:wallet/selected-primary-keypair?])
keypair-name (or keypair-title
@@ -135,7 +135,8 @@
[quo/category
{:list-type :settings
:label (i18n/label :t/origin)
:data (get-keypair-data {:primary-keypair? primary?
:data (get-keypair-data {:keycard? keycard?
:primary-keypair? primary?
:title keypair-name
:derivation-path derivation-path
:customization-color customization-color})}]]))
@@ -229,20 +230,37 @@
set-derivation-path #(reset! derivation-path %)]
(fn [{:keys [on-change-text set-account-color set-emoji customization-color error]}]
(let [{:keys [derived-from
key-uid]} (rf/sub [:wallet/selected-keypair])
on-auth-success (rn/use-callback
(fn [password]
(let [preferences {:account-name @account-name
:color @account-color
:emoji @emoji}]
(rf/dispatch
[:wallet/derive-address-and-add-account
{:password password
:derived-from-address derived-from
:key-uid key-uid
:derivation-path @derivation-path
:account-preferences preferences}])))
[derived-from])]
key-uid keycards]} (rf/sub [:wallet/selected-keypair])
keycard? (boolean (seq keycards))
on-auth-success (rn/use-callback
(fn [password]
(let [preferences {:account-name @account-name
:color @account-color
:emoji @emoji}]
(rf/dispatch
[:wallet/derive-address-and-add-account
{:password password
:derived-from-address derived-from
:key-uid key-uid
:derivation-path @derivation-path
:account-preferences preferences}])))
[derived-from])
on-complete (rn/use-callback
(fn []
(let [preferences {:account-name @account-name
:color @account-color
:emoji @emoji}]
(rf/dispatch
[:standard-auth/authorize-with-keycard
{:on-complete
#(rf/dispatch
[:keycard/connect-derive-address-and-add-account
{:pin %
:derived-from-address derived-from
:key-uid key-uid
:derivation-path @derivation-path
:account-preferences preferences}])}])))
[derived-from])]
(rn/use-effect
#(rf/dispatch
[:wallet/next-derivation-path
@@ -252,10 +270,13 @@
[floating-button
{:account-color @account-color
:slide-button-props {:on-auth-success on-auth-success
:disabled? (or (empty? @account-name)
(string/blank? @derivation-path)
(some? error))}}
:slide-button-props {:on-auth-success (when-not keycard? on-auth-success)
:on-complete
(when keycard? on-complete)
:disabled?
(or (empty? @account-name)
(string/blank? @derivation-path)
(some? error))}}
[avatar
{:account-color @account-color
:emoji @emoji
@@ -269,7 +290,8 @@
{:account-color @account-color
:set-account-color set-account-color}]
[new-account-origin
{:derivation-path @derivation-path
{:keycard? keycard?
:derivation-path @derivation-path
:customization-color customization-color}]]))))
(defn view
@@ -1,5 +1,6 @@
(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]
@@ -14,34 +15,28 @@
(defn- bridge-token-component
[]
(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)]
(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)]
[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 (and token-available-on-network? (not= chain-id selected-network-chain-id))
:default
:disabled)
:state (if token-available-on-network? :default :disabled)
:on-press #(rf/dispatch [:wallet/select-bridge-network
{:network-chain-id chain-id
:stack-id :screen/wallet.bridge-to}])}])))
@@ -51,7 +46,6 @@
(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)
@@ -61,7 +55,8 @@
(assoc account-token
:networks (:networks token)
:supported-networks (:supported-networks token)))
bridge-to-title (i18n/label :t/select-network-to-receive)]
bridge-to-title (i18n/label :t/bridge-to
{:name (string/upper-case (str token-symbol))})]
(hot-reload/use-safe-unmount #(rf/dispatch [:wallet/clean-bridge-to-selection]))
@@ -73,11 +68,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 selected-network]]
[bridge-token-component (assoc mainnet :network-name :t/mainnet) account-token]]
[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 selected-network])
[bridge-token-component network account-token])
:content-container-style style/content-container}]]))
@@ -1,7 +1,9 @@
(ns status-im.contexts.wallet.bridge.flow-config)
(def steps
[{:screen-id :screen/wallet.bridge-select-asset
[{: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
: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])))}
@@ -30,7 +30,8 @@
type :no-title}}]
(let [{:keys [color emoji watch-only?]} (rf/sub [:wallet/current-viewing-account])
networks (rf/sub [:wallet/selected-network-details])
sending-collectible? (rf/sub [:wallet/sending-collectible?])]
sending-collectible? (rf/sub [:wallet/sending-collectible?])
keycard? (rf/sub [:wallet/selected-keypair-keycard?])]
[quo/page-nav
{:type type
:icon-name icon-name
@@ -45,7 +46,11 @@
(not watch-only?)
show-dapps-button?)
{:icon-name :i/dapps
:on-press #(rf/dispatch [:navigate-to :screen/wallet.connected-dapps])})
:on-press #(rf/dispatch
(if keycard?
[:keycard/feature-unavailable-show
{:feature-name :wallet.show-connected-dapps}]
[:navigate-to :screen/wallet.connected-dapps]))})
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
@@ -13,7 +13,7 @@
{:title (i18n/label :t/scan-qr)
:subtitle (i18n/label :t/scan-an-address-qr-code)
:error-message (i18n/label :t/oops-this-qr-does-not-contain-an-address)
:validate-fn #(utils-address/supported-address? %)
:validate-fn #(utils-address/supported-scan-address? %)
:on-success-scan (fn [result]
(let [address (utils-address/supported-address->eth-address result)]
(when on-result (on-result address))
@@ -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?}
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?
:asset-to-receive asset-to-receive}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
@@ -108,13 +108,9 @@
(action-send params entry-point))
(action-receive selected-account)
(when token-owned?
(action-swap (assoc params
:asset-to-receive
asset-to-receive)))
(action-swap params))
(when token-owned?
(action-bridge (assoc params
:network-details
network-details
:bridge-disabled?
(send-utils/bridge-disabled? token-symbol))))]))]]))
@@ -1,11 +1,23 @@
(ns status-im.contexts.wallet.common.wizard.events
(:require [status-im.contexts.wallet.common.wizard.utils :as 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]
[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-utils/wizard-find-next-screen db flow-id current-screen)]
(let [next-screen (wizard-find-next-screen db flow-id current-screen)]
{:fx [[:dispatch
(if start-flow?
[:open-modal (:screen-id next-screen)]
@@ -1,14 +0,0 @@
(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))))
@@ -291,3 +291,10 @@
(cske/transform-keys transforms/->kebab-case-keyword)
(map #(assoc % :unique-id (collectible-utils/get-collectible-unique-id %)))
vec))
(defn selected-keypair-keycard?
[db]
(let [keypairs (get-in db [:wallet :keypairs])
selected-keypair-uid (get-in db [:wallet :ui :create-account :selected-keypair-uid])
keypair (get keypairs selected-keypair-uid)]
(boolean (seq (:keycards keypair)))))
@@ -231,3 +231,37 @@
:wallet true
:default-account? true})]}}}
(sut/reconcile-keypairs [raw-keypair-profile])))))
(def mock-db
{:wallet {:keypairs
{"0x123" {:type "key"
:key-uid "0x123"
:keycards [{:id "keycard1"}]}
"0x456" {:type "key"
:key-uid "0x456"
:keycards []}}
:ui {:create-account {:selected-keypair-uid "0x123"}}}})
(deftest selected-keypair-keycard?-test
(testing "returns true when the selected keypair has keycards"
(is (true? (sut/selected-keypair-keycard? mock-db))))
(testing "returns false when the selected keypair does not have keycards"
(let [db (assoc-in mock-db [:wallet :ui :create-account :selected-keypair-uid] "0x456")]
(is (false? (sut/selected-keypair-keycard? db)))))
(testing "returns false when the selected keypair does not exist"
(let [db (assoc-in mock-db [:wallet :ui :create-account :selected-keypair-uid] "0x789")]
(is (false? (sut/selected-keypair-keycard? db)))))
(testing "returns false when keypairs map is empty"
(let [db (assoc-in mock-db [:wallet :keypairs] {})]
(is (false? (sut/selected-keypair-keycard? db)))))
(testing "returns false when no keypair is selected"
(let [db (assoc-in mock-db [:wallet :ui :create-account :selected-keypair-uid] nil)]
(is (false? (sut/selected-keypair-keycard? db)))))
(testing "returns false when db does not contain wallet data"
(let [db {}]
(is (false? (sut/selected-keypair-keycard? db))))))
+46 -78
View File
@@ -10,13 +10,11 @@
[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]
@@ -116,7 +114,7 @@
(rf/reg-event-fx
:wallet/fetch-assets-for-address
(fn [_ [address]]
{:fx [[:dispatch [:wallet/get-wallet-token-for-account address]]
{:fx [[:dispatch [:wallet/get-wallet-token-for-accounts [address]]]
[:dispatch [:wallet/request-collectibles-for-account address]]]}))
(defn- reconcile-accounts
@@ -206,34 +204,42 @@
(rf/reg-event-fx :wallet/get-wallet-token-for-all-accounts
(fn [{:keys [db]}]
{:fx (->> (get-in db [:wallet :accounts])
vals
(mapv
(fn [{:keys [address]}]
[:dispatch [:wallet/get-wallet-token-for-account address]])))}))
(let [addresses (->> (get-in db [:wallet :accounts])
(vals)
(keep :address)
(vec))]
{:fx [[:dispatch [:wallet/get-wallet-token-for-accounts addresses]]]})))
(rf/reg-event-fx :wallet/get-wallet-token-for-account
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :ui :tokens-loading address] true)
(rf/reg-event-fx :wallet/get-wallet-token-for-accounts
(fn [{:keys [db]} [addresses]]
{:db (reduce
(fn [db address]
(assoc-in db [:wallet :ui :tokens-loading address] true))
db
addresses)
:fx [[:json-rpc/call
[{:method "wallet_fetchOrGetCachedWalletBalances"
:params [[address] true]
:on-success [:wallet/store-wallet-token address]
:on-error [:wallet/get-wallet-token-for-account-failed address]}]]]}))
:params [addresses true]
:on-success [:wallet/store-wallet-token addresses]
:on-error [:wallet/get-wallet-token-for-accounts-failed addresses]}]]]}))
(rf/reg-event-fx
:wallet/get-wallet-token-for-account-failed
(fn [{:keys [db]} [address error]]
:wallet/get-wallet-token-for-accounts-failed
(fn [{:keys [db]} [addresses error]]
(log/info "failed to get wallet token "
{:error error
:event :wallet/get-wallet-token-for-account
:params address})
:event :wallet/get-wallet-token-for-accounts
:params addresses})
{:fx [[:dispatch [:wallet/get-last-wallet-token-update-if-needed]]]
:db (assoc-in db [:wallet :ui :tokens-loading address] false)}))
:db (reduce
(fn [db address]
(assoc-in db [:wallet :ui :tokens-loading address] false))
db
addresses)}))
(rf/reg-event-fx
:wallet/store-wallet-token
(fn [{:keys [db]} [address raw-tokens-data]]
(fn [{:keys [db]} [addresses raw-tokens-data]]
(let [supported-chains-by-token-symbol (get-in db [:wallet :tokens :supported-chains-by-symbol])
profile-currency (get-in db [:profile/profile :currency])
tokens (data-store/rpc->tokens raw-tokens-data
@@ -246,13 +252,18 @@
accounts))
stored-accounts
tokens-per-account))
symbols (reduce-kv (fn [acc _ v]
(into acc (map :symbol v)))
#{}
tokens)]
symbols (reduce-kv
(fn [acc _ tokens-data]
(into acc (map :symbol tokens-data)))
#{}
tokens)]
{:db (-> db
(update-in [:wallet :accounts] add-tokens tokens)
(assoc-in [:wallet :ui :tokens-loading address] false))
((fn [db]
(reduce (fn [db address]
(assoc-in db [:wallet :ui :tokens-loading address] false))
db
addresses))))
:fx [[:dispatch [:wallet/get-last-wallet-token-update-if-needed]]
[:effects.wallet.tokens/fetch-market-values
{:symbols symbols
@@ -309,9 +320,8 @@
(rf/reg-event-fx :wallet/add-account
(fn [_
[{:keys [key-uid password account-name emoji color type]
:or {type :generated}}
{:keys [public-key address path] :as _derived-account}]]
[{:keys [key-uid password account-name emoji color type] :or {type :generated}}
{:keys [public-key address path]}]]
(let [lowercase-address (some-> address
string/lower-case)
account-config {:key-uid (when (= type :generated) key-uid)
@@ -345,61 +355,19 @@
(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 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))]
(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)]
{: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 (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)]))}])}]]]))})))
:fx [[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]})))
(rf/reg-event-fx :wallet/start-bridge
(fn [{:keys [db]}]
@@ -116,20 +116,19 @@
address-2 "0x2"]
(reset! rf-db/app-db {:wallet {:accounts {address-1 {:address address-1}
address-2 {:address address-2}}}})
(is (match? [[:dispatch [:wallet/get-wallet-token-for-account address-1]]
[:dispatch [:wallet/get-wallet-token-for-account address-2]]]
(is (match? [[:dispatch [:wallet/get-wallet-token-for-accounts [address-1 address-2]]]]
(:fx (dispatch [event-id]))))))
(h/deftest-event :wallet/get-wallet-token-for-account
(h/deftest-event :wallet/get-wallet-token-for-accounts
[event-id dispatch]
(let [expected-effects {:db {:wallet {:ui {:tokens-loading {address true}}}}
:fx [[:json-rpc/call
[{:method "wallet_fetchOrGetCachedWalletBalances"
:params [[address] true]
:on-success [:wallet/store-wallet-token address]
:on-error [:wallet/get-wallet-token-for-account-failed
address]}]]]}]
(is (match? expected-effects (dispatch [event-id address])))))
:on-success [:wallet/store-wallet-token [address]]
:on-error [:wallet/get-wallet-token-for-accounts-failed
[address]]}]]]}]
(is (match? expected-effects (dispatch [event-id [address]])))))
(h/deftest-event :wallet/reconcile-keypairs
[event-id dispatch]
+18 -13
View File
@@ -15,19 +15,24 @@
(defn new-account
[]
[quo/action-drawer
[[{:icon :i/add
:accessibility-label :start-a-new-chat
:label (i18n/label :t/add-account)
:sub-label (i18n/label :t/add-account-description)
:on-press #(rf/dispatch [:navigate-to :screen/wallet.create-account])}
(when (ff/enabled? ::ff/wallet.add-watched-address)
{:icon :i/reveal
:accessibility-label :add-a-contact
:label (i18n/label :t/add-address-to-watch)
:sub-label (i18n/label :t/add-address-to-watch-description)
:on-press #(rf/dispatch [:navigate-to :screen/wallet.add-address-to-watch])
:add-divider? true})]]])
(let [keycard? (rf/sub [:keycard/keycard-profile?])]
[quo/action-drawer
[[{:icon :i/add
:accessibility-label :start-a-new-chat
:label (i18n/label :t/add-account)
:sub-label (i18n/label :t/add-account-description)
:on-press (fn []
(if keycard?
(rf/dispatch [:keycard/feature-unavailable-show
{:feature-name :wallet.new-account}])
(rf/dispatch [:navigate-to :screen/wallet.create-account])))}
(when (ff/enabled? ::ff/wallet.add-watched-address)
{:icon :i/reveal
:accessibility-label :add-a-contact
:label (i18n/label :t/add-address-to-watch)
:sub-label (i18n/label :t/add-address-to-watch-description)
:on-press #(rf/dispatch [:navigate-to :screen/wallet.add-address-to-watch])
:add-divider? true})]]]))
(defn- new-account-card-data
[]
+127 -132
View File
@@ -5,10 +5,8 @@
[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]
@@ -150,16 +148,23 @@
(rf/reg-event-fx
:wallet/select-send-address
(fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]]
(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))]
(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))]
{: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 :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))
:fx [(when (and collectible-tx? one-collectible?)
[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]])
[:dispatch
@@ -177,12 +182,14 @@
(rf/reg-event-fx
:wallet/set-token-to-send
(fn [{:keys [db]}
[{:keys [token-symbol token network stack-id start-flow? owners] :as params} entry-point]]
(fn [{:keys [db]} [{:keys [token-symbol token stack-id start-flow? owners]} 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 [wallet]} db
(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)
unique-owner (when (= (count owners) 1)
(first owners))
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
@@ -190,25 +197,17 @@
(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))
next-screen (:screen-id (wizard-utils/wizard-find-next-screen db
:wallet-send-flow
stack-id))]
(network-utils/sorted-networks-with-details))]
(when (or token-data token-symbol)
{:db (cond-> db
network (update-in [:wallet :ui :send]
:always (update-in [:wallet :ui :send]
#(-> %
(dissoc :collectible :tx-type)
(assoc :network network)))
(assoc :token-not-supported-in-receiver-networks?
unsupported-token?)))
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
token-data (update-in [:wallet :ui :send]
#(assoc %
@@ -217,35 +216,20 @@
(network-utils/network-list
token-data
network-details))
:token-display-name (:symbol token-data)
:token-symbol (:symbol token-data)))
:token-display-name (: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 (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)]))}])}]]]))}))))
: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}]]]}))))
(rf/reg-event-fx
:wallet/edit-token-to-send
@@ -304,7 +288,7 @@
collectible
(str (:name collection-data) " #" collectible-id))
owner-address (-> collectible :ownership first :address)
owner-address (-> db :wallet :current-viewing-account-address)
collectible-tx (-> db
(update-in [:wallet :ui :send] dissoc :token)
(assoc-in [:wallet :ui :send :collectible] collectible)
@@ -420,62 +404,89 @@
(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
network bridge-to-chain-id]
receiver-networks disabled-from-chain-ids
from-locked-amounts 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
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)}]]
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)}]]
(when (and to-address from-address amount-in token-id)
{:db (update-in db
[:wallet :ui :send]
@@ -701,7 +712,7 @@
(rf/reg-event-fx
:wallet/select-from-account
(fn [{db :db} [{:keys [address stack-id network-details network start-flow?] :as params}]]
(fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]]
(let [{:keys [token-symbol
tx-type]} (-> db :wallet :ui :send)
token (when token-symbol
@@ -713,38 +724,22 @@
: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 (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)]))}])}]]])})))
: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}]]]})))
(rf/reg-event-fx
:wallet/transaction-confirmation-navigate-back
@@ -1,7 +1,6 @@
(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]
@@ -580,23 +579,8 @@
[event-id dispatch]
(let [stack-id :screen/stack
start-flow? false
address "0x01"
network {:chain-id 1}]
address "0x01"]
(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
@@ -612,7 +596,6 @@
(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
@@ -629,7 +612,6 @@
(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
@@ -698,6 +680,5 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?
:netork-details network-details}])))))))
@@ -14,7 +14,9 @@
(-> db :wallet :ui :send :token some?))
(def steps
[{:screen-id :screen/wallet.select-address
[{: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
: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,28 +15,27 @@
(rf/dispatch [:wallet/select-from-account
{:address address
:network-details network-details
:stack-id :screen/wallet.select-from
:start-flow? true}]))
:stack-id :screen/wallet.select-from}]))
(defn- on-close
[]
(rf/dispatch [:wallet/clean-current-viewing-account]))
(defn- render-fn
[item _ _ {:keys [network-details]}]
(let [has-balance (money/above-zero? (:asset-pay-balance item))]
(let [transformed-address (rf/sub [:wallet/account-address (:address item)
(:network-preferences-names item)])]
[quo/account-item
{: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)}
{:on-press #(on-account-press (:address item) network-details)
:account-props (assoc item
:address (:formatted-address item)
:address transformed-address
:full-address? true)}]))
(defn view
[]
(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])
(let [accounts (rf/sub [:wallet/accounts-with-current-asset])
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
@@ -102,6 +102,7 @@
:wallet/wallet-send-tx-type :tx/send
:wallet/wallet-send-fee-fiat-formatted "$5,00"
:wallet/sending-collectible? false
:wallet/selected-keypair-keycard? false
:wallet/send-total-amount-formatted "250 ETH"
:wallet/total-amount (money/bignumber "250")
:wallet/prices-per-token {:ETH {:usd 10}}
@@ -30,16 +30,19 @@
(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])]
(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}])))
[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))]))
(defn view
[{:keys [loading-routes? fees]}]
@@ -59,6 +62,6 @@
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/max-fees)
:title (i18n/label :t/fees)
:subtitle fees}]
[received-amount {:loading-routes? loading-routes?}]])
@@ -25,9 +25,10 @@
:height 40})
(def fees-data-item
{:flex 1
:height 40
:background-color :transparent})
{:flex 1
:height 40
:margin-horizontal 16
:background-color :transparent})
(def amount-data-item
{:flex 1
@@ -2,6 +2,7 @@
(: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]
@@ -14,18 +15,29 @@
[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-token)
{:title (i18n/label :t/select-asset)
:container-style {:padding-bottom 8}}]
[asset-list/view
{:content-container-style {:padding-horizontal 8
@@ -35,6 +47,37 @@
(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}
@@ -76,7 +119,7 @@
(defn- insufficient-asset-amount?
[{:keys [token-symbol owned-eth-token input-state no-routes-found? limit-exceeded?
enough-assets?]}]
sender-network-values 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
@@ -86,6 +129,7 @@
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
@@ -98,104 +142,122 @@
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])
{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)
[(: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])
(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?)
[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?)))]
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])
{: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-effect
(fn []
(when active-screen?
@@ -225,9 +287,8 @@
[current-address])
(rn/use-effect
(fn []
(when active-screen?
(request-fetch-routes 2000)))
[amount-in-crypto valid-input?])
(request-fetch-routes 0))
[send-from-locked-amounts])
[rn/view
{:style style/screen
:accessibility-label (str "container"
@@ -255,7 +316,7 @@
conversion-rate)
conversion-rate))
:hint-component [quo/network-tags
{:networks (seq [network])
{:networks (seq from-enabled-networks)
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
@@ -267,9 +328,17 @@
(controlled-input/upper-limit-bn
input-state)))})
:status (when (controlled-input/input-error input-state) :error)}]}]
(if (= tx-type :tx/bridge)
[routes/view {:token token-by-symbol}]
[rn/view {:style {:flex 1}}])
[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])
(when not-enough-asset?
[not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route))
@@ -290,10 +359,14 @@
loading-routes?
(and (not should-try-again?)
confirm-disabled?))
:on-press (if should-try-again?
:on-press (cond
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,3 +34,19 @@
: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,23 +1,87 @@
(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 receiver? loading-routes?
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[rn/view
(doall
@@ -32,19 +96,27 @@
(= network-value-type :not-available)
:disabled
:else network-value-type)
amount-formatted (-> total-amount
(common-utils/sanitized-token-amount-to-display
constants/min-token-decimals-to-display)
amount-formatted (-> (rf/sub [:wallet/send-amount-fixed total-amount])
(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}]]))
{: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)))}]]))
network-values))])
(defn render-network-links
@@ -84,15 +156,59 @@
: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]}]
(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)]
[{: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])
[rn/scroll-view {:content-container-style style/routes-container}
(when show-routes?
[rn/view {:style style/routes-header-container}
@@ -104,18 +220,27 @@
: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
:receiver? false
:theme theme
:loading-routes? loading-routes?}]
{: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}]
[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
:receiver? true
:loading-routes? loading-routes?
:theme theme}]]]))
{: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)}]]]))
@@ -78,7 +78,7 @@
:on-press on-close
:switcher-type :select-account}]
[quo/page-top
{:title (i18n/label :t/select-token)
{:title (i18n/label :t/select-asset)
:title-accessibility-label :title-label}]
[quo/segmented-control
{:size 32
@@ -248,7 +248,9 @@
account))}
user-props {:full-name to-address
:address (utils/get-shortened-address
to-address)}]
to-address)}
sign-on-keycard? (get-in transaction-for-signing
[:signingDetails :signOnKeycard])]
;; In token send flow we already have transaction built when
;; we reach confirmation screen. But in send collectible flow
;; routes request happens at the same time with navigation to
@@ -279,20 +281,24 @@
:transaction-type transaction-type}]
(when (and (not loading-suggested-routes?) route (seq route))
[standard-auth/slide-button
{:keycard-supported? true
:size :size-48
{:size :size-48
:track-text (if (= transaction-type :tx/bridge)
(i18n/label :t/slide-to-bridge)
(i18n/label :t/slide-to-send))
:container-style {:z-index 2}
:disabled? (not transaction-for-signing)
:customization-color account-color
:auth-button-label (i18n/label :t/confirm)
:on-complete (when sign-on-keycard?
#(rf/dispatch
[:wallet/prepare-signatures-for-transactions
:send
""]))
:on-auth-success
(fn [data]
(fn [psw]
(rf/dispatch
[:wallet/prepare-signatures-for-transactions
:send data]))
:auth-button-label (i18n/label :t/confirm)}])]
[:wallet/prepare-signatures-for-transactions :send
psw]))}])]
:gradient-cover? true
:customization-color (:color account)}
[rn/view
+38 -11
View File
@@ -229,17 +229,44 @@
safe-add-type-edit)))
(defn loading-network-amounts
[{: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)))
[{: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))))
(defn network-links
[route from-values-by-chain to-values-by-chain]
@@ -8,16 +8,12 @@
[utils.re-frame :as rf]))
(defn- network-item
[{:keys [network on-select-network source]}]
[{:keys [network on-select-network]}]
(let [{:keys [network-name
chain-id]} network
chain-id]} network
{balance-in-crypto :crypto
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)]
balance-in-fiat :fiat} (rf/sub [:wallet/swap-asset-to-pay-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)
@@ -27,28 +23,21 @@
:container-style (style/network-list-container mainnet?)}]))
(defn view
[{: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]))
[{: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])
render-fn (rn/use-callback (fn [network]
[network-item
{:network network
:on-select-network on-select-network
:source source}]))]
{:network network
:on-select-network
on-select-network}]))]
[:<>
[rn/view {:style style/header-container}
[quo/text
{:size :heading-2
:weight :semi-bold}
title]
(i18n/label :t/select-network)]
[quo/context-tag
{:type :token
:size 24
@@ -57,8 +46,7 @@
(when mainnet-network
[network-item
{:network mainnet-network
:on-select-network on-select-network
:source source}])
:on-select-network on-select-network}])
[quo/divider-label {:container-style style/divider-label}
(i18n/label :t/layer-2)]
[rn/flat-list
@@ -34,8 +34,7 @@
(defn view
[]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
accounts (rf/sub [:wallet/accounts-with-balances asset-to-pay])]
(let [accounts (rf/sub [:wallet/accounts-with-balances])]
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav
@@ -217,18 +217,23 @@
(defn- slide-button
[]
(let [loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
on-auth-success (rn/use-callback
#(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap %]))]
(let [loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
transaction-for-signing (rf/sub [:wallet/swap-transaction-for-signing])
sign-on-keycard? (get-in transaction-for-signing
[:signingDetails :signOnKeycard])
on-auth-success (rn/use-callback
#(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap %]))
on-complete (rn/use-callback
#(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap ""]))]
[standard-auth/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-sign)
:container-style {:z-index 2}
:customization-color (:color account)
:disabled? (or loading-swap-proposal? (not swap-proposal))
:keycard-supported? true
:on-complete (when sign-on-keycard? on-complete)
:on-auth-success on-auth-success
:auth-button-label (i18n/label :t/confirm)}]))
@@ -168,7 +168,9 @@
transaction-for-signing (rf/sub [:wallet/swap-transaction-for-signing])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
account-color (:color account)]
account-color (:color account)
sign-on-keycard? (get-in transaction-for-signing
[:signingDetails :signOnKeycard])]
[standard-auth/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-swap)
@@ -178,7 +180,11 @@
(not swap-proposal)
(not transaction-for-signing))
:auth-button-label (i18n/label :t/confirm)
:keycard-supported? true
:on-complete (when sign-on-keycard?
#(rf/dispatch
[:wallet/prepare-signatures-for-transactions
:swap
""]))
:on-auth-success (fn [data]
(rf/dispatch [:wallet/stop-get-swap-proposal])
(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap data]))}]))
@@ -2,6 +2,7 @@
(:require [clojure.string :as string]
[re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.contexts.wallet.data-store :as wallet-data-store]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
@@ -30,11 +31,19 @@
expired? (-> parsed-uri
:expiryTimestamp
uri/timestamp-expired?)
version-supported? (uri/version-supported? version)]
(if (or (not valid-wc-uri?)
(not version-supported?)
(= network-status :offline)
expired?)
version-supported? (uri/version-supported? version)
keycard? (wallet-data-store/selected-keypair-keycard? db)]
(cond
keycard?
{:fx [[:dispatch
[:keycard/feature-unavailable-show
{:feature-name :wallet.scan-dapp-connection}]]]}
(or (not valid-wc-uri?)
(not version-supported?)
(= network-status :offline)
expired?)
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
@@ -54,6 +63,8 @@
:else
(i18n/label :t/something-went-wrong))}]]]}
:else
{:fx [[:dispatch [:wallet-connect/pair scanned-text]]]}))))
(rf/reg-event-fx
+1
View File
@@ -27,6 +27,7 @@
status-im.contexts.contact.blocking.events
status-im.contexts.keycard.effects
status-im.contexts.keycard.events
status-im.contexts.keycard.feature-unavailable.events
status-im.contexts.network.effects
status-im.contexts.network.events
status-im.contexts.onboarding.common.overlay.events
+2 -2
View File
@@ -11,13 +11,13 @@
(def ^:private initial-flags
{::community.edit-account-selection (enabled-in-env? :FLAG_EDIT_ACCOUNT_SELECTION_ENABLED)
::community.view-token-requirements (enabled-in-env? :FLAG_VIEW_TOKEN_REQUIREMENTS)
;; Feature toggled (off by default) because the desktop app disabled this
;; feature and we want both clients in sync. We keep the code because it
;; works and we may re-enable it by default.
::profile-pictures-visibility (enabled-in-env? :FLAG_PROFILE_PICTURES_VISIBILITY_ENABLED)
::settings.import-all-keypairs (enabled-in-env?
:FLAG_WALLET_SETTINGS_IMPORT_ALL_KEYPAIRS)
::settings.import-all-keypairs (enabled-in-env? :FLAG_WALLET_SETTINGS_IMPORT_ALL_KEYPAIRS)
::wallet.add-watched-address (enabled-in-env? :FLAG_ADD_WATCHED_ADDRESS)
::wallet.advanced-sending (enabled-in-env? :FLAG_ADVANCED_SENDING)
::wallet.assets-modal-hide (enabled-in-env? :FLAG_ASSETS_MODAL_HIDE)
+9 -10
View File
@@ -25,16 +25,15 @@
(defn root-options
[{:keys [background-color theme status-bar-theme nav-bar-color]}]
(merge
(statusbar-and-navbar-options theme status-bar-theme nav-bar-color)
{:topBar {:visible false}
:layout {:componentBackgroundColor (or background-color
(colors/theme-colors colors/white colors/neutral-100 theme))
:orientation ["portrait"]
:backgroundColor (or background-color
(colors/theme-colors colors/white
colors/neutral-100
theme))}}))
(let [layout-background (or background-color
(colors/theme-colors colors/white colors/neutral-100 theme))
component-background (or background-color
(colors/theme-colors colors/white colors/neutral-100 theme))]
(assoc (statusbar-and-navbar-options theme status-bar-theme nav-bar-color)
:topBar {:visible false}
:layout {:componentBackgroundColor component-background
:orientation ["portrait"]
:backgroundColor layout-background})))
(defn dark-root-options
[]
+84 -68
View File
@@ -6,6 +6,8 @@
[status-im.common.enter-seed-phrase.view :as enter-seed-phrase]
[status-im.common.lightbox.view :as lightbox]
[status-im.common.pdf-viewer.view :as pdf-viewer]
[status-im.common.privacy.view :as privacy-policy]
[status-im.common.terms.view :as terms-of-use]
[status-im.config :as config]
[status-im.contexts.chat.group.create.view :as group.create]
[status-im.contexts.chat.group.details.view :as group.details]
@@ -73,6 +75,7 @@
[status-im.contexts.profile.settings.screens.password.view :as settings-password]
[status-im.contexts.profile.settings.screens.syncing.view :as settings.syncing]
[status-im.contexts.profile.settings.view :as settings]
[status-im.contexts.settings.about.view :as settings.about]
[status-im.contexts.settings.keycard.view :as settings.keycard]
[status-im.contexts.settings.language-and-currency.currency.view :as settings.currency-selection]
[status-im.contexts.settings.language-and-currency.view :as settings.language-and-currency]
@@ -153,39 +156,39 @@
(def chat-screens
[{:name :start-a-new-chat
:metrics {:track? :true
:metrics {:track? true
:alias-id :messenger.new-chat}
:options {:sheet? true}
:component new-chat/view}
{:name :chat
:metrics {:track? :true
:metrics {:track? true
:alias-id :messenger.chat}
:options {:popGesture false
:animations transitions/stack-transition-from-bottom}
:component chat/chat}
{:name :screen/group-create
:metrics {:track? :true
:metrics {:track? true
:alias-id :messenger.new-group}
:options {:sheet? true
:skip-background? true}
:component group.create/view}
{:name :screen/group-details
:metrics {:track? :true
:metrics {:track? true
:alias-id :messenger.group-details}
:component group.details/view}
{:name :screen/group-update
:metrics {:track? :true
:metrics {:track? true
:alias-id :messenger.group-update}
:options {:sheet? true
:skip-background? true}
:component group.update/view}
{:name :screen/group-add-manage-members
:metrics {:track? :true
:metrics {:track? true
:flow :messenger
:alias-id :messenger.group-manage-members}
:options {:sheet? true}
@@ -193,12 +196,12 @@
(def community-screens
[{:name :discover-communities
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.discover}
:component communities.discover/view}
{:name :community-overview
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.overview}
:options {:animations transitions/stack-transition-from-bottom}
:component communities.overview/view}
@@ -207,79 +210,79 @@
;; joining a community. The non-sheet screen is used when editing shared
;; addresses after the join request was sent.
{:name :community-account-selection-sheet
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.select-addresses-for-joining-community}
:options {:sheet? true}
:component communities.accounts-selection/view}
{:name :community-account-selection
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.select-addresses-for-community}
:options {:insets {:top? true}}
:component communities.accounts-selection/view}
{:name :community-requests-to-join
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.request-to-join}
:options {:sheet? true}
:component join-menu/view}
{:name :screen/share-community
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.share-community}
:options options/transparent-screen-options
:component share-community/view}
{:name :invite-people-community
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.invite-people}
:options {:sheet? true}
:component communities.invite/view}
{:name :share-community-channel
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.share-channel}
:options options/transparent-screen-options
:component share-community-channel/view}
{:name :screen/chat.view-channel-members-and-details
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.view-channel-members-and-details}
:options {:insets {:top? true}}
:component channel-view-channel-members-and-details/view}
{:name :addresses-for-permissions
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.choose-addresses-for-permissions}
:options {:insets {:top? true}}
:component addresses-for-permissions/view}
{:name :address-for-airdrop
:metrics {:track? :true
:metrics {:track? true
:alias-id :community.choose-addresses-for-airdrop}
:options {:insets {:top? true}}
:component airdrop-addresses/view}])
(def contact-screens
[{:name :new-contact
:metrics {:track? :true
:metrics {:track? true
:alias-id :contact.new-contact}
:options {:sheet? true}
:component add-new-contact/new-contact}
{:name :scan-profile-qr-code
:metrics {:track? :true
:metrics {:track? true
:alias-id :contact.scan-profile-qr-code}
:options options/dark-screen
:component scan-profile-qr-page/view}
{:name :contact-profile
:metrics {:track? :true
:metrics {:track? true
:alias-id :contact.contact-profile}
:options {:modalPresentationStyle :overCurrentContext}
:component contact-profile/view}
{:name :share-contact
:metrics {:track? :true
:metrics {:track? true
:alias-id :contact.share-profile}
:options options/transparent-screen-options
:component share-contact/view}])
@@ -311,56 +314,56 @@
(def settings-screens
[{:name :settings
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.profile-settings}
:options options/transparent-screen-options
:component settings/view}
{:name :screen/settings.keycard
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.keycard}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component settings.keycard/view}
{:name :edit-profile
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.edit-profile}
:options options/transparent-modal-screen-options
:component edit-profile/view}
{:name :edit-accent-colour
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.edit-profile-accent-colour}
:options options/transparent-modal-screen-options
:component edit-accent-colour/view}
{:name :edit-name
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.edit-profile-name}
:options options/transparent-modal-screen-options
:component edit-name/view}
{:name :edit-bio
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.edit-profile-bio}
:options options/transparent-modal-screen-options
:component edit-bio/view}
{:name :screen/settings-password
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.password}
:options options/transparent-modal-screen-options
:component settings-password/view}
{:name :screen/change-password
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.change-password}
:options (assoc options/transparent-modal-screen-options :theme :dark)
:component change-password/view}
{:name :screen/change-password-loading
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.change-password-loading}
:options (assoc
options/transparent-modal-screen-options
@@ -371,25 +374,25 @@
:component change-password-loading/view}
{:name :screen/settings-messages
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.messages}
:options options/transparent-modal-screen-options
:component settings.messages/view}
{:name :screen/settings-blocked-users
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.blocked-users}
:options options/transparent-modal-screen-options
:component settings.blocked-users/view}
{:name :screen/settings-privacy-and-security
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.private-and-security}
:options options/transparent-modal-screen-options
:component settings.privacy-and-security/view}
{:name :screen/settings.share-usage-data
:metrics {:track? :true}
:metrics {:track? true}
:options options/transparent-modal-screen-options
:component settings.share-usage/view}
@@ -405,89 +408,104 @@
:component syncing-devices-list/view}
{:name :screen/settings.language-and-currency
:metrics {:track? :true}
:metrics {:track? true}
:options options/transparent-modal-screen-options
:component settings.language-and-currency/view}
{:name :screen/settings.currency-selection
:metrics {:track? :true}
:metrics {:track? true}
:options options/transparent-modal-screen-options
:component settings.currency-selection/view}])
:component settings.currency-selection/view}
{:name :screen/settings.about
:metrics {:track? true}
:options options/transparent-modal-screen-options
:component settings.about/view}
{:name :screen/settings.privacy-policy
:metrics {:track? true}
:options options/transparent-modal-screen-options
:component privacy-policy/view}
{:name :screen/settings.terms-of-use
:metrics {:track? true}
:options options/transparent-modal-screen-options
:component terms-of-use/view}])
(def wallet-settings-screens
[{:name :screen/settings.wallet
:metrics {:track? :true}
:metrics {:track? true}
:options options/transparent-modal-screen-options
:component wallet-options/view}
{:name :screen/settings.rename-keypair
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-rename-keypair}
:options options/transparent-screen-options
:component keypair-rename/view}
{:name :screen/settings.encrypted-keypair-qr
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-encrypted-keypair-qr}
:options options/transparent-screen-options
:component encrypted-keypair-qr/view}
{:name :screen/settings.saved-addresses
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-saved-addresses}
:options options/transparent-modal-screen-options
:component saved-addresses-settings/view}
{:name :screen/settings.keypairs-and-accounts
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-keypairs-and-accounts}
:options options/transparent-modal-screen-options
:component keypairs-and-accounts/view}
{:name :screen/settings.scan-keypair-qr
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-scan-keypair-qr}
:options options/transparent-screen-options
:component scan-keypair-qr/view}
{:name :screen/settings.missing-keypair.import-seed-phrase
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-missing-keypair-import-seed-phrase}
:options options/transparent-screen-options
:component missing-keypairs.import-seed-phrase/view}
{:name :screen/settings.missing-keypair-import-private-key
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-missing-keypair-import-private-key}
:options options/transparent-screen-options
:component missing-keypairs.import-private-key/view}
{:name :screen/settings.network-settings
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-network-settings}
:options options/transparent-modal-screen-options
:component network-settings/view}
{:name :screen/settings.save-address
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-saved-addresses}
:options options/transparent-modal-screen-options
:component wallet-save-address/view}
{:name :screen/settings.edit-saved-address
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-edit-saved-addresses}
:options (assoc options/dark-screen :sheet? true)
:component wallet-save-address/view}
{:name :screen/settings.add-address-to-save
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-add-saved-address}
:options options/transparent-modal-screen-options
:component wallet-add-address-to-save/view}
{:name :screen/settings.share-saved-address
:metrics {:track? :true
:metrics {:track? true
:alias-id :settings.wallet-share-saved-address}
:options options/transparent-screen-options
:component share-saved-address/view}])
@@ -757,9 +775,7 @@
:metrics {:track? true}
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations (merge
transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)
:animations transitions/modal-animations-vertical
:popGesture false
:modalPresentationStyle :overCurrentContext}
:component onboarding.share-usage/view})
@@ -876,48 +892,48 @@
(def keycard-screens
[{:name :screen/keycard.check
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.check/view}
{:name :screen/keycard.empty
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.empty/view}
{:name :screen/keycard.empty-create
:metrics {:track? :true}
:metrics {:track? true}
:options {:insets {:top? true :bottom? true}}
:component keycard.empty/create}
{:name :screen/keycard.error
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.error/view}
{:name :screen/keycard.not-keycard
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.not-keycard/view}
{:name :screen/keycard.authorise
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.authorise/view}
{:name :screen/keycard.migrate
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.migrate/view}
{:name :screen/keycard.re-encrypting
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}
:popGesture false
@@ -926,7 +942,7 @@
:component keycard.re-encrypting/view}
{:name :screen/keycard.migrate.success
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}
:popGesture false
@@ -935,7 +951,7 @@
:component keycard.migrate.success/view}
{:name :screen/keycard.migrate.fail
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}
:popGesture false
@@ -944,30 +960,30 @@
:component keycard.migrate.fail/view}
{:name :screen/keycard.pin.create
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.pin.create/view}
{:name :screen/keycard.pin.enter
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.pin.enter/view}
{:name :screen/keycard.profile-keys
:metrics {:track? :true}
:metrics {:track? true}
:options {:theme :dark
:insets {:top? true :bottom? true}}
:component keycard.migrate.profile-keys/view}
{:name :screen/keycard.create-profile
:metrics {:track? :true}
:metrics {:track? true}
:options {:insets {:top? true :bottom? true}}
:component keycard.create/view}
{:name :screen/keycard.create.ready-to-add
:metrics {:track? :true}
:metrics {:track? true}
:options {:insets {:top? true :bottom? true}}
:component keycard.create/ready-to-add}])
@@ -40,6 +40,14 @@
:to (:width (rn/get-window))
:duration constants/onboarding-modal-animation-duration}}})
(def modal-animations-vertical
{:showModal {:translationY {:from (:height (rn/get-window))
:to 0
:duration constants/onboarding-modal-animation-duration}}
:dismissModal {:translationY {:from 0
:to (:height (rn/get-window))
:duration constants/onboarding-modal-animation-duration}}})
;;;; Stack Transitions
(def stack-slide-transition
{:push {:content {:translationX {:from (:width (rn/get-window))
+3 -3
View File
@@ -56,12 +56,12 @@
(re-frame/reg-sub
:wallet/network-values
:<- [:wallet/wallet-send]
(fn [{:keys [from-values-by-chain to-values-by-chain token-display-name token] :as send-data}
:<- [:wallet/send-display-token-decimals]
(fn [[{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} token-decimals]
[_ 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))
token-decimals (:decimals token)]
(-> send-data :token :symbol))]
(reduce-kv
(fn [acc chain-id amount]
(let [network-name (network-utils/id->network chain-id)
+2 -48
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-tab-constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money]
@@ -34,11 +34,6 @@
:<- [: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]
@@ -59,11 +54,6 @@
:<- [: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]
@@ -87,7 +77,7 @@
(->> address-activity
(sort :timestamp)
(keep (fn [{:keys [activity-type recipient]}]
(when (= activity-tab-constants/wallet-activity-type-send activity-type)
(when (= activity-constants/wallet-activity-type-send activity-type)
recipient)))
(distinct)))))
@@ -183,39 +173,3 @@
(= (: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,3 +334,24 @@
: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))))
+9 -21
View File
@@ -286,6 +286,12 @@
:<- [:wallet/create-account]
:-> :selected-keypair-uid)
(rf/reg-sub
:wallet/selected-keypair-keycard?
:<- [:wallet/selected-keypair]
(fn [{:keys [keycards]}]
(boolean (seq keycards))))
(rf/reg-sub
:wallet/selected-keypair
:<- [:wallet/keypairs]
@@ -343,12 +349,13 @@
:<- [:wallet/keypairs-list]
(fn [keypairs [_ format-options]]
(reduce
(fn [acc {:keys [accounts name type key-uid lowest-operability]}]
(fn [acc {:keys [accounts name type key-uid lowest-operability keycards]}]
(if (= lowest-operability :no)
(update acc
:missing
conj
{:type type
:keycard? (boolean (seq keycards))
:name name
:key-uid key-uid
:accounts (format-settings-missing-keypair-accounts accounts)})
@@ -356,6 +363,7 @@
:operable
conj
{:type type
:keycard? (boolean (seq keycards))
:name name
:key-uid key-uid
:accounts (format-settings-keypair-accounts accounts format-options)})))
@@ -901,23 +909,3 @@
:<- [: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))))
@@ -1133,3 +1133,25 @@
"0")
:has-error false}}}]}}))
(is (false? (rf/sub [sub-name])))))
(h/deftest-sub :wallet/selected-keypair-keycard?
[sub-name]
(testing "returns true if the selected keypair has keycards"
(swap! rf-db/app-db
#(assoc-in %
[:wallet :keypairs]
{:keypair-1 {:id :keypair-1
:keycards [:keycard-1 :keycard-2]}}))
(swap! rf-db/app-db
#(assoc-in % [:wallet :ui :create-account :selected-keypair-uid] :keypair-1))
(is (true? (rf/sub [sub-name]))))
(testing "returns false if the selected keypair has no keycards"
(swap! rf-db/app-db
#(assoc-in %
[:wallet :keypairs]
{:keypair-2 {:id :keypair-2
:keycards []}}))
(swap! rf-db/app-db
#(assoc-in % [:wallet :ui :create-account :selected-keypair-uid] :keypair-2))
(is (false? (rf/sub [sub-name])))))
+34 -1
View File
@@ -2,7 +2,8 @@
(:require
[clojure.string :as string]
[native-module.core :as native-module]
[utils.ethereum.eip.eip55 :as eip55]))
[utils.ethereum.eip.eip55 :as eip55]
[utils.ethereum.eip.eip681 :as eip681]))
(def hex-prefix "0x")
@@ -105,11 +106,23 @@
[address]
(re-find regx-eip-3770-address address))
(defn eip-681-address?
[scanned-text]
(-> scanned-text
eip681/parse-uri
:address
boolean))
(defn supported-address?
[s]
(boolean (or (eip-3770-address? s)
(metamask-address? s))))
(defn supported-scan-address?
[s]
(boolean (or (eip-681-address? s)
(supported-address? s))))
(defn metamask-address->eip-3770-address
[metamask-address]
(when-let [[_ address metamask-network-suffix] (split-metamask-address metamask-address)]
@@ -129,9 +142,18 @@
[eip-3770-address]
(extract-address-without-chains-info eip-3770-address))
(defn eip-681-address->eth-address
[eip-681-address]
(-> eip-681-address
eip681/parse-uri
:address))
(defn supported-address->eth-address
[address]
(cond
(eip-681-address? address)
(eip-681-address->eth-address address)
(eip-3770-address? address)
(eip-3770-address->eth-address address)
@@ -141,3 +163,14 @@
(defn zero-address?
[address]
(= address zero-address))
(defn public-key->address
[public-key]
(let [length (count public-key)
normalized-key (case length
132 (str "0x" (subs public-key 4))
130 public-key
128 (str "0x" public-key)
nil)]
(when normalized-key
(subs (native-module/sha3 normalized-key) 26))))
+10 -6
View File
@@ -6,7 +6,7 @@
e.g. ethereum:0x1234@1/transfer?to=0x5678&value=1e18&gas=5000"
(:require
[clojure.string :as string]
[utils.address :as address]
[native-module.core :as native-module]
[utils.ens.core :as utils.ens]
[utils.ethereum.chain :as chain]))
@@ -18,6 +18,10 @@
(def parameter-separator "&")
(def key-value-separator "=")
(defn- address?
[address]
(native-module/address? address))
(def uri-pattern
(re-pattern (str scheme scheme-separator "([^" query-separator "]*)(?:\\" query-separator "(.*))?")))
(def authority-path-pattern
@@ -63,16 +67,16 @@
Invalid URI will be parsed as `nil`."
[s]
(when (string? s)
(if (address/address? s)
(if (address? s)
{:address s}
(let [[_ authority-path query] (re-find uri-pattern s)]
(when authority-path
(let [[_ raw-address chain-id function-name] (re-find authority-path-pattern authority-path)]
(when (or (or (utils.ens/is-valid-eth-name? raw-address) (address/address? raw-address))
(when (or (or (utils.ens/is-valid-eth-name? raw-address) (address? raw-address))
(when (string/starts-with? raw-address "pay-")
(let [pay-address (string/replace-first raw-address "pay-" "")]
(or (utils.ens/is-valid-eth-name? pay-address)
(address/address? pay-address)))))
(address? pay-address)))))
(let [address (if (string/starts-with? raw-address "pay-")
(string/replace-first raw-address "pay-" "")
raw-address)]
@@ -80,7 +84,7 @@
(let [contract-address (get-in arguments [:function-arguments :address])]
(if-not (or (not contract-address)
(or (utils.ens/is-valid-eth-name? contract-address)
(address/address? contract-address)))
(address? contract-address)))
nil
(merge {:address address
:chain-id (if chain-id
@@ -98,7 +102,7 @@
"Generate a EIP 681 URI based on `address` and a map (keywords / {bignumbers/strings} ) of extra properties.
No validation of address format is performed."
[address {:keys [chain-id function-name function-arguments] :as m}]
(when (address/address? address)
(when (address? address)
(let [parameters (dissoc (into {} (filter second m)) :chain-id)] ;; filter nil values
(str scheme
scheme-separator
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v7.0.0",
"commit-sha1": "84493728d9ac41553297e8084943c1429cdfe59a",
"src-sha256": "11dwdlk9svmc6f8wmya43fis9jfigvh5idxpiwmia9gkad1zal6y"
"version": "8acd48240bfeacd119bb4a41985f52be5ef00038",
"commit-sha1": "8acd48240bfeacd119bb4a41985f52be5ef00038",
"src-sha256": "0hn350bxs14gnm39dg9x588142nj7n79k313487y659yk1142ksx"
}
@@ -148,14 +148,6 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
@marks.testrail_id(702777)
def test_add_contact_field_validation(self):
# Profile link encoded data validation
encoded_data = re.findall("u/(.*)#", self.profile_link_2)[0]
decoded_string = base64.b64decode(encoded_data).decode("utf-8", "ignore")
decoded_username = re.sub('[^A-Za-z0-9]+', '', decoded_string)
if decoded_username != self.username_2:
self.errors.append(
"Can't find username '%s' in data which profile link '%s' contains. String '%s' is found instead" % (
self.username_2, self.profile_link_2, decoded_username))
public_key_2 = self.profile_link_2.split("#")[-1]
def _device_1_creates_user():
@@ -453,7 +453,7 @@ class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
current_time = datetime.datetime.strptime(device_time, "%Y-%m-%dT%H:%M:%S%z")
expected_times = [current_time + datetime.timedelta(minutes=i) for i in range(479, 482)]
expected_texts = [
"Muted until %s %s" % (exp_time.strftime('%H:%M'), "today" if current_time.hour < 23 else "tomorrow") for
"Muted until %s %s" % (exp_time.strftime('%H:%M'), "today" if current_time.hour < 16 else "tomorrow") for
exp_time in expected_times]
chat = self.homes[1].get_chat(self.chat_name)
chat.long_press_element()
@@ -483,7 +483,7 @@ class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
if after_mute_counter > initial_counter:
self.errors.append("New messages counter near chats tab button is %s after mute, but should be %s" % (
after_mute_counter, initial_counter))
if not chat.chat_preview.text.startswith("%s: %s" % (self.usernames[0], muted_message)):
if not chat.chat_preview.text.startswith("%s: %s" % (self.usernames[0], muted_message[:25])):
self.errors.append("Message text '%s' is not shown in chat preview after mute" % muted_message)
chat.click()
if not self.chats[1].chat_element_by_text(muted_message).is_element_displayed(30):
@@ -1021,6 +1021,8 @@ class TestCommunityMultipleDeviceMergedTwo(MultipleSharedDeviceTestCase):
self.errors.append('Community is still shown in the list after leave')
self.errors.verify_no_errors()
@marks.xfail(reason="Can't navigate to a community channel before joining the community, "
"https://github.com/status-im/status-mobile/issues/21776")
@marks.testrail_id(702948)
def test_community_hashtag_links_to_community_channels(self):
for home in self.homes:
@@ -1121,6 +1123,8 @@ class TestCommunityMultipleDeviceMergedTwo(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors()
@marks.xfail(reason="Can't navigate to a community channel before joining the community, "
"https://github.com/status-im/status-mobile/issues/21776")
@marks.testrail_id(703629)
def test_community_join_when_node_owner_offline(self):
for home in self.homes:
@@ -89,7 +89,7 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
self.home.get_chat(snt_community_name, community=True).click()
snt_community_url = self.community_view.copy_community_link()
self.home.reopen_app(sign_in=False)
self.sign_in.create_user(username="second user", first_user=False)
self.sign_in.create_user(first_user=False)
self.home.browser_tab.click()
old, new = "https://status.app/", "status-app://"
+1 -1
View File
@@ -269,7 +269,7 @@ class TestFallbackMultipleDevice(MultipleSharedDeviceTestCase):
self.sign_in_2.passphrase_edit_box.clear()
self.sign_in_2.passphrase_edit_box.send_keys(transaction_senders['A']['passphrase'])
self.sign_in_2.continue_button.click()
if not self.sign_in_2.profile_title_input.is_element_displayed():
if not self.sign_in_2.password_input.is_element_displayed():
self.errors.append("Can't recover an access with a valid passphrase")
self.sign_in_2.click_system_back_button()
+4 -4
View File
@@ -59,19 +59,19 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
def wait_for_wallet_balance_to_update(wallet_view, user_name, initial_eth_amount):
wallet_view.just_fyi("Getting ETH amount in the wallet of the %s after transaction" % user_name)
if user_name == 'sender':
if user_name == self.sender_username:
exp_amount = round(initial_eth_amount - amount_to_send, 4)
else:
exp_amount = round(initial_eth_amount + amount_to_send, 4)
# for _ in range(12): # ToDo: 120 sec wait time, enable when autoupdate feature is ready
new_eth_amount = round(wallet_view.get_asset(asset_name='Ether').get_amount(), 4)
if user_name == 'sender' and new_eth_amount <= exp_amount:
if user_name == self.sender_username and new_eth_amount <= exp_amount:
return
if user_name == 'receiver' and new_eth_amount >= exp_amount:
if user_name == self.receiver_username and new_eth_amount >= exp_amount:
return
self.errors.append(
"Eth amount in the %ss wallet is %s but should be %s" % (user_name, new_eth_amount, exp_amount))
"Eth amount in the %s's wallet is %s but should be %s" % (user_name, new_eth_amount, exp_amount))
# ToDo: disable relogin when autoupdate feature is ready
self.home_1.just_fyi("Relogin for getting an updated balance")
+1
View File
@@ -626,6 +626,7 @@ class HomeView(BaseView):
return element.text
def get_username(self):
self.toast_content_element.wait_for_invisibility_of_element()
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)
+4 -2
View File
@@ -361,8 +361,10 @@ class SignInView(BaseView):
def get_user_profile_by_name(self, username: str):
self.driver.info("Getting username card by '%s'" % username)
expected_element = UserProfileElement(self.driver, username=username)
return expected_element if expected_element.is_element_displayed(10) else self.driver.fail(
"User %s is not found!" % username)
if expected_element.is_element_displayed(10):
return expected_element
else:
raise NoSuchElementException(msg="User %s is not found!" % username)
def get_user_profile_by_index(self, index: int):
self.driver.info("Getting username card by index %s" % index)

Some files were not shown because too many files have changed in this diff Show More