Compare commits

..
Author SHA1 Message Date
Brian Sztamfater 984bc74b6d lint fixes 2023-12-05 02:31:24 -03:00
Brian Sztamfater 17cd163f25 some cleanup 2023-12-05 02:23:06 -03:00
Brian Sztamfater f3c967f26b improve flow 2023-12-05 01:13:05 -03:00
Brian Sztamfater 9f78eaa91d integrate select input screen to the flow, also calculate routes prior showing transaction confirmation screen 2023-12-04 21:46:33 -03:00
Brian Sztamfater 778b06abb2 some refactor 2023-12-04 20:48:48 -03:00
Brian Sztamfater 68d7eddb4d only allow ETH transfers for now 2023-12-04 20:48:48 -03:00
Brian Sztamfater c7165cf078 calculate fiat balances correctly and some other fixes 2023-12-04 20:48:46 -03:00
Brian Sztamfater 50dcf9d9b9 goerli as default suggested routes network 2023-12-04 20:48:22 -03:00
Brian Sztamfater 372bf83da5 some polishing 2023-12-04 20:48:21 -03:00
Brian Sztamfater 710829d248 fix endpoints integration 2023-12-04 20:47:45 -03:00
Brian Sztamfater 23fae29f9d try basic integration 2023-12-04 20:47:42 -03:00
Brian Sztamfater daf3e52281 some cleanup 2023-12-04 20:46:45 -03:00
Jamie Caprani 3338a4b1a1 chore 2023-12-04 20:03:28 -03:00
Jamie Caprani 9ead91c383 progress 2023-12-04 20:02:09 -03:00
Ajay Sivan fb84885af0 Add Test Networks Enabled option in settings 2023-12-04 19:59:06 -03:00
Ulises M 4aac04e931 Add utils.money/add version able to work with nils 2023-12-04 19:59:05 -03:00
Brian Sztamfater 90166e8762 some refactor 2023-12-01 13:38:57 -03:00
Brian Sztamfater d68fdb18f4 adjustment to summary info component 2023-12-01 13:38:57 -03:00
Brian Sztamfater e46f4d9081 adjust text color 2023-12-01 13:38:56 -03:00
Brian Sztamfater dd2fd3410b some refactor 2023-12-01 13:38:56 -03:00
Brian Sztamfater 085e2df34c localize strings 2023-12-01 13:38:55 -03:00
Brian Sztamfater 51bfd3567c most of the ui in place, polishing pending 2023-12-01 13:38:54 -03:00
Brian Sztamfater fb6d907cc5 some progress 2023-12-01 13:38:54 -03:00
Brian Sztamfater a88c5b7bf7 feat: transaction confirmation page ui 2023-12-01 13:38:52 -03:00
Jamie Caprani f9f328107d chore: add ability to create watch address & some related ui changes on account page (#17868) 2023-12-01 04:29:01 -08:00
Yevheniia Berdnyk 04dee8be45 e2e: updated data for waku backup test 2023-12-01 12:28:26 +02:00
Alexander 52f7fc59ee Updated data-item, monospaced & aligned address (#18008)
* Upd

* Fix
2023-12-01 10:03:08 +01:00
Mohamed Javid b5202b4d28 [Feature] Added activity check on entered watch address (#17970)
This commit 
 - implements the activity check for the entered watch address.
 - added warning colors
 - fixes bottom inset in add address to watch screen

---------

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2023-12-01 00:28:09 +05:30
Andrea Maria Piana 210cf64ce0 Fix panic syncing community token gated
https://github.com/status-im/status-go/compare/8a4c2d8d...8206ffc7
2023-11-30 13:49:47 +00:00
Lungu Cristian ed270b2162 Enabling biometry without password during sync (#17960)
* feat: added migration for the keychain hashed password

* feat: added sync biometry without password entry

* fix: biometry typo from develop

* ref: moved migration side-effects outside the event

* ref: some renaming for keychain migration

* ref: addressed @cammellos' review comments

* ref: removed unnecessary anon fn

* fix: addressed @ilmotta's review comments

* ref: removed theme from enable-biometrics

* ref: addressed J-Son89's review comments

* test: added tests for mask-data and hash-masked-password

* test: added schema to hash-masked-password and fixed test

* fix: forgot the threading

* ref: improved the masked data schema

* fix: no biometry error when canceled by user

* fix: biometry error wasn't propagated during login

* fix: alert dismiss button not passed properly

* fix: show biometrics NOT_ENROLLED error only once

* lint: removed unused require
2023-11-30 13:40:29 +02:00
Andrea Maria Piana d412fa3c63 Set up skeleton for communities request to join 2023-11-30 09:49:43 +00:00
Yevheniia Berdnyk b7f675aa5c e2e: fix for deep links test 2023-11-30 01:09:42 +02:00
Volodymyr Kozieiev bfa23c182f Add requirement for component tests (#18015) 2023-11-29 18:50:49 +00:00
Mohsen 6b721265af [#17986] feat: implement new settings ui, header section (#17991) 2023-11-29 20:54:11 +03:00
Jamie CapraniandRende11 589a581298 chore(wallet): add ui for send page (#17786) - merged in (#18017)
---------
Co-authored-by: Rende11 <artamonovn@gmail.com>
2023-11-29 05:12:21 -08:00
Yevheniia Berdnyk 0546a87e9a e2e: deep links tests added 2023-11-29 14:09:31 +02:00
Parvesh Monu e5ce7fed3a fix position of the last item in the community chat list and the position of the jump-to button (#17999) 2023-11-29 16:45:30 +05:30
Siddarth Kumar 58d5c3d7a5 don't restrict diawi upload for iOS release builds (#18016)
This commit removes the restriction of uploading iOS release builds to Diawi

Its helpful to have the status app uploaded to Diawi to test release builds on our iPhones.

The installer is very straightforward and makes life easier when we want to try out release builds.
2023-11-28 22:46:58 +05:30
Ibrahem Khalil 6ccea1ddb2 Continue button is invisible behind keyboard in Add Address To Watch … (#17940) 2023-11-28 18:38:06 +02:00
Alex Tumanov a9a17aaae8 Implement dropdown-input based on the original dropdown component (#17927) 2023-11-28 15:21:15 +00:00
98 changed files with 2431 additions and 422 deletions
-1
View File
@@ -77,7 +77,6 @@ pipeline {
}
}
stage('Upload') {
when { expression { !utils.isReleaseBuild() } }
steps { script {
env.DIAWI_URL = ios.uploadToDiawi()
env.PKG_URL = env.DIAWI_URL
+1 -1
View File
@@ -716,7 +716,7 @@ src
- `src/js`: Raw Javascript files, e.g. React Native Reanimated worklets.
- `src/mocks`: Plumbing configuration to be able to run tests.
- `src/quo/`: The component library for Status Mobile.
- `src/quo/`: The component library for Status Mobile. [Read more...](../src/quo/README.md)
- `src/react_native/`: Contains only low-level constructs to help React Native
work in tandem with Clojure(Script).
- `src/status_im2/`: Directory where we try to be as strict as possible about
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

+11
View File
@@ -116,6 +116,17 @@ rendered. We use [React Native Testing Library](https://callstack.github.io/reac
There are dozens of examples in the repository, so use them as a reference. A
good and complete example is [quo.components.avatars.user-avatar.component-spec](/src/quo/components/avatars/user_avatar/component_spec.cljs)
### No-props test
When writing tests for the component that has props, please add one test that covers situation when props aren't passed. Because even if component not showing anything meaningful without props, it shouldn't crash.
```clojure
(h/describe "Transaction Progress"
(h/test "component renders without props"
(h/render [quo/transaction-progress {}])
(h/is-truthy (h/get-by-label-text :transaction-progress)))
```
## Do not couple the library with re-frame
Don't use re-frame inside this library (e.g. dispatch & subscribe). If a
@@ -30,6 +30,6 @@
{:accessibility-label :account-emoji
:adjusts-font-size-to-fit true
:style {:font-size emoji-size}}
(string/trim emoji)]]))
(when emoji (string/trim emoji))]]))
(def view (quo.theme/with-theme view-internal))
@@ -67,7 +67,7 @@
[rn/text
{:style (style/emoji-size size)
:accessibility-label :emoji}
(string/trim emoji)])
(when emoji (string/trim emoji))])
[lock locked? size theme]])
(def view (quo.theme/with-theme view-internal))
@@ -56,6 +56,6 @@
:size (:font-size (size properties))
:weight (if monospace? :monospace (:font-weight (size properties)))
:style (style/text customization-color neutral? theme)}
(if lowercase? (string/lower-case initials) initials)]]))
(if (and initials lowercase?) (string/lower-case initials) initials)]]))
(def wallet-user-avatar (quo.theme/with-theme view-internal))
@@ -0,0 +1,56 @@
(ns quo.components.dropdowns.dropdown-input.component-spec
(:require
[quo.components.dropdowns.dropdown-input.view :as dropdown-input]
[test-helpers.component :as h]))
(h/describe "Dropdown Input"
(h/test "default render"
(let [header-label "Label"
label "Dropdown"]
(h/render [dropdown-input/view
{:label? true
:header-label
header-label} label])
(h/is-truthy (h/get-by-label-text :dropdown))
(h/is-truthy (h/get-by-text header-label))
(h/is-truthy (h/get-by-text label))))
(h/test "render with icon"
(let [label "Dropdown"]
(h/render [dropdown-input/view
{:icon? true
:icon-name
:i/wallet
:label? true}
label])
(h/is-truthy (h/get-by-label-text :dropdown))
(h/is-truthy (h/get-by-label-text :left-icon))
(h/is-truthy (h/get-by-text label))))
(h/test "render in active state"
(let [label "Dropdown"]
(h/render [dropdown-input/view
{:state :active
:label?
true} label])
(h/is-truthy (h/get-by-label-text :dropdown))
(h/is-truthy (h/get-by-label-text :right-icon "Expecting active state icon"))))
(h/test "render in disabled state"
(let [label "Dropdown"]
(h/render [dropdown-input/view
{:state :disabled
:label? true} label])
(h/is-truthy (h/get-by-label-text :dropdown))
(h/is-disabled (h/get-by-text label "Dropdown should be disabled in disabled state"))))
(h/test "on-press"
(let [event (h/mock-fn)
label "Dropdown"]
(h/render [dropdown-input/view
{:on-press event
:label?
true} label])
(h/fire-event :press (h/get-by-text label))
(h/was-called event))))
@@ -0,0 +1,49 @@
(ns quo.components.dropdowns.dropdown-input.properties
(:require
[quo.foundations.colors :as colors]))
(def icon-size 20)
(defn- grey-blur
[theme active?]
{:left-icon-color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-70 theme)
:right-icon-color (colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme)
:right-icon-color-2 (colors/theme-colors colors/neutral-100 colors/white theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
:border-color (if active?
(colors/theme-colors colors/neutral-80-opa-20
colors/white-opa-40
theme)
(colors/theme-colors colors/neutral-80-opa-10
colors/white-opa-10
theme))})
(defn- outline
[theme active?]
{:left-icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:right-icon-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
:right-icon-color-2 (colors/theme-colors colors/neutral-100 colors/white theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
:border-color (if active?
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
(colors/theme-colors colors/neutral-30 colors/neutral-70 theme))})
(defn- grey
[theme active?]
{:left-icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:right-icon-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
:right-icon-color-2 (colors/theme-colors colors/neutral-100 colors/white theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
:border-color (if active?
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
(colors/theme-colors colors/neutral-20
colors/neutral-60
theme))})
(defn get-colors
[{:keys [theme state blur?]}]
(let [active? (= state :active)]
(cond
blur? (grey-blur theme active?)
(= theme :dark) (outline theme active?)
(= theme :light) (grey theme active?))))
@@ -0,0 +1,62 @@
(ns quo.components.dropdowns.dropdown-input.style
(:require
[quo.foundations.colors :as colors]))
(def gap 8)
(def blur-view
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0})
(def left-icon
{:margin-right gap})
(defn right-icon
[theme]
{:color (colors/theme-colors colors/neutral-100 colors/white theme)
:margin-right gap})
(defn header-label
[theme blur?]
{:color (if blur?
(colors/theme-colors colors/neutral-80-opa-40
colors/white-opa-70
theme)
(colors/theme-colors colors/neutral-50
colors/neutral-40
theme))
:margin-bottom gap})
(def root-container
{:width "100%"
:height 40})
(defn container
[{:keys [background-color border-color]}
{:keys [icon? state]}]
(cond-> {:align-items :center
:justify-content :space-between
:flex-direction :row
:padding-vertical 9
:padding-left 16
:padding-right 12
:overflow :hidden
:background-color background-color
:border-radius 12}
icon?
(assoc :padding-left 12)
border-color
(assoc :border-color border-color
:border-width 1)
(= state :disabled)
(assoc :opacity 0.3)))
(def right-half-container
{:flex-direction :row
:align-items :center})
@@ -0,0 +1,68 @@
(ns quo.components.dropdowns.dropdown-input.view
(:require
[quo.components.dropdowns.dropdown-input.properties :as properties]
[quo.components.dropdowns.dropdown-input.style :as style]
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.theme :as theme]
[react-native.core :as rn]))
(defn- view-internal
[{:keys [state theme on-press icon?
label? blur?
accessibility-label header-label]
:or {state :default
icon? true
label? true
blur? false
header-label "Label"}
:as props}
label]
(let [{:keys [left-icon-color right-icon-color right-icon-color-2]
:as colors} (properties/get-colors props)
right-icon (if (= state :active) :i/pullup :i/dropdown)]
[rn/view
{:style style/root-container}
(when label?
[text/text
{:size :paragraph-2
:weight :medium
:align :left
:style (style/header-label theme blur?)} header-label])
[rn/pressable
{:accessibility-label (or accessibility-label :dropdown)
:disabled (= state :disabled)
:on-press on-press
:style (style/container colors props)}
[rn/view
{:style style/right-half-container}
(when icon?
[icon/icon
:i/placeholder
{:accessibility-label :left-icon
:color left-icon-color
:size properties/icon-size
:container-style style/left-icon}])
[text/text
{:size :paragraph-1
:weight :regular
:number-of-lines 1
:style (style/right-icon theme)}
label]]
[icon/icon
right-icon
{:size properties/icon-size
:accessibility-label :right-icon
:color right-icon-color
:color-2 right-icon-color-2}]]]))
(def view
"Props:
- state: :default (default) | :active | :disabled
- label: string
- header-label: string
- icon?: boolean
- label?: boolean
- blur?: boolean
- on-press: function"
(theme/with-theme view-internal))
@@ -11,6 +11,7 @@
(case k
:success (colors/resolve-color :success theme)
:error (colors/resolve-color :danger theme)
:warning (colors/resolve-color :warning theme)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)))
(defn view-internal
@@ -36,7 +36,7 @@
:blur? false
:type :digit} 1])
(h/is-truthy (h/query-by-label-text :text-label))
(h/fire-event :press (h/query-by-label-text :keyboard-key))
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/was-called on-press)))
(h/test "Is not pressable when disabled is true"
@@ -47,5 +47,5 @@
:blur? false
:type :digit} 1])
(h/is-truthy (h/query-by-label-text :text-label))
(h/fire-event :press (h/query-by-label-text :keyboard-key))
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/was-not-called on-press))))
@@ -7,6 +7,11 @@
[react-native.core :as rn]
[reagent.core :as reagent]))
(defn- label->accessibility-label
[label]
(let [label-name (if (keyword? label) (name label) label)]
(keyword (str "keyboard-key-" label-name))))
(defn- view-internal
[]
(let [pressed? (reagent/atom false)]
@@ -14,9 +19,11 @@
(let [label-color (style/get-label-color disabled? theme blur?)
background-color (style/toggle-background-color @pressed? blur? theme)]
[rn/pressable
{:accessibility-label :keyboard-key
{:accessibility-label (label->accessibility-label label)
:disabled (or disabled? (not label))
:on-press (fn [] (on-press label))
:on-press (fn []
(when on-press
(on-press label)))
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
:style (style/container background-color)}
@@ -1,7 +1,7 @@
(ns quo.components.numbered-keyboard.numbered-keyboard.style)
(def container
{:flex 1
{:display :flex
:padding-top 8
:padding-horizontal 48})
@@ -17,13 +17,17 @@
(defn- view-internal
[]
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press]}]
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press on-delete
container-style]
:or {left-action :none}}]
[rn/view
{:style style/container}
{:style (merge style/container
container-style)}
(for [row-index (range 1 4)]
^{:key row-index}
[rn/view {:style style/row-container}
(for [column-index (range 1 4)]
^{:key (str row-index column-index)}
[keyboard-item
{:item (+ (* (dec row-index) 3) column-index)
:type :digit
@@ -58,10 +62,10 @@
:theme theme}]
(if delete-key?
[keyboard-item
{:item :i/delete
{:item :i/backspace
:type :key
:disabled? disabled?
:on-press on-press
:on-press on-delete
:blur? blur?
:theme theme}]
[keyboard-item])]]))
+22
View File
@@ -0,0 +1,22 @@
(ns quo.components.overlay.style
(:require [quo.foundations.colors :as colors]))
(defn overlay-background
[type]
(let [background-color (case type
:shell colors/neutral-80-opa-80-blur
:drawer colors/neutral-100-opa-70-blur
nil)]
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0
:background-color background-color}))
(def container
{:flex 1})
(def blur-container
{:flex 1
:background-color :transparent})
+20
View File
@@ -0,0 +1,20 @@
(ns quo.components.overlay.view
(:require
[quo.components.overlay.style :as style]
[react-native.blur :as blur]
[react-native.core :as rn]))
(defn view
[{:keys [type]} & children]
[rn/view {:style (style/overlay-background type)}
(if (= type :shell)
[blur/view
{:blur-amount 20
:blur-radius 25
:blur-type :transparent
:overlay-color :transparent
:style style/container}
[rn/view {:style style/blur-container}
children]]
[rn/view {:style style/container}
children])])
@@ -10,14 +10,14 @@
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? false
:card? true
:label :none
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
@@ -27,14 +27,14 @@
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? false
:card? false
:label :none
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
@@ -45,14 +45,14 @@
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? false
:card? true
:label :none
:status :default
:size :small
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
@@ -63,14 +63,14 @@
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? false
:card? true
:label :none
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
@@ -81,31 +81,31 @@
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? false
:card? true
:label :none
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
(h/is-truthy (h/get-by-text "Description")))
(h/is-truthy (h/get-by-text "Subtitle")))
(h/test "right icon is not visible when icon-right? is false"
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? false
:card? true
:label :none
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
@@ -115,7 +115,6 @@
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? true
:right-icon :i/chevron-right
:card? true
@@ -123,17 +122,17 @@
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
(h/is-truthy (h/query-by-label-text :icon-right)))
(h/test "description icon is visible when description is icon"
(h/test "icon is visible when subtitle type is icon"
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :icon
:icon-right? true
:right-icon :i/chevron-right
:card? true
@@ -141,17 +140,17 @@
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :icon
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
(h/is-truthy (h/query-by-label-text :description-icon)))
(h/is-truthy (h/query-by-label-text :subtitle-type-icon)))
(h/test "description image is visible when description is network"
(h/test "image is visible when subtitle type is network"
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :network
:icon-right? true
:right-icon :i/chevron-right
:card? true
@@ -159,18 +158,18 @@
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :network
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow
:network-image (quo.resources/get-network :ethereum)}])
(h/is-truthy (h/query-by-label-text :description-image)))
(h/is-truthy (h/query-by-label-text :subtitle-type-image)))
(h/test "description emoji is visible when description is account"
(h/test "emoji is visible when subtitle type is account"
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:description :account
:icon-right? true
:right-icon :i/chevron-right
:card? true
@@ -178,7 +177,8 @@
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:subtitle-type :account
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
@@ -37,8 +37,8 @@
:align-items :center})
(defn subtitle-icon-container
[description]
{:margin-right (when (not= :default description) 4)
[subtitle-type]
{:margin-right (when (not= :default subtitle-type) 4)
:justify-content :center})
(defn title
+11 -11
View File
@@ -16,14 +16,15 @@
[rn/view {:style (style/loading-container size blur? theme)}])
(defn- left-subtitle
[{:keys [theme size description icon icon-color blur? subtitle customization-color emoji
network-image]}]
[{:keys [theme size subtitle-type icon icon-color blur? subtitle customization-color emoji
network-image]
:or {subtitle-type :default}}]
[rn/view {:style style/subtitle-container}
(when (not= :small size)
[rn/view {:style (style/subtitle-icon-container description)}
(case description
[rn/view {:style (style/subtitle-icon-container subtitle-type)}
(case subtitle-type
:icon [icons/icon icon
{:accessibility-label :description-icon
{:accessibility-label :subtitle-type-icon
:size 16
:color icon-color}]
:account [account-avatar/view
@@ -32,7 +33,7 @@
:emoji emoji
:type :default}]
:network [rn/image
{:accessibility-label :description-image
{:accessibility-label :subtitle-type-image
:source network-image
:style style/image}]
nil)])
@@ -58,10 +59,9 @@
(i18n/label :t/days)])])
(defn- left-side
"The description can either be given as a string `description` or a component `custom-subtitle`"
[{:keys [theme title status size blur? description custom-subtitle icon subtitle label icon-color
customization-color network-image
emoji]
"The description can either be given as a string `subtitle-type` or a component `custom-subtitle`"
[{:keys [theme title status size blur? custom-subtitle icon subtitle subtitle-type label icon-color
customization-color network-image emoji]
:as props}]
[rn/view {:style style/left-side}
[left-title
@@ -79,7 +79,7 @@
[left-subtitle
{:theme theme
:size size
:description description
:subtitle-type subtitle-type
:icon icon
:icon-color icon-color
:blur? blur?
@@ -9,3 +9,8 @@
(def description-description-text
{:margin-top 8})
(def emoji-hash
{:margin-top 8
:letter-spacing 0.5
:line-height 20.5})
@@ -27,7 +27,8 @@
description-accessibility-label
button-icon
button-on-press
customization-color]}]
customization-color
emoji-hash]}]
[rn/view {:style container-style}
[rn/view
{:style {:flex-direction :row
@@ -55,6 +56,11 @@
:weight :regular
:size :paragraph-1
:style style/description-description-text}
description])])
description])
(when emoji-hash
[text/text
{:number-of-lines 1
:style style/emoji-hash}
emoji-hash])])
(def view (theme/with-theme view-internal))
@@ -133,7 +133,7 @@
| key | description |
| -------------|-------------|
| `:account-name` | A value representing the account name (default `nil`)
| `:account` | A value that represents if the account is a `:watched-account` or a `:default` account. (default `nil`)
| `:account` | A value that represents if the account is a `:watched-address` or a `:default` account. (default `nil`)
| `:time-frame` | A value that represents the type of the timeframe, Can be from a preset of time frames. `[:1-week :1-month :3-months :1-year :custom]` (default `nil`) if custom is set, We expect a start time and if there's a space between two times that are in `time-frame-string` we'll split them with an arrow to the right icon
| `:loading?` | A value that indicates that component is loading data. (default `nil`)
| `:metrics` | A value that indicates if the account value have increased can be `:positive` or `:negative` (default `nil`)
@@ -4,7 +4,7 @@
(defn container
[networks? theme]
{:width 335
{:width "100%"
:height (if networks? 90 56)
:border-radius 16
:border-width 1
+43 -23
View File
@@ -15,18 +15,26 @@
(defn calc-value
[crypto? currency token value conversion]
(if crypto?
(str (get common/currency-label currency) (.toFixed (* value conversion) 2))
(str (.toFixed (/ value conversion) 2) " " (string/upper-case (clj->js token)))))
(let [num-value (if (string? value) (parse-double (or value "0")) value)]
(if crypto?
(str (get common/currency-label currency) (.toFixed (* num-value conversion) 2))
(str (.toFixed (/ num-value conversion) 2) " " (string/upper-case (or (clj->js token) ""))))))
(defn- view-internal
[]
(let [width (:width (rn/get-window))
value (reagent/atom 0)
crypto? (reagent/atom true)
input-ref (atom nil)]
(fn [{:keys [theme token currency conversion networks title customization-color]}]
[rn/view {:style (style/main-container width)}
[{external-value :value}]
(let [width (:width (rn/get-window))
value (reagent/atom nil)
crypto? (reagent/atom true)
input-ref (atom nil)
controlled-input? (some? external-value)]
(fn [{:keys [theme token currency conversion networks title customization-color
on-change-text on-swap container-style show-keyboard?]
:or {show-keyboard? true}
external-value :value}]
[rn/view
{:style (merge
(style/main-container width)
container-style)}
[rn/view {:style style/amount-container}
[rn/pressable
{:on-press #(when @input-ref (.focus ^js @input-ref))
@@ -35,29 +43,41 @@
:align-items :flex-end}}
[rn/image
{:style style/token
:source (resources/get-token token)}]
:source (resources/get-token (keyword (string/lower-case token)))}]
[rn/text-input
{:ref #(reset! input-ref %)
:placeholder "0"
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
:keyboard-type :numeric
:max-length 12
:default-value @value
:on-change-text #(reset! value %)
:style (style/text-input theme)
:selection-color customization-color}]
(cond-> {:auto-focus true
:ref #(reset! input-ref %)
:placeholder "0"
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:keyboard-type :numeric
:max-length 12
:on-change-text (fn [v]
(when-not controlled-input?
(reset! value v))
(when on-change-text
(on-change-text v)))
:style (style/text-input theme)
:selection-color customization-color
:show-soft-input-on-focus show-keyboard?}
controlled-input? (assoc :value external-value)
(not controlled-input?) (assoc :default-value @value))]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:margin-right 8
:padding-bottom 2}}
(string/upper-case (clj->js (if @crypto? token currency)))]]
(string/upper-case (or (clj->js (if @crypto? token currency)) ""))]]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press #(swap! crypto? not)
:on-press (fn []
(swap! crypto? not)
(when on-swap
(on-swap @crypto?)))
:type :outline
:accessibility-label :reorder}
:i/reorder]]
@@ -68,6 +88,6 @@
{:size :paragraph-2
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(calc-value @crypto? currency token @value conversion)]]])))
(calc-value @crypto? currency token (or external-value @value) conversion)]]])))
(def view (quo.theme/with-theme view-internal))
+6
View File
@@ -48,6 +48,7 @@
quo.components.drawers.drawer-buttons.view
quo.components.drawers.drawer-top.view
quo.components.drawers.permission-context.view
quo.components.dropdowns.dropdown-input.view
quo.components.dropdowns.dropdown.view
quo.components.dropdowns.network-dropdown.view
quo.components.empty-state.empty-state.view
@@ -100,6 +101,7 @@
quo.components.numbered-keyboard.keyboard-key.view
quo.components.numbered-keyboard.numbered-keyboard.view
quo.components.onboarding.small-option-card.view
quo.components.overlay.view
quo.components.password.tips.view
quo.components.profile.collectible.view
quo.components.profile.profile-card.view
@@ -232,6 +234,7 @@
;;;; Dropdowns
(def dropdown quo.components.dropdowns.dropdown.view/view)
(def dropdown-input quo.components.dropdowns.dropdown-input.view/view)
(def network-dropdown quo.components.dropdowns.network-dropdown.view/view)
;;;; Empty State
@@ -314,6 +317,9 @@
(def notification quo.components.notifications.notification.view/notification)
(def toast quo.components.notifications.toast.view/toast)
;;;; Overlay
(def overlay quo.components.overlay.view/view)
;;;; Password
(def tips quo.components.password.tips.view/view)
+1
View File
@@ -26,6 +26,7 @@
[quo.components.drawers.drawer-buttons.component-spec]
[quo.components.drawers.drawer-top.component-spec]
[quo.components.drawers.permission-context.component-spec]
[quo.components.dropdowns.dropdown-input.component-spec]
[quo.components.dropdowns.dropdown.component-spec]
[quo.components.dropdowns.network-dropdown.component-spec]
[quo.components.gradient.gradient-cover.component-spec]
+16 -2
View File
@@ -148,7 +148,9 @@
(def black-opa-30 (alpha black 0.3))
(def black-opa-60 (alpha black 0.6))
(def onboarding-header-black "#000716")
(def border-avatar-light
"Simulates a blurred, transparent border for avatars in light mode"
"#475060")
;;;;Primary
;;Solid
@@ -201,6 +203,16 @@
(def danger-50-opa-30 (alpha danger-50 0.3))
(def danger-50-opa-40 (alpha danger-50 0.4))
;;;;Warning
(def warning-50 "#FF7D46")
(def warning-60 "#CC6438")
;;50 with transparency
(def warning-50-opa-5 (alpha warning-50 0.05))
(def warning-50-opa-10 (alpha warning-50 0.1))
(def warning-50-opa-20 (alpha warning-50 0.2))
(def warning-50-opa-30 (alpha warning-50 0.3))
(def warning-50-opa-40 (alpha warning-50 0.4))
;; Colors for customizing users account
(def customization
@@ -254,7 +266,9 @@
:danger {50 danger-50
60 danger-60}
:success {50 success-50
60 success-60}}
60 success-60}
:warning {50 warning-50
60 warning-60}}
customization
networks))
+10 -8
View File
@@ -58,14 +58,16 @@
(defn save-credentials
"Stores the credentials for the address to the Keychain"
[server username password callback]
(-> (.setInternetCredentials ^js react-native-keychain
(string/lower-case server)
username
password
keychain-secure-hardware
keychain-restricted-availability)
(.then callback)))
([server username password]
(save-credentials server username password identity))
([server username password callback]
(-> (.setInternetCredentials ^js react-native-keychain
(string/lower-case server)
username
password
keychain-secure-hardware
keychain-restricted-availability)
(.then callback))))
(defn get-credentials
"Gets the credentials for a specified server from the Keychain"
+1 -1
View File
@@ -14,6 +14,6 @@
(rf/merge cofx
(multiaccounts.update/multiaccount-update
:currency
currency
"USD"
{})
(prices/update-prices)))
+1 -2
View File
@@ -172,8 +172,7 @@
:use-mailservers? true
:recovered recovered}
config/default-multiaccount)
;; The address from which we derive any chat
;; account/encryption keys
;; The address from which we derive any chat account/encryption keys
eip1581-address
(assoc :eip1581-address eip1581-address)
save-mnemonic?
+4 -4
View File
@@ -31,8 +31,8 @@
(let [old-network-status (:network-status db)
old-network-type (:network/type db)
connectivity-status (if isConnected :online :offline)
status-changed? (not= connectivity-status old-network-status)
type-changed? (not= type old-network-type)]
status-changed? (= connectivity-status old-network-status)
type-changed? (= type old-network-type)]
(log/debug "[net-info]"
"old-network-status" old-network-status
"old-network-type" old-network-type
@@ -40,9 +40,9 @@
"type" type
"details" details)
(rf/merge cofx
(when status-changed?
(when-not status-changed?
(change-network-status isConnected))
(when type-changed?
(when-not type-changed?
(change-network-type old-network-type type (:is-connection-expensive details))))))
(defn add-net-info-listener
@@ -14,7 +14,8 @@
transactions-management-enabled?
wakuv2-flag
current-fleet
webview-debug]}]
webview-debug
test-networks-enabled?]}]
(keep
identity
[{:size :small
@@ -95,6 +96,15 @@
[:profile.settings/change-webview-debug (not webview-debug)])
:accessory :switch
:active webview-debug}
{:size :small
:title "Test Networks Enabled"
:accessibility-label :test-networks-enabled
:container-margin-bottom 8
:on-press
#(re-frame/dispatch
[:profile.settings/change-test-networks-enabled (not test-networks-enabled?)])
:accessory :switch
:active test-networks-enabled?}
{:size :small
:title (i18n/label :t/waku-bloom-filter-mode)
:accessibility-label :waku-bloom-filter-mode-settings-switch
@@ -117,7 +127,8 @@
(views/defview advanced-settings
[]
(views/letsubs [{:keys [webview-debug]} [:profile/profile]
(views/letsubs [{:keys [webview-debug
test-networks-enabled?]} [:profile/profile]
network-name [:network-name]
waku-bloom-filter-mode [:waku/bloom-filter-mode]
wakuv2-flag [:waku/v2-flag]
@@ -133,6 +144,7 @@
:dev-mode? false
:wakuv2-flag wakuv2-flag
:waku-bloom-filter-mode waku-bloom-filter-mode
:webview-debug webview-debug})
:webview-debug webview-debug
:test-networks-enabled? test-networks-enabled?})
:key-fn (fn [_ i] (str i))
:render-fn render-item}]))
+3 -21
View File
@@ -246,8 +246,8 @@
assets (get visible-tokens chain)
tokens (->> (vals all-tokens)
(remove #(or (:hidden? %)
;;if not scan-all-tokens? remove not
;;visible tokens
;;if not scan-all-tokens? remove
;;not visible tokens
(and (not scan-all-tokens?)
(not (get assets (:symbol %))))))
(reduce (fn [acc {:keys [address symbol]}]
@@ -1075,7 +1075,7 @@
(re-frame/reg-fx
;;TODO: this could be replaced by a single API call on status-go side
:wallet-legacy/initialize-wallet
(fn [[network-id network callback]]
(fn [[network-id callback]]
(-> (js/Promise.all
(clj->js
[(js/Promise.
@@ -1083,24 +1083,6 @@
(json-rpc/call {:method "accounts_getAccounts"
:on-success resolve-fn
:on-error reject})))
(js/Promise.
(fn [resolve-fn _]
(json-rpc/call
{:method "wallet_addEthereumChain"
:params
[{:isTest false
:tokenOverrides []
:rpcUrl (get-in network [:config :UpstreamConfig :URL])
:chainColor "green"
:chainName (:name network)
:nativeCurrencyDecimals 10
:shortName "erc20"
:layer 1
:chainId (int network-id)
:enabled false
:fallbackURL (get-in network [:config :UpstreamConfig :URL])}]
:on-success resolve-fn
:on-error (fn [_] (resolve-fn nil))})))
(js/Promise.
(fn [resolve-fn _]
(json-rpc/call {:method "wallet_getTokens"
+1 -1
View File
@@ -22,7 +22,7 @@
(vector
action-button
dismiss-button)
dismiss-button)
(vector dismiss-button))
(when on-dismiss
{:cancelable false})))))
+51 -5
View File
@@ -2,9 +2,11 @@
(:require
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[react-native.async-storage :as async-storage]
[react-native.platform :as platform]
[react-native.touch-id :as touch-id]
[status-im2.common.keychain.events :as keychain]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -41,16 +43,60 @@
{:db (assoc db :biometric/supported-type supported-type)})
(rf/defn show-message
{:events [:biometric/show-message]}
[_ code]
(let [content (if (#{"NOT_AVAILABLE" "NOT_ENROLLED"} code)
(i18n/label :t/grant-face-id-permissions)
(when-not (or (= code "USER_CANCELED") (= code "USER_FALLBACK"))
(i18n/label :t/biometric-auth-error {:code code})))]
(when content
(let [handle-error? (and code
(not (contains? #{"USER_CANCELED" "USER_FALLBACK"} code)))
content (if (#{"NOT_AVAILABLE" "NOT_ENROLLED"} code)
(i18n/label :t/grant-face-id-permissions)
(i18n/label :t/biometric-auth-error {:code code}))]
(when handle-error?
{:utils/show-popup
{:title (i18n/label :t/biometric-auth-login-error-title)
:content content}})))
(defn- supress-biometry-error-key
[key-uid]
(keyword (str "biometric/supress-not-enrolled-error-" key-uid)))
;; NOTE: if the account had biometrics registered, but it's not enrolled at the moment,
;; we should show the error message only once and supress further "NOT_ENROLLED" errors
;; until biometry is enrolled again. Note that we can only know that when :biometric/authenticate
;; is dispatched and fails with "NOT_ENROLLED", since :biometric/get-supported-biometric-type
;; only tells us what kind of biometric is available on the device, but it doesn't know of its
;; enrollment status.
(re-frame/reg-fx
:biometric/supress-not-enrolled-error
(fn [[key-uid dispatch-event]]
(let [storage-key (supress-biometry-error-key key-uid)]
(-> (async-storage/get-item storage-key identity)
(.then (fn [item]
(when (not item)
(rf/dispatch dispatch-event)
(async-storage/set-item! storage-key true))))
(.catch (fn [err]
(log/error "Couldn't supress biometry NOT_ENROLLED error"
{:key-uid key-uid
:event :biometric/supress-not-enrolled-error
:error err})))))))
;; NOTE: when biometrics is re-enrolled, we erase the flag in async-storage to assure
;; the "NOT_ENROLLED" error message will be shown again if biometrics is un-enrolled
;; in the future.
(re-frame/reg-fx
:biometric/reset-not-enrolled-error
(fn [key-uid]
(let [storage-key (supress-biometry-error-key key-uid)]
(-> (async-storage/get-item storage-key identity)
(.then (fn [supress?]
(when supress?
(async-storage/set-item! storage-key nil))))
(.catch (fn [err]
(log/error "Couldn't reset supressing biometry NOT_ENROLLED error"
{:key-uid key-uid
:event :biometric/reset-not-enrolled-error
:error err})))))))
(re-frame/reg-fx
:biometric/authenticate
(fn [options]
+1 -1
View File
@@ -117,7 +117,7 @@
[rn/view
{:style (style/sheet-content theme padding-bottom-override insets shell? bottom-margin)
:on-layout #(reset! sheet-height (.-nativeEvent.layout.height ^js %))}
(when gradient-cover?
(when (and gradient-cover? customization-color)
[rn/view {:style style/gradient-bg}
[quo/gradient-cover
{:customization-color customization-color
+68 -14
View File
@@ -49,16 +49,15 @@
(defn save-auth-method!
[key-uid method]
(keychain/save-credentials
(str key-uid "-auth")
key-uid
method
#(when-not %
(log/error
(str "Error while saving auth method."
" "
"The app will continue to work normally, "
"but you will have to login again next time you launch it.")))))
(-> (keychain/save-credentials
(str key-uid "-auth")
key-uid
method)
(.catch (fn [err]
(log/error "Failed to save auth method in the keychain"
{:error err
:key-uid key-uid
:auth-method method})))))
(re-frame/reg-fx
:keychain/save-auth-method
@@ -79,27 +78,59 @@
(if can-save?
(keychain/get-credentials
(str key-uid "-auth")
#(callback (if % (oops/oget % "password") auth-method-none)))
(fn [value]
(callback (if value (oops/oget value "password") auth-method-none))))
(callback nil))))))
(defn save-user-password!
[key-uid password]
(keychain/save-credentials key-uid key-uid (security/safe-unmask-data password) #()))
(defn get-user-password!
[key-uid callback]
(keychain/get-credentials key-uid
(fn [value]
(callback (when value
(-> value
(oops/oget "password")
(security/mask-data)))))))
(re-frame/reg-fx
:keychain/get-user-password
(fn [[key-uid callback]]
(keychain/get-credentials
key-uid
#(if % (callback (security/mask-data (oops/oget % "password"))) (callback nil)))))
(get-user-password! key-uid callback)))
(rf/defn get-user-password
[_ key-uid callback]
{:keychain/get-user-password [key-uid callback]})
(defn- password-migration-key-name
[key-uid]
(str key-uid "-password-migration"))
(defn save-password-migration!
[key-uid]
(-> (keychain/save-credentials
(password-migration-key-name key-uid)
key-uid
;; NOTE: using the key-uid as the password, but we don't really care about the
;; value, we only care that it's there
key-uid)
(.catch (fn [error]
(log/error "Failed to get the keychain password migration flag"
{:error error
:key-uid key-uid})))))
(defn get-password-migration!
[key-uid callback]
(keychain/get-credentials
(password-migration-key-name key-uid)
(comp callback boolean)))
(re-frame/reg-fx
:keychain/clear-user-password
(fn [key-uid]
(keychain/reset-credentials (password-migration-key-name key-uid))
(keychain/reset-credentials key-uid)))
(re-frame/reg-fx
@@ -107,5 +138,28 @@
(fn [{:keys [key-uid masked-password on-success on-error]}]
(-> (save-user-password! key-uid masked-password)
(.then #(save-auth-method! key-uid auth-method-biometric))
(.then #(save-password-migration! key-uid))
(.then #(when on-success (on-success)))
(.catch #(when on-error (on-error %))))))
;; NOTE: migrating the plaintext password in the keychain
;; with the hashed one. Added due to the sync onboarding
;; flow, where the password arrives already hashed.
(re-frame/reg-fx
:keychain/password-hash-migration
(fn [{:keys [key-uid callback]
:or {callback identity}}]
(-> (get-password-migration! key-uid identity)
(.then (fn [migrated?]
(if migrated?
(callback)
(-> (get-user-password! key-uid identity)
(.then security/hash-masked-password)
(.then #(save-user-password! key-uid %))
(.then #(save-password-migration! key-uid))
(.then callback)))))
(.catch (fn [err]
(log/error "Failed to migrate the keychain password"
{:error err
:key-uid key-uid
:event :keychain/password-hash-migration}))))))
@@ -14,8 +14,11 @@
(defn authorize
[{:keys [on-enter-password biometric-auth? on-auth-success on-auth-fail on-close
auth-button-label theme blur? auth-button-icon-left]}]
(println auth-button-label "dasasdasdadsdas")
(biometric/get-supported-type
(fn [biometric-type]
(println biometric-type "biometric-type")
(if (and biometric-auth? biometric-type)
(biometric/authenticate
{:reason (i18n/label :t/biometric-auth-confirm-message)
@@ -30,8 +33,10 @@
:shell? blur?
:content (fn []
[enter-password/view
{:on-enter-password on-enter-password}])}]))})
{:button-label auth-button-label
:on-enter-password on-enter-password}])}]))})
(do
(println "222222")
(reset-password)
(rf/dispatch [:show-bottom-sheet
{:on-close on-close
+3 -1
View File
@@ -74,7 +74,7 @@
(def default-multiaccount
{:preview-privacy? blank-preview?
:wallet-legacy/visible-tokens {:mainnet #{:SNT}}
:wallet-legacy/visible-tokens {:mainnet #{:SNT :ETH :DAI}}
:currency :usd
:appearance 0
:profile-pictures-show-to 2
@@ -170,3 +170,5 @@
(def default-kdf-iterations 3200)
(def shell-navigation-disabled? false)
(def community-accounts-selection-enabled? false)
@@ -0,0 +1,40 @@
(ns status-im2.contexts.communities.actions.accounts-selection.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im2.common.password-authentication.view :as password-authentication]
[status-im2.contexts.communities.actions.request-to-join.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn join-community-and-navigate-back
[id]
(rf/dispatch [:password-authentication/show
{:content (fn [] [password-authentication/view])}
{:label (i18n/label :t/join-open-community)
:on-press #(rf/dispatch [:communities/request-to-join
{:community-id id :password %}])}])
(rf/dispatch [:navigate-back]))
(defn view
[]
(fn []
(let [{:keys [_name
id
_images]} (rf/sub [:get-screen-params])]
[rn/view {:flex 1}
[gesture/scroll-view {:style {:flex 1}}
[rn/view style/page-container
[rn/view {:style (style/bottom-container)}
[quo/button
{:accessibility-label :cancel
:on-press #(rf/dispatch [:navigate-back])
:type :grey
:container-style style/cancel-button}
(i18n/label :t/cancel)]
[quo/button
{:accessibility-label :join-community-button
:on-press #(join-community-and-navigate-back id)
:container-style {:flex 1}}
(i18n/label :t/request-to-join)]]]]])))
@@ -242,6 +242,7 @@
(defn- internal-discover-view
[params]
(rf/dispatch [:fetch-contract-communities])
[:f> f-view-internal params])
(def view (quo.theme/with-theme internal-discover-view))
@@ -1,6 +1,7 @@
(ns status-im2.contexts.communities.overview.style
(:require
[quo.foundations.colors :as colors]))
[quo.foundations.colors :as colors]
[status-im2.contexts.shell.jump-to.constants :as jump-to.constants]))
(def screen-horizontal-padding 20)
@@ -41,6 +42,16 @@
:right 0
:bottom 0})
(def floating-shell-button
{:position :absolute
:bottom 21})
(defn channel-list-component
[]
{:margin-top 8
:margin-bottom (+ 21 jump-to.constants/floating-shell-button-height)
:flex 1})
(defn token-gated-container
[]
{:border-radius 16
@@ -10,6 +10,7 @@
[status-im2.common.password-authentication.view :as password-authentication]
[status-im2.common.scroll-page.style :as scroll-page.style]
[status-im2.common.scroll-page.view :as scroll-page]
[status-im2.config :as config]
[status-im2.constants :as constants]
[status-im2.contexts.communities.actions.chat.view :as chat-actions]
[status-im2.contexts.communities.actions.community-options.view :as options]
@@ -82,7 +83,7 @@
{:on-layout #(on-first-channel-height-changed
(+ 38 (int (Math/ceil (layout-y %))))
(into #{} (map (comp :name second) channels-list)))
:style {:margin-top 8 :flex 1}}
:style (style/channel-list-component)}
(for [[category-id {:keys [chats name collapsed?]}] channels-list]
[rn/view
{:key category-id
@@ -91,12 +92,13 @@
:on-layout #(on-category-layout name (int (layout-y %)))}
(when-not (= constants/empty-category-id category-id)
[quo/divider-label
{:on-press #(collapse-category community-id category-id collapsed?)
:chevron-icon (if collapsed? :i/chevron-right :i/chevron-down)
:chevron :left}
{:on-press #(collapse-category community-id category-id collapsed?)
:chevron-icon (if collapsed? :i/chevron-right :i/chevron-down)
:container-style {:margin-top 8}
:chevron :left}
name])
(when-not collapsed?
[rn/view {:style {:padding-horizontal 8 :padding-bottom 8}}
[rn/view {:style {:padding-horizontal 8}}
(for [chat chats]
^{:key (:id chat)}
[channel-chat-item community-id community-color chat])])])])
@@ -181,10 +183,14 @@
(if (seq token-permissions)
[token-gates community]
[quo/button
{:on-press #(rf/dispatch [:open-modal :community-requests-to-join community])
{:on-press
(if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection community])
#(rf/dispatch [:open-modal :community-requests-to-join community]))
:accessibility-label :show-request-to-join-screen-button
:customization-color color
:icon-left :i/communities}
:icon-left :i/communities}
(i18n/label :t/request-to-join-community)]))
(when (not (or joined pending? token-permissions))
@@ -375,5 +381,4 @@
{:jump-to {:on-press #(rf/dispatch [:shell/navigate-to-jump-to])
:customization-color customization-color
:label (i18n/label :t/jump-to)}}
{:position :absolute
:bottom 34}]]))
style/floating-shell-button]]))
@@ -1,7 +1,6 @@
(ns status-im2.contexts.onboarding.enable-biometrics.view
(:require
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im2.common.biometric.events :as biometric]
@@ -12,8 +11,7 @@
[status-im2.contexts.onboarding.enable-biometrics.style :as style]
[status-im2.navigation.state :as state]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
[utils.re-frame :as rf]))
(defn page-title
@@ -26,7 +24,7 @@
:description-accessibility-label :enable-biometrics-sub-title}])
(defn enable-biometrics-buttons
[insets theme]
[insets]
(let [supported-biometric-type (rf/sub [:biometric/supported-type])
bio-type-label (biometric/get-label-by-type supported-biometric-type)
profile-color (or (:color (rf/sub [:onboarding-2/profile]))
@@ -34,23 +32,12 @@
syncing-results? (= :syncing-results @state/root-id)]
[rn/view {:style (style/buttons insets)}
[standard-auth/button
(merge
{:size 40
:accessibility-label :enable-biometrics-button
:icon-left :i/face-id
:customization-color profile-color
:button-label (i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})}
(if syncing-results?
{:theme theme
:blur? true
:on-enter-password (fn [entered-password]
(rf/dispatch
[:onboarding-2/authenticate-enable-biometrics
(security/safe-unmask-data
entered-password)])
(rf/dispatch [:hide-bottom-sheet]))
:auth-button-label (i18n/label :t/confirm)}
{:on-press #(rf/dispatch [:onboarding-2/enable-biometrics])}))]
{:size 40
:accessibility-label :enable-biometrics-button
:icon-left :i/face-id
:customization-color profile-color
:on-press #(rf/dispatch [:onboarding-2/enable-biometrics])
:button-label (i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})}]
[quo/button
{:accessibility-label :maybe-later-button
:background :blur
@@ -78,18 +65,16 @@
:source (resources/get-image :biometrics)}]))
(defn f-enable-biometrics
[{:keys [theme]}]
[]
(let [insets (safe-area/get-insets)]
[rn/view {:style (style/page-container insets)}
[page-title]
(if whitelist/whitelisted?
[enable-biometrics-parallax]
[enable-biometrics-simple])
[enable-biometrics-buttons insets theme]]))
[enable-biometrics-buttons insets]]))
(defn view
[]
[:f> f-enable-biometrics])
(defn- internale-enable-biometrics
[params]
[:f> f-enable-biometrics params])
(def view (quo.theme/with-theme internale-enable-biometrics))
+3 -10
View File
@@ -35,15 +35,6 @@
{:biometric/authenticate {:on-success #(rf/dispatch [:onboarding-2/biometrics-done])
:on-fail #(rf/dispatch [:onboarding-2/biometrics-fail %])}})
(rf/defn authenticate-enable-biometrics
{:events [:onboarding-2/authenticate-enable-biometrics]}
[{:keys [db]} password]
{:db (-> db
(assoc-in [:onboarding-2/profile :password] password)
(assoc-in [:onboarding-2/profile :syncing?] true))
:biometric/authenticate {:on-success #(rf/dispatch [:onboarding-2/biometrics-done])
:on-fail #(rf/dispatch [:onboarding-2/biometrics-fail %])}})
(rf/defn navigate-to-enable-notifications
{:events [:onboarding-2/navigate-to-enable-notifications]}
[{:keys [db]}]
@@ -154,7 +145,9 @@
biometric-enabled?
(assoc :keychain/save-password-and-auth-method
{:key-uid key-uid
:masked-password masked-password
:masked-password (if syncing?
masked-password
(security/hash-masked-password masked-password))
:on-success (fn []
(if syncing?
(rf/dispatch [:onboarding-2/navigate-to-enable-notifications])
@@ -11,7 +11,6 @@
[status-im.group-chats.core :as group-chats]
[status-im.mobile-sync-settings.core :as mobile-network]
[status-im.transport.core :as transport]
[status-im2.common.biometric.events :as biometric]
[status-im2.common.keychain.events :as keychain]
[status-im2.common.log :as logging]
[status-im2.config :as config]
@@ -42,11 +41,22 @@
{:db (assoc-in db [:profile/login :processing] true)
::login [key-uid (native-module/sha3 (security/safe-unmask-data password))]}))
(rf/defn biometrics-login
{:events [:profile.login/biometrics-login]}
[{:keys [db]}]
(let [{:keys [key-uid password]} (:profile/login db)]
{:db (assoc-in db [:profile/login :processing] true)
::login [key-uid (security/safe-unmask-data password)]}))
(rf/defn login-local-paired-user
{:events [:profile.login/local-paired-user]}
[{:keys [db]}]
(let [{:keys [key-uid password]} (get-in db [:syncing :profile])]
{::login [key-uid password]}))
(let [{:keys [key-uid password]} (get-in db [:syncing :profile])
masked-password (security/mask-data password)]
{:db (-> db
(assoc-in [:onboarding-2/profile :password] masked-password)
(assoc-in [:onboarding-2/profile :syncing?] true))
::login [key-uid password]}))
(rf/defn redirect-to-root
[{:keys [db] :as cofx}]
@@ -109,7 +119,6 @@
(cond-> {:wallet-legacy/initialize-transactions-management-enabled nil
:wallet-legacy/initialize-wallet
[network-id
current-network-config
(fn [accounts tokens custom-tokens favourites]
(re-frame/dispatch [:wallet-legacy/initialize-wallet
accounts tokens custom-tokens favourites]))]
@@ -151,20 +160,16 @@
(rf/defn get-auth-method-success
{:events [:profile.login/get-auth-method-success]}
[{:keys [db]} auth-method]
[{:keys [db]} auth-method key-uid]
(merge {:db (assoc db :auth-method auth-method)}
(when (= auth-method keychain/auth-method-biometric)
{:biometric/authenticate
{:on-success #(rf/dispatch [:profile.login/biometric-success])
:on-faile #(rf/dispatch [:profile.login/biometric-auth-fail])}})))
(rf/defn biometric-auth-success
{:events [:profile.login/biometric-success]}
[{:keys [db] :as cofx}]
(let [key-uid (get-in db [:profile/login :key-uid])]
(keychain/get-user-password cofx
key-uid
#(rf/dispatch [:profile.login/get-user-password-success %]))))
{:keychain/password-hash-migration
{:key-uid key-uid
:callback (fn []
(rf/dispatch [:biometric/authenticate
{:on-success #(rf/dispatch [:profile.login/biometric-success])
:on-fail #(rf/dispatch
[:profile.login/biometric-auth-fail %])}]))}})))
;; result of :keychain/get-auth-method above
(rf/defn get-user-password-success
@@ -175,14 +180,29 @@
cofx
{:db (assoc-in db [:profile/login :password] password)}
(navigation/init-root :progress)
(login))))
(biometrics-login))))
(rf/reg-event-fx
:profile.login/biometric-success
(fn [{:keys [db]}]
(let [key-uid (get-in db [:profile/login :key-uid])]
{:db db
:fx [[:biometric/reset-not-enrolled-error key-uid]
[:keychain/get-user-password
[key-uid #(rf/dispatch [:profile.login/get-user-password-success %])]]]})))
(rf/reg-event-fx
:profile.login/biometric-auth-fail
(fn [{:keys [db]} [code]]
(let [key-uid (get-in db [:profile/login :key-uid])]
{:db db
:fx [[:dispatch [:init-root :profiles]]
(if (= code "NOT_ENROLLED")
[:biometric/supress-not-enrolled-error
[key-uid
[:biometric/show-message code]]]
[:dispatch [:biometric/show-message code]])]})))
(rf/defn biometric-auth-fail
{:events [:profile.login/biometric-auth-fail]}
[{:keys [db] :as cofx} code]
(rf/merge cofx
(navigation/init-root :profiles)
(biometric/show-message code)))
(rf/defn verify-database-password
{:events [:profile.login/verify-database-password]}
@@ -197,7 +217,7 @@
(rf/defn verify-database-password-success
{:events [:profile.login/verified-database-password]}
[{:keys [db] :as cofx} valid? callback]
[{:keys [db]} valid? callback]
(if valid?
(do
(when (fn? callback)
@@ -14,6 +14,7 @@
:on-success #(log/debug "sent contact update")}]}))
(rf/defn profile-update
{:events [:profile.settings/profile-update]}
[{:keys [db] :as cofx}
setting setting-value
{:keys [dont-sync? on-success] :or {on-success #()}}]
@@ -56,6 +57,10 @@
{:profile.settings/webview-debug-changed value}
(profile-update :webview-debug (boolean value) {})))
(rf/reg-event-fx :profile.settings/change-test-networks-enabled
(fn [_ [value]]
{:fx [[:dispatch [:profile.settings/profile-update :test-networks-enabled? (boolean value) {}]]]}))
(rf/defn change-preview-privacy-flag
{:events [:profile.settings/change-preview-privacy]}
[{:keys [db] :as cofx} private?]
@@ -0,0 +1,43 @@
(ns status-im2.contexts.profile.settings.header.avatar
(:require [quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.reanimated :as reanimated]
[status-im2.contexts.profile.settings.header.style :as style]))
(def scroll-animation-input-range [0 50])
(def header-extrapolation-option
{:extrapolateLeft "clamp"
:extrapolateRight "clamp"})
(defn f-avatar
[{:keys [scroll-y full-name online? profile-picture customization-color]}]
(let [image-scale-animation (reanimated/interpolate scroll-y
scroll-animation-input-range
[1 0.5]
header-extrapolation-option)
image-top-margin-animation (reanimated/interpolate scroll-y
scroll-animation-input-range
[0 20]
header-extrapolation-option)
image-side-margin-animation (reanimated/interpolate scroll-y
scroll-animation-input-range
[0 -20]
header-extrapolation-option)
theme (quo.theme/get-theme)]
[reanimated/view
{:style (style/avatar-container theme
image-scale-animation
image-top-margin-animation
image-side-margin-animation)}
[quo/user-avatar
{:full-name full-name
:online? online?
:profile-picture profile-picture
:status-indicator? true
:ring? true
:customization-color customization-color
:size :big}]]))
(defn view
[props]
[:f> f-avatar props])
@@ -0,0 +1,36 @@
(ns status-im2.contexts.profile.settings.header.header-shape
(:require [quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.svg :as svg]
[status-im2.contexts.profile.settings.header.style :as style]))
(defn left-radius
[background-color]
[svg/svg {:width "20" :height "20" :viewBox "0 0 20 20" :fill "none"}
[svg/path
{:d "M20 0C7 2 0 10 0 20V0H15Z"
:fill background-color}]])
(defn right-radius
[background-color]
[svg/svg {:width "20" :height "21" :viewBox "0 0 20 21" :fill "none"}
[svg/path
{:d "M20 20V0H0C11 0 20 9 20 20Z"
:fill background-color}]])
(defn f-view
[{:keys [scroll-y customization-color theme]}]
(let [background-color (colors/resolve-color customization-color theme 40)
opacity-animation (reanimated/interpolate scroll-y
[0 45 50]
[1 1 0])]
[:<>
[rn/view {:style (style/header-middle-shape background-color)}]
[reanimated/view {:style (style/radius-container opacity-animation)}
[left-radius background-color]
[right-radius background-color]]]))
(defn view
[props]
[:f> f-view props])
@@ -0,0 +1,48 @@
(ns status-im2.contexts.profile.settings.header.style
(:require [quo.foundations.colors :as colors]
[react-native.reanimated :as reanimated]))
(defn header-view
[customization-color theme]
{:background-color (colors/resolve-color customization-color theme 40)
:min-height 100
:flex 1})
(def avatar-row-wrapper
{:display :flex
:padding-left 16
:padding-right 12
:margin-top -60
:margin-bottom -4
:align-items :flex-end
:justify-content :space-between
:flex-direction :row})
(def title-container
{:padding-horizontal 20
:padding-vertical 12})
(defn header-middle-shape
[background-color]
{:background-color background-color
:height 48
:flex-grow 1})
(defn radius-container
[opacity-animation]
(reanimated/apply-animations-to-style
{:opacity opacity-animation}
{:flex-direction :row
:justify-content :space-between}))
(defn avatar-container
[theme scale-animation top-margin-animation side-margin-animation]
(reanimated/apply-animations-to-style
{:transform [{:scale scale-animation}]
:margin-top top-margin-animation
:margin-left side-margin-animation
:margin-bottom side-margin-animation}
{:align-items :flex-start
:border-width 4
:border-color (colors/theme-colors colors/border-avatar-light colors/neutral-80-opa-80 theme)
:border-radius 100}))
@@ -0,0 +1,49 @@
(ns status-im2.contexts.profile.settings.header.view
(:require [clojure.string :as string]
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.contexts.profile.settings.header.avatar :as header.avatar]
[status-im2.contexts.profile.settings.header.header-shape :as header.shape]
[status-im2.contexts.profile.settings.header.style :as style]
[status-im2.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- f-view
[{:keys [theme scroll-y]}]
(let [{:keys [public-key emoji-hash] :as profile} (rf/sub [:profile/profile-with-image])
online? (rf/sub [:visibility-status-updates/online?
public-key])
customization-color (rf/sub [:profile/customization-color])
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
emoji-string (string/join emoji-hash)]
[:<>
[header.shape/view
{:scroll-y scroll-y
:customization-color customization-color
:theme theme}]
[rn/view {:style style/avatar-row-wrapper}
[header.avatar/view
{:scroll-y scroll-y
:display-name full-name
:online? online?
:customization-color customization-color
:profile-picture profile-picture}]
[rn/view {:style {:margin-bottom 4}}
[quo/dropdown
{:background :blur
:size :size-32
:type :outline
:icon? true
:icon-name :i/online
:on-press not-implemented/alert}
(i18n/label :t/online)]]]
[quo/text-combinations
{:container-style style/title-container
:emoji-hash emoji-string
:title full-name}]]))
(def view (quo.theme/with-theme f-view))
@@ -0,0 +1,83 @@
(ns status-im2.contexts.profile.settings.list-items
(:require [status-im2.common.not-implemented :as not-implemented]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def items
[[{:title (i18n/label :t/edit-profile)
:on-press not-implemented/alert
:image-props :i/edit
:image :icon
:action :arrow}
{:title (i18n/label :t/password)
:on-press not-implemented/alert
:image-props :i/password
:image :icon
:action :arrow}]
[{:title (i18n/label :t/messages)
:on-press not-implemented/alert
:image-props :i/messages
:image :icon
:action :arrow}
{:title (i18n/label :t/wallet)
:on-press not-implemented/alert
:image-props :i/wallet
:image :icon
:action :arrow}
{:title (i18n/label :t/dapps)
:on-press not-implemented/alert
:image-props :i/placeholder
:image :icon
:action :arrow}
{:title "Browser"
:on-press not-implemented/alert
:image-props :i/browser
:image :icon
:action :arrow}
{:title (i18n/label :t/keycard)
:on-press not-implemented/alert
:image-props :i/keycard
:image :icon
:action :arrow}]
[{:title (i18n/label :t/syncing)
:on-press not-implemented/alert
:image-props :i/syncing
:image :icon
:action :arrow}
{:title (i18n/label :t/notifications)
:on-press not-implemented/alert
:image-props :i/notifications
:image :icon
:action :arrow}
{:title (i18n/label :t/appearance)
:on-press not-implemented/alert
:image-props :i/light
:image :icon
:action :arrow}
{:title (i18n/label :t/language-and-currency)
:on-press not-implemented/alert
:image-props :i/globe
:image :icon
:action :arrow}]
[{:title (i18n/label :t/data-usage)
:on-press not-implemented/alert
:image-props :i/mobile
:image :icon
:action :arrow}
{:title (i18n/label :t/advanced)
:on-press not-implemented/alert
:image-props :i/settings
:image :icon
:action :arrow}]
;; temporary link to legacy settings
[{:title "Legacy settings"
:on-press #(rf/dispatch [:navigate-to :my-profile])
:action :arrow
:image :icon
:image-props :i/toggle}]
[{:title (i18n/label :t/about)
:on-press not-implemented/alert
:action :arrow}
{:title (i18n/label :t/status-help)
:on-press not-implemented/alert
:action :arrow}]])
@@ -0,0 +1,11 @@
(ns status-im2.contexts.profile.settings.style
(:require [quo.foundations.colors :as colors]))
(defn navigation-wrapper
[{:keys [customization-color inset theme]}]
{:padding-top inset
:background-color (colors/resolve-color customization-color theme 40)})
(def footer-container
{:padding-horizontal 20
:padding-vertical 12})
@@ -0,0 +1,71 @@
(ns status-im2.contexts.profile.settings.view
(:require [oops.core :as oops]
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.contexts.profile.settings.header.view :as settings.header]
[status-im2.contexts.profile.settings.list-items :as settings.items]
[status-im2.contexts.profile.settings.style :as style]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- settings-item-view
[data]
[quo/category
{:list-type :settings
:blur? true
:data data}])
(defn scroll-handler
[event scroll-y]
(let [current-y (oops/oget event "nativeEvent.contentOffset.y")]
(reanimated/set-shared-value scroll-y current-y)))
(defn- footer
[logout-press]
[rn/view {:style style/footer-container}
[quo/button
{:on-press logout-press
:type :danger
:icon-left :i/log-out}
(i18n/label :t/logout)]])
(defn- settings-view
[theme]
(let [insets (safe-area/get-insets)
customization-color (rf/sub [:profile/customization-color])
scroll-y (reanimated/use-shared-value 0)
logout-press #(rf/dispatch [:multiaccounts.logout.ui/logout-pressed])]
[quo/overlay {:type :shell}
[rn/view
{:style (style/navigation-wrapper {:customization-color customization-color
:inset (:top insets)
:theme theme})}
[quo/page-nav
{:background :blur
:icon-name :i/close
:on-press #(rf/dispatch [:navigate-back])
:right-side [{:icon-name :i/multi-profile :on-press #(rf/dispatch [:open-modal :sign-in])}
{:icon-name :i/qr-code :on-press not-implemented/alert}
{:icon-name :i/share
:on-press #(debounce/dispatch-and-chill [:open-modal :share-shell] 1000)}]}]]
[rn/flat-list
{:header [settings.header/view {:scroll-y scroll-y}]
:data settings.items/items
:key-fn :title
:shows-vertical-scroll-indicator false
:render-fn settings-item-view
:footer [footer logout-press]
:scroll-event-throttle 16
:on-scroll #(scroll-handler % scroll-y)
:bounces false}]]))
(defn- internal-view
[props]
[:f> settings-view props])
(def view (quo.theme/with-theme internal-view))
@@ -0,0 +1,47 @@
(ns status-im2.contexts.quo-preview.dropdowns.dropdown-input
(:require
[quo.core :as quo]
[reagent.core :as reagent]
[status-im2.contexts.quo-preview.preview :as preview]))
(def descriptor
[{:key :state
:type :select
:options [{:key :default}
{:key :active}
{:key :disabled}]}
{:key :icon?
:type :boolean}
{:key :label?
:type :boolean}
{:key :blur?
:type :boolean}
{:key :header-label
:type :text}
{:key :label
:type :text}])
(defn view
[]
(let [state (reagent/atom {:state :default
:label "Dropdown"
:header-label "Label"
:icon? true
:label? true
:blur? false})
label (reagent/cursor state [:label])
blur? (reagent/cursor state [:blur?])]
[:f>
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:component-container-style (when-not @blur?
{:align-items :center
:margin-horizontal 30})
:blur-container-style {:align-items :center}
:blur? @blur?
:show-blur-background? true}
[quo/dropdown-input
(assoc @state :on-press #(js/alert "Pressed dropdown"))
@label]])]))
@@ -58,6 +58,8 @@
[status-im2.contexts.quo-preview.drawers.permission-drawers :as
permission-drawers]
[status-im2.contexts.quo-preview.dropdowns.dropdown :as dropdown]
[status-im2.contexts.quo-preview.dropdowns.dropdown-input :as
dropdown-input]
[status-im2.contexts.quo-preview.dropdowns.network-dropdown :as
network-dropdown]
[status-im2.contexts.quo-preview.empty-state.empty-state :as empty-state]
@@ -275,7 +277,9 @@
:dropdowns [{:name :dropdown
:component dropdown/view}
{:name :network-dropdown
:component network-dropdown/view}]
:component network-dropdown/view}
{:name :dropdown-input
:component dropdown-input/view}]
:empty-state [{:name :empty-state
:component empty-state/view}]
:gradient [{:name :gradient-cover
@@ -16,7 +16,7 @@
{:key :graph}
{:key :preview}]}
{:type :select
:key :description
:key :subtitle-type
:options [{:key :default}
{:key :icon}
{:key :network}
@@ -39,14 +39,14 @@
[]
(let [state (reagent/atom {:on-press #(js/alert (str "pressed"))
:blur? false
:description :account
:subtitle-type :account
:icon-right? false
:card? true
:label :none
:status :default
:size :default
:title "Label"
:subtitle "Description"
:subtitle "Subtitle"
:icon :i/placeholder
:right-icon :i/chevron-right
:emoji "🎮"
@@ -8,8 +8,8 @@
(def descriptor
[{:key :title :type :text}
{:key :avatar :type :boolean}
{:key :description
:type :text}])
{:key :description :type :text}
{:key :emoji-hash :type :text}])
(defn state->text-combinations-props
[state]
@@ -22,7 +22,8 @@
(let [state (reagent/atom {:title "Title"
:title-accessibility-label :title
:description ""
:description-accessibility-label :subtitle})]
:description-accessibility-label :subtitle
:emoji-hash "🐲🍀🎭🌟🚀🐠🌈🏰🔮🦉🐼🍉🎨🚲🌙🍔🌵"})]
(fn []
[preview/preview-container
{:state state
@@ -1,31 +1,12 @@
(ns status-im2.contexts.wallet.account.tabs.about.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im2.contexts.wallet.account.tabs.about.style :as style]
[status-im2.contexts.wallet.common.temp :as temp]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn description
[{:keys [address theme]}]
(let [networks-list (rf/sub [:wallet/network-details])]
[quo/text {:size :paragraph-2}
(map (fn [{:keys [chain-id short-name network-name]}]
^{:key (str chain-id short-name)}
[quo/text
{:size :paragraph-2
:weight :medium
:style {:color (colors/resolve-color network-name theme)}}
(str short-name ":")])
networks-list)
[quo/text
{:size :paragraph-2
:weight :monospace}
address]]))
(defn about-options
[]
[quo/action-drawer
@@ -51,19 +32,25 @@
:accessibility-label :share-address
:label (i18n/label :t/share-address)}]]])
(defn- view-internal
(defn view
[]
[rn/view {:style style/about-tab}
[quo/data-item
(merge temp/data-item-state
{:custom-subtitle (fn [] [quo/address-text
{:networks [{:network-name :ethereum :short-name "eth"}
{:network-name :optimism :short-name "opt"}
{:network-name :arbitrum :short-name "arb1"}]
:address temp/address
:format :long}])
:container-style {:margin-bottom 12}
:on-press #(rf/dispatch [:show-bottom-sheet {:content about-options}])})]
[quo/account-origin temp/account-origin-state]])
(def view (quo.theme/with-theme view-internal))
(let [{:keys [type address]} (rf/sub [:wallet/current-viewing-account])
networks (rf/sub [:wallet/network-details])
watch-only? (= type :watch)]
[rn/view {:style style/about-tab}
[quo/data-item
{:description :default
:icon-right? true
:right-icon :i/options
:card? true
:label :none
:status :default
:size :default
:title (if watch-only? (i18n/label :t/watched-address) (i18n/label :t/address))
:custom-subtitle (fn [] [quo/address-text
{:networks networks
:address address
:format :long}])
:container-style {:margin-bottom 12}
:on-press #(rf/dispatch [:show-bottom-sheet {:content about-options}])}]
(when (not watch-only?) [quo/account-origin temp/account-origin-state])]))
@@ -21,37 +21,41 @@
:padding-bottom 8}
:render-fn quo/settings-item}]])
(def tabs-data
[{:id :assets :label (i18n/label :t/assets) :accessibility-label :assets-tab}
{:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}
{:id :dapps :label (i18n/label :t/dapps) :accessibility-label :dapps}
{:id :about :label (i18n/label :t/about) :accessibility-label :about}])
(def first-tab-id :assets)
(defn tabs-data
[watch-only?]
(cond-> [{:id :assets :label (i18n/label :t/assets) :accessibility-label :assets-tab}
{:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}]
(not watch-only?) (conj {:id :dapps :label (i18n/label :t/dapps) :accessibility-label :dapps})
true (conj {:id :about :label (i18n/label :t/about) :accessibility-label :about})))
(defn view
[]
(let [selected-tab (reagent/atom (:id (first tabs-data)))]
(let [selected-tab (reagent/atom first-tab-id)]
(fn []
(let [{:keys [name color balance]} (rf/sub [:wallet/current-viewing-account])
]
(let [{:keys [name color balance type]} (rf/sub [:wallet/current-viewing-account])
watch-only? (= type :watch)]
[rn/view {:style {:flex 1}}
[account-switcher/view {:on-press #(rf/dispatch [:wallet/close-account-page])}]
[quo/account-overview
{:current-value (utils/prettify-balance balance)
:account-name name
:account :default
:account (if watch-only? :watched-address :default)
:customization-color color}]
[quo/wallet-graph {:time-frame :empty}]
[quo/wallet-ctas
{:send-action #(rf/dispatch [:open-modal :wallet-select-address])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-drawer}])
:bridge-action #(rf/dispatch [:open-modal :wallet-bridge])}]
(when (not watch-only?)
[quo/wallet-ctas
{:send-action #(rf/dispatch [:open-modal :wallet-select-address])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-drawer}])
:bridge-action #(rf/dispatch [:open-modal :wallet-bridge])}])
[quo/tabs
{:style style/tabs
:size 32
:default-active @selected-tab
:data tabs-data
:data (tabs-data watch-only?)
:on-change #(reset! selected-tab %)
:scrollable? true
:scroll-on-press? true}]
@@ -13,11 +13,11 @@
(h/describe "select address for watch only account"
(h/test "validation messages show for already used addressed"
(setup-subs {:wallet/scanned-address nil
:wallet/addresses (set
["0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"])
:profile/customization-color :blue})
(setup-subs {:wallet/scanned-address nil
:wallet/addresses #{"0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"}
:wallet/watch-address-activity-state nil
:profile/customization-color :blue})
(h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text
@@ -26,11 +26,11 @@
(h/is-truthy (h/get-by-translation-text :address-already-in-use))))
(h/test "validation messages show for invalid address"
(setup-subs {:wallet/scanned-address nil
:wallet/addresses (set
["0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"])
:profile/customization-color :blue})
(setup-subs {:wallet/scanned-address nil
:wallet/addresses #{"0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"}
:wallet/watch-address-activity-state nil
:profile/customization-color :blue})
(h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "0x12E838Ae1f769147b")
@@ -0,0 +1,26 @@
(ns status-im2.contexts.wallet.add-address-to-watch.confirm-address.component-spec
(:require
[re-frame.core :as re-frame]
[status-im2.contexts.wallet.add-address-to-watch.confirm-address.view :as confirm-address]
[test-helpers.component :as h]
[utils.re-frame :as rf]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(h/describe "Add Watch Only Account Page"
(h/test "Create Account button is disabled while no account name exists"
(let [callback (h/mock-fn)]
(with-redefs [rf/dispatch #(callback)]
(setup-subs {:profile/wallet-accounts []
:get-screen-params {:address "0xmock-address"}})
(h/render [confirm-address/view {}])
(h/is-truthy (h/get-by-text "0xmock-address"))
(h/was-not-called callback)
(h/fire-event :change-text (h/get-by-label-text :profile-title-input) "NAME")
(h/fire-event :press (h/get-by-translation-text :t/add-watched-address))
(h/was-called callback)))))
@@ -3,7 +3,6 @@
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[re-frame.core :as re-frame]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.contexts.emoji-picker.utils :as emoji-picker.utils]
@@ -43,8 +42,14 @@
:bottom-action-label :t/add-watched-address
:bottom-action-props {:customization-color @account-color
:disabled? (string/blank? @account-name)
:on-press #(re-frame/dispatch [:navigate-to
:wallet-account])}}
:on-press #(rf/dispatch [:wallet/add-account
nil
{:type :watch
:account-name @account-name
:emoji @account-emoji
:color @account-color}
{:address address
:public-key ""}])}}
[quo/data-item
{:card? true
:right-icon :i/advanced
@@ -54,5 +59,12 @@
:subtitle address
:status :default
:size :default
:subtitle-type :default
:custom-subtitle (fn [] [quo/text
{:size :paragraph-2
;; TODO: monospace font
;; https://github.com/status-im/status-mobile/issues/17009
:weight :monospace}
address])
:container-style style/data-item
:on-press #(js/alert "To be implemented")}]]])))
@@ -5,15 +5,8 @@
:margin-top 12
:margin-bottom 20})
(def button-container
{:position :absolute
:bottom 22
:left 20
:right 20})
(def scan
{:align-self
:flex-end})
{:margin-top 26})
(def input
{:flex 1
@@ -5,6 +5,7 @@
[react-native.clipboard :as clipboard]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.common.floating-button-page.view :as floating-button-page]
[status-im2.contexts.wallet.add-address-to-watch.style :as style]
[status-im2.contexts.wallet.common.validation :as validation]
[utils.i18n :as i18n]
@@ -26,11 +27,14 @@
(let [scanned-address (rf/sub [:wallet/scanned-address])
empty-input? (and (string/blank? @input-value)
(string/blank? scanned-address))
on-change-text (fn [new-text]
(reset! validation-msg (validate new-text))
(reset! input-value new-text)
(if (and (not-empty new-text) (nil? (validate new-text)))
(rf/dispatch [:wallet/get-address-details new-text])
(rf/dispatch [:wallet/clear-address-activity-check]))
(when (and scanned-address (not= scanned-address new-text))
(rf/dispatch [:wallet/clear-address-activity-check])
(rf/dispatch [:wallet/clean-scanned-address])))
paste-on-input #(clipboard/get-string
(fn [clipboard-text]
@@ -66,6 +70,32 @@
:icon-only? true}
:i/scan]]))
(defn activity-indicator
[]
(let [activity-state (rf/sub [:wallet/watch-address-activity-state])
{:keys [accessibility-label icon type message]}
(case activity-state
:has-activity {:accessibility-label :account-has-activity
:icon :i/done
:type :success
:message :t/this-address-has-activity}
:no-activity {:accessibility-label :account-has-no-activity
:icon :i/info
:type :warning
:message :t/this-address-has-no-activity}
{:accessibility-label :searching-for-activity
:icon :i/pending-state
:type :default
:message :t/searching-for-activity})]
(when activity-state
[quo/info-message
{:accessibility-label accessibility-label
:size :default
:icon icon
:type type
:style style/info-message}
(i18n/label message)])))
(defn view
[]
(let [addresses (rf/sub [:wallet/addresses])
@@ -76,40 +106,46 @@
clear-input (fn []
(reset! input-value nil)
(reset! validation-msg nil)
(rf/dispatch [:wallet/clear-address-activity-check])
(rf/dispatch [:wallet/clean-scanned-address]))
customization-color (rf/sub [:profile/customization-color])]
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:wallet/clear-address-activity-check])
(fn []
[rn/view
{:style {:flex 1}}
[quo/page-nav
{:type :no-title
:icon-name :i/close
:on-press (fn []
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:navigate-back]))}]
[quo/text-combinations
{:container-style style/header-container
:title (i18n/label :t/add-address)
:description (i18n/label :t/enter-eth)}]
[:f> address-input
{:input-value input-value
:validate validate
:validation-msg validation-msg
:clear-input clear-input}]
(when @validation-msg
[quo/info-message
{:accessibility-label :error-message
:size :default
:icon :i/info
:type :error
:style style/info-message}
@validation-msg])
[quo/button
{:customization-color customization-color
:disabled? (string/blank? @input-value)
:on-press #(rf/dispatch [:navigate-to
:confirm-address-to-watch
{:address @input-value}])
:container-style style/button-container}
(i18n/label :t/continue)]])))
[floating-button-page/view
{:header [quo/page-nav
{:type :no-title
:icon-name :i/close
:on-press (fn []
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:wallet/clear-address-activity-check])
(rf/dispatch [:navigate-back]))}]
:footer
[quo/button
{:customization-color customization-color
:disabled? (or (string/blank? @input-value) (some? (validate @input-value)))
:on-press #(rf/dispatch [:navigate-to
:confirm-address-to-watch
{:address @input-value}])
:container-style {:z-index 2}}
(i18n/label :t/continue)]}
[quo/text-combinations
{:container-style style/header-container
:title (i18n/label :t/add-address)
:description (i18n/label :t/enter-eth)}]
[:f> address-input
{:input-value input-value
:validate validate
:validation-msg validation-msg
:clear-input clear-input}]
(when @validation-msg
[quo/info-message
{:accessibility-label :error-message
:size :default
:icon :i/info
:type :error
:style style/info-message}
@validation-msg])
[activity-indicator]]])))
@@ -68,12 +68,12 @@
[rn/flat-list
{:render-fn (fn [{:keys [trait-type value]}]
[quo/data-item
{:description :default
:card? true
{:card? true
:status :default
:size :default
:title trait-type
:subtitle value
:subtitle-type :default
:container-style style/traits-item}])
:data traits
:key :collectibles-list
@@ -91,24 +91,24 @@
{:style style/info-container}
[rn/view {:style style/account}
[quo/data-item
{:description :account
:card? true
{:card? true
:status :default
:size :default
:title (i18n/label :t/account-title)
:subtitle "Collectibles vault"
:subtitle-type :account
:emoji "🎮"
:customization-color :yellow}]]
[rn/view {:style style/network}
[quo/data-item
{:description :network
:card? true
{:card? true
:status :default
:size :default
:title (i18n/label :t/network)
:network-image (quo.resources/get-network network-keyword)
:subtitle network-name}]]]))
:subtitle network-name
:subtitle-type :network}]]]))
(defn view
[]
@@ -44,12 +44,12 @@
[quo/data-item
{:status :default
:size :default
:description :default
:label :none
:blur? false
:card? true
:title (i18n/label :t/address)
:subtitle address
:subtitle-type :default
:container-style (merge style/data-item
{:background-color (colors/theme-colors colors/neutral-2_5
colors/neutral-90
@@ -1,28 +1,11 @@
(ns status-im2.contexts.wallet.common.temp
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[react-native.core :as rn]
[status-im2.common.resources :as status.resources]
[status-im2.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn wallet-temporary-navigation
[]
[rn/view
{:style {:flex 1
:align-items :center
:justify-content :center}}
[quo/text {} "TEMPORARY NAVIGATION"]
[quo/button {:on-press #(rf/dispatch [:navigate-to :wallet-accounts])}
"Navigate to Account"]
[quo/button {:on-press #(rf/dispatch [:navigate-to :wallet-create-account])}
"Create Account"]
[quo/button {:on-press #(rf/dispatch [:navigate-to :wallet-saved-addresses])}
"Saved Addresses"]])
[utils.i18n :as i18n]))
(defn wallet-overview-state
[networks]
@@ -36,12 +36,15 @@
(map #(calculate-raw-balance (:raw-balance %) decimals))
(reduce +)))
(defn calculate-balance-for-token
[token]
(* (total-token-value-in-all-chains token)
(-> token :market-values-per-currency :usd :price)))
(defn calculate-balance
[tokens-in-account]
(->> tokens-in-account
(map (fn [token]
(* (total-token-value-in-all-chains token)
(-> token :market-values-per-currency :usd :price))))
(map #(calculate-balance-for-token %))
(reduce +)))
(defn network-list
@@ -79,7 +79,7 @@
[quo/data-item
{:status :default
:size :default
:description :default
:subtitle-type :default
:label :none
:blur? false
:icon-right? true
+188 -42
View File
@@ -9,8 +9,10 @@
[status-im2.common.data-store.wallet :as data-store]
[status-im2.contexts.wallet.temp :as temp]
[taoensso.timbre :as log]
[utils.datetime :as datetime]
[utils.ethereum.chain :as chain]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.number]
[utils.re-frame :as rf]
[utils.security.core :as security]
@@ -18,14 +20,14 @@
(rf/reg-event-fx :wallet/show-account-created-toast
(fn [{:keys [db]} [address]]
(let [{:keys [name]} (get-in db [:wallet :accounts address])]
(let [account (get-in db [:wallet :accounts address])]
{:db (update db :wallet dissoc :navigate-to-account :new-account?)
:fx [[:dispatch
[:toasts/upsert
{:id :new-wallet-account-created
:icon :i/correct
:icon-color colors/success-50
:text (i18n/label :t/account-created {:name name})}]]]})))
:text (i18n/label :t/account-created {:name (:name account)})}]]]})))
(rf/reg-event-fx :wallet/navigate-to-account
(fn [{:keys [db]} [address]]
@@ -44,24 +46,17 @@
(rf/reg-event-fx
:wallet/get-accounts-success
(fn [{:keys [db]} [accounts]]
(let [wallet-db (get db :wallet)
(let [wallet-accounts (filter #(not (:chat %)) accounts)
wallet-db (get db :wallet)
new-account? (:new-account? wallet-db)
navigate-to-account (:navigate-to-account wallet-db)]
(cond-> {:db (reduce (fn [db {:keys [address] :as account}]
(assoc-in db [:wallet :accounts address] account))
db
(data-store/rpc->accounts accounts))
:fx [[:dispatch [:wallet/get-wallet-token]]]}
new-account?
(update :fx
conj
[:dispatch [:hide-bottom-sheet]]
[:dispatch-later
[{:dispatch [:navigate-back]
:ms 100}
{:dispatch [:wallet/navigate-to-account navigate-to-account]
:ms 300}]])))))
{:db (reduce (fn [db {:keys [address] :as account}]
(assoc-in db [:wallet :accounts address] account))
db
(data-store/rpc->accounts wallet-accounts))
:fx [[:dispatch [:wallet/get-wallet-token]]
(when new-account?
[:dispatch [:wallet/navigate-to-new-account navigate-to-account]])]})))
(rf/reg-event-fx
:wallet/get-accounts
@@ -132,7 +127,7 @@
(rf/defn clean-scanned-address
{:events [:wallet/clean-scanned-address]}
[{:keys [db]}]
{:db (dissoc db :wallet/scanned-address :wallet/send-address)})
{:db (update-in db [:wallet :ui :send] dissoc :to-address)})
(rf/reg-event-fx :wallet/create-derived-addresses
(fn [{:keys [db]} [password {:keys [path]} on-success]]
@@ -144,29 +139,35 @@
:on-success on-success
:on-error #(log/info "failed to derive address " %)}]]]})))
(rf/reg-event-fx
:wallet/add-account
(fn [{:keys [db]} [password {:keys [emoji account-name color]} {:keys [public-key address path]}]]
(let [key-uid (get-in db [:profile/profile :key-uid])
sha3-pwd (native-module/sha3 (security/safe-unmask-data password))
account-config {:key-uid key-uid
:wallet false
:chat false
:type :generated
:name account-name
:emoji emoji
:path path
:address address
:public-key public-key
:colorID color}]
{:db (update db
:wallet assoc
:navigate-to-account address
:new-account? true)
:fx [[:json-rpc/call
(rf/reg-event-fx :wallet/add-account-success
(fn [{:keys [db]} [address]]
{:db (update db
:wallet assoc
:navigate-to-account address
:new-account? true)
:fx [[:dispatch [:wallet/get-accounts]]]}))
(rf/reg-event-fx :wallet/add-account
(fn [{:keys [db]}
[password {:keys [emoji account-name color type] :or {type :generated}}
{:keys [public-key address path]}]]
(let [lowercase-address (if address (string/lower-case address) address)
key-uid (get-in db [:profile/profile :key-uid])
sha3-pwd (native-module/sha3 (security/safe-unmask-data password))
account-config {:key-uid (when (= type :generated) key-uid)
:wallet false
:chat false
:type type
:name account-name
:emoji emoji
:path path
:address lowercase-address
:public-key public-key
:colorID color}]
{:fx [[:json-rpc/call
[{:method "accounts_addAccount"
:params [sha3-pwd account-config]
:on-success [:wallet/get-accounts]
:params [(when (= type :generated) sha3-pwd) account-config]
:on-success [:wallet/add-account-success lowercase-address]
:on-error #(log/info "failed to create account " %)}]]]})))
(rf/reg-event-fx
@@ -354,4 +355,149 @@
(rf/reg-event-fx :wallet/select-send-address
(fn [{:keys [db]} [address]]
{:db (assoc db :wallet/send-address address)}))
{:db (assoc-in db [:wallet :ui :send :to-address] address)}))
(rf/reg-event-fx :wallet/get-address-details-success
(fn [{:keys [db]} [{:keys [hasActivity]}]]
{:db (assoc-in db
[:wallet :ui :watch-address-activity-state]
(if hasActivity :has-activity :no-activity))}))
(rf/reg-event-fx :wallet/clear-address-activity-check
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :watch-address-activity-state)}))
(rf/reg-event-fx :wallet/get-address-details
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :ui :watch-address-activity-state] :scanning)
:fx [[:json-rpc/call
[{:method "wallet_getAddressDetails"
:params [(chain/chain-id db) address]
:on-success [:wallet/get-address-details-success]
:on-error #(log/info "failed to get address details"
{:error %
:event :wallet/get-address-details})}]]]}))
(rf/reg-event-fx :wallet/send-select-token
(fn [{:keys [db]} [token stack-id]]
{:db (assoc-in db [:wallet :ui :send :token] token)
:fx [[:navigate-to-within-stack [:wallet-send-input-amount stack-id]]]}))
(rf/reg-event-fx :wallet/send-select-amount
(fn [{:keys [db]} [amount stack-id]]
{:db (assoc-in db [:wallet :ui :send :amount] amount)
:fx [[:navigate-to-within-stack [:wallet-transaction-confirmation stack-id]]]}))
(rf/reg-event-fx :wallet/suggested-routes-success
(fn [{:keys [db]} [suggested-routes timestamp]]
(when (= (get-in db [:wallet :ui :send :suggested-routes-call-timestamp]) timestamp)
{:db (-> db
(assoc-in [:wallet :ui :send :suggested-routes] suggested-routes)
(assoc-in [:wallet :ui :send :route] (first (:Best suggested-routes)))
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false))})))
(rf/reg-event-fx :wallet/suggested-routes-error
(fn [{:keys [db]} [_error]]
{:db (-> db
(update-in [:wallet :ui :send] dissoc :suggested-routes)
(update-in [:wallet :ui :send] dissoc :route)
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false))}))
(rf/reg-event-fx :wallet/clean-suggested-routes
(fn [{:keys [db]}]
{:db (-> db
(update-in [:wallet :ui :send] dissoc :suggested-routes)
(update-in [:wallet :ui :send] dissoc :route)
(update-in [:wallet :ui :send] dissoc :loading-suggested-routes?))}))
(rf/reg-event-fx :wallet/get-suggested-routes
(fn [{:keys [db]} [amount]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
token (get-in db [:wallet :ui :send :token])
to-address (get-in db [:wallet :ui :send :to-address])
token-decimal 18
token-id (:symbol token)
value_ (js/parseFloat amount)
network-preferences [1]
gas-rates 0 ;low
amount-in (money/mul (money/bignumber value_)
(money/from-decimal token-decimal))
from-address wallet-address
request-params [0
from-address
to-address
(money/to-hex amount-in)
token-id
[]
[]
network-preferences
gas-rates
{}]
timestamp (datetime/timestamp)]
(prn "=====" request-params)
{:db (-> db
(assoc-in [:wallet :ui :send :loading-suggested-routes?] true)
(assoc-in [:wallet :ui :send :suggested-routes-call-timestamp]
timestamp))
:json-rpc/call [{:method "wallet_getSuggestedRoutes"
:params request-params
:on-success (fn [suggested-routes]
(rf/dispatch [:wallet/suggested-routes-success suggested-routes
timestamp]))
:on-error (fn [error]
(rf/dispatch [:wallet/suggested-routes-error error])
(log/error "failed to get suggested routes"
{:event :wallet/get-suggested-routes
:error error
:params request-params}))}]})))
(rf/reg-event-fx :wallet/send-transaction
(fn [{:keys [db]} [password]]
(let [route (get-in db [:wallet :ui :send :route])
from-address (get-in db [:wallet :current-viewing-account-address])
to-address (get-in db [:wallet :ui :send :to-address])
from (:From route)
token (get-in db [:wallet :ui :send :token])
token-id (:symbol token)
from-asset token-id
to-asset token-id
bridge-name (:BridgeName route)
chain-id (:chainId from)
multi-transaction-command {:fromAddress from-address
:toAddress to-address
:fromAsset from-asset
:toAsset to-asset
:fromAmount (:AmountOut route)
:type 0}
transaction-bridge
[{:BridgeName bridge-name
:ChainID chain-id
:TransferTx {:From from-address
:To to-address
:Gas (money/to-hex (:GasAmount route))
:GasPrice (money/to-hex (money/->wei :gwei
(:gasPrice (:GasFees route))))
:Value (:AmountOut route)
:Nonce nil
:MaxFeePerGas (money/to-hex
(money/->wei :gwei
(:maxFeePerGasMedium (:GasFees route))))
:MaxPriorityFeePerGas (money/to-hex (money/->wei :gwei
(:maxPriorityFeePerGas
(:GasFees
route))))
:Input ""
:Data "0x"}}]
sha3-pwd (native-module/sha3 (str (security/safe-unmask-data password)))
request-params [multi-transaction-command transaction-bridge sha3-pwd]]
(prn "=====" request-params)
{:json-rpc/call [{:method "wallet_createMultiTransaction"
:params request-params
:on-success (fn [response]
(prn response "======+++++++++========++++++++")
(rf/dispatch [:dismiss-modal :wallet-select-address]))
:on-error (fn [error]
(log/error "failed to send transaction"
{:event :wallet/send-transaction
:error error
:params request-params}))}]})))
@@ -0,0 +1,88 @@
(ns status-im2.contexts.wallet.send.input-amount.component-spec
(:require
[re-frame.core :as re-frame]
[status-im2.contexts.wallet.send.input-amount.view :as input-amount]
[test-helpers.component :as h]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(def sub-mocks
{:profile/profile {:currency :usd}
:wallet/network-details [{:source 525
:short-name "eth"
:network-name :ethereum
:chain-id 1
:related-chain-id 5}]})
(h/describe "Send > input amount screen"
(h/test "Default render"
(setup-subs sub-mocks)
(h/render [input-amount/view {}])
(h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "ETH"))
(h/is-truthy (h/get-by-text "$0.00"))
(h/is-disabled (h/get-by-label-text :button-one)))
(h/test "Fill token input and confirm"
(setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render [input-amount/view
{:on-confirm on-confirm
:rate 10}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-3))
(h/fire-event :press (h/query-by-label-text :keyboard-key-.))
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$1234.50")))
(h/is-truthy (h/get-by-label-text :button-one))
(h/fire-event :press (h/get-by-label-text :button-one))
(h/was-called on-confirm)))
(h/test "Try to fill more than limit"
(setup-subs sub-mocks)
(h/render [input-amount/view
{:rate 10
:limit 286}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$290.00")))
(h/fire-event :press (h/query-by-label-text :keyboard-key-backspace))
(h/fire-event :press (h/query-by-label-text :keyboard-key-8))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$2850.00"))))
(h/test "Switch from crypto to fiat and check limit"
(setup-subs sub-mocks)
(h/render [input-amount/view
{:rate 10
:limit 250}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-0))
(h/wait-for #(h/is-truthy (h/get-by-text "$200.00")))
(h/fire-event :press (h/query-by-label-text :reorder))
(h/wait-for #(h/is-truthy (h/get-by-text "2.00 ETH")))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "205.50 ETH")))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "205.50 ETH")))))
@@ -0,0 +1,12 @@
(ns status-im2.contexts.wallet.send.input-amount.style)
(def screen
{:flex 1})
(def input-container
{:padding-top 12
:padding-bottom 0})
(defn keyboard-container
[bottom]
{:padding-bottom bottom})
@@ -0,0 +1,169 @@
(ns status-im2.contexts.wallet.send.input-amount.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im2.contexts.wallet.send.input-amount.style :as style]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- make-limit-label
[{:keys [amount currency]}]
(str amount " " (string/upper-case (name currency))))
(def not-digits-or-dot-pattern
#"[^0-9+\.]")
(def dot ".")
(defn valid-input?
[current v]
(let [max-length 12
length-owerflow? (>= (count current) max-length)
extra-dot? (and (= v dot) (string/includes? current dot))
extra-leading-zero? (and (= current "0") (= "0" (str v)))
non-numeric? (re-find not-digits-or-dot-pattern (str v))]
(not (or non-numeric? extra-dot? extra-leading-zero? length-owerflow?))))
(defn- normalize-input
[current v]
(cond
(and (string/blank? current) (= v dot))
(str "0" v)
(and (= current "0") (not= v dot))
(str v)
:else
(str current v)))
(defn- make-new-input
[current v]
(if (valid-input? current v)
(normalize-input current v)
current))
(defn- f-view-internal
[{:keys [rate]}]
(let [bottom (safe-area/get-bottom)
{:keys [currency]} (rf/sub [:profile/profile])
networks (rf/sub [:wallet/network-details])
wallet-send (get-in (rf/sub [:wallet]) [:ui :send])
token (:token wallet-send)
token-symbol (:symbol token)
limit-crypto (:total-balance token)
limit-fiat (:total-balance-fiat token)
conversion-rate (or rate 10)
input-value (reagent/atom "")
current-limit (reagent/atom {:amount limit-crypto
:currency token-symbol})
loading-suggested-routes? (:loading-suggested-routes? wallet-send)
handle-swap (fn [crypto?]
(let [num-value (parse-double @input-value)]
(reset! current-limit (if crypto?
{:amount limit-crypto
:currency token-symbol}
{:amount limit-fiat
:currency currency}))
(when (> num-value (:amount @current-limit))
(reset! input-value ""))))
handle-keyboard-press (fn [v]
(let [current-value @input-value
new-value (make-new-input current-value v)
num-value (or (parse-double new-value) 0)]
(when (and (not loading-suggested-routes?)
(<= num-value (:amount @current-limit)))
(reset! input-value new-value)
(reagent/flush))))
handle-delete (fn [_]
(when-not loading-suggested-routes?
(swap! input-value #(subs % 0 (dec (count %))))
(reagent/flush)))
handle-on-change (fn [v]
(when (valid-input? @input-value v)
(let [num-value (or (parse-double v) 0)
current-limit-amount (:amount @current-limit)]
(if (> num-value current-limit-amount)
(reset! input-value (str current-limit-amount))
(reset! input-value v))
(reagent/flush))))]
(fn [{:keys [on-confirm]
:or {on-confirm #(rf/dispatch [:wallet/send-select-amount @input-value
:wallet-send-input-amount])}}]
(let [limit-label (make-limit-label @current-limit)
input-num-value (parse-double @input-value)
route (get-in (rf/sub [:wallet]) [:ui :send :route])
loading-suggested-routes? (get-in (rf/sub [:wallet]) [:ui :send :loading-suggested-routes?])
confirm-disabled? (or
(nil? route)
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value (:amount @current-limit)))]
(rn/use-effect
(fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
#(.remove app-keyboard-listener))))
(rn/use-effect (fn []
(rf/dispatch [:wallet/clean-suggested-routes])
(when-not (or
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value (:amount @current-limit)))
(debounce/debounce-and-dispatch [:wallet/get-suggested-routes @input-value]
100)))
[@input-value])
[rn/view
{:style style/screen}
[quo/page-nav
{:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back-within-stack :wallet-send-input-amount])
:right-side :account-switcher
:account-switcher {:customization-color :yellow
:emoji "🎮"
:on-press #(js/alert "Switch account")}}]
[quo/token-input
{:container-style style/input-container
:token token-symbol
:currency currency
:networks networks
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:on-swap handle-swap
:on-change-text (fn [text]
(handle-on-change text))}]
;; Network routing content to be added
[rn/scroll-view
{:content-container-style {:flex-grow 1
:align-items :center
:justify-content :center}}
(cond loading-suggested-routes?
[quo/text "Loading routes"]
(and (not loading-suggested-routes?) route)
[quo/text "Route found"]
(and (not loading-suggested-routes?) (nil? route))
[quo/text "Route not found"])]
[quo/bottom-actions
{:actions :1-action
:button-one-label (i18n/label :t/confirm)
:button-one-props {:disabled? confirm-disabled?
:on-press on-confirm}}]
[quo/numbered-keyboard
{:container-style (style/keyboard-container bottom)
:left-action :dot
:delete-key? true
:on-press handle-keyboard-press
:on-delete handle-delete}]]))))
(defn- view-internal
[props]
[:f> f-view-internal props])
(def view (quo.theme/with-theme view-internal))
@@ -48,7 +48,7 @@
[input-value input-focused?]
(fn []
(let [scanned-address (rf/sub [:wallet/scanned-address])
send-address (rf/sub [:wallet/send-address])
send-address (get-in (rf/sub [:wallet]) [:ui :send :to-address])
valid-ens-or-address? (rf/sub [:wallet/valid-ens-or-address?])]
[quo/address-input
{:on-focus (fn []
@@ -18,10 +18,13 @@
(defn- asset-component
[]
(fn [token _ _ _]
(let [on-press #(js/alert "Not implemented yet")
(let [on-press
#(if (= (:symbol token) "ETH")
(rf/dispatch [:wallet/send-select-token token :wallet-select-asset])
(js/alert "Only ETH transfers are allowed."))
total-balance-formatted (.toFixed (:total-balance token) 2)
balance-fiat-formatted (.toFixed (:total-balance-fiat token) 2)
currency-symbol "$"]
balance-fiat-formatted (.toFixed (:total-balance-fiat token) 2)
currency-symbol "$"]
[quo/token-network
{:token (quo.resources/get-token (keyword (string/lower-case (:symbol token))))
:label (:name token)
@@ -0,0 +1,41 @@
(ns status-im2.contexts.wallet.send.transaction-confirmation.style
(:require [quo.foundations.colors :as colors]))
(defn container
[margin-top]
{:position :absolute
:top margin-top
:right 0
:left 0
:bottom 0})
(def content-container
{:padding-top 12
:padding-horizontal 20
:padding-bottom 32})
(def title-container
{:margin-right 4})
(defn details-container
[theme]
{:flex-direction :row
:justify-content :space-between
:height 52
:padding-horizontal 12
:padding-top 7
:padding-bottom 8
:border-radius 16
:border-width 1
:border-color (colors/theme-colors colors/neutral-10 colors/neutral-90 theme)})
(def slide-button-container
{:position :absolute
:right 20
:left 20
:bottom 20})
(defn section-label
[theme]
{:margin-bottom 8
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
@@ -0,0 +1,202 @@
(ns status-im2.contexts.wallet.send.transaction-confirmation.view
(:require
[quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.utils.utils :as utils]
[status-im2.common.resources :as resources]
[status-im2.common.standard-authentication.core :as standard-auth]
[status-im2.contexts.wallet.send.transaction-confirmation.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- transaction-title
[token amount account to-address]
[rn/view {:style style/content-container}
[rn/view {:style {:flex-direction :row}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(i18n/label :t/send)]
[quo/summary-tag
{:label (str amount " " (:symbol token))
:type :token
:image-source (quo.resources/get-token :eth)}]]
[rn/view
{:style {:flex-direction :row
:margin-top 4}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(i18n/label :t/from)]
[quo/summary-tag
{:label (:name account)
:type :account
:emoji (:emoji account)
:customization-color (:color account)}]]
[rn/view
{:style {:flex-direction :row
:margin-top 4}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(i18n/label :t/to)]
[quo/summary-tag
{:type :address
:label (utils/get-shortened-address to-address)}]]])
(defn- transaction-from
[amount account-props theme]
[rn/view
{:style {:padding-horizontal 20
:padding-bottom 16}}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label :summary-from-label}
(i18n/label :t/from-capitalized)]
[quo/summary-info
{:type :status-account
:networks? true
:values {:ethereum amount
:optimism 0
:arbitrum 0}
:account-props account-props}]])
(defn- transaction-to
[amount user-props theme]
[rn/view
{:style {:padding-horizontal 20
:padding-bottom 16}}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label :summary-from-label}
(i18n/label :t/to-capitalized)]
[quo/summary-info
{:type :user
:networks? true
:values {:ethereum amount
:optimism 0
:arbitrum 0}
:account-props user-props}]])
(defn- transaction-details
[estimated-time-min max-fees-eth token amount to-address theme]
[rn/view
{:style {:padding-horizontal 20
:padding-bottom 16}}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label :summary-from-label}
(i18n/label :t/details)]
[rn/view
{:style (style/details-container theme)}
[quo/data-item
{:container-style {:flex 1
:height 36}
:blur? false
:description :default
:icon-right? false
:card? false
:label :none
:status :default
:size :small
:title (i18n/label :t/est-time)
:subtitle (str estimated-time-min " min")}]
[quo/data-item
{:container-style {:flex 1
:height 36}
:blur? false
:description :default
:icon-right? false
:card? false
:label :none
:status :default
:size :small
:title (i18n/label :t/max-fees)
:subtitle (str "$" max-fees-eth)}]
[quo/data-item
{:container-style {:flex 1
:height 36}
:blur? false
:description :default
:icon-right? false
:card? false
:label :none
:status :default
:size :small
:title (i18n/label :t/user-gets {:name (utils/get-shortened-address to-address)})
:subtitle (str amount " " (:symbol token))}]]])
(defn- f-view-internal
[_]
(let [margin-top (safe-area/get-top)
biometric-auth? false
on-close #(rf/dispatch [:navigate-back-within-stack :wallet-select-asset])
send-transaction-data (get-in (rf/sub [:wallet]) [:ui :send])
token (:token send-transaction-data)
amount (:amount send-transaction-data)
route (:route send-transaction-data)
estimated-time-min (:EstimatedTime route)
max-fees-eth 5.28 ; calculate gas price in fiat
to-address (:to-address send-transaction-data)
account (rf/sub [:wallet/current-viewing-account])
account-props {:customization-color (:color account)
:size 32
:emoji (:emoji account)
:type :default
:name (:name account)
:address (utils/get-shortened-address (:address account))}
user-props {:full-name "M L"
:status-indicator? false
:size :small
:ring-background (resources/get-mock-image :ring)
:customization-color :blue
:name "Mark Libot"
:address "0x0ah...78b"
:status-account (merge account-props
{:size 16
:name "New house"
:emoji "🍔"})}]
(fn [{:keys [theme]}]
[rn/view {:style {:flex 1}}
[quo/gradient-cover {:customization-color (:color account)}]
[rn/view {:style (style/container margin-top)}
[quo/page-nav
{:icon-name :i/arrow-left
:on-press on-close
:accessibility-label :top-bar
:right-side [{:icon-name :i/advanced
:on-press (fn callback [] nil)
:accessibility-label "Advanced"}]}]
[transaction-title token amount account to-address]
[transaction-from amount account-props theme]
[transaction-to amount user-props theme]
[transaction-details estimated-time-min max-fees-eth token amount to-address theme]
[rn/view {:style style/slide-button-container}
[standard-auth/slide-button
{:size :size-40
:track-text (i18n/label :t/slide-to-send)
:customization-color (:color account)
:on-enter-password #(rf/dispatch [:wallet/send-transaction %])
:biometric-auth? biometric-auth?
:auth-button-label (i18n/label :t/confirm)}]]]])))
(defn view-internal
[props]
[:f> f-view-internal props])
(def view (quo.theme/with-theme view-internal))
+39
View File
@@ -0,0 +1,39 @@
{:send-push-notifications? true
:send-status-updates? true
:key-uid "0x2a2d3b6722dcbcabaeae48adc0f4147bcb3d94b3cc9606da1a080b765ec5d9b1"
:backup-enabled? true
:address "0x88e052c915950060e6a51438a0524ca76b50d8f4"
:preview-privacy? true
:use-mailservers? true
:signing-phrase "beef flax tram"
:url-unfurling-mode 1
:profile-pictures-visibility 2
:messages-from-contacts-only false
:eip1581-address "0x56b5eb0c8d257a249eb1d6f5838cc356bfa9cbe7"
:name "Red Mediumslateblue Otter"
:latest-derived-path 0
:wallet-root-address "0xd00fc8f05dcf8f2676b066d9fb0e67db794eeb69"
:last-backup 1700127251
:networks/networks []
:gifs/api-key ""
:currency "usd"
:gifs/favorite-gifs nil
:default-sync-period 777600
:photo-path ""
:compressedKey "zQ3sha1FRZXLtZ5aG43vRhGbm1W16crtPMQ1CSbyyqSZXB7Bw"
:current-user-status {:statusType 1 :clock 1700127405 :text ""}
:dapps-address "0x5ffa75ce51c3a7ebe23bde37b5e3a0143dfbcee0"
:display-name "A1234"
:test-networks-enabled? true
:gifs/recent-gifs nil
:appearance 0
:emojiHash ["🫒" "👨🏽‍🏫" "🥄" "👦🏽" "👭" "🚣‍♂️" "🧓🏽" "👩🏿" "👨🏽‍⚖️" "👵🏿" "🕴🏻" "😦" "🙅"
"👩🏻‍🚀"]
:link-preview-request-enabled true
:profile-pictures-show-to 2
:device-name ""
:networks/current-network "mainnet_rpc"
:mutual-contact-enabled? false
:public-key
"0x04bb2024ce5d72e45d4a4f8589ae657ef9745855006996115a23a1af88d536cf02c0524a585fce7bfa79d6a9669af735eda6205d6c7e5b3cdc2b8ff7b2fa1f0b56"
:installation-id "38ff0c3e-58cf-41b3-bc01-48869af9776f"}
+125
View File
@@ -0,0 +1,125 @@
{:Best
[
{:From {:isTest true
:tokenOverrides nil
:rpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:chainColor "#939BA1"
:chainName "Ethereum Mainnet"
:nativeCurrencyName "Ether"
:nativeCurrencyDecimals 18
:blockExplorerUrl "https://goerli.etherscan.io/"
:relatedChainId 1
:shortName "goEth"
:layer 1
:chainId 5
:originalFallbackURL "https://goerli.infura.io/v3/"
:iconUrl "network/Network=Testnet"
:originalRpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:enabled true
:nativeCurrencySymbol "ETH"
:fallbackURL "https://goerli.infura.io/v3/"}
:AmountInLocked false
:AmountIn "0x16345785d8a0000"
:MaxAmountIn "0x362680d85001805"
:GasFees {:gasPrice "1.3e-08"
:baseFee "1"
:maxPriorityFeePerGas "2e-09"
:maxFeePerGasLow "9e-09"
:maxFeePerGasMedium "1"
:maxFeePerGasHigh "2"
:eip1559Enabled true}
:BridgeName "Transfer"
:AmountOut "0x16345785d8a0000" ;no bridge amount in === amount out, otherwise different
:ApprovalContractAddress nil
:ApprovalRequired false ; always fast - when using bridge
:EstimatedTime 3 ; an enum - see status go
:ApprovalGasFees "0"
:To {:isTest true
:tokenOverrides nil
:rpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:chainColor "#939BA1"
:chainName "Ethereum Mainnet"
:nativeCurrencyName "Ether"
:nativeCurrencyDecimals 18
:blockExplorerUrl "https://goerli.etherscan.io/"
:relatedChainId 1
:shortName "goEth"
:layer 1
:chainId 5
:originalFallbackURL "https://goerli.infura.io/v3/"
:iconUrl "network/Network=Testnet"
:originalRpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:enabled true
:nativeCurrencySymbol "ETH"
:fallbackURL "https://goerli.infura.io/v3/"}
:BonderFees "0x0"
:ApprovalAmountRequired nil
:Cost "4.3119595404000002308e-10"
:TokenFees "0"
:GasAmount 23487}]
:Candidates
[{:From {:isTest true
:tokenOverrides nil
:rpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:chainColor "#939BA1"
:chainName "Ethereum Mainnet"
:nativeCurrencyName "Ether"
:nativeCurrencyDecimals 18
:blockExplorerUrl "https://goerli.etherscan.io/"
:relatedChainId 1
:shortName "goEth"
:layer 1
:chainId 5
:originalFallbackURL "https://goerli.infura.io/v3/"
:iconUrl "network/Network=Testnet"
:originalRpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:enabled true
:nativeCurrencySymbol "ETH"
:fallbackURL "https://goerli.infura.io/v3/"}
:AmountInLocked false
:AmountIn "0x16345785d8a0000"
:MaxAmountIn "0x362680d85001805"
:GasFees {:gasPrice "1.3e-08"
:baseFee "1"
:maxPriorityFeePerGas "2e-09"
:maxFeePerGasLow "9e-09"
:maxFeePerGasMedium "1"
:maxFeePerGasHigh "2"
:eip1559Enabled true}
:BridgeName "Transfer"
:AmountOut "0x16345785d8a0000"
:ApprovalContractAddress nil
:ApprovalRequired false
:EstimatedTime 3
:ApprovalGasFees "0"
:To {:isTest true
:tokenOverrides nil
:rpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:chainColor "#939BA1"
:chainName "Ethereum Mainnet"
:nativeCurrencyName "Ether"
:nativeCurrencyDecimals 18
:blockExplorerUrl "https://goerli.etherscan.io/"
:relatedChainId 1
:shortName "goEth"
:layer 1
:chainId 5
:originalFallbackURL "https://goerli.infura.io/v3/"
:iconUrl "network/Network=Testnet"
:originalRpcUrl "https://goerli-archival.gateway.pokt.network/v1/lb/3ef2018191814b7e1009b8d9"
:enabled true
:nativeCurrencySymbol "ETH"
:fallbackURL "https://goerli.infura.io/v3/"}
:BonderFees "0x0"
:ApprovalAmountRequired nil
:Cost "4.3119595404000002308e-10" ; "USD" or local currency
:TokenFees "0"
:GasAmount 23487}]
:TokenPrice 2039.88
:NativeChainTokenPrice 2039.88}
+3 -1
View File
@@ -4,4 +4,6 @@
[status-im2.contexts.chat.messages.content.audio.component-spec]
[status-im2.contexts.communities.actions.community-options.component-spec]
[status-im2.contexts.wallet.add-address-to-watch.component-spec]
[status-im2.contexts.wallet.create-account.edit-derivation-path.component-spec]))
[status-im2.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
[status-im2.contexts.wallet.create-account.edit-derivation-path.component-spec]
[status-im2.contexts.wallet.send.input-amount.component-spec]))
+22 -2
View File
@@ -9,6 +9,7 @@
[status-im2.contexts.chat.messages.view :as chat]
[status-im2.contexts.chat.new-chat.view :as new-chat]
[status-im2.contexts.chat.photo-selector.view :as photo-selector]
[status-im2.contexts.communities.actions.accounts-selection.view :as communities.accounts-selection]
[status-im2.contexts.communities.actions.request-to-join.view :as join-menu]
[status-im2.contexts.communities.discover.view :as communities.discover]
[status-im2.contexts.communities.overview.view :as communities.overview]
@@ -27,6 +28,7 @@
[status-im2.contexts.onboarding.syncing.results.view :as syncing-results]
[status-im2.contexts.onboarding.welcome.view :as welcome]
[status-im2.contexts.profile.profiles.view :as profiles]
[status-im2.contexts.profile.settings.view :as settings]
[status-im2.contexts.quo-preview.component-preview.view :as component-preview]
[status-im2.contexts.quo-preview.main :as quo.preview]
[status-im2.contexts.shell.activity-center.view :as activity-center]
@@ -49,8 +51,10 @@
[status-im2.contexts.wallet.edit-account.view :as wallet-edit-account]
[status-im2.contexts.wallet.saved-addresses.view :as wallet-saved-addresses]
[status-im2.contexts.wallet.scan-account.view :as scan-address]
[status-im2.contexts.wallet.send.input-amount.view :as wallet-send-input-amount]
[status-im2.contexts.wallet.send.select-address.view :as wallet-select-address]
[status-im2.contexts.wallet.send.select-asset.view :as wallet-select-asset]
[status-im2.contexts.wallet.send.transaction-confirmation.view :as wallet-transaction-confirmation]
[status-im2.navigation.options :as options]
[status-im2.navigation.transitions :as transitions]))
@@ -87,6 +91,10 @@
:options {:sheet? true}
:component join-menu/request-to-join}
{:name :community-account-selection
:options {:sheet? true}
:component communities.accounts-selection/view}
{:name :lightbox
:options options/lightbox
:component lightbox/lightbox}
@@ -117,6 +125,10 @@
{:name :community-overview
:component communities.overview/overview}
{:name :settings
:options options/transparent-screen-options
:component settings/view}
{:name :settings-syncing
:options (merge options/dark-screen {:insets {:top? true}})
:component settings-syncing/view}
@@ -259,8 +271,7 @@
:component wallet-edit-account/view}
{:name :add-address-to-watch
:options {:insets {:top? true
:bottom? true}}
:options {:insets {:top? true}}
:component add-address-to-watch/view}
{:name :confirm-address-to-watch
@@ -288,6 +299,11 @@
{:name :wallet-saved-addresses
:component wallet-saved-addresses/view}
{:name :wallet-send-input-amount
:options {:modalPresentationStyle :overCurrentContext
:insets {:top? true}}
:component wallet-send-input-amount/view}
{:name :wallet-select-address
:options {:modalPresentationStyle :overCurrentContext}
:component wallet-select-address/view}
@@ -296,6 +312,10 @@
:options {:insets {:top? true}}
:component wallet-select-asset/view}
{:name :wallet-transaction-confirmation
:options {:insets {:bottom? true}}
:component wallet-transaction-confirmation/view}
{:name :scan-address
:options (merge
options/dark-screen
-1
View File
@@ -156,7 +156,6 @@
(reg-root-key-sub :wallet/scanned-address :wallet/scanned-address)
(reg-root-key-sub :wallet/local-suggestions :wallet/local-suggestions)
(reg-root-key-sub :wallet/valid-ens-or-address? :wallet/valid-ens-or-address?)
(reg-root-key-sub :wallet/send-address :wallet/send-address)
;;debug
(when js/goog.DEBUG
+7 -3
View File
@@ -14,6 +14,11 @@
:<- [:wallet/ui]
:-> :tokens-loading?)
(rf/reg-sub
:wallet/watch-address-activity-state
:<- [:wallet/ui]
:-> :watch-address-activity-state)
(rf/reg-sub
:wallet/accounts
:<- [:wallet]
@@ -46,7 +51,7 @@
(mapv (fn [{:keys [color address] :as account}]
(assoc account
:customization-color color
:type :empty
:type (if (= type :watch) :watch-only :empty)
:on-press #(rf/dispatch [:wallet/navigate-to-account address])
:loading? tokens-loading?
:balance (utils/prettify-balance (get balances address))))
@@ -70,7 +75,7 @@
(assoc token
:networks (utils/network-list token networks)
:total-balance (utils/total-token-value-in-all-chains token)
:total-balance-fiat (utils/calculate-balance token)))
:total-balance-fiat (utils/calculate-balance-for-token token)))
(:tokens account))
sorted-tokens
@@ -81,5 +86,4 @@
(string/starts-with? (string/lower-case (:symbol %))
(string/lower-case query)))
sorted-tokens)]
(println filtered-tokens "3421342342432")
filtered-tokens)))
@@ -181,3 +181,15 @@
(= (set ["0x1" "0x2"])
(rf/sub [sub-name])))))
(h/deftest-sub :wallet/watch-address-activity-state
[sub-name]
(testing "watch address activity state with nil value"
(is (= nil (rf/sub [sub-name]))))
(testing "watch address activity state with no-activity value"
(swap! rf-db/app-db #(assoc-in % [:wallet :ui :watch-address-activity-state] :no-activity))
(is (= :no-activity (rf/sub [sub-name]))))
(testing "watch address activity state with has-activity value"
(swap! rf-db/app-db #(assoc-in % [:wallet :ui :watch-address-activity-state] :has-activity))
(is (= :has-activity (rf/sub [sub-name])))))
+6 -2
View File
@@ -152,7 +152,7 @@
;; E.g. for Ether, it's smallest part is wei or 10^(-18) of 1 ether
;; for arbitrary ERC20 token the smallest part is 10^(-decimals) of 1 token
;;
;; Different tokens can have different number of allowed decimals, so it's neccessary to include the
;; Different tokens can have different number of allowed decimals, so it's necessary to include the
;; decimals parameter
;; to get the amount scale right.
@@ -205,10 +205,14 @@
(with-precision 2)
str))
(defn add
(defn- add*
[bn1 n2]
(.add ^js bn1 n2))
(def add
"Add with defaults, this version is able to receive `nil` and takes them as 0."
(fnil add* (bignumber 0) (bignumber 0)))
(defn mul
[bn1 bn2]
(.mul ^js bn1 bn2))
+22
View File
@@ -1,5 +1,7 @@
(ns utils.security.core
(:require
[native-module.core :as native-module]
[schema.core :as schema]
[utils.security.security-html :as h]))
(defprotocol Unmaskable
@@ -58,3 +60,23 @@
and does not contain an rtlo character, which might mean that the url is spoofed"
[text]
(not (re-matches rtlo-link-regex text)))
(defn hash-masked-password
[masked-password]
(-> masked-password
safe-unmask-data
native-module/sha3
mask-data))
(defn masked-data-instance?
[value]
(instance? MaskedData value))
(def ?masked-password
[:fn {:error/message "should be an instance of utils.security.core/MaskedData"}
masked-data-instance?])
(schema/=> hash-masked-password
[:=>
[:cat ?masked-password]
?masked-password])
+24
View File
@@ -1,6 +1,7 @@
(ns utils.security.security-test
(:require
[cljs.test :refer-macros [deftest is testing]]
[native-module.core :as native-module]
[utils.security.core :as security]))
(def rtlo-link "http://google.com")
@@ -29,3 +30,26 @@
(deftest safe-link-text-test-exceptions
(testing "rtlo links"
(is (not (security/safe-link-text? rtlo-link-text)))))
(deftest mask-data-test
(testing "returns an instance of MaskedData"
(is (instance? security/MaskedData (security/mask-data "test"))))
(testing "hides the original value"
(is (= "******" (str (security/mask-data "test")))))
(testing "succeeds the equality check between same MaskedData instances"
(is (= (security/mask-data "value") (security/mask-data "value"))))
(testing "fails the equality check between different MaskedData instances"
(is (not (= (security/mask-data "value-A") (security/mask-data "value-B")))))
(testing "fails the equality check with non-MaskedData instances"
(is (not (= (security/mask-data "value") "value"))))
(testing "counts the masked data correctly"
(is (= (count "test") (count (security/mask-data "test")))))
(testing "unmasks the data correctly"
(is (= "test" (-> "test" security/mask-data security/safe-unmask-data)))))
(deftest hash-masked-password-test
(testing "returns an instance of MaskedData with the hashed content"
(is (= (-> "test" native-module/sha3 security/mask-data)
(-> "test" security/mask-data security/hash-masked-password))))
(testing "throws a schema exception if the argument is not an instance of MaskedData"
(is (thrown? js/Error (security/hash-masked-password "test")))))
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "fix/community-offline-updates",
"commit-sha1": "49d0e9465b423e15e1139898de19df626a5cf62d",
"src-sha256": "1dgg0jjv54i2270j790m2n89hxdrrzpcnxmf8frif6jr4jk6bf0n"
"version": "v0.171.25",
"commit-sha1": "8206ffc736cdf054f0699b98c3cb52ce6bd6ed96",
"src-sha256": "12lfn5xlg9n1fgk9zbi92kmzsqz7wv9zm84xsiisl495w4y8dvsl"
}
+1 -1
View File
@@ -1,5 +1,5 @@
Appium-Python-Client==2.11.1
Pillow==8.1.0
Pillow==10.0.0
PyGithub==1.55
PyYAML==5.3.1
Cython<3.0
+1 -1
View File
@@ -123,7 +123,7 @@ class TestrailReport(BaseTestReport):
test_cases['pr']['critical'] = 50955
test_cases['pr']['one_to_one_chat'] = 50956
# test_cases['pr']['deep_links'] = 50967
test_cases['pr']['deep_links'] = 51535
test_cases['pr']['group_chat'] = 50964
test_cases['pr']['community_single'] = 50983
test_cases['pr']['community_multiple'] = 50982
@@ -0,0 +1,124 @@
import pytest
from tests import marks
from tests.base_test_case import create_shared_drivers, MultipleSharedDeviceTestCase
from views.sign_in_view import SignInView
@pytest.mark.xdist_group(name="new_one_1")
@marks.new_ui_critical
class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.drivers, self.loop = create_shared_drivers(1)
self.driver = self.drivers[0]
self.sign_in = SignInView(self.driver)
self.username = 'test user'
self.home = self.sign_in.create_user(username=self.username)
self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button)
self.community_name = "open community"
self.channel_name = "general"
self.community = self.home.create_community(community_type="open")
self.profile_view = self.home.get_profile_view()
self.browser_view = self.home.get_dapp_view()
self.home.get_chat(self.community_name, community=True).click()
self.community_view = self.home.get_community_view()
self.channel = self.community_view.get_channel(self.channel_name).click()
@marks.testrail_id(704613)
def test_links_open_universal_links_from_chat(self):
profile_urls = [
"https://status.app/u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj",
"https://status.app/u#zQ3shVVxZMwLVEQvuu1KF6h4D2mzVyCC4F4mHLZm5dz5XU1aa"]
for url in profile_urls:
self.channel.send_message(url)
self.channel.chat_element_by_text(url).click_on_link_inside_message_body()
if not self.channel.profile_add_to_contacts_button.is_element_displayed(
10) or not self.profile_view.default_username_text.text.endswith(url[-6:]):
self.errors.append("Profile was not opened by the profile url %s" % url)
self.home.navigate_back_to_chat_view()
closed_community_urls = [
"https://status.app/c/G8EAAGTiXKuwNbVVAu0GNLD-XzX4oz_E8oC1-7qSLikaTnCuG9Ag13ZgQKrMd8En9Qcpuaj3Qx3mfZ1atZzH8Zw-x_sFJ_MDv0P_7YfqoV-pNr3V4dsza-jVk41GaCGWasJb92Oer8qggaoNWf0tYCgSH19VonXciKPUz3ITdgke#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK",
"https://status.app/c/Ow==#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK"
"https://status.app/c#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK",
]
for url in closed_community_urls:
self.channel.send_message(url)
self.channel.chat_element_by_text(url).click_on_link_inside_message_body()
if not self.channel.element_by_translation_id(
"community-admins-will-review-your-request").is_element_displayed(10):
self.errors.append("Closed community was not requested to join by the url %s" % url)
self.home.jump_to_card_by_text(self.community_name)
self.errors.verify_no_errors()
@marks.testrail_id(702775)
def test_links_deep_links(self):
self.home.navigate_back_to_home_view()
self.home.browser_tab.click()
profile_links = {
"status-app://u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj": None,
"status-app://u#zQ3shVVxZMwLVEQvuu1KF6h4D2mzVyCC4F4mHLZm5dz5XU1aa": None,
"status-app://u/Ow==#zQ3shsKnV5HJMWJR61c6dssWzHszdbLfBoMF1gcLtSQAYdw2d": "Restored desktop"
}
for link, text in profile_links.items():
self.browser_view.open_url(link)
if text:
name_is_shown = self.profile_view.default_username_text.text == text \
or self.profile_view.default_username_text.text.endswith(link[-6:])
else:
name_is_shown = self.profile_view.default_username_text.text.endswith(link[-6:])
if not self.channel.profile_add_to_contacts_button.is_element_displayed(10) or not name_is_shown:
self.errors.append("Profile was not opened by the profile deep link %s" % link)
self.browser_view.click_system_back_button()
community_links = [
"status-app://c/G8EAAGTiXKuwNbVVAu0GNLD-XzX4oz_E8oC1-7qSLikaTnCuG9Ag13ZgQKrMd8En9Qcpuaj3Qx3mfZ1atZzH8Zw-x_sFJ_MDv0P_7YfqoV-pNr3V4dsza-jVk41GaCGWasJb92Oer8qggaoNWf0tYCgSH19VonXciKPUz3ITdgke#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK",
"status-app://c/Ow==#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK",
"status-app://c#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK"
]
for link in community_links:
self.browser_view.open_url(link)
if not self.channel.element_by_translation_id(
"community-admins-will-review-your-request").is_element_displayed(10):
self.errors.append("Closed community was not requested to join by the deep link %s" % link)
self.home.navigate_back_to_home_view()
self.home.browser_tab.click()
self.errors.verify_no_errors()
@marks.testrail_id(704614)
def test_links_open_universal_links_from_other_apps(self):
app_package = self.driver.current_package
self.home.just_fyi("Opening a profile URL from google search bar when user is still logged in")
profile_url = "https://status.app/u#zQ3shVVxZMwLVEQvuu1KF6h4D2mzVyCC4F4mHLZm5dz5XU1aa"
self.home.click_system_home_button()
self.home.open_link_from_google_search_app(profile_url, app_package)
if not self.channel.profile_add_to_contacts_button.is_element_displayed(
10) or not self.profile_view.default_username_text.text.endswith(profile_url[-6:]):
self.errors.append("Profile was not opened by the url %s when user is logged in" % profile_url)
self.home.just_fyi("Opening a community URL from google search bar when user is logged out")
self.driver.terminate_app(app_package)
community_url = "https://status.app/c/Ow==#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK"
self.home.open_link_from_google_search_app(community_url, app_package)
self.sign_in.sign_in()
if not self.home.element_by_translation_id(
"community-admins-will-review-your-request").is_element_displayed(10):
self.errors.append("Closed community was not requested to join by the url %s" % community_url)
self.home.just_fyi("Opening a community channel URL from google search bar with no account created")
self.driver.reset()
self.home.click_system_home_button()
channel_url = "https://status.app/cc/Ow==#zQ3shbmfT3hvh4mKa1v6uAjjyztQEroh8Mfn6Ckegjd7LT3XK"
self.home.open_link_from_google_search_app(channel_url, app_package)
self.sign_in.create_user()
if not self.home.element_by_translation_id(
"community-admins-will-review-your-request").is_element_displayed(10):
self.errors.append("Created user was not redirected to a community channel by the url %s" % channel_url)
self.errors.verify_no_errors()
+13
View File
@@ -98,6 +98,10 @@ class BrowserTab(TabButton):
def __init__(self, driver):
super().__init__(driver, accessibility_id="browser-stack-tab")
def navigate(self):
from views.dapps_view import DappsView
return DappsView(self.driver)
class DappTabButton(TabButton):
def __init__(self, driver):
@@ -837,3 +841,12 @@ class BaseView(object):
except TimeoutException:
raise TimeoutException(
"Device %s: expected element is not stale after %s seconds" % (self.driver.number, seconds)) from None
def open_link_from_google_search_app(self, link_text: str, app_package: str):
Button(self.driver, xpath="//*[contains(@resource-id,'search_container_all_apps')]").click()
EditBox(self.driver, xpath="//android.widget.EditText").send_keys(link_text)
self.driver.press_keycode(66)
text_to_click = "Status PR" if app_package.endswith(".pr") else "Status"
Button(self.driver,
xpath="//*[@resource-id='android:id/resolver_list']//*[@text='%s']" % text_to_click).click_if_shown()
Button(self.driver, xpath="//*[@resource-id='android:id/button_once']").click()
+3 -3
View File
@@ -3,12 +3,12 @@ display_name = 'End to end'
communities = {
'admin_open': 'Open-comm-adm',
'member_open': 'e2e_open',
'admin_closed': 'admin-closed-comm',
'admin_closed': 'closed community',
'member_closed': 'test_comm_enc',
'member_pending': 'RC1 testing community'
}
#contacts = ['Test_contact', 'MyCustomNickname'] # enable back when https://github.com/status-im/status-mobile/issues/15500 is fixed
# contacts = ['Test_contact', 'MyCustomNickname'] # enable back when https://github.com/status-im/status-mobile/issues/15500 is fixed
contacts = ['zQ3...LSpUs', 'zQ3...xAZUU']
contacts = ['zQ3...pJN5P', 'zQ3...UxXqE']
blocked_user = 'Clear Flat Milkweedbug'
+13 -3
View File
@@ -480,7 +480,7 @@
"done": "Done",
"edit": "Edit",
"edit-group": "Edit group",
"edit-profile": "Edit profile",
"edit-profile": "Edit Profile",
"emojihash": "Emojihash",
"emojihash-description": "A visual representation of your chat key. It will help other users recognize your profile.",
"emojis": "Emojis",
@@ -1005,6 +1005,8 @@
"buy-crypto": "Buy crypto",
"buy-crypto-choose-a-service": "Choose a service you'd like to use to buy crypto",
"buy-crypto-leaving": "You are leaving Status and entering a third party website to complete your purchase",
"data-usage": "Data usage",
"language-and-currency": "Language and currency",
"opening-buy-crypto": "Opening {{site}}...",
"network": "Network",
"network-chain": "Network chain",
@@ -1313,6 +1315,7 @@
"status-pending": "Pending",
"status-tx-not-found": "TX not found",
"status-sent": "Sent",
"status-help": "Status Help",
"status-not-sent-tap": "Not confirmed. Tap for options",
"status-not-sent-click": "Not confirmed. Click for options",
"step-i-of-n": "Step {{step}} of {{number}}",
@@ -2391,6 +2394,13 @@
"address-already-in-use": "Address already being used",
"address-copied": "Address copied",
"no-dapps-description": "We want dApps!",
"select-asset": "Select asset"
"select-asset": "Select asset",
"send-limit": "Max: {{limit}}",
"searching-for-activity": "Searching for activity...",
"this-address-has-no-activity": "This address has no activity",
"this-address-has-activity": "This address has activity",
"details": "Details",
"est-time": "Est. time",
"user-gets": "{{name}} gets",
"slide-to-send": "Slide to send"
}