Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edd50dfe18 | ||
|
|
d5fa11a918 | ||
|
|
6e5214da72 | ||
|
|
9e2f590e3c | ||
|
|
23d3a2db2a | ||
|
|
a85ca5bd3d | ||
|
|
0f036ca9c4 | ||
|
|
c19128e8b1 | ||
|
|
8d615801d1 | ||
|
|
4f6e1897fe | ||
|
|
93b5f8d180 | ||
|
|
0e607dc293 | ||
|
|
9ced0e4131 | ||
|
|
55ee800e18 | ||
|
|
676998b83b | ||
|
|
755ef19315 | ||
|
|
bc9b0f8c87 | ||
|
|
c8b063493b | ||
|
|
949e78a17e | ||
|
|
a821ec5a14 | ||
|
|
4648066ec7 | ||
|
|
e85bc539d3 | ||
|
|
c26d298573 | ||
|
|
cdde36ca28 | ||
|
|
3c3ef20769 | ||
|
|
2150f6db48 | ||
|
|
10101a23e6 | ||
|
|
f75fff9dd8 |
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 306 KiB |
|
After Width: | Height: | Size: 542 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -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
|
||||
|
||||
@@ -61,14 +61,17 @@
|
||||
(defn view
|
||||
[{:keys [header footer customization-color footer-container-padding header-container-style
|
||||
content-container-style gradient-cover? keyboard-should-persist-taps shell-overlay?
|
||||
blur-options content-avoid-keyboard? automatically-adjust-keyboard-insets]
|
||||
blur-options content-avoid-keyboard? automatically-adjust-keyboard-insets
|
||||
;; Note: Provide `initial-header-height` to avoid a jump due to the on-layout 1-frame
|
||||
;; delay. Revisit this on RN 0.76 since the on-layout delay has been fixed.
|
||||
initial-header-height]
|
||||
:or {footer-container-padding (safe-area/get-top)}}
|
||||
& children]
|
||||
(reagent/with-let [scroll-view-ref (atom nil)
|
||||
set-scroll-ref #(reset! scroll-view-ref %)
|
||||
window-height (:height (rn/get-window))
|
||||
footer-container-height (reagent/atom 0)
|
||||
header-height (reagent/atom 0)
|
||||
header-height (reagent/atom nil)
|
||||
content-container-height (reagent/atom 0)
|
||||
content-scroll-y (reagent/atom 0)
|
||||
keyboard-height (reagent/atom 0)
|
||||
@@ -112,7 +115,7 @@
|
||||
{:style
|
||||
(if content-avoid-keyboard?
|
||||
(style/content-keyboard-avoiding-view
|
||||
{:top @header-height
|
||||
{:top (or @header-height initial-header-height)
|
||||
:bottom (if keyboard-shown?
|
||||
@footer-container-height
|
||||
(+ bottom-safe-area @footer-container-height))})
|
||||
|
||||
@@ -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]]])
|
||||
|
||||
@@ -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]
|
||||
(rf/dispatch [:standard-auth/authorize
|
||||
{:on-close (fn [success?]
|
||||
(js/setTimeout #(reset success?) 200))
|
||||
:auth-button-icon-left auth-button-icon-left
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:keycard-supported? keycard-supported?
|
||||
:biometric-auth? biometric-auth?
|
||||
:on-auth-success on-auth-success
|
||||
:on-auth-fail on-auth-fail
|
||||
:auth-button-label auth-button-label}]))
|
||||
(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)
|
||||
|
||||
@@ -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]]])
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
:long-enough?
|
||||
:short-enough?)))))
|
||||
|
||||
(def password-tip? (set constants/password-tips))
|
||||
|
||||
(defn strength
|
||||
[validations]
|
||||
(->> (select-keys validations constants/password-tips)
|
||||
(vals)
|
||||
(filter true?)
|
||||
count))
|
||||
(->> validations
|
||||
(filter #(and (password-tip? (key %)) (val %)))
|
||||
(count)))
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
{:eventName "navigation"
|
||||
:platform platform-os
|
||||
:appVersion app-version
|
||||
:eventValue {:viewId "onboarding.create-profile-info"}}}]
|
||||
(tracking/track-view-id-event :screen/onboarding.create-profile)))
|
||||
:eventValue {:viewId "onboarding.create-profile-password"}}}]
|
||||
(tracking/track-view-id-event :screen/onboarding.create-profile-password)))
|
||||
(is (= [] (tracking/track-view-id-event :unknown-stack)))))
|
||||
|
||||
(deftest tracked-event-test
|
||||
|
||||
@@ -280,17 +280,31 @@
|
||||
{:style (merge style/container {:margin-horizontal 0})}
|
||||
[chat-item item]])
|
||||
|
||||
(defn- bottom-sheet-options
|
||||
[chat-id chat-type]
|
||||
(cond-> {:content (fn []
|
||||
(let [chat (rf/sub [:chats/chat-by-id chat-id])]
|
||||
[actions/chat-actions chat false]))}
|
||||
(= chat-type constants/one-to-one-chat-type)
|
||||
(assoc :selected-item
|
||||
(fn []
|
||||
(let [chat (rf/sub [:chats/chat-by-id chat-id])]
|
||||
[chat-user chat])))))
|
||||
|
||||
(defn chat-list-item
|
||||
[{:keys [chat-id chat-type]
|
||||
:as item} theme]
|
||||
(let [customization-color (rf/sub [:profile/customization-color])]
|
||||
[{:keys [chat-id]}]
|
||||
(let [{:keys [chat-type]
|
||||
:as chat} (rf/sub [:chats/chat-by-id chat-id])
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
theme (quo.theme/use-theme)
|
||||
on-long-press (rn/use-callback
|
||||
(fn []
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
(bottom-sheet-options chat-id chat-type)]))
|
||||
[chat-id chat-type])]
|
||||
[rn/touchable-highlight
|
||||
{:style style/container
|
||||
:on-press (open-chat chat-id)
|
||||
:underlay-color (colors/resolve-color customization-color theme 5)
|
||||
:on-long-press #(rf/dispatch [:show-bottom-sheet
|
||||
(cond-> {:content (fn [] [actions/chat-actions item false])}
|
||||
(= chat-type constants/one-to-one-chat-type)
|
||||
(assoc :selected-item
|
||||
(fn [] [chat-user item])))])}
|
||||
[chat-item item]]))
|
||||
:on-long-press on-long-press}
|
||||
[chat-item chat]]))
|
||||
|
||||
@@ -23,12 +23,10 @@
|
||||
[_ index]
|
||||
#js {:length 56 :offset (* 56 index) :index index})
|
||||
|
||||
(defn filter-and-sort-items-by-tab
|
||||
(defn filter-by-tab
|
||||
[tab items]
|
||||
(let [k (if (= tab :tab/groups) :group-chat :chat-id)]
|
||||
(->> items
|
||||
(filter k)
|
||||
(sort-by :timestamp >))))
|
||||
(filter k items)))
|
||||
|
||||
(defn empty-state-content
|
||||
[theme]
|
||||
@@ -45,32 +43,43 @@
|
||||
:description (i18n/label :t/no-messages-description)
|
||||
:image (resources/get-themed-image :cat-in-box theme)}})
|
||||
|
||||
(defn on-end-reached
|
||||
[]
|
||||
(re-frame/dispatch [:chat/show-more-chats]))
|
||||
|
||||
(defn chat-key-fn
|
||||
[chat]
|
||||
(or (:chat-id chat) (:public-key chat) (:id chat)))
|
||||
|
||||
(defn chats
|
||||
[{:keys [theme selected-tab set-scroll-ref scroll-shared-value]}]
|
||||
(let [unfiltered-items (rf/sub [:chats/chats-stack-items])
|
||||
items (filter-and-sort-items-by-tab selected-tab unfiltered-items)]
|
||||
items (filter-by-tab selected-tab unfiltered-items)
|
||||
on-scroll (rn/use-callback
|
||||
(fn [event]
|
||||
(common.banner/set-scroll-shared-value
|
||||
{:scroll-input (oops/oget event
|
||||
"nativeEvent.contentOffset.y")
|
||||
:shared-value scroll-shared-value})))]
|
||||
(if (empty? items)
|
||||
[common.empty-state/view
|
||||
{:selected-tab selected-tab
|
||||
:tab->content (empty-state-content theme)}]
|
||||
[reanimated/flat-list
|
||||
{:ref set-scroll-ref
|
||||
:key-fn #(or (:chat-id %) (:public-key %) (:id %))
|
||||
:key-fn chat-key-fn
|
||||
:content-inset-adjustment-behavior :never
|
||||
:header [common.header-spacing/view]
|
||||
:get-item-layout get-item-layout
|
||||
:on-end-reached #(re-frame/dispatch [:chat/show-more-chats])
|
||||
:on-end-reached on-end-reached
|
||||
:keyboard-should-persist-taps :always
|
||||
:data items
|
||||
:render-fn (fn [item]
|
||||
(chat-list-item/chat-list-item item theme))
|
||||
:render-fn chat-list-item/chat-list-item
|
||||
:scroll-event-throttle 8
|
||||
:content-container-style {:padding-bottom
|
||||
shell.constants/floating-shell-button-height
|
||||
:padding-top 8}
|
||||
:on-scroll #(common.banner/set-scroll-shared-value
|
||||
{:scroll-input (oops/oget % "nativeEvent.contentOffset.y")
|
||||
:shared-value scroll-shared-value})}])))
|
||||
:on-scroll on-scroll}])))
|
||||
|
||||
(defn contact-item-render
|
||||
[{:keys [public-key] :as item} theme]
|
||||
|
||||
@@ -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))])]))
|
||||
|
||||
@@ -130,4 +130,12 @@
|
||||
:role-permissions? true}
|
||||
:profile/test-networks-enabled? false})
|
||||
(h/render [options/community-options-bottom-sheet {:id "test"}])
|
||||
(h/is-truthy (h/get-by-translation-text :t/unmute-community))))
|
||||
(h/is-truthy (h/get-by-translation-text :t/unmute-community)))
|
||||
|
||||
(h/test "opened community"
|
||||
(h/setup-subs {:communities/my-pending-request-to-join nil
|
||||
:communities/community {:joined false
|
||||
:spectated true}
|
||||
:profile/test-networks-enabled? false})
|
||||
(h/render [options/community-options-bottom-sheet {:id "test"}])
|
||||
(h/is-truthy (h/get-by-translation-text :t/close-community))))
|
||||
|
||||
@@ -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)})
|
||||
|
||||
@@ -124,6 +125,14 @@
|
||||
:on-press #(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [leave-menu/leave-sheet id color])}])})
|
||||
|
||||
(defn close-community
|
||||
[id]
|
||||
{:icon :i/close-circle
|
||||
:label (i18n/label :t/close-community)
|
||||
:accessibility-label :close-community
|
||||
:danger? true
|
||||
:on-press #(rf/dispatch [:communities/leave id])})
|
||||
|
||||
(defn cancel-request-to-join
|
||||
[id request-id]
|
||||
{:icon :i/block
|
||||
@@ -135,10 +144,12 @@
|
||||
request-id])}])})
|
||||
|
||||
(defn not-joined-options
|
||||
[id token-gated? intro-message test-networks-enabled?]
|
||||
[{:keys [id join-pending? spectated? token-gated? intro-message test-networks-enabled?]}]
|
||||
(let [common [(show-qr id) (share-community id)]
|
||||
specific (cond
|
||||
(and token-gated? (not test-networks-enabled?))
|
||||
(and token-gated?
|
||||
(not test-networks-enabled?)
|
||||
(ff/enabled? ::ff/community.view-token-requirements))
|
||||
[(invite-contacts id) (view-token-gating id)]
|
||||
|
||||
token-gated?
|
||||
@@ -146,22 +157,26 @@
|
||||
|
||||
(not token-gated?)
|
||||
[(view-members id) (view-rules id intro-message) (invite-contacts id)])]
|
||||
[(concat specific common)]))
|
||||
[(concat specific
|
||||
common
|
||||
(when (and spectated? (not join-pending?))
|
||||
[(assoc (close-community id) :add-divider? true)]))]))
|
||||
|
||||
(defn join-request-sent-options
|
||||
[id token-gated? request-id intro-message test-networks-enabled?]
|
||||
[(conj (first (not-joined-options id token-gated? intro-message test-networks-enabled?))
|
||||
[{:keys [id request-id] :as config}]
|
||||
[(conj (first (not-joined-options config))
|
||||
(assoc (cancel-request-to-join id request-id) :add-divider? true))])
|
||||
|
||||
(defn banned-options
|
||||
[id token-gated? intro-message test-networks-enabled?]
|
||||
(not-joined-options id token-gated? intro-message test-networks-enabled?))
|
||||
[config]
|
||||
(not-joined-options config))
|
||||
|
||||
(defn joined-options
|
||||
[id token-gated? muted? muted-till color intro-message]
|
||||
[{:keys [id token-gated? muted? muted-till color intro-message]}]
|
||||
[[(view-members id)
|
||||
(view-rules id intro-message)
|
||||
(when token-gated? (view-token-gating id))
|
||||
(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)
|
||||
@@ -173,7 +188,7 @@
|
||||
[(assoc (leave-community id color) :add-divider? true)]])
|
||||
|
||||
(defn owner-options
|
||||
[id token-gated? muted? muted-till intro-message]
|
||||
[{:keys [id token-gated? muted? muted-till intro-message]}]
|
||||
[[(view-members id)
|
||||
(view-rules id intro-message)
|
||||
(when token-gated? (view-token-gating id))
|
||||
@@ -186,22 +201,29 @@
|
||||
|
||||
(defn get-context-drawers
|
||||
[{:keys [id]}]
|
||||
(let [{:keys [role-permissions? admin joined
|
||||
muted banList muted-till color
|
||||
intro-message]} (rf/sub [:communities/community id])
|
||||
request-id (rf/sub [:communities/my-pending-request-to-join id])
|
||||
test-networks? (rf/sub [:profile/test-networks-enabled?])]
|
||||
(let [{:keys
|
||||
[id admin joined
|
||||
banList color muted muted-till
|
||||
spectated role-permissions?
|
||||
intro-message]} (rf/sub [:communities/community id])
|
||||
request-id (rf/sub [:communities/my-pending-request-to-join id])
|
||||
test-networks? (rf/sub [:profile/test-networks-enabled?])
|
||||
config {:id id
|
||||
:color color
|
||||
:muted-till muted-till
|
||||
:muted? muted
|
||||
:spectated? spectated
|
||||
:token-gated? role-permissions?
|
||||
:intro-message intro-message
|
||||
:test-networks? test-networks?
|
||||
:join-pending? (boolean request-id)
|
||||
:request-id request-id}]
|
||||
(cond
|
||||
admin
|
||||
(owner-options id role-permissions? muted muted-till intro-message)
|
||||
joined
|
||||
(joined-options id role-permissions? muted muted-till color intro-message)
|
||||
request-id
|
||||
(join-request-sent-options id role-permissions? request-id intro-message test-networks?)
|
||||
banList
|
||||
(banned-options id role-permissions? intro-message test-networks?)
|
||||
:else
|
||||
(not-joined-options id role-permissions? intro-message test-networks?))))
|
||||
admin (owner-options config)
|
||||
joined (joined-options config)
|
||||
request-id (join-request-sent-options config)
|
||||
banList (banned-options config)
|
||||
:else (not-joined-options config))))
|
||||
|
||||
(defn community-options-bottom-sheet
|
||||
[id]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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)]]]])
|
||||
@@ -13,7 +13,7 @@
|
||||
customization-color (rf/sub [:profile/customization-color])]
|
||||
[:<>
|
||||
[quo/page-top
|
||||
{:title (i18n/label :t/failed-to-migrate-key-pair)
|
||||
{:title (i18n/label :t/something-didnt-go-as-planned)
|
||||
:description :context-tag
|
||||
:context-tag {:full-name profile-name
|
||||
:profile-picture profile-picture
|
||||
@@ -24,9 +24,9 @@
|
||||
{:resize-mode :contain
|
||||
:source (resources/get-image :keycard-migration-failed)}]]
|
||||
[quo/divider-label (i18n/label :t/what-you-can-do)]
|
||||
[quo/markdown-list {:description (i18n/label :t/log-out-remove-profile)}]
|
||||
[quo/markdown-list {:description (i18n/label :t/recover-status-profile)}]
|
||||
[quo/markdown-list {:description (i18n/label :t/keycard-migration-failed-instruction-1)}]
|
||||
[quo/markdown-list {:description (i18n/label :t/keycard-migration-failed-instruction-2)}]
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/log-out-remove)
|
||||
:button-one-label (i18n/label :t/logout)
|
||||
:button-one-props {:on-press #(rf/dispatch [:profile/logout])}}]]))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im.contexts.onboarding.create-password.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]))
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.platform :as platform]))
|
||||
|
||||
(def heading {:margin-bottom 20})
|
||||
(def heading-subtitle {:color colors/white})
|
||||
@@ -21,6 +22,18 @@
|
||||
:padding-top 12
|
||||
:padding-horizontal 20})
|
||||
|
||||
(def disclaimer-container {:padding-horizontal 20})
|
||||
(def footer-container {:padding-bottom 12})
|
||||
(def footer-button-container {:margin-top 20 :padding-horizontal 20})
|
||||
|
||||
(def blur-options
|
||||
{:blur-amount 34
|
||||
:blur-radius 20
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent
|
||||
:background-color (if platform/android?
|
||||
colors/neutral-100
|
||||
colors/neutral-80-opa-1-blur)
|
||||
:padding-vertical 0
|
||||
:padding-horizontal 0})
|
||||
|
||||
(def page-nav-height 56)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
(ns status-im.contexts.onboarding.create-password.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.floating-button-page.view :as floating-button]
|
||||
[status-im.common.password-with-hint.view :as password-with-hint]
|
||||
@@ -29,22 +27,37 @@
|
||||
(i18n/label :t/password-creation-subtitle)]])
|
||||
|
||||
(defn password-inputs
|
||||
[{:keys [passwords-match? on-change-password on-change-repeat-password on-input-focus
|
||||
password-long-enough? password-short-enough? empty-password? show-password-validation?
|
||||
on-blur-repeat-password]}]
|
||||
(let [hint-1-status (if password-long-enough? :success :default)
|
||||
hint-2-status (if passwords-match? :success :error)
|
||||
hint-2-text (if passwords-match?
|
||||
(i18n/label :t/password-creation-match)
|
||||
(i18n/label :t/password-creation-dont-match))
|
||||
error? (and show-password-validation?
|
||||
(not passwords-match?)
|
||||
(not empty-password?))]
|
||||
[{:keys [set-password set-repeat-password same-password-length? same-passwords?
|
||||
password-long-enough? password-short-enough? non-empty-password?]}]
|
||||
(let [[show-validation?
|
||||
set-show-validation?] (rn/use-state false)
|
||||
on-change-password (rn/use-callback
|
||||
(fn [new-value]
|
||||
(set-password new-value)
|
||||
(when same-password-length?
|
||||
(set-show-validation? true)))
|
||||
[same-password-length?])
|
||||
on-change-repeat-password (rn/use-callback
|
||||
(fn [new-value]
|
||||
(set-repeat-password new-value)
|
||||
(when same-password-length?
|
||||
(set-show-validation? true)))
|
||||
[same-password-length?])
|
||||
on-blur-repeat-password (rn/use-callback
|
||||
#(set-show-validation? non-empty-password?)
|
||||
[non-empty-password?])
|
||||
hint-1-status (if password-long-enough? :success :default)
|
||||
hint-2-status (if same-passwords? :success :error)
|
||||
hint-2-text (if same-passwords?
|
||||
(i18n/label :t/password-creation-match)
|
||||
(i18n/label :t/password-creation-dont-match))
|
||||
error? (and show-validation?
|
||||
(not same-passwords?)
|
||||
non-empty-password?)]
|
||||
[:<>
|
||||
[password-with-hint/view
|
||||
{:hint (if (not password-short-enough?)
|
||||
{:text (i18n/label
|
||||
:t/password-creation-max-length-hint)
|
||||
{:text (i18n/label :t/password-creation-max-length-hint)
|
||||
:status :error
|
||||
:shown? true}
|
||||
{:text (i18n/label :t/password-creation-hint)
|
||||
@@ -52,55 +65,50 @@
|
||||
:shown? true})
|
||||
:placeholder (i18n/label :t/password-creation-placeholder-1)
|
||||
:on-change-text on-change-password
|
||||
:on-focus on-input-focus
|
||||
:auto-focus true}]
|
||||
[rn/view {:style style/space-between-inputs}]
|
||||
[password-with-hint/view
|
||||
{:hint {:text hint-2-text
|
||||
:status hint-2-status
|
||||
:shown? (and (not empty-password?)
|
||||
show-password-validation?)}
|
||||
:shown? (and non-empty-password? show-validation?)}
|
||||
:error? error?
|
||||
:placeholder (i18n/label :t/password-creation-placeholder-2)
|
||||
:on-change-text on-change-repeat-password
|
||||
:on-focus on-input-focus
|
||||
:on-blur on-blur-repeat-password}]]))
|
||||
|
||||
(defn help
|
||||
[{{:keys [lower-case? upper-case? numbers? symbols?]} :validations
|
||||
password-strength :password-strength}]
|
||||
[rn/view
|
||||
[quo/strength-divider {:type (constants/strength-status password-strength :info)}
|
||||
(i18n/label :t/password-creation-tips-title)]
|
||||
[rn/view {:style style/password-tips}
|
||||
[quo/tips {:completed? lower-case?}
|
||||
(i18n/label :t/password-creation-tips-1)]
|
||||
[quo/tips {:completed? upper-case?}
|
||||
(i18n/label :t/password-creation-tips-2)]
|
||||
[quo/tips {:completed? numbers?}
|
||||
(i18n/label :t/password-creation-tips-3)]
|
||||
[quo/tips {:completed? symbols?}
|
||||
(i18n/label :t/password-creation-tips-4)]]])
|
||||
[{:keys [lower-case? upper-case? numbers? symbols?] :as validations}]
|
||||
(let [password-strength (constants/strength-status (password/strength validations) :info)]
|
||||
[rn/view
|
||||
[quo/strength-divider {:type password-strength}
|
||||
(i18n/label :t/password-creation-tips-title)]
|
||||
[rn/view {:style style/password-tips}
|
||||
[quo/tips {:completed? lower-case?}
|
||||
(i18n/label :t/password-creation-tips-1)]
|
||||
[quo/tips {:completed? upper-case?}
|
||||
(i18n/label :t/password-creation-tips-2)]
|
||||
[quo/tips {:completed? numbers?}
|
||||
(i18n/label :t/password-creation-tips-3)]
|
||||
[quo/tips {:completed? symbols?}
|
||||
(i18n/label :t/password-creation-tips-4)]]]))
|
||||
|
||||
(defn- use-password-checks
|
||||
[password]
|
||||
(rn/use-memo
|
||||
(fn []
|
||||
(let [{:keys [long-enough? short-enough?]
|
||||
:as validations} (password/validate password)]
|
||||
{:password-long-enough? long-enough?
|
||||
:password-short-enough? short-enough?
|
||||
:password-validations validations
|
||||
:password-strength (password/strength validations)
|
||||
:empty-password? (empty? password)}))
|
||||
(-> password
|
||||
(password/validate)
|
||||
(assoc :non-empty? (seq password))))
|
||||
[password]))
|
||||
|
||||
(defn- use-repeat-password-checks
|
||||
[password repeat-password]
|
||||
(rn/use-memo
|
||||
(fn []
|
||||
{:same-password-length? (and (seq password) (= (count password) (count repeat-password)))
|
||||
:same-passwords? (and (seq password) (= password repeat-password))})
|
||||
{:same-password-length? (and (seq password)
|
||||
(= (count password) (count repeat-password)))
|
||||
:same-passwords? (and (seq password)
|
||||
(= password repeat-password))})
|
||||
[password repeat-password]))
|
||||
|
||||
(defn create-password-doc
|
||||
@@ -119,102 +127,70 @@
|
||||
{:content create-password-doc
|
||||
:shell? true}]))
|
||||
|
||||
(defn- navigate-back
|
||||
[]
|
||||
(rf/dispatch [:navigate-back]))
|
||||
(defn- navigate-back [] (rf/dispatch [:navigate-back]))
|
||||
|
||||
(defn- page-nav
|
||||
[]
|
||||
(let [{:keys [top]} (safe-area/get-insets)]
|
||||
[quo/page-nav
|
||||
{:margin-top top
|
||||
:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press navigate-back
|
||||
:right-side [{:icon-name :i/info
|
||||
:on-press on-press-info}]}]))
|
||||
[top]
|
||||
[quo/page-nav
|
||||
{:margin-top top
|
||||
:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press navigate-back
|
||||
:right-side [{:icon-name :i/info
|
||||
:on-press on-press-info}]}])
|
||||
|
||||
(defn- help-and-confirm-button
|
||||
[{:keys [password-validations same-passwords? on-submit]}]
|
||||
(let [{customization-color :color} (rf/sub [:onboarding/profile])
|
||||
all-requirements-met? (and (:non-empty? password-validations)
|
||||
(:long-enough? password-validations)
|
||||
(:short-enough? password-validations)
|
||||
same-passwords?)]
|
||||
[rn/view {:style style/footer-container}
|
||||
[help password-validations]
|
||||
[quo/button
|
||||
{:container-style style/footer-button-container
|
||||
:disabled? (not all-requirements-met?)
|
||||
:customization-color customization-color
|
||||
:on-press on-submit}
|
||||
(i18n/label :t/password-creation-confirm)]]))
|
||||
|
||||
(defn- on-confirm-password
|
||||
[password]
|
||||
(rf/dispatch [:onboarding/password-set (security/mask-data password)]))
|
||||
|
||||
(defn create-password
|
||||
[]
|
||||
(let [[password set-password] (rn/use-state "")
|
||||
[repeat-password set-repeat-password] (rn/use-state "")
|
||||
[accepts-disclaimer? set-accepts-disclaimer?] (rn/use-state false)
|
||||
[focused-input set-focused-input] (rn/use-state nil)
|
||||
[show-password-validation?
|
||||
set-show-password-validation?] (rn/use-state false)
|
||||
{user-color :color} (rf/sub [:onboarding/profile])
|
||||
|
||||
|
||||
{:keys [password-long-enough?
|
||||
password-short-enough?
|
||||
password-validations password-strength
|
||||
empty-password?]} (use-password-checks password)
|
||||
|
||||
{:keys [same-password-length? same-passwords?]} (use-repeat-password-checks password
|
||||
repeat-password)
|
||||
|
||||
meet-requirements? (and (not empty-password?)
|
||||
password-long-enough?
|
||||
password-short-enough?
|
||||
same-passwords?
|
||||
accepts-disclaimer?)]
|
||||
|
||||
(let [[password set-password] (rn/use-state "")
|
||||
[repeat-password
|
||||
set-repeat-password] (rn/use-state "")
|
||||
{:keys [long-enough? short-enough? non-empty?]
|
||||
:as password-validations} (use-password-checks password)
|
||||
{:keys [same-password-length?
|
||||
same-passwords?]} (use-repeat-password-checks password repeat-password)
|
||||
on-submit (rn/use-callback
|
||||
#(on-confirm-password password)
|
||||
[password])
|
||||
top (safe-area/get-top)]
|
||||
[floating-button/view
|
||||
{:header [page-nav]
|
||||
:keyboard-should-persist-taps :handled
|
||||
:content-avoid-keyboard? true
|
||||
{:header [page-nav top]
|
||||
:initial-header-height (+ style/page-nav-height top)
|
||||
:keyboard-should-persist-taps :handled
|
||||
:content-avoid-keyboard? true
|
||||
:automatically-adjust-keyboard-insets true
|
||||
:blur-options
|
||||
{:blur-amount 34
|
||||
:blur-radius 20
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent
|
||||
:background-color (if platform/android? colors/neutral-100 colors/neutral-80-opa-1-blur)
|
||||
:padding-vertical 0
|
||||
:padding-horizontal 0}
|
||||
:footer-container-padding 0
|
||||
:footer
|
||||
[rn/view
|
||||
{:style style/footer-container}
|
||||
(when same-passwords?
|
||||
[rn/view {:style style/disclaimer-container}
|
||||
[quo/disclaimer
|
||||
{:blur? true
|
||||
:on-change (partial set-accepts-disclaimer? not)
|
||||
:checked? accepts-disclaimer?
|
||||
:customization-color user-color}
|
||||
(i18n/label :t/password-creation-disclaimer)]])
|
||||
(when (and (= focused-input :password) (not same-passwords?))
|
||||
[help
|
||||
{:validations password-validations
|
||||
:password-strength password-strength}])
|
||||
[quo/button
|
||||
{:container-style style/footer-button-container
|
||||
:disabled? (not meet-requirements?)
|
||||
:customization-color user-color
|
||||
:on-press #(rf/dispatch
|
||||
[:onboarding/password-set
|
||||
(security/mask-data password)])}
|
||||
(i18n/label :t/password-creation-confirm)]]}
|
||||
|
||||
:blur-options style/blur-options
|
||||
:footer-container-padding 0
|
||||
:footer [help-and-confirm-button
|
||||
{:password-validations password-validations
|
||||
:same-passwords? same-passwords?
|
||||
:on-submit on-submit}]}
|
||||
[rn/view {:style style/form-container}
|
||||
[header]
|
||||
[password-inputs
|
||||
{:password-long-enough? password-long-enough?
|
||||
:password-short-enough? password-short-enough?
|
||||
:passwords-match? same-passwords?
|
||||
:empty-password? empty-password?
|
||||
:show-password-validation? show-password-validation?
|
||||
:on-input-focus #(set-focused-input :password)
|
||||
:on-change-password (fn [new-value]
|
||||
(set-password new-value)
|
||||
(when same-password-length?
|
||||
(set-show-password-validation? true)))
|
||||
|
||||
:on-change-repeat-password (fn [new-value]
|
||||
(set-repeat-password new-value)
|
||||
(when same-password-length?
|
||||
(set-show-password-validation? true)))
|
||||
:on-blur-repeat-password #(if empty-password?
|
||||
(set-show-password-validation? false)
|
||||
(set-show-password-validation? true))}]]]))
|
||||
{:password-long-enough? long-enough?
|
||||
:password-short-enough? short-enough?
|
||||
:non-empty-password? non-empty?
|
||||
:same-passwords? same-passwords?
|
||||
:same-password-length? same-password-length?
|
||||
:set-password set-password
|
||||
:set-repeat-password set-repeat-password}]]]))
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
(ns status-im.contexts.onboarding.create-profile.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.platform :as platform]))
|
||||
|
||||
(def continue-button
|
||||
{:width "100%"
|
||||
:margin-left :auto
|
||||
:margin-top (if platform/android? :auto 0)
|
||||
:margin-right :auto})
|
||||
|
||||
(def button-container
|
||||
{:width "100%"
|
||||
:padding-left 20
|
||||
:padding-right 20
|
||||
:padding-top 12
|
||||
:align-self :flex-end
|
||||
:height 64})
|
||||
|
||||
(defn view-button-container
|
||||
[keyboard-shown?]
|
||||
(merge button-container
|
||||
(if platform/ios?
|
||||
{:margin-bottom (if keyboard-shown? 0 34)}
|
||||
{:margin-bottom (if keyboard-shown? 12 34)})))
|
||||
|
||||
(def blur-button-container
|
||||
(merge button-container
|
||||
(when platform/android? {:padding-bottom 12})))
|
||||
|
||||
(def page-container
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
:z-index 100})
|
||||
|
||||
(def info-message
|
||||
{:margin-top 8})
|
||||
|
||||
(def title
|
||||
{:color colors/white
|
||||
:margin-top 12
|
||||
:margin-bottom 18})
|
||||
|
||||
(def color-title
|
||||
{:color colors/white-70-blur
|
||||
:margin-top 20
|
||||
:margin-bottom 16})
|
||||
|
||||
(def content-container
|
||||
{:padding-horizontal 20})
|
||||
|
||||
(def input-container
|
||||
{:align-items :flex-start})
|
||||
|
||||
(def profile-input-container
|
||||
{:flex-direction :row
|
||||
:justify-content :center})
|
||||
@@ -1,210 +0,0 @@
|
||||
(ns status-im.contexts.onboarding.create-profile.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[oops.core :as oops]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.hooks :as hooks]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.avatar-picture-picker.view :as profile-picture-picker]
|
||||
[status-im.common.validation.profile :as profile-validator]
|
||||
[status-im.constants :as c]
|
||||
[status-im.contexts.onboarding.create-profile.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.responsiveness :as responsiveness]))
|
||||
|
||||
(def scroll-view-height (reagent/atom 0))
|
||||
(def content-container-height (reagent/atom 0))
|
||||
|
||||
(defn show-button-background
|
||||
[keyboard-height keyboard-shown content-scroll-y]
|
||||
(let [button-container-height 64
|
||||
keyboard-view-height (+ keyboard-height button-container-height)]
|
||||
(when keyboard-shown
|
||||
(cond
|
||||
platform/android?
|
||||
(< (- @scroll-view-height button-container-height) @content-container-height)
|
||||
|
||||
platform/ios?
|
||||
(< (- @scroll-view-height keyboard-view-height) (- @content-container-height content-scroll-y))
|
||||
|
||||
:else
|
||||
false))))
|
||||
|
||||
(defn button-container
|
||||
[show-keyboard? keyboard-shown show-background? keyboard-height children]
|
||||
(let [height (reagent/atom 0)]
|
||||
(reset! height (if show-keyboard? (if keyboard-shown keyboard-height 0) 0))
|
||||
[rn/view {:style {:margin-top :auto}}
|
||||
(cond
|
||||
(and (> @height 0) show-background?)
|
||||
[quo/blur
|
||||
(when keyboard-shown
|
||||
{:blur-amount 34
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent
|
||||
:background-color (if platform/android? colors/neutral-100 colors/neutral-80-opa-1-blur)
|
||||
:style style/blur-button-container})
|
||||
children]
|
||||
|
||||
(and (> @height 0) (not show-background?))
|
||||
[rn/view {:style (style/view-button-container true)}
|
||||
children]
|
||||
|
||||
(not show-keyboard?)
|
||||
[rn/view {:style (style/view-button-container false)}
|
||||
children])]))
|
||||
|
||||
(defn- page
|
||||
[{:keys [onboarding-profile-data navigation-bar-top]}]
|
||||
(reagent/with-let [show-keyboard? (reagent/atom false)
|
||||
content-scroll-y (reagent/atom 0)
|
||||
show-listener (oops/ocall rn/keyboard
|
||||
"addListener"
|
||||
(if platform/android?
|
||||
"keyboardDidShow"
|
||||
"keyboardWillShow")
|
||||
#(reset! show-keyboard? true))
|
||||
hide-listener (oops/ocall rn/keyboard
|
||||
"addListener"
|
||||
(if platform/android?
|
||||
"keyboardDidHide"
|
||||
"keyboardWillHide")
|
||||
#(reset! show-keyboard? false))
|
||||
{:keys [image-path display-name color]} onboarding-profile-data
|
||||
full-name (reagent/atom display-name)
|
||||
validation-msg (reagent/atom
|
||||
(profile-validator/validation-name
|
||||
@full-name))
|
||||
on-change-text (fn [s]
|
||||
(reset! validation-msg
|
||||
(profile-validator/validation-name
|
||||
s))
|
||||
(reset! full-name (string/trim s)))
|
||||
custom-color (reagent/atom (or color
|
||||
c/profile-default-color))
|
||||
profile-pic (reagent/atom image-path)
|
||||
on-change-profile-pic #(reset! profile-pic %)
|
||||
on-change #(reset! custom-color %)]
|
||||
(let [name-too-short? (profile-validator/name-too-short? @full-name)
|
||||
valid-name? (and (not @validation-msg) (not name-too-short?))
|
||||
info-message (if @validation-msg
|
||||
@validation-msg
|
||||
(i18n/label :t/minimum-characters
|
||||
{:min-chars
|
||||
profile-validator/min-length}))
|
||||
info-type (cond @validation-msg :error
|
||||
name-too-short? :default
|
||||
:else :success)
|
||||
{:keys [keyboard-shown keyboard-height]} (hooks/use-keyboard)
|
||||
show-background? (show-button-background keyboard-height
|
||||
keyboard-shown
|
||||
@content-scroll-y)
|
||||
{window-width :width} (rn/get-window)]
|
||||
[rn/view {:style style/page-container}
|
||||
[quo/page-nav
|
||||
{:margin-top navigation-bar-top
|
||||
:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press #(rf/dispatch [:navigate-back])}]
|
||||
[rn/scroll-view
|
||||
{:on-layout (fn [event]
|
||||
(let [height (oops/oget event "nativeEvent.layout.height")]
|
||||
(reset! scroll-view-height height)
|
||||
(reset! content-scroll-y 0)))
|
||||
:on-scroll (fn [event]
|
||||
(let [y (oops/oget event "nativeEvent.contentOffset.y")]
|
||||
(reset! content-scroll-y y)))
|
||||
:scroll-event-throttle 64
|
||||
:content-container-style {:flexGrow 1}}
|
||||
[rn/view
|
||||
{:on-layout (fn [event]
|
||||
(let [height (oops/oget event "nativeEvent.layout.height")]
|
||||
(reset! content-container-height height)))}
|
||||
[rn/view
|
||||
{:style style/content-container}
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/title} (i18n/label :t/create-profile)]
|
||||
[rn/view
|
||||
{:style style/input-container}
|
||||
[rn/view
|
||||
{:style style/profile-input-container}
|
||||
[quo/profile-input
|
||||
{:customization-color @custom-color
|
||||
:placeholder (i18n/label :t/your-name)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[profile-picture-picker/view
|
||||
{:on-result on-change-profile-pic
|
||||
:has-picture? false}])
|
||||
:shell? true}]))
|
||||
:image-picker-props {:profile-picture @profile-pic
|
||||
:full-name (if (seq @full-name)
|
||||
@full-name
|
||||
(i18n/label :t/your-name))
|
||||
:customization-color @custom-color}
|
||||
:title-input-props {:default-value display-name
|
||||
:auto-focus true
|
||||
:max-length c/profile-name-max-length
|
||||
:on-change-text on-change-text}}]]
|
||||
|
||||
[quo/info-message
|
||||
{:status info-type
|
||||
:size :default
|
||||
:icon (if valid-name? :i/positive-state :i/info)
|
||||
:color (when (= :default info-type) colors/white-70-blur)
|
||||
:container-style style/info-message}
|
||||
info-message]
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style style/color-title}
|
||||
(i18n/label :t/accent-colour)]]]
|
||||
[quo/color-picker
|
||||
{:blur? true
|
||||
:default-selected :blue
|
||||
:on-change on-change
|
||||
:window-width window-width
|
||||
:container-style {:padding-left (responsiveness/iphone-11-Pro-20-pixel-from-width
|
||||
window-width)}}]]]
|
||||
|
||||
[rn/keyboard-avoiding-view
|
||||
{:style {:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0}
|
||||
:pointer-events :box-none}
|
||||
[button-container @show-keyboard? keyboard-shown show-background? keyboard-height
|
||||
[quo/button
|
||||
{:accessibility-label :submit-create-profile-button
|
||||
:type :primary
|
||||
:customization-color @custom-color
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:onboarding/profile-data-set
|
||||
{:image-path @profile-pic
|
||||
:display-name @full-name
|
||||
:color @custom-color}]))
|
||||
:container-style style/continue-button
|
||||
:disabled? (or (not valid-name?) (not (seq @full-name)))}
|
||||
(i18n/label :t/continue)]]]])
|
||||
(finally
|
||||
(oops/ocall show-listener "remove")
|
||||
(oops/ocall hide-listener "remove"))))
|
||||
|
||||
(defn create-profile
|
||||
[]
|
||||
(let [{:keys [top]} (safe-area/get-insets)
|
||||
onboarding-profile-data (rf/sub [:onboarding/profile])]
|
||||
[page
|
||||
{:navigation-bar-top top
|
||||
:onboarding-profile-data onboarding-profile-data}]))
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.contexts.onboarding.events
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]
|
||||
[re-frame.core :as re-frame]
|
||||
status-im.common.biometric.events
|
||||
[status-im.constants :as constants]
|
||||
@@ -111,63 +112,57 @@
|
||||
(rf/reg-event-fx
|
||||
:onboarding/password-set
|
||||
(fn [{:keys [db]} [masked-password]]
|
||||
(let [biometric-supported-type (get-in db [:biometrics :supported-type])]
|
||||
(let [biometric-supported-type (get-in db [:biometrics :supported-type])
|
||||
from-screen (get db
|
||||
:onboarding/navigated-to-enter-seed-phrase-from-screen
|
||||
:screen/onboarding.new-to-status)]
|
||||
{:db (-> db
|
||||
(assoc-in [:onboarding/profile :password] masked-password)
|
||||
(assoc-in [:onboarding/profile :auth-method] constants/auth-method-password))
|
||||
:fx [[:dispatch
|
||||
(if biometric-supported-type
|
||||
[:navigate-to-within-stack
|
||||
[:screen/onboarding.enable-biometrics
|
||||
(get db
|
||||
:onboarding/navigated-to-enter-seed-phrase-from-screen
|
||||
:screen/onboarding.new-to-status)]]
|
||||
[:navigate-to-within-stack [:screen/onboarding.enable-biometrics from-screen]]
|
||||
[:onboarding/create-account-and-login])]]})))
|
||||
|
||||
(rf/defn navigate-to-enable-biometrics
|
||||
{:events [:onboarding/navigate-to-enable-biometrics]}
|
||||
[{:keys [db]}]
|
||||
(let [supported-type (get-in db [:biometrics :supported-type])]
|
||||
{:dispatch (if supported-type
|
||||
[:open-modal :screen/onboarding.enable-biometrics]
|
||||
[:open-modal :screen/onboarding.enable-notifications])}))
|
||||
(rf/reg-event-fx
|
||||
:onboarding/navigate-to-enable-biometrics
|
||||
(fn [{:keys [db]}]
|
||||
(let [supported-type (get-in db [:biometrics :supported-type])]
|
||||
{:dispatch (if supported-type
|
||||
[:open-modal :screen/onboarding.enable-biometrics]
|
||||
[:open-modal :screen/onboarding.enable-notifications])})))
|
||||
|
||||
(rf/defn seed-phrase-validated
|
||||
{:events [:onboarding/seed-phrase-validated]}
|
||||
[{:keys [db]} seed-phrase key-uid]
|
||||
(let [syncing-account-recovered? (and (seq (:syncing/key-uid db))
|
||||
(= (:syncing/key-uid db) key-uid))
|
||||
next-screen (if syncing-account-recovered?
|
||||
:screen/onboarding.create-profile-password
|
||||
:screen/onboarding.create-profile)]
|
||||
(if (contains? (:profile/profiles-overview db) key-uid)
|
||||
{:effects.utils/show-confirmation
|
||||
{:title (i18n/label :t/multiaccount-exists-title)
|
||||
:content (i18n/label :t/multiaccount-exists-content)
|
||||
:confirm-button-text (i18n/label :t/unlock)
|
||||
:on-accept (fn []
|
||||
(re-frame/dispatch [:pop-to-root :screen/profile.profiles])
|
||||
(re-frame/dispatch
|
||||
[:profile/profile-selected key-uid]))
|
||||
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}}
|
||||
{:db (-> db
|
||||
(assoc-in [:onboarding/profile :seed-phrase] seed-phrase)
|
||||
(assoc-in [:onboarding/profile :key-uid] key-uid)
|
||||
(assoc-in [:onboarding/profile :color] constants/profile-default-color))
|
||||
:fx [[:dispatch
|
||||
[:navigate-to-within-stack
|
||||
[next-screen
|
||||
(get db
|
||||
:onboarding/navigated-to-enter-seed-phrase-from-screen
|
||||
:screen/onboarding.new-to-status)]]]]})))
|
||||
(rf/reg-event-fx
|
||||
:onboarding/seed-phrase-validated
|
||||
(fn [{:keys [db]} [seed-phrase key-uid]]
|
||||
(let [next-screen :screen/onboarding.create-profile-password
|
||||
from-screen (get db
|
||||
:onboarding/navigated-to-enter-seed-phrase-from-screen
|
||||
:screen/onboarding.new-to-status)]
|
||||
(if (contains? (:profile/profiles-overview db) key-uid)
|
||||
{:fx [[:effects.utils/show-confirmation
|
||||
{:title (i18n/label :t/multiaccount-exists-title)
|
||||
:content (i18n/label :t/multiaccount-exists-content)
|
||||
:confirm-button-text (i18n/label :t/unlock)
|
||||
:on-accept (fn []
|
||||
(re-frame/dispatch [:pop-to-root :screen/profile.profiles])
|
||||
(re-frame/dispatch [:profile/profile-selected key-uid]))
|
||||
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}]]}
|
||||
{:db (-> db
|
||||
(assoc-in [:onboarding/profile :seed-phrase] seed-phrase)
|
||||
(assoc-in [:onboarding/profile :key-uid] key-uid)
|
||||
(assoc-in [:onboarding/profile :color] constants/profile-default-color))
|
||||
:fx [[:dispatch [:navigate-to-within-stack [next-screen from-screen]]]]}))))
|
||||
|
||||
(rf/defn navigate-to-create-profile
|
||||
{:events [:onboarding/navigate-to-create-profile]}
|
||||
[{:keys [db]}]
|
||||
;; Restart the flow
|
||||
{:db (dissoc db :onboarding/profile)
|
||||
:dispatch [:navigate-to-within-stack
|
||||
[:screen/onboarding.create-profile :screen/onboarding.new-to-status]]})
|
||||
(rf/reg-event-fx
|
||||
:onboarding/navigate-to-create-profile
|
||||
(fn [{:keys [db]}]
|
||||
{:db (-> db
|
||||
(assoc-in [:onboarding/profile :color] (rand-nth colors/account-colors))
|
||||
(update :onboarding/profile dissoc :image-path))
|
||||
:fx [[:dispatch
|
||||
[:navigate-to-within-stack
|
||||
[:screen/onboarding.create-profile-password :screen/onboarding.new-to-status]]]]}))
|
||||
|
||||
(rf/reg-event-fx :onboarding/navigate-to-sign-in-by-syncing
|
||||
(fn [{:keys [db]}]
|
||||
@@ -180,26 +175,43 @@
|
||||
(fn [{:keys [db]} [auth-method]]
|
||||
{:db (assoc db :auth-method auth-method)}))
|
||||
|
||||
(rf/defn onboarding-new-account-finalize-setup
|
||||
{:events [:onboarding/finalize-setup]}
|
||||
[{:keys [db]}]
|
||||
(let [masked-password (get-in db [:onboarding/profile :password])
|
||||
key-uid (get-in db [:profile/profile :key-uid])
|
||||
syncing? (get-in db [:onboarding/profile :syncing?])
|
||||
auth-method (get-in db [:onboarding/profile :auth-method])
|
||||
biometric-enabled? (= auth-method constants/auth-method-biometric)]
|
||||
(cond-> {:db (assoc db :onboarding/generated-keys? true)}
|
||||
biometric-enabled?
|
||||
(assoc :keychain/save-password-and-auth-method
|
||||
{:key-uid key-uid
|
||||
:masked-password (if syncing?
|
||||
masked-password
|
||||
(security/hash-masked-password masked-password))
|
||||
:on-success (fn []
|
||||
(rf/dispatch [:onboarding/set-auth-method auth-method])
|
||||
(when syncing?
|
||||
(rf/dispatch
|
||||
[:onboarding/navigate-to-enable-notifications-from-syncing])))
|
||||
:on-error #(log/error "failed to save biometrics"
|
||||
{:key-uid key-uid
|
||||
:error %})}))))
|
||||
(def ^:const temp-display-name
|
||||
"While creating a profile, we cannot use an empty string; this value works as a
|
||||
placeholder that will be updated later once the compressed key exists. See
|
||||
`status-im.contexts.profile.edit.name.events/get-default-display-name` for more details."
|
||||
"temporal username")
|
||||
|
||||
(rf/reg-event-fx
|
||||
:onboarding/use-temporary-display-name
|
||||
(fn [{:keys [db]} [temporary-display-name?]]
|
||||
{:db (assoc db
|
||||
:onboarding/profile
|
||||
{:temporary-display-name? temporary-display-name?
|
||||
:display-name (if temporary-display-name?
|
||||
temp-display-name
|
||||
"")})}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:onboarding/finalize-setup
|
||||
(fn [{db :db}]
|
||||
(let [{:keys [password syncing? auth-method
|
||||
temporary-display-name?]} (:onboarding/profile db)
|
||||
{:keys [key-uid] :as profile} (:profile/profile db)
|
||||
biometric-enabled? (= auth-method constants/auth-method-biometric)]
|
||||
{:db (assoc db :onboarding/generated-keys? true)
|
||||
:fx [(when temporary-display-name?
|
||||
[:dispatch [:profile/set-default-profile-name profile]])
|
||||
(when biometric-enabled?
|
||||
[:keychain/save-password-and-auth-method
|
||||
{:key-uid key-uid
|
||||
:masked-password (if syncing?
|
||||
password
|
||||
(security/hash-masked-password password))
|
||||
:on-success (fn []
|
||||
(rf/dispatch [:onboarding/set-auth-method auth-method])
|
||||
(when syncing?
|
||||
(rf/dispatch
|
||||
[:onboarding/navigate-to-enable-notifications-from-syncing])))
|
||||
:on-error #(log/error "failed to save biometrics"
|
||||
{:key-uid key-uid
|
||||
:error %})}])]})))
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
[]
|
||||
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
|
||||
(blur-show-fn))
|
||||
(rf/dispatch [:onboarding/use-temporary-display-name false])
|
||||
(rf/dispatch [:open-modal
|
||||
:screen/onboarding.share-usage
|
||||
{:next-screen :screen/onboarding.sync-or-recover-profile}]))
|
||||
@@ -60,6 +61,7 @@
|
||||
[]
|
||||
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
|
||||
(blur-show-fn))
|
||||
(rf/dispatch [:onboarding/use-temporary-display-name true])
|
||||
(rf/dispatch [:open-modal :screen/onboarding.share-usage
|
||||
{:next-screen :screen/onboarding.new-to-status}]))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
(ns status-im.contexts.profile.edit.name.events
|
||||
(:require [utils.i18n :as i18n]
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-event-fx :profile/edit-profile-name-success
|
||||
@@ -20,3 +22,16 @@
|
||||
:on-success [:profile/edit-profile-name-success]}]]]})
|
||||
|
||||
(rf/reg-event-fx :profile/edit-name edit-profile-name)
|
||||
|
||||
(defn display-name-from-compressed-key
|
||||
[profile]
|
||||
(-> profile :compressed-key (string/split #"zQ3") second (subs 0 constants/profile-name-max-length)))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:profile/set-default-profile-name
|
||||
(fn [{db :db} [profile]]
|
||||
(let [default-display-name (display-name-from-compressed-key profile)]
|
||||
{:db (assoc-in db [:profile/profile :display-name] default-display-name)
|
||||
:fx [[:json-rpc/call
|
||||
[{:method "wakuext_setDisplayName"
|
||||
:params [default-display-name]}]]]})))
|
||||
|
||||
@@ -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]]]
|
||||
@@ -152,15 +155,12 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:profile.login/login-node-signal
|
||||
(fn [{{:onboarding/keys [recovered-account? new-account?] :as db} :db}
|
||||
[{:keys [settings account ensUsernames error]}]]
|
||||
(fn [{db :db} [{:keys [settings account ensUsernames error]}]]
|
||||
(log/debug "[signals] node.login" "error" error)
|
||||
(if error
|
||||
{:db (update db :profile/login #(-> % (dissoc :processing) (assoc :error error)))}
|
||||
{:db (dissoc db :profile/login)
|
||||
:fx [(when (and new-account? (not recovered-account?))
|
||||
[:dispatch-later [{:ms 1000 :dispatch [:wallet-legacy/set-initial-blocks-range]}]])
|
||||
[:dispatch-later [{:ms 2000 :dispatch [:ens/update-usernames ensUsernames]}]]
|
||||
:fx [[:dispatch-later [{:ms 2000 :dispatch [:ens/update-usernames ensUsernames]}]]
|
||||
[:dispatch [:profile.login/login-existing-profile settings account]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
[]
|
||||
(when @push-animation-fn-atom
|
||||
(@push-animation-fn-atom))
|
||||
(rf/dispatch [:onboarding/use-temporary-display-name true])
|
||||
(debounce/throttle-and-dispatch
|
||||
[:open-modal :screen/onboarding.new-to-status]
|
||||
1000))
|
||||
@@ -55,6 +56,7 @@
|
||||
[]
|
||||
(when @push-animation-fn-atom
|
||||
(@push-animation-fn-atom))
|
||||
(rf/dispatch [:onboarding/use-temporary-display-name false])
|
||||
(debounce/throttle-and-dispatch
|
||||
[:open-modal :screen/onboarding.sync-or-recover-profile]
|
||||
1000))
|
||||
|
||||
@@ -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,6 +1,5 @@
|
||||
(ns status-im.contexts.wallet.bridge.bridge-to.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.resources :as quo.resources]
|
||||
[quo.theme]
|
||||
@@ -16,27 +15,34 @@
|
||||
(defn- bridge-token-component
|
||||
[]
|
||||
(fn [{:keys [chain-id network-name]} {:keys [supported-networks] :as token}]
|
||||
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
|
||||
currency (rf/sub [:profile/currency])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
prices-per-token (rf/sub [:wallet/prices-per-token])
|
||||
balance (utils/calculate-total-token-balance token [chain-id])
|
||||
crypto-value (utils/get-standard-crypto-format token balance prices-per-token)
|
||||
fiat-value (utils/calculate-token-fiat-value
|
||||
{:currency currency
|
||||
:balance balance
|
||||
:token token
|
||||
:prices-per-token prices-per-token})
|
||||
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
|
||||
fiat-value)
|
||||
token-available-on-network? (network-utils/token-available-on-network? supported-networks
|
||||
chain-id)]
|
||||
(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 (rf/sub [:wallet/send-network])
|
||||
{selected-network-chain-id :chain-id} selected-network
|
||||
balance (utils/calculate-total-token-balance token [chain-id])
|
||||
crypto-value (utils/get-standard-crypto-format token
|
||||
balance
|
||||
prices-per-token)
|
||||
fiat-value (utils/calculate-token-fiat-value
|
||||
{:currency currency
|
||||
:balance balance
|
||||
:token token
|
||||
:prices-per-token prices-per-token})
|
||||
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
|
||||
fiat-value)
|
||||
token-available-on-network? (network-utils/token-available-on-network?
|
||||
supported-networks
|
||||
chain-id)]
|
||||
[quo/network-list
|
||||
{:label (name network-name)
|
||||
:network-image (quo.resources/get-network (:network-name network))
|
||||
:token-value (str crypto-value " " (:symbol token))
|
||||
:fiat-value fiat-formatted
|
||||
:state (if token-available-on-network? :default :disabled)
|
||||
:state (if (and token-available-on-network? (not= chain-id selected-network-chain-id))
|
||||
:default
|
||||
:disabled)
|
||||
:on-press #(rf/dispatch [:wallet/select-bridge-network
|
||||
{:network-chain-id chain-id
|
||||
:stack-id :screen/wallet.bridge-to}])}])))
|
||||
@@ -55,8 +61,7 @@
|
||||
(assoc account-token
|
||||
:networks (:networks token)
|
||||
:supported-networks (:supported-networks token)))
|
||||
bridge-to-title (i18n/label :t/bridge-to
|
||||
{:name (string/upper-case (str token-symbol))})]
|
||||
bridge-to-title (i18n/label :t/select-network-to-receive)]
|
||||
|
||||
(hot-reload/use-safe-unmount #(rf/dispatch [:wallet/clean-bridge-to-selection]))
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
(ns status-im.contexts.wallet.bridge.flow-config)
|
||||
|
||||
(def steps
|
||||
[{:screen-id :screen/wallet.select-from
|
||||
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
|
||||
{:screen-id :screen/wallet.bridge-select-asset
|
||||
[{:screen-id :screen/wallet.bridge-select-asset
|
||||
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :token])))}
|
||||
{:screen-id :screen/wallet.bridge-to
|
||||
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :bridge-to-chain-id])))}
|
||||
|
||||
@@ -21,5 +21,4 @@
|
||||
:stack-id :screen/wallet.bridge-input-amount}]))
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/clean-send-amount]))}]])
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -77,24 +77,31 @@
|
||||
(defn token-value-drawer
|
||||
[token watch-only? entry-point]
|
||||
(let [token-symbol (:token token)
|
||||
token-data (rf/sub [:wallet/token-by-symbol token-symbol])
|
||||
token-data (rf/sub [:wallet/token-by-symbol-from-first-available-account-with-balance
|
||||
token-symbol])
|
||||
selected-account (rf/sub [:wallet/current-viewing-account-address])
|
||||
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
|
||||
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
|
||||
account-owns-token? (rf/sub [:wallet/current-account-owns-token token-symbol])
|
||||
network-details (rf/sub [:wallet/network-details])
|
||||
receive-token-symbol (if (= token-symbol "SNT") "ETH" "SNT")
|
||||
token-owned? (if selected-account account-owns-token? (seq token-owners))
|
||||
asset-to-receive (rf/sub [:wallet/token-by-symbol receive-token-symbol])
|
||||
params (cond-> {:start-flow? true
|
||||
:owners token-owners
|
||||
:testnet-mode? testnet-mode?
|
||||
:asset-to-receive asset-to-receive}
|
||||
unique-owner? (= (count token-owners) 1)
|
||||
params (cond-> {:start-flow? true
|
||||
:owners token-owners
|
||||
:testnet-mode? testnet-mode?}
|
||||
selected-account
|
||||
(assoc :token token-data
|
||||
:stack-id :screen/wallet.accounts
|
||||
:has-balance? (-> (get-in token [:values :fiat-unformatted-value])
|
||||
money/above-zero?))
|
||||
(not selected-account)
|
||||
(and (not selected-account) unique-owner?)
|
||||
(assoc :token-symbol token-symbol
|
||||
:token token-data
|
||||
:stack-id :wallet-stack)
|
||||
|
||||
(and (not selected-account) (not unique-owner?))
|
||||
(assoc :token-symbol token-symbol
|
||||
:stack-id :wallet-stack))]
|
||||
[quo/action-drawer
|
||||
@@ -108,9 +115,13 @@
|
||||
(action-send params entry-point))
|
||||
(action-receive selected-account)
|
||||
(when token-owned?
|
||||
(action-swap params))
|
||||
(action-swap (assoc params
|
||||
:asset-to-receive
|
||||
asset-to-receive)))
|
||||
(when token-owned?
|
||||
(action-bridge (assoc params
|
||||
:network-details
|
||||
network-details
|
||||
:bridge-disabled?
|
||||
(send-utils/bridge-disabled? token-symbol))))]))]]))
|
||||
|
||||
@@ -120,7 +131,11 @@
|
||||
(cond-> item
|
||||
(or (not watch-only?) (ff/enabled? ::ff/wallet.long-press-watch-only-asset))
|
||||
(assoc :on-long-press
|
||||
#(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [token-value-drawer item watch-only? entry-point])
|
||||
:selected-item (fn [] [quo/token-value item])}])))])
|
||||
(fn []
|
||||
(when (= entry-point :wallet-stack)
|
||||
(rf/dispatch [:wallet/close-account-page])
|
||||
(rf/dispatch [:wallet/clean-current-viewing-account]))
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [token-value-drawer item watch-only? entry-point])
|
||||
:selected-item (fn [] [quo/token-value item])}]))))])
|
||||
|
||||
@@ -546,14 +546,20 @@
|
||||
(let [priority #(get constants/token-sort-priority (:symbol %) ##Inf)]
|
||||
(sort-by (juxt :symbol priority) tokens)))
|
||||
|
||||
(defn token-with-balance
|
||||
([token networks]
|
||||
(token-with-balance token networks nil))
|
||||
([token networks chain-ids]
|
||||
(assoc token
|
||||
:networks (network-utils/network-list-with-positive-balance token networks)
|
||||
:supported-networks (network-utils/network-list token networks)
|
||||
:available-balance (calculate-total-token-balance token)
|
||||
:total-balance (calculate-total-token-balance token chain-ids))))
|
||||
|
||||
(defn tokens-with-balance
|
||||
[tokens networks chain-ids]
|
||||
(map (fn [token]
|
||||
(assoc token
|
||||
:networks (network-utils/network-list-with-positive-balance token networks)
|
||||
:supported-networks (network-utils/network-list token networks)
|
||||
:available-balance (calculate-total-token-balance token)
|
||||
:total-balance (calculate-total-token-balance token chain-ids)))
|
||||
(token-with-balance token networks chain-ids))
|
||||
tokens))
|
||||
|
||||
(defn estimated-time-format
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
(ns status-im.contexts.wallet.common.wizard.events
|
||||
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
|
||||
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]
|
||||
(:require [status-im.contexts.wallet.common.wizard.utils :as wizard-utils]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- wizard-find-next-screen
|
||||
[db flow-id current-screen]
|
||||
(let [flow-config (case flow-id
|
||||
:wallet-send-flow wallet-send-flow/steps
|
||||
:wallet-bridge-flow wallet-bridge-flow/steps
|
||||
nil)]
|
||||
(first (filter (fn [{:keys [skip-step? screen-id]}]
|
||||
(and (not= screen-id current-screen)
|
||||
(not (and (fn? skip-step?) (skip-step? db)))))
|
||||
flow-config))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/wizard-navigate-forward
|
||||
(fn [{:keys [db]} [{:keys [current-screen flow-id start-flow?]}]]
|
||||
(let [next-screen (wizard-find-next-screen db flow-id current-screen)]
|
||||
(let [next-screen (wizard-utils/wizard-find-next-screen db flow-id current-screen)]
|
||||
{:fx [[:dispatch
|
||||
(if start-flow?
|
||||
[:open-modal (:screen-id next-screen)]
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
(ns status-im.contexts.wallet.common.wizard.utils
|
||||
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
|
||||
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]))
|
||||
|
||||
(defn wizard-find-next-screen
|
||||
[db flow-id current-screen]
|
||||
(let [flow-config (case flow-id
|
||||
:wallet-send-flow wallet-send-flow/steps
|
||||
:wallet-bridge-flow wallet-bridge-flow/steps
|
||||
nil)]
|
||||
(first (filter (fn [{:keys [skip-step? screen-id]}]
|
||||
(and (not= screen-id current-screen)
|
||||
(not (and (fn? skip-step?) (skip-step? db)))))
|
||||
flow-config))))
|
||||
@@ -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))))))
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
[status-im.contexts.settings.wallet.effects]
|
||||
[status-im.contexts.settings.wallet.events]
|
||||
[status-im.contexts.wallet.common.activity-tab.events]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[status-im.contexts.wallet.common.utils.external-links :as external-links]
|
||||
[status-im.contexts.wallet.common.utils.networks :as network-utils]
|
||||
[status-im.contexts.wallet.data-store :as data-store]
|
||||
[status-im.contexts.wallet.db :as db]
|
||||
[status-im.contexts.wallet.item-types :as item-types]
|
||||
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
|
||||
[status-im.contexts.wallet.tokens.events]
|
||||
[status-im.feature-flags :as ff]
|
||||
[taoensso.timbre :as log]
|
||||
@@ -114,7 +116,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
|
||||
@@ -204,34 +206,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]]
|
||||
: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
|
||||
@@ -244,13 +254,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
|
||||
@@ -307,9 +322,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)
|
||||
@@ -343,19 +357,79 @@
|
||||
(rf/reg-event-fx :wallet/get-keypairs get-keypairs)
|
||||
|
||||
(rf/reg-event-fx :wallet/bridge-select-token
|
||||
(fn [{:keys [db]} [{:keys [token token-symbol stack-id start-flow?]}]]
|
||||
(let [missing-recipient? (-> db :wallet :ui :send :to-address nil?)
|
||||
to-address (-> db :wallet :current-viewing-account-address)]
|
||||
(fn [{:keys [db]}
|
||||
[{:keys [token token-symbol stack-id network owners network-details start-flow?] :as params}]]
|
||||
(let [{:keys [wallet]} db
|
||||
unique-owner (when (= (count owners) 1)
|
||||
(first owners))
|
||||
token (if (and unique-owner (nil? token))
|
||||
(let [token (utils/get-token-from-account db
|
||||
token-symbol
|
||||
unique-owner)]
|
||||
(utils/token-with-balance token network-details))
|
||||
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))
|
||||
account-not-defined? (and (not unique-owner) multi-account-balance?)
|
||||
networks-with-balance (when (and token (:balances-per-chain token))
|
||||
(filter #(not= (:balance %) "0")
|
||||
(vals (:balances-per-chain token))))
|
||||
balance-in-only-one-network? (when networks-with-balance (= (count networks-with-balance) 1))
|
||||
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||
network-details (-> (get-in db
|
||||
[:wallet :networks
|
||||
(if test-networks-enabled? :test :prod)])
|
||||
(network-utils/sorted-networks-with-details))
|
||||
network (if balance-in-only-one-network?
|
||||
(first (filter #(= (:chain-id %)
|
||||
(:chain-id (first networks-with-balance)))
|
||||
network-details))
|
||||
network)]
|
||||
{:db (cond-> db
|
||||
:always (assoc-in [:wallet :ui :send :tx-type] :tx/bridge)
|
||||
token (assoc-in [:wallet :ui :send :token] token)
|
||||
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
|
||||
network (assoc-in [:wallet :ui :send :network] network)
|
||||
missing-recipient? (assoc-in [:wallet :ui :send :to-address] to-address))
|
||||
:fx [[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id :wallet-bridge-flow}]]]})))
|
||||
:fx (cond
|
||||
;; If the token has a balance in more than one account and this was dispatched from the
|
||||
;; general wallet screen, open the account selection screen.
|
||||
(and account-not-defined? root-screen?)
|
||||
[[:dispatch [:open-modal :screen/wallet.select-from]]]
|
||||
|
||||
;; If the token has a balance in only one account (or this was dispatched from the
|
||||
;; account screen) and the network is already set, navigate forward in the bridge flow.
|
||||
(some? network)
|
||||
[[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id :wallet-bridge-flow}]]]
|
||||
|
||||
;; If we know which account to bridge the token from but the network is not set yet,
|
||||
;; show the network selection drawer.
|
||||
:else
|
||||
[[:dispatch [:wallet/switch-current-viewing-account to-address]]
|
||||
[:dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[network-selection/view
|
||||
{:title (i18n/label :t/select-network-to-bridge-from)
|
||||
:token-symbol (or token-symbol (:symbol token))
|
||||
:source :bridge
|
||||
:on-select-network (fn [network]
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch
|
||||
[:wallet/bridge-select-token
|
||||
(assoc params :network network)]))}])}]]])})))
|
||||
|
||||
(rf/reg-event-fx :wallet/start-bridge
|
||||
(fn [{:keys [db]}]
|
||||
|
||||
@@ -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]]
|
||||
: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])))))
|
||||
:params [[address] true]
|
||||
: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]
|
||||
|
||||
@@ -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
|
||||
[]
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
[status-im.contexts.wallet.common.utils.networks :as network-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]
|
||||
@@ -41,64 +42,42 @@
|
||||
(fn [{:keys [db]} [suggested-routes-data enough-assets?]]
|
||||
(if-not enough-assets?
|
||||
{:db (update-in db [:wallet :ui :send] add-not-enough-assets-data (:best suggested-routes-data))}
|
||||
(let [chosen-route (:best suggested-routes-data)
|
||||
(let [chosen-route (:best suggested-routes-data)
|
||||
{:keys [token collectible token-display-name
|
||||
receiver-networks
|
||||
receiver-network-values
|
||||
sender-network-values tx-type
|
||||
disabled-from-chain-ids
|
||||
from-locked-amounts]} (get-in db [:wallet :ui :send])
|
||||
token-decimals (if collectible 0 (:decimals token))
|
||||
native-token? (and token (= token-display-name "ETH"))
|
||||
routes-available? (pos? (count chosen-route))
|
||||
token-networks (:supported-networks token)
|
||||
token-networks-ids (when token-networks
|
||||
(map #(:chain-id %) token-networks))
|
||||
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
|
||||
{:route chosen-route
|
||||
:token-decimals token-decimals
|
||||
:native-token? native-token?
|
||||
:receiver? false})
|
||||
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
|
||||
{:route chosen-route
|
||||
:token-decimals token-decimals
|
||||
:native-token? native-token?
|
||||
:receiver? true})
|
||||
sender-possible-chain-ids (map :chain-id sender-network-values)
|
||||
sender-network-values (if routes-available?
|
||||
(send-utils/network-amounts
|
||||
{:network-values
|
||||
(if (= tx-type :tx/bridge)
|
||||
from-network-amounts-by-chain
|
||||
(send-utils/add-zero-values-to-network-values
|
||||
from-network-amounts-by-chain
|
||||
sender-possible-chain-ids))
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:from-locked-amounts from-locked-amounts
|
||||
:tx-type tx-type
|
||||
:receiver? false})
|
||||
(send-utils/reset-loading-network-amounts-to-zero
|
||||
sender-network-values))
|
||||
receiver-network-values (if routes-available?
|
||||
(send-utils/network-amounts
|
||||
{:network-values to-network-amounts-by-chain
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? true})
|
||||
(cond->
|
||||
(send-utils/reset-loading-network-amounts-to-zero
|
||||
receiver-network-values)
|
||||
|
||||
(not= tx-type :tx/bridge)
|
||||
send-utils/safe-add-type-edit))
|
||||
network-links (when routes-available?
|
||||
(send-utils/network-links chosen-route
|
||||
sender-network-values
|
||||
receiver-network-values))]
|
||||
sender-network-values tx-type]} (get-in db [:wallet :ui :send])
|
||||
token-decimals (if collectible 0 (:decimals token))
|
||||
native-token? (and token (= token-display-name "ETH"))
|
||||
routes-available? (pos? (count chosen-route))
|
||||
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
|
||||
{:route chosen-route
|
||||
:token-decimals token-decimals
|
||||
:native-token? native-token?
|
||||
:receiver? false})
|
||||
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
|
||||
{:route chosen-route
|
||||
:token-decimals token-decimals
|
||||
:native-token? native-token?
|
||||
:receiver? true})
|
||||
sender-possible-chain-ids (map :chain-id sender-network-values)
|
||||
sender-network-values (if routes-available?
|
||||
(send-utils/network-amounts
|
||||
(if (= tx-type :tx/bridge)
|
||||
from-network-amounts-by-chain
|
||||
(send-utils/add-zero-values-to-network-values
|
||||
from-network-amounts-by-chain
|
||||
sender-possible-chain-ids)))
|
||||
(send-utils/reset-loading-network-amounts-to-zero
|
||||
sender-network-values))
|
||||
receiver-network-values (if routes-available?
|
||||
(send-utils/network-amounts
|
||||
to-network-amounts-by-chain)
|
||||
(send-utils/reset-loading-network-amounts-to-zero
|
||||
receiver-network-values))
|
||||
network-links (when routes-available?
|
||||
(send-utils/network-links chosen-route
|
||||
sender-network-values
|
||||
receiver-network-values))]
|
||||
{:db (update-in db
|
||||
[:wallet :ui :send]
|
||||
assoc
|
||||
@@ -137,34 +116,19 @@
|
||||
(fn [{:keys [db]}]
|
||||
{:db (update-in db [:wallet :ui :send] dissoc :amount)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/clean-disabled-from-networks
|
||||
(fn [{:keys [db]}]
|
||||
{:db (update-in db [:wallet :ui :send] dissoc :disabled-from-chain-ids)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/clean-from-locked-amounts
|
||||
(fn [{:keys [db]}]
|
||||
{:db (update-in db [:wallet :ui :send] dissoc :from-locked-amounts)}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/select-send-address
|
||||
(fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]]
|
||||
(let [[prefix to-address] (utils/split-prefix-and-address address)
|
||||
testnet-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||
receiver-networks (network-utils/resolve-receiver-networks
|
||||
{:prefix prefix
|
||||
:testnet-enabled? testnet-enabled?})
|
||||
collectible-tx? (send-utils/tx-type-collectible?
|
||||
(-> db :wallet :ui :send :tx-type))
|
||||
collectible (when collectible-tx?
|
||||
(-> db :wallet :ui :send :collectible))
|
||||
one-collectible? (when collectible-tx?
|
||||
(= (collectible.utils/collectible-balance collectible) 1))]
|
||||
(let [[_ to-address] (utils/split-prefix-and-address address)
|
||||
collectible-tx? (send-utils/tx-type-collectible?
|
||||
(-> db :wallet :ui :send :tx-type))
|
||||
collectible (when collectible-tx?
|
||||
(-> db :wallet :ui :send :collectible))
|
||||
one-collectible? (when collectible-tx?
|
||||
(= (collectible.utils/collectible-balance collectible) 1))]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :ui :send :recipient] (or recipient address))
|
||||
(assoc-in [:wallet :ui :send :to-address] to-address)
|
||||
(assoc-in [:wallet :ui :send :address-prefix] prefix)
|
||||
(assoc-in [:wallet :ui :send :receiver-preferred-networks] receiver-networks)
|
||||
(assoc-in [:wallet :ui :send :receiver-networks] receiver-networks))
|
||||
(assoc-in [:wallet :ui :send :to-address] to-address))
|
||||
:fx [(when (and collectible-tx? one-collectible?)
|
||||
[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]])
|
||||
[:dispatch
|
||||
@@ -182,32 +146,47 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/set-token-to-send
|
||||
(fn [{:keys [db]} [{:keys [token-symbol token stack-id start-flow? owners]} entry-point]]
|
||||
(fn [{:keys [db]}
|
||||
[{:keys [token-symbol token network stack-id start-flow? owners] :as params} entry-point]]
|
||||
;; `token` is a map extracted from the sender, but in the wallet home page we don't know the
|
||||
;; sender yet, so we only provide the `token-symbol`, later in
|
||||
;; `:wallet/select-from-account` the `token` key will be set.
|
||||
(let [{:keys [networks]} token
|
||||
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
|
||||
token-networks-ids (map :chain-id networks)
|
||||
unsupported-token? (not-any? (set receiver-networks) token-networks-ids)
|
||||
unique-owner (when (= (count owners) 1)
|
||||
(first owners))
|
||||
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
|
||||
token-data (or token
|
||||
(when (and token-symbol unique-owner)
|
||||
(some #(when (= (:symbol %) token-symbol) %)
|
||||
unique-owner-tokens)))
|
||||
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))]
|
||||
(let [{:keys [wallet]} db
|
||||
unique-owner (when (= (count owners) 1)
|
||||
(first owners))
|
||||
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
|
||||
token-data (or token
|
||||
(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))
|
||||
account-not-defined? (and (not unique-owner) multi-account-balance?)
|
||||
networks-with-balance (when (and token-data (:balances-per-chain token))
|
||||
(filter #(not= (:balance %) "0")
|
||||
(vals (:balances-per-chain token))))
|
||||
balance-in-only-one-network? (when networks-with-balance (= (count networks-with-balance) 1))
|
||||
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||
network-details (-> (get-in db
|
||||
[:wallet :networks
|
||||
(if test-networks-enabled? :test :prod)])
|
||||
(network-utils/sorted-networks-with-details))
|
||||
network (if balance-in-only-one-network?
|
||||
(first (filter #(= (:chain-id %)
|
||||
(:chain-id (first networks-with-balance)))
|
||||
network-details))
|
||||
network)]
|
||||
(println multi-account-balance? "43247329479847392")
|
||||
(when (or token-data token-symbol)
|
||||
{:db (cond-> db
|
||||
:always (update-in [:wallet :ui :send]
|
||||
network (update-in [:wallet :ui :send]
|
||||
#(-> %
|
||||
(dissoc :collectible :tx-type)
|
||||
(assoc :token-not-supported-in-receiver-networks?
|
||||
unsupported-token?)))
|
||||
(assoc :network network)))
|
||||
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
|
||||
token-data (update-in [:wallet :ui :send]
|
||||
#(assoc %
|
||||
@@ -216,20 +195,45 @@
|
||||
(network-utils/network-list
|
||||
token-data
|
||||
network-details))
|
||||
:token-display-name (:symbol token-data)))
|
||||
:token-display-name (:symbol token-data)
|
||||
:token-symbol (:symbol token-data)))
|
||||
unique-owner (assoc-in [:wallet :current-viewing-account-address] unique-owner)
|
||||
entry-point (assoc-in [:wallet :ui :send :entry-point] entry-point))
|
||||
:fx [[:dispatch [:wallet/stop-and-clean-suggested-routes]]
|
||||
[:dispatch
|
||||
;; ^:flush-dom allows us to make sure the re-frame DB state is always synced
|
||||
;; before the navigation occurs, so the new screen is always rendered with
|
||||
;; the DB state set by this event. By adding the metadata we are omitting
|
||||
;; a 1-frame blink when the screen is mounted.
|
||||
^:flush-dom
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id :wallet-send-flow}]]]}))))
|
||||
:fx (cond
|
||||
;; If the token has a balance in more than one account and this was dispatched from
|
||||
;; the general wallet screen, open the account selection screen.
|
||||
(and account-not-defined? root-screen?)
|
||||
[[:dispatch [:open-modal :screen/wallet.select-from]]]
|
||||
|
||||
;; If the token has a balance in only one account (or this was dispatched from the
|
||||
;; account screen) and the network is already set, stop and clean suggested routes,
|
||||
;; then navigate forward in the send flow.
|
||||
(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}]]]
|
||||
|
||||
;; If we don't know the network but need to set it, show the network selection drawer.
|
||||
:else
|
||||
[[:dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[network-selection/view
|
||||
{:token-symbol (or token-symbol (:symbol token-data))
|
||||
:source :send
|
||||
:on-select-network (fn [network]
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch
|
||||
[:wallet/set-token-to-send
|
||||
(assoc params :network network)]))}])}]]])}))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/edit-token-to-send
|
||||
@@ -246,8 +250,7 @@
|
||||
(assoc-in [:wallet :ui :send :token-not-supported-in-receiver-networks?]
|
||||
token-not-supported-in-receiver-networks?))
|
||||
:fx [[:dispatch [:hide-bottom-sheet]]
|
||||
[:dispatch [:wallet/stop-and-clean-suggested-routes]]
|
||||
[:dispatch [:wallet/clean-from-locked-amounts]]]})))
|
||||
[:dispatch [:wallet/stop-and-clean-suggested-routes]]]})))
|
||||
|
||||
(rf/reg-event-fx :wallet/clean-selected-token
|
||||
(fn [{:keys [db]}]
|
||||
@@ -367,32 +370,15 @@
|
||||
(fn [{:keys [db]}]
|
||||
(let [keys-to-remove [:to-values-by-chain :network-links :sender-network-values :route
|
||||
:receiver-network-values :suggested-routes :from-values-by-chain
|
||||
:loading-suggested-routes? :amount]]
|
||||
:loading-suggested-routes? :amount :enough-assets?]]
|
||||
{:db (update-in db [:wallet :ui :send] #(apply dissoc % keys-to-remove))})))
|
||||
|
||||
(rf/reg-event-fx :wallet/disable-from-networks
|
||||
(fn [{:keys [db]} [chain-ids]]
|
||||
{:db (assoc-in db [:wallet :ui :send :disabled-from-chain-ids] chain-ids)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/lock-from-amount
|
||||
(fn [{:keys [db]} [chain-id amount]]
|
||||
{:db (assoc-in db [:wallet :ui :send :from-locked-amounts chain-id] amount)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/unlock-from-amount
|
||||
(fn [{:keys [db]} [chain-id]]
|
||||
{:db (update-in db [:wallet :ui :send :from-locked-amounts] dissoc chain-id)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/reset-network-amounts-to-zero
|
||||
(fn [{:keys [db]}]
|
||||
(let [sender-network-values (get-in db [:wallet :ui :send :sender-network-values])
|
||||
receiver-network-values (get-in db [:wallet :ui :send :receiver-network-values])
|
||||
disabled-from-chain-ids (get-in db [:wallet :ui :send :disabled-from-chain-ids])
|
||||
sender-network-values (send-utils/reset-network-amounts-to-zero
|
||||
{:network-amounts sender-network-values
|
||||
:disabled-chain-ids disabled-from-chain-ids})
|
||||
receiver-network-values (send-utils/reset-network-amounts-to-zero
|
||||
{:network-amounts receiver-network-values
|
||||
:disabled-chain-ids []})]
|
||||
sender-network-values (send-utils/reset-network-amounts-to-zero sender-network-values)
|
||||
receiver-network-values (send-utils/reset-network-amounts-to-zero receiver-network-values)]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :ui :send :sender-network-values] sender-network-values)
|
||||
(assoc-in [:wallet :ui :send :receiver-network-values] receiver-network-values)
|
||||
@@ -404,89 +390,62 @@
|
||||
|
||||
(rf/reg-event-fx :wallet/start-get-suggested-routes
|
||||
(fn [{:keys [db]} [{:keys [amount amount-out updated-token] :as args :or {amount-out "0"}}]]
|
||||
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
|
||||
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
|
||||
{:keys [token tx-type collectible to-address
|
||||
receiver-networks disabled-from-chain-ids
|
||||
from-locked-amounts bridge-to-chain-id]
|
||||
network bridge-to-chain-id]
|
||||
:or {token updated-token}} (get-in db [:wallet :ui :send])
|
||||
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||
networks (get-in db [:wallet :networks (if test-networks-enabled? :test :prod)])
|
||||
network-chain-ids (map :chain-id networks)
|
||||
token-decimal (when token (:decimals token))
|
||||
token-id (utils/format-token-id token collectible)
|
||||
to-token-id ""
|
||||
gas-rates constants/gas-rate-medium
|
||||
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
|
||||
amount-in (to-hex amount)
|
||||
amount-out (to-hex amount-out)
|
||||
from-address wallet-address
|
||||
disabled-to-chain-ids (if (= tx-type :tx/bridge)
|
||||
(filter #(not= % bridge-to-chain-id) network-chain-ids)
|
||||
(filter (fn [chain-id]
|
||||
(not (some #(= chain-id %)
|
||||
receiver-networks)))
|
||||
network-chain-ids))
|
||||
from-locked-amount (update-vals from-locked-amounts to-hex)
|
||||
send-type (case tx-type
|
||||
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
|
||||
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
|
||||
:tx/bridge constants/send-type-bridge
|
||||
constants/send-type-transfer)
|
||||
balances-per-chain (when token (:balances-per-chain token))
|
||||
sender-token-available-networks-for-suggested-routes
|
||||
(when token
|
||||
(send-utils/token-available-networks-for-suggested-routes
|
||||
{:balances-per-chain balances-per-chain
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:only-with-balance? true}))
|
||||
receiver-token-available-networks-for-suggested-routes
|
||||
(when token
|
||||
(send-utils/token-available-networks-for-suggested-routes
|
||||
{:balances-per-chain balances-per-chain
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:only-with-balance? false}))
|
||||
token-networks-ids (when token (map #(:chain-id %) (:supported-networks token)))
|
||||
sender-network-values (when sender-token-available-networks-for-suggested-routes
|
||||
(send-utils/loading-network-amounts
|
||||
{:valid-networks
|
||||
(if (= tx-type :tx/bridge)
|
||||
(remove #(= bridge-to-chain-id %)
|
||||
sender-token-available-networks-for-suggested-routes)
|
||||
sender-token-available-networks-for-suggested-routes)
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? false}))
|
||||
receiver-network-values (when receiver-token-available-networks-for-suggested-routes
|
||||
(send-utils/loading-network-amounts
|
||||
{:valid-networks
|
||||
(if (= tx-type :tx/bridge)
|
||||
(filter
|
||||
#(= bridge-to-chain-id %)
|
||||
receiver-token-available-networks-for-suggested-routes)
|
||||
receiver-token-available-networks-for-suggested-routes)
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? true}))
|
||||
request-uuid (str (random-uuid))
|
||||
params [{:uuid request-uuid
|
||||
:sendType send-type
|
||||
:addrFrom from-address
|
||||
:addrTo to-address
|
||||
:amountIn amount-in
|
||||
:amountOut amount-out
|
||||
:tokenID token-id
|
||||
:toTokenID to-token-id
|
||||
:disabledFromChainIDs disabled-from-chain-ids
|
||||
:disabledToChainIDs disabled-to-chain-ids
|
||||
:gasFeeMode gas-rates
|
||||
:fromLockedAmount from-locked-amount
|
||||
:username (:username args)
|
||||
:publicKey (:publicKey args)
|
||||
:packID (:packID args)}]]
|
||||
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||
networks (get-in db
|
||||
[:wallet :networks
|
||||
(if test-networks-enabled? :test :prod)])
|
||||
network-chain-ids (map :chain-id networks)
|
||||
token-decimal (when token (:decimals token))
|
||||
token-id (utils/format-token-id token collectible)
|
||||
to-token-id ""
|
||||
gas-rates constants/gas-rate-medium
|
||||
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
|
||||
amount-in (to-hex amount)
|
||||
amount-out (to-hex amount-out)
|
||||
from-address wallet-address
|
||||
network-chain-id (if collectible
|
||||
(get-in collectible [:id :contract-id :chain-id])
|
||||
(:chain-id network))
|
||||
disabled-from-chain-ids (filter #(not= % network-chain-id) network-chain-ids)
|
||||
disabled-to-chain-ids (filter #(not= %
|
||||
(if (= tx-type :tx/bridge)
|
||||
bridge-to-chain-id
|
||||
network-chain-id))
|
||||
network-chain-ids)
|
||||
send-type (case tx-type
|
||||
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
|
||||
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
|
||||
:tx/bridge constants/send-type-bridge
|
||||
constants/send-type-transfer)
|
||||
sender-network-values (when (= tx-type :tx/bridge)
|
||||
(send-utils/loading-network-amounts
|
||||
{:networks [network-chain-id]
|
||||
:values {network-chain-id amount}
|
||||
:receiver? false}))
|
||||
receiver-network-values (when (= tx-type :tx/bridge)
|
||||
(send-utils/loading-network-amounts
|
||||
{:networks [bridge-to-chain-id]
|
||||
:receiver? true}))
|
||||
request-uuid (str (random-uuid))
|
||||
params [{:uuid request-uuid
|
||||
:sendType send-type
|
||||
:addrFrom from-address
|
||||
:addrTo to-address
|
||||
:amountIn amount-in
|
||||
:amountOut amount-out
|
||||
:tokenID token-id
|
||||
:toTokenID to-token-id
|
||||
:disabledFromChainIDs disabled-from-chain-ids
|
||||
:disabledToChainIDs disabled-to-chain-ids
|
||||
:gasFeeMode gas-rates
|
||||
:fromLockedAmount {}
|
||||
:username (:username args)
|
||||
:publicKey (:publicKey args)
|
||||
:packID (:packID args)}]]
|
||||
(when (and to-address from-address amount-in token-id)
|
||||
{:db (update-in db
|
||||
[:wallet :ui :send]
|
||||
@@ -619,19 +578,20 @@
|
||||
|
||||
(rf/reg-event-fx :wallet/clean-up-transaction-flow
|
||||
(fn [_]
|
||||
{:fx [[:dispatch [:wallet/clean-send-data]]
|
||||
{:fx [[:dispatch [:dismiss-modal :screen/wallet.transaction-confirmation]]
|
||||
[:dispatch [:wallet/clean-scanned-address]]
|
||||
[:dispatch [:wallet/clean-local-suggestions]]
|
||||
[:dispatch [:wallet/clean-send-address]]
|
||||
[:dispatch [:wallet/clean-disabled-from-networks]]
|
||||
[:dispatch [:wallet/select-address-tab nil]]]}))
|
||||
|
||||
(rf/reg-event-fx :wallet/end-transaction-flow
|
||||
(fn [{:keys [db]}]
|
||||
(let [address (get-in db [:wallet :current-viewing-account-address])]
|
||||
{:fx [[:dispatch [:dismiss-modal :screen/wallet.transaction-confirmation]]
|
||||
[:dispatch [:wallet/navigate-to-account-within-stack address]]
|
||||
[:dispatch [:wallet/select-account-tab :activity]]]})))
|
||||
{:fx [[:dispatch [:wallet/navigate-to-account-within-stack address]]
|
||||
[:dispatch [:wallet/select-account-tab :activity]]
|
||||
[:dispatch-later
|
||||
[{:ms 20
|
||||
:dispatch [:wallet/clean-up-transaction-flow]}]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/build-transactions-from-route
|
||||
@@ -710,34 +670,60 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/select-from-account
|
||||
(fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]]
|
||||
(fn [{db :db} [{:keys [address stack-id network-details network start-flow?] :as params}]]
|
||||
(let [{:keys [token-symbol
|
||||
tx-type]} (-> db :wallet :ui :send)
|
||||
token (when token-symbol
|
||||
;; When this flow has started in the wallet home page, we know the
|
||||
;; token or collectible to send, but we don't know from which
|
||||
;; account, so we extract the token data from the picked account.
|
||||
(let [token (utils/get-token-from-account db token-symbol address)]
|
||||
(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))))
|
||||
bridge-tx? (= tx-type :tx/bridge)
|
||||
flow-id (if bridge-tx?
|
||||
:wallet-bridge-flow
|
||||
:wallet-send-flow)]
|
||||
tx-type]} (-> db :wallet :ui :send)
|
||||
token (when token-symbol
|
||||
;; When this flow has started in the wallet home page, we
|
||||
;; know the token or collectible to send, but we don't know
|
||||
;; from which account, so we extract the token data from the
|
||||
;; picked account.
|
||||
(let [token (utils/get-token-from-account db
|
||||
token-symbol
|
||||
address)]
|
||||
(utils/token-with-balance token network-details)))
|
||||
bridge-tx? (= tx-type :tx/bridge)
|
||||
flow-id (if bridge-tx?
|
||||
:wallet-bridge-flow
|
||||
:wallet-send-flow)
|
||||
networks-with-balance (when (and token (:balances-per-chain token))
|
||||
(filter #(not= (:balance %) "0")
|
||||
(vals (:balances-per-chain token))))
|
||||
balance-in-only-one-network? (when networks-with-balance (= (count networks-with-balance) 1))
|
||||
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||
network-details (-> (get-in db
|
||||
[:wallet :networks
|
||||
(if test-networks-enabled? :test :prod)])
|
||||
(network-utils/sorted-networks-with-details))
|
||||
network (if balance-in-only-one-network?
|
||||
(first (filter #(= (:chain-id %)
|
||||
(:chain-id (first networks-with-balance)))
|
||||
network-details))
|
||||
network)]
|
||||
{:db (cond-> db
|
||||
network (assoc-in [:wallet :ui :send :network] network)
|
||||
token-symbol (assoc-in [:wallet :ui :send :token] token)
|
||||
bridge-tx? (assoc-in [:wallet :ui :send :to-address] address))
|
||||
:fx [[:dispatch [:wallet/switch-current-viewing-account address]]
|
||||
[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id flow-id}]]]})))
|
||||
:fx (if (some? network)
|
||||
[[:dispatch [:wallet/switch-current-viewing-account address]]
|
||||
[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? (and start-flow? (not balance-in-only-one-network?))
|
||||
:flow-id flow-id}]]]
|
||||
[[:dispatch [:dismiss-modal :screen/wallet.select-from]]
|
||||
[:dispatch [:wallet/switch-current-viewing-account address]]
|
||||
[:dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[network-selection/view
|
||||
{:token-symbol (or token-symbol (:symbol token))
|
||||
:source :send
|
||||
:on-select-network (fn [network]
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch
|
||||
[:wallet/select-from-account
|
||||
(assoc params :network network)]))}])}]]])})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/transaction-confirmation-navigate-back
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(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]
|
||||
status-im.contexts.wallet.send.events
|
||||
[status-im.contexts.wallet.send.utils :as send-utils]
|
||||
[test-helpers.unit :as h]
|
||||
@@ -40,19 +40,17 @@
|
||||
receiver-networks [421614 11155420]]
|
||||
(testing "can be called with :token"
|
||||
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
|
||||
:token-not-supported-in-receiver-networks? false}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
|
||||
:token-symbol token-symbol}}}}
|
||||
_ (reset! rf-db/app-db initial-db)
|
||||
result (dispatch [event-id {:token token}])]
|
||||
(is (match? expected-db (:db result)))))
|
||||
(testing "can be called with :token-symbol"
|
||||
(testing "can be called with :token"
|
||||
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-symbol token-symbol
|
||||
:token-not-supported-in-receiver-networks? true}}}}
|
||||
_ (reset! rf-db/app-db initial-db)
|
||||
result (dispatch [event-id {:token-symbol token-symbol}])]
|
||||
(is (match? expected-db (:db result))))))
|
||||
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-symbol token-symbol}}}}
|
||||
_ (reset! rf-db/app-db initial-db)
|
||||
result (dispatch [event-id {:token-symbol token-symbol}])]
|
||||
(is (match? expected-db (:db result)))))
|
||||
(testing "shouldn't have changes if called without :token or :token-symbol")
|
||||
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
|
||||
expected-db nil
|
||||
@@ -62,16 +60,16 @@
|
||||
(testing "should clean :collectible set"
|
||||
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks
|
||||
:collectible "some-collectible"}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
|
||||
:token-not-supported-in-receiver-networks? false}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-display-name token-symbol}}}}
|
||||
_ (reset! rf-db/app-db initial-db)
|
||||
result (dispatch [event-id {:token token}])]
|
||||
result (dispatch [event-id
|
||||
{:token token
|
||||
:network "network"}])]
|
||||
(is (match? expected-db (:db result)))
|
||||
(is (match? nil (get-in result [:db :wallet :ui :send :collectible])))))
|
||||
(testing "should set :token-not-supported-in-receiver-networks?"
|
||||
(let [initial-db {:wallet {:ui {:send {:receiver-networks []}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
|
||||
:token-not-supported-in-receiver-networks? true}}}}
|
||||
expected-db {:wallet {:ui {:send {:token-display-name token-symbol}}}}
|
||||
_ (reset! rf-db/app-db initial-db)
|
||||
result (dispatch [event-id {:token token}])]
|
||||
(is (match? expected-db (:db result)))))))
|
||||
@@ -211,47 +209,6 @@
|
||||
:other-props :value}}}})
|
||||
(is (match-strict? expected-db (:db (dispatch [event-id]))))))
|
||||
|
||||
(h/deftest-event :wallet/clean-disabled-from-networks
|
||||
[event-id dispatch]
|
||||
(let [expected-db {:wallet {:ui {:send {:other-props :value}}}}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:disabled-from-chain-ids [:optimism]
|
||||
:other-props :value}}}})
|
||||
(is (match-strict? expected-db (:db (dispatch [event-id]))))))
|
||||
|
||||
(h/deftest-event :wallet/clean-from-locked-amounts
|
||||
[event-id dispatch]
|
||||
(let [expected-db {:wallet {:ui {:send {:other-props :value}}}}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:from-locked-amounts "value"
|
||||
:other-props :value}}}})
|
||||
(is (match-strict? expected-db (:db (dispatch [event-id]))))))
|
||||
|
||||
(h/deftest-event :wallet/disable-from-networks
|
||||
[event-id dispatch]
|
||||
(let [expected-db {:wallet {:ui {:send {:disabled-from-chain-ids [:optimism]
|
||||
:other-props :value}}}}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:other-props :value}}}})
|
||||
(is (match? expected-db (:db (dispatch [event-id [:optimism]]))))))
|
||||
|
||||
(h/deftest-event :wallet/unlock-from-amount
|
||||
[event-id dispatch]
|
||||
(let [expected-db {:wallet {:ui {:send {:other-props :value
|
||||
:from-locked-amounts {}}}}}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:other-props :value
|
||||
:from-locked-amounts {:chain-id [1 10]}}}}})
|
||||
(is (match? expected-db (:db (dispatch [event-id :chain-id]))))))
|
||||
|
||||
(h/deftest-event :wallet/lock-from-amount
|
||||
[event-id dispatch]
|
||||
(let [expected-db {:wallet {:ui {:send {:other-props :value
|
||||
:from-locked-amounts {:amount 10}}}}}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:other-props :value}}}})
|
||||
(is (match? expected-db (:db (dispatch [event-id :amount 10]))))))
|
||||
|
||||
(h/deftest-event :wallet/clean-selected-token
|
||||
[event-id dispatch]
|
||||
(let [expected-db {:wallet {:ui {:send {:other-props :value}}}}]
|
||||
@@ -303,13 +260,8 @@
|
||||
(let [sender-network-values [{:chain-id 1 :total-amount (money/bignumber "100") :type :default}
|
||||
{:chain-id 10 :total-amount (money/bignumber "200") :type :default}]
|
||||
receiver-network-values [{:chain-id 1 :total-amount (money/bignumber "100") :type :loading}]
|
||||
disabled-from-chain-ids [:ethereum]
|
||||
sender-network-zero (send-utils/reset-network-amounts-to-zero
|
||||
{:network-amounts sender-network-values
|
||||
:disabled-chain-ids disabled-from-chain-ids})
|
||||
receiver-network-zero (send-utils/reset-network-amounts-to-zero
|
||||
{:network-amounts receiver-network-values
|
||||
:disabled-chain-ids []})]
|
||||
sender-network-zero (send-utils/reset-network-amounts-to-zero sender-network-values)
|
||||
receiver-network-zero (send-utils/reset-network-amounts-to-zero receiver-network-values)]
|
||||
(testing "if sender-network-value and receiver-network-value are not empty"
|
||||
(let [expected-db {:wallet {:ui {:send {:other-props :value
|
||||
:sender-network-values sender-network-zero
|
||||
@@ -347,7 +299,6 @@
|
||||
(h/deftest-event :wallet/select-send-address
|
||||
[event-id dispatch]
|
||||
(let [address "eth:arb1:0x01"
|
||||
prefix "eth:arb1:"
|
||||
to-address "0x01"
|
||||
recipient {:type :saved-address
|
||||
:label "0x01...23f"}
|
||||
@@ -355,27 +306,20 @@
|
||||
start-flow? false
|
||||
tx-type :tx/collectible-erc-721]
|
||||
(testing "testing when collectible balance is more than 1"
|
||||
(let [collectible (collectible-with-balance 2)
|
||||
testnet-enabled? false
|
||||
receiver-networks (network-utils/resolve-receiver-networks
|
||||
{:prefix prefix
|
||||
:testnet-enabled? testnet-enabled?})
|
||||
expected-result {:db {:wallet {:ui {:send {:other-props :value
|
||||
:recipient recipient
|
||||
:to-address to-address
|
||||
:address-prefix prefix
|
||||
:receiver-preferred-networks
|
||||
receiver-networks
|
||||
:receiver-networks receiver-networks
|
||||
:tx-type tx-type
|
||||
:collectible collectible}}}
|
||||
:profile/profile {:test-networks-enabled? false}}
|
||||
:fx [nil
|
||||
[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id :wallet-send-flow}]]]}]
|
||||
(let [collectible (collectible-with-balance 2)
|
||||
testnet-enabled? false
|
||||
expected-result {:db {:wallet {:ui {:send {:other-props :value
|
||||
:recipient recipient
|
||||
:to-address to-address
|
||||
:tx-type tx-type
|
||||
:collectible collectible}}}
|
||||
:profile/profile {:test-networks-enabled? false}}
|
||||
:fx [nil
|
||||
[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id :wallet-send-flow}]]]}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:other-props :value
|
||||
:tx-type tx-type
|
||||
@@ -388,27 +332,20 @@
|
||||
:stack-id stack-id
|
||||
:start-flow? start-flow?}])))))
|
||||
(testing "testing when collectible balance is 1"
|
||||
(let [collectible (collectible-with-balance 1)
|
||||
testnet-enabled? false
|
||||
receiver-networks (network-utils/resolve-receiver-networks
|
||||
{:prefix prefix
|
||||
:testnet-enabled? testnet-enabled?})
|
||||
expected-result {:db {:wallet {:ui {:send {:other-props :value
|
||||
:recipient recipient
|
||||
:to-address to-address
|
||||
:address-prefix prefix
|
||||
:receiver-preferred-networks
|
||||
receiver-networks
|
||||
:receiver-networks receiver-networks
|
||||
:tx-type tx-type
|
||||
:collectible collectible}}}
|
||||
:profile/profile {:test-networks-enabled? false}}
|
||||
:fx [[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]]
|
||||
[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id :wallet-send-flow}]]]}]
|
||||
(let [collectible (collectible-with-balance 1)
|
||||
testnet-enabled? false
|
||||
expected-result {:db {:wallet {:ui {:send {:other-props :value
|
||||
:recipient recipient
|
||||
:to-address to-address
|
||||
:tx-type tx-type
|
||||
:collectible collectible}}}
|
||||
:profile/profile {:test-networks-enabled? false}}
|
||||
:fx [[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]]
|
||||
[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
{:current-screen stack-id
|
||||
:start-flow? start-flow?
|
||||
:flow-id :wallet-send-flow}]]]}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:other-props :value
|
||||
:tx-type tx-type
|
||||
@@ -484,7 +421,6 @@
|
||||
:networks #{{:chain-id 421614}
|
||||
{:chain-id 11155420}
|
||||
{:chain-id 11155111}}}
|
||||
token-networks (:networks token)
|
||||
routes-available? (pos? (count chosen-route))
|
||||
sender-network-values [1 10]
|
||||
receiver-network-values [1 10]
|
||||
@@ -506,37 +442,18 @@
|
||||
to-network-values-for-ui (send-utils/network-values-for-ui to-network-amounts-by-chain)
|
||||
tx-type :tx/collectible-erc-1155
|
||||
from-network-values-for-ui (send-utils/network-values-for-ui from-network-amounts-by-chain)
|
||||
disabled-from-chain-ids [:421614]
|
||||
token-networks-ids (when token-networks (mapv #(:chain-id %) token-networks))
|
||||
sender-possible-chain-ids (mapv :chain-id sender-network-values)
|
||||
receiver-network-values (if routes-available?
|
||||
(send-utils/network-amounts
|
||||
{:network-values to-network-values-for-ui
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? true})
|
||||
(cond->
|
||||
(send-utils/reset-loading-network-amounts-to-zero
|
||||
receiver-network-values)
|
||||
|
||||
(not= tx-type :tx/bridge)
|
||||
send-utils/safe-add-type-edit))
|
||||
(send-utils/network-amounts to-network-values-for-ui)
|
||||
(send-utils/reset-loading-network-amounts-to-zero
|
||||
receiver-network-values))
|
||||
sender-network-values (if routes-available?
|
||||
(send-utils/network-amounts
|
||||
{:network-values
|
||||
(if (= tx-type :tx/bridge)
|
||||
(if (= tx-type :tx/bridge)
|
||||
from-network-values-for-ui
|
||||
(send-utils/add-zero-values-to-network-values
|
||||
from-network-values-for-ui
|
||||
(send-utils/add-zero-values-to-network-values
|
||||
from-network-values-for-ui
|
||||
sender-possible-chain-ids))
|
||||
:disabled-chain-ids disabled-from-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:from-locked-amounts {}
|
||||
:tx-type tx-type
|
||||
:receiver? false})
|
||||
sender-possible-chain-ids)))
|
||||
(send-utils/reset-loading-network-amounts-to-zero
|
||||
sender-network-values))
|
||||
expected-db {:wallet {:ui {:send
|
||||
@@ -544,7 +461,6 @@
|
||||
:suggested-routes suggested-routes-data
|
||||
:route chosen-route
|
||||
:token token
|
||||
:disabled-from-chain-ids disabled-from-chain-ids
|
||||
:suggested-routes-call-timestamp timestamp
|
||||
:collectible (collectible-with-balance 1)
|
||||
:token-display-name token-symbol
|
||||
@@ -559,8 +475,7 @@
|
||||
chosen-route
|
||||
sender-network-values
|
||||
receiver-network-values))
|
||||
:loading-suggested-routes? false
|
||||
:from-locked-amounts {}}}}}]
|
||||
:loading-suggested-routes? false}}}}]
|
||||
(reset! rf-db/app-db
|
||||
{:wallet {:ui {:send {:other-props :value
|
||||
:suggested-routes-call-timestamp timestamp
|
||||
@@ -570,17 +485,29 @@
|
||||
:receiver-networks receiver-networks
|
||||
:receiver-network-values [1 10]
|
||||
:sender-network-values [1 10]
|
||||
:tx-type tx-type
|
||||
:disabled-from-chain-ids disabled-from-chain-ids
|
||||
:from-locked-amounts {}}}}})
|
||||
:tx-type tx-type}}}})
|
||||
(is (match? expected-db (:db (dispatch [event-id suggested-routes timestamp]))))))
|
||||
|
||||
(h/deftest-event :wallet/select-from-account
|
||||
[event-id dispatch]
|
||||
(let [stack-id :screen/stack
|
||||
start-flow? false
|
||||
address "0x01"]
|
||||
address "0x01"
|
||||
network {:chain-id 1}]
|
||||
(testing "when tx-type is :tx/bridge and token-symbol is nil"
|
||||
(let [tx-type :tx/bridge
|
||||
expected-result {:db {:wallet {:ui {:send {:to-address address
|
||||
:tx-type tx-type}}}}
|
||||
:fx [[:dispatch [:dismiss-modal :screen/wallet.select-from]]
|
||||
[:dispatch [:wallet/switch-current-viewing-account address]]
|
||||
[:dispatch [:show-bottom-sheet {:content (m/pred fn?)}]]]}]
|
||||
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type}}}})
|
||||
(is (match? expected-result
|
||||
(dispatch [event-id
|
||||
{:address address
|
||||
:stack-id stack-id
|
||||
:start-flow? start-flow?}])))))
|
||||
(testing "when tx-type is :tx/bridge, network is selected and token-symbol is nil"
|
||||
(let [flow-id :wallet-bridge-flow
|
||||
tx-type :tx/bridge
|
||||
expected-result {:db {:wallet {:ui {:send {:to-address address
|
||||
@@ -596,6 +523,7 @@
|
||||
(dispatch [event-id
|
||||
{:address address
|
||||
:stack-id stack-id
|
||||
:network network
|
||||
:start-flow? start-flow?}])))))
|
||||
(testing "when tx-type is not :tx/bridge and token-symbol is nil"
|
||||
(let [flow-id :wallet-send-flow
|
||||
@@ -612,42 +540,22 @@
|
||||
(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
|
||||
tx-type :tx/bridge
|
||||
tokens [{:symbol "ETH"
|
||||
:chain-id 1
|
||||
:balances-per-chain {1 {:raw-balance (money/bignumber 100)}
|
||||
10 {:raw-balance (money/bignumber 200)}
|
||||
42161 {:raw-balance (money/bignumber 300)}}
|
||||
:decimals 2}]
|
||||
network-details #{{:chain-id 1}
|
||||
{:chain-id 10}
|
||||
{:chain-id 42161}}
|
||||
expected-result {:db {:wallet {:ui {:send {:to-address address
|
||||
:tx-type tx-type
|
||||
:token-symbol "ETH"
|
||||
:token (assoc (first tokens)
|
||||
:networks #{nil}
|
||||
:total-balance
|
||||
(money/bignumber 6))}}
|
||||
:accounts {address {:tokens tokens}}}}
|
||||
: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}]]]}]
|
||||
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type
|
||||
:token-symbol "ETH"}}
|
||||
:accounts {address {:tokens tokens}}}})
|
||||
(let [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
|
||||
:token-symbol "ETH"}}}})
|
||||
(is (match? expected-result
|
||||
(dispatch [event-id
|
||||
{:address address
|
||||
:stack-id stack-id
|
||||
:start-flow? start-flow?
|
||||
:netork-details network-details}])))))
|
||||
{:address address
|
||||
:stack-id stack-id
|
||||
:start-flow? start-flow?}])))))
|
||||
(testing "when tx-type is not :tx/bridge and token-symbol is not nil"
|
||||
(let [flow-id :wallet-send-flow
|
||||
tx-type :tx/collectible-erc-721
|
||||
@@ -680,5 +588,6 @@
|
||||
(dispatch [event-id
|
||||
{:address address
|
||||
:stack-id stack-id
|
||||
:network network
|
||||
:start-flow? start-flow?
|
||||
:netork-details network-details}])))))))
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
(-> db :wallet :ui :send :token some?))
|
||||
|
||||
(def steps
|
||||
[{:screen-id :screen/wallet.select-from
|
||||
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
|
||||
{:screen-id :screen/wallet.select-address
|
||||
[{:screen-id :screen/wallet.select-address
|
||||
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :recipient])))}
|
||||
{:screen-id :screen/wallet.select-asset
|
||||
:skip-step? (fn [db] (or (token-selected? db) (collectible-selected? db)))}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
(ns status-im.contexts.wallet.send.from.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.floating-button-page.view :as floating-button-page]
|
||||
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
|
||||
[status-im.contexts.wallet.send.from.style :as style]
|
||||
[status-im.setup.hot-reload :as hot-reload]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.money :as money]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- on-account-press
|
||||
@@ -15,27 +16,29 @@
|
||||
(rf/dispatch [:wallet/select-from-account
|
||||
{:address address
|
||||
:network-details network-details
|
||||
:stack-id :screen/wallet.select-from}]))
|
||||
|
||||
(defn- on-close
|
||||
[]
|
||||
(rf/dispatch [:wallet/clean-current-viewing-account]))
|
||||
:stack-id :screen/wallet.select-from
|
||||
:start-flow? true}]))
|
||||
|
||||
(defn- render-fn
|
||||
[item _ _ {:keys [network-details]}]
|
||||
(let [transformed-address (rf/sub [:wallet/account-address (:address item)
|
||||
(:network-preferences-names item)])]
|
||||
(let [has-balance (money/above-zero? (string/replace-first (:asset-pay-balance item) "<" ""))]
|
||||
[quo/account-item
|
||||
{:on-press #(on-account-press (:address item) network-details)
|
||||
{:type (if has-balance :tag :default)
|
||||
:on-press #(on-account-press (:address item) network-details)
|
||||
:state (if has-balance :default :disabled)
|
||||
:token-props {:symbol (:asset-pay-symbol item)
|
||||
:value (:asset-pay-balance item)}
|
||||
:account-props (assoc item
|
||||
:address transformed-address
|
||||
:address (:formatted-address item)
|
||||
:full-address? true)}]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [accounts (rf/sub [:wallet/accounts-with-current-asset])
|
||||
(let [token-symbol (rf/sub [:wallet/send-token-symbol])
|
||||
token (rf/sub [:wallet/token-by-symbol-from-first-available-account-with-balance
|
||||
token-symbol])
|
||||
accounts (rf/sub [:wallet/accounts-with-balances token])
|
||||
network-details (rf/sub [:wallet/network-details])]
|
||||
(hot-reload/use-safe-unmount on-close)
|
||||
[floating-button-page/view
|
||||
{:footer-container-padding 0
|
||||
:header [account-switcher/view
|
||||
|
||||
@@ -12,102 +12,92 @@
|
||||
(set! debounce/debounce-and-dispatch #())
|
||||
|
||||
(def sub-mocks
|
||||
{:profile/profile {:currency :usd}
|
||||
:wallet/selected-network-details [{:source 525
|
||||
:short-name "eth"
|
||||
:network-name :mainnet
|
||||
:chain-id 1
|
||||
:related-chain-id 5}]
|
||||
:wallet/current-viewing-account-address "0x1"
|
||||
:wallet/current-viewing-account {:path "m/44'/60'/0'/0/1"
|
||||
:emoji "💎"
|
||||
:key-uid "0x2f5ea39"
|
||||
:address "0x1"
|
||||
:wallet false
|
||||
:name "Account One"
|
||||
:type :generated
|
||||
:watch-only? false
|
||||
:chat false
|
||||
:test-preferred-chain-ids #{5 420 421613}
|
||||
:color :purple
|
||||
:hidden false
|
||||
:prod-preferred-chain-ids #{1 10 42161}
|
||||
:network-preferences-names #{:mainnet :arbitrum
|
||||
:optimism}
|
||||
:position 1
|
||||
:clock 1698945829328
|
||||
:created-at 1698928839000
|
||||
:operable :fully
|
||||
:mixedcase-address "0x7bcDfc75c431"
|
||||
:public-key "0x04371e2d9d66b82f056bc128064"
|
||||
:removed false}
|
||||
:wallet/current-viewing-account-color :purple
|
||||
:wallet/wallet-send-enough-assets? true
|
||||
:wallet/wallet-send-token {:symbol "ETH"
|
||||
:networks [{:source 879
|
||||
:short-name "eth"
|
||||
:network-name :mainnet
|
||||
:abbreviated-name
|
||||
"Eth."
|
||||
:full-name "Mainnet"
|
||||
:chain-id 1
|
||||
:related-chain-id 1
|
||||
:layer 1}]
|
||||
:balances-per-chain {1 {:raw-balance
|
||||
(money/bignumber
|
||||
"2500")
|
||||
:has-error false}}
|
||||
:total-balance 100
|
||||
:available-balance 100}
|
||||
:wallet/wallet-send-loading-suggested-routes? false
|
||||
:wallet/wallet-send-route [{:from {:chainid 1
|
||||
:native-currency-symbol "ETH"}
|
||||
:to {:chain-id 1
|
||||
:native-currency-symbol "ETH"}
|
||||
:gas-amount "23487"
|
||||
:gas-fees {:base-fee "32.325296406"
|
||||
:max-priority-fee-per-gas "0.011000001"
|
||||
:eip1559-enabled true}}]
|
||||
:wallet/wallet-send-suggested-routes nil
|
||||
:wallet/wallet-send-receiver-networks [1]
|
||||
:view-id :screen/wallet.send-input-amount
|
||||
:wallet/wallet-send-to-address "0x04371e2d9d66b82f056bc128064"
|
||||
:profile/currency-symbol "$"
|
||||
:profile/currency :usd
|
||||
:wallet/token-by-symbol {:symbol "ETH"
|
||||
:total-balance 100
|
||||
:available-balance 100
|
||||
:balances-per-chain {1 {:raw-balance
|
||||
(money/bignumber
|
||||
"2500")
|
||||
:has-error false}}}
|
||||
:wallet/wallet-send-disabled-from-chain-ids []
|
||||
:wallet/wallet-send-from-locked-amounts {}
|
||||
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
|
||||
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}
|
||||
:wallet/wallet-send-sender-network-values nil
|
||||
:wallet/wallet-send-receiver-network-values nil
|
||||
:wallet/wallet-send-network-links nil
|
||||
:wallet/wallet-send-receiver-preferred-networks [1]
|
||||
:wallet/wallet-send-enabled-networks [{:source 879
|
||||
:short-name "eth"
|
||||
:network-name :mainnet
|
||||
:abbreviated-name "Eth."
|
||||
:full-name "Mainnet"
|
||||
:chain-id 1
|
||||
:related-chain-id 1
|
||||
:layer 1}]
|
||||
:wallet/wallet-send-enabled-from-chain-ids [1]
|
||||
:wallet/send-amount nil
|
||||
:wallet/wallet-send-tx-type :tx/send
|
||||
:wallet/wallet-send-fee-fiat-formatted "$5,00"
|
||||
:wallet/sending-collectible? false
|
||||
:wallet/send-total-amount-formatted "250 ETH"
|
||||
:wallet/total-amount (money/bignumber "250")
|
||||
:wallet/prices-per-token {:ETH {:usd 10}}
|
||||
:wallet/bridge-to-network-details nil
|
||||
:wallet/send-amount-fixed ""
|
||||
:wallet/send-display-token-decimals 5})
|
||||
{:profile/profile {:currency :usd}
|
||||
:wallet/selected-network-details [{:source 525
|
||||
:short-name "eth"
|
||||
:network-name :mainnet
|
||||
:chain-id 1
|
||||
:related-chain-id 5}]
|
||||
:wallet/current-viewing-account-address "0x1"
|
||||
:wallet/current-viewing-account {:path "m/44'/60'/0'/0/1"
|
||||
:emoji "💎"
|
||||
:key-uid "0x2f5ea39"
|
||||
:address "0x1"
|
||||
:wallet false
|
||||
:name "Account One"
|
||||
:type :generated
|
||||
:watch-only? false
|
||||
:chat false
|
||||
:test-preferred-chain-ids #{5 420 421613}
|
||||
:color :purple
|
||||
:hidden false
|
||||
:prod-preferred-chain-ids #{1 10 42161}
|
||||
:network-preferences-names #{:mainnet :arbitrum
|
||||
:optimism}
|
||||
:position 1
|
||||
:clock 1698945829328
|
||||
:created-at 1698928839000
|
||||
:operable :fully
|
||||
:mixedcase-address "0x7bcDfc75c431"
|
||||
:public-key "0x04371e2d9d66b82f056bc128064"
|
||||
:removed false}
|
||||
:wallet/current-viewing-account-color :purple
|
||||
:wallet/wallet-send-enough-assets? true
|
||||
:wallet/wallet-send-token {:symbol "ETH"
|
||||
:networks [{:source 879
|
||||
:short-name "eth"
|
||||
:network-name :mainnet
|
||||
:abbreviated-name
|
||||
"Eth."
|
||||
:full-name "Mainnet"
|
||||
:chain-id 1
|
||||
:related-chain-id 1
|
||||
:layer 1}]
|
||||
:balances-per-chain {1 {:raw-balance
|
||||
(money/bignumber
|
||||
"2500")
|
||||
:has-error false}}
|
||||
:total-balance 100
|
||||
:available-balance 100}
|
||||
:wallet/wallet-send-loading-suggested-routes? false
|
||||
:wallet/wallet-send-route [{:from {:chainid 1
|
||||
:native-currency-symbol "ETH"}
|
||||
:to {:chain-id 1
|
||||
:native-currency-symbol "ETH"}
|
||||
:gas-amount "23487"
|
||||
:gas-fees {:base-fee "32.325296406"
|
||||
:max-priority-fee-per-gas "0.011000001"
|
||||
:eip1559-enabled true}}]
|
||||
:wallet/wallet-send-suggested-routes nil
|
||||
:wallet/wallet-send-receiver-networks [1]
|
||||
:view-id :screen/wallet.send-input-amount
|
||||
:wallet/wallet-send-to-address "0x04371e2d9d66b82f056bc128064"
|
||||
:profile/currency-symbol "$"
|
||||
:profile/currency :usd
|
||||
:wallet/token-by-symbol {:symbol "ETH"
|
||||
:total-balance 100
|
||||
:available-balance 100
|
||||
:balances-per-chain {1 {:raw-balance
|
||||
(money/bignumber
|
||||
"2500")
|
||||
:has-error false}}}
|
||||
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
|
||||
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}
|
||||
:wallet/wallet-send-sender-network-values nil
|
||||
:wallet/wallet-send-receiver-network-values nil
|
||||
:wallet/wallet-send-network-links nil
|
||||
:wallet/send-amount nil
|
||||
: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}}
|
||||
:wallet/bridge-to-network-details nil
|
||||
:wallet/send-amount-fixed ""
|
||||
:wallet/send-display-token-decimals 5
|
||||
:wallet/send-network {:chain-id 1}})
|
||||
|
||||
(h/describe "Send > input amount screen"
|
||||
(h/setup-restorable-re-frame)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im.contexts.wallet.send.input-amount.estimated-fees
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.not-implemented :as not-implemented]
|
||||
@@ -30,38 +31,40 @@
|
||||
(let [amount (rf/sub [:wallet/send-total-amount-formatted])
|
||||
tx-type (rf/sub [:wallet/wallet-send-tx-type])
|
||||
{:keys [full-name]} (rf/sub [:wallet/bridge-to-network-details])]
|
||||
[quo/data-item
|
||||
(cond-> {:container-style style/amount-data-item
|
||||
:status (if loading-routes? :loading :default)
|
||||
:size :small
|
||||
:title (i18n/label :t/recipient-gets)
|
||||
:subtitle amount}
|
||||
|
||||
(= tx-type :tx/bridge)
|
||||
(assoc
|
||||
:title-icon :i/info
|
||||
:title (i18n/label :t/bridged-to
|
||||
{:network full-name})
|
||||
:on-press show-bonder-fee-info))]))
|
||||
(when (= tx-type :tx/bridge)
|
||||
[quo/data-item
|
||||
{:container-style style/amount-data-item
|
||||
:status (if loading-routes? :loading :default)
|
||||
:size :small
|
||||
:title-icon :i/info
|
||||
:title (i18n/label :t/bridged-to
|
||||
{:network full-name})
|
||||
:subtitle amount
|
||||
:on-press show-bonder-fee-info}])))
|
||||
|
||||
(defn view
|
||||
[{:keys [loading-routes? fees]}]
|
||||
[rn/view {:style style/estimated-fees-container}
|
||||
(when (ff/enabled? ::ff/wallet.advanced-sending)
|
||||
[rn/view {:style style/estimated-fees-content-container}
|
||||
[quo/button
|
||||
{:icon-only? true
|
||||
:type :outline
|
||||
:size 32
|
||||
:inner-style {:opacity 1}
|
||||
:accessibility-label :advanced-button
|
||||
:disabled? loading-routes?
|
||||
:on-press not-implemented/alert}
|
||||
:i/advanced]])
|
||||
[quo/data-item
|
||||
{:container-style style/fees-data-item
|
||||
:status (if loading-routes? :loading :default)
|
||||
:size :small
|
||||
:title (i18n/label :t/fees)
|
||||
:subtitle fees}]
|
||||
[received-amount {:loading-routes? loading-routes?}]])
|
||||
[{:keys [not-enough-asset? loading-routes? fees]}]
|
||||
(let [theme (quo.theme/use-theme)]
|
||||
[rn/view {:style style/estimated-fees-container}
|
||||
(when (ff/enabled? ::ff/wallet.advanced-sending)
|
||||
[rn/view {:style style/estimated-fees-content-container}
|
||||
[quo/button
|
||||
{:icon-only? true
|
||||
:type :outline
|
||||
:size 32
|
||||
:inner-style {:opacity 1}
|
||||
:accessibility-label :advanced-button
|
||||
:disabled? loading-routes?
|
||||
:on-press not-implemented/alert}
|
||||
:i/advanced]])
|
||||
[quo/data-item
|
||||
(cond-> {:container-style style/fees-data-item
|
||||
:status (if loading-routes? :loading :default)
|
||||
:size :small
|
||||
:title (i18n/label :t/max-fees)
|
||||
:subtitle fees}
|
||||
not-enough-asset? (assoc :subtitle-color
|
||||
(colors/theme-colors colors/danger-50
|
||||
colors/danger-60
|
||||
theme)))]
|
||||
[received-amount {:loading-routes? loading-routes?}]]))
|
||||
|
||||
@@ -25,10 +25,9 @@
|
||||
:height 40})
|
||||
|
||||
(def fees-data-item
|
||||
{:flex 1
|
||||
:height 40
|
||||
:margin-horizontal 16
|
||||
:background-color :transparent})
|
||||
{:flex 1
|
||||
:height 40
|
||||
:background-color :transparent})
|
||||
|
||||
(def amount-data-item
|
||||
{:flex 1
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
@@ -15,29 +14,18 @@
|
||||
[status-im.contexts.wallet.send.input-amount.style :as style]
|
||||
[status-im.contexts.wallet.send.routes.view :as routes]
|
||||
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
|
||||
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
|
||||
[status-im.setup.hot-reload :as hot-reload]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.money :as money]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- every-network-value-is-zero?
|
||||
[sender-network-values]
|
||||
(every? (fn [{:keys [total-amount]}]
|
||||
(and
|
||||
total-amount
|
||||
(money/bignumber? total-amount)
|
||||
(money/equal-to total-amount
|
||||
(money/bignumber "0"))))
|
||||
sender-network-values))
|
||||
|
||||
(defn select-asset-bottom-sheet
|
||||
[clear-input!]
|
||||
(let [{preselected-token-symbol :symbol} (rf/sub [:wallet/wallet-send-token])]
|
||||
[:<> ;; Need to be a `:<>` to keep `asset-list` scrollable.
|
||||
[quo/drawer-top
|
||||
{:title (i18n/label :t/select-asset)
|
||||
{:title (i18n/label :t/select-token)
|
||||
:container-style {:padding-bottom 8}}]
|
||||
[asset-list/view
|
||||
{:content-container-style {:padding-horizontal 8
|
||||
@@ -47,37 +35,6 @@
|
||||
(rf/dispatch [:wallet/edit-token-to-send token])
|
||||
(clear-input!))}]]))
|
||||
|
||||
(defn- token-not-available
|
||||
[token-symbol receiver-networks token-networks]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
add-token-networks (fn []
|
||||
(let [chain-ids (concat receiver-networks
|
||||
(mapv #(:chain-id %) token-networks))]
|
||||
(rf/dispatch [:wallet/update-receiver-networks chain-ids])))]
|
||||
[rn/view {:style (style/token-not-available-container theme)}
|
||||
[rn/view
|
||||
[quo/icon :i/alert
|
||||
{:size 16
|
||||
:color (colors/resolve-color :danger theme)}]]
|
||||
[rn/view {:style style/token-not-available-content-container}
|
||||
[quo/text
|
||||
{:style (style/token-not-available-text theme)
|
||||
:size :paragraph-2}
|
||||
(i18n/label :t/token-not-available-on-receiver-networks {:token-symbol token-symbol})]
|
||||
[quo/button
|
||||
{:size 24
|
||||
:customization-color (colors/resolve-color :danger theme)
|
||||
:on-press add-token-networks}
|
||||
(i18n/label :t/add-networks-token-can-be-sent-to {:token-symbol token-symbol})]]]))
|
||||
|
||||
(defn- show-unpreferred-networks-alert
|
||||
[on-confirm]
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[unpreferred-networks-alert/view
|
||||
{:on-confirm on-confirm}])}]))
|
||||
|
||||
(defn- no-routes-found
|
||||
[]
|
||||
[rn/view {:style style/no-routes-found-container}
|
||||
@@ -118,8 +75,7 @@
|
||||
(rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])))
|
||||
|
||||
(defn- insufficient-asset-amount?
|
||||
[{:keys [token-symbol owned-eth-token input-state no-routes-found? limit-exceeded?
|
||||
sender-network-values enough-assets?]}]
|
||||
[{:keys [token-symbol owned-eth-token input-state limit-exceeded? 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
|
||||
@@ -128,9 +84,9 @@
|
||||
exceeded-input? (if eth-selected?
|
||||
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?)))))
|
||||
(or exceeded-input?
|
||||
limit-exceeded?
|
||||
(false? enough-assets?))))
|
||||
|
||||
(defn view
|
||||
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
|
||||
@@ -142,124 +98,103 @@
|
||||
button-one-props :button-one-props
|
||||
current-screen-id :current-screen-id
|
||||
initial-crypto-currency? :initial-crypto-currency?
|
||||
enabled-from-chain-ids :enabled-from-chain-ids
|
||||
from-enabled-networks :from-enabled-networks
|
||||
:or {initial-crypto-currency? true}}]
|
||||
(let [view-id (rf/sub [:view-id])
|
||||
active-screen? (= view-id current-screen-id)
|
||||
bottom (safe-area/get-bottom)
|
||||
[crypto-currency?
|
||||
set-crypto-currency] (rn/use-state initial-crypto-currency?)
|
||||
handle-on-confirm (fn [amount]
|
||||
(rf/dispatch [:wallet/set-token-amount-to-send
|
||||
{:amount amount
|
||||
:stack-id current-screen-id}]))
|
||||
{fiat-currency :currency} (rf/sub [:profile/profile])
|
||||
{token-symbol :symbol
|
||||
token-networks :networks
|
||||
:as token} (rf/sub [:wallet/wallet-send-token])
|
||||
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
|
||||
(let [view-id (rf/sub [:view-id])
|
||||
{fiat-currency :currency} (rf/sub [:profile/profile])
|
||||
currency (rf/sub [:profile/currency])
|
||||
{token-symbol :symbol
|
||||
:as token} (rf/sub [:wallet/wallet-send-token])
|
||||
network (rf/sub [:wallet/send-network])
|
||||
{:keys [total-balance]
|
||||
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
|
||||
(str token-symbol)
|
||||
enabled-from-chain-ids])
|
||||
token-balance (or default-limit-crypto total-balance)
|
||||
prices-per-token (rf/sub [:wallet/prices-per-token])
|
||||
usd-conversion-rate (utils/token-usd-price token prices-per-token)
|
||||
currency (rf/sub [:profile/currency])
|
||||
token-decimals (rf/sub [:wallet/send-display-token-decimals])
|
||||
|
||||
conversion-rate (utils/token-price-by-symbol prices-per-token
|
||||
token-symbol
|
||||
currency)
|
||||
[input-state set-input-state] (rn/use-state controlled-input/init-state)
|
||||
clear-input! #(set-input-state controlled-input/delete-all)
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
|
||||
route (rf/sub [:wallet/wallet-send-route])
|
||||
on-confirm (or default-on-confirm handle-on-confirm)
|
||||
max-limit (if crypto-currency?
|
||||
(utils/cut-crypto-decimals-to-fit-usd-cents
|
||||
token-balance
|
||||
usd-conversion-rate)
|
||||
(utils/cut-fiat-balance-to-two-decimals
|
||||
(money/crypto->fiat token-balance conversion-rate)))
|
||||
input-value (controlled-input/input-value input-state)
|
||||
valid-input? (not (or (controlled-input/empty-value? input-state)
|
||||
(controlled-input/input-error input-state)))
|
||||
amount-in-crypto (if crypto-currency?
|
||||
input-value
|
||||
(rf/sub [:wallet/send-amount-fixed
|
||||
(/ input-value conversion-rate)]))
|
||||
show-select-asset-sheet #(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[select-asset-bottom-sheet
|
||||
clear-input!])}])
|
||||
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
|
||||
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
|
||||
tx-type (rf/sub [:wallet/wallet-send-tx-type])
|
||||
unsupported-token-in-receiver? (and (not= tx-type :tx/bridge)
|
||||
(->> receiver-network-values
|
||||
(remove #(= (:type %) :add))
|
||||
(every? #(= (:type %) :not-available))))
|
||||
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
|
||||
routes (when suggested-routes
|
||||
(or (:best suggested-routes) []))
|
||||
no-routes-found? (and
|
||||
(every-network-value-is-zero? sender-network-values)
|
||||
(some? routes)
|
||||
(not loading-routes?)
|
||||
(not unsupported-token-in-receiver?))
|
||||
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
|
||||
receiver-preferred-networks (rf/sub [:wallet/wallet-send-receiver-preferred-networks])
|
||||
receiver-preferred-network? (set receiver-preferred-networks)
|
||||
sending-to-unpreferred-networks? (some (comp not receiver-preferred-network?)
|
||||
receiver-networks)
|
||||
input-error (controlled-input/input-error input-state)
|
||||
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
|
||||
current-address (rf/sub [:wallet/current-viewing-account-address])
|
||||
current-color (rf/sub [:wallet/current-viewing-account-color])
|
||||
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
|
||||
owned-eth-token (rf/sub [:wallet/token-by-symbol
|
||||
(string/upper-case constants/mainnet-short-name)
|
||||
enabled-from-chain-ids])
|
||||
not-enough-asset? (insufficient-asset-amount?
|
||||
{:enough-assets? enough-assets?
|
||||
:token-symbol token-symbol
|
||||
:owned-eth-token owned-eth-token
|
||||
:input-state input-state
|
||||
:no-routes-found? no-routes-found?
|
||||
:limit-exceeded? limit-exceeded?
|
||||
:sender-network-values sender-network-values})
|
||||
should-try-again? (and (not limit-exceeded?)
|
||||
no-routes-found?
|
||||
(not not-enough-asset?))
|
||||
show-no-routes? (and (or no-routes-found? limit-exceeded?)
|
||||
(not-empty sender-network-values)
|
||||
(not not-enough-asset?))
|
||||
confirm-disabled? (or (nil? route)
|
||||
(empty? route)
|
||||
(not valid-input?))
|
||||
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
|
||||
(get-fee-formatted route))
|
||||
request-fetch-routes (fn [bounce-duration-ms]
|
||||
(fetch-routes
|
||||
{:amount amount-in-crypto
|
||||
:valid-input? valid-input?
|
||||
:bounce-duration-ms bounce-duration-ms
|
||||
:token token
|
||||
:reset-amounts-to-zero? (and limit-exceeded?
|
||||
(some? routes))}))
|
||||
swap-between-fiat-and-crypto (fn []
|
||||
(if crypto-currency?
|
||||
(set-input-state
|
||||
#(controlled-input/->fiat % conversion-rate))
|
||||
(set-input-state
|
||||
#(controlled-input/->crypto % conversion-rate)))
|
||||
(set-crypto-currency (not crypto-currency?)))]
|
||||
(rn/use-unmount
|
||||
#(rf/dispatch [:wallet/clean-up-transaction-flow]))
|
||||
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
|
||||
(str token-symbol)
|
||||
[(:chain-id network)]])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
|
||||
route (rf/sub [:wallet/wallet-send-route])
|
||||
current-address (rf/sub [:wallet/current-viewing-account-address])
|
||||
current-color (rf/sub [:wallet/current-viewing-account-color])
|
||||
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
|
||||
owned-eth-token (rf/sub [:wallet/token-by-symbol
|
||||
(string/upper-case constants/mainnet-short-name)
|
||||
[(:chain-id network)]])
|
||||
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
|
||||
tx-type (rf/sub [:wallet/wallet-send-tx-type])
|
||||
token-decimals (rf/sub [:wallet/send-display-token-decimals])
|
||||
prices-per-token (rf/sub [:wallet/prices-per-token])
|
||||
[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)
|
||||
usd-conversion-rate (utils/token-usd-price token prices-per-token)
|
||||
conversion-rate (utils/token-price-by-symbol prices-per-token
|
||||
token-symbol
|
||||
currency)
|
||||
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
|
||||
:limit-exceeded? limit-exceeded?})
|
||||
should-try-again? (and (not limit-exceeded?)
|
||||
no-routes-found?
|
||||
(not not-enough-asset?))
|
||||
show-no-routes? (and (or no-routes-found? limit-exceeded?)
|
||||
(not not-enough-asset?))
|
||||
confirm-disabled? (or (nil? route)
|
||||
(empty? route)
|
||||
(not valid-input?))
|
||||
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
|
||||
(get-fee-formatted route))
|
||||
handle-on-confirm (fn [amount]
|
||||
(rf/dispatch [:wallet/set-token-amount-to-send
|
||||
{:amount amount
|
||||
:stack-id current-screen-id}]))
|
||||
on-confirm (or default-on-confirm handle-on-confirm)
|
||||
show-select-asset-sheet #(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[select-asset-bottom-sheet
|
||||
clear-input!])}])
|
||||
request-fetch-routes (fn [bounce-duration-ms]
|
||||
(fetch-routes
|
||||
{:amount amount-in-crypto
|
||||
:valid-input? valid-input?
|
||||
:bounce-duration-ms bounce-duration-ms
|
||||
:token token
|
||||
:reset-amounts-to-zero? (and limit-exceeded?
|
||||
(some? route))}))
|
||||
swap-between-fiat-and-crypto (fn []
|
||||
(if crypto-currency?
|
||||
(set-input-state
|
||||
#(controlled-input/->fiat % conversion-rate))
|
||||
(set-input-state
|
||||
#(controlled-input/->crypto % conversion-rate)))
|
||||
(set-crypto-currency (not crypto-currency?)))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when active-screen?
|
||||
@@ -284,13 +219,13 @@
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(clear-input!)
|
||||
(rf/dispatch [:wallet/stop-and-clean-suggested-routes])
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks]))
|
||||
(rf/dispatch [:wallet/stop-and-clean-suggested-routes]))
|
||||
[current-address])
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(request-fetch-routes 0))
|
||||
[send-from-locked-amounts])
|
||||
(when active-screen?
|
||||
(request-fetch-routes 2000)))
|
||||
[amount-in-crypto valid-input?])
|
||||
[rn/view
|
||||
{:style style/screen
|
||||
:accessibility-label (str "container"
|
||||
@@ -318,7 +253,7 @@
|
||||
conversion-rate)
|
||||
conversion-rate))
|
||||
:hint-component [quo/network-tags
|
||||
{:networks (seq from-enabled-networks)
|
||||
{:networks (seq [network])
|
||||
:title (i18n/label
|
||||
:t/send-limit
|
||||
{:limit (if crypto-currency?
|
||||
@@ -330,24 +265,16 @@
|
||||
(controlled-input/upper-limit-bn
|
||||
input-state)))})
|
||||
:status (when (controlled-input/input-error input-state) :error)}]}]
|
||||
[routes/view
|
||||
{:token token-by-symbol
|
||||
:input-value input-value
|
||||
:valid-input? valid-input?
|
||||
:token-not-supported-in-receiver-networks? unsupported-token-in-receiver?
|
||||
:current-screen-id current-screen-id
|
||||
:request-fetch-routes request-fetch-routes}]
|
||||
(when (and (not loading-routes?)
|
||||
sender-network-values
|
||||
unsupported-token-in-receiver?)
|
||||
[token-not-available token-symbol receiver-networks token-networks])
|
||||
(if (= tx-type :tx/bridge)
|
||||
[routes/view]
|
||||
[rn/view {:style {:flex 1}}])
|
||||
(when not-enough-asset?
|
||||
[not-enough-asset])
|
||||
(when (or (and (not no-routes-found?) (or loading-routes? route))
|
||||
not-enough-asset?)
|
||||
(when (or loading-routes? route)
|
||||
[estimated-fees/view
|
||||
{:loading-routes? loading-routes?
|
||||
:fees fee-formatted}])
|
||||
{:not-enough-asset? not-enough-asset?
|
||||
:loading-routes? loading-routes?
|
||||
:fees fee-formatted}])
|
||||
(when show-no-routes?
|
||||
[no-routes-found])
|
||||
[quo/bottom-actions
|
||||
@@ -361,14 +288,10 @@
|
||||
loading-routes?
|
||||
(and (not should-try-again?)
|
||||
confirm-disabled?))
|
||||
:on-press (cond
|
||||
should-try-again?
|
||||
:on-press (if should-try-again?
|
||||
#(rf/dispatch [:wallet/start-get-suggested-routes
|
||||
{:amount amount-in-crypto
|
||||
:updated-token token-by-symbol}])
|
||||
sending-to-unpreferred-networks?
|
||||
#(show-unpreferred-networks-alert on-confirm)
|
||||
:else
|
||||
#(on-confirm amount-in-crypto))
|
||||
:customization-color current-color}
|
||||
(when should-try-again?
|
||||
|
||||
@@ -34,19 +34,3 @@
|
||||
:top margin-top}
|
||||
inverted?
|
||||
(assoc :transform [{:scaleY -1}])))
|
||||
|
||||
(def disclaimer
|
||||
{:margin-horizontal 20
|
||||
:margin-top 20
|
||||
:margin-bottom 8})
|
||||
|
||||
(def input-container
|
||||
{:margin-top 8
|
||||
:margin-bottom 12})
|
||||
|
||||
(defn keyboard-container
|
||||
[bottom]
|
||||
{:padding-bottom bottom})
|
||||
|
||||
(def error-box
|
||||
{:margin-horizontal 20})
|
||||
|
||||
@@ -1,122 +1,39 @@
|
||||
(ns status-im.contexts.wallet.send.routes.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.contexts.wallet.common.utils :as common-utils]
|
||||
[status-im.contexts.wallet.common.utils.networks :as network-utils]
|
||||
[status-im.contexts.wallet.send.routes.style :as style]
|
||||
[status-im.contexts.wallet.send.utils :as send-utils]
|
||||
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
||||
(def row-height 44)
|
||||
(def space-between-rows 11)
|
||||
(def network-link-linear-height 10)
|
||||
(def network-link-1x-height 56)
|
||||
(def network-link-2x-height 111)
|
||||
|
||||
|
||||
(defn- open-preferences
|
||||
[]
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content
|
||||
(fn []
|
||||
(let [receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
|
||||
receiver-preferred-networks (rf/sub
|
||||
[:wallet/wallet-send-receiver-preferred-networks])
|
||||
{token-symbol :symbol
|
||||
token-networks :supported-networks} (rf/sub [:wallet/wallet-send-token])
|
||||
token-chain-ids-set (set (mapv #(:chain-id %) token-networks))
|
||||
[selected-receiver-networks
|
||||
set-selected-receiver-networks] (rn/use-state receiver-networks)
|
||||
receiver-preferred-networks-set (set receiver-preferred-networks)
|
||||
receiver-selected-preferred-networks (filter #(contains?
|
||||
receiver-preferred-networks-set
|
||||
%)
|
||||
selected-receiver-networks)
|
||||
receiver-selected-non-preferred-networks (filter #(not (contains?
|
||||
receiver-preferred-networks-set
|
||||
%))
|
||||
selected-receiver-networks)
|
||||
not-available-preferred-networks (filter (fn [preferred-chain-id]
|
||||
(not (contains? token-chain-ids-set
|
||||
preferred-chain-id)))
|
||||
receiver-selected-preferred-networks)
|
||||
not-available-non-preferred-networks (filter (fn [preferred-chain-id]
|
||||
(not (contains?
|
||||
token-chain-ids-set
|
||||
preferred-chain-id)))
|
||||
receiver-selected-non-preferred-networks)
|
||||
first-section-warning-label (when (not-empty not-available-preferred-networks)
|
||||
(i18n/label
|
||||
:t/token-not-available-on-networks
|
||||
{:token-symbol token-symbol
|
||||
:networks
|
||||
(network-utils/network-ids->formatted-text
|
||||
not-available-preferred-networks)}))
|
||||
second-section-warning-label (when (not-empty
|
||||
not-available-non-preferred-networks)
|
||||
(i18n/label
|
||||
:t/token-not-available-on-networks
|
||||
{:token-symbol token-symbol
|
||||
:networks
|
||||
(network-utils/network-ids->formatted-text
|
||||
not-available-non-preferred-networks)}))]
|
||||
[network-preferences/view
|
||||
{:title (i18n/label :t/edit-receiver-networks)
|
||||
:first-section-label (i18n/label :t/preferred-by-receiver)
|
||||
:second-section-label (i18n/label :t/not-preferred-by-receiver)
|
||||
:selected-networks (set (map network-utils/id->network
|
||||
receiver-networks))
|
||||
:receiver-preferred-networks receiver-preferred-networks
|
||||
:button-label (i18n/label :t/apply-changes)
|
||||
:first-section-warning-label first-section-warning-label
|
||||
:second-section-warning-label second-section-warning-label
|
||||
:on-change #(set-selected-receiver-networks %)
|
||||
:on-save (fn [chain-ids]
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch [:wallet/update-receiver-networks
|
||||
chain-ids]))}]))}]))
|
||||
|
||||
(defn render-network-values
|
||||
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
|
||||
token-not-supported-in-receiver-networks?]}]
|
||||
[{:keys [network-values token-symbol token-decimals receiver?]}]
|
||||
[rn/view
|
||||
(doall
|
||||
(map-indexed (fn [index
|
||||
{chain-id :chain-id
|
||||
network-value-type :type
|
||||
total-amount :total-amount}]
|
||||
(let [status (cond (and (= network-value-type :not-available)
|
||||
loading-routes?
|
||||
token-not-supported-in-receiver-networks?)
|
||||
:loading
|
||||
(= network-value-type :not-available)
|
||||
:disabled
|
||||
:else network-value-type)
|
||||
amount-formatted (-> (rf/sub [:wallet/send-amount-fixed total-amount])
|
||||
(let [amount-formatted (-> total-amount
|
||||
(common-utils/sanitized-token-amount-to-display
|
||||
token-decimals)
|
||||
(str " " token-symbol))]
|
||||
[rn/view
|
||||
{:key (str (if receiver? "to" "from") "-" chain-id)
|
||||
:style {:margin-top (if (pos? index) 11 7.5)}}
|
||||
[quo/network-bridge
|
||||
{:amount (if (= network-value-type :not-available)
|
||||
(i18n/label :t/not-available)
|
||||
amount-formatted)
|
||||
:network (network-utils/id->network chain-id)
|
||||
:status status
|
||||
:on-press #(when (not loading-routes?)
|
||||
(cond
|
||||
(= network-value-type :edit)
|
||||
(open-preferences)
|
||||
on-press (on-press chain-id total-amount)))
|
||||
:on-long-press #(when (and (not loading-routes?) (not= status :disabled))
|
||||
(cond
|
||||
(= network-value-type :add)
|
||||
(open-preferences)
|
||||
on-long-press (on-long-press chain-id total-amount)))}]]))
|
||||
{:amount amount-formatted
|
||||
:network (network-utils/id->network chain-id)
|
||||
:status network-value-type}]]))
|
||||
network-values))])
|
||||
|
||||
(defn render-network-links
|
||||
@@ -156,59 +73,14 @@
|
||||
:destination destination}]]]))
|
||||
network-links)])
|
||||
|
||||
(defn disable-chain
|
||||
[chain-id disabled-from-chain-ids token-available-networks-for-suggested-routes]
|
||||
(let [disabled-chain-ids
|
||||
(if (contains? (set
|
||||
disabled-from-chain-ids)
|
||||
chain-id)
|
||||
(vec (remove #(= % chain-id)
|
||||
disabled-from-chain-ids))
|
||||
(conj disabled-from-chain-ids
|
||||
chain-id))
|
||||
re-enabling-chain?
|
||||
(< (count disabled-chain-ids)
|
||||
(count disabled-from-chain-ids))]
|
||||
(if (or re-enabling-chain?
|
||||
(> (count token-available-networks-for-suggested-routes) 1))
|
||||
(rf/dispatch [:wallet/disable-from-networks
|
||||
disabled-chain-ids])
|
||||
(rf/dispatch [:toasts/upsert
|
||||
{:id :disable-chain-error
|
||||
:type :negative
|
||||
:text (i18n/label :t/at-least-one-network-must-be-activated)}]))))
|
||||
|
||||
(defn view
|
||||
[{:keys [token theme valid-input? request-fetch-routes on-press-to-network current-screen-id
|
||||
token-not-supported-in-receiver-networks? input-value]}]
|
||||
(let [token-symbol (:symbol token)
|
||||
nav-current-screen-id (rf/sub [:view-id])
|
||||
active-screen? (= nav-current-screen-id current-screen-id)
|
||||
loading-routes? (rf/sub
|
||||
[:wallet/wallet-send-loading-suggested-routes?])
|
||||
sender-network-values (rf/sub
|
||||
[:wallet/wallet-send-sender-network-values])
|
||||
receiver-network-values (rf/sub
|
||||
[:wallet/wallet-send-receiver-network-values])
|
||||
network-links (rf/sub [:wallet/wallet-send-network-links])
|
||||
disabled-from-chain-ids (rf/sub
|
||||
[:wallet/wallet-send-disabled-from-chain-ids])
|
||||
{token-balances-per-chain :balances-per-chain} (rf/sub [:wallet/wallet-send-token])
|
||||
token-available-networks-for-suggested-routes
|
||||
(send-utils/token-available-networks-for-suggested-routes
|
||||
{:balances-per-chain token-balances-per-chain
|
||||
:disabled-chain-ids disabled-from-chain-ids})
|
||||
show-routes? (not-empty sender-network-values)]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when (and active-screen?
|
||||
(> (count token-available-networks-for-suggested-routes) 0))
|
||||
(request-fetch-routes 2000)))
|
||||
[input-value valid-input?])
|
||||
(rn/use-effect
|
||||
#(when (and active-screen? (> (count token-available-networks-for-suggested-routes) 0))
|
||||
(request-fetch-routes 0))
|
||||
[disabled-from-chain-ids])
|
||||
[]
|
||||
(let [token-symbol (rf/sub [:wallet/wallet-send-token-symbol])
|
||||
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])
|
||||
token-decimals (rf/sub [:wallet/send-display-token-decimals])
|
||||
show-routes? (not-empty sender-network-values)]
|
||||
[rn/scroll-view {:content-container-style style/routes-container}
|
||||
(when show-routes?
|
||||
[rn/view {:style style/routes-header-container}
|
||||
@@ -220,27 +92,16 @@
|
||||
:container-style style/section-label-right}]])
|
||||
[rn/view {:style style/routes-inner-container}
|
||||
[render-network-values
|
||||
{:token-symbol token-symbol
|
||||
:network-values sender-network-values
|
||||
:on-press (fn [chain-id-to-disable]
|
||||
(disable-chain
|
||||
chain-id-to-disable
|
||||
disabled-from-chain-ids
|
||||
token-available-networks-for-suggested-routes))
|
||||
:receiver? false
|
||||
:theme theme
|
||||
:loading-routes? loading-routes?
|
||||
:token-not-supported-in-receiver-networks? false}]
|
||||
{:token-symbol token-symbol
|
||||
:network-values sender-network-values
|
||||
:token-decimals token-decimals
|
||||
:receiver? 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
|
||||
:on-press on-press-to-network
|
||||
:receiver? true
|
||||
:loading-routes? loading-routes?
|
||||
:theme theme
|
||||
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
|
||||
:on-save #(request-fetch-routes 0)}]]]))
|
||||
{:token-symbol token-symbol
|
||||
:network-values receiver-network-values
|
||||
:token-decimals token-decimals
|
||||
:receiver? true}]]]))
|
||||
|
||||
|
||||
@@ -176,7 +176,6 @@
|
||||
(rf/dispatch [:wallet/clean-local-suggestions])
|
||||
(rf/dispatch [:wallet/clean-selected-collectible])
|
||||
(rf/dispatch [:wallet/clean-send-address])
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/select-address-tab nil])
|
||||
(rf/dispatch [:wallet/clean-current-viewing-account
|
||||
:ignore-just-complete-transaction]))
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
:on-press on-close
|
||||
:switcher-type :select-account}]
|
||||
[quo/page-top
|
||||
{:title (i18n/label :t/select-asset)
|
||||
{:title (i18n/label :t/select-token)
|
||||
:title-accessibility-label :title-label}]
|
||||
[quo/segmented-control
|
||||
{:size 32
|
||||
|
||||
@@ -8,12 +8,8 @@
|
||||
(defn view
|
||||
[]
|
||||
[input-amount/view
|
||||
{:current-screen-id :screen/wallet.send-input-amount
|
||||
:button-one-label (i18n/label :t/review-send)
|
||||
:enabled-from-chain-ids (rf/sub [:wallet/wallet-send-enabled-from-chain-ids])
|
||||
:from-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/clean-from-locked-amounts])
|
||||
(rf/dispatch [:wallet/clean-send-amount]))}])
|
||||
{:current-screen-id :screen/wallet.send-input-amount
|
||||
:button-one-label (i18n/label :t/review-send)
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
|
||||
(rf/dispatch [:wallet/clean-send-amount]))}])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -48,11 +48,10 @@
|
||||
(defn- path-amount-out
|
||||
[path]
|
||||
(if (= (:bridge-name path) constants/bridge-name-hop)
|
||||
(let [{:keys [token-fees bonder-fees amount-in]} path]
|
||||
(let [{:keys [token-fees amount-in]} path]
|
||||
(-> amount-in
|
||||
money/from-hex
|
||||
(money/sub token-fees)
|
||||
(money/add bonder-fees)))
|
||||
(money/sub token-fees)))
|
||||
(-> path :amount-out money/from-hex)))
|
||||
|
||||
(defn convert-wei-to-eth
|
||||
@@ -138,12 +137,6 @@
|
||||
:optimism 2
|
||||
:arbitrum 3})
|
||||
|
||||
(defn safe-add-type-edit
|
||||
[network-values]
|
||||
(if (or (empty? network-values) (some #(= (:type %) :edit) network-values))
|
||||
network-values
|
||||
(conj network-values {:type :edit})))
|
||||
|
||||
(defn reset-loading-network-amounts-to-zero
|
||||
[network-amounts]
|
||||
(mapv
|
||||
@@ -155,118 +148,39 @@
|
||||
network-amounts))
|
||||
|
||||
(defn reset-network-amounts-to-zero
|
||||
[{:keys [network-amounts disabled-chain-ids]}]
|
||||
[network-amounts]
|
||||
(map
|
||||
(fn [network-amount]
|
||||
(let [disabled-chain-ids-set (set disabled-chain-ids)
|
||||
disabled? (contains? disabled-chain-ids-set
|
||||
(:chain-id network-amount))]
|
||||
(cond-> network-amount
|
||||
(and (not= (:type network-amount) :edit)
|
||||
(not= (:type network-amount) :not-available))
|
||||
(assoc :total-amount (money/bignumber "0")
|
||||
:type (if disabled? :disabled :default)))))
|
||||
(assoc network-amount
|
||||
:total-amount (money/bignumber "0")
|
||||
:type :default))
|
||||
network-amounts))
|
||||
|
||||
(defn network-amounts
|
||||
[{:keys [network-values disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?
|
||||
from-locked-amounts]}]
|
||||
(let [disabled-set (set disabled-chain-ids)
|
||||
receiver-networks-set (set receiver-networks)
|
||||
network-values-keys (set (keys network-values))
|
||||
routes-found? (pos? (count network-values-keys))
|
||||
token-networks-ids-set (set token-networks-ids)
|
||||
not-available-networks (if receiver?
|
||||
(filter #(not (token-networks-ids-set %))
|
||||
receiver-networks)
|
||||
[])
|
||||
not-available-networks-set (set not-available-networks)
|
||||
network-values-with-disabled-chains (when routes-found?
|
||||
(reduce
|
||||
(fn [acc k]
|
||||
(if (or (contains? network-values-keys k)
|
||||
receiver?)
|
||||
acc
|
||||
(assoc acc k (money/bignumber "0"))))
|
||||
network-values
|
||||
disabled-chain-ids))
|
||||
network-values-with-not-available-chains (if (and receiver? routes-found?)
|
||||
(let [network-values-keys
|
||||
(set (keys
|
||||
network-values-with-disabled-chains))]
|
||||
(reduce
|
||||
(fn [acc k]
|
||||
(if (not (contains? network-values-keys k))
|
||||
(assoc acc k nil)
|
||||
acc))
|
||||
network-values-with-disabled-chains
|
||||
not-available-networks))
|
||||
network-values-with-disabled-chains)]
|
||||
(cond-> (->>
|
||||
network-values-with-not-available-chains
|
||||
(map
|
||||
(fn [[chain-id amount]]
|
||||
{:chain-id chain-id
|
||||
:total-amount (get from-locked-amounts chain-id amount)
|
||||
:type (cond
|
||||
(contains? from-locked-amounts chain-id) :locked
|
||||
(contains? not-available-networks-set chain-id) :not-available
|
||||
(or receiver? (not (contains? disabled-set chain-id))) :default
|
||||
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}))
|
||||
(sort-by (fn [network-amount]
|
||||
(get network-priority-score
|
||||
(network-utils/id->network (:chain-id network-amount)))))
|
||||
(filter
|
||||
(fn [network-amount]
|
||||
(or (and receiver?
|
||||
(or (contains? receiver-networks-set (:chain-id network-amount))
|
||||
(money/above-zero? (:total-amount network-amount))))
|
||||
(not receiver?))))
|
||||
(vec))
|
||||
(and receiver?
|
||||
routes-found?
|
||||
(not= tx-type :tx/bridge))
|
||||
safe-add-type-edit)))
|
||||
[network-values]
|
||||
(->> network-values
|
||||
(map
|
||||
(fn [[chain-id amount]]
|
||||
{:chain-id chain-id
|
||||
:total-amount amount
|
||||
:type :default}))
|
||||
(sort-by (fn [network-amount]
|
||||
(get network-priority-score
|
||||
(network-utils/id->network (:chain-id network-amount)))))
|
||||
(vec)))
|
||||
|
||||
(defn loading-network-amounts
|
||||
[{:keys [valid-networks disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?]}]
|
||||
(let [disabled-set (set disabled-chain-ids)
|
||||
receiver-networks-set (set receiver-networks)
|
||||
token-networks-ids-set (set token-networks-ids)
|
||||
valid-networks-set (set valid-networks)
|
||||
not-available-networks (if receiver?
|
||||
(filter #(not (token-networks-ids-set %)) receiver-networks)
|
||||
[])
|
||||
not-available-networks-set (set not-available-networks)
|
||||
valid-networks (-> (concat valid-networks
|
||||
(when (not (and receiver? (= tx-type :tx/bridge)))
|
||||
disabled-chain-ids)
|
||||
(when receiver?
|
||||
(filter #(not (valid-networks-set %))
|
||||
not-available-networks)))
|
||||
(distinct))]
|
||||
(->> valid-networks
|
||||
(map
|
||||
(fn [chain-id]
|
||||
(cond->
|
||||
{:chain-id chain-id
|
||||
:type (cond
|
||||
(contains? not-available-networks-set chain-id) :not-available
|
||||
(or receiver?
|
||||
(not (contains? disabled-set chain-id))) :loading
|
||||
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}
|
||||
(and (not receiver?) (contains? disabled-set chain-id))
|
||||
(assoc :total-amount (money/bignumber "0")))))
|
||||
(filter
|
||||
(fn [network-amount]
|
||||
(or (and receiver?
|
||||
(or (= tx-type :tx/bridge)
|
||||
(contains? receiver-networks-set (:chain-id network-amount))))
|
||||
(not receiver?))))
|
||||
(sort-by (fn [network-amount]
|
||||
(get network-priority-score
|
||||
(network-utils/id->network (:chain-id network-amount)))))
|
||||
(vec))))
|
||||
[{:keys [networks values receiver?]}]
|
||||
(->> networks
|
||||
(map
|
||||
(fn [chain-id]
|
||||
(let [network-value (when values (get values chain-id))]
|
||||
(cond-> {:chain-id chain-id
|
||||
:type (if network-value :default :loading)}
|
||||
network-value (assoc :total-amount
|
||||
(money/bignumber network-value))
|
||||
(and (not network-value) (not receiver?)) (assoc :total-amount (money/bignumber "0"))))))
|
||||
(vec)))
|
||||
|
||||
(defn network-links
|
||||
[route from-values-by-chain to-values-by-chain]
|
||||
|
||||
@@ -85,11 +85,9 @@
|
||||
(testing "Correctly calculates network (out) amounts for bridge transaction"
|
||||
(let [route [{:bridge-name "Hop"
|
||||
:amount-in "0xde0b6b3a7640000"
|
||||
:bonder-fees (money/bignumber "200000000000000")
|
||||
:token-fees (money/bignumber "230000000000000")
|
||||
:to {:chain-id 1}}
|
||||
{:bridge-name "Hop"
|
||||
:bonder-fees (money/bignumber "300000000000000")
|
||||
:token-fees (money/bignumber "410000000000000")
|
||||
:amount-in "0xde0b6b3a7640000"
|
||||
:to {:chain-id 10}}]
|
||||
@@ -100,8 +98,8 @@
|
||||
:token-decimals token-decimals
|
||||
:native-token? native-token?
|
||||
:receiver? receiver?})
|
||||
expected {1 (money/bignumber "0.99997")
|
||||
10 (money/bignumber "0.99989")}]
|
||||
expected {1 (money/bignumber "0.99977")
|
||||
10 (money/bignumber "0.99959")}]
|
||||
(doseq [[chain-id exp-value] expected]
|
||||
(is (money/equal-to (get result chain-id) exp-value))))))
|
||||
|
||||
@@ -350,171 +348,12 @@
|
||||
(is (every? identity comparisons)))))
|
||||
|
||||
(deftest network-amounts-test
|
||||
(testing "Handles disabled and receiver networks correctly when receiver? is true"
|
||||
(let [network-values {10 (money/bignumber "200")}
|
||||
disabled-chain-ids [1]
|
||||
receiver-networks [10]
|
||||
token-networks-ids [1 10 42161]
|
||||
receiver? true
|
||||
expected [{:chain-id 10
|
||||
:total-amount (money/bignumber "200")
|
||||
:type :default}
|
||||
{:type :edit}]
|
||||
tx-type :tx/send
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||
|
||||
(testing "Adds default amount for non-disabled non-receiver networks when receiver? is false"
|
||||
(let [network-values {1 (money/bignumber "100")}
|
||||
disabled-chain-ids [10]
|
||||
receiver-networks []
|
||||
token-networks-ids [1 10 42161]
|
||||
receiver? false
|
||||
expected [{:chain-id 1
|
||||
:total-amount (money/bignumber "100")
|
||||
:type :default}
|
||||
{:chain-id 10
|
||||
:total-amount (money/bignumber "0")
|
||||
:type :disabled}]
|
||||
tx-type :tx/send
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||
|
||||
(testing "Handles empty inputs correctly"
|
||||
(let [network-values {}
|
||||
disabled-chain-ids []
|
||||
receiver-networks []
|
||||
token-networks-ids []
|
||||
receiver? true
|
||||
expected []
|
||||
tx-type :tx/send
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(is (= expected result))))
|
||||
|
||||
(testing "Processes case with multiple network interactions"
|
||||
(let [network-values {1 (money/bignumber "300")
|
||||
10 (money/bignumber "400")
|
||||
42161 (money/bignumber "500")}
|
||||
disabled-chain-ids [1 42161]
|
||||
receiver-networks [10]
|
||||
token-networks-ids [1 10 42161]
|
||||
receiver? true
|
||||
expected [{:chain-id 1
|
||||
:total-amount (money/bignumber "300")
|
||||
:type :default}
|
||||
{:chain-id 10
|
||||
:total-amount (money/bignumber "400")
|
||||
:type :default}
|
||||
{:chain-id 42161
|
||||
:total-amount (money/bignumber "500")
|
||||
:type :default}
|
||||
{:type :edit}]
|
||||
tx-type :tx/send
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||
|
||||
(testing "Does not assign :not-available type when receiver? is false"
|
||||
(let [network-values {1 (money/bignumber "100")}
|
||||
disabled-chain-ids [10]
|
||||
receiver-networks [1]
|
||||
token-networks-ids [1 10]
|
||||
receiver? false
|
||||
expected [{:chain-id 1
|
||||
:total-amount (money/bignumber "100")
|
||||
:type :default}
|
||||
{:chain-id 10
|
||||
:total-amount (money/bignumber "0")
|
||||
:type :disabled}]
|
||||
tx-type :tx/send
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||
|
||||
(testing
|
||||
"Assigns :not-available type to networks not available in token-networks-ids when receiver? is true"
|
||||
(let [network-values {1 (money/bignumber "100")}
|
||||
disabled-chain-ids []
|
||||
receiver-networks [1 10]
|
||||
token-networks-ids [1]
|
||||
receiver? false
|
||||
expected [{:chain-id 1
|
||||
:total-amount (money/bignumber "100")
|
||||
:type :default}
|
||||
{:chain-id 10
|
||||
:total-amount nil
|
||||
:type :not-available}]
|
||||
tx-type :tx/send
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||
|
||||
(testing
|
||||
"Handles disabled and receiver networks correctly when receiver? is false and tx-type is :tx/bridge"
|
||||
(let [network-values {10 (money/bignumber "200")}
|
||||
disabled-chain-ids [1]
|
||||
receiver-networks [10]
|
||||
token-networks-ids [1 10]
|
||||
tx-type :tx/bridge
|
||||
receiver? false
|
||||
expected [{:chain-id 1
|
||||
:total-amount (money/bignumber "0")
|
||||
:type :disabled}
|
||||
{:chain-id 10
|
||||
:total-amount (money/bignumber "200")
|
||||
:type :default}]
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||
|
||||
(testing
|
||||
"Handles disabled and receiver networks correctly when receiver? is true and tx-type is :tx/bridge"
|
||||
(let [network-values {10 (money/bignumber "200")}
|
||||
disabled-chain-ids [1]
|
||||
receiver-networks [10]
|
||||
token-networks-ids [1 10]
|
||||
tx-type :tx/bridge
|
||||
receiver? true
|
||||
expected [{:chain-id 10
|
||||
:total-amount (money/bignumber "200")
|
||||
:type :default}]
|
||||
result (utils/network-amounts {:network-values network-values
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})]
|
||||
(testing "Handles network values with one chain"
|
||||
(let [network-values {10 (money/bignumber "200")}
|
||||
expected [{:chain-id 10
|
||||
:total-amount (money/bignumber "200")
|
||||
:type :default}]
|
||||
result (utils/network-amounts network-values)]
|
||||
(is (every? identity (map #(map/deep-compare %1 %2) expected result))))))
|
||||
|
||||
(deftest loading-network-amounts-test
|
||||
@@ -538,27 +377,6 @@
|
||||
result)]
|
||||
(is (every? identity comparisons))))
|
||||
|
||||
(testing "Assigns :disabled type with zero total-amount to disabled networks when receiver? is false"
|
||||
(let [valid-networks [1 10 42161]
|
||||
disabled-chain-ids [10 42161]
|
||||
receiver-networks [1]
|
||||
token-networks-ids [1 10 42161]
|
||||
receiver? false
|
||||
expected [{:chain-id 1 :type :loading}
|
||||
{:chain-id 10 :type :disabled :total-amount (money/bignumber "0")}
|
||||
{:chain-id 42161 :type :disabled :total-amount (money/bignumber "0")}]
|
||||
tx-type :tx/send
|
||||
result (utils/loading-network-amounts {:valid-networks valid-networks
|
||||
:disabled-chain-ids disabled-chain-ids
|
||||
:receiver-networks receiver-networks
|
||||
:token-networks-ids token-networks-ids
|
||||
:tx-type tx-type
|
||||
:receiver? receiver?})
|
||||
comparisons (map #(map/deep-compare %1 %2)
|
||||
expected
|
||||
result)]
|
||||
(is (every? identity comparisons))))
|
||||
|
||||
(testing "Filters out networks not in receiver networks when receiver? is true"
|
||||
(let [valid-networks [1 10 42161 59144]
|
||||
disabled-chain-ids [10]
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- network-item
|
||||
[{:keys [network on-select-network]}]
|
||||
(let [{:keys [network-name
|
||||
chain-id]} network
|
||||
[{:keys [network on-select-network source]}]
|
||||
(let [{:keys [network-name chain-id]} network
|
||||
{balance-in-crypto :crypto
|
||||
balance-in-fiat :fiat} (rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
|
||||
mainnet? (= network-name constants/mainnet-network-name)]
|
||||
balance-in-fiat :fiat} (if (= source :swap)
|
||||
(rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
|
||||
(rf/sub [:wallet/send-token-network-balance chain-id]))
|
||||
mainnet?
|
||||
(= network-name constants/mainnet-network-name)]
|
||||
[quo/network-list
|
||||
{:label (name network-name)
|
||||
:network-image (quo.resources/get-network network-name)
|
||||
@@ -23,21 +25,24 @@
|
||||
:container-style (style/network-list-container mainnet?)}]))
|
||||
|
||||
(defn view
|
||||
[{:keys [on-select-network]}]
|
||||
(let [token-symbol (rf/sub [:wallet/swap-asset-to-pay-token-symbol])
|
||||
{mainnet-network :mainnet-network
|
||||
layer-2-networks :layer-2-networks} (rf/sub [:wallet/swap-asset-to-pay-networks])
|
||||
[{:keys [token-symbol on-select-network source title]
|
||||
:or {source :swap
|
||||
title (i18n/label :t/select-network)}}]
|
||||
(let [{mainnet-network :mainnet-network
|
||||
layer-2-networks :layer-2-networks} (if (= source :swap)
|
||||
(rf/sub [:wallet/swap-asset-to-pay-networks])
|
||||
(rf/sub [:wallet/send-token-grouped-networks]))
|
||||
render-fn (rn/use-callback (fn [network]
|
||||
[network-item
|
||||
{:network network
|
||||
:on-select-network
|
||||
on-select-network}]))]
|
||||
{:network network
|
||||
:on-select-network on-select-network
|
||||
:source source}]))]
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
[quo/text
|
||||
{:size :heading-2
|
||||
:weight :semi-bold}
|
||||
(i18n/label :t/select-network)]
|
||||
title]
|
||||
[quo/context-tag
|
||||
{:type :token
|
||||
:size 24
|
||||
@@ -46,7 +51,8 @@
|
||||
(when mainnet-network
|
||||
[network-item
|
||||
{:network mainnet-network
|
||||
:on-select-network on-select-network}])
|
||||
:on-select-network on-select-network
|
||||
:source source}])
|
||||
[quo/divider-label {:container-style style/divider-label}
|
||||
(i18n/label :t/layer-2)]
|
||||
[rn/flat-list
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [accounts (rf/sub [:wallet/accounts-with-balances])]
|
||||
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||
accounts (rf/sub [:wallet/accounts-with-balances asset-to-pay])]
|
||||
[floating-button-page/view
|
||||
{:footer-container-padding 0
|
||||
:header [quo/page-nav
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||