Compare commits

...
Author SHA1 Message Date
Andrea Maria Piana 009ba8f511 wip 2024-02-13 08:43:20 +00:00
Andrea Maria Piana 4fad90dfbc Fix permission screen & trailing zeros
https://github.com/status-im/status-go/compare/c15f9e73...b7b7660a

There were a few issues with the permission screen:
1) Wrong permission was displayed when able to join
2) If not able to join, we were showing both admin/member permissions
3) Trailing zeros in token amount
2024-02-13 08:18:26 +00:00
Mohamed Javid d1f22cd2b8 Wallet - Clear input on long press delete (#18732)
This commit allows clearing the amount input (in the send flow) by long-pressing the delete key.

---------

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-10 00:09:15 +05:30
Parvesh Monu 9dacc9e8af error and app freezing when deselecting account (#18759) 2024-02-09 23:32:58 +05:30
Jamie Caprani 9acf67dd5b feat: add basic infrastructure for contract tests (#18665) 2024-02-09 09:10:09 -08:00
Brian Sztamfater ab73c7e56a feat: show estimated fees when calculating route (#18309)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-02-09 13:43:14 -03:00
Yevheniia Berdnyk f1db73d149 e2e: updated deep/universal links tests 2024-02-09 15:51:14 +02:00
Ajay Sivan d78d7719cd Account permissions component malli schema fix (#18770) 2024-02-09 05:44:11 -08:00
Alexander e381998b19 Toast notification design fixes (#18468)
* Toast notification design fixes

* Code style fix

* Smaller fix

* Style fix

* Fixes

* Style fixes

* Fixes

* Fixes
2024-02-09 09:54:14 +01:00
Brian Sztamfater 025c38ae88 fix: address is lost on re-render (#18593)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-02-08 16:29:05 -03:00
mmilad75 523a5809d0 Wallet - Add malli spec to Quo Wallet components (batch 1) #18354 (#18707)
* draft

* add account_card

* add account-origin

* add account_overview

* add account_permission

* add address_text

* add confirmation_progress

* wrapping up

* resolve comments and fix ci issues

* fix ci issues

* fix transaction_progress tests

* fix all tests

* fix lint issues
2024-02-08 20:31:57 +03:30
Icaro Motta 505f2fe624 Communities: Show relevant tokens (#18636)
Show "relevant tokens" in account selection step. Related status-go PR
https://github.com/status-im/status-go/pull/4631.

- Balances are fetched on the account selection screen. Assuming most users
  won't care about selecting addresses, this eliminates unnecessary calls to get
  balances, at the expense of refetching them every time the component is
  mounted. I think for now this is acceptable.
- Relevant tokens is a terminology used in Figma, but more specifically, it
  means "balances for all assets that have at least one Token Criteria
  associated, and for a given address". Or, as I tried to give it a more
  distinct name, "permissioned balances".

Areas that may be impacted: None because all affected code is behind a disabled
feature flag.

Fixes https://github.com/status-im/status-mobile/issues/18126
2024-02-08 11:17:15 -03:00
Ajay Sivan ac32de89f9 Update drawer/bottom-actions component with top & top error variants (#18513) 2024-02-08 02:31:50 -08:00
Mohamed Javid 52a82fffab Bump status-go version to 0.174.2 (#18745)
This commit updates the status-go version to use v0.174.2

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-08 15:19:27 +05:30
Mohamed Javid d1c8f6411d [Feature] Wallet - Display token price (#18438)
This commit adds a feature to display token prices for each token in the Wallet home and Account screens.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-08 13:31:42 +05:30
Mohsen 7b639f2ffe [#18453] feat: implement change accent colour screen (#18517) 2024-02-07 19:49:21 +03:00
Brian Sztamfater ae7ee33f00 fix: ENS regex not being used to detect an ENS on address input (#18626)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-02-07 12:39:49 -03:00
Mohamed Javid c70646bd5c [Fix] Scanned address not populated in send flow (#18727)
This commit fixes the scanned address not pasted/populated in the "Send to" input field.

---

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-07 19:56:42 +05:30
mmilad75andLungu Cristian 484ab919b1 Update Wallet: Share QR Code #18454 (#18479)
* add icon

* fix lint issues

* fix preview props and header icon

* fix watched-address icon condition

* fix header tabs

* draft

* fix network name in multichain mode

* fix lint issues

* update tests

* update comments

* add background color

* update preview file

* update usages

* feat: add schema to quo/share-qr-code component

* test: fixed & re-factored tests after adding schema

* bring back gradient structure

* fix scrolling issue

* fix lint issues

* fix usage

---------

Co-authored-by: Lungu Cristian <lungucristian95@gmail.com>
2024-02-07 17:08:41 +03:30
Omar Basem 052d88b531 Wallet: bridging - assets list (#18713)
Wallet: bridging - assets list
2024-02-07 16:55:14 +04:00
Omar Basem 0aba789b5c feat: new keypair - check your backup (#18667)
* feat: new keypair - check your backup
2024-02-07 15:53:29 +04:00
Omar Basem 73450d736a Wallet: fix nested scrollviews warning (#18714)
Wallet: fix nested scrollviews warning (#18714)
2024-02-07 09:32:53 +04:00
Siddarth Kumar cc63c8fff5 init: maestro dev automation (#18712)
The purpose of this commit is to allow running `maestro` automations to enhance productivity by **automating** mundane tasks.

Other flows like
- send contact request
- sync devices
- join a community
etc will be a part of follow ups.

In this commit we provide a `make` command to run a very common task like creating account & login :
`make auto-login`

We also allow another `make` command to run any other custom flow :
`make auto-custom FLOW="maestro/create-account-or-login.yaml"`

A `maestro` folder has been added with 3 `yaml` files which are very easy to understand
ref : https://maestro.mobile.dev/api-reference/commands
2024-02-06 23:29:35 +05:30
Jamie Caprani ef6027575f Add a UI for toggling developer feature flags (#18602) 2024-02-06 08:24:45 -08:00
138 changed files with 2799 additions and 1343 deletions
+13 -12
View File
@@ -16,18 +16,19 @@
:clj-kondo-config {:level :error}
:cond-else {:level :error}
:consistent-alias {:level :error
:aliases {clojure.set set
clojure.string string
clojure.walk walk
malli.core malli
malli.dev.pretty malli.pretty
malli.dev.virhe malli.virhe
malli.error malli.error
malli.generator malli.generator
malli.transform malli.transform
malli.util malli.util
schema.core schema
taoensso.timbre log}}
:aliases {clojure.set set
clojure.string string
clojure.walk walk
malli.core malli
malli.dev.pretty malli.pretty
malli.dev.virhe malli.virhe
malli.error malli.error
malli.generator malli.generator
malli.transform malli.transform
malli.util malli.util
schema.core schema
status-im.feature-flags ff
taoensso.timbre log}}
:deprecated-namespace {:level :warning}
:docstring-blank {:level :error}
:equals-true {:level :error}
+22 -2
View File
@@ -373,15 +373,20 @@ test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connection
"until [ -f $$SHADOW_OUTPUT_TO ] ; do sleep 1 ; done ; node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO --repl"
test-unit: export SHADOW_OUTPUT_TO := target/unit_test/test.js
test-unit: export SHADOW_NS_REGEXP := ^(?!status-im\.integration-test).*-test$$
test-unit: export SHADOW_NS_REGEXP := ^(?!tests\.integration-test)(?!tests-im\.contract-test).*-test$$
test-unit: ##@test Run unit tests
test-unit: _test-clojure
test-integration: export SHADOW_OUTPUT_TO := target/integration_test/test.js
test-integration: export SHADOW_NS_REGEXP := ^status-im\.integration-test.*$$
test-integration: export SHADOW_NS_REGEXP := ^tests\.integration-test.*$$
test-integration: ##@test Run integration tests
test-integration: _test-clojure
test-contract: export SHADOW_OUTPUT_TO := target/contract_test/test.js
test-contract: export SHADOW_NS_REGEXP := ^tests\.contract-test.*$$
test-contract: ##@test Run contract tests
test-contract: _test-clojure
android-test: jsbundle
android-test: export TARGET := android
android-test:
@@ -457,3 +462,18 @@ repl-clojure: ##@repl Start Clojure repl for mobile App
yarn shadow-cljs cljs-repl mobile
repl-nix: nix-repl ##@repl Start an interactive Nix REPL
#--------------
# Dev Automation Flows
#--------------
auto-login: export TARGET := default
auto-login: ##@auto runs flow for login or onboarding app on simulator/emulator
maestro test "maestro/create-account-or-login.yaml"
auto-custom: export TARGET := default
auto-custom: ##@auto runs any custom maestro automation flow on simulator/emulator
ifndef FLOW
$(error Usage: make automate FLOW=your-maestro-flow-file.yaml)
endif
maestro test "$(FLOW)"
+11
View File
@@ -0,0 +1,11 @@
appId: im.status.ethereum.debug
---
- runFlow: # to run onboarding flow only when device is in onboarding state
when:
visible: "Im new to Status"
file: onboarding.yaml
- runFlow: # to run login flow only when device is on login screen
when:
visible: "Profile password"
file: login.yaml
+5
View File
@@ -0,0 +1,5 @@
appId: im.status.ethereum.debug
---
- tapOn: "password-input"
- inputText: "password123" # update your account password here
- tapOn: login-button
+15
View File
@@ -0,0 +1,15 @@
appId: im.status.ethereum.debug
---
- tapOn: "Im new to Status"
- tapOn: "small-option-card"
- tapOn: "Your name"
- inputText: "DeviceA"
- tapOn: "submit-create-profile-button"
- inputText: "password123"
- tapOn: "Repeat password"
- inputText: "password123"
- tapOn: "disclaimer-touchable-opacity"
- tapOn: "Confirm password"
- tapOn: "skip-identifiers"
- tapOn: "enable-notifications-later-button"
- tapOn: "welcome-button"
+1
View File
@@ -97,4 +97,5 @@ in {
patchMavenSources = callPackage ./pkgs/patch-maven-srcs { };
goMavenResolver = callPackage ./pkgs/go-maven-resolver { };
xcbeautify = callPackage ./pkgs/xcbeautify { };
idb-companion = callPackage ./pkgs/idb-companion { };
}
+29
View File
@@ -0,0 +1,29 @@
{ stdenv, fetchurl, lib }:
stdenv.mkDerivation rec {
pname = "idb-companion";
version = "1.1.8";
src = fetchurl {
url = "https://github.com/facebook/idb/releases/download/v${version}/idb-companion.universal.tar.gz";
sha256 = "sha256-O3LMappbGiKhiCBahAkNOilDR6hGGA79dVzxo8hI4+c=";
};
buildInputs = [ ];
unpackPhase = ''
tar -xzf $src
'';
installPhase = ''
mkdir -p $out/bin
cp -r ./* $out/bin/
'';
meta = with lib; {
description = "A powerful command line tool for automating iOS simulators and devices";
homepage = "https://github.com/facebook/idb";
license = licenses.mit;
platforms = platforms.darwin;
};
}
+2 -2
View File
@@ -23,9 +23,9 @@ in mkShell {
# build specific utilities
clojure maven watchman
# other nice to have stuff
yarn nodejs python310
yarn nodejs python310 maestro
] # and some special cases
++ lib.optionals stdenv.isDarwin ([ cocoapods clang tcl ] ++ appleSDKFrameworks)
++ lib.optionals stdenv.isDarwin ([ cocoapods clang tcl idb-companion ] ++ appleSDKFrameworks)
++ lib.optionals (!stdenv.isDarwin) [ gcc8 ]
);
Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

-1
View File
@@ -176,7 +176,6 @@
;; option setupFilesAfterEnv.
test-helpers.component-tests-preload
status-im.setup.schema-preload
quo.core-spec
status-im.core-spec]
:ns-regexp "component-spec$"
@@ -4,14 +4,15 @@
(def backgrounds #{:photo :blur})
(defn custom-color-type
[customization-color icon-only?]
{:icon-color colors/white-opa-70
:label-color colors/white
:background-color (colors/custom-color customization-color 50)
:border-radius (when icon-only? 24)})
(defn- custom-color-type
[customization-color icon-only? theme]
{:icon-color colors/white-opa-70
:label-color colors/white
:background-color (colors/resolve-color customization-color theme)
:border-radius (when icon-only? 24)
:overlay-customization-color customization-color})
(defn grey-photo
(defn- grey-photo
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-70 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -24,7 +25,7 @@
theme))
:blur-type (if (= theme :light) :light :dark)})
(defn grey-blur
(defn- grey-blur
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-70 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -32,7 +33,7 @@
(colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme)
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 theme))})
(defn outline-blur
(defn- outline-blur
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -40,7 +41,7 @@
(colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 theme)
(colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme))})
(defn grey
(defn- grey
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -50,7 +51,7 @@
colors/neutral-90
theme))})
(defn dark-grey
(defn- dark-grey
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -58,7 +59,7 @@
(colors/theme-colors colors/neutral-30 colors/neutral-60 theme)
(colors/theme-colors colors/neutral-20 colors/neutral-90 theme))})
(defn outline
(defn- outline
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -66,29 +67,33 @@
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
(colors/theme-colors colors/neutral-30 colors/neutral-70 theme))})
(defn ghost
(defn- ghost
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
:background-color (when pressed?
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))})
(defn black
(defn- black
[pressed?]
{:label-color colors/white
:background-color (if pressed? colors/neutral-80 colors/neutral-95)})
(defn get-values
[{:keys [customization-color theme type background pressed? icon-only?]}]
(cond
(= type :primary) (custom-color-type customization-color icon-only?)
(= type :positive) (custom-color-type customization-color icon-only?)
(= type :danger) (custom-color-type customization-color icon-only?)
(and (= :photo background) (= type :grey)) (grey-photo theme pressed?)
(and (= :blur background) (= type :grey)) (grey-blur theme pressed?)
(and (= :blur background) (= type :outline)) (outline-blur theme pressed?)
(= type :grey) (grey theme pressed?)
(= type :dark-grey) (dark-grey theme pressed?)
(= type :outline) (outline theme pressed?)
(= type :ghost) (ghost theme pressed?)
(= type :black) (black pressed?)))
(let [customization-color (get {:primary customization-color
:positive :success
:danger :danger}
type)]
(cond
(contains? #{:primary :positive :danger} type) (custom-color-type customization-color
icon-only?
theme)
(and (= :photo background) (= type :grey)) (grey-photo theme pressed?)
(and (= :blur background) (= type :grey)) (grey-blur theme pressed?)
(and (= :blur background) (= type :outline)) (outline-blur theme pressed?)
(= type :grey) (grey theme pressed?)
(= type :dark-grey) (dark-grey theme pressed?)
(= type :outline) (outline theme pressed?)
(= type :ghost) (ghost theme pressed?)
(= type :black) (black pressed?))))
+4 -7
View File
@@ -37,13 +37,10 @@
pressed? on-press-in on-press-out allow-multiple-presses?]
:or {type :primary
size 40
customization-color (cond (= type :primary) :blue
(= type :positive) :success
(= type :danger) :danger
:else nil)}}
customization-color (if (= type :primary) :blue nil)}}
children]
(let [{:keys [icon-color background-color label-color border-color blur-type
blur-overlay-color border-radius]}
blur-overlay-color border-radius overlay-customization-color]}
(button-properties/get-values {:customization-color customization-color
:background background
:type type
@@ -79,9 +76,9 @@
:icon-left icon-left
:icon-right icon-right})
inner-style)}
(when customization-color
(when overlay-customization-color
[customization-colors/overlay
{:customization-color customization-color
{:customization-color overlay-customization-color
:theme theme
:pressed? (if pressed? pressed? @pressed-state?)}])
(when (= background :photo)
@@ -35,7 +35,7 @@
(when (and @ref (>= index 0))
(some-> ^js @ref
(.scrollToIndex #js
{:animated true
{:animated false
:index index
:viewPosition 0.5})))))
50)))
@@ -3,34 +3,60 @@
[quo.components.drawers.bottom-actions.view :as bottom-actions]
[test-helpers.component :as h]))
(defn- render
[component]
(h/render-with-theme-provider component :light))
(h/describe "bottom actions tests"
(h/test "default render with no description and single action button"
(h/render [bottom-actions/view {:button-one-label "Request to join"}])
(render [bottom-actions/view {:actions :one-action :button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Request to join")))
(h/test "render with description"
(h/render [bottom-actions/view {:description "Sample description"}])
(h/is-truthy (h/get-by-text "Sample description")))
(h/test "render with description & 1 action"
(render [bottom-actions/view
{:description :bottom
:description-text "Sample description"
:actions :one-action
:button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Sample description"))
(h/is-truthy (h/get-by-text "Request to join")))
(h/test "render with 2 actions"
(let [button-one "Button One"
button-two "Button Two"]
(h/render [bottom-actions/view
{:actions :2-actions
:button-one-label button-one
:button-one-props {:icon-left :i/arrow-left}
:button-two-label button-two}])
(render [bottom-actions/view
{:actions :two-actions
:button-one-label button-one
:button-one-props {:icon-left :i/arrow-left}
:button-two-label button-two}])
(h/is-truthy (h/get-by-text button-one))
(h/is-truthy (h/get-by-label-text :icon))
(h/is-truthy (h/get-by-text button-two))))
(h/test "render disabled button"
(h/render [bottom-actions/view
{:description "Sample description"
:button-one-props {:disabled? true}
:button-one-label "button"}])
(render [bottom-actions/view
{:description :bottom
:actions :one-action
:description-text "Sample description"
:button-one-props {:disabled? true}
:button-one-label "button"}])
(h/is-disabled (h/get-by-label-text :button-one)))
(h/test "sane defaults"
(h/render [bottom-actions/view {}])
(h/is-truthy (h/get-by-label-text :button-one))))
(h/test "render with error & 1 action"
(render [bottom-actions/view
{:description :top-error
:error-message "Sample error"
:actions :one-action
:button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Sample error"))
(h/is-truthy (h/get-by-text "Request to join")))
(h/test "render with top description & 1 action"
(render [bottom-actions/view
{:description :top
:role :admin
:actions :one-action
:button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Eligible to join as"))
(h/is-truthy (h/get-by-text "Admin"))
(h/is-truthy (h/get-by-text "Request to join"))))
@@ -2,34 +2,58 @@
(:require
[quo.foundations.colors :as colors]))
(defn- text-color
[scroll? blur? theme]
(cond
scroll? (if blur?
colors/white-70-blur
(colors/theme-colors colors/neutral-80-opa-70
colors/white-opa-70
theme))
blur? colors/white-opa-40
:else (colors/theme-colors colors/neutral-50
colors/neutral-40
theme)))
(defn container
[scroll? blur? theme]
{:background-color (when-not (or scroll? blur?)
(colors/theme-colors colors/white colors/neutral-95 theme))})
(def buttons-container
{:flex-direction :row
:justify-content :space-around
:padding-vertical 12
:gap 12
:padding-horizontal 20})
(def button-container
{:flex 1})
(def button-container-2-actions
(assoc button-container :margin-right 12))
(def description-top
{:flex-direction :row
:align-items :center
:gap 5
:padding-top 12
:padding-bottom 4
:justify-content :center})
(defn description
[theme scroll?]
{:color (colors/theme-colors
(if scroll?
(colors/custom-color colors/neutral-80 70)
colors/neutral-50)
(if scroll?
colors/white-opa-70
colors/neutral-40)
theme)
(defn description-top-text
[scroll? blur? theme]
{:color (text-color scroll? blur? theme)})
(defn description-bottom
[scroll? blur? theme]
{:color (text-color scroll? blur? theme)
:text-align :center
:padding-bottom 12
:padding-horizontal 40})
(def scroll
{:margin-top 21
:margin-horizontal 120
:margin-bottom 8
:width 134
:height 5})
(def error-message
{:flex-direction :row
:gap 4
:justify-content :center
:align-items :center
:padding-top 15
:padding-horizontal 20
:padding-bottom 7})
@@ -2,55 +2,96 @@
(:require
[quo.components.buttons.button.view :as button]
[quo.components.drawers.bottom-actions.style :as style]
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.components.tags.context-tag.view :as context-tag]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
[react-native.core :as rn]
[schema.core :as schema]
[utils.i18n :as i18n]))
(def default-props
{:button-one-type :primary
:button-two-type :grey
:customization-color :blue})
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:actions [:maybe [:enum :one-action :two-actions]]]
[:description {:optional true} [:maybe [:enum :top :bottom :top-error]]]
[:description-text {:optional true} [:maybe :string]]
[:error-message {:optional true} [:maybe :string]]
[:role {:optional true} [:maybe [:enum :admin :member :token-master :owner]]]
[:button-one-label {:optional true} [:maybe :string]]
[:button-two-label {:optional true} [:maybe :string]]
[:button-one-props {:optional true} [:maybe :map]]
[:button-two-props {:optional true} [:maybe :map]]
[:theme :schema.common/theme]
[:scroll? {:optional true} [:maybe :boolean]]
[:blur? {:optional true} [:maybe :boolean]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(def ^:private role-icon
{:admin :i/gavel
:member :i/members
:token-master :i/token-master
:owner :i/crown})
(defn- view-internal
"Options:
[{:keys [actions description description-text error-message role button-one-label button-two-label
blur? button-one-props button-two-props theme scroll? container-style]}]
[rn/view
{:style (merge (style/container scroll? blur? theme) container-style)}
(when (= description :top-error)
[rn/view {:style style/error-message}
[icon/icon
:i/alert
{:color (colors/theme-colors colors/danger-50 colors/danger-60 theme)
:size 16}]
[text/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/danger-50 colors/danger-60 theme)}}
error-message]])
:actions - keyword (default nil) - :1-action/:2-actions
:description - string (default nil) - Description to display below the title
:button-one-label - string (default nil) - Label for the first button
:button-two-label - string (default nil) - Label for the second button
:button-one-props - map with props for button one
:button-two-props - map with props for button two
:theme - :light/:dark
:scroll? - bool (default false) - Whether the iOS Home Indicator should be rendered"
[props]
(let [{:keys [actions description button-one-label button-two-label
button-one-props button-two-props theme scroll? customization-color container-style]}
(merge default-props props)]
[rn/view {:style container-style}
[rn/view {:style style/buttons-container}
(when (= actions :2-actions)
[button/button
(merge
{:size 40
:background (when scroll? :blur)
:container-style style/button-container-2-actions
:theme theme
:accessibility-label :button-two}
button-two-props)
button-two-label])
(when (= description :top)
[rn/view
{:style style/description-top}
[text/text
{:size :paragraph-2
:style (style/description-top-text scroll? blur? theme)}
(i18n/label :t/eligible-to-join-as)]
[context-tag/view
{:type :icon
:size 24
:icon (role role-icon)
:blur? blur?
:context (i18n/label (keyword "t" role))}]])
[rn/view {:style style/buttons-container}
(when (= actions :two-actions)
[button/button
(merge
{:size 40
:background (when (or blur? scroll?) :blur)
:container-style style/button-container
:background (when scroll? :blur)
:theme theme
:accessibility-label :button-one
:customization-color customization-color}
button-one-props)
button-one-label]]
(when description
[text/text
{:size :paragraph-2
:style (style/description theme scroll?)} description])]))
:accessibility-label :button-two}
button-two-props)
button-two-label])
[button/button
(merge
{:size 40
:container-style style/button-container
:background (when (or blur? scroll?) :blur)
:theme theme
:accessibility-label :button-one}
button-one-props)
button-one-label]]
(when (= description :bottom)
[text/text
{:size :paragraph-2
:style (style/description-bottom scroll? blur? theme)} description-text])])
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal ?schema)))
@@ -3,97 +3,110 @@
[quo.core :as quo]
[test-helpers.component :as h]))
(def ^:private theme :light)
(h/describe "drawer top tests"
(h/test "component renders in default type"
(h/render [quo/drawer-top
{:title "Title"
:type :default}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :default}]
theme)
(h/is-truthy (h/get-by-text "Title")))
(h/test "component renders in default + description type"
(h/render [quo/drawer-top
{:title "Title"
:type :default
:description "Description"}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :default
:description "Description"}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "Description")))
(h/test "component renders in info type"
(h/render [quo/drawer-top
{:title "Title"
:type :info}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :info}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :info-icon)))
(h/test "component renders in info + description type"
(h/render [quo/drawer-top
{:title "Title"
:description "Description"
:type :info}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:description "Description"
:type :info}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "Description"))
(h/is-truthy (h/get-by-label-text :info-icon)))
(h/test "component renders in context-tag type"
(h/render [quo/drawer-top
{:title "Title"
:type :context-tag
:community-name "Coinbase"}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :context-tag
:community-name "Coinbase"}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :context-tag-wrapper)))
(h/test "component renders in context-tag + button type"
(h/render [quo/drawer-top
{:title "Title"
:type :context-tag
:button-icon :i/placeholder
:community-name "Coinbase"}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :context-tag
:button-icon :i/placeholder
:community-name "Coinbase"}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :button-icon))
(h/is-truthy (h/get-by-label-text :context-tag-wrapper)))
(h/test "component renders in account type"
(h/render [quo/drawer-top
{:title "Title"
:type :account
:account-avatar-emoji "🍿"
:networks [{:name :ethereum :short-name "eth"}]
:description "0x62b...0a5"
:customization-color :purple}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :account
:account-avatar-emoji "🍿"
:networks [{:name :ethereum :short-name "eth"}]
:description "0x62b...0a5"
:customization-color :purple}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "0x62b...0a5"))
(h/is-truthy (h/get-by-label-text :account-avatar)))
(h/test "component renders in keypair type when keycard? is false"
(h/render [quo/drawer-top
{:title "Title"
:keycard? false
:icon-avatar :i/placeholder
:type :keypair}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:keycard? false
:icon-avatar :i/placeholder
:type :keypair}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(-> (h/expect (h/get-by-translation-text :t/on-device))
(.toBeTruthy)))
(h/test "component renders in keypair type when keycard? is true"
(h/render [quo/drawer-top
{:title "Title"
:keycard? true
:icon-avatar :i/placeholder
:type :keypair}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:keycard? true
:icon-avatar :i/placeholder
:type :keypair}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(-> (h/expect (h/get-by-translation-text :t/on-keycard))
(.toBeTruthy)))
(h/test "component renders in default-keypair type"
(h/render [quo/drawer-top
{:title "Title"
:description "0x62b...0a5"
:type :default-keypair}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:description "0x62b...0a5"
:type :default-keypair}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :default-keypair-text)))
(h/test "component renders in label type"
(h/render [quo/drawer-top
{:label "label"
:type :label}])
(h/render-with-theme-provider [quo/drawer-top
{:label "label"
:type :label}]
theme)
(h/is-truthy (h/get-by-text "label"))))
@@ -61,24 +61,30 @@
value (reagent/atom "")
focused? (atom false)]
(fn [{:keys [scanned-value theme blur? on-change-text on-blur on-focus on-clear on-scan
on-detect-ens on-detect-address address-regex valid-ens-or-address?
container-style]}]
on-detect-ens on-detect-address on-detect-unclassified address-regex ens-regex
valid-ens-or-address? container-style]}]
(let [on-change (fn [text]
(when (not= @value text)
(let [address? (when address-regex
(boolean (re-matches address-regex text)))]
(boolean (re-matches address-regex text)))
ens? (when ens-regex
(boolean (re-matches ens-regex text)))]
(if (> (count text) 0)
(reset! status :typing)
(reset! status :active))
(reset! value text)
(when on-change-text
(on-change-text text))
(when (and on-detect-ens (> (count text) 0))
(when (and on-detect-ens ens?)
(reset! status :loading)
(on-detect-ens text #(reset! status :typing)))
(when (and address? on-detect-address)
(reset! status :loading)
(on-detect-address text)))))
(on-detect-address text))
(when (and (not address?)
(not ens?)
on-detect-unclassified)
(on-detect-unclassified text)))))
on-paste (fn []
(clipboard/get-string
(fn [clipboard]
@@ -4,79 +4,83 @@
[quo.foundations.colors :as colors]
[test-helpers.component :as h]))
(def ^:private theme :light)
(h/describe "List items: account"
(h/test "default render'"
(h/render [account/view])
(h/render-with-theme-provider [account/view] theme)
(h/is-truthy (h/query-by-label-text :container)))
(h/test "on-press-in changes state to :pressed"
(h/render [account/view])
(h/render-with-theme-provider [account/view] theme)
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 5)})))
(h/test "on-press-in changes state to :pressed with blur? enabled"
(h/render [account/view {:blur? true}])
(h/render-with-theme-provider [account/view {:blur? true}] theme)
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor colors/white-opa-5})))
(h/test "render with state :active"
(h/render [account/view {:state :active}])
(h/render-with-theme-provider [account/view {:state :active}] theme)
(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 10)}))
(h/test "render with state :active and blur? enabled"
(h/render [account/view
{:blur? true
:state :active}])
(h/render-with-theme-provider [account/view
{:blur? true
:state :active}]
theme)
(h/has-style (h/query-by-label-text :container)
{:backgroundColor colors/white-opa-10}))
(h/test "render with state :selected"
(h/render [account/view {:state :selected}])
(h/render-with-theme-provider [account/view {:state :selected}] theme)
(h/is-truthy (h/query-by-label-text :check-icon)))
(h/test "calls on-press"
(let [on-press (h/mock-fn)]
(h/render [account/view {:on-press on-press}])
(h/render-with-theme-provider [account/view {:on-press on-press}] theme)
(h/fire-event :on-press (h/get-by-label-text :container))
(h/was-called on-press)))
(h/test "renders token props if type :tag"
(h/render [account/view {:type :tag}])
(h/render-with-theme-provider [account/view {:type :tag}] theme)
(h/is-truthy (h/query-by-label-text :tag-container)))
(h/test "renders keycard icon if title-icon is present"
(h/render [account/view {:title-icon :i/placeholder}])
(h/render-with-theme-provider [account/view {:title-icon :i/placeholder}] theme)
(h/is-truthy (h/query-by-label-text :title-icon)))
(h/test "doesn't render keycard icon if title-icon is missing"
(h/render [account/view])
(h/render-with-theme-provider [account/view] theme)
(h/is-falsy (h/query-by-label-text :title-icon)))
(h/test "renders balance container but not arrow icon if type :balance-neutral"
(h/render [account/view {:type :balance-neutral}])
(h/render-with-theme-provider [account/view {:type :balance-neutral}] theme)
(h/is-truthy (h/query-by-label-text :balance-container))
(h/is-falsy (h/query-by-label-text :arrow-icon)))
(h/test "renders balance container and negative arrow icon if type :balance-negative"
(h/render [account/view {:type :balance-negative}])
(h/render-with-theme-provider [account/view {:type :balance-negative}] theme)
(h/is-truthy (h/query-by-label-text :balance-container))
(h/is-truthy (h/query-by-label-text :icon-negative))
(h/is-falsy (h/query-by-label-text :icon-positive)))
(h/test "renders balance container and positive arrow icon if type :balance-positive"
(h/render [account/view {:type :balance-positive}])
(h/render-with-theme-provider [account/view {:type :balance-positive}] theme)
(h/is-truthy (h/query-by-label-text :balance-container))
(h/is-falsy (h/query-by-label-text :icon-negative))
(h/is-truthy (h/query-by-label-text :icon-positive)))
(h/test "renders options button if type :action"
(let [on-options-press (h/mock-fn)]
(h/render [account/view
{:type :action
:on-options-press on-options-press}])
(h/render-with-theme-provider [account/view
{:type :action
:on-options-press on-options-press}]
theme)
(h/is-truthy (h/query-by-label-text :options-button))
(h/fire-event :on-press (h/get-by-label-text :options-button))
(h/was-called on-options-press))))
@@ -9,8 +9,10 @@
[utils.i18n :as i18n]))
(defn- view-internal
[{:keys [state theme number word] :as props}]
[rn/view {:style (style/container props)}
[{:keys [state theme number word on-press] :as props}]
[rn/pressable
{:style (style/container props)
:on-press on-press}
(if (or (= state :empty) (= state :disabled))
[rn/view
{:style (style/num-container props)
@@ -24,9 +24,7 @@
{:background-color (colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 theme)
:flex-direction :row
:justify-content :space-between
:padding-vertical 8
:padding-left 10
:padding-right 8
:padding 8
:border-radius 12})
(defn title
@@ -47,8 +45,13 @@
theme))
:container-style {:width 20 :height 20}})
(def left-side-container {:padding 2})
(def right-side-container {:padding 4 :flex 1})
(def left-side-container
{:padding 2
:padding-top 3})
(def right-side-container
{:padding 4
:flex 1})
(defn action-container
[theme]
@@ -45,7 +45,6 @@
:blur-radius 10
:blur-type :transparent
:overlay-color :transparent}]
[rn/view {:style (style/content-container theme)}
[rn/view {:style style/left-side-container}
left]
@@ -77,16 +76,19 @@
:or {type :neutral icon :i/placeholder}}]
(let [context-theme (or theme (quo.theme/get-theme))
icon-name (case type
:positive :i/correct
:negative :i/incorrect
:positive (if (= theme :light)
:i/correct
:i/correct-dark)
:negative (if (= theme :light)
:i/incorrect
:i/incorrect-dark)
:neutral icon)]
[quo.theme/provider {:theme context-theme}
[toast-container
{:left (cond user
[user-avatar/user-avatar user]
icon-name
[icon/icon icon-name (style/icon type context-theme)]
)
[icon/icon icon-name (style/icon type context-theme)])
:title title
:text text
:right (if undo-duration
@@ -15,7 +15,7 @@
(defn- view-internal
[]
(let [pressed? (reagent/atom false)]
(fn [{:keys [disabled? theme blur? on-press type]} label]
(fn [{:keys [disabled? theme blur? on-press on-long-press type]} label]
(let [label-color (style/get-label-color disabled? theme blur?)
background-color (style/toggle-background-color @pressed? blur? theme)]
[rn/pressable
@@ -24,6 +24,9 @@
:on-press (fn []
(when on-press
(on-press label)))
:on-long-press (fn []
(when (fn? on-long-press)
(on-long-press label)))
:allow-multiple-presses? true
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
@@ -6,18 +6,19 @@
[react-native.core :as rn]))
(defn keyboard-item
[{:keys [item type disabled? on-press blur? theme]}]
[{:keys [item type disabled? on-press on-long-press blur? theme]}]
[keyboard-key/view
{:disabled? disabled?
:on-press on-press
:blur? blur?
:theme theme
:type type}
{:disabled? disabled?
:on-press on-press
:on-long-press on-long-press
:blur? blur?
:theme theme
:type type}
item])
(defn- view-internal
[]
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press on-delete
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press on-delete on-long-press-delete
container-style]
:or {left-action :none}}]
[rn/view
@@ -62,12 +63,13 @@
:theme theme}]
(if delete-key?
[keyboard-item
{:item :i/backspace
:type :key
:disabled? disabled?
:on-press on-delete
:blur? blur?
:theme theme}]
{:item :i/backspace
:type :key
:disabled? disabled?
:on-press on-delete
:on-long-press on-long-press-delete
:blur? blur?
:theme theme}]
[keyboard-item])]]))
(def view (quo.theme/with-theme view-internal))
@@ -121,7 +121,7 @@
{:accessibility-label :data-item
:disabled (not icon-right?)
:on-press on-press
:style (merge container-style (style/container size card? blur? theme))}
:style (merge (style/container size card? blur? theme) container-style)}
[left-side props]
(when (and (= :default status) (not= :small size))
[right-side
@@ -2,9 +2,64 @@
(:require [quo.components.share.share-qr-code.view :as share-qr-code]
[test-helpers.component :as h]))
(def qr-label "Text shown below QR")
(def default-props
{:qr-data qr-label
:qr-image-uri "test-uri"
:full-name "Default name"})
(def profile-props
{:type :profile
:profile-picture 123})
(def wallet-props {:networks '(:ethereum)})
(def text-press-event
{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press})
(def text-long-press-event
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press})
(def share-press-event
{:test-name "Share button"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press})
(def multichain-press-event
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press})
(def legacy-press-event
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press})
(def settings-press-event
{:test-name "Settings pressed"
:accessibility-label :share-qr-code-settings
:event-name :press
:callback-prop-key :on-settings-press})
(defn render-share-qr-code
[{share-qr-type :type :as props}]
(let [component-rendered (h/render [share-qr-code/view {:type share-qr-type}])
[props]
;; NOTE: rendering the profile type first, so that the re-render is triggered.
;; Passing a :key breaks it for some reason.
(let [component-rendered (h/render [share-qr-code/view
(merge
default-props
profile-props
{:qr-data "initial-render"})])
rerender-fn (h/get-rerender-fn component-rendered)
share-qr-code (h/get-by-label-text :share-qr-code)]
;; Fires on-layout since it's needed to render the content
@@ -13,23 +68,52 @@
(h/describe "Share QR Code component"
(h/describe "Renders share-qr-code component in all types"
(let [qr-label "Text shown below QR"]
(h/test "Profile"
(render-share-qr-code {:type :profile
:qr-data qr-label})
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Profile"
(render-share-qr-code (merge default-props
profile-props))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Wallet Legacy"
(render-share-qr-code {:type :wallet-legacy
:qr-data qr-label
:emoji "👻"})
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Wallet Legacy"
(render-share-qr-code (merge default-props
wallet-props
{:type :wallet
:address :legacy}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Wallet Multichain"
(render-share-qr-code {:type :wallet-multichain
:qr-data qr-label
:emoji "👻"})
(h/is-truthy (h/get-by-text qr-label)))))
(h/test "Wallet multichain"
(render-share-qr-code (merge default-props
wallet-props
{:type :wallet
:address :multichain}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Saved address legacy"
(render-share-qr-code (merge default-props
wallet-props
{:type :saved-address
:address :legacy}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Saved address multichain"
(render-share-qr-code (merge default-props
wallet-props
{:type :saved-address
:address :multichain}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Watched address legacy"
(render-share-qr-code (merge default-props
wallet-props
{:type :watched-address
:address :legacy}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Watched address multichain"
(render-share-qr-code (merge default-props
wallet-props
{:type :watched-address
:address :multichain}))
(h/is-truthy (h/get-by-text qr-label))))
(h/describe "Fires all events for all types"
(letfn [(test-fire-events [props test-seq]
@@ -44,68 +128,86 @@
(h/describe "Profile"
(test-fire-events
{:type :profile}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}]))
(merge default-props
profile-props)
[text-press-event
text-long-press-event
share-press-event]))
(h/describe "Wallet Legacy"
(test-fire-events
{:type :wallet-legacy :emoji "👽"}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button pressed"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press}
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press}]))
(merge default-props
wallet-props
{:type :wallet
:address :legacy
:emoji "👻"})
[text-press-event
text-long-press-event
share-press-event
multichain-press-event
legacy-press-event]))
(h/describe "Wallet Multichain"
(test-fire-events
{:type :wallet-multichain :emoji "👽"}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button pressed"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press}
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press}
{:test-name "Settings pressed"
:accessibility-label :share-qr-code-settings
:event-name :press
:callback-prop-key :on-settings-press}])))))
(merge default-props
wallet-props
{:type :wallet
:address :multichain
:emoji "👻"})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event
settings-press-event]))
(h/describe "Saved Address Legacy"
(test-fire-events
(merge default-props
wallet-props
{:type :saved-address
:address :legacy})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event]))
(h/describe "Saved Address Multichain"
(test-fire-events
(merge default-props
wallet-props
{:type :saved-address :address :multichain})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event
settings-press-event]))
(h/describe "Watched Address Legacy"
(test-fire-events
(merge default-props
wallet-props
{:type :watched-address
:address :legacy
:emoji "👽"})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event]))
(h/describe "Watched Address Multichain"
(test-fire-events
(merge default-props
wallet-props
{:type :watched-address
:address :multichain
:emoji "👽"})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event
settings-press-event])))))
@@ -0,0 +1,68 @@
(ns quo.components.share.share-qr-code.schema
(:require
[quo.foundations.resources :as resources]))
(defn- valid-network?
[network]
(-> network resources/get-network boolean))
(def ?base
[:map
[:type [:enum :profile :wallet :saved-address :watched-address]]
[:full-name :string]
[:qr-image-uri :string]
[:qr-data :string]
[:on-text-press {:optional true} [:maybe fn?]]
[:on-text-long-press {:optional true} [:maybe fn?]]
[:on-share-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:unblur-on-android? {:optional true} [:maybe :boolean]]])
(def ?emoji :string)
(def ?profile
[:map
[:type [:= :profile]]
[:profile-picture :schema.common/image-source]])
(def ?wallet
[:map
[:type [:= :wallet]]
[:emoji {:optional true} [:maybe ?emoji]]])
(def ?address-multichain
[:map
[:address [:= :multichain]]
[:on-settings-press {:optional true} fn?]])
(def ?address-base
[:merge
[:map
[:networks [:sequential [:fn valid-network?]]]
[:on-legacy-press {:optional true} [:maybe fn?]]
[:on-multichain-press {:optional true} [:maybe fn?]]
[:address [:enum :legacy :multichain]]]
[:multi {:dispatch :address}
[:multichain ?address-multichain]]])
(def ?saved-address
[:map
[:type [:= :saved-address]]
[:on-settings-press {:optional true} fn?]])
(def ?watched-address
[:map
[:type [:= :watched-address]]
[:emoji {:optional true} [:maybe ?emoji]]])
(def ?schema
[:=>
[:catn
[:props
[:multi {:dispatch :type}
[:profile [:merge ?base ?profile]]
[:wallet [:merge ?base ?address-base ?wallet]]
[:saved-address [:merge ?base ?address-base ?saved-address]]
[:watched-address [:merge ?base ?address-base ?watched-address]]]]]
:any])
@@ -106,3 +106,6 @@
{:color (-> network-short-name
(get-network-full-name :unknown)
(colors/resolve-color nil))})
(def watched-account-icon
{:margin-left 4})
@@ -3,19 +3,25 @@
[clojure.string :as string]
[oops.core :as oops]
[quo.components.avatars.account-avatar.view :as account-avatar]
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
[quo.components.buttons.button.view :as button]
[quo.components.gradient.gradient-cover.view :as gradient-cover]
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.share.qr-code.view :as qr-code]
[quo.components.share.share-qr-code.schema :as component-schema]
[quo.components.share.share-qr-code.style :as style]
[quo.components.tabs.tab.view :as tab]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]
[utils.i18n :as i18n]))
(defn- header
[{:keys [share-qr-type on-legacy-press on-multichain-press]}]
[{:keys [on-legacy-press on-multichain-press address]}]
[rn/view {:style style/header-container}
[tab/view
{:accessibility-label :share-qr-code-legacy-tab
@@ -23,7 +29,7 @@
:active-item-container-style style/header-tab-active
:item-container-style style/header-tab-inactive
:size 24
:active (= :wallet-legacy share-qr-type)
:active (= :legacy address)
:on-press on-legacy-press}
(i18n/label :t/legacy)]
[rn/view {:style style/space-between-tabs}]
@@ -33,7 +39,7 @@
:active-item-container-style style/header-tab-active
:item-container-style style/header-tab-inactive
:size 24
:active (= :wallet-multichain share-qr-type)
:active (= :multichain address)
:on-press on-multichain-press}
(i18n/label :t/multichain)]])
@@ -121,77 +127,69 @@
:on-press on-settings-press}
:i/advanced]])
(defn- header-icon
[{:keys [share-qr-type customization-color emoji profile-picture full-name]}]
(case share-qr-type
:profile [user-avatar/user-avatar
{:size :small
:status-indicator? false
:profile-picture profile-picture
:customization-color customization-color}]
(:wallet :watched-address) [account-avatar/view
{:customization-color customization-color
:emoji emoji
:size 32}]
:saved-address [wallet-user-avatar/wallet-user-avatar
{:size :size-32
:customization-color customization-color
:full-name full-name}]
nil))
(defn- share-qr-code
[{:keys [share-qr-type qr-image-uri component-width customization-color full-name
profile-picture emoji on-share-press]
profile-picture emoji on-share-press address]
:as props}]
[:<>
[rn/view {:style style/gradient-bg}
[gradient-cover/view {:customization-color customization-color :height 463}]]
[rn/view {:style style/content-container}
[rn/view
{:style style/share-qr-container}
[rn/view
{:style style/share-qr-inner-container}
[account-avatar/view
{:customization-color customization-color
:emoji emoji
:size 32}]
[rn/view {:style style/share-qr-container}
[rn/view {:style style/share-qr-inner-container}
[header-icon props]
[text/text
{:size :heading-2
:weight :semi-bold
:style {:margin-left 8}} full-name]]
:style {:margin-left 8}} full-name]
(when (= share-qr-type :watched-address)
[icons/icon
:i/reveal
{:color colors/white-opa-40
:container-style style/watched-account-icon}])]
[share-button {:on-press on-share-press}]]
(when (#{:wallet-legacy :wallet-multichain} share-qr-type)
(when (not= share-qr-type :profile)
[header props])
[quo.theme/provider {:theme :light}
[qr-code/view
{:qr-image-uri qr-image-uri
:size (style/qr-code-size component-width)
:avatar (if (= share-qr-type :profile)
:profile
:wallet-account)
:avatar (case share-qr-type
:profile :profile
(:watched-address :wallet) :wallet-account
:saved-address :saved-address
nil)
:customization-color customization-color
:full-name full-name
:profile-picture profile-picture
:emoji emoji}]]
[rn/view {:style style/bottom-container}
(case share-qr-type
:profile [profile-bottom props]
:wallet-legacy [wallet-legacy-bottom props]
:wallet-multichain [wallet-multichain-bottom props]
nil)]]])
(if (= share-qr-type :profile)
[profile-bottom props]
(case address
:legacy [wallet-legacy-bottom props]
:multichain [wallet-multichain-bottom props]
nil))]]])
(defn- view-internal
"Receives the following properties:
- type: :profile | :wallet-legacy | :wallet-multichain
- qr-image-uri: Image source value.
- qr-data: Text to show below the QR code.
- on-text-press: Callback for the `qr-data` text.
- on-text-long-press: Callback for the `qr-data` text.
- on-share-press: Callback for the share button.
- customization-color: Custom color for the QR code component.
- unblur-on-android?: [Android only] disables blur for this component.
Depending on the `type`, different properties are accepted:
`:profile`
- full-name: User full name.
- profile-picture: map ({:source image-source}) or any image source.
`:wallet-legacy`
- emoji: Emoji in a string to show in the QR code.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
`:wallet-multichain`
- networks: A vector of network names as keywords (`[:ethereum, :my-net, ...]`).
- on-settings-press: Callback for the settings button.
- emoji: Emoji in a string to show in the QR code.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
WARNING on Android:
Sometimes while using a blur layer on top of another on Android, this component looks
bad because of the `blur/view`, so we can set `unblur-on-android? true` to fix it.
"
[props]
(reagent/with-let [component-width (reagent/atom nil)
container-component [rn/view {:background-color style/overlay-color}]]
@@ -207,5 +205,6 @@
(assoc :component-width @component-width)
(clojure.set/rename-keys {:type :share-qr-type}))]))]]))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -5,6 +5,7 @@
(def username "Alisher account")
(def empty-username "Account 1")
(def ^:private theme :light)
(defn get-test-data
[{:keys [type watch-only? empty-type? loading? no-metrics?]}]
@@ -21,54 +22,54 @@
(h/describe "Account_card tests"
(h/test "Renders Default"
(let [data (get-test-data {:type :default})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text username))))
(h/test "Renders Watch-Only"
(let [data (get-test-data {:type :watch-only
:watch-only? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text username))))
(h/test "Renders Add-Account"
(let [data {:type :add-account}]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-label-text :add-account))))
(h/test "Renders Empty"
(let [data (get-test-data {:type :empty
:empty-type? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text empty-username))))
(h/test "Renders Missing Keypair"
(let [data (get-test-data {:type :missing-keypair})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text username))))
(h/test "Add account on press fires correctly"
(let [on-press (h/mock-fn)
data {:type :add-account
:on-press on-press}]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/fire-event :on-press (h/get-by-label-text :add-account))
(h/was-called on-press)))
(h/test "Renders component without metrics"
(let [data (get-test-data {:type :default
:no-metrics? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-falsy (h/query-by-label-text :metrics))))
(h/test "Renders loading state"
(let [data (get-test-data {:type :default
:loading? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-label-text :loading))))
(h/test "Renders loading state without metrics"
(let [data (get-test-data {:type :default
:no-metrics? true
:loading? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-falsy (h/query-by-label-text :metrics)))))
@@ -0,0 +1,33 @@
(ns quo.components.wallet.account-card.schema)
(def ^:private ?base
[:map
[:type {:optional true} [:enum :default :watch-only :add-account :empty :missing-keypair]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:theme :schema.common/theme]
[:metrics? {:optional true} [:maybe :boolean]]
[:on-press {:optional true} [:maybe fn?]]])
(def ^:private ?amount
[:map
[:amount {:optional true} [:maybe :string]]])
(def ^:private ?card
[:map
[:balance {:optional true} [:maybe :string]]
[:loading? {:optional true} [:maybe :boolean]]
[:name {:optional true} [:maybe :string]]
[:percentage-value {:optional true} [:maybe :string]]
[:emoji {:optional true} [:maybe :string]]])
(def ?schema
[:=>
[:catn
[:props
[:multi {:dispatch :type}
[:default [:merge ?base ?amount ?card]]
[:watch-only [:merge ?base ?amount ?card]]
[:missing-keypair [:merge ?base ?amount ?card]]
[:empty [:merge ?base ?card]]
[:add-account ?base]]]]
:any])
@@ -4,13 +4,15 @@
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.components.wallet.account-card.properties :as properties]
[quo.components.wallet.account-card.schema :as component-schema]
[quo.components.wallet.account-card.style :as style]
[quo.foundations.colors :as colors]
[quo.foundations.customization-colors :as customization-colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[reagent.core :as reagent]))
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- loading-view
[{:keys [customization-color type theme metrics?]}]
@@ -183,4 +185,6 @@
:add-account [add-account-view props]
nil))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -3,92 +3,104 @@
[quo.core :as quo]
[test-helpers.component :as h]))
(def ^:private theme :light)
(h/describe "account origin tests"
(h/test "component renders"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(-> (h/expect (h/get-by-translation-text :t/origin))
(.toBeTruthy)))
(h/test "recovery phrase icon is visible when :type is :recovery-phrase"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :recovery-phrase-icon)))
(h/test "recovery phrase icon is visible when :type is :recovery-phrase"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :recovery-phrase-icon)))
(h/test "private-key-icon is visible when :type is :private-key"
(h/render [quo/account-origin
{:type :private-key
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :private-key
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :private-key-icon)))
(h/test "derivation-path-icon is visible when :type is :default-keypair"
(h/render [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :derivation-path-icon)))
(h/test "derivation-path-icon is visible when :type is :recovery-phrase"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :derivation-path-icon)))
(h/test "on-device text is visible when :stored is :on-device"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-device
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-device
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(-> (h/expect (h/get-by-translation-text :t/on-device))
(.toBeTruthy)))
(h/test "on-keycard text is visible when :stored is :on-keycard"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(-> (h/expect (h/get-by-translation-text :t/on-keycard))
(.toBeTruthy)))
(h/test "on-press is called when :type is :recovery-phrase"
(let [on-press (h/mock-fn)]
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}]
theme)
(h/fire-event :press (h/query-by-label-text :derivation-path-button))
(h/was-called on-press)))
(h/test "on-press is called when :type is :default-keypair"
(let [on-press (h/mock-fn)]
(h/render [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}])
(h/render-with-theme-provider [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}]
theme)
(h/fire-event :press (h/query-by-label-text :derivation-path-button))
(h/was-called on-press))))
@@ -0,0 +1,30 @@
(ns quo.components.wallet.account-origin.schema)
(def ^:private ?base
[:map
[:type {:optional true} [:enum :default-keypair :recovery-phrase :private-key]]
[:stored {:optional true} [:enum :on-device :on-keycard]]
[:theme :schema.common/theme]])
(def ^:private ?default-keypair
[:map
[:user-name {:optional true} [:maybe :string]]
[:profile-picture {:optional true} [:maybe [:or :schema.common/image-source :string]]]
[:derivation-path {:optional true} [:maybe :string]]
[:on-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]])
(def ^:private ?recovery-phrase
[:map
[:derivation-path {:optional true} [:maybe :string]]
[:on-press {:optional true} [:maybe fn?]]])
(def ?schema
[:=>
[:catn
[:props
[:multi {:dispatch :type}
[:default-keypair [:merge ?base ?default-keypair]]
[:recovery-phrase [:merge ?base ?recovery-phrase]]
[:private-key ?base]]]]
:any])
@@ -3,10 +3,12 @@
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.wallet.account-origin.schema :as component-schema]
[quo.components.wallet.account-origin.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[schema.core :as schema]
[utils.i18n :as i18n]))
(defn- row-title
@@ -107,14 +109,5 @@
[card-view theme derivation-path secondary-color on-press])]))
(def view
"Create an account-origin UI component.
| key | values |
| ----------------------|------------------------------------------------|
| :type | :default-keypair :recovery-phrase :private-key
| :stored | :on-device :on-keycard
| :profile-picture | image source
| :customization-color | profile color
| :derivation-path | string
| :user-name | string
| :on-press | function"
(quo.theme/with-theme view-internal))
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -0,0 +1,20 @@
(ns quo.components.wallet.account-overview.schema)
(def ?schema
[:=>
[:catn
[:props
[:map
[:account-name {:optional true} [:maybe :string]]
[:currency-change {:optional true} [:maybe :string]]
[:current-value {:optional true} [:maybe :string]]
[:percentage-change {:optional true} [:maybe :string]]
[:time-frame-string {:optional true} [:maybe :string]]
[:time-frame-to-string {:optional true} [:maybe :string]]
[:state {:optional true} [:enum :default :loading]]
[:metrics {:optional true} [:enum :negative :positive]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:account {:optional true} [:enum :default :watched-address]]
[:time-frame {:optional true}
[:enum :one-week :one-month :three-months :one-year :all-time :custom]]]]]
:any])
@@ -2,10 +2,12 @@
(:require
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.wallet.account-overview.schema :as component-schema]
[quo.components.wallet.account-overview.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[schema.core :as schema]
[utils.i18n :as i18n]))
(defn- loading-state
@@ -129,16 +131,5 @@
[numeric-changes percentage-change currency-change customization-color theme up?])]])]))
(def view
"Create a account-overview UI component.
| key | description |
| -------------|-------------|
| `:account-name` | A value representing the account name (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`)
| `:time-frame-string` | A value representing time frame string. (default `nil`)
| `:current-value` | A value representing the current value of the selected account. (default `nil`)
| `:currency-change` | A value representing the change of the value of the selected account in the selected currency. (default `nil`)
| `:percentage-change` | A value representing the change of the value of the selected account in percentage relative to it's previous value. (default `nil`)"
(quo.theme/with-theme view-internal))
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -42,18 +42,22 @@
(render [account-permissions/view
{:account account
:token-details [{:token "SNT"
:amount "100"}]}])
:amount "100"
:type :token}]}])
(h/is-truthy (h/get-by-text "100 SNT")))
(h/test "render with multiple token details"
(render [account-permissions/view
{:account account
:token-details [{:token "SNT"
:amount "100"}
:amount "100"
:type :token}
{:token "ETH"
:amount "18"}
:amount "18"
:type :token}
{:token "BTM"
:amount "1000"}]}])
:amount "1000"
:type :token}]}])
(h/is-truthy (h/get-by-text "100 SNT"))
(h/is-truthy (h/get-by-text "18 ETH"))
(h/is-truthy (h/get-by-text "1000 BTM")))
@@ -0,0 +1,22 @@
(ns quo.components.wallet.account-permissions.schema
(:require [quo.components.wallet.required-tokens.view :as required-tokens]))
(def ?schema
[:=>
[:catn
[:props
[:map
[:account
[:map
[:name [:maybe :string]]
[:address [:maybe :string]]
[:emoji [:maybe :string]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]
[:token-details {:optional true} [:maybe [:sequential required-tokens/?schema]]]
[:keycard? {:optional true} [:maybe :boolean]]
[:checked? {:optional true} [:maybe :boolean]]
[:disabled? {:optional true} [:maybe :boolean]]
[:on-change {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]
[:theme :schema.common/theme]]]]
:any])
@@ -4,6 +4,7 @@
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.selectors.selectors.view :as selectors]
[quo.components.wallet.account-permissions.schema :as component-schema]
[quo.components.wallet.account-permissions.style :as style]
[quo.components.wallet.address-text.view :as address-text]
[quo.components.wallet.required-tokens.view :as required-tokens]
@@ -13,26 +14,6 @@
[schema.core :as schema]
[utils.i18n :as i18n]))
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:account
[:map {:closed true}
[:name [:maybe :string]]
[:address [:maybe :string]]
[:emoji [:maybe :string]]
[:customization-color [:maybe [:or :string :keyword]]]]]
[:token-details {:optional true} [:maybe [:vector required-tokens/?schema]]]
[:keycard? {:optional true} [:maybe :boolean]]
[:checked? {:optional true} [:maybe :boolean]]
[:disabled? {:optional true} [:maybe :boolean]]
[:on-change {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]
[:theme :schema.common/theme]]]]
:any])
(defn- token-details-section
[tokens]
(when tokens
@@ -47,14 +28,16 @@
(i18n/label :t/no-relevant-tokens)]
(let [token-length (dec (count tokens))]
(map-indexed (fn [idx {:keys [token amount]}]
(map-indexed (fn [idx {:keys [type token amount collectible-name collectible-img-src]}]
^{:key idx}
[required-tokens/view
{:container-style style/token-and-text
:type :token
:amount amount
:token token
:divider? (not= token-length idx)}])
{:container-style style/token-and-text
:type type
:amount amount
:token token
:collectible-img-src collectible-img-src
:collectible-name collectible-name
:divider? (not= token-length idx)}])
tokens)))]]))
(defn- view-internal
@@ -93,4 +76,4 @@
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal ?schema)))
(schema/instrument #'view-internal component-schema/?schema)))
@@ -0,0 +1,14 @@
(ns quo.components.wallet.address-text.schema)
(def ?schema
[:=>
[:catn
[:props
[:map
[:address {:optional true} [:maybe :string]]
[:blur? {:optional true} [:maybe :boolean]]
[:format {:optional true} [:enum :short :long]]
[:theme :schema.common/theme]
[:networks {:optional true}
[:maybe [:sequential [:map [:name :keyword] [:short-name :string]]]]]]]]
:any])
@@ -1,8 +1,10 @@
(ns quo.components.wallet.address-text.view
(:require [quo.components.markdown.text :as text]
[quo.components.wallet.address-text.schema :as component-schema]
[quo.components.wallet.address-text.style :as style]
[quo.foundations.colors :as colors]
[quo.theme]
[schema.core :as schema]
[utils.address :as utils]))
(defn- colored-network-text
@@ -28,4 +30,6 @@
(into [text/text] network-text-xf $)
(conj $ address-text))))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -1,99 +1,111 @@
(ns quo.components.wallet.confirmation-progress.component-spec
(:require [quo.core :as quo]
[reagent.core :as reagent]
[test-helpers.component :as h]))
(def ^:private theme :light)
(defn- get-test-data
[{:keys [state network]
:or {state :pending network :mainnet}}]
{:counter (reagent/atom 0)
{:counter 0
:total-box 85
:progress-value "10"
:progress-value 10
:network network
:state state
:customization-color :blue})
(h/describe "Confirmation Progress"
(h/test "component renders when state is sending and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :sending
:network :optimism})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :sending
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is confirmed and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :confirmed
:network :optimism})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :confirmed
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalising and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalising
:network :optimism})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :finalising
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalized and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalized
:network :optimism})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :finalized
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is error and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :error
:network :optimism})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :error
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is sending and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :sending
:network :arbitrum})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :sending
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is confirmed and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :confirmed
:network :arbitrum})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :confirmed
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalising and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalising
:network :arbitrum})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :finalising
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalized and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalized
:network :arbitrum})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :finalized
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is error and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :error
:network :arbitrum})])
(h/render-with-theme-provider [quo/confirmation-propgress
(get-test-data {:state :error
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is pending and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {})])
(h/render-with-theme-provider [quo/confirmation-propgress (get-test-data {})] theme)
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is sending and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :sending})])
(h/render-with-theme-provider [quo/confirmation-propgress (get-test-data {:state :sending})] theme)
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is confirmed and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :confirmed})])
(h/render-with-theme-provider [quo/confirmation-propgress (get-test-data {:state :confirmed})] theme)
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is finalising and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :finalising})])
(h/render-with-theme-provider [quo/confirmation-propgress (get-test-data {:state :finalising})]
theme)
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is finalized and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :finalized})])
(h/render-with-theme-provider [quo/confirmation-propgress (get-test-data {:state :finalized})] theme)
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is error and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :error})])
(h/render-with-theme-provider [quo/confirmation-propgress (get-test-data {:state :error})] theme)
(h/is-truthy (h/get-by-label-text :mainnet-progress-box))))
@@ -0,0 +1,15 @@
(ns quo.components.wallet.confirmation-progress.schema)
(def ?schema
[:=>
[:catn
[:props
[:map
[:total-box {:optional true} [:maybe :int]]
[:counter {:optional true} [:maybe :int]]
[:progress-value {:optional true} [:maybe :int]]
[:network {:optional true} [:enum :mainnet :optimism :arbitrum]]
[:state {:optional true} [:enum :pending :sending :confirmed :finalising :finalized :error]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:theme :schema.common/theme]]]]
:any])
@@ -1,8 +1,10 @@
(ns quo.components.wallet.confirmation-progress.view
(:require [quo.components.wallet.confirmation-progress.style :as style]
(:require [quo.components.wallet.confirmation-progress.schema :as component-schema]
[quo.components.wallet.confirmation-progress.style :as style]
[quo.components.wallet.progress-bar.view :as progress-box]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
[react-native.core :as rn]
[schema.core :as schema]))
(def ^:private max-progress 100)
(def ^:private min-progress 0)
@@ -66,4 +68,6 @@
(:arbitrum :optimism) [progress-boxes-sidenet props]
nil))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -3,6 +3,8 @@
[quo.components.wallet.keypair.view :as keypair]
[test-helpers.component :as h]))
(def ^:private theme :light)
(def accounts
[{:account-props {:customization-color :turquoise
:size 32
@@ -23,41 +25,45 @@
(h/describe "Wallet: Keypair"
(h/test "Default keypair title renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :default-keypair
:stored :on-device
:action :selector
:details default-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :default-keypair
:stored :on-device
:action :selector
:details default-details}]
theme)
(h/is-truthy (h/get-by-label-text :title)))
(h/test "On device renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-device
:action :selector
:details other-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-device
:action :selector
:details other-details}]
theme)
(h/is-truthy (h/get-by-label-text :details)))
(h/test "Selector action renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :selector
:details other-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :selector
:details other-details}]
theme)
(h/is-truthy (h/get-by-label-text :radio-on)))
(h/test "Options action renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :options
:details other-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :options
:details other-details}]
theme)
(h/is-truthy (h/get-by-label-text :options-button))))
@@ -15,7 +15,7 @@
[:amount {:optional true} [:maybe [:or :string :int]]]
[:token {:optional true} [:maybe :string]]
[:token-img-src {:optional true} [:maybe :schema.common/image-source]]
[:collectible-img-src {:optional true} :schema.common/image-source]
[:collectible-img-src {:optional true} [:maybe :schema.common/image-source]]
[:collectible-name {:optional true} [:maybe :string]]
[:divider? {:optional true} [:maybe :boolean]]
[:theme :schema.common/theme]
@@ -1,200 +1,225 @@
(ns quo.components.wallet.transaction-progress.component-spec
(:require [quo.core :as quo]
[reagent.core :as reagent]
[test-helpers.component :as h]))
(def ^:private theme :light)
(defn- get-test-data
[{:keys [state network]
:or {state :pending network :mainnet}}]
{:title "Title"
:tag-name "Doodle"
:tag-number "120"
:tag-number 120
:network network
:customization-color :blue
:networks [{:network :mainnet
:state state
:counter (reagent/atom 0)
:counter 0
:total-box 85
:progress 30
:epoch-number "123"}
{:network :optimism
:state state
:progress "50"
:progress 50
:epoch-number "123"}
{:network :arbitrum
:state state
:progress "30"
:progress 30
:epoch-number "123"}]
:on-press (fn []
(js/alert "Transaction progress item pressed"))})
(h/describe "Transaction Progress"
(h/test "component renders without props"
(h/render [quo/transaction-progress])
(h/render-with-theme-provider [quo/transaction-progress] theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is pending and network is mainnet"
(h/render [quo/transaction-progress (get-test-data {:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is sending and network is optimism-arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :sending
:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :sending
:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is confirmed and network is optimism-arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :confirmed
:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :confirmed
:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalising and network is optimism-arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :finalising
:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :finalising
:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalized and network is optimism-arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :finalized
:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :finalized
:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is error and network is optimism-arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :error
:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :error
:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is sending and network is optimism"
(h/render [quo/transaction-progress
(get-test-data {:state :sending
:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :sending
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is confirmed and network is optimism"
(h/render [quo/transaction-progress
(get-test-data {:state :confirmed
:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :confirmed
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalising and network is optimism"
(h/render [quo/transaction-progress
(get-test-data {:state :finalising
:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :finalising
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalized and network is optimism"
(h/render [quo/transaction-progress
(get-test-data {:state :finalized
:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :finalized
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is error and network is optimism"
(h/render [quo/transaction-progress
(get-test-data {:state :error
:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :error
:network :optimism})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is sending and network is arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :sending
:network :arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :sending
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is confirmed and network is arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :confirmed
:network :arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :confirmed
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalising and network is arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :finalising
:network :arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :finalising
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalized and network is arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :finalized
:network :arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :finalized
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is error and network is arbitrum"
(h/render [quo/transaction-progress
(get-test-data {:state :error
:network :arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:state :error
:network :arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is pending and network is mainnet"
(h/render [quo/transaction-progress (get-test-data {})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {})] theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is sending and network is mainnet"
(h/render [quo/transaction-progress (get-test-data {:state :sending})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:state :sending})] theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is confirmed and network is mainnet"
(h/render [quo/transaction-progress (get-test-data {:state :confirmed})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:state :confirmed})] theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalising and network is mainnet"
(h/render [quo/transaction-progress (get-test-data {:state :finalising})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:state :finalising})] theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is finalized and network is mainnet"
(h/render [quo/transaction-progress (get-test-data {:state :finalized})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:state :finalized})] theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "component renders when state is error and network is mainnet"
(h/render [quo/transaction-progress (get-test-data {:state :error})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:state :error})] theme)
(h/is-truthy (h/get-by-label-text :transaction-progress)))
(h/test "mainnet progress box is visible network is mainnet"
(h/render [quo/transaction-progress (get-test-data {})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {})] theme)
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "arbitrum-optimism progress box is visible network is optimism-arbitrum"
(h/render [quo/transaction-progress (get-test-data {:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-all-by-label-text :progress-box)))
(h/test "arbitrum progress box is visible network is arbitrum"
(h/render [quo/transaction-progress (get-test-data {:network :arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:network :arbitrum})] theme)
(h/is-truthy (h/get-all-by-label-text :progress-box)))
(h/test "optimism progress box is visible network is optimism"
(h/render [quo/transaction-progress (get-test-data {:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:network :optimism})] theme)
(h/is-truthy (h/get-all-by-label-text :progress-box)))
(h/test "title is visible network is optimism-arbitrum"
(h/render [quo/transaction-progress (get-test-data {:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-text "Title")))
(h/test "title is visible network is mainnet"
(h/render [quo/transaction-progress (get-test-data {})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {})] theme)
(h/is-truthy (h/get-by-text "Title")))
(h/test "title is visible network is optimism"
(h/render [quo/transaction-progress (get-test-data {:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:network :optimism})] theme)
(h/is-truthy (h/get-by-text "Title")))
(h/test "title is visible network is arbitrum"
(h/render [quo/transaction-progress (get-test-data {:network :arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:network :arbitrum})] theme)
(h/is-truthy (h/get-by-text "Title")))
(h/test "context tag is visible network is optimism-arbitrum"
(h/render [quo/transaction-progress (get-test-data {:network :optimism-arbitrum})])
(h/render-with-theme-provider [quo/transaction-progress
(get-test-data {:network :optimism-arbitrum})]
theme)
(h/is-truthy (h/get-by-label-text :context-tag)))
(h/test "context tag is visible network is mainnet"
(h/render [quo/transaction-progress (get-test-data {})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {})] theme)
(h/is-truthy (h/get-by-label-text :context-tag)))
(h/test "context tag is visible network is optimism"
(h/render [quo/transaction-progress (get-test-data {:network :optimism})])
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:network :optimism})] theme)
(h/is-truthy (h/get-by-label-text :context-tag)))
(h/test "context tag is visible network is optimism"
(h/render [quo/transaction-progress (get-test-data {:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :context-tag))))
(h/render-with-theme-provider [quo/transaction-progress (get-test-data {:network :arbitrum})] theme)
(h/is-truthy (h/get-by-label-text :context-tag)))
)
+1 -1
View File
@@ -22,7 +22,7 @@
{:min 1}
[:map
{:closed true}
[:method :string]
[:method [:or :keyword :string]]
[:params [:sequential :any]]
[:js-response {:optional true} :any]
[:on-success [:or fn? [:cat keyword? [:* :any]]]]
+34 -29
View File
@@ -54,38 +54,43 @@
(defn share-qr-code
"Receives the following properties:
- type: :profile | :wallet-legacy | :wallet-multichain
- qr-data: Text to encode in the QR code
- qr-data-label-shown: Text to show below the QR code
- on-text-press: Callback for the `qr-data-label-shown` text.
- on-text-long-press: Callback for the `qr-data-label-shown` text.
- on-share-press: Callback for the share button.
- customization-color: Custom color for the QR code component.
- unblur-on-android?: [Android only] disables blur for this component.
Depending on the `type`, different properties are accepted:
`:profile`
- full-name: User full name.
- profile-picture: map ({:source image-source}) or any image source.
`:wallet-legacy`
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
`:wallet-multichain`
- networks: A vector of network names (`[:ethereum, :optimism, :my-net, ,,,]`)
they will add network prefixes to the address in
`qr-data-label-shown` and the QR code generated.
- on-settings-press: Callback for the settings button.
- emoji: Emoji in a string to show in the QR code.
- on-info-press: Callback for the info icon.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab."
[{:keys [qr-data qr-data-label-shown networks]
- type: :profile | :wallet | :saved-address | :watched-address
- qr-image-uri: Image source value.
- qr-data: Text to show below the QR code.
- on-text-press: Callback for the `qr-data` text.
- on-text-long-press: Callback for the `qr-data` text.
- on-share-press: Callback for the share button.
- customization-color: Custom color for the QR code component.
- unblur-on-android?: [Android only] disables blur for this component.
- full-name: User full name.
Depending on the `type`, different properties are accepted:
`:profile`
- profile-picture: map ({:source image-source}) or any image source.
`:wallet`
- networks: A vector of network names as keywords (`[:ethereum, :my-net, ...]`).
- emoji: Emoji in a string to show in the QR code.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
- address: :legacy | :multichain
`:saved-address`
- networks: A vector of network names as keywords (`[:ethereum, :my-net, ...]`).
- on-settings-press: Callback for the settings button.
- on-legacy-press: Callback for the legacy tab.
- address: :legacy | :multichain
- on-multichain-press: Callback for the multichain tab.
`:watched-address`
- networks: A vector of network names as keywords (`[:ethereum, :my-net, ...]`).
- on-settings-press: Callback for the settings button.
- emoji: Emoji in a string to show in the QR code.
- on-legacy-press: Callback for the legacy tab.
- address: :legacy | :multichain
- on-multichain-press: Callback for the multichain tab."
[{:keys [qr-data qr-data-label-shown networks address]
share-qr-type :type
:as props}]
(let [label (or qr-data-label-shown qr-data)
string-to-encode (if (= share-qr-type :wallet-multichain)
string-to-encode (if (and (= share-qr-type :wallet) (= address :multichain))
(get-qr-data-for-wallet-multichain qr-data networks)
qr-data)
qr-media-server-uri (image-server/get-qr-image-uri-for-any-url
+5 -3
View File
@@ -1,9 +1,11 @@
(ns status-im.common.toasts.style)
(ns status-im.common.toasts.style
(:require [react-native.safe-area :as safe-area]))
(def outmost-transparent-container
(defn outmost-transparent-container
[]
{:elevation 2
:pointer-events :box-none
:padding-top 52
:padding-top (+ (safe-area/get-top) 6)
:flex-direction :column
:justify-content :center
:align-items :center})
+1 -1
View File
@@ -60,5 +60,5 @@
[]
(->> (rf/sub [:toasts])
:ordered
(into [rn/view {:style style/outmost-transparent-container}]
(into [rn/view {:style (style/outmost-transparent-container)}]
(map #(with-meta [:f> f-container %] {:key %})))))
+1 -6
View File
@@ -167,10 +167,5 @@
(def default-kdf-iterations 3200)
(def community-accounts-selection-enabled? (enabled? (get-config :ACCOUNT_SELECTION_ENABLED "0")))
(def community-accounts-selection-enabled? true)
(def fetch-messages-enabled? (enabled? (get-config :FETCH_MESSAGES_ENABLED "1")))
(def wallet-feature-flags
{:edit-default-keypair false
:bridge-token false
:remove-account false})
+3
View File
@@ -159,6 +159,9 @@
community-token-permission-become-token-master
community-token-permission-become-token-owner})
(def ^:const community-token-type-erc20 1)
(def ^:const community-token-type-erc721 2)
;; Community rules for joining
(def ^:const community-rule-ens-only "ens-only")
@@ -153,19 +153,19 @@
50)}]))
:max-duration-ms constants/audio-max-duration-ms}]]))
(defn images-limit-toast
(defn photo-limit-toast
[]
(rf/dispatch [:toasts/upsert
{:id :random-id
:type :negative
:container-style {:top (when platform/ios? 20)}
:text (i18n/label :t/only-6-images)}]))
{:id :random-id
:type :negative
:text (i18n/label :t/hit-photos-limit
{:max-photos constants/max-album-photos})}]))
(defn go-to-camera
[images-count]
(device-permissions/camera #(if (>= images-count constants/max-album-photos)
(images-limit-toast)
(photo-limit-toast)
(rf/dispatch [:navigate-to :camera-screen]))))
(defn camera-button
@@ -55,6 +55,7 @@
^js identity-images (.-identityImages response-js)
^js accounts (.-accounts response-js)
^js ens-username-details-js (.-ensUsernameDetails response-js)
^js customization-color-js (.-customizationColor response-js)
sync-handler (when-not process-async process-response)]
(cond
@@ -204,7 +205,14 @@
(js-delete response-js "ensUsernameDetails")
(rf/merge cofx
(process-next response-js sync-handler)
(rf/dispatch [:ens/update-usernames ens-username-details-clj]))))))
(rf/dispatch [:ens/update-usernames ens-username-details-clj])))
(seq customization-color-js)
(let [customization-color (types/js->clj customization-color-js)]
(js-delete response-js "customizationColor")
(rf/merge cofx
(process-next response-js sync-handler)
(rf/dispatch [:profile/save-local-accent-color (keyword customization-color)]))))))
(defn group-by-and-update-unviewed-counts
"group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats"
@@ -75,3 +75,6 @@
{:position :absolute
:top 8
:right 8})
(def photo-limit-toast-container
{:top (if platform/ios? 6 16)})
@@ -4,7 +4,6 @@
[react-native.core :as rn]
[react-native.gesture :as gesture]
[react-native.linear-gradient :as linear-gradient]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
@@ -17,13 +16,14 @@
(def min-scroll-to-blur 5)
(defn show-toast
(defn show-photo-limit-toast
[]
(rf/dispatch [:toasts/upsert
{:id :random-id
:type :negative
:container-style {:top (when platform/ios? 20)}
:text (i18n/label :t/only-6-images)}]))
:container-style style/photo-limit-toast-container
:text (i18n/label :t/hit-photos-limit
{:max-photos constants/max-album-photos})}]))
(defn on-press-confirm-selection
[selected close]
@@ -74,7 +74,7 @@
(if item-selected?
(swap! selected remove-selected item)
(if (>= (count @selected) constants/max-album-photos)
(show-toast)
(show-photo-limit-toast)
(swap! selected conj item))))
:accessibility-label (str "image-" index)}
[rn/image
@@ -18,7 +18,7 @@
{:community-id id :password %}])}])
(rf/dispatch [:navigate-back]))
(defn f-view-internal
(defn view
[]
(let [{id :community-id} (rf/sub [:get-screen-params])
{:keys [name color images]} (rf/sub [:communities/community id])
@@ -83,7 +83,3 @@
:track-icon :i/face-id
:customization-color color
:on-complete #(join-community-and-navigate-back id)}]]]))
(defn view
[]
[:f> f-view-internal])
@@ -0,0 +1,58 @@
(ns status-im.contexts.communities.actions.addresses-for-permissions.events
(:require
[schema.core :as schema]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn get-permissioned-balances
[{[event-id _] :original-event} [community-id]]
{:fx [[:json-rpc/call
[{:method :wakuext_getCommunityPermissionedBalances
:params [{:communityId community-id}]
:on-success [:communities/get-permissioned-balances-success community-id]
:on-error (fn [error]
(log/error "failed to get balances"
{:community-id community-id
:event event-id
:error error}))}]]]})
(schema/=> get-permissioned-balances
[:=>
[:catn
[:cofx :schema.re-frame/cofx]
[:args [:schema [:catn [:community-id :string]]]]]
[:map {:closed true}
[:fx
[:tuple
[:tuple [:= :json-rpc/call] :schema.common/rpc-call]]]]])
(rf/reg-event-fx :communities/get-permissioned-balances get-permissioned-balances)
(defn get-permissioned-balances-success
[{:keys [db]} [community-id response]]
{:db (assoc-in db [:communities/permissioned-balances community-id] response)})
(def ^:private ?account-address keyword?)
(def ^:private ?permissioned-balances-response
[:map-of
?account-address
[:sequential
[:map
[:type int?]
[:symbol string?]
[:decimals int?]
[:amount string?]]]])
(schema/=> get-permissioned-balances-success
[:=>
[:catn
[:cofx :schema.re-frame/cofx]
[:args
[:schema
[:catn
[:community-id string?]
[:response ?permissioned-balances-response]]]]]
map?])
(rf/reg-event-fx :communities/get-permissioned-balances-success get-permissioned-balances-success)
@@ -0,0 +1,15 @@
(ns status-im.contexts.communities.actions.addresses-for-permissions.events-test
(:require
[cljs.test :refer [deftest is]]
[status-im.contexts.communities.actions.addresses-for-permissions.events :as sut]))
(def community-id "0x1")
(deftest get-permissioned-balances-test
(let [cofx {:db {}}]
(is (match? {:fx [[:json-rpc/call
[{:method :wakuext_getCommunityPermissionedBalances
:params [{:communityId community-id}]
:on-success [:communities/get-permissioned-balances-success community-id]
:on-error fn?}]]]}
(sut/get-permissioned-balances cofx [community-id])))))
@@ -8,12 +8,6 @@
:padding-horizontal 20
:padding-vertical 12})
(def error-message
{:flex-direction :row
:gap 4
:justify-content :center
:margin-bottom 8})
(def highest-role
{:flex-direction :row
:gap 4
@@ -3,90 +3,111 @@
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[status-im.common.not-implemented :as not-implemented]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[status-im.contexts.communities.actions.addresses-for-permissions.style :as style]
[status-im.contexts.communities.utils :as communities.utils]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- balances->components-props
[balances]
(for [{:keys [amount decimals type name] :as balance} balances]
(cond-> balance
true
(assoc :type
(condp = type
constants/community-token-type-erc20 :token
constants/community-token-type-erc721 :collectible
:token))
(= type constants/community-token-type-erc721)
(assoc :collectible-name name
:collectible-img-src (resources/get-mock-image :collectible))
(= type constants/community-token-type-erc20)
(assoc :amount (str (money/with-precision (money/token->unit amount decimals) 3))
:token (:symbol balance)))))
(defn- account-item
[item _ _ [selected-addresses community-id]]
[quo/account-permissions
{:account {:name (:name item)
:address (:address item)
:emoji (:emoji item)
:customization-color (:customization-color item)}
:token-details []
:checked? (contains? selected-addresses (:address item))
:on-change #(rf/dispatch [:communities/toggle-selected-permission-address
(:address item) community-id])
:container-style {:margin-bottom 8}}])
[{:keys [customization-color address name emoji]} _ _ [selected-addresses community-id]]
(let [balances (rf/sub [:communities/permissioned-balances-by-address community-id address])]
[quo/account-permissions
{:account {:name name
:address address
:emoji emoji
:customization-color customization-color}
:token-details (balances->components-props balances)
:checked? (contains? selected-addresses address)
:on-change #(rf/dispatch [:communities/toggle-selected-permission-address
address community-id])
:container-style {:margin-bottom 8}}]))
(defn view
[]
(let [{id :community-id} (rf/sub [:get-screen-params])
{:keys [name color images]} (rf/sub [:communities/community id])
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
accounts (rf/sub [:wallet/accounts-with-customization-color])
selected-addresses (rf/sub [:communities/selected-permission-addresses id])
highest-role-text
(i18n/label
(communities.utils/role->translation-key highest-permission-role))]
[rn/safe-area-view {:style style/container}
[quo/drawer-top
{:type :context-tag
:title (i18n/label :t/addresses-for-permissions)
:context-tag-type :community
:community-name name
:button-icon :i/info
:on-button-press not-implemented/alert
:community-logo (get-in images [:thumbnail :uri])
:customization-color color}]
(let [{id :community-id} (rf/sub [:get-screen-params])]
(rf/dispatch [:communities/get-permissioned-balances id])
(fn []
(let [{:keys [name color images]} (rf/sub [:communities/community id])
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
accounts (rf/sub [:wallet/accounts-with-customization-color])
selected-addresses (rf/sub [:communities/selected-permission-addresses id])
highest-role-text (when highest-permission-role
(i18n/label (communities.utils/role->translation-key
highest-permission-role)))]
[rn/safe-area-view {:style style/container}
[quo/drawer-top
{:type :context-tag
:title (i18n/label :t/addresses-for-permissions)
:context-tag-type :community
:community-name name
:button-icon :i/info
:on-button-press not-implemented/alert
:community-logo (get-in images [:thumbnail :uri])
:customization-color color}]
[rn/flat-list
{:render-fn account-item
:render-data [selected-addresses id]
:content-container-style {:padding 20}
:key-fn :address
:data accounts}]
[rn/flat-list
{:render-fn account-item
:render-data [selected-addresses id]
:content-container-style {:padding 20}
:key-fn :address
:data accounts}]
(when (and highest-permission-role (seq selected-addresses))
[rn/view
{:style style/highest-role}
[quo/text
{:size :paragraph-2
:style {:color colors/neutral-50}}
(i18n/label :t/eligible-to-join-as {:role ""})]
[quo/context-tag
{:type :icon
:icon :i/members
:size 24
:context highest-role-text}]])
(if (and highest-permission-role (seq selected-addresses))
[rn/view
{:style style/highest-role}
[quo/text
{:size :paragraph-2
:style {:color colors/neutral-50}}
(i18n/label :t/eligible-to-join-as)]
[quo/context-tag
{:type :icon
:icon :i/members
:size 24
:context highest-role-text}]]
[quo/info-message
{:type :error
:size :default
:icon :i/info
:style {:justify-content :center}}
(if (empty? selected-addresses)
(i18n/label :t/no-addresses-selected)
(i18n/label :t/addresses-dont-contain-tokens-needed))])
(when (empty? selected-addresses)
[rn/view
{:style style/error-message}
[quo/icon
:i/info
{:color colors/danger-50
:size 16}]
[quo/text
{:size :paragraph-2
:style {:color colors/danger-50}}
(i18n/label :t/no-addresses-selected)]])
[rn/view {:style style/buttons}
[quo/button
{:type :grey
:container-style {:flex 1}
:on-press (fn []
(rf/dispatch [:communities/reset-selected-permission-addresses id])
(rf/dispatch [:navigate-back]))}
(i18n/label :t/cancel)]
[quo/button
{:container-style {:flex 1}
:customization-color color
:disabled? (empty? selected-addresses)
:on-press (fn []
(rf/dispatch [:communities/update-previous-permission-addresses id])
[rn/view {:style style/buttons}
[quo/button
{:type :grey
:container-style {:flex 1}
:on-press (fn []
(rf/dispatch [:communities/reset-selected-permission-addresses id])
(rf/dispatch [:navigate-back]))}
(i18n/label :t/confirm-changes)]]]))
(i18n/label :t/cancel)]
[quo/button
{:container-style {:flex 1}
:customization-color color
:disabled? (empty? selected-addresses)
:on-press (fn []
(rf/dispatch [:communities/update-previous-permission-addresses id])
(rf/dispatch [:navigate-back]))}
(i18n/label :t/confirm-changes)]]]))))
@@ -9,6 +9,7 @@
[schema.core :as schema]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.messages.link-preview.events :as link-preview.events]
status-im.contexts.communities.actions.addresses-for-permissions.events
status-im.contexts.communities.actions.community-options.events
status-im.contexts.communities.actions.leave.events
[status-im.navigation.events :as navigation]
@@ -10,16 +10,16 @@
{:db (-> db
(assoc-in [:community-channels-permissions community-id]
(data-store/rpc->channel-permissions (:channels response)))
(assoc-in [:communities community-id :checking-all-channels-permissions?] false))}))
(assoc-in [:communities/channel-permissions-check community-id] false))}))
(rf/reg-event-fx :communities/check-all-community-channels-permissions-failed
(fn [{:keys [db]} [community-id]]
{:db (assoc-in db [:communities community-id :checking-all-channels-permissions?] false)}))
{:db (assoc-in db [:communities/channel-permissions-check community-id] false)}))
(rf/reg-event-fx :communities/check-all-community-channels-permissions
(fn [{:keys [db]} [community-id]]
(when (get-in db [:communities community-id])
{:db (assoc-in db [:communities community-id :checking-all-channels-permissions?] true)
{:db (assoc-in db [:communities/channel-permissions-check community-id] true)
:fx [[:json-rpc/call
[{:method "wakuext_checkAllCommunityChannelsPermissions"
:params [{:CommunityID community-id}]
@@ -38,21 +38,19 @@
(let [token-permissions-check (cond-> result
based-on-client-selection? (assoc :based-on-client-selection? true))]
{:db (-> db
(assoc-in [:communities community-id :checking-permissions?] false)
(assoc-in [:communities community-id :token-permissions-check]
token-permissions-check))})))
(assoc-in [:communities/permissions-check community-id] {:checking? false
:check token-permissions-check})
)})))
(rf/reg-event-fx :communities/check-permissions-to-join-community-failed
(fn [{:keys [db]} [community-id]]
{:db (assoc-in db [:communities community-id :checking-permissions?] false)}))
{:db (assoc-in db [:communities/permissions-check community-id :checking?] false)}))
(rf/reg-event-fx :communities/check-permissions-to-join-community
(fn [{:keys [db]} [community-id addresses based-on-client-selection?]]
(when-let [community (get-in db [:communities community-id])]
(when-not (:checking-permissions? community)
{:db (-> db
(assoc-in [:communities community-id :checking-permissions?] true)
(assoc-in [:communities community-id :can-request-access?] false))
{:db (assoc-in db [:communities/permissions-check community-id :checking?] true)
:json-rpc/call [{:method "wakuext_checkPermissionsToJoinCommunity"
:params [(cond-> {:communityId community-id}
addresses
@@ -110,66 +110,82 @@
[rn/view
[quo/icon :i/info {:no-color true}]]])
(defn- network-not-supported
[]
[quo/text (i18n/label :network-not-supported)])
(defn- request-access-button
[id color]
[quo/button
{:on-press (if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
:accessibility-label :show-request-to-join-screen-button
:customization-color color
:icon-left :i/communities}
(i18n/label :t/request-to-join)])
(defn- token-requirements
[{:keys [id color]}]
[{:keys [id color role-permissions?]}]
(let [{:keys [can-request-access?
number-of-hold-tokens tokens
no-member-permission?
tokens
networks-not-supported?
highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text
(i18n/label
(communities.utils/role->translation-key highest-permission-role :t/member))]
[rn/view {:style (style/token-gated-container)}
[rn/view
{:style {:padding-horizontal 12
:flex-direction :row
:align-items :center
:justify-content :space-between
:flex 1}}
[quo/text {:weight :medium}
(if can-request-access?
(i18n/label :t/you-eligible-to-join-as {:role highest-role-text})
(i18n/label :t/you-not-eligible-to-join))]
[info-button]]
(when (pos? number-of-hold-tokens)
(cond
networks-not-supported?
[network-not-supported]
(or (not role-permissions?) no-member-permission?)
[request-access-button id color]
:else
[rn/view {:style (style/token-gated-container)}
[rn/view
{:style {:padding-horizontal 12
:flex-direction :row
:align-items :center
:justify-content :space-between
:flex 1}}
[quo/text {:weight :medium}
(if (and can-request-access? highest-permission-role)
(i18n/label :t/you-eligible-to-join-as {:role highest-role-text})
(i18n/label :t/you-not-eligible-to-join))]
[info-button]]
[quo/text {:style {:padding-horizontal 12 :padding-bottom 18} :size :paragraph-2}
(if can-request-access?
(i18n/label :t/you-hold-number-of-hold-tokens-of-these
{:number-of-hold-tokens number-of-hold-tokens})
(i18n/label :t/you-must-hold))])
[quo/token-requirement-list
{:tokens tokens
:padding? true}]
[quo/button
{:on-press (if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
:accessibility-label :join-community-button
:customization-color color
:container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12}
:disabled? (not can-request-access?)
:icon-left (if can-request-access? :i/unlocked :i/locked)}
(i18n/label :t/join-open-community)]]))
(i18n/label :t/you-hodl)
(i18n/label :t/you-must-hold))]
[quo/token-requirement-list
{:tokens tokens
:padding? true}]
[quo/button
{:on-press (if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
:accessibility-label :join-community-button
:customization-color color
:container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12}
:disabled? (not can-request-access?)
:icon-left (if can-request-access? :i/unlocked :i/locked)}
(i18n/label :t/join-open-community)]])))
(defn- join-community
[{:keys [id color joined permissions role-permissions? can-join?] :as community}]
[{:keys [id joined permissions role-permissions? can-join?] :as community}]
(let [pending? (rf/sub [:communities/my-pending-request-to-join id])
access-type (get-access-type (:access permissions))
unknown-access? (= access-type :unknown-access)
invite-only? (= access-type :invite-only)]
[:<>
(when-not (or joined pending? invite-only? unknown-access?)
(if role-permissions?
[token-requirements community]
[quo/button
{:on-press (if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
:accessibility-label :show-request-to-join-screen-button
:customization-color color
:icon-left :i/communities}
(i18n/label :t/request-to-join)]))
[token-requirements community])
(when (not (or pending? role-permissions? can-join?))
[quo/text
{:size :paragraph-2
@@ -237,14 +253,16 @@
:description-accessibility-label :community-description}])
(defn- community-content
[{:keys [id]}]
[id]
(rf/dispatch [:communities/check-all-community-channels-permissions id])
(fn [{:keys [name description joined spectated images tags color id membership-permissions?]
:as community}
{:keys [on-category-layout
(fn [id {:keys [on-category-layout
collapsed?
on-first-channel-height-changed]}]
(let [joined-or-spectated (or joined spectated)
on-first-channel-height-changed] :as something}]
(let [{:keys [name description joined spectated images tags color id membership-permissions?]
:as community}
(rf/sub [:communities/community id])
joined-or-spectated (or joined spectated)
chats-by-category (rf/sub [:communities/categorized-channels id])]
[:<>
[rn/view {:style style/community-content-container}
@@ -295,20 +313,24 @@
(and (>= scroll-height (+ height first-channel-height))
category)))))
(defn- community-scroll-page
[{:keys [joined]}]
(let [scroll-height (reagent/atom 0)
categories-heights (reagent/atom {})
first-channel-height (reagent/atom 0)
;; We track the initial value of joined
;; as we open the page to avoid switching
;; from not collapsed to collapsed if the
;; user is on this page
initial-joined? joined]
(fn [{:keys [id name images] :as community}]
(defn- community-scroll-page
[id initial-joined? name images]
(let [scroll-height (reagent/atom 0)
categories-heights (reagent/atom {})
first-channel-height (reagent/atom 0)
on-category-layout (partial add-category-height categories-heights)
on-first-channel-height-changed (fn [height categories]
(swap! categories-heights select-keys categories)
(reset! first-channel-height height))]
(fn [id joined name images]
(let [cover {:uri (get-in images [:banner :uri])}
logo {:uri (get-in images [:thumbnail :uri])}
collapsed? (and initial-joined? (:joined community))
collapsed? (and initial-joined? joined)
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))]
[scroll-page/scroll-page
{:cover-image cover
@@ -331,24 +353,22 @@
@first-channel-height
@categories-heights)}]}
[community-content
community
{:on-category-layout (partial add-category-height categories-heights)
id
{:on-category-layout on-category-layout
:collapsed? collapsed?
:on-first-channel-height-changed
;; Here we set the height of the component and we filter out the categories, as some
;; might have been removed.
(fn [height categories]
(swap! categories-heights select-keys categories)
(reset! first-channel-height height))}]]))))
on-first-channel-height-changed}]]))))
(defn- community-card-page-view
[id]
(let [{:keys [id joined]
(let [{:keys [id joined name images]
:as community} (rf/sub [:communities/community id])]
(when community
(when joined
(rf/dispatch [:activity-center.notifications/dismiss-community-overview id]))
[community-scroll-page community])))
[community-scroll-page id joined name images])))
(defn view
[id]
@@ -0,0 +1,5 @@
(ns status-im.contexts.preview.feature-flags.style)
(def container
{:flex 1
:margin-left 20})
@@ -0,0 +1,39 @@
(ns status-im.contexts.preview.feature-flags.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[re-frame.core :as rf]
[react-native.core :as rn]
[status-im.contexts.preview.feature-flags.style :as style]
[status-im.feature-flags :as ff]))
(defn view
[]
[rn/view {:style {:flex 1}}
[quo/page-nav
{:type :title
:text-align :left
:title "Features Flags"
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])}]
(doall
(for [context-name ff/feature-flags-categories
:let [context-flags (filter (fn [[k]]
(string/includes? (str k) context-name))
(ff/feature-flags))]]
^{:key (str context-name)}
[rn/view {:style style/container}
[quo/text
context-name]
(doall
(for [i (range (count context-flags))
:let [[flag] (nth context-flags i)]]
^{:key (str context-name flag i)}
[rn/view {:style {:flex-direction :row}}
[quo/selectors
{:type :toggle
:checked? (ff/enabled? flag)
:container-style {:margin-right 8}
:on-change #(ff/toggle flag)}]
[quo/text (second (string/split (name flag) "."))]]))]))])
@@ -19,8 +19,10 @@
(def descriptor
[{:type :select
:key :actions
:options [{:key :1-action}
{:key :2-actions}]}
:options [{:key :one-action}
{:key :two-actions}]}
{:key :button-one-label
:type :text}
{:label "Button 1 type"
:type :select
:key :type
@@ -30,6 +32,8 @@
:type :boolean
:key :disabled?
:path [:button-one-props]}
{:key :button-two-label
:type :text}
{:label "Button 2 type"
:type :select
:key :type
@@ -39,32 +43,59 @@
:type :boolean
:key :disabled?
:path [:button-two-props]}
{:key :description
:type :text}
{:key :button-one-label
:type :text}
{:key :button-two-label
:type :text}
{:key :blur?
:type :boolean}
{:key :scroll?
:type :boolean}])
:type :boolean}
{:key :description
:type :select
:options [{:key nil
:value :none}
{:key :top}
{:key :bottom}
{:key :top-error}]}])
(def role-descriptor
{:key :role
:type :select
:options [{:key :owner}
{:key :token-master}
{:key :admin}
{:key :member}]})
(def description-descriptor
{:key :description-text
:type :text})
(def error-descriptor
{:key :error-message
:type :text})
(defn view
[]
(let [state (reagent/atom {:actions :2-actions
:description description
(let [state (reagent/atom {:actions :two-actions
:description :bottom
:description-text description
:error-message "Error message"
:button-one-label button-one
:button-two-label button-two
:button-one-props {:on-press (button-press 1)
:type :primary
:icon-left :i/arrow-up}
:button-one-props {:on-press (button-press 1)
:type :primary}
:button-two-props {:on-press (button-press 2)
:type :grey}
:blur? false
:role :owner
:scroll? false})]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:blur? (:scroll? @state)
:descriptor (merge descriptor
(case (:description @state)
:top role-descriptor
:bottom description-descriptor
:top-error error-descriptor
nil))
:blur? (:blur? @state)
:show-blur-background? true
:blur-dark-only? true
:component-container-style {:margin-top 40
@@ -1,7 +1,6 @@
(ns status-im.contexts.preview.quo.share.share-qr-code
(:require
[quo.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.common.resources :as resources]
[status-im.contexts.preview.quo.preview :as preview]
@@ -15,8 +14,23 @@
{:key :type
:type :select
:options [{:key :profile}
{:key :wallet-legacy}
{:key :wallet-multichain}]}])
{:key :wallet}
{:key :saved-address}
{:key :watched-address}]}])
(def possible-networks [:ethereum :optimism :arbitrum :myNet])
(def networks-selector
{:key :networks
:type :select
:options [{:key (take 1 possible-networks)
:value "Ethereum"}
{:key (take 2 possible-networks)
:value "Ethereum and Optimism"}
{:key (take 3 possible-networks)
:value "Ethereum, Optimism and Arbitrum"}
{:key (take 4 possible-networks)
:value "Ethereum, Optimism, Arbitrum and unknown"}]})
(def profile-descriptor
[{:key :profile-picture
@@ -24,40 +38,44 @@
:options [{:key (resources/get-mock-image :user-picture-female2)
:value "User 1"}
{:key (resources/get-mock-image :user-picture-male4)
:value "User 2"}
{:key nil
:value "No picture"}]}
:value "User 2"}]}
{:key :full-name
:type :text}
(preview/customization-color-option)])
(def wallet-legacy-descriptor
[{:key :emoji
(def wallet-descriptor
[{:key :address
:type :select
:options [{:key :legacy}
{:key :multichain}]}
{:key :emoji
:type :select
:options [{:key "🐈"}
{:key "👻"}
{:key "🐧"}]}
networks-selector
(preview/customization-color-option)])
(def possible-networks [:ethereum :optimism :arbitrum :myNet])
(def saved-address-descriptor
[{:key :address
:type :select
:options [{:key :legacy}
{:key :multichain}]}
networks-selector
(preview/customization-color-option)])
(def wallet-multichain-descriptor
[{:key :emoji
(def watched-address-descriptor
[{:key :address
:type :select
:options [{:key :legacy}
{:key :multichain}]}
{:key :emoji
:type :select
:options [{:key "🐈"}
{:key "👻"}
{:key "🐧"}]}
(preview/customization-color-option)
{:key :networks
:type :select
:options [{:key (take 1 possible-networks)
:value "Ethereum"}
{:key (take 2 possible-networks)
:value "Ethereum and Optimism"}
{:key (take 3 possible-networks)
:value "Ethereum, Optimism and Arbitrum"}
{:key (take 4 possible-networks)
:value "Ethereum, Optimism, Arbitrum and unknown"}]}])
networks-selector
(preview/customization-color-option)])
(defn- get-network-short-name-url
[network]
@@ -84,22 +102,23 @@
(defn view
[]
(let [state (reagent/atom {:type :profile
:address :legacy
:qr-data profile-link
:on-share-press #(js/alert "share pressed")
:on-text-press #(js/alert "text pressed")
:on-text-long-press #(js/alert "text long press")
:profile-picture nil
:profile-picture (resources/get-mock-image :user-picture-female2)
:full-name "My User"
:customization-color :purple
:emoji "🐈"
:on-info-press #(js/alert "Info pressed")
:on-legacy-press #(js/alert (str "Tab " % " pressed"))
:on-multichain-press #(js/alert (str "Tab " % " pressed"))
:networks (take 2 possible-networks)
:on-settings-press #(js/alert "Settings pressed")})
_ (add-watch state :change set-qr-data-based-on-type)]
(fn []
(let [qr-url (if (= (:type @state) :wallet-multichain)
(let [qr-url (if (and (= (:address @state) :multichain)
(not= (:type @state) :profile))
(as-> (:networks @state) $
(map get-network-short-name-url $)
(apply str $)
@@ -112,31 +131,20 @@
:error-level :highest})
typed-descriptor (concat descriptor
(case (:type @state)
:profile profile-descriptor
:wallet-legacy wallet-legacy-descriptor
:wallet-multichain wallet-multichain-descriptor
:profile profile-descriptor
:wallet wallet-descriptor
:saved-address saved-address-descriptor
:watched-address watched-address-descriptor
nil))]
[preview/preview-container
{:state state
:descriptor typed-descriptor
:component-container-style {:padding-horizontal 0}}
[rn/view
{:style {:flex 1
:justify-content :flex-end
:align-items :center
:padding-horizontal 20
:padding-vertical 30}}
[rn/view
{:style {:position :absolute
:top 0
:bottom 0
:left 0
:right 0}}
[rn/image
{:style {:flex 1
:resize-mode :stretch}
:source (resources/get-mock-image :dark-blur-bg)}]]
[quo/share-qr-code
(assoc @state
:qr-image-uri qr-media-server-uri
:qr-data qr-url)]]]))))
:blur? true
:blur-height 500
:component-container-style {:padding-horizontal 0}
:show-blur-background? true
:blur-dark-only? true}
[quo/share-qr-code
(assoc @state
:qr-image-uri qr-media-server-uri
:qr-data qr-url)]]))))
@@ -6,7 +6,7 @@
[status-im.contexts.preview.quo.preview :as preview]))
(def descriptor
[{:type :text
[{:type :number
:key :progress-value}
{:type :select
:key :network
@@ -78,7 +78,7 @@
[]
(let [state (reagent/atom
{:total-box total-box
:progress-value "10"
:progress-value 10
:network :mainnet
:state :pending
:customization-color :blue})]
@@ -19,9 +19,9 @@
:key :epoch-number-arbitrum}
{:type :text
:key :tag-number}
{:type :text
{:type :number
:key :optimism-progress-percentage}
{:type :text
{:type :number
:key :arbitrum-progress-percentage}
{:type :select
:key :network
@@ -142,8 +142,8 @@
:epoch-number-mainnet "181,329"
:epoch-number-optimism "181,329"
:epoch-number-arbitrum "181,329"
:optimism-progress-percentage "10"
:arbitrum-progress-percentage "10"
:optimism-progress-percentage 10
:arbitrum-progress-percentage 10
:network :mainnet
:state-mainnet :pending
:state-arbitrum :pending
@@ -0,0 +1,30 @@
(ns status-im.contexts.profile.edit.accent-colour.events
(:require [taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/reg-event-fx :profile/edit-accent-colour-success
(fn [_ [customization-color]]
{:fx [[:dispatch [:profile/save-local-accent-color customization-color]]
[:dispatch [:navigate-back]]
[:dispatch
[:toasts/upsert
{:type :positive
:theme :dark
:text (i18n/label :t/accent-colour-updated)}]]]}))
(rf/reg-event-fx :profile/save-local-accent-color
(fn [{:keys [db]} [customization-color]]
{:db (assoc-in db [:profile/profile :customization-color] customization-color)}))
(defn edit-accent-colour
[{:keys [db]} [customization-color]]
(let [key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:json-rpc/call
[{:method "wakuext_setCustomizationColor"
:params [{:customizationColor customization-color
:keyUid key-uid}]
:on-success [:profile/edit-accent-colour-success customization-color]
:on-error #(log/error "failed to edit accent color." {:error %})}]]]}))
(rf/reg-event-fx :profile/edit-accent-colour edit-accent-colour)
@@ -0,0 +1,17 @@
(ns status-im.contexts.profile.edit.accent-colour.events-test
(:require [cljs.test :refer [deftest is]]
matcher-combinators.test
[status-im.contexts.profile.edit.accent-colour.events :as sut]))
(deftest edit-accent-color-test
(let [new-color :yellow
key-uid "key-uid"
cofx {:db {:profile/profile {:key-uid key-uid}}}
expected {:fx [[:json-rpc/call
[{:method "wakuext_setCustomizationColor"
:params [{:customizationColor new-color
:keyUid key-uid}]
:on-success [:profile/edit-accent-colour-success new-color]
:on-error fn?}]]]}]
(is (match? expected
(sut/edit-accent-colour cofx [new-color])))))
@@ -0,0 +1,30 @@
(ns status-im.contexts.profile.edit.accent-colour.style
(:require [quo.foundations.colors :as colors]))
(defn page-wrapper
[insets]
{:padding-top (:top insets)
:padding-bottom (:bottom insets)
:padding-horizontal 1
:flex 1})
(def screen-container
{:flex 1
:padding-top 14
:justify-content :space-between})
(def padding-horizontal
{:padding-horizontal 20})
(def color-title
{:color colors/white-70-blur
:margin-top 20
:margin-bottom 16})
(def button-wrapper
{:margin-vertical 12
:padding-horizontal 20})
(def profile-card
{:margin-top 22
:margin-bottom 5})
@@ -0,0 +1,66 @@
(ns status-im.contexts.profile.edit.accent-colour.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.constants :as constants]
[status-im.contexts.profile.edit.accent-colour.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn f-view
[{:keys [customization-color]}]
(let [insets (safe-area/get-insets)
{window-width :width} (rn/get-window)
unsaved-custom-color (reagent/atom customization-color constants/profile-default-color)
on-change #(reset! unsaved-custom-color %)]
(fn [{:keys [customization-color]}]
(let [profile (rf/sub [:profile/profile-with-image])
profile-picture (profile.utils/photo profile)
display-name (profile.utils/displayed-name profile)]
(rn/use-effect
#(on-change customization-color)
[customization-color])
[quo/overlay
{:type :shell
:container-style (style/page-wrapper insets)}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])}]
[rn/view
{:key :content
:style style/screen-container}
[rn/view {:style {:flex 1}}
[rn/view {:style style/padding-horizontal}
[quo/text-combinations {:title (i18n/label :t/accent-colour)}]
[quo/profile-card
{:profile-picture profile-picture
:name display-name
:card-style style/profile-card
:customization-color @unsaved-custom-color}]
[quo/text
{:size :paragraph-2
:weight :medium
:style style/color-title}
(i18n/label :t/accent-colour)]]
[quo/color-picker
{:blur? true
:default-selected @unsaved-custom-color
:on-change on-change
:container-style style/padding-horizontal
:window-width window-width}]]
[rn/view {:style style/button-wrapper}
[quo/button
{:type :primary
:customization-color @unsaved-custom-color
:on-press (fn []
(rf/dispatch [:profile/edit-accent-colour @unsaved-custom-color]))}
(i18n/label :t/save-colour)]]]]))))
(defn view
[]
(let [customization-color (rf/sub [:profile/customization-color])]
[:f> f-view {:customization-color customization-color}]))
@@ -25,7 +25,7 @@
:action :arrow
:container-style style/item-container}
{:title (i18n/label :t/accent-colour)
:on-press not-implemented/alert
:on-press #(rf/dispatch [:open-modal :edit-accent-colour])
:label :color
:label-props (colors/resolve-color customization-color theme)
:blur? true
@@ -3,6 +3,7 @@
[legacy.status-im.data-store.settings :as data-store.settings]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.contexts.profile.edit.accent-colour.events]
[status-im.contexts.profile.edit.header.events]
[status-im.contexts.profile.edit.name.events]
[status-im.contexts.profile.login.events :as profile.login]
@@ -96,6 +96,13 @@
:action :arrow
:image :icon
:blur? true
:image-props :i/light})
(when config/quo-preview-enabled?
{:title "Feature Flags"
:on-press #(rf/dispatch [:navigate-to :feature-flags])
:action :arrow
:image :icon
:blur? true
:image-props :i/light})]
[{:title (i18n/label :t/about)
:on-press not-implemented/alert
@@ -149,7 +149,9 @@
(not hidden-screen?)
(:current-chat-id db))
(conj [:chat/close]))})
{:db (assoc db :view-id go-to-view-id)
{:db (-> db
(assoc :view-id go-to-view-id)
(dissoc :modal-view-ids))
:navigate-to go-to-view-id}))
(rf/defn shell-navigate-back
@@ -174,7 +176,8 @@
shell.constants/close-screen-with-slide-to-right-animation))}
(when (and current-chat-id community-id)
{:dispatch [:shell/add-switcher-card shell.constants/community-screen community-id]}))
{:navigate-back nil})))
{:navigate-back nil
:db (dissoc db :modal-view-ids)})))
(rf/defn floating-screen-opened
{:events [:shell/floating-screen-opened]}
@@ -48,7 +48,7 @@
[props]
(let [{:keys [account width index]} props
selected-networks (reagent/atom [:ethereum :optimism :arbitrum])
wallet-type (reagent/atom :wallet-legacy)]
wallet-type (reagent/atom :legacy)]
(fn []
(let [share-title (str (:name account) " " (i18n/label :t/address))
qr-url (utils/get-wallet-qr {:wallet-type @wallet-type
@@ -62,7 +62,8 @@
[rn/view {:style {:height qr-size :width width :margin-left (if (zero? index) 0 -30)}}
[rn/view {:style style/qr-code-container}
[quo/share-qr-code
{:type @wallet-type
{:type :wallet
:address @wallet-type
:qr-image-uri qr-media-server-uri
:qr-data qr-url
:networks @selected-networks
@@ -71,8 +72,8 @@
:full-name (:name account)
:customization-color (:color account)
:emoji (:emoji account)
:on-multichain-press #(reset! wallet-type :wallet-multichain)
:on-legacy-press #(reset! wallet-type :wallet-legacy)
:on-multichain-press #(reset! wallet-type :multichain)
:on-legacy-press #(reset! wallet-type :legacy)
:on-settings-press #(open-preferences @selected-networks)}]]]))))
(def wallet-qr-code-item (memoize wallet-qr-code-item-internal))
@@ -1,42 +1,34 @@
(ns status-im.contexts.wallet.account.bridge.view
(:require
[quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.wallet.account.bridge.style :as style]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.temp :as temp]
[status-im.contexts.wallet.common.asset-list.view :as asset-list]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn network-logo
[item]
{:source (quo.resources/get-network (:network-name item))})
(defn- bridge-token-component
[]
(fn [token]
(let [on-press #(rf/dispatch [:wallet/select-bridge-to
{:token token
:stack-id :wallet-bridge}])]
[quo/token-network (assoc token :on-press on-press)])))
(defn view
[]
(let [networks (rf/sub [:wallet/network-details])
networks-logos (map network-logo networks)]
[rn/view {:style {:flex 1}}
[account-switcher/view
{:on-press #(rf/dispatch [:navigate-back])
:accessibility-label :top-bar}]
[quo/text-combinations
{:container-style style/header-container
:title (i18n/label :t/bridge)}]
[quo/input
{:container-style style/input-container
:icon-name :i/search
:placeholder (i18n/label :t/search-assets)}]
[rn/flat-list
{:data (temp/bridge-token-list networks-logos)
:render-fn bridge-token-component
:content-container-style style/list-content-container}]]))
(let [search-text (reagent/atom "")]
(fn []
[rn/view {:style {:flex 1}}
[account-switcher/view
{:on-press #(rf/dispatch [:navigate-back])
:accessibility-label :top-bar}]
[quo/text-combinations
{:container-style style/header-container
:title (i18n/label :t/bridge)}]
[quo/input
{:container-style style/input-container
:icon-name :i/search
:on-change-text #(reset! search-text %)
:placeholder (i18n/label :t/search-assets)}]
[asset-list/view
{:search-text @search-text
:on-token-press (fn [token]
(rf/dispatch [:wallet/bridge-select-token
{:token token
:stack-id :wallet-bridge}]))}]])))
@@ -3,11 +3,11 @@
[quo.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.config :as config]
[status-im.contexts.wallet.account.style :as style]
[status-im.contexts.wallet.account.tabs.view :as tabs]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.temp :as temp]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -53,9 +53,8 @@
:receive-action #(rf/dispatch [:open-modal :wallet-share-address {:status :receive}])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-drawer}])
:bridge-action (if (:bridge-token config/wallet-feature-flags)
#(rf/dispatch [:open-modal :wallet-bridge])
#(js/alert "feature disabled in config file"))}])
:bridge-action #(ff/alert ::ff/wallet.bridge-token
(fn [] (rf/dispatch [:open-modal :wallet-bridge])))}])
[quo/tabs
{:style style/tabs
:size 32
@@ -0,0 +1,37 @@
(ns status-im.contexts.wallet.common.asset-list.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.common.utils :as utils]
[utils.re-frame :as rf]))
(defn- asset-component
[token _ _ {:keys [currency currency-symbol on-token-press]}]
(let [token-units (utils/total-token-units-in-all-chains token)
crypto-formatted (utils/get-standard-crypto-format token token-units)
fiat-value (utils/total-token-fiat-value currency token)
fiat-formatted (utils/get-standard-fiat-format crypto-formatted currency-symbol fiat-value)]
[quo/token-network
{:token (:symbol token)
:label (:name token)
:token-value (str crypto-formatted " " (:symbol token))
:fiat-value fiat-formatted
:networks (:networks token)
:on-press #(on-token-press token)}]))
(defn view
[{:keys [search-text on-token-press]}]
(let [filtered-tokens (rf/sub [:wallet/tokens-filtered search-text])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])]
[rn/flat-list
{:data filtered-tokens
:render-data {:currency currency
:currency-symbol currency-symbol
:on-token-press on-token-press}
:style {:flex 1}
:content-container-style {:padding-horizontal 8}
:keyboard-should-persist-taps :handled
:key-fn :symbol
:on-scroll-to-index-failed identity
:render-fn asset-component}]))
@@ -29,7 +29,7 @@
{:customization-color account-color
:container-style (style/gradient-cover-container margin-top)}]
(into
[rn/scroll-view {:bounces false}
[:<>
[rn/view {:style style/account-avatar-container}
[quo/account-avatar
{:customization-color account-color
@@ -9,10 +9,10 @@
[react-native.gesture :as gesture]
[react-native.platform :as platform]
[reagent.core :as reagent]
[status-im.config :as config]
[status-im.contexts.wallet.common.sheets.account-options.style :as style]
[status-im.contexts.wallet.common.sheets.remove-account.view :as remove-account]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -100,13 +100,12 @@
:accessibility-label :remove-account
:label (i18n/label :t/remove-account)
:danger? true
:on-press (fn []
(if (:remove-account config/wallet-feature-flags)
(rf/dispatch [:show-bottom-sheet
{:content
(fn []
[remove-account/view])}])
(js/alert "Feature disabled in config file")))})]]]
:on-press #(ff/alert ::ff/wallet.remove-account
(fn []
(rf/dispatch [:show-bottom-sheet
{:content
(fn []
[remove-account/view])}])))})]]]
(when show-account-selector?
[:<>
[quo/divider-line {:container-style style/divider-label}]
@@ -110,7 +110,8 @@
network))}))
layer-2-networks)}]
[quo/bottom-actions
{:button-one-label (i18n/label :t/update)
{:actions :one-action
:button-one-label (i18n/label :t/update)
:button-one-props {:disabled? (= @state :default)
:on-press (fn []
(let [chain-ids (map :chain-id current-networks)]
@@ -74,3 +74,6 @@
(def secret-phrase
["witch" "collapse" "practice" "feed" "shame" "open" "lion"
"collapse" "umbrella" "fabric" "sadness" "obligue"])
(def random-words
["cousin" "roof" "minute" "swallow" "wing" "motion" "stomach"
"abuse" "banner" "noble" "poet" "wrist"])
+27 -13
View File
@@ -182,7 +182,7 @@
(defn get-wallet-qr
[{:keys [wallet-type selected-networks address]}]
(if (= wallet-type :wallet-multichain)
(if (= wallet-type :multichain)
(as-> selected-networks $
(map qr-codes/get-network-short-name-url $)
(apply str $)
@@ -218,32 +218,46 @@
(defn get-standard-fiat-format
[crypto-value currency-symbol fiat-value]
(if (string/includes? crypto-value "<")
"<$0.01"
(str "<" currency-symbol "0.01")
(prettify-balance currency-symbol fiat-value)))
(defn prettify-percentage-change
"Returns unsigned precentage"
[percentage]
(-> (if (number? percentage) percentage 0)
money/bignumber
money/absolute-value
(money/to-fixed 2)))
(defn calculate-token-value
"This function returns token values in the props of token-value (quo) component"
[{:keys [token color currency currency-symbol]}]
(let [token-units (total-token-units-in-all-chains token)
fiat-value (total-token-fiat-value currency token)
market-values (or (get-in token [:market-values-per-currency currency])
(get-in token
[:market-values-per-currency
constants/profile-default-currency]))
{:keys [change-pct-24hour]} market-values
crypto-value (get-standard-crypto-format token token-units)
fiat-value (get-standard-fiat-format crypto-value currency-symbol fiat-value)]
(let [token-units (total-token-units-in-all-chains token)
fiat-value (total-token-fiat-value currency token)
market-values (or (get-in token [:market-values-per-currency currency])
(get-in token
[:market-values-per-currency
constants/profile-default-currency]))
{:keys [price change-pct-24hour]} market-values
formatted-token-price (prettify-balance currency-symbol price)
percentage-change (prettify-percentage-change change-pct-24hour)
crypto-value (get-standard-crypto-format token token-units)
fiat-value (get-standard-fiat-format crypto-value
currency-symbol
fiat-value)]
{:token (:symbol token)
:token-name (:name token)
:state :default
:metrics? true
:status (cond
(pos? change-pct-24hour) :positive
(neg? change-pct-24hour) :negative
:else :empty)
:customization-color color
:values {:crypto-value crypto-value
:fiat-value fiat-value}}))
:values {:crypto-value crypto-value
:fiat-value fiat-value
:fiat-change formatted-token-price
:percentage-change percentage-change}}))
(defn get-multichain-address
[networks address]
@@ -0,0 +1,18 @@
(ns status-im.contexts.wallet.common.utils.send
(:require [utils.money :as money]))
(defn calculate-gas-fee
[data]
(let [gas-amount (money/bignumber (get data :gas-amount))
gas-fees (get data :gas-fees)
eip1559-enabled? (get gas-fees :eip1559-enabled)
billion (money/bignumber "1000000000")]
(if eip1559-enabled?
(let [base-fee (money/bignumber (get gas-fees :base-fee))
priority-fee (money/bignumber (get gas-fees :max-priority-fee-per-gas))
fee-with-tip (money/bignumber (money/add base-fee priority-fee))
total-gas-fee (money/mul gas-amount fee-with-tip)]
(money/with-precision (money/div total-gas-fee billion) 10))
(let [gas-price (money/bignumber (get gas-fees :gas-price))
total-gas-fee (money/mul gas-amount gas-price)]
(money/with-precision (money/div total-gas-fee billion) 10)))))
@@ -0,0 +1,22 @@
(ns status-im.contexts.wallet.common.utils.send-test
(:require [cljs.test :refer [deftest is testing]]
[status-im.contexts.wallet.common.utils.send :as utils]
[utils.money :as money]))
(deftest test-calculate-gas-fee
(testing "Test calculate-gas-fee function with EIP-1559 enabled"
(let [data-eip1559-enabled {:gas-amount "23487"
:gas-fees {:base-fee "32.325296406"
:max-priority-fee-per-gas "0.011000001"
:eip1559-enabled true}}
expected-eip1559-enabled-result (money/bignumber 0.0007594826)]
(is (money/equal-to (utils/calculate-gas-fee data-eip1559-enabled)
expected-eip1559-enabled-result)))
(testing "Test calculate-gas-fee function with EIP-1559 disabled"
(let [data-eip1559-disabled {:gas-amount "23487"
:gas-fees {:gas-price "32.375609968"
:eip1559-enabled false}}
expected-eip1559-disabled-result (money/bignumber 0.000760406)]
(is (money/equal-to (utils/calculate-gas-fee data-eip1559-disabled)
expected-eip1559-disabled-result))))))
@@ -94,10 +94,10 @@
(deftest test-get-wallet-qr
(testing "Test get-wallet-qr function"
(let [wallet-multichain {:wallet-type :wallet-multichain
(let [wallet-multichain {:wallet-type :multichain
:selected-networks [:ethereum :optimism]
:address "x000"}
wallet-singlechain {:wallet-type :wallet-singlechain
wallet-singlechain {:wallet-type :singlechain
:selected-networks [:ethereum :optimism]
:address "x000"}]
@@ -106,3 +106,12 @@
(is (= (utils/get-wallet-qr wallet-singlechain)
"x000")))))
(deftest test-prettify-percentage-change
(testing "prettify-percentage-change function"
(is (= (utils/prettify-percentage-change nil) "0.00"))
(is (= (utils/prettify-percentage-change "") "0.00"))
(is (= (utils/prettify-percentage-change 0.5) "0.50"))
(is (= (utils/prettify-percentage-change 1.113454) "1.11"))
(is (= (utils/prettify-percentage-change -0.35) "0.35"))
(is (= (utils/prettify-percentage-change -0.78234) "0.78"))))
@@ -3,9 +3,13 @@
[status-im.contexts.wallet.create-account.edit-derivation-path.view :as edit-derivation-path]
[test-helpers.component :as h]))
(defn- render
[component]
(h/render-with-theme-provider component :light))
(h/describe "Edit derivation path page"
(h/test "Default render"
(h/render [edit-derivation-path/view {}])
(render [edit-derivation-path/view {}])
(h/is-truthy (h/get-by-translation-text :t/edit-derivation-path))
(h/is-truthy (h/get-by-translation-text :t/path-format))
(h/is-truthy (h/get-by-translation-text :t/derivation-path))
@@ -15,14 +19,14 @@
(h/test "Reveal address pressed"
(let [on-reveal (h/mock-fn)]
(h/render [edit-derivation-path/view {:on-reveal on-reveal}])
(render [edit-derivation-path/view {:on-reveal on-reveal}])
(h/fire-event :press (h/get-by-translation-text :t/reveal-address))
(h/was-called on-reveal)
(h/wait-for #(h/is-truthy (h/get-by-translation-text :t/address-activity)))))
(h/test "Reset button pressed"
(let [on-reset (h/mock-fn)]
(h/render [edit-derivation-path/view {:on-reset on-reset}])
(render [edit-derivation-path/view {:on-reset on-reset}])
(h/fire-event :press (h/get-by-translation-text :t/reset))
(h/was-called on-reset)
(h/wait-for #(h/is-truthy (h/get-by-translation-text :t/derive-addresses))))))
@@ -72,7 +72,7 @@
:default
[quo/bottom-actions
{:theme theme
:actions :1-action
:actions :one-action
:button-one-label (i18n/label :t/reveal-address)
:button-one-props {:type :outline
:icon-left :i/keycard-card
@@ -81,7 +81,7 @@
:empty
[quo/bottom-actions
{:theme theme
:actions :1-action
:actions :one-action
:button-one-label (i18n/label :t/derive-addresses)
:button-one-props {:type :outline
:icon-left :i/keycard-card
@@ -111,7 +111,7 @@
[rn/view {:style (style/save-button-container bottom)}
[quo/bottom-actions
{:theme theme
:actions :1-action
:actions :one-action
:button-one-label (i18n/label :t/save)
:button-one-props {:type :primary
:on-press #(js/alert "Save!")
@@ -1,4 +1,4 @@
(ns status-im.contexts.wallet.create-account.backup-recovery-phrase.style
(ns status-im.contexts.wallet.create-account.new-keypair.backup-recovery-phrase.style
(:require
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]

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