Compare commits

..
Author SHA1 Message Date
Siddarth Kumar 89d7f4a950 fix: make linter happy 2024-12-10 15:26:52 +05:30
Siddarth Kumar c96d3f427a fix: update SENTRY_DSN_STATUS_GO config 2024-12-10 15:08:07 +05:30
Icaro Motta 1d3e0be2e2 TEMP FOR PR: Show if MixPanel token is set 2024-12-09 14:33:47 -03:00
marko 52cbdf2aaa ci: enable sentry for releases
referenced issue: https://github.com/status-im/status-mobile/issues/21706

Signed-off-by: markoburcul <marko@status.im>
2024-12-09 13:03:16 -03:00
Icaro Motta 32fe1a04e1 TEMP for PR bump VERSION 2024-12-09 13:03:16 -03:00
Icaro Motta 0dab5b47ad Add SENTRY_DSN_STATUS_GO to SECRETS_ENV_VARS 2024-12-09 13:03:16 -03:00
Icaro Motta ec58e7e3c4 Warn in UI if Sentry DSN isn't set 2024-12-09 13:03:16 -03:00
Icaro Motta 7d60849b13 TEMPORARILY enable intentional crash flag
To work in all builds, including release
2024-12-09 13:03:16 -03:00
Icaro Motta cb1f807841 Configure Sentry 2024-12-09 13:03:16 -03:00
74 changed files with 1332 additions and 1018 deletions
+1
View File
@@ -36,3 +36,4 @@ SHOW_NOT_IMPLEMENTED_FEATURES=0
ENABLE_ALERT_BANNER=0
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=1
SENTRY_ENVIRONMENT=ci-main
+1
View File
@@ -40,3 +40,4 @@ ENABLE_ALERT_BANNER=0
FLAG_WALLET_CONNECT_ENABLED=1
MOBILE_DATA_SYNCING_TOGGLE_ENABLE=0
API_LOGGING_ENABLED=1
SENTRY_ENVIRONMENT=ci-main
+1
View File
@@ -37,3 +37,4 @@ TEST_NETWORKS_ENABLED=1
ENABLE_ALERT_BANNER=1
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=1
SENTRY_ENVIRONMENT=ci-main
+1
View File
@@ -24,3 +24,4 @@ TEST_NETWORKS_ENABLED=0
ENABLE_ALERT_BANNER=1
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=0
SENTRY_ENVIRONMENT=ci-nightly
+1
View File
@@ -21,3 +21,4 @@ TEST_NETWORKS_ENABLED=0
STATUS_PROXY_STAGE_NAME=prod
FLAG_WALLET_CONNECT_ENABLED=1
API_LOGGING_ENABLED=0
SENTRY_ENVIRONMENT=production
+1 -1
View File
@@ -1 +1 @@
2.32.0
2.32.21682
+2 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.15'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
@@ -41,6 +41,7 @@ pipeline {
BUILD_ENV = 'prod'
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
FASTLANE_DISABLE_COLORS = 1
SENTRY_PRODUCTION = "${utils.isReleaseBuild() ? 'true' : 'false'}"
}
stages {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.15'
import groovy.json.JsonBuilder
+2 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.13'
library 'status-jenkins-lib@v1.9.15'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
@@ -41,6 +41,7 @@ pipeline {
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
FASTLANE_DISABLE_COLORS = 1
BUNDLE_PATH = "${HOME}/.bundle"
SENTRY_PRODUCTION = "${utils.isReleaseBuild() ? 'true' : 'false'}"
}
stages {
+12 -15
View File
@@ -23,18 +23,15 @@ PODS:
- secp256k1
- SSZipArchive
- libevent (2.1.12)
- libwebp (1.3.2):
- libwebp/demux (= 1.3.2)
- libwebp/mux (= 1.3.2)
- libwebp/sharpyuv (= 1.3.2)
- libwebp/webp (= 1.3.2)
- libwebp/demux (1.3.2):
- libwebp (1.2.4):
- libwebp/demux (= 1.2.4)
- libwebp/mux (= 1.2.4)
- libwebp/webp (= 1.2.4)
- libwebp/demux (1.2.4):
- libwebp/webp
- libwebp/mux (1.3.2):
- libwebp/mux (1.2.4):
- libwebp/demux
- libwebp/sharpyuv (1.3.2)
- libwebp/webp (1.3.2):
- libwebp/sharpyuv
- libwebp/webp (1.2.4)
- RCT-Folly (2022.05.16.00):
- boost
- DoubleConversion
@@ -1192,7 +1189,7 @@ PODS:
- secp256k1 (0.1.6)
- SocketRocket (0.6.1)
- SSZipArchive (2.4.3)
- TOCropViewController (2.7.4)
- TOCropViewController (2.6.1)
- Yoga (1.14.0)
DEPENDENCIES:
@@ -1492,7 +1489,7 @@ CHECKOUT OPTIONS:
:git: https://github.com/status-im/Keycard.swift.git
:tag: 3.1.1
secp256k1:
:commit: 4ab977cc2b2d7319be858bcb30a5d189bb149884
:commit: 46a1fa30d9b8babeae85ff519050f42394ab5fcc
:git: https://github.com/status-im/secp256k1.swift.git
:submodules: true
@@ -1509,7 +1506,7 @@ SPEC CHECKSUMS:
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
Keycard: 3bd24405b9a875b3b3cf1ab1be69b5c30c5f28bc
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: 2544c0f1081a5fa12e108bb8cb40e5f4581ccd87
RCTTypeSafety: 50efabe2b115c11ed03fbf3fd79e2f163ddb5d7c
@@ -1593,9 +1590,9 @@ SPEC CHECKSUMS:
secp256k1: f61d67e6fdcb85fd727acf1bf35ace6036db540c
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
Yoga: a716eea57d0d3430219c0a5a233e1e93ee931eb7
PODFILE CHECKSUM: 56d9f695324306d6db368d1172d211dfab55b7cb
PODFILE CHECKSUM: 81a66b45adddb083171ade882c343deb4faf2a8b
COCOAPODS: 1.15.2
@@ -108,6 +108,15 @@ class StatusModule(private val reactContext: ReactApplicationContext, private va
)
}
@ReactMethod
fun intendedPanic(message: String) {
StatusBackendClient.executeStatusGoRequest(
endpoint = "IntendedPanic",
requestBody = "",
statusgoFunction = { Statusgo.intendedPanic(message) },
)
}
@ReactMethod
fun addCentralizedMetric(request: String, callback: Callback) {
StatusBackendClient.executeStatusGoRequestWithCallback(
@@ -109,6 +109,13 @@ RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
callback:callback];
}
RCT_EXPORT_METHOD(intendedPanic:(NSString *)message) {
#if DEBUG
NSLog(@"IntendedPanic() method called");
#endif
StatusgoIntendedPanic(message);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(fleets) {
return [StatusBackendClient executeStatusGoRequestWithResult:@"Fleets"
body:@""
+1 -1
View File
@@ -51,7 +51,7 @@ in {
yarn = super.yarn.override { nodejs = super.nodejs_20; };
openjdk = super.openjdk17_headless;
xcodeWrapper = callPackage ./pkgs/xcodeenv/compose-xcodewrapper.nix { } {
versions = ["15.1" "15.2" "15.3" "15.4" "16.0" "16.1"];
versions = ["15.1" "15.2" "15.3" "15.4"];
};
go = super.go_1_21;
clang = super.clang_15;
+5 -1
View File
@@ -11,7 +11,7 @@
, outputFileName ? "status-go-${source.shortRev}-${platform}.aar" }:
let
inherit (lib) concatStringsSep optionalString optional splitString;
inherit (lib) concatStringsSep optionalString optional splitString fileContents;
isIOS = platform == "ios";
isAndroid = platform == "android";
enforceXCodeAvailable = callPackage ./enforceXCodeAvailable.nix { };
@@ -46,6 +46,10 @@ in buildGoPackage rec {
# TODO: try removing when go is upgraded to 1.22
GODEBUG = "netdns=cgo+2";
# Sentry for status-go
SENTRY_CONTEXT_NAME = "status-mobile";
SENTRY_CONTEXT_VERSION = fileContents ../../../VERSION;
preBuild = ''
echo 'Generate static files'
pushd go/src/$goPackagePath
+1
View File
@@ -45,6 +45,7 @@ SECRETS_ENV_VARS=(
'MIXPANEL_APP_ID'
'MIXPANEL_TOKEN'
'POKT_TOKEN'
'SENTRY_DSN_STATUS_GO'
)
# Secrets like this can't be passed via args or they end up in derivation.
+2 -1
View File
@@ -123,7 +123,8 @@
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ARBITRUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_MAINNET_TOKEN #shadow/env "ALCHEMY_OPTIMISM_MAINNET_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"}
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"
status-im.config/SENTRY_DSN_STATUS_GO #shadow/env "SENTRY_DSN_STATUS_GO"}
:compiler-options {:output-feature-set :es6
;;disable for android build as there
;;is an intermittent warning with deftype
@@ -1,10 +1,13 @@
(ns legacy.status-im.ui.screens.advanced-settings.views
(:require
[clojure.string :as string]
[legacy.status-im.ui.components.core :as components]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.list.views :as list]
[quo.core :as quo]
[re-frame.core :as re-frame]
[status-im.config :as config]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf])
(:require-macros [legacy.status-im.utils.views :as views]))
@@ -22,7 +25,23 @@
peer-syncing-enabled?]}]
(keep
identity
[{:size :small
[;; TODO(ilmotta): REMOVE BEFORE MERGE
{:size :small
:title (str "Is MixPanel token set?"
(if (string/blank? config/mixpanel-token)
"No"
"Yes"))
:on-press identity}
(when (ff/enabled? ::ff/app-monitoring.intentional-crash)
{:size :small
:title (str "Force crash immediately"
(when (string/blank? config/sentry-dsn-status-go)
" (Sentry DSN is not set)"))
:accessibility-label :intended-panic
:on-press (fn []
(re-frame/dispatch [:app-monitoring/intended-panic
"status-mobile intentional panic"]))})
{:size :small
:title (i18n/label :t/log-level)
:accessibility-label :log-level-settings-button
:on-press
+4
View File
@@ -581,3 +581,7 @@
(.createAccountFromPrivateKey ^js (account-manager)
(types/clj->json {:privateKey private-key})
callback)))
(defn intended-panic
[message]
(.intendedPanic ^js (status) message))
@@ -0,0 +1,8 @@
(ns status-im.common.app-monitoring.effects
(:require
[native-module.core :as native-module]
[utils.re-frame :as rf]))
(rf/reg-fx :effects.app-monitoring/intended-panic
(fn [message]
(native-module/intended-panic message)))
@@ -0,0 +1,8 @@
(ns status-im.common.app-monitoring.events
(:require
status-im.common.app-monitoring.effects
[utils.re-frame :as rf]))
(rf/reg-event-fx :app-monitoring/intended-panic
(fn [_ [message]]
{:fx [[:effects.app-monitoring/intended-panic message]]}))
@@ -61,17 +61,14 @@
(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
;; 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]
blur-options content-avoid-keyboard? automatically-adjust-keyboard-insets]
: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 nil)
header-height (reagent/atom 0)
content-container-height (reagent/atom 0)
content-scroll-y (reagent/atom 0)
keyboard-height (reagent/atom 0)
@@ -115,7 +112,7 @@
{:style
(if content-avoid-keyboard?
(style/content-keyboard-avoiding-view
{:top (or @header-height initial-header-height)
{:top @header-height
:bottom (if keyboard-shown?
@footer-container-height
(+ bottom-safe-area @footer-container-height))})
@@ -15,18 +15,18 @@
auth-method (rf/sub [:auth-method])
biometric-auth? (= auth-method constants/auth-method-biometric)
on-complete (rn/use-callback
(fn [reset-slider-fn]
(js/setTimeout #(reset-slider-fn false) 500)
(rf/dispatch
[:standard-auth/authorize
{:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
:keycard-supported? keycard-supported?
:biometric-auth? biometric-auth?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label}]))
(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])]
[quo/slide-button
@@ -28,10 +28,9 @@
:long-enough?
:short-enough?)))))
(def password-tip? (set constants/password-tips))
(defn strength
[validations]
(->> validations
(filter #(and (password-tip? (key %)) (val %)))
(count)))
(->> (select-keys validations constants/password-tips)
(vals)
(filter true?)
count))
+5
View File
@@ -150,3 +150,8 @@
(goog-define STATUS_BACKEND_SERVER_ROOT_DATA_DIR "")
;; if you're using android simulator, I suggest set the env variable to "http://10.0.2.2:"
(goog-define STATUS_BACKEND_SERVER_IMAGE_SERVER_URI_PREFIX "https://localhost:")
;;;; Sentry
;; Documentation: status-go/internal/sentry/README.md
(goog-define SENTRY_DSN_STATUS_GO "")
(def sentry-dsn-status-go SENTRY_DSN_STATUS_GO)
@@ -52,8 +52,8 @@
{:eventName "navigation"
:platform platform-os
:appVersion app-version
:eventValue {:viewId "onboarding.create-profile-password"}}}]
(tracking/track-view-id-event :screen/onboarding.create-profile-password)))
:eventValue {:viewId "onboarding.create-profile-info"}}}]
(tracking/track-view-id-event :screen/onboarding.create-profile)))
(is (= [] (tracking/track-view-id-event :unknown-stack)))))
(deftest tracked-event-test
@@ -1,7 +1,6 @@
(ns status-im.contexts.onboarding.create-password.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
[quo.foundations.colors :as colors]))
(def heading {:margin-bottom 20})
(def heading-subtitle {:color colors/white})
@@ -22,18 +21,6 @@
: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,7 +1,9 @@
(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]
@@ -27,37 +29,22 @@
(i18n/label :t/password-creation-subtitle)]])
(defn password-inputs
[{: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?)]
[{: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?))]
[:<>
[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)
@@ -65,50 +52,55 @@
: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 non-empty-password? show-validation?)}
:shown? (and (not empty-password?)
show-password-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?] :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)]]]))
[{{: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)]]])
(defn- use-password-checks
[password]
(rn/use-memo
(fn []
(-> password
(password/validate)
(assoc :non-empty? (seq password))))
(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]))
(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
@@ -127,70 +119,102 @@
{:content create-password-doc
:shell? true}]))
(defn- navigate-back [] (rf/dispatch [:navigate-back]))
(defn- navigate-back
[]
(rf/dispatch [:navigate-back]))
(defn- page-nav
[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)]))
[]
(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}]}]))
(defn create-password
[]
(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)]
(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?)]
[floating-button/view
{:header [page-nav top]
:initial-header-height (+ style/page-nav-height top)
:keyboard-should-persist-taps :handled
:content-avoid-keyboard? true
{:header [page-nav]
:keyboard-should-persist-taps :handled
:content-avoid-keyboard? true
:automatically-adjust-keyboard-insets true
: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}]}
: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)]]}
[rn/view {:style style/form-container}
[header]
[password-inputs
{: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}]]]))
{: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))}]]]))
@@ -0,0 +1,60 @@
(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})
@@ -0,0 +1,210 @@
(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}]))
+71 -83
View File
@@ -1,6 +1,5 @@
(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]
@@ -112,57 +111,63 @@
(rf/reg-event-fx
:onboarding/password-set
(fn [{:keys [db]} [masked-password]]
(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)]
(let [biometric-supported-type (get-in db [:biometrics :supported-type])]
{: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 from-screen]]
[:navigate-to-within-stack
[:screen/onboarding.enable-biometrics
(get db
:onboarding/navigated-to-enter-seed-phrase-from-screen
:screen/onboarding.new-to-status)]]
[:onboarding/create-account-and-login])]]})))
(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 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/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 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/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/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-sign-in-by-syncing
(fn [{:keys [db]}]
@@ -175,43 +180,26 @@
(fn [{:keys [db]} [auth-method]]
{:db (assoc db :auth-method auth-method)}))
(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 %})}])]})))
(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 %})}))))
@@ -52,7 +52,6 @@
[]
(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}]))
@@ -61,7 +60,6 @@
[]
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
(blur-show-fn))
(rf/dispatch [:onboarding/use-temporary-display-name true])
(rf/dispatch [:open-modal :screen/onboarding.share-usage
{:next-screen :screen/onboarding.new-to-status}]))
@@ -1,7 +1,5 @@
(ns status-im.contexts.profile.edit.name.events
(:require [clojure.string :as string]
[status-im.constants :as constants]
[utils.i18n :as i18n]
(:require [utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/reg-event-fx :profile/edit-profile-name-success
@@ -22,16 +20,3 @@
: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]}]]]})))
@@ -37,6 +37,7 @@
{:dataDir (native-module/backup-disabled-data-dir)
:mixpanelAppId config/mixpanel-app-id
:mixpanelToken config/mixpanel-token
:sentryDSN config/sentry-dsn-status-go
:mediaServerEnableTLS (config/enabled? config/STATUS_BACKEND_SERVER_MEDIA_SERVER_ENABLE_TLS)
:logEnabled (not (string/blank? config/log-level))
:logLevel config/log-level
@@ -152,12 +152,15 @@
(rf/reg-event-fx
:profile.login/login-node-signal
(fn [{db :db} [{:keys [settings account ensUsernames error]}]]
(fn [{{:onboarding/keys [recovered-account? new-account?] :as 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 [[:dispatch-later [{:ms 2000 :dispatch [:ens/update-usernames ensUsernames]}]]
: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]}]]
[:dispatch [:profile.login/login-existing-profile settings account]]]})))
(rf/reg-event-fx
@@ -47,7 +47,6 @@
[]
(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))
@@ -56,7 +55,6 @@
[]
(when @push-animation-fn-atom
(@push-animation-fn-atom))
(rf/dispatch [:onboarding/use-temporary-display-name false])
(debounce/throttle-and-dispatch
[:open-modal :screen/onboarding.sync-or-recover-profile]
1000))
@@ -1,5 +1,6 @@
(ns status-im.contexts.wallet.bridge.bridge-to.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[quo.theme]
@@ -14,34 +15,28 @@
(defn- bridge-token-component
[]
(fn [{:keys [chain-id network-name]} {:keys [supported-networks] :as token} selected-network]
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])
{selected-network-chain-id :chain-id} selected-network
balance (utils/calculate-total-token-balance token [chain-id])
crypto-value (utils/get-standard-crypto-format token
balance
prices-per-token)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
fiat-value)
token-available-on-network? (network-utils/token-available-on-network?
supported-networks
chain-id)]
(fn [{:keys [chain-id network-name]} {:keys [supported-networks] :as token}]
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])
balance (utils/calculate-total-token-balance token [chain-id])
crypto-value (utils/get-standard-crypto-format token balance prices-per-token)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
fiat-value)
token-available-on-network? (network-utils/token-available-on-network? supported-networks
chain-id)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network (:network-name network))
:token-value (str crypto-value " " (:symbol token))
:fiat-value fiat-formatted
:state (if (and token-available-on-network? (not= chain-id selected-network-chain-id))
:default
:disabled)
:state (if token-available-on-network? :default :disabled)
:on-press #(rf/dispatch [:wallet/select-bridge-network
{:network-chain-id chain-id
:stack-id :screen/wallet.bridge-to}])}])))
@@ -51,7 +46,6 @@
(let [network-details (rf/sub [:wallet/network-details])
account (rf/sub [:wallet/current-viewing-account])
token (rf/sub [:wallet/wallet-send-token])
selected-network (rf/sub [:wallet/send-network])
token-symbol (:symbol token)
tokens (:tokens account)
mainnet (first network-details)
@@ -61,7 +55,8 @@
(assoc account-token
:networks (:networks token)
:supported-networks (:supported-networks token)))
bridge-to-title (i18n/label :t/select-network-to-receive)]
bridge-to-title (i18n/label :t/bridge-to
{:name (string/upper-case (str token-symbol))})]
(hot-reload/use-safe-unmount #(rf/dispatch [:wallet/clean-bridge-to-selection]))
@@ -73,11 +68,11 @@
:switcher-type :select-account}]
[quo/page-top {:title bridge-to-title}]
[rn/view style/content-container
[bridge-token-component (assoc mainnet :network-name :t/mainnet) account-token selected-network]]
[bridge-token-component (assoc mainnet :network-name :t/mainnet) account-token]]
[quo/divider-label (i18n/label :t/layer-2)]
[rn/flat-list
{:data layer-2-networks
:render-fn (fn [network]
[bridge-token-component network account-token selected-network])
[bridge-token-component network account-token])
:content-container-style style/content-container}]]))
@@ -1,7 +1,9 @@
(ns status-im.contexts.wallet.bridge.flow-config)
(def steps
[{:screen-id :screen/wallet.bridge-select-asset
[{:screen-id :screen/wallet.select-from
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
{:screen-id :screen/wallet.bridge-select-asset
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :token])))}
{:screen-id :screen/wallet.bridge-to
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :bridge-to-chain-id])))}
@@ -82,13 +82,13 @@
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
account-owns-token? (rf/sub [:wallet/current-account-owns-token token-symbol])
network-details (rf/sub [:wallet/network-details])
receive-token-symbol (if (= token-symbol "SNT") "ETH" "SNT")
token-owned? (if selected-account account-owns-token? (seq token-owners))
asset-to-receive (rf/sub [:wallet/token-by-symbol receive-token-symbol])
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?}
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?
:asset-to-receive asset-to-receive}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
@@ -108,13 +108,9 @@
(action-send params entry-point))
(action-receive selected-account)
(when token-owned?
(action-swap (assoc params
:asset-to-receive
asset-to-receive)))
(action-swap params))
(when token-owned?
(action-bridge (assoc params
:network-details
network-details
:bridge-disabled?
(send-utils/bridge-disabled? token-symbol))))]))]]))
@@ -1,11 +1,23 @@
(ns status-im.contexts.wallet.common.wizard.events
(:require [status-im.contexts.wallet.common.wizard.utils :as wizard-utils]
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]
[utils.re-frame :as rf]))
(defn- wizard-find-next-screen
[db flow-id current-screen]
(let [flow-config (case flow-id
:wallet-send-flow wallet-send-flow/steps
:wallet-bridge-flow wallet-bridge-flow/steps
nil)]
(first (filter (fn [{:keys [skip-step? screen-id]}]
(and (not= screen-id current-screen)
(not (and (fn? skip-step?) (skip-step? db)))))
flow-config))))
(rf/reg-event-fx
:wallet/wizard-navigate-forward
(fn [{:keys [db]} [{:keys [current-screen flow-id start-flow?]}]]
(let [next-screen (wizard-utils/wizard-find-next-screen db flow-id current-screen)]
(let [next-screen (wizard-find-next-screen db flow-id current-screen)]
{:fx [[:dispatch
(if start-flow?
[:open-modal (:screen-id next-screen)]
@@ -1,14 +0,0 @@
(ns status-im.contexts.wallet.common.wizard.utils
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]))
(defn wizard-find-next-screen
[db flow-id current-screen]
(let [flow-config (case flow-id
:wallet-send-flow wallet-send-flow/steps
:wallet-bridge-flow wallet-bridge-flow/steps
nil)]
(first (filter (fn [{:keys [skip-step? screen-id]}]
(and (not= screen-id current-screen)
(not (and (fn? skip-step?) (skip-step? db)))))
flow-config))))
+8 -52
View File
@@ -10,13 +10,11 @@
[status-im.contexts.settings.wallet.effects]
[status-im.contexts.settings.wallet.events]
[status-im.contexts.wallet.common.activity-tab.events]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.external-links :as external-links]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.db :as db]
[status-im.contexts.wallet.item-types :as item-types]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[status-im.contexts.wallet.tokens.events]
[status-im.feature-flags :as ff]
[taoensso.timbre :as log]
@@ -345,61 +343,19 @@
(rf/reg-event-fx :wallet/get-keypairs get-keypairs)
(rf/reg-event-fx :wallet/bridge-select-token
(fn [{:keys [db]}
[{:keys [token token-symbol stack-id network owners network-details start-flow?] :as params}]]
(let [{:keys [wallet]} db
unique-owner (when (= (count owners) 1)
(first owners))
token (if (and unique-owner (nil? token))
(let [token (utils/get-token-from-account db
token-symbol
unique-owner)]
(assoc token
:networks (network-utils/network-list-with-positive-balance
token
network-details)
:supported-networks (network-utils/network-list
token
network-details)
:total-balance (utils/calculate-total-token-balance token)))
token)
missing-recipient? (-> db :wallet :ui :send :to-address nil?)
to-address (or unique-owner
(-> db :wallet :current-viewing-account-address)
(utils/get-default-account (-> db :wallet :accounts)))
view-id (:view-id db)
root-screen? (or (= view-id :wallet-stack) (nil? view-id))
multi-account-balance? (-> (utils/get-accounts-with-token-balance (:accounts wallet)
token)
(count)
(> 1))]
(fn [{:keys [db]} [{:keys [token token-symbol stack-id start-flow?]}]]
(let [missing-recipient? (-> db :wallet :ui :send :to-address nil?)
to-address (-> db :wallet :current-viewing-account-address)]
{:db (cond-> db
:always (assoc-in [:wallet :ui :send :tx-type] :tx/bridge)
token (assoc-in [:wallet :ui :send :token] token)
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
network (assoc-in [:wallet :ui :send :network] network)
missing-recipient? (assoc-in [:wallet :ui :send :to-address] to-address))
:fx (if (and (not unique-owner) multi-account-balance? root-screen?)
[[:dispatch [:open-modal :screen/wallet.select-from]]]
(if (some? network)
[[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]
[[:dispatch [:wallet/switch-current-viewing-account to-address]]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:title (i18n/label :t/select-network-to-bridge-from)
:token-symbol (or token-symbol (:symbol token))
:source :bridge
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/bridge-select-token
(assoc params :network network)]))}])}]]]))})))
:fx [[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]})))
(rf/reg-event-fx :wallet/start-bridge
(fn [{:keys [db]}]
+127 -132
View File
@@ -5,10 +5,8 @@
[status-im.contexts.wallet.collectible.utils :as collectible.utils]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.common.wizard.utils :as wizard-utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[taoensso.timbre :as log]
[utils.money :as utils.money]
[utils.number]
@@ -150,16 +148,23 @@
(rf/reg-event-fx
:wallet/select-send-address
(fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]]
(let [[_ to-address] (utils/split-prefix-and-address address)
collectible-tx? (send-utils/tx-type-collectible?
(-> db :wallet :ui :send :tx-type))
collectible (when collectible-tx?
(-> db :wallet :ui :send :collectible))
one-collectible? (when collectible-tx?
(= (collectible.utils/collectible-balance collectible) 1))]
(let [[prefix to-address] (utils/split-prefix-and-address address)
testnet-enabled? (get-in db [:profile/profile :test-networks-enabled?])
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
collectible-tx? (send-utils/tx-type-collectible?
(-> db :wallet :ui :send :tx-type))
collectible (when collectible-tx?
(-> db :wallet :ui :send :collectible))
one-collectible? (when collectible-tx?
(= (collectible.utils/collectible-balance collectible) 1))]
{:db (-> db
(assoc-in [:wallet :ui :send :recipient] (or recipient address))
(assoc-in [:wallet :ui :send :to-address] to-address))
(assoc-in [:wallet :ui :send :to-address] to-address)
(assoc-in [:wallet :ui :send :address-prefix] prefix)
(assoc-in [:wallet :ui :send :receiver-preferred-networks] receiver-networks)
(assoc-in [:wallet :ui :send :receiver-networks] receiver-networks))
:fx [(when (and collectible-tx? one-collectible?)
[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]])
[:dispatch
@@ -177,12 +182,14 @@
(rf/reg-event-fx
:wallet/set-token-to-send
(fn [{:keys [db]}
[{:keys [token-symbol token network stack-id start-flow? owners] :as params} entry-point]]
(fn [{:keys [db]} [{:keys [token-symbol token stack-id start-flow? owners]} entry-point]]
;; `token` is a map extracted from the sender, but in the wallet home page we don't know the
;; sender yet, so we only provide the `token-symbol`, later in
;; `:wallet/select-from-account` the `token` key will be set.
(let [{:keys [wallet]} db
(let [{:keys [networks]} token
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
token-networks-ids (map :chain-id networks)
unsupported-token? (not-any? (set receiver-networks) token-networks-ids)
unique-owner (when (= (count owners) 1)
(first owners))
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
@@ -190,25 +197,17 @@
(when (and token-symbol unique-owner)
(some #(when (= (:symbol %) token-symbol) %)
unique-owner-tokens)))
view-id (:view-id db)
root-screen? (or (= view-id :wallet-stack) (nil? view-id))
multi-account-balance? (-> (utils/get-accounts-with-token-balance (:accounts wallet)
token)
(count)
(> 1))
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
network-details (-> (get-in db
[:wallet :networks (if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))
next-screen (:screen-id (wizard-utils/wizard-find-next-screen db
:wallet-send-flow
stack-id))]
(network-utils/sorted-networks-with-details))]
(when (or token-data token-symbol)
{:db (cond-> db
network (update-in [:wallet :ui :send]
:always (update-in [:wallet :ui :send]
#(-> %
(dissoc :collectible :tx-type)
(assoc :network network)))
(assoc :token-not-supported-in-receiver-networks?
unsupported-token?)))
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
token-data (update-in [:wallet :ui :send]
#(assoc %
@@ -217,35 +216,20 @@
(network-utils/network-list
token-data
network-details))
:token-display-name (:symbol token-data)
:token-symbol (:symbol token-data)))
:token-display-name (:symbol token-data)))
unique-owner (assoc-in [:wallet :current-viewing-account-address] unique-owner)
entry-point (assoc-in [:wallet :ui :send :entry-point] entry-point))
:fx (if (and (not unique-owner) multi-account-balance? root-screen?)
[[:dispatch [:open-modal :screen/wallet.select-from]]]
(if (or (= next-screen :screen/wallet.select-from) (some? network))
[[:dispatch [:wallet/stop-and-clean-suggested-routes]]
[:dispatch
;; ^:flush-dom allows us to make sure the re-frame DB state is always synced
;; before the navigation occurs, so the new screen is always rendered with
;; the DB state set by this event. By adding the metadata we are omitting
;; a 1-frame blink when the screen is mounted.
^:flush-dom
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]
[[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (or token-symbol (:symbol token-data))
:source :send
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/set-token-to-send
(assoc params :network network)]))}])}]]]))}))))
:fx [[:dispatch [:wallet/stop-and-clean-suggested-routes]]
[:dispatch
;; ^:flush-dom allows us to make sure the re-frame DB state is always synced
;; before the navigation occurs, so the new screen is always rendered with
;; the DB state set by this event. By adding the metadata we are omitting
;; a 1-frame blink when the screen is mounted.
^:flush-dom
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}))))
(rf/reg-event-fx
:wallet/edit-token-to-send
@@ -304,7 +288,7 @@
collectible
(str (:name collection-data) " #" collectible-id))
owner-address (-> collectible :ownership first :address)
owner-address (-> db :wallet :current-viewing-account-address)
collectible-tx (-> db
(update-in [:wallet :ui :send] dissoc :token)
(assoc-in [:wallet :ui :send :collectible] collectible)
@@ -420,62 +404,89 @@
(rf/reg-event-fx :wallet/start-get-suggested-routes
(fn [{:keys [db]} [{:keys [amount amount-out updated-token] :as args :or {amount-out "0"}}]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
{:keys [token tx-type collectible to-address
network bridge-to-chain-id]
receiver-networks disabled-from-chain-ids
from-locked-amounts bridge-to-chain-id]
:or {token updated-token}} (get-in db [:wallet :ui :send])
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db
[:wallet :networks
(if test-networks-enabled? :test :prod)])
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
to-token-id ""
gas-rates constants/gas-rate-medium
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
amount-in (to-hex amount)
amount-out (to-hex amount-out)
from-address wallet-address
network-chain-id (if collectible
(get-in collectible [:id :contract-id :chain-id])
(:chain-id network))
disabled-from-chain-ids (filter #(not= % network-chain-id) network-chain-ids)
disabled-to-chain-ids (filter #(not= %
(if (= tx-type :tx/bridge)
bridge-to-chain-id
network-chain-id))
network-chain-ids)
send-type (case tx-type
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
:tx/bridge constants/send-type-bridge
constants/send-type-transfer)
sender-network-values (when (= tx-type :tx/bridge)
(send-utils/loading-network-amounts
{:networks [network-chain-id]
:values {network-chain-id amount}
:receiver? false}))
receiver-network-values (when (= tx-type :tx/bridge)
(send-utils/loading-network-amounts
{:networks [bridge-to-chain-id]
:receiver? true}))
request-uuid (str (random-uuid))
params [{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
:amountIn amount-in
:amountOut amount-out
:tokenID token-id
:toTokenID to-token-id
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount {}
:username (:username args)
:publicKey (:publicKey args)
:packID (:packID args)}]]
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db [:wallet :networks (if test-networks-enabled? :test :prod)])
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
to-token-id ""
gas-rates constants/gas-rate-medium
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
amount-in (to-hex amount)
amount-out (to-hex amount-out)
from-address wallet-address
disabled-to-chain-ids (if (= tx-type :tx/bridge)
(filter #(not= % bridge-to-chain-id) network-chain-ids)
(filter (fn [chain-id]
(not (some #(= chain-id %)
receiver-networks)))
network-chain-ids))
from-locked-amount (update-vals from-locked-amounts to-hex)
send-type (case tx-type
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
:tx/bridge constants/send-type-bridge
constants/send-type-transfer)
balances-per-chain (when token (:balances-per-chain token))
sender-token-available-networks-for-suggested-routes
(when token
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain balances-per-chain
:disabled-chain-ids disabled-from-chain-ids
:only-with-balance? true}))
receiver-token-available-networks-for-suggested-routes
(when token
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain balances-per-chain
:disabled-chain-ids disabled-from-chain-ids
:only-with-balance? false}))
token-networks-ids (when token (map #(:chain-id %) (:supported-networks token)))
sender-network-values (when sender-token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
{:valid-networks
(if (= tx-type :tx/bridge)
(remove #(= bridge-to-chain-id %)
sender-token-available-networks-for-suggested-routes)
sender-token-available-networks-for-suggested-routes)
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? false}))
receiver-network-values (when receiver-token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
{:valid-networks
(if (= tx-type :tx/bridge)
(filter
#(= bridge-to-chain-id %)
receiver-token-available-networks-for-suggested-routes)
receiver-token-available-networks-for-suggested-routes)
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true}))
request-uuid (str (random-uuid))
params [{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
:amountIn amount-in
:amountOut amount-out
:tokenID token-id
:toTokenID to-token-id
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount from-locked-amount
:username (:username args)
:publicKey (:publicKey args)
:packID (:packID args)}]]
(when (and to-address from-address amount-in token-id)
{:db (update-in db
[:wallet :ui :send]
@@ -701,7 +712,7 @@
(rf/reg-event-fx
:wallet/select-from-account
(fn [{db :db} [{:keys [address stack-id network-details network start-flow?] :as params}]]
(fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]]
(let [{:keys [token-symbol
tx-type]} (-> db :wallet :ui :send)
token (when token-symbol
@@ -713,38 +724,22 @@
:networks (network-utils/network-list-with-positive-balance
token
network-details)
:supported-networks (network-utils/network-list
token
network-details)
:supported-networks (network-utils/network-list token
network-details)
:total-balance (utils/calculate-total-token-balance token))))
bridge-tx? (= tx-type :tx/bridge)
flow-id (if bridge-tx?
:wallet-bridge-flow
:wallet-send-flow)]
{:db (cond-> db
network (assoc-in [:wallet :ui :send :network] network)
token-symbol (assoc-in [:wallet :ui :send :token] token)
bridge-tx? (assoc-in [:wallet :ui :send :to-address] address))
:fx (if (some? network)
[[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id flow-id}]]]
[[:dispatch [:dismiss-modal :screen/wallet.select-from]]
[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (or token-symbol (:symbol token))
:source :send
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/select-from-account
(assoc params :network network)]))}])}]]])})))
:fx [[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id flow-id}]]]})))
(rf/reg-event-fx
:wallet/transaction-confirmation-navigate-back
@@ -1,7 +1,6 @@
(ns status-im.contexts.wallet.send.events-test
(:require
[cljs.test :refer-macros [is testing]]
[matcher-combinators.matchers :as m]
matcher-combinators.test
[re-frame.db :as rf-db]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
@@ -580,23 +579,8 @@
[event-id dispatch]
(let [stack-id :screen/stack
start-flow? false
address "0x01"
network {:chain-id 1}]
address "0x01"]
(testing "when tx-type is :tx/bridge and token-symbol is nil"
(let [flow-id :wallet-bridge-flow
tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address
:tx-type tx-type}}}}
:fx [[:dispatch [:dismiss-modal :screen/wallet.select-from]]
[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch [:show-bottom-sheet {:content (m/pred fn?)}]]]}]
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type}}}})
(is (match? expected-result
(dispatch [event-id
{:address address
:stack-id stack-id
:start-flow? start-flow?}])))))
(testing "when tx-type is :tx/bridge, network is selected and token-symbol is nil"
(let [flow-id :wallet-bridge-flow
tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address
@@ -612,7 +596,6 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?}])))))
(testing "when tx-type is not :tx/bridge and token-symbol is nil"
(let [flow-id :wallet-send-flow
@@ -629,7 +612,6 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?}])))))
(testing "when tx-type is :tx/bridge and token-symbol is not nil"
(let [flow-id :wallet-bridge-flow
@@ -698,6 +680,5 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?
:netork-details network-details}])))))))
@@ -14,7 +14,9 @@
(-> db :wallet :ui :send :token some?))
(def steps
[{:screen-id :screen/wallet.select-address
[{:screen-id :screen/wallet.select-from
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
{:screen-id :screen/wallet.select-address
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :recipient])))}
{:screen-id :screen/wallet.select-asset
:skip-step? (fn [db] (or (token-selected? db) (collectible-selected? db)))}
@@ -6,8 +6,8 @@
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.send.from.style :as style]
[status-im.setup.hot-reload :as hot-reload]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- on-account-press
@@ -15,28 +15,27 @@
(rf/dispatch [:wallet/select-from-account
{:address address
:network-details network-details
:stack-id :screen/wallet.select-from
:start-flow? true}]))
:stack-id :screen/wallet.select-from}]))
(defn- on-close
[]
(rf/dispatch [:wallet/clean-current-viewing-account]))
(defn- render-fn
[item _ _ {:keys [network-details]}]
(let [has-balance (money/above-zero? (:asset-pay-balance item))]
(let [transformed-address (rf/sub [:wallet/account-address (:address item)
(:network-preferences-names item)])]
[quo/account-item
{:type (if has-balance :tag :default)
:on-press #(on-account-press (:address item) network-details)
:state (if has-balance :default :disabled)
:token-props {:symbol (:asset-pay-symbol item)
:value (:asset-pay-balance item)}
{:on-press #(on-account-press (:address item) network-details)
:account-props (assoc item
:address (:formatted-address item)
:address transformed-address
:full-address? true)}]))
(defn view
[]
(let [token-symbol (rf/sub [:wallet/send-token-symbol])
token (rf/sub [:wallet/token-by-symbol token-symbol])
accounts (rf/sub [:wallet/accounts-with-balances token])
(let [accounts (rf/sub [:wallet/accounts-with-current-asset])
network-details (rf/sub [:wallet/network-details])]
(hot-reload/use-safe-unmount on-close)
[floating-button-page/view
{:footer-container-padding 0
:header [account-switcher/view
@@ -30,16 +30,19 @@
(let [amount (rf/sub [:wallet/send-total-amount-formatted])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
{:keys [full-name]} (rf/sub [:wallet/bridge-to-network-details])]
(when (= tx-type :tx/bridge)
[quo/data-item
{:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:subtitle amount
:on-press show-bonder-fee-info}])))
[quo/data-item
(cond-> {:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/recipient-gets)
:subtitle amount}
(= tx-type :tx/bridge)
(assoc
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:on-press show-bonder-fee-info))]))
(defn view
[{:keys [loading-routes? fees]}]
@@ -59,6 +62,6 @@
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/max-fees)
:title (i18n/label :t/fees)
:subtitle fees}]
[received-amount {:loading-routes? loading-routes?}]])
@@ -25,9 +25,10 @@
:height 40})
(def fees-data-item
{:flex 1
:height 40
:background-color :transparent})
{:flex 1
:height 40
:margin-horizontal 16
:background-color :transparent})
(def amount-data-item
{:flex 1
@@ -2,6 +2,7 @@
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
@@ -14,18 +15,29 @@
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.contexts.wallet.send.routes.view :as routes]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
[status-im.setup.hot-reload :as hot-reload]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- every-network-value-is-zero?
[sender-network-values]
(every? (fn [{:keys [total-amount]}]
(and
total-amount
(money/bignumber? total-amount)
(money/equal-to total-amount
(money/bignumber "0"))))
sender-network-values))
(defn select-asset-bottom-sheet
[clear-input!]
(let [{preselected-token-symbol :symbol} (rf/sub [:wallet/wallet-send-token])]
[:<> ;; Need to be a `:<>` to keep `asset-list` scrollable.
[quo/drawer-top
{:title (i18n/label :t/select-token)
{:title (i18n/label :t/select-asset)
:container-style {:padding-bottom 8}}]
[asset-list/view
{:content-container-style {:padding-horizontal 8
@@ -35,6 +47,37 @@
(rf/dispatch [:wallet/edit-token-to-send token])
(clear-input!))}]]))
(defn- token-not-available
[token-symbol receiver-networks token-networks]
(let [theme (quo.theme/use-theme)
add-token-networks (fn []
(let [chain-ids (concat receiver-networks
(mapv #(:chain-id %) token-networks))]
(rf/dispatch [:wallet/update-receiver-networks chain-ids])))]
[rn/view {:style (style/token-not-available-container theme)}
[rn/view
[quo/icon :i/alert
{:size 16
:color (colors/resolve-color :danger theme)}]]
[rn/view {:style style/token-not-available-content-container}
[quo/text
{:style (style/token-not-available-text theme)
:size :paragraph-2}
(i18n/label :t/token-not-available-on-receiver-networks {:token-symbol token-symbol})]
[quo/button
{:size 24
:customization-color (colors/resolve-color :danger theme)
:on-press add-token-networks}
(i18n/label :t/add-networks-token-can-be-sent-to {:token-symbol token-symbol})]]]))
(defn- show-unpreferred-networks-alert
[on-confirm]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[unpreferred-networks-alert/view
{:on-confirm on-confirm}])}]))
(defn- no-routes-found
[]
[rn/view {:style style/no-routes-found-container}
@@ -76,7 +119,7 @@
(defn- insufficient-asset-amount?
[{:keys [token-symbol owned-eth-token input-state no-routes-found? limit-exceeded?
enough-assets?]}]
sender-network-values enough-assets?]}]
(let [eth-selected? (= token-symbol (string/upper-case constants/mainnet-short-name))
zero-owned-eth? (money/equal-to (:total-balance owned-eth-token) 0)
input-at-max-owned-amount? (money/equal-to
@@ -86,6 +129,7 @@
input-at-max-owned-amount?
zero-owned-eth?)]
(and (or no-routes-found? limit-exceeded?)
(seq sender-network-values)
(or exceeded-input? (not enough-assets?)))))
(defn view
@@ -98,104 +142,122 @@
button-one-props :button-one-props
current-screen-id :current-screen-id
initial-crypto-currency? :initial-crypto-currency?
enabled-from-chain-ids :enabled-from-chain-ids
from-enabled-networks :from-enabled-networks
:or {initial-crypto-currency? true}}]
(let [view-id (rf/sub [:view-id])
{fiat-currency :currency} (rf/sub [:profile/profile])
currency (rf/sub [:profile/currency])
{token-symbol :symbol
:as token} (rf/sub [:wallet/wallet-send-token])
network (rf/sub [:wallet/send-network])
{:keys [total-balance]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
[(:chain-id network)]])
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
[(:chain-id network)]])
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
(let [view-id (rf/sub [:view-id])
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
[crypto-currency?
set-crypto-currency] (rn/use-state initial-crypto-currency?)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
token-balance (or default-limit-crypto total-balance)
prices-per-token (rf/sub [:wallet/prices-per-token])
usd-conversion-rate (utils/token-usd-price token prices-per-token)
conversion-rate (utils/token-price-by-symbol prices-per-token
token-symbol
currency)
token-decimals (rf/sub [:wallet/send-display-token-decimals])
clear-input! #(set-input-state controlled-input/delete-all)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(rf/sub [:wallet/send-amount-fixed
(/ input-value conversion-rate)]))
routes-request-uuid (when suggested-routes
(:uuid suggested-routes))
no-routes-found? (and
routes-request-uuid
(empty? route)
(not loading-routes?))
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:no-routes-found? no-routes-found?
:limit-exceeded? limit-exceeded?})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
on-confirm (or default-on-confirm handle-on-confirm)
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? route))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
set-crypto-currency] (rn/use-state initial-crypto-currency?)
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
token-networks :networks
:as token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
{:keys [total-balance]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
prices-per-token (rf/sub [:wallet/prices-per-token])
usd-conversion-rate (utils/token-usd-price token prices-per-token)
currency (rf/sub [:profile/currency])
token-decimals (rf/sub [:wallet/send-display-token-decimals])
conversion-rate (utils/token-price-by-symbol prices-per-token
token-symbol
currency)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(rf/sub [:wallet/send-amount-fixed
(/ input-value conversion-rate)]))
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
unsupported-token-in-receiver? (and (not= tx-type :tx/bridge)
(->> receiver-network-values
(remove #(= (:type %) :add))
(every? #(= (:type %) :not-available))))
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
routes (when suggested-routes
(or (:best suggested-routes) []))
no-routes-found? (and
(every-network-value-is-zero? sender-network-values)
(some? routes)
(not loading-routes?)
(not unsupported-token-in-receiver?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub [:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-network? (set receiver-preferred-networks)
sending-to-unpreferred-networks? (some (comp not receiver-preferred-network?)
receiver-networks)
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:no-routes-found? no-routes-found?
:limit-exceeded? limit-exceeded?
:sender-network-values sender-network-values})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? routes))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
(rn/use-effect
(fn []
(when active-screen?
@@ -225,9 +287,8 @@
[current-address])
(rn/use-effect
(fn []
(when active-screen?
(request-fetch-routes 2000)))
[amount-in-crypto valid-input?])
(request-fetch-routes 0))
[send-from-locked-amounts])
[rn/view
{:style style/screen
:accessibility-label (str "container"
@@ -255,7 +316,7 @@
conversion-rate)
conversion-rate))
:hint-component [quo/network-tags
{:networks (seq [network])
{:networks (seq from-enabled-networks)
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
@@ -267,9 +328,17 @@
(controlled-input/upper-limit-bn
input-state)))})
:status (when (controlled-input/input-error input-state) :error)}]}]
(if (= tx-type :tx/bridge)
[routes/view {:token token-by-symbol}]
[rn/view {:style {:flex 1}}])
[routes/view
{:token token-by-symbol
:input-value input-value
:valid-input? valid-input?
:token-not-supported-in-receiver-networks? unsupported-token-in-receiver?
:current-screen-id current-screen-id
:request-fetch-routes request-fetch-routes}]
(when (and (not loading-routes?)
sender-network-values
unsupported-token-in-receiver?)
[token-not-available token-symbol receiver-networks token-networks])
(when not-enough-asset?
[not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route))
@@ -290,10 +359,14 @@
loading-routes?
(and (not should-try-again?)
confirm-disabled?))
:on-press (if should-try-again?
:on-press (cond
should-try-again?
#(rf/dispatch [:wallet/start-get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount-in-crypto))
:customization-color current-color}
(when should-try-again?
@@ -34,3 +34,19 @@
:top margin-top}
inverted?
(assoc :transform [{:scaleY -1}])))
(def disclaimer
{:margin-horizontal 20
:margin-top 20
:margin-bottom 8})
(def input-container
{:margin-top 8
:margin-bottom 12})
(defn keyboard-container
[bottom]
{:padding-bottom bottom})
(def error-box
{:margin-horizontal 20})
@@ -1,23 +1,87 @@
(ns status-im.contexts.wallet.send.routes.view
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.routes.style :as style]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def row-height 44)
(def space-between-rows 11)
(def network-link-linear-height 10)
(def network-link-1x-height 56)
(def network-link-2x-height 111)
(defn- open-preferences
[]
(rf/dispatch
[:show-bottom-sheet
{:content
(fn []
(let [receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub
[:wallet/wallet-send-receiver-preferred-networks])
{token-symbol :symbol
token-networks :supported-networks} (rf/sub [:wallet/wallet-send-token])
token-chain-ids-set (set (mapv #(:chain-id %) token-networks))
[selected-receiver-networks
set-selected-receiver-networks] (rn/use-state receiver-networks)
receiver-preferred-networks-set (set receiver-preferred-networks)
receiver-selected-preferred-networks (filter #(contains?
receiver-preferred-networks-set
%)
selected-receiver-networks)
receiver-selected-non-preferred-networks (filter #(not (contains?
receiver-preferred-networks-set
%))
selected-receiver-networks)
not-available-preferred-networks (filter (fn [preferred-chain-id]
(not (contains? token-chain-ids-set
preferred-chain-id)))
receiver-selected-preferred-networks)
not-available-non-preferred-networks (filter (fn [preferred-chain-id]
(not (contains?
token-chain-ids-set
preferred-chain-id)))
receiver-selected-non-preferred-networks)
first-section-warning-label (when (not-empty not-available-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-preferred-networks)}))
second-section-warning-label (when (not-empty
not-available-non-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-non-preferred-networks)}))]
[network-preferences/view
{:title (i18n/label :t/edit-receiver-networks)
:first-section-label (i18n/label :t/preferred-by-receiver)
:second-section-label (i18n/label :t/not-preferred-by-receiver)
:selected-networks (set (map network-utils/id->network
receiver-networks))
:receiver-preferred-networks receiver-preferred-networks
:button-label (i18n/label :t/apply-changes)
:first-section-warning-label first-section-warning-label
:second-section-warning-label second-section-warning-label
:on-change #(set-selected-receiver-networks %)
:on-save (fn [chain-ids]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}]))
(defn render-network-values
[{:keys [network-values token-symbol receiver? loading-routes?
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[rn/view
(doall
@@ -32,19 +96,27 @@
(= network-value-type :not-available)
:disabled
:else network-value-type)
amount-formatted (-> total-amount
(common-utils/sanitized-token-amount-to-display
constants/min-token-decimals-to-display)
amount-formatted (-> (rf/sub [:wallet/send-amount-fixed total-amount])
(str " " token-symbol))]
[rn/view
{:key (str (if receiver? "to" "from") "-" chain-id)
:style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge
{:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available)
amount-formatted)
:network (network-utils/id->network chain-id)
:status status}]]))
{:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available)
amount-formatted)
:network (network-utils/id->network chain-id)
:status status
:on-press #(when (not loading-routes?)
(cond
(= network-value-type :edit)
(open-preferences)
on-press (on-press chain-id total-amount)))
:on-long-press #(when (and (not loading-routes?) (not= status :disabled))
(cond
(= network-value-type :add)
(open-preferences)
on-long-press (on-long-press chain-id total-amount)))}]]))
network-values))])
(defn render-network-links
@@ -84,15 +156,59 @@
:destination destination}]]]))
network-links)])
(defn disable-chain
[chain-id disabled-from-chain-ids token-available-networks-for-suggested-routes]
(let [disabled-chain-ids
(if (contains? (set
disabled-from-chain-ids)
chain-id)
(vec (remove #(= % chain-id)
disabled-from-chain-ids))
(conj disabled-from-chain-ids
chain-id))
re-enabling-chain?
(< (count disabled-chain-ids)
(count disabled-from-chain-ids))]
(if (or re-enabling-chain?
(> (count token-available-networks-for-suggested-routes) 1))
(rf/dispatch [:wallet/disable-from-networks
disabled-chain-ids])
(rf/dispatch [:toasts/upsert
{:id :disable-chain-error
:type :negative
:text (i18n/label :t/at-least-one-network-must-be-activated)}]))))
(defn view
[{:keys [token]}]
(let [theme (quo.theme/use-theme)
token-symbol (:symbol token)
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
network-links (rf/sub [:wallet/wallet-send-network-links])
show-routes? (not-empty sender-network-values)]
[{:keys [token theme valid-input? request-fetch-routes on-press-to-network current-screen-id
token-not-supported-in-receiver-networks? input-value]}]
(let [token-symbol (:symbol token)
nav-current-screen-id (rf/sub [:view-id])
active-screen? (= nav-current-screen-id current-screen-id)
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub
[:wallet/wallet-send-receiver-network-values])
network-links (rf/sub [:wallet/wallet-send-network-links])
disabled-from-chain-ids (rf/sub
[:wallet/wallet-send-disabled-from-chain-ids])
{token-balances-per-chain :balances-per-chain} (rf/sub [:wallet/wallet-send-token])
token-available-networks-for-suggested-routes
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain token-balances-per-chain
:disabled-chain-ids disabled-from-chain-ids})
show-routes? (not-empty sender-network-values)]
(rn/use-effect
(fn []
(when (and active-screen?
(> (count token-available-networks-for-suggested-routes) 0))
(request-fetch-routes 2000)))
[input-value valid-input?])
(rn/use-effect
#(when (and active-screen? (> (count token-available-networks-for-suggested-routes) 0))
(request-fetch-routes 0))
[disabled-from-chain-ids])
[rn/scroll-view {:content-container-style style/routes-container}
(when show-routes?
[rn/view {:style style/routes-header-container}
@@ -104,18 +220,27 @@
:container-style style/section-label-right}]])
[rn/view {:style style/routes-inner-container}
[render-network-values
{:token-symbol token-symbol
:network-values sender-network-values
:receiver? false
:theme theme
:loading-routes? loading-routes?}]
{:token-symbol token-symbol
:network-values sender-network-values
:on-press (fn [chain-id-to-disable]
(disable-chain
chain-id-to-disable
disabled-from-chain-ids
token-available-networks-for-suggested-routes))
:receiver? false
:theme theme
:loading-routes? loading-routes?
:token-not-supported-in-receiver-networks? false}]
[render-network-links
{:network-links network-links
:sender-network-values sender-network-values}]
[render-network-values
{:token-symbol token-symbol
:network-values receiver-network-values
:receiver? true
:loading-routes? loading-routes?
:theme theme}]]]))
{:token-symbol token-symbol
:network-values receiver-network-values
:on-press on-press-to-network
:receiver? true
:loading-routes? loading-routes?
:theme theme
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:on-save #(request-fetch-routes 0)}]]]))
@@ -78,7 +78,7 @@
:on-press on-close
:switcher-type :select-account}]
[quo/page-top
{:title (i18n/label :t/select-token)
{:title (i18n/label :t/select-asset)
:title-accessibility-label :title-label}]
[quo/segmented-control
{:size 32
+38 -11
View File
@@ -229,17 +229,44 @@
safe-add-type-edit)))
(defn loading-network-amounts
[{:keys [networks values receiver?]}]
(->> networks
(map
(fn [chain-id]
(let [network-value (when values (get values chain-id))]
(cond-> {:chain-id chain-id
:type (if network-value :default :loading)}
network-value (assoc :total-amount
(money/bignumber network-value))
(and (not network-value) (not receiver?)) (assoc :total-amount (money/bignumber "0"))))))
(vec)))
[{:keys [valid-networks disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?]}]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
token-networks-ids-set (set token-networks-ids)
valid-networks-set (set valid-networks)
not-available-networks (if receiver?
(filter #(not (token-networks-ids-set %)) receiver-networks)
[])
not-available-networks-set (set not-available-networks)
valid-networks (-> (concat valid-networks
(when (not (and receiver? (= tx-type :tx/bridge)))
disabled-chain-ids)
(when receiver?
(filter #(not (valid-networks-set %))
not-available-networks)))
(distinct))]
(->> valid-networks
(map
(fn [chain-id]
(cond->
{:chain-id chain-id
:type (cond
(contains? not-available-networks-set chain-id) :not-available
(or receiver?
(not (contains? disabled-set chain-id))) :loading
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}
(and (not receiver?) (contains? disabled-set chain-id))
(assoc :total-amount (money/bignumber "0")))))
(filter
(fn [network-amount]
(or (and receiver?
(or (= tx-type :tx/bridge)
(contains? receiver-networks-set (:chain-id network-amount))))
(not receiver?))))
(sort-by (fn [network-amount]
(get network-priority-score
(network-utils/id->network (:chain-id network-amount)))))
(vec))))
(defn network-links
[route from-values-by-chain to-values-by-chain]
@@ -8,16 +8,12 @@
[utils.re-frame :as rf]))
(defn- network-item
[{:keys [network on-select-network source]}]
[{:keys [network on-select-network]}]
(let [{:keys [network-name
chain-id]} network
chain-id]} network
{balance-in-crypto :crypto
balance-in-fiat :fiat} (case source
:swap
(rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
(rf/sub [:wallet/send-token-network-balance chain-id]))
mainnet?
(= network-name constants/mainnet-network-name)]
balance-in-fiat :fiat} (rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
mainnet? (= network-name constants/mainnet-network-name)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network network-name)
@@ -27,28 +23,21 @@
:container-style (style/network-list-container mainnet?)}]))
(defn view
[{:keys [token-symbol on-select-network source title]
:or {source :swap
title (i18n/label :t/select-network)}}]
(let [{mainnet-network :mainnet-network
layer-2-networks :layer-2-networks} (case source
:swap
(rf/sub [:wallet/swap-asset-to-pay-networks])
:send
(rf/sub [:wallet/send-token-grouped-networks])
:bridge
(rf/sub [:wallet/send-token-grouped-networks]))
[{:keys [on-select-network]}]
(let [token-symbol (rf/sub [:wallet/swap-asset-to-pay-token-symbol])
{mainnet-network :mainnet-network
layer-2-networks :layer-2-networks} (rf/sub [:wallet/swap-asset-to-pay-networks])
render-fn (rn/use-callback (fn [network]
[network-item
{:network network
:on-select-network on-select-network
:source source}]))]
{:network network
:on-select-network
on-select-network}]))]
[:<>
[rn/view {:style style/header-container}
[quo/text
{:size :heading-2
:weight :semi-bold}
title]
(i18n/label :t/select-network)]
[quo/context-tag
{:type :token
:size 24
@@ -57,8 +46,7 @@
(when mainnet-network
[network-item
{:network mainnet-network
:on-select-network on-select-network
:source source}])
:on-select-network on-select-network}])
[quo/divider-label {:container-style style/divider-label}
(i18n/label :t/layer-2)]
[rn/flat-list
@@ -34,8 +34,7 @@
(defn view
[]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
accounts (rf/sub [:wallet/accounts-with-balances asset-to-pay])]
(let [accounts (rf/sub [:wallet/accounts-with-balances])]
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav
+1
View File
@@ -2,6 +2,7 @@
(:require
status-im.common.alert-banner.events
status-im.common.alert.effects
status-im.common.app-monitoring.events
status-im.common.async-storage.effects
status-im.common.emoji-picker.events
status-im.common.font.events
+1
View File
@@ -11,6 +11,7 @@
(def ^:private initial-flags
{::community.edit-account-selection (enabled-in-env? :FLAG_EDIT_ACCOUNT_SELECTION_ENABLED)
::app-monitoring.intentional-crash true ; (enabled-in-env? :FLAG_INTENTIONAL_CRASH_ENABLED)
;; 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
+12
View File
@@ -43,6 +43,7 @@
[status-im.contexts.keycard.pin.enter.view :as keycard.pin.enter]
[status-im.contexts.onboarding.create-or-sync-profile.view :as create-or-sync-profile]
[status-im.contexts.onboarding.create-password.view :as create-password]
[status-im.contexts.onboarding.create-profile.view :as create-profile]
[status-im.contexts.onboarding.enable-biometrics.view :as enable-biometrics]
[status-im.contexts.onboarding.enable-notifications.view :as enable-notifications]
[status-im.contexts.onboarding.identifiers.view :as identifiers]
@@ -727,6 +728,16 @@
:modalPresentationStyle :overCurrentContext}
:component create-or-sync-profile/sync-or-recover-profile})
(def onboarding-create-profile
{:name :screen/onboarding.create-profile
:metrics {:track? true
:alias-id :onboarding.create-profile-info}
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations transitions/push-animations-for-transparent-background
:popGesture false}
:component create-profile/create-profile})
(def onboarding-create-profile-password
{:name :screen/onboarding.create-profile-password
:metrics {:track? true
@@ -861,6 +872,7 @@
[onboarding-intro
onboarding-new-to-status
onboarding-sync-or-recover-profile
onboarding-create-profile
onboarding-create-profile-password
onboarding-enable-biometrics
onboarding-preparing-status
+3 -3
View File
@@ -56,12 +56,12 @@
(re-frame/reg-sub
:wallet/network-values
:<- [:wallet/wallet-send]
(fn [{:keys [from-values-by-chain to-values-by-chain token-display-name token] :as send-data}
:<- [:wallet/send-display-token-decimals]
(fn [[{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} token-decimals]
[_ to-values?]]
(let [network-values (if to-values? to-values-by-chain from-values-by-chain)
token-symbol (or token-display-name
(-> send-data :token :symbol))
token-decimals (:decimals token)]
(-> send-data :token :symbol))]
(reduce-kv
(fn [acc chain-id amount]
(let [network-name (network-utils/id->network chain-id)
+2 -48
View File
@@ -2,7 +2,7 @@
(:require
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-tab-constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money]
@@ -34,11 +34,6 @@
:<- [:wallet/wallet-send]
:-> :token)
(rf/reg-sub
:wallet/send-token-symbol
:<- [:wallet/wallet-send]
:-> :token-symbol)
(rf/reg-sub
:wallet/send-transaction-ids
:<- [:wallet/wallet-send]
@@ -59,11 +54,6 @@
:<- [:wallet/wallet-send]
:-> :tx-type)
(rf/reg-sub
:wallet/send-network
:<- [:wallet/wallet-send]
:-> :network)
(rf/reg-sub
:wallet/sending-collectible?
:<- [:wallet/send-tx-type]
@@ -87,7 +77,7 @@
(->> address-activity
(sort :timestamp)
(keep (fn [{:keys [activity-type recipient]}]
(when (= activity-tab-constants/wallet-activity-type-send activity-type)
(when (= activity-constants/wallet-activity-type-send activity-type)
recipient)))
(distinct)))))
@@ -183,39 +173,3 @@
(= (:chain-id network) bridge-to-chain-id)
network))
networks))))
(rf/reg-sub
:wallet/send-token-grouped-networks
:<- [:wallet/wallet-send-token]
(fn [token]
(let [{token-networks :networks} token
grouped-networks (group-by :layer
token-networks)
mainnet-network (first (get grouped-networks constants/layer-1-network))
layer-2-networks (get grouped-networks constants/layer-2-network)]
{:mainnet-network mainnet-network
:layer-2-networks layer-2-networks})))
(rf/reg-sub
:wallet/send-token-network-balance
:<- [:wallet/wallet-send-token]
:<- [:profile/currency]
:<- [:profile/currency-symbol]
:<- [:wallet/prices-per-token]
(fn [[token currency currency-symbol prices-per-token] [_ chain-id]]
(let [{:keys [balances-per-chain
decimals]} token
balance-for-chain (get balances-per-chain chain-id)
total-balance (money/token->unit (:raw-balance balance-for-chain) decimals)
fiat-value (common-utils/calculate-token-fiat-value
{:currency currency
:balance total-balance
:token token
:prices-per-token prices-per-token})
crypto-formatted (common-utils/get-standard-crypto-format token
total-balance
prices-per-token)
fiat-formatted (common-utils/fiat-formatted-for-ui currency-symbol
fiat-value)]
{:crypto (str crypto-formatted " " (:symbol token))
:fiat fiat-formatted})))
+21
View File
@@ -334,3 +334,24 @@
:token token-for-fees
:prices-per-token prices-per-token})]
fee-in-fiat)))
(rf/reg-sub
:wallet/accounts-with-balances
:<- [:wallet/operable-accounts]
:<- [:wallet/swap-asset-to-pay]
(fn [[accounts asset-to-pay]]
(let [token-symbol (:symbol asset-to-pay)]
(map
(fn [account]
(let [tokens (:tokens account)
filtered-tokens (filter #(= (:symbol %) token-symbol) tokens)
asset-pay-balance (utils/calculate-total-token-balance filtered-tokens)
formatted-address (network-utils/format-address (:address account)
(:network-preferences-names account))]
(assoc account
:formatted-address formatted-address
:asset-pay-balance (utils/sanitized-token-amount-to-display
asset-pay-balance
constants/min-token-decimals-to-display)
:asset-pay-symbol token-symbol)))
accounts))))
+7 -25
View File
@@ -711,11 +711,13 @@
(rf/reg-sub
:wallet/zero-balance-in-all-non-watched-accounts?
:<- [:wallet/aggregated-tokens]
(fn [aggregated-tokens]
(let [zero-balance? (->> aggregated-tokens
(mapcat (comp vals :balances-per-chain))
(every? #(money/equal-to (:raw-balance %) 0)))]
(and (not-empty aggregated-tokens) zero-balance?))))
:<- [:profile/currency]
:<- [:wallet/prices-per-token]
(fn [[aggregated-tokens currency prices-per-token]]
(let [balance (utils/calculate-balance-from-tokens {:currency currency
:tokens aggregated-tokens
:prices-per-token prices-per-token})]
(and (not-empty aggregated-tokens) (money/equal-to balance 0)))))
(rf/reg-sub
:wallet/network-preference-details
@@ -901,23 +903,3 @@
:<- [:wallet/accounts-without-current-viewing-account]
(fn [accounts]
(get-emoji-and-colors-from-accounts accounts)))
(rf/reg-sub
:wallet/accounts-with-balances
:<- [:wallet/operable-accounts]
(fn [accounts [_ token]]
(let [token-symbol (:symbol token)]
(map
(fn [account]
(let [tokens (:tokens account)
filtered-tokens (filter #(= (:symbol %) token-symbol) tokens)
asset-pay-balance (utils/calculate-total-token-balance filtered-tokens)
formatted-address (network-utils/format-address (:address account)
(:network-preferences-names account))]
(assoc account
:formatted-address formatted-address
:asset-pay-balance (utils/sanitized-token-amount-to-display
asset-pay-balance
constants/min-token-decimals-to-display)
:asset-pay-symbol token-symbol)))
accounts))))
-125
View File
@@ -1008,128 +1008,3 @@
(update accounts "0x2" assoc :operable :partially)))
(is (true? (rf/sub [sub-name])))))
(h/deftest-sub :wallet/zero-balance-in-all-non-watched-accounts?
[sub-name]
(testing "returns true if the balance is zero in all non-watched accounts"
(swap! rf-db/app-db
#(assoc-in %
[:wallet :accounts]
{"0x1" {:address "0x1"
:watch-only? false
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}
"0x2" {:address "0x2"
:watch-only? false
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}
"0x3"
{:address "0x3"
:watch-only? true
:tokens [{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance
(money/bignumber
"2")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance
(money/bignumber
"1")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance
(money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance
(money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance
(money/bignumber
"2")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance
(money/bignumber
"0")
:has-error
false}}}]}}))
(is (true? (rf/sub [sub-name]))))
(testing "returns false if the balance is not zero in all non-watched accounts"
(swap! rf-db/app-db
#(assoc-in %
[:wallet :accounts]
{"0x1" {:address "0x1"
:watch-only? false
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"1")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}
"0x2" {:address "0x2"
:watch-only? true
:tokens
[{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"1")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}
{:balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}
constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber
"2")
:has-error false}
constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber
"0")
:has-error false}}}]}}))
(is (false? (rf/sub [sub-name])))))
+6 -9
View File
@@ -126,12 +126,9 @@
(def status
(clj->js
{:getNodeConfig
(fn [] (types/clj->json {:WakuV2Config ""}))
:addCentralizedMetric
(fn [_ callback]
(callback))
:fleets
(fn [] (.fleets native-status))
:startLocalNotifications
identity}))
{:intendedPanic identity
:getNodeConfig (fn [] (types/clj->json {:WakuV2Config ""}))
:addCentralizedMetric (fn [_ callback]
(callback))
:fleets (fn [] (.fleets native-status))
:startLocalNotifications identity}))
@@ -17,10 +17,11 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user,))))
self.username_1, self.username_2 = 'sender', 'receiver'
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = self.home_1.get_profile_view(), self.home_2.get_profile_view()
self.public_key_1 = self.home_1.get_public_key()
self.profile_link_2 = self.home_2.get_link_to_profile()
@@ -97,8 +98,8 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
def test_activity_center_contact_request_accept_swipe_mark_all_as_read(self):
self.device_2.just_fyi('Creating a new user on Device2')
self.home_2.reopen_app(sign_in=False)
self.device_2.create_user(first_user=False)
new_username = self.home_2.get_username()
new_username = "new user"
self.device_2.create_user(username=new_username, first_user=False)
self.device_2.just_fyi('Device2 sends a contact request to Device1 via Paste button and check user details')
self.home_2.chats_tab.click()
@@ -158,19 +159,20 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
self.username_2, self.profile_link_2, decoded_username))
public_key_2 = self.profile_link_2.split("#")[-1]
new_username_1 = "test user 123"
def _device_1_creates_user():
self.home_1.just_fyi("Device 1 creates a new user")
self.home_1.reopen_app(sign_in=False)
self.device_1.create_user(first_user=False)
return self.home_1.get_username()
self.device_1.create_user(username=new_username_1, first_user=False)
def _device_2_sign_in():
self.home_2.just_fyi("Device 2 sign in, user name is " + self.username_2)
self.home_2.reopen_app(sign_in=False)
self.device_2.sign_in(user_name=self.username_2)
new_username_1, _ = self.loop.run_until_complete(run_in_parallel(((_device_1_creates_user, {}),
(_device_2_sign_in, {}))))
self.loop.run_until_complete(run_in_parallel(((_device_1_creates_user, {}),
(_device_2_sign_in, {}))))
self.device_1.just_fyi('Device1 sends a contact request to Device2 using his profile link')
self.home_1.chats_tab.click()
@@ -240,11 +242,11 @@ class TestActivityMultipleDevicePR(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = 'user1', 'user2'
self.loop.run_until_complete(
run_in_parallel(((self.device_1.create_user,),
(self.device_2.create_user,))))
run_in_parallel(((self.device_1.create_user, {'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = self.home_1.get_profile_view(), self.home_2.get_profile_view()
self.public_key_2 = self.home_2.get_public_key()
self.home_2.navigate_back_to_home_view()
@@ -380,11 +382,11 @@ class TestActivityMultipleDevicePRTwo(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.username_1, self.username_2 = 'user1', 'user2'
self.loop.run_until_complete(
run_in_parallel(((self.device_1.create_user,),
(self.device_2.create_user,))))
run_in_parallel(((self.device_1.create_user, {'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = self.home_1.get_profile_view(), self.home_2.get_profile_view()
self.public_key_2 = self.home_2.get_public_key()
self.home_2.navigate_back_to_home_view()
@@ -20,11 +20,13 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user, {'enable_notifications': True}))))
self.username_1, self.username_2 = 'sender', 'receiver'
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'enable_notifications': True,
'username': self.username_2}))))
self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.homes = (self.home_1, self.home_2)
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = (home.get_profile_view() for home in self.homes)
self.public_key_2 = self.home_2.get_public_key()
@@ -541,11 +543,13 @@ class TestOneToOneChatMultipleSharedDevicesNewUiTwo(MultipleSharedDeviceTestCase
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user, {'enable_notifications': True}))))
self.username_1, self.username_2 = 'sender', 'receiver'
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'enable_notifications': True,
'username': self.username_2}))))
self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.homes = (self.home_1, self.home_2)
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.profile_1, self.profile_2 = (home.get_profile_view() for home in self.homes)
self.public_key_2 = self.home_2.get_public_key()
@@ -21,25 +21,17 @@ class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
self.message_to_admin = 'Hey, admin!'
self.public_keys, self.usernames, self.chats = {}, {}, {}
self.sign_in_views = [SignInView(self.drivers[key]) for key in self.drivers]
self.usernames = ('user admin', 'member_1', 'member_2')
self.loop.run_until_complete(
run_in_parallel(
(
(self.sign_in_views[0].create_user, {'enable_notifications': True}),
(self.sign_in_views[1].create_user, {'enable_notifications': True}),
(self.sign_in_views[2].create_user, {'enable_notifications': True})
(self.sign_in_views[0].create_user, {'enable_notifications': True, 'username': self.usernames[0]}),
(self.sign_in_views[1].create_user, {'enable_notifications': True, 'username': self.usernames[1]}),
(self.sign_in_views[2].create_user, {'enable_notifications': True, 'username': self.usernames[2]})
)
)
)
self.homes = [sign_in.get_home_view() for sign_in in self.sign_in_views]
self.usernames = self.loop.run_until_complete(
run_in_parallel(
(
(self.homes[0].get_username,),
(self.homes[1].get_username,),
(self.homes[2].get_username,)
)
)
)
self.public_keys = self.loop.run_until_complete(
run_in_parallel(
(
@@ -21,9 +21,9 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(1)
self.sign_in = SignInView(self.drivers[0])
self.username = 'first user'
self.home = self.sign_in.create_user()
self.username = self.home.get_username()
self.home = self.sign_in.create_user(username=self.username)
self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button)
self.community_name = "closed community"
self.channel_name = "cats"
@@ -158,8 +158,8 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
def test_restore_multiaccount_with_waku_backup_remove_profile_switch(self):
self.home.reopen_app(sign_in=False)
self.home.just_fyi("Restore user with predefined communities and contacts")
self.sign_in.recover_access(passphrase=waku_user.seed, second_user=True)
recover_user_name = self.home.get_username()
recover_user_name = 'Recover user'
self.sign_in.recover_access(passphrase=waku_user.seed, second_user=True, username=recover_user_name)
self.home.just_fyi("Check contacts/blocked users")
self.home.chats_tab.click()
@@ -306,10 +306,11 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user,))))
self.username_1, self.username_2 = "user_1", "user_2"
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'username': self.username_2}))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.public_key_2 = self.home_2.get_public_key()
self.profile_1 = self.home_1.get_profile_view()
[home.navigate_back_to_home_view() for home in self.homes]
@@ -835,10 +836,12 @@ class TestCommunityMultipleDeviceMergedTwo(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(2)
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True}),
(self.device_2.create_user, {'enable_notifications': True}))))
self.username_1, self.username_2 = "user_1", "user_2"
self.loop.run_until_complete(run_in_parallel(((self.device_1.create_user, {'enable_notifications': True,
'username': self.username_1}),
(self.device_2.create_user, {'enable_notifications': True,
'username': self.username_2}))))
self.homes = self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
self.username_1, self.username_2 = self.home_1.get_username(), self.home_2.get_username()
self.public_key_2 = self.home_2.get_public_key()
self.profile_1 = self.home_1.get_profile_view()
[home.navigate_back_to_home_view() for home in self.homes]
@@ -13,9 +13,9 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
self.drivers, self.loop = create_shared_drivers(1)
self.driver = self.drivers[0]
self.sign_in = SignInView(self.driver)
self.username = 'test user'
self.home = self.sign_in.create_user()
self.username = self.home.get_username()
self.home = self.sign_in.create_user(username=self.username)
self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button)
self.open_community_name = "open community"
self.channel_name = "general"
+3 -3
View File
@@ -20,10 +20,10 @@ class TestFallbackMultipleDevice(MultipleSharedDeviceTestCase):
self.sign_in_3.get_home_view()
self.sign_in_1.just_fyi("Device 1: create a new user")
self.sign_in_3.just_fyi("Device 3: create a new user")
self.user_name_1, self.user_name_3 = 'first user', 'third user'
self.loop.run_until_complete(
run_in_parallel(((self.sign_in_1.create_user,),
(self.sign_in_3.create_user,))))
self.user_name_1, self.user_name_3 = self.home_1.get_username(), self.home_3.get_username()
run_in_parallel(((self.sign_in_1.create_user, {'username': self.user_name_1}),
(self.sign_in_3.create_user, {'username': self.user_name_3}))))
self.profile_1 = self.home_1.profile_button.click()
self.profile_2 = self.home_2.get_profile_view()
self.sign_in_3.just_fyi("Device 3: get public key")
+7 -5
View File
@@ -24,11 +24,13 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
self.sender, self.receiver = transaction_senders['ETH_1'], transaction_senders['ETH_2']
self.sender['wallet_address'] = '0x' + self.sender['address']
self.receiver['wallet_address'] = '0x' + self.receiver['address']
self.sender_username, self.receiver_username = 'sender', 'receiver'
self.loop.run_until_complete(
run_in_parallel(((self.sign_in_1.recover_access, {'passphrase': self.sender['passphrase']}),
(self.sign_in_2.recover_access, {'passphrase': self.receiver['passphrase']}))))
run_in_parallel(((self.sign_in_1.recover_access, {'passphrase': self.sender['passphrase'],
'username': self.sender_username}),
(self.sign_in_2.recover_access, {'passphrase': self.receiver['passphrase'],
'username': self.receiver_username}))))
self.home_1, self.home_2 = self.sign_in_1.get_home_view(), self.sign_in_2.get_home_view()
self.sender_username, self.receiver_username = self.home_1.get_username(), self.home_2.get_username()
self.wallet_1, self.wallet_2 = self.sign_in_1.get_wallet_view(), self.sign_in_2.get_wallet_view()
self.wallet_1.wallet_tab.click()
self.wallet_2.wallet_tab.click()
@@ -204,10 +206,10 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
self.arb_balance = {'Ether': 0.0001, 'USDCoin': 0.0, 'Status': 0.0, 'Uniswap': 0.5, 'Dai Stablecoin': 0.0}
self.sender['wallet_address'] = '0x' + self.sender['address']
self.receiver['wallet_address'] = '0x' + self.receiver['address']
self.sign_in_view.recover_access(passphrase=self.sender['passphrase'])
self.sender_username, self.receiver_username = 'sender', 'receiver'
self.sign_in_view.recover_access(passphrase=self.sender['passphrase'], username=self.sender_username)
self.home_view = self.sign_in_view.get_home_view()
self.sender_username = self.home_view.get_username()
self.wallet_view = self.home_view.wallet_tab.click()
@marks.testrail_id(740490)
-8
View File
@@ -624,11 +624,3 @@ class HomeView(BaseView):
element = Text(self.driver, accessibility_id='new-device-installation-id')
element.wait_for_visibility_of_element()
return element.text
def get_username(self):
profile_view = self.get_profile_view()
profile_view = self.profile_button.click_until_presence_of_element(profile_view.default_username_text)
profile_view.default_username_text.wait_for_element(3)
username = profile_view.default_username_text.text
profile_view.click_system_back_button()
return username
+1 -1
View File
@@ -179,7 +179,7 @@ class ProfileView(BaseView):
# Header
self.public_key_text = Text(self.driver, accessibility_id="chat-key")
self.default_username_text = Text(self.driver, accessibility_id="username")
self.default_username_text = Text(self.driver, accessibility_id="default-username")
self.contact_name_text = Text(self.driver, accessibility_id="contact-name")
self.share_my_profile_button = Button(self.driver, accessibility_id="share-header-button")
self.profile_picture = ProfilePictureElement(self.driver)
+8 -3
View File
@@ -211,7 +211,7 @@ class SignInView(BaseView):
self.profile_continue_button = Button(self.driver, accessibility_id="submit-create-profile-button")
self.profile_password_edit_box = EditBox(self.driver, translation_id="password-creation-placeholder-1")
self.profile_repeat_password_edit_box = EditBox(self.driver, translation_id="password-creation-placeholder-2")
self.profile_confirm_password_button = Button(self.driver, accessibility_id="Confirm password")
self.profile_confirm_password_button = Button(self.driver, translation_id="password-creation-confirm")
self.enable_biometric_maybe_later_button = Button(self.driver, translation_id="maybe-later")
self.identifiers_button = Button(self.driver, accessibility_id="skip-identifiers")
self.start_button = Button(self.driver, accessibility_id="welcome-button")
@@ -228,6 +228,8 @@ class SignInView(BaseView):
input_elements[0].send_keys(password)
input_elements[1].click()
input_elements[1].send_keys(password)
self.checkbox_button.scroll_to_element()
self.checkbox_button.click()
self.profile_confirm_password_button.click()
def set_profile(self, username: str, set_image=False):
@@ -236,7 +238,7 @@ class SignInView(BaseView):
if set_image:
pass
def create_user(self, password=common_password, first_user=True, enable_notifications=False):
def create_user(self, password=common_password, username="test user", first_user=True, enable_notifications=False):
self.driver.info("## Creating new multiaccount with password:'%s'" % password, device=False)
if first_user:
self.create_profile_button.click_until_presence_of_element(self.start_fresh_lets_go_button)
@@ -247,6 +249,7 @@ class SignInView(BaseView):
self.plus_profiles_button.click()
self.create_new_profile_button.click()
self.start_fresh_lets_go_button.click_until_presence_of_element(self.profile_title_input)
self.set_profile(username)
self.set_password(password)
self.chats_tab.wait_for_visibility_of_element(30)
self.driver.info("## New multiaccount is created successfully!", device=False)
@@ -258,7 +261,7 @@ class SignInView(BaseView):
return home_view
def recover_access(self, passphrase: str, password: str = common_password, enable_notifications=False,
second_user=False, after_sync_code=False):
second_user=False, username='Restore user', set_image=False, after_sync_code=False):
self.driver.info("## Recover access (password:%s)" % password, device=False)
if not after_sync_code:
@@ -271,6 +274,8 @@ class SignInView(BaseView):
self.use_recovery_phrase_button.click()
self.passphrase_edit_box.send_keys(passphrase)
self.continue_button.click_until_presence_of_element(self.profile_title_input)
if not after_sync_code:
self.set_profile(username, set_image)
self.set_password(password)
self.chats_tab.wait_for_visibility_of_element(30)
self.driver.info("## Multiaccount is recovered successfully!", device=False)
+3 -8
View File
@@ -799,6 +799,7 @@
"do-not-cheat": "Don't try to cheat",
"do-not-cheat-description": "These 12 words give access to all of your funds so it is important that you write them in the correct order, take it seriously.",
"do-not-quit": "Do not quit the application or turn off your device. Doing so will lead to data corruption, loss of your Status profile and the inability to use Status.",
"do-not-share": "Do not share",
"done": "Done",
"dont-ask": "Don't ask me again",
@@ -1214,10 +1215,7 @@
"include": "Include",
"incoming": "Incoming",
"incoming-transaction": "Incoming transaction",
"incorrect-code": [
"str",
"Sorry the code was incorrect, please enter it again"
],
"incorrect-code": ["str", "Sorry the code was incorrect, please enter it again"],
"incorrect-private-key": "This is not the private key for this key pair",
"increase-gas": "Increase Gas",
"information-you-input-and-send": "Information you input and send",
@@ -1932,7 +1930,7 @@
"password-creation-max-length-hint": "Maximum 100 characters",
"password-creation-placeholder-1": "Type password",
"password-creation-placeholder-2": "Repeat password",
"password-creation-subtitle": "This password can't be recovered",
"password-creation-subtitle": "To log in to Status and sign transactions",
"password-creation-tips-1": "Lower case",
"password-creation-tips-2": "Upper case",
"password-creation-tips-3": "Numbers",
@@ -2282,11 +2280,8 @@
"select-chat": "Select chat to start messaging",
"select-network": "Select network",
"select-network-for-buying": "Select network for buying",
"select-network-to-bridge-from": "Select network to bridge from",
"select-network-to-receive": "Select network to receive",
"select-networks": "Select networks",
"select-new-location-for-keys": "Select a new location to save your private key(s)",
"select-token": "Select token",
"select-token-to-receive": "Select token to receive",
"select-token-to-swap": "Select token to Swap",
"selected": "Selected",