Compare commits

...
Author SHA1 Message Date
Ulises M a0a3a97596 WIP: first attempt 2023-11-21 17:30:40 -06:00
Rende11 664048468b Send page > input amount #16926
Numbered keyboard sane defaults

Top bar

Use input

Progress

Update token-input

Update keyboard

Base specs

Backspace icon

Revert "Backspace icon"

This reverts commit e591c5297ae149eb3800df853b956d3154fe206c.

Update tests

Clean up

Fix formatting

Fix styles

Add autofocus

Add icons and update tests

Add comment

Add on-swap

Current limit state

Update tests

Update column key

Improve input interaction

Extract make-new-input
2023-11-21 16:06:25 +03:00
Omar Basem 73e9f67dbd Wallet: illustrations (#17931)
* wallet: illustrations
2023-11-21 10:55:46 +04:00
Jamie Caprani 86c5505c94 chore(wallet): hook up qr scanner on watch only flow. (#17829) 2023-11-19 16:26:00 -08:00
Icaro Motta c1dcd7a764 Introduce malli library (#17867)
This commit is the foundational step to start using malli
(https://github.com/metosin/malli) in this project.

Take in consideration we will only be able to realize malli's full power in
future iterations.

For those without context: the mobile team watched a presentation about malli
and went through a light RFC to put everyone on the same page, among other
discussions here and there in PRs.

To keep things relatively short:

1. Unit, integration and component tests will short-circuit (fail) when
   inputs/outputs don't conform to their respective function schemas (CI should
   fail too).
2. Failed schema checks will not block the app from initializing, nor throw an
   exception that would trigger the LogBox. Exceptions are only thrown in the
   scope of automated tests.
3. There's zero performance impact in production code because we only
   instrument. Instrumentation is removed from the compiled code due to the
   usage of "^boolean js.goog/DEBUG".
4. We shouldn't expect any meaningful slowdown during development.

**What are we instrumenting in this PR?**

Per our team's agreement, we're only instrumenting the bare minimum to showcase 2 examples.

- Instrument a utility function utils.money/format-amount using the macro
  approach.
- Instrument a quo component quo.components.counter.step.view/view using the
  functional approach.

Both approaches are useful, the functional approach is powerful and allow us to
instrument anonymous functions, like the ones we pass to subscriptions or event
handlers, or the higher-order function quo.theme/with-theme. The macro approach
is perfect for functions already defined with defn.

**I evaluated the schema or function in the REPL but nothing changes**

- If you evaluate the source function, you need to evaluate schema/=> or
  schema/instrument as well.
- Remember to *var quote* when using schema/instrument.
- You must call "(status-im2.setup.schema/setup!)" after any var is
  re-instrumented. It's advisable to add a keybinding in your editor to send
  this expression automatically to the CLJS REPL, or add the call at the end of
  the namespace you are working on (similar to how some devs add "(run-tests)"
  at the end of test namespaces).

**Where should schemas be defined?**

For the moment, we should focus on instrumenting quo components, so define each
function schema in the same namespace as the component's public "view" var.

To be specific:

- A schema used only to instrument a single function and not used elsewhere,
  like a quo component schema, wouldn't benefit from being defined in a separate
  namespace because that would force the developer to constantly open two files
  instead of one to check function signatures.
- A common schema reused across the repo, like ":schema.common/theme" should be
  registered in the global registry "schema.registry" so that consumers can just
  refer to it by keyword, as if it was a built-in malli schema.
- A common schema describing status-go entities like message, notification,
  community, etc can be stored either in the respective
  "src/status_im2/contexts/*" or registered globally, or even somewhere else.
  This is yet to be defined, but since I chose not to include schemas for them,
  we can postpone this guideline.
2023-11-18 11:04:48 -03:00
Icaro Motta 17ebedd6b8 Fix: resolve our Clojure source dependencies first in the classpath (#17919)
In PR https://github.com/status-im/status-mobile/pull/17867 we have a namespace
named schema.core, but this namespace is taken by library prismatic/schema
already (see https://github.com/plumatic/schema/tree/master/src/cljc/schema), a
library used by our direct dependency on bidi 2.1.6. This leads to a broken
build where the ClojureScript compiler reports undeclared vars
(https://clojurescript.org/reference/compiler-options#warnings).

We change the order Java resolves dependencies via the classpath mechanism. We
now first resolve our own Clojure sources, and then project dependencies.
2023-11-18 08:50:11 -03:00
Jamie CapraniandUlises M 2e2d15adfb chore: add floating button page component (#17737)
Co-authored-by: Ulises M <ulises.ssb@hotmail.com>
2023-11-17 09:16:55 -08:00
Yevheniia Berdnyk 1d1d586223 e2e: added back status link opening 2023-11-17 18:09:57 +02:00
John Ngei 88dd5e04bb enable biometric when user signs in with syncing (#17627) 2023-11-17 17:58:51 +03:00
112 changed files with 1725 additions and 343 deletions
+12 -4
View File
@@ -16,10 +16,18 @@
:clj-kondo-config {:level :error}
:cond-else {:level :error}
:consistent-alias {:level :error
:aliases {clojure.string string
clojure.set set
clojure.walk walk
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
taoensso.timbre log}}
:deprecated-namespace {:level :warning}
:docstring-blank {:level :error}
:equals-true {:level :error}
+2
View File
@@ -0,0 +1,2 @@
{:lint-as {malli.experimental/defn schema.core/defn}
:linters {:unresolved-symbol {:exclude [(malli.core/=>)]}}}
+1
View File
@@ -36,6 +36,7 @@
"deftest-sub" :arg1-body
"wait-for" :arg1-body
"with-deps-check" :arg1-body
"schema/=>" :arg1-body
"->" [:noarg1-body
{:list {:constant-pair? false :force-nl? false}
:next-inner-restore [[:list :constant-pair?]]}]
+48 -12
View File
@@ -45,11 +45,20 @@
},
{
"path": "borkdude/edamame/1.1.17/edamame-1.1.17",
"path": "borkdude/dynaload/0.3.5/dynaload-0.3.5",
"host": "https://repo.clojars.org",
"jar": {
"sha1": "9087f7abf0104e0354d7db7fc4576608eac558f4",
"sha256": "1n1872i240lakn4pzsag4grf7bv7lcsipmqllxd9m4k1zp3dgla1"
"sha1": "accd696ba364b850b4d92e38f5a34d0e828a0ad1",
"sha256": "0k62m1f29xfh3cp67w7kcvkp5aj35simi8kf95ycvkmgp76w11q8"
}
},
{
"path": "borkdude/edamame/1.3.23/edamame-1.3.23",
"host": "https://repo.clojars.org",
"jar": {
"sha1": "254d023e97ed438f0f44532b5a06d928d031ede4",
"sha256": "0l7mxza2nimslhg0qh6jp7gmb8sd6l89fk5d1zvzq2xskscq2vly"
}
},
@@ -81,20 +90,20 @@
},
{
"path": "cider/cider-nrepl/0.29.0/cider-nrepl-0.29.0",
"path": "cider/cider-nrepl/0.25.3/cider-nrepl-0.25.3",
"host": "https://repo.clojars.org",
"jar": {
"sha1": "45f6034b26a14138e74145b7a4059628c0fedcd1",
"sha256": "1dy1l6y8cb8xiqq97a4lf8giyiicq4wfl4s2lxn5fb6614cjxqx2"
"sha1": "5ae0efd9377a5e60c084bdaf4a2ce094f759ce23",
"sha256": "0drxf9nm23i1pcgrkwbcr09msq37csilzww38709add0hz8spjhq"
}
},
{
"path": "cider/piggieback/0.5.2/piggieback-0.5.2",
"path": "cider/piggieback/0.4.1/piggieback-0.4.1",
"host": "https://repo.clojars.org",
"jar": {
"sha1": "ecfd5c286a85db3f059e75c37fca5722d9e26f79",
"sha256": "1ps9yf3cxmlm447hqkidjb5xry90n0wl3jk0jn28fagq31lzylkl"
"sha1": "0a02a3e2ecd7a126ab60d8a44793342f20ced79b",
"sha256": "142vl5np33akcrnn6pksi0rjfsmmi528villxsj6cwcndvybiw4m"
}
},
@@ -539,6 +548,24 @@
}
},
{
"path": "metosin/malli/0.13.0/malli-0.13.0",
"host": "https://repo.clojars.org",
"jar": {
"sha1": "286c52d26a3a9d613b5f692971bd19c581cdc4b0",
"sha256": "0vaq9d6cln5j4ww302fwlgg7m647cf2j16gs2i1p1d4klbn4jddz"
}
},
{
"path": "mvxcvi/arrangement/2.1.0/arrangement-2.1.0",
"host": "https://repo.clojars.org",
"jar": {
"sha1": "1bc8f3bba7a28de85f319b7d464aa8d955f44918",
"sha256": "1j1rkwrs4wm8zk9v7ilgpnyav5sipdd2bmb83sazh392blw52wpk"
}
},
{
"path": "net/cgrand/macrovich/0.2.1/macrovich-0.2.1",
"host": "https://repo.clojars.org",
@@ -728,6 +755,15 @@
}
},
{
"path": "org/clojure/test.check/1.1.1/test.check-1.1.1",
"host": "https://repo1.maven.org/maven2",
"jar": {
"sha1": "f33d988fd57bc9c11af1952db81c10f319c91416",
"sha256": "0y2hpkj7zl4yrpsl35ifpdaja5c72b8fpjcnmdgmld9c7cb1hlcl"
}
},
{
"path": "org/clojure/tools.analyzer/1.1.0/tools.analyzer-1.1.0",
"host": "https://repo1.maven.org/maven2",
@@ -909,11 +945,11 @@
},
{
"path": "refactor-nrepl/refactor-nrepl/3.6.0/refactor-nrepl-3.6.0",
"path": "refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0",
"host": "https://repo.clojars.org",
"jar": {
"sha1": "2b3bb82da53b5db9c2b2aa298417816b81d0ed97",
"sha256": "1ysqabmlnghki6x0636zngxza2d83c85276wp9ma9wk183mkv52a"
"sha1": "6bc3441afc94f7ca024e41a864ca75e05df7e207",
"sha256": "0w8hax99y98l53mixxzx2ja0vcnhjv8dnsaz1zj3vqk775ns5w6i"
}
},
+8 -4
View File
@@ -3,12 +3,13 @@ babashka/fs/0.2.16/fs-0.2.16.jar
bidi/bidi/2.1.6/bidi-2.1.6.jar
binaryage/env-config/0.2.2/env-config-0.2.2.jar
binaryage/oops/0.7.2/oops-0.7.2.jar
borkdude/edamame/1.1.17/edamame-1.1.17.jar
borkdude/dynaload/0.3.5/dynaload-0.3.5.jar
borkdude/edamame/1.3.23/edamame-1.3.23.jar
borkdude/sci.impl.reflector/0.0.1/sci.impl.reflector-0.0.1.jar
camel-snake-kebab/camel-snake-kebab/0.4.3/camel-snake-kebab-0.4.3.jar
cheshire/cheshire/5.11.0/cheshire-5.11.0.jar
cider/cider-nrepl/0.29.0/cider-nrepl-0.29.0.jar
cider/piggieback/0.5.2/piggieback-0.5.2.jar
cider/cider-nrepl/0.25.3/cider-nrepl-0.25.3.jar
cider/piggieback/0.4.1/piggieback-0.4.1.jar
clj-kondo/clj-kondo/2023.09.07/clj-kondo-2023.09.07.jar
cljs-bean/cljs-bean/1.9.0/cljs-bean-1.9.0.jar
clout/clout/2.1.2/clout-2.1.2.jar
@@ -58,6 +59,8 @@ javax/annotation/jsr250-api/1.0/jsr250-api-1.0.jar
javax/servlet/servlet-api/2.5/servlet-api-2.5.jar
javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar
medley/medley/0.8.2/medley-0.8.2.jar
metosin/malli/0.13.0/malli-0.13.0.jar
mvxcvi/arrangement/2.1.0/arrangement-2.1.0.jar
net/cgrand/macrovich/0.2.1/macrovich-0.2.1.jar
net/java/dev/jna/jna/5.12.1/jna-5.12.1.jar
nrepl/bencode/1.1.0/bencode-1.1.0.jar
@@ -79,6 +82,7 @@ org/clojure/data.priority-map/1.1.0/data.priority-map-1.1.0.jar
org/clojure/google-closure-library/0.0-20230227-c7c0a541/google-closure-library-0.0-20230227-c7c0a541.jar
org/clojure/google-closure-library-third-party/0.0-20230227-c7c0a541/google-closure-library-third-party-0.0-20230227-c7c0a541.jar
org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.jar
org/clojure/test.check/1.1.1/test.check-1.1.1.jar
org/clojure/tools.analyzer/1.1.0/tools.analyzer-1.1.0.jar
org/clojure/tools.analyzer.jvm/1.2.2/tools.analyzer.jvm-1.2.2.jar
org/clojure/tools.cli/1.0.206/tools.cli-1.0.206.jar
@@ -99,7 +103,7 @@ org/wildfly/common/wildfly-common/1.5.2.Final/wildfly-common-1.5.2.Final.jar
prismatic/schema/1.1.7/schema-1.1.7.jar
reagent/reagent/1.2.0/reagent-1.2.0.jar
re-com/re-com/2.8.0/re-com-2.8.0.jar
refactor-nrepl/refactor-nrepl/3.6.0/refactor-nrepl-3.6.0.jar
refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0.jar
re-frame/re-frame/1.3.0/re-frame-1.3.0.jar
re-frisk-remote/re-frisk-remote/1.6.0/re-frisk-remote-1.6.0.jar
re-frisk/sente/1.15.0/sente-1.15.0.jar
+6 -3
View File
@@ -67,9 +67,12 @@ stdenv.mkDerivation {
'';
buildPhase = ''
# Assemble CLASSPATH from available clojure dependencies.
# We append 'src' so it can find the local sources.
export CLASS_PATH="$(find ${deps.clojure} \
-iname '*.jar' | tr '\n' ':')src"
# We prepend 'src' so it can find the local sources and prioritize
# our own namespaces over dependencies, given that indirect dependencies
# can also cause naming conflicts (e.g. prismatic/schema already uses
# namespace schema.core).
export CLASS_PATH="src:$(find ${deps.clojure} \
-iname '*.jar' | tr '\n' ':')"
# target must be one of the builds defined in shadow-cljs.edn
java -cp "$CLASS_PATH" clojure.main \
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+18 -2
View File
@@ -9,6 +9,7 @@
[cljs-bean "1.9.0"]
[com.cognitect/transit-cljs "0.8.280"]
[camel-snake-kebab "0.4.3"]
[metosin/malli "0.13.0"]
;; Dev dependencies
[refactor-nrepl "2.5.0"]
@@ -50,6 +51,7 @@
:after-load-async status-im2.setup.hot-reload/reload
:build-notify status-im2.setup.hot-reload/build-notify
:preloads [re-frisk-remote.preload
status-im2.setup.schema-preload
;; In order to use component test helpers in the REPL we
;; need to preload namespaces that are not normally required
;; by production code, such as
@@ -71,6 +73,10 @@
:warnings {:fn-deprecated false}
:closure-defines {re-frame.trace/trace-enabled? true}
:source-map false
;; This seems to be necessary while using the REPL,
;; otherwise sometimes you'll get weird errors when
;; instrumenting functions.
:static-fns false
:infer-externs true}
;; if you want to use a real device, set your local ip
;; in the SHADOW_HOST env variable to make sure that
@@ -104,13 +110,15 @@
:output-dir "target/test"
:optimizations :simple
:target :node-test
:dev {:devtools {:preloads [status-im2.setup.schema-preload]}}
;; Uncomment line below to `make test-watch` a specific file
;; :ns-regexp "status-im2.subs.messages-test$"
:main status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl
:ui-driven true
:closure-defines
{status-im2.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
{schema.core/throw-on-error? true
status-im2.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
status-im2.config/INFURA_TOKEN #shadow/env "INFURA_TOKEN"
status-im2.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"
status-im2.config/ALCHEMY_ARBITRUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ARBITRUM_GOERLI_TOKEN"
@@ -139,9 +147,17 @@
:compiler-options {:optimizations :simple
:source-map false}}
:component-test {:target :npm-module
:entries [quo.core-spec status-im2.core-spec]
:entries [;; We need to tell shadow-cljs to compile
;; the preloads namespace because it will
;; be used directly by Jest in the option
;; setupFilesAfterEnv.
status-im2.setup.schema-preload
quo.core-spec
status-im2.core-spec]
:ns-regexp "component-spec$"
:output-dir "component-spec"
:closure-defines {schema.core/throw-on-error? true}
:compiler-options {:warnings-as-errors false
:static-fns false
:infer-externs true}}}}
@@ -3,18 +3,22 @@
[quo.components.counter.step.view :as step]
[test-helpers.component :as h]))
(defn render
[component]
(h/render-with-theme-provider component :dark))
(h/describe "step component"
(h/test "default render of step component"
(h/render [step/view {} nil])
(render [step/view {} nil])
(-> (h/expect (h/query-by-label-text :step-counter))
(h/is-truthy)))
(h/test "renders step with a string value"
(h/render [step/view {} "1"])
(render [step/view {} "1"])
(-> (h/expect (h/get-by-text "1"))
(h/is-truthy)))
(h/test "renders step with an integer value"
(h/render [step/view {} 1])
(render [step/view {} 1])
(-> (h/expect (h/get-by-text "1"))
(h/is-truthy))))
+20 -3
View File
@@ -2,10 +2,24 @@
(:require
[quo.components.counter.step.style :as style]
[quo.components.markdown.text :as text]
[quo.theme :as theme]
quo.theme
[react-native.core :as rn]
[schema.core :as schema]
[utils.number]))
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:accessibility-label {:optional true} [:maybe :keyword]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:in-blur-view? {:optional true} [:maybe :boolean]]
[:theme :schema.common/theme]
[:type {:optional true} [:enum :active :complete :neutral]]]]
[:value [:maybe [:or :string :int]]]]
:any])
(defn- view-internal
[{:keys [type accessibility-label theme in-blur-view? customization-color]} value]
(let [type (or type :neutral)
@@ -23,6 +37,9 @@
[text/text
{:weight :medium
:size :label
:style {:color (style/text-color type theme)}} label]]))
:style {:color (style/text-color type theme)}}
label]]))
(def view (theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal ?schema)))
@@ -19,3 +19,7 @@
:background-color colors/danger-50
:align-items :center
:justify-content :center})
(def empty-state
{:width 375
:height 116})
@@ -2,11 +2,12 @@
(:require
[quo.components.graph.utils :as utils]
[quo.components.graph.wallet-graph.style :as style]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.foundations.resources :as resources]
[quo.theme :as quo.theme]
[react-native.charts :as charts]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]
[react-native.linear-gradient :as linear-gradient]))
(defn- time-frame->max-data-points
@@ -42,11 +43,10 @@
gradient-colors [(colors/alpha line-color 0.1) (colors/alpha line-color 0)]
fill-color (colors/theme-colors colors/white colors/neutral-95)]
(if (= time-frame :empty)
[rn/view
{:accessibility-label :illustration
:style style/illustration}
[text/text {:style {:color colors/white}}
"Illustration here"]]
[fast-image/fast-image
{:style style/empty-state
:source (resources/get-themed-image :no-funds theme)
:accessibility-label :illustration}]
[rn/view
[linear-gradient/linear-gradient
{:colors gradient-colors
+16 -18
View File
@@ -3,22 +3,17 @@
[quo.components.icon :as quo.icons]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as theme]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
(def themes
{:light {:default colors/neutral-50
:success colors/success-50
:error colors/danger-50}
:dark {:default colors/neutral-40
:success colors/success-60
:error colors/danger-60}})
(defn get-color
[k]
(get-in themes [(theme/get-theme) k]))
[k theme]
(case k
:success (colors/resolve-color :success theme)
:error (colors/resolve-color :danger theme)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)))
(defn info-message
(defn view-internal
"[info-message opts \"message\"]
opts
{:type :default/:success/:error
@@ -27,11 +22,11 @@
:text-color colors/white ;; text color override
:icon-color colors/white ;; icon color override
:no-icon-color? false ;; disable tint color for icon"
[{:keys [type size icon text-color icon-color no-icon-color? style]} message]
[{:keys [type size theme icon text-color icon-color no-icon-color? style accessibility-label]} message]
(let [weight (if (= size :default) :regular :medium)
icon-size (if (= size :default) 16 12)
size (if (= size :default) :paragraph-2 :label)
text-color (or text-color (get-color type))
text-color (or text-color (get-color type theme))
icon-color (or icon-color text-color)]
[rn/view
{:style (merge {:flex-direction :row
@@ -42,7 +37,10 @@
:no-color no-icon-color?
:size icon-size}]
[text/text
{:size size
:weight weight
:style {:color text-color
:margin-horizontal 4}} message]]))
{:accessibility-label accessibility-label
:size size
:weight weight
:style {:color text-color
:margin-horizontal 4}} message]]))
(def info-message (quo.theme/with-theme view-internal))
@@ -22,9 +22,10 @@
(h/is-truthy (h/get-by-text "test description")))
(h/test "renders step component when step-number is valid and type is step"
(h/render [list/view
{:type :step
:step-number 1}])
(h/render-with-theme-provider [list/view
{:type :step
:step-number 1}]
:dark)
(h/is-truthy (h/get-by-label-text :step-counter)))
(h/test "renders decription with a context tag component and description after the tag"
@@ -36,7 +36,7 @@
:blur? false
:type :digit} 1])
(h/is-truthy (h/query-by-label-text :text-label))
(h/fire-event :press (h/query-by-label-text :keyboard-key))
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/was-called on-press)))
(h/test "Is not pressable when disabled is true"
@@ -47,5 +47,5 @@
:blur? false
:type :digit} 1])
(h/is-truthy (h/query-by-label-text :text-label))
(h/fire-event :press (h/query-by-label-text :keyboard-key))
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/was-not-called on-press))))
@@ -7,6 +7,11 @@
[react-native.core :as rn]
[reagent.core :as reagent]))
(defn- label->accessibility-label
[label]
(let [label-name (if (keyword? label) (name label) label)]
(keyword (str "keyboard-key-" label-name))))
(defn- view-internal
[]
(let [pressed? (reagent/atom false)]
@@ -14,9 +19,11 @@
(let [label-color (style/get-label-color disabled? theme blur?)
background-color (style/toggle-background-color @pressed? blur? theme)]
[rn/pressable
{:accessibility-label :keyboard-key
{:accessibility-label (label->accessibility-label label)
:disabled (or disabled? (not label))
:on-press (fn [] (on-press label))
:on-press (fn []
(when on-press
(on-press label)))
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
:style (style/container background-color)}
@@ -17,13 +17,15 @@
(defn- view-internal
[]
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press]}]
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press on-delete]
:or {left-action :none}}]
[rn/view
{:style style/container}
(for [row-index (range 1 4)]
^{:key row-index}
[rn/view {:style style/row-container}
(for [column-index (range 1 4)]
^{:key (str row-index column-index)}
[keyboard-item
{:item (+ (* (dec row-index) 3) column-index)
:type :digit
@@ -58,10 +60,10 @@
:theme theme}]
(if delete-key?
[keyboard-item
{:item :i/delete
{:item :i/travel
:type :key
:disabled? disabled?
:on-press on-press
:on-press on-delete
:blur? blur?
:theme theme}]
[keyboard-item])]]))
@@ -14,7 +14,10 @@
:padding-bottom 4
:height 36
:flex-direction :row
:justify-content :space-between})
:justify-content :space-between
;;
:background-color :pink
})
(def token
{:width 32
@@ -22,14 +25,12 @@
(defn text-input
[theme]
(merge typography/heading-1
{:font-weight "600"
:margin-left 8
:margin-right (if platform/ios? 6 4)
:color (colors/theme-colors colors/neutral-100 colors/white theme)
:padding 0
:text-align :center
:height "100%"}))
(assoc typography/heading-1
:font-weight "600"
:color (colors/theme-colors colors/neutral-100 colors/white theme)
:padding 0
;; TODO: fix the padding
:height "100%"))
(defn divider
[width theme]
+94 -60
View File
@@ -1,73 +1,107 @@
(ns quo.components.wallet.token-input.view
(:require
[clojure.string :as string]
[quo.components.buttons.button.view :as button]
[quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text]
[quo.components.tags.network-tags.view :as network-tag]
[quo.components.wallet.token-input.style :as style]
[quo.foundations.colors :as colors]
[quo.foundations.common :as common]
[quo.foundations.resources :as resources]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
[clojure.string :as string]
[quo.components.buttons.button.view :as button]
[quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text]
[quo.components.tags.network-tags.view :as network-tag]
[quo.components.wallet.token-input.style :as style]
[quo.foundations.colors :as colors]
[quo.foundations.common :as common]
[oops.core :as oops]
[quo.foundations.resources :as resources]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.platform :as platform]
[reagent.core :as reagent]))
(defn calc-value
[crypto? currency token value conversion]
(if crypto?
(str (get common/currency-label currency) (.toFixed (* value conversion) 2))
(str (.toFixed (/ value conversion) 2) " " (string/upper-case (clj->js token)))))
(let [num-value (if (string? value) (parse-double (or value "0")) value)]
(if crypto?
(str (get common/currency-label currency) (.toFixed (* num-value conversion) 2))
(str (.toFixed (/ num-value conversion) 2) " " (string/upper-case (or (clj->js token) ""))))))
(defn- view-internal
[]
(let [width (:width (rn/get-window))
value (reagent/atom 0)
value (reagent/atom nil)
crypto? (reagent/atom true)
input-ref (atom nil)]
(fn [{:keys [theme token currency conversion networks title customization-color]}]
[rn/view {:style (style/main-container width)}
[rn/view {:style style/amount-container}
[rn/pressable
{:on-press #(when @input-ref (.focus ^js @input-ref))
:style {:flex-direction :row
:flex-grow 1
:align-items :flex-end}}
[rn/image
{:style style/token
:source (resources/get-token token)}]
[rn/text-input
{:ref #(reset! input-ref %)
:placeholder "0"
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
:keyboard-type :numeric
:max-length 12
:default-value @value
:on-change-text #(reset! value %)
:style (style/text-input theme)
:selection-color customization-color}]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:margin-right 8
:padding-bottom 2}}
(string/upper-case (clj->js (if @crypto? token currency)))]]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press #(swap! crypto? not)
:type :outline
:accessibility-label :reorder}
:i/reorder]]
[divider-line/view {:container-style {:margin-vertical 8}}]
[rn/view {:style style/data-container}
[network-tag/view {:networks networks :title title}]
[text/text
{:size :paragraph-2
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(calc-value @crypto? currency token @value conversion)]]])))
(fn [{:keys [theme token currency conversion networks title customization-color
on-swap container-style show-keyboard?]
:or {show-keyboard? true}
external-value :value}]
(let [number-of-chars (count (or external-value @value))
text-unit-position (+ 23
(* (if (<= number-of-chars 1)
0
(- number-of-chars 1))
15.333))]
[rn/view
{:style (merge
(style/main-container width)
container-style)}
[rn/view {:style style/amount-container}
[rn/pressable
{:on-press #(when @input-ref (.focus ^js @input-ref))
:style {:flex 1
:background-color :orange
}}
[rn/image
{:style style/token
:source (resources/get-token token)}]
[rn/view {:style {:background-color :lightblue
;;
:position :absolute
:top 0
:bottom 0
:left 40
:right 0
}}
[rn/text-input
{:ref #(reset! input-ref %)
:auto-focus true
:placeholder "0"
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:default-value (or external-value @value)
:keyboard-type :numeric
:max-length 12
:on-change-text #(reset! value %)
:style (style/text-input theme)
:selection-color customization-color
:show-soft-input-on-focus show-keyboard?}]
;;
[rn/view {:style {:position :absolute
:bottom 1
:left text-unit-position}}
[text/text
{:size :paragraph-2
:weight :semi-bold
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(string/upper-case (or (clj->js (if @crypto? token currency)) ""))]]]]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press (fn []
(swap! crypto? not)
(when on-swap
(on-swap @crypto?)))
:type :outline
:accessibility-label :reorder}
:i/reorder]]
[divider-line/view {:container-style {:margin-vertical 8}}]
[rn/view {:style style/data-container}
[network-tag/view {:networks networks :title title}]
[text/text
{:size :paragraph-2
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(calc-value @crypto? currency token (or external-value @value) conversion)]]]))))
(def view (quo.theme/with-theme view-internal))
+1 -1
View File
@@ -212,7 +212,7 @@
;;;; Counter
(def counter quo.components.counter.counter.view/view)
(def step quo.components.counter.step.view/view)
(def step #'quo.components.counter.step.view/view)
;;;; Dividers
(def divider-label quo.components.dividers.divider-label.view/view)
+9
View File
@@ -7,6 +7,15 @@
:keycard-watermark (js/require "../resources/images/ui2/keycard-watermark.png")
:bored-ape (js/require "../resources/images/mock2/bored-ape.png")})
(def ui-themed
{:no-funds
{:light (js/require "../resources/images/ui2/no-funds-light.png")
:dark (js/require "../resources/images/ui2/no-funds-dark.png")}})
(defn get-themed-image
[k theme]
(get-in ui-themed [k theme]))
(defn get-image
[k]
(get ui k))
+68
View File
@@ -0,0 +1,68 @@
# Schemas
This document will grow to describe how we use Malli in the project and our
conventions. It's still early days 🐪
## Guidelines
### Use var quote `#'` when aliasing instrumented vars
It is common in this repository to have aliases to vars. For example, `view`
referring to `var-internal`, or `quo.core/button` referring to
`quo.components.buttons.button.view`.
If the original var being aliased is instrumented, the alias var MUST [var
quote](https://clojure.org/guides/weird_characters#_var_quote) the original var.
If you don't do this, the aliased var will not be instrumented.
```clojure
;; bad, view-internal is instrumented, but both aliases don't use a var quote.
(schema.core/=> view-internal ?schema)
(def view (quo.theme/with-theme view-internal))
(def button quo.components.buttons.button.view/button)
;; good
(schema.core/=> view-internal ?schema)
(def view (quo.theme/with-theme #'view-internal))
(def button #'quo.components.buttons.button.view/button)
```
### Prefix schema references with `?`
Prefix schema bindings and vars with a question mark `?`. This is the naming
convention used by malli itself when functions receive instances of schemas and
it's an unambiguous way to avoid naming clashes.
```clojure
;; bad
(def message-type [:enum ...])
(defn view
[message-type] ; Shadows `message-type` schema
(do-something message-type))
;; good
(def ?message-type [:enum ...])
(defn view
[message-type] ; Unambiguous naming strategy
(do-something message-type))
```
### Define schemas as functions when needed
Malli has many utility functions to manipulate schemas as data, and they will
automatically check if the schemas were already defined in the registry.
For schemas we want to conveniently access from the global registry, like
`:schema.common/theme`, they must be registered before Malli tries to use them.
```clojure
;; bad, will fail if :schema.common/bar is not registered.
(def ^:private ?foo
(malli.util/select-keys :schema.common/bar [:id :name]))
;; good, execution will be delayed until the schema ?foo is correctly registered.
(defn- ?foo
[]
(malli.util/select-keys :schema.common/bar [:id :name]))
```
+14
View File
@@ -0,0 +1,14 @@
(ns schema.common
(:require
[schema.registry :as registry]))
(def ^:private ?theme
[:enum :light :dark])
(def ^:private ?customization-color
[:or :string :keyword])
(defn register-schemas
[]
(registry/register ::theme ?theme)
(registry/register ::customization-color ?customization-color))
+17
View File
@@ -0,0 +1,17 @@
(ns schema.core)
(defmacro =>
"Similar to `malli.core/=>`, but instrumentation code will be completely removed
in non-debug environments.
`value` is first transformed via `malli.core/schema` to make sure we fail fast
and only register valid schemas."
[sym value]
`(if ^boolean js/goog.DEBUG
(try
(malli.core/=> ~sym (malli.core/schema ~value))
(catch js/Error e#
(taoensso.timbre/error "Failed to instrument function"
{:symbol ~sym :error e#})
~sym))
~sym))
+95
View File
@@ -0,0 +1,95 @@
(ns schema.core
(:require-macros schema.core)
(:require
[malli.core :as malli]
[malli.dev.pretty :as malli.pretty]
schema.state
[taoensso.timbre :as log]))
(goog-define throw-on-error? false)
(defn- ui-reporter
"Prints to STDOUT and signals a schema error should be displayed on screen."
[schema-id printer]
(let [report (malli.pretty/reporter printer)]
(fn [type data]
(report type data)
(swap! schema.state/errors conj schema-id))))
(defn- thrower
"Similar to `malli.dev.pretty/thrower`, but this reporter uses js/Error instead
of ex-info, otherwise invalid schema errors will be printed in one long and
incomprehensible line in unit test failures."
([] (thrower (malli.pretty/-printer)))
([printer]
(let [report (malli.pretty/reporter printer)]
(fn [type data]
(let [message (with-out-str (report type data))]
(throw (js/Error. (str "\n" message))))))))
(defn reporter
([]
(reporter nil nil))
([schema-id]
(reporter schema-id nil))
([schema-id opts]
(let [printer (malli.pretty/-printer
(merge {:width 60
:print-length 6
:print-level 3
:print-meta false}
opts))]
(if throw-on-error?
;; The thrower reporter should be used to short-circuit tests on schema errors.
(thrower printer)
(ui-reporter schema-id printer)))))
(defn- with-clear-schema-error
"Clears current schema error on app if args passed to `f` are valid."
[schema-id validate-input f]
(fn [& args]
(when (validate-input args)
(swap! schema.state/errors disj schema-id))
(apply f args)))
(defn instrument
"Similar to `malli/-instrument`, but will automatically clear up visible schema
errors if `f` is called with valid arguments.
We use a validator cached by `malli.core/validator`, so that validation is
performed once.
If `?schema` is invalid, then behave like a nop, log the error and return `f`.
`schema-id` is optional, but should be passed when instrumenting anonymous
functions. Consider using a namespaced keyword. For example:
(schema/instrument ::foo
(fn [x] (inc x))
[:=> [:cat :int] :int])
"
([f ?schema]
(if ^boolean js/goog.DEBUG
(let [schema-id (when (var? f) (symbol f))]
(when-not schema-id
(log/warn "Anonymous function instrumented without an explicit identifier."
{:schema ?schema}))
(instrument schema-id f ?schema))
f))
([schema-id f ?schema]
(if ^boolean js/goog.DEBUG
(try
(let [?schema (malli/schema ?schema)
{schema-input :input} (malli/-function-info ?schema)
[validate-input _] (malli/-vmap malli/validator [schema-input])]
(malli/-instrument {:schema ?schema
:report (reporter schema-id
{:title (str "Schema error at " schema-id)})}
(with-clear-schema-error schema-id validate-input f)))
(catch js/Error e
(log/error "Failed to instrument function"
{:schema-id schema-id
:error e
:function f})
f))
f)))
+26
View File
@@ -0,0 +1,26 @@
(ns schema.registry
(:refer-clojure :exclude [merge])
(:require
[malli.core :as malli]
malli.registry))
(defonce ^:private registry
(atom (malli/default-schemas)))
(defn init-global-registry
[]
(malli.registry/set-default-registry! (malli.registry/mutable-registry registry)))
(defn register
"Defines a new schema in mutable `registry`.
We normalize `?schema` by always registering it as a proper instance of
`malli.core/Schema` to avoid inconsistencies down the road."
[type ?schema]
(swap! registry assoc type (malli/schema ?schema))
?schema)
(defn merge
[& schemas]
(apply swap! registry cljs.core/merge schemas)
schemas)
+11
View File
@@ -0,0 +1,11 @@
(ns schema.state
(:require [reagent.core :as reagent]))
(def errors
"Set of schema identifiers, usually namespaced keywords. When the set is empty,
no schema errors will be displayed on the app. See `schema.view/view`."
(reagent/atom #{}))
(defn clear-errors
[]
(reset! errors #{}))
+28
View File
@@ -0,0 +1,28 @@
(ns schema.style)
(defn container
[{:keys [bottom-inset]}]
{:align-items :center
:flex-direction :row
:background-color "#cc0000"
:border-bottom-left-radius 8
:border-top-left-radius 8
:justify-content :center
:padding-vertical 6
:padding-right 16
:position :absolute
:bottom (+ 12 bottom-inset)
:right 0
:z-index 10000000})
(def icon
{:margin-horizontal 8})
(def text
{:font-family "Inter-SemiBold"
:font-size 13
:color "#ddd"})
(def text-suffix
{:font-style :italic
:font-size 9})
+18
View File
@@ -0,0 +1,18 @@
(ns schema.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
schema.state
[schema.style :as style]))
(defn view
[]
(when (seq @schema.state/errors)
[rn/pressable
{:on-press schema.state/clear-errors
:style (style/container {:bottom-inset (safe-area/get-bottom)})}
[quo/icon :i/close {:size 12 :color "#ddd" :container-style style/icon}]
[rn/text {:style style/text}
"Schema error(s)"
[rn/text {:style (merge style/text style/text-suffix)} " check logs"]]]))
+2
View File
@@ -8,6 +8,7 @@
status-im.subs.root
status-im2.setup.i18n-resources
[status-im2.setup.interceptors :as interceptors]
[status-im2.setup.schema :as schema]
status-im2.subs.root
[utils.re-frame :as rf]))
@@ -118,6 +119,7 @@
[& args]
(reset-test-data!)
(interceptors/register-global-interceptors)
(schema/setup!)
(rf/set-mergeable-keys #{:filters/load-filters
:pairing/set-installation-metadata
:dispatch-n
@@ -131,6 +131,13 @@
:accessibility-label :appearance-settings-button
:chevron true
:on-press #(re-frame/dispatch [:navigate-to :quo-preview])}])
(when config/quo-preview-enabled?
[list.item/list-item
{:icon :main-icons/appearance
:title "Status IM Components"
:accessibility-label :status-im-common-components
:chevron true
:on-press #(re-frame/dispatch [:navigate-to :status-im-preview])}])
[list.item/list-item
{:icon :main-icons/appearance
:title (i18n/label :t/appearance)
@@ -0,0 +1,32 @@
(ns status-im2.common.floating-button-page.component-spec
(:require [quo.core :as quo]
[status-im2.common.floating-button-page.view :as floating-button-page]
[test-helpers.component :as h]))
(h/describe "floating button page"
(h/test "renders with a header and standard button"
(h/render [floating-button-page/view
{:header [quo/page-nav
{:type :title-description
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:background :blur
:icon-name :i/close}]
:footer [quo/button {} "continue"]}])
(h/is-truthy (h/get-by-text "continue"))
(h/is-truthy (h/get-by-text "floating button page")))
(h/test "renders with a header and a slide button"
(h/render [floating-button-page/view
{:header [quo/page-nav
{:type :title-description
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:background :blur
:icon-name :i/close}]
:footer [quo/slide-button
{:track-text "We gotta slide"
:track-icon :face-id}]}])
(h/is-truthy (h/get-by-text "We gotta slide"))))
@@ -0,0 +1,17 @@
(ns status-im2.common.floating-button-page.floating-container.style
(:require [react-native.safe-area :as safe-area]))
(defn content-container
[blur? keyboard-shown?]
(let [margin-bottom (if keyboard-shown? 0 (safe-area/get-bottom))]
(cond-> {:margin-top :auto
:overflow :hidden
:margin-bottom margin-bottom
:padding-vertical 12
:padding-horizontal 20}
blur? (dissoc :padding-vertical :padding-horizontal))))
(def blur-inner-container
{:background-color :transparent ; required, otherwise blur-view will shrink
:padding-vertical 12
:padding-horizontal 20})
@@ -0,0 +1,25 @@
(ns status-im2.common.floating-button-page.floating-container.view
(:require [quo.theme :as quo.theme]
[react-native.blur :as blur]
[react-native.core :as rn]
[status-im2.common.floating-button-page.floating-container.style :as style]))
(defn- blur-container
[child theme]
[blur/view
{:blur-amount 12
:blur-radius 12
:blur-type (quo.theme/theme-value :light :dark theme)}
[rn/view {:style style/blur-inner-container}
child]])
(defn view-internal
[{:keys [theme on-layout keyboard-shown? blur?]} child]
[rn/view
{:style (style/content-container blur? keyboard-shown?)
:on-layout on-layout}
(if blur?
[blur-container child theme]
child)])
(def view (quo.theme/with-theme view-internal))
@@ -0,0 +1,15 @@
(ns status-im2.common.floating-button-page.style)
(def page-container
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def keyboard-avoiding-view
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
@@ -0,0 +1,102 @@
(ns status-im2.common.floating-button-page.view
(:require
[oops.core :as oops]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im2.common.floating-button-page.floating-container.view :as floating-container]
[status-im2.common.floating-button-page.style :as style]))
(defn- show-background
[{:keys [window-height keyboard-height footer-container-height content-scroll-y
content-container-height header-height keyboard-shown?]}]
(let [available-space (- window-height
(safe-area/get-top)
header-height
keyboard-height ; Already contains the bottom safe area value
footer-container-height)
scroll-view-height (- content-container-height content-scroll-y)
overlap? (< available-space scroll-view-height)]
(and keyboard-shown? overlap?)))
(defn- set-height-on-layout
[ratom]
(fn [event]
(let [height (oops/oget event "nativeEvent.layout.height")]
(reset! ratom height))))
(defn- init-keyboard-listeners
[{:keys [on-did-show]}]
(let [keyboard-will-show? (reagent/atom false)
keyboard-did-show? (reagent/atom false)
add-listener (fn [listener callback]
(oops/ocall rn/keyboard "addListener" listener callback))
will-show-listener (add-listener "keyboardWillShow"
#(reset! keyboard-will-show? true))
did-show-listener (add-listener "keyboardDidShow"
(fn [e]
(reset! keyboard-did-show? true)
(when on-did-show (on-did-show e))))
will-hide-listener (add-listener "keyboardWillHide"
#(reset! keyboard-will-show? false))
did-hide-listener (add-listener "keyboardDidHide"
#(reset! keyboard-did-show? false))
remove-listeners (fn []
(doseq [listener [will-show-listener will-hide-listener
did-show-listener did-hide-listener]]
(oops/ocall listener "remove")))]
{:keyboard-will-show? keyboard-will-show?
:keyboard-did-show? keyboard-did-show?
:remove-listeners remove-listeners}))
(defn view
[{:keys [header footer]} & children]
(reagent/with-let [window-height (:height (rn/get-window))
footer-container-height (reagent/atom 0)
header-height (reagent/atom 0)
content-container-height (reagent/atom 0)
content-scroll-y (reagent/atom 0)
keyboard-height (reagent/atom 0)
{:keys [keyboard-will-show?
keyboard-did-show?
remove-listeners]} (init-keyboard-listeners
{:on-did-show
(fn [e]
(reset! keyboard-height
(oops/oget e "endCoordinates.height")))})
set-header-height (set-height-on-layout header-height)
set-content-container-height (set-height-on-layout content-container-height)
set-footer-container-height (set-height-on-layout footer-container-height)
set-content-y-scroll (fn [event]
(reset! content-scroll-y
(oops/oget event "nativeEvent.contentOffset.y")))]
(let [keyboard-shown? (if platform/ios? @keyboard-will-show? @keyboard-did-show?)
show-background? (show-background {:window-height window-height
:footer-container-height @footer-container-height
:keyboard-height @keyboard-height
:content-scroll-y @content-scroll-y
:content-container-height @content-container-height
:header-height @header-height
:keyboard-shown? keyboard-shown?})]
[rn/view {:style style/page-container}
[rn/view {:on-layout set-header-height}
header]
[rn/scroll-view
{:on-scroll set-content-y-scroll
:scroll-event-throttle 64
:content-container-style {:flex-grow 1}}
(into [rn/view {:on-layout set-content-container-height}]
children)]
[rn/keyboard-avoiding-view
{:style style/keyboard-avoiding-view
:keyboard-vertical-offset (if platform/ios? (safe-area/get-top) 0)
:pointer-events :box-none}
[floating-container/view
{:on-layout set-footer-container-height
:keyboard-shown? keyboard-shown?
:blur? show-background?}
footer]]])
(finally
(remove-listeners))))
@@ -31,10 +31,10 @@
:on-change-text #(reset! entered-password %)}]
(when (not-empty error)
[quo/info-message
{:type :error
:size :default
:icon :i/info
:style {:margin-top 8}}
{:type :error
:size :default
:icon :i/info
:containstyle {:margin-top 8}}
(i18n/label :t/oops-wrong-password)])
[quo/button
{:container-style {:margin-bottom 12 :margin-top 40}
+13 -1
View File
@@ -79,7 +79,19 @@
:dark (js/require "../resources/images/ui2/no-contacts-to-invite-dark.png")}
:no-notifications
{:light (js/require "../resources/images/ui2/no-notifications-light.png")
:dark (js/require "../resources/images/ui2/no-notifications-dark.png")}})
:dark (js/require "../resources/images/ui2/no-notifications-dark.png")}
:no-activity
{:light (js/require "../resources/images/ui2/no-activity-light.png")
:dark (js/require "../resources/images/ui2/no-activity-dark.png")}
:no-collectibles
{:light (js/require "../resources/images/ui2/no-collectibles-light.png")
:dark (js/require "../resources/images/ui2/no-collectibles-dark.png")}
:no-dapps
{:light (js/require "../resources/images/ui2/no-dapps-light.png")
:dark (js/require "../resources/images/ui2/no-dapps-dark.png")}
:no-permissions
{:light (js/require "../resources/images/ui2/no-permissions-light.png")
:dark (js/require "../resources/images/ui2/no-permissions-dark.png")}})
(def mock-images
{:bored-ape (js/require "../resources/images/mock2/bored-ape.png")
@@ -0,0 +1,7 @@
(ns status-im2.common.standard-authentication.core
(:require
status-im2.common.standard-authentication.standard-auth.button.view
status-im2.common.standard-authentication.standard-auth.slide-button.view))
(def button status-im2.common.standard-authentication.standard-auth.button.view/view)
(def slide-button status-im2.common.standard-authentication.standard-auth.slide-button.view/view)
@@ -1,10 +1,6 @@
(ns status-im2.common.standard-authentication.standard-auth.view
(ns status-im2.common.standard-authentication.standard-auth.authorize
(:require
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.touch-id :as biometric]
[reagent.core :as reagent]
[status-im2.common.standard-authentication.enter-password.view :as enter-password]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
@@ -46,39 +42,3 @@
{:on-enter-password on-enter-password
:button-icon-left auth-button-icon-left
:button-label auth-button-label}])}]))))))
(defn- view-internal
[_]
(let [reset-slider? (reagent/atom false)
on-close #(reset! reset-slider? true)]
(fn [{:keys [biometric-auth?
track-text
customization-color
auth-button-label
on-enter-password
on-auth-success
on-auth-fail
auth-button-icon-left
size
theme
blur?
container-style]}]
[rn/view {:style {:flex 1}}
[quo/slide-button
{:size size
:container-style container-style
:customization-color customization-color
:on-reset (when @reset-slider? #(reset! reset-slider? false))
:on-complete #(authorize {:on-close on-close
:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
:on-enter-password on-enter-password
:biometric-auth? biometric-auth?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label})
:track-icon (if biometric-auth? :i/face-id :password)
:track-text track-text}]])))
(def view (quo.theme/with-theme view-internal))
@@ -0,0 +1,44 @@
(ns status-im2.common.standard-authentication.standard-auth.button.view
(:require
[quo.core :as quo]
[quo.theme :as quo.theme]
[reagent.core :as reagent]
[status-im2.common.standard-authentication.standard-auth.authorize :as authorize]))
(defn- view-internal
[_]
(let [reset-slider? (reagent/atom false)
on-close #(reset! reset-slider? true)]
(fn [{:keys [biometric-auth?
customization-color
auth-button-label
on-enter-password
on-auth-success
on-press
on-auth-fail
auth-button-icon-left
size
button-label
theme
blur?
container-style
icon-left]}]
[quo/button
{:size size
:container-style container-style
:customization-color customization-color
:icon-left icon-left
:on-press (if on-press
on-press
#(authorize/authorize {:on-close on-close
:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
:on-enter-password on-enter-password
:biometric-auth? biometric-auth?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label}))}
button-label])))
(def view (quo.theme/with-theme view-internal))
@@ -0,0 +1,43 @@
(ns status-im2.common.standard-authentication.standard-auth.slide-button.view
(:require
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.common.standard-authentication.standard-auth.authorize :as authorize]))
(defn- view-internal
[_]
(let [reset-slider? (reagent/atom false)
on-close #(reset! reset-slider? true)]
(fn [{:keys [biometric-auth?
track-text
customization-color
auth-button-label
on-enter-password
on-auth-success
on-auth-fail
auth-button-icon-left
size
theme
blur?
container-style]}]
[rn/view {:style {:flex 1}}
[quo/slide-button
{:size size
:container-style container-style
:customization-color customization-color
:on-reset (when @reset-slider? #(reset! reset-slider? false))
:on-complete #(authorize/authorize {:on-close on-close
:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
:on-enter-password on-enter-password
:biometric-auth? biometric-auth?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label})
:track-icon (if biometric-auth? :i/face-id :password)
:track-text track-text}]])))
(def view (quo.theme/with-theme view-internal))
@@ -1,15 +1,20 @@
(ns status-im2.contexts.onboarding.enable-biometrics.view
(:require
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im2.common.biometric.events :as biometric]
[status-im2.common.parallax.view :as parallax]
[status-im2.common.parallax.whitelist :as whitelist]
[status-im2.common.resources :as resources]
[status-im2.common.standard-authentication.core :as standard-auth]
[status-im2.contexts.onboarding.enable-biometrics.style :as style]
[status-im2.navigation.state :as state]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn page-title
[]
@@ -21,22 +26,39 @@
:description-accessibility-label :enable-biometrics-sub-title}])
(defn enable-biometrics-buttons
[insets]
[insets theme]
(let [supported-biometric-type (rf/sub [:biometric/supported-type])
bio-type-label (biometric/get-label-by-type supported-biometric-type)
profile-color (:color (rf/sub [:onboarding-2/profile]))]
profile-color (or (:color (rf/sub [:onboarding-2/profile]))
(rf/sub [:profile/customization-color]))
syncing-results? (= :syncing-results @state/root-id)]
[rn/view {:style (style/buttons insets)}
[quo/button
{:accessibility-label :enable-biometrics-button
:on-press #(rf/dispatch [:onboarding-2/enable-biometrics])
:icon-left :i/face-id
:customization-color profile-color}
(i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})]
[standard-auth/button
(merge
{:size 40
:accessibility-label :enable-biometrics-button
:icon-left :i/face-id
:customization-color profile-color
:button-label (i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})}
(if syncing-results?
{:theme theme
:blur? true
:on-enter-password (fn [entered-password]
(rf/dispatch
[:onboarding-2/authenticate-enable-biometrics
(security/safe-unmask-data
entered-password)])
(rf/dispatch [:hide-bottom-sheet]))
:auth-button-label (i18n/label :t/confirm)}
{:on-press #(rf/dispatch [:onboarding-2/enable-biometrics])}))]
[quo/button
{:accessibility-label :maybe-later-button
:background :blur
:type :grey
:on-press #(rf/dispatch [:onboarding-2/create-account-and-login])
:on-press #(rf/dispatch (if syncing-results?
[:navigate-to-within-stack
[:enable-notifications :enable-biometrics]]
[:onboarding-2/create-account-and-login]))
:container-style {:margin-top 12}}
(i18n/label :t/maybe-later)]]))
@@ -55,12 +77,19 @@
:style (style/page-illustration width)
:source (resources/get-image :biometrics)}]))
(defn enable-biometrics
[]
(defn f-enable-biometrics
[{:keys [theme]}]
(let [insets (safe-area/get-insets)]
[rn/view {:style (style/page-container insets)}
[page-title]
(if whitelist/whitelisted?
[enable-biometrics-parallax]
[enable-biometrics-simple])
[enable-biometrics-buttons insets]]))
[enable-biometrics-buttons insets theme]]))
(defn- internale-enable-biometrics
[params]
[:f> f-enable-biometrics params])
(def view (quo.theme/with-theme internale-enable-biometrics))
@@ -51,7 +51,7 @@
[quo/page-nav
{:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back-within-stack :new-to-status])}]
:on-press #(rf/dispatch [:navigate-back-within-stack :enable-biometrics])}]
[page-title]
[rn/view {:style style/page-illustration}
[quo/text
+34 -3
View File
@@ -35,11 +35,30 @@
{:biometric/authenticate {:on-success #(rf/dispatch [:onboarding-2/biometrics-done])
:on-fail #(rf/dispatch [:onboarding-2/biometrics-fail %])}})
(rf/defn authenticate-enable-biometrics
{:events [:onboarding-2/authenticate-enable-biometrics]}
[{:keys [db]} password]
{:db (-> db
(assoc-in [:onboarding-2/profile :password] password)
(assoc-in [:onboarding-2/profile :syncing?] true))
:biometric/authenticate {:on-success #(rf/dispatch [:onboarding-2/biometrics-done])
:on-fail #(rf/dispatch [:onboarding-2/biometrics-fail %])}})
(rf/defn navigate-to-enable-notifications
{:events [:onboarding-2/navigate-to-enable-notifications]}
[{:keys [db]}]
(let [key-uid (get-in db [:profile/profile :key-uid])]
{:db (dissoc db :onboarding-2/profile)
:dispatch [:navigate-to-within-stack [:enable-notifications :enable-biometrics]]}))
(rf/defn biometrics-done
{:events [:onboarding-2/biometrics-done]}
[{:keys [db]}]
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
:dispatch [:onboarding-2/create-account-and-login]})
(let [syncing? (get-in db [:onboarding-2/profile :syncing?])]
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
:dispatch (if syncing?
[:onboarding-2/finalize-setup]
[:onboarding-2/create-account-and-login])}))
(rf/defn biometrics-fail
{:events [:onboarding-2/biometrics-fail]}
@@ -83,6 +102,14 @@
[:navigate-to-within-stack [:enable-biometrics :new-to-status]]
[:onboarding-2/create-account-and-login])}))
(rf/defn navigate-to-enable-biometrics
{:events [:onboarding-2/navigate-to-enable-biometrics]}
[{:keys [db]}]
(let [supported-type (:biometric/supported-type db)]
{:dispatch (if supported-type
[:open-modal :enable-biometrics]
[:open-modal :enable-notifications])}))
(rf/defn seed-phrase-entered
{:events [:onboarding-2/seed-phrase-entered]}
[_ seed-phrase on-error]
@@ -120,6 +147,7 @@
[{:keys [db]}]
(let [masked-password (get-in db [:onboarding-2/profile :password])
key-uid (get-in db [:profile/profile :key-uid])
syncing? (get-in db [:onboarding-2/profile :syncing?])
biometric-enabled? (= (get-in db [:onboarding-2/profile :auth-method])
constants/auth-method-biometric)]
(cond-> {:db (assoc db :onboarding-2/generated-keys? true)}
@@ -127,7 +155,10 @@
(assoc :keychain/save-password-and-auth-method
{:key-uid key-uid
:masked-password masked-password
:on-success #(log/debug "successfully saved biometric")
:on-success (fn []
(if syncing?
(rf/dispatch [:onboarding-2/navigate-to-enable-notifications])
(log/error "successfully saved biometrics")))
:on-error #(log/error "failed to save biometrics"
{:key-uid key-uid
:error %})}))))
@@ -54,7 +54,7 @@
{:on-press (fn []
(when on-press
(on-press))
(rf/dispatch [:open-modal :enable-notifications]))
(rf/dispatch [:onboarding-2/navigate-to-enable-biometrics]))
:accessibility-label :continue-button
:customization-color profile-color
:container-style style/continue-button}
@@ -6,14 +6,14 @@
(defn- view-internal
[{:keys [theme]}]
[{:keys [theme title]}]
(let [logged-in? (rf/sub [:multiaccount/logged-in?])
has-profiles? (boolean (rf/sub [:profile/profiles-overview]))
root (if has-profiles? :profiles :intro)
light? (= theme :light)]
[quo/page-nav
{:type :title
:title "quo components preview"
:title title
:text-align :left
:icon-name :i/close
:right-side [{:icon-name (if light? :i/dark :i/light)
@@ -26,4 +26,4 @@
:descriptor descriptor
:blur? (:in-blur-view? @state)
:show-blur-background? (:in-blur-view? @state)}
[quo/step @state (:value @state)]])))
[quo/step (dissoc @state :value) (:value @state)]])))
@@ -487,7 +487,7 @@
(defn- main-screen
[]
[:<>
[common/navigation-bar]
[common/navigation-bar {:title "Quo components preview"}]
[rn/scroll-view {:style (style/main)}
(for [category (sort screens-categories)]
^{:key (first category)}
@@ -317,10 +317,10 @@
children)])
(defn- f-preview-container
[{:keys [state descriptor blur? blur-dark-only?
[{:keys [title state descriptor blur? blur-dark-only?
component-container-style
blur-container-style blur-view-props blur-height show-blur-background?]
:or {blur-height 200}}
:or {blur-height 200 title "quo component"}}
& children]
(let [theme (quo.theme/use-theme-value)]
(rn/use-effect (fn []
@@ -332,7 +332,7 @@
[rn/view
{:style {:top (safe-area/get-top)
:flex 1}}
[common/navigation-bar]
[common/navigation-bar {:title title}]
[rn/scroll-view
{:style (style/panel-basic)
:shows-vertical-scroll-indicator false}
@@ -0,0 +1,22 @@
(ns status-im2.contexts.status-im-preview.common.floating-button-page.style
(:require [quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
(defn container
[]
{:flex 1
:margin-top (safe-area/get-top)})
(def background-image
{:position :absolute
:top (- (safe-area/get-top))
:left 0
:right 0
:bottom 200})
(defn page-content
[height]
{:flex 1
:height height
:overflow :hidden
:background-color (colors/resolve-color :army 30)})
@@ -0,0 +1,55 @@
(ns status-im2.contexts.status-im-preview.common.floating-button-page.view
(:require [quo.core :as quo]
[re-frame.core :as rf]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.common.floating-button-page.view :as floating-button-page]
[status-im2.common.resources :as resources]
[status-im2.contexts.status-im-preview.common.floating-button-page.style :as style]))
(defn view
[]
(let [content-height (reagent/atom 450)
slide? (reagent/atom false)]
(fn []
[rn/view {:style (style/container)}
(when-not @slide?
[rn/image
{:style style/background-image
:source (resources/get-mock-image :dark-blur-bg)}])
[floating-button-page/view
{:header [quo/page-nav
{:type :title-description
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:right-side [{:icon-name :i/swap
:on-press #(swap! slide? not)}]
:background :blur
:icon-name :i/close
:on-press #(rf/dispatch [:navigate-back])}]
:footer (if @slide?
[quo/slide-button
{:track-text "We gotta slide"
:track-icon :face-id
:container-style {:z-index 2}
:customization-color :blue
:on-complete #(js/alert "button slid")}
"Save address"]
[quo/button
{:container-style {:z-index 2}
:on-press #(js/alert "button pressed")}
"Save address"])}
[rn/view {:style (style/page-content @content-height)}
[quo/text {:size :heading-1} "Page Content"]
[quo/input
{:auto-focus true
:value ""}]
[quo/button
{:type :outline
:on-press #(swap! content-height (fn [v] (+ v 10)))}
"increase height"]
[quo/button
{:type :outline
:on-press #(swap! content-height (fn [v] (- v 10)))}
"decrease height"]]]])))
@@ -0,0 +1,59 @@
(ns status-im2.contexts.status-im-preview.main
(:refer-clojure :exclude [filter])
(:require
[quo.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.contexts.quo-preview.common :as common]
[status-im2.contexts.status-im-preview.common.floating-button-page.view :as floating-button-page]
[status-im2.contexts.status-im-preview.style :as style]
[utils.re-frame :as rf]))
(def screens-categories
{:common [{:name :floating-button-page
:component floating-button-page/view}]})
(defn- category-view
[]
(let [open? (reagent/atom false)
on-press #(swap! open? not)]
(fn [category]
[rn/view {:style {:margin-vertical 8}}
[quo/dropdown
{:type :grey
:state (if @open? :active :default)
:on-press on-press}
(name (key category))]
(when @open?
(for [{category-name :name} (val category)]
^{:key category-name}
[quo/button
{:type :outline
:container-style {:margin-vertical 8}
:on-press #(rf/dispatch [:navigate-to category-name])}
(name category-name)]))])))
(defn- main-screen
[]
[:<>
[common/navigation-bar {:title "Status IM components"}]
[rn/scroll-view {:style (style/main)}
(for [category (sort screens-categories)]
^{:key (first category)}
[category-view category])]])
(def screens
(->> screens-categories
(map val)
flatten
(map (fn [subcategory]
(update-in subcategory
[:options :topBar]
merge
{:visible false})))))
(def main-screens
[{:name :status-im-preview
:options {:topBar {:visible false}
:insets {:top? true}}
:component main-screen}])
@@ -0,0 +1,10 @@
(ns status-im2.contexts.status-im-preview.style
(:require
[quo.foundations.colors :as colors]))
(defn main
[]
{:flex 1
:padding-bottom 8
:padding-horizontal 16
:background-color (colors/theme-colors colors/white colors/neutral-90)})
@@ -8,7 +8,7 @@
[reagent.core :as reagent]
[status-im2.common.qr-codes.view :as qr-codes]
[status-im2.common.resources :as resources]
[status-im2.common.standard-authentication.standard-auth.view :as standard-auth]
[status-im2.common.standard-authentication.core :as standard-auth]
[status-im2.contexts.syncing.setup-syncing.style :as style]
[status-im2.contexts.syncing.utils :as sync-utils]
[utils.datetime :as datetime]
@@ -119,7 +119,7 @@
(i18n/label :t/copy-qr)]])
(when-not (sync-utils/valid-connection-string? @code)
[rn/view {:style style/standard-auth}
[standard-auth/view
[standard-auth/slide-button
{:blur? true
:size :size-40
:track-text (i18n/label :t/slide-to-reveal-code)
@@ -3,11 +3,10 @@
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im2.common.resources :as resources]
[status-im2.contexts.wallet.account.tabs.dapps.style :as style]
[status-im2.contexts.wallet.common.empty-tab.view :as empty-tab]
[status-im2.contexts.wallet.common.temp :as temp]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.i18n :as i18n]))
(defn dapp-options
[]
@@ -23,13 +22,12 @@
(defn- view-internal
[{:keys [theme]}]
(let [dapps-list (temp/dapps-list {:on-press-icon #(rf/dispatch [:show-bottom-sheet
{:content dapp-options}])})]
(let [dapps-list []]
(if (empty? dapps-list)
[empty-tab/view
{:title (i18n/label :t/no-dapps)
:description (i18n/label :t/no-collectibles-description)
:placeholder? true}]
{:title (i18n/label :t/no-dapps)
:description (i18n/label :t/no-dapps-description)
:image (resources/get-themed-image :no-dapps theme)}]
[rn/view {:style style/dapps-container}
[rn/flat-list
{:data dapps-list
@@ -1,6 +1,8 @@
(ns status-im2.contexts.wallet.account.tabs.view
(:require
[quo.theme]
[react-native.core :as rn]
[status-im2.common.resources :as resources]
[status-im2.contexts.wallet.account.tabs.about.view :as about]
[status-im2.contexts.wallet.account.tabs.dapps.view :as dapps]
[status-im2.contexts.wallet.common.activity-tab.view :as activity]
@@ -10,8 +12,8 @@
[status-im2.contexts.wallet.common.token-value.view :as token-value]
[utils.i18n :as i18n]))
(defn view
[{:keys [selected-tab]}]
(defn- view-internal
[{:keys [selected-tab theme]}]
(case selected-tab
:assets [rn/flat-list
{:render-fn token-value/view
@@ -20,8 +22,13 @@
:collectibles [collectibles/view]
:activity [activity/view]
:permissions [empty-tab/view
{:title (i18n/label :t/no-permissions)
:description (i18n/label :t/no-collectibles-description)
:placeholder? true}]
{:title (i18n/label :t/no-permissions)
:description (i18n/label :t/no-collectibles-description)
:image (resources/get-image
(quo.theme/theme-value :no-permissions-light
:no-permissions-dark
theme))}]
:dapps [dapps/view]
[about/view]))
(def view (quo.theme/with-theme view-internal))
@@ -25,7 +25,6 @@
[{:id :assets :label (i18n/label :t/assets) :accessibility-label :assets-tab}
{:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}
{:id :permissions :label (i18n/label :t/permissions) :accessibility-label :permissions}
{:id :dapps :label (i18n/label :t/dapps) :accessibility-label :dapps}
{:id :about :label (i18n/label :t/about) :accessibility-label :about}])
@@ -0,0 +1,38 @@
(ns status-im2.contexts.wallet.add-address-to-watch.component-spec
(:require
[re-frame.core :as re-frame]
[status-im2.contexts.wallet.add-address-to-watch.view :as add-address-to-watch]
[test-helpers.component :as h]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(h/describe "select address for watch only account"
(h/test "validation messages show for already used addressed"
(setup-subs {:wallet/scanned-address nil
:wallet/addresses (set
["0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"])
:profile/customization-color :blue})
(h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text
(h/get-by-label-text :add-address-to-watch)
"0x12E838Ae1f769147b12956485dc56e57138f3AC8")
(h/is-truthy (h/get-by-translation-text :address-already-in-use))))
(h/test "validation messages show for invalid address"
(setup-subs {:wallet/scanned-address nil
:wallet/addresses (set
["0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"])
:profile/customization-color :blue})
(h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "0x12E838Ae1f769147b")
(h/is-truthy (h/get-by-translation-text :invalid-address)))
@@ -3,18 +3,7 @@
(def container
{:flex 1})
(def input
{:margin-right 12
:flex 1})
(def data-item
{:margin-horizontal 20
:padding-vertical 8
:padding-horizontal 12})
(defn button-container
[bottom]
{:position :absolute
:bottom (+ bottom 12)
:left 20
:right 20})
@@ -3,7 +3,6 @@
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[re-frame.core :as re-frame]
[react-native.core :as rn]
[reagent.core :as reagent]
@@ -14,13 +13,12 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- view-internal
(defn view
[]
(let [{:keys [address]} (rf/sub [:get-screen-params])
number-of-accounts (count (rf/sub [:profile/wallet-accounts]))
account-name (reagent/atom (i18n/label :t/default-account-name
{:number (inc number-of-accounts)}))
address-title (i18n/label :t/watch-address)
account-color (reagent/atom (rand-nth colors/account-colors))
account-emoji (reagent/atom (emoji-picker.utils/random-emoji))
on-change-name #(reset! account-name %)
@@ -40,8 +38,9 @@
:on-change-name on-change-name
:on-change-color on-change-color
:on-change-emoji on-change-emoji
:watch-only? true
:bottom-action? true
:bottom-action-label :t/create-account
:bottom-action-label :t/add-watched-address
:bottom-action-props {:customization-color @account-color
:disabled? (string/blank? @account-name)
:on-press #(re-frame/dispatch [:navigate-to
@@ -51,11 +50,9 @@
:right-icon :i/advanced
:icon-right? true
:emoji @account-emoji
:title address-title
:title (i18n/label :t/watched-address)
:subtitle address
:status :default
:size :default
:container-style style/data-item
:on-press #(js/alert "To be implemented")}]]])))
(def view (quo.theme/with-theme view-internal))
@@ -5,13 +5,24 @@
:margin-top 12
:margin-bottom 20})
(def input-container
{:flex-direction :row
:padding-horizontal 20
:align-items :flex-end})
(def button-container
{:position :absolute
:bottom 22
:left 20
:right 20})
(def scan
{:align-self
:flex-end})
(def input
{:flex 1
:margin-right 12})
(def input-container
{:flex-direction :row
:margin-horizontal 20})
(def info-message
{:margin-top 8
:margin-left 20})
@@ -2,51 +2,114 @@
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.theme :as quo.theme]
[re-frame.core :as re-frame]
[react-native.clipboard :as clipboard]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.contexts.wallet.add-address-to-watch.style :as style]
[status-im2.contexts.wallet.common.validation :as validation]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view-internal
(defn validate-message
[addresses]
(fn [s]
(cond
(or (= s nil) (= s "")) nil
(contains? addresses s) (i18n/label :t/address-already-in-use)
(not (or (validation/eth-address? s)
(validation/ens-name? s))) (i18n/label :t/invalid-address)
:else nil)))
(defn- address-input
[{:keys [input-value validation-msg validate
clear-input]}]
(let [scanned-address (rf/sub [:wallet/scanned-address])
empty-input? (and (string/blank? @input-value)
(string/blank? scanned-address))
on-change-text (fn [new-text]
(reset! validation-msg (validate new-text))
(reset! input-value new-text)
(when (and scanned-address (not= scanned-address new-text))
(rf/dispatch [:wallet/clean-scanned-address])))
paste-on-input #(clipboard/get-string
(fn [clipboard-text]
(on-change-text clipboard-text)))]
(rn/use-effect (fn []
(when-not (string/blank? scanned-address)
(on-change-text scanned-address)))
[scanned-address])
[rn/view
{:style style/input-container}
[quo/input
{:accessibility-label :add-address-to-watch
:placeholder (i18n/label :t/address-placeholder)
:container-style style/input
:label (i18n/label :t/eth-or-ens)
:auto-capitalize :none
:multiline? true
:on-clear clear-input
:return-key-type :done
:clearable? (not empty-input?)
:on-change-text on-change-text
:button (when empty-input?
{:on-press paste-on-input
:text (i18n/label :t/paste)})
:value @input-value}]
[quo/button
{:type :outline
:on-press (fn []
(rn/dismiss-keyboard!)
(rf/dispatch [:open-modal :scan-address]))
:container-style style/scan
:size 40
:icon-only? true}
:i/scan]]))
(defn view
[]
(let [input-value (reagent/atom "")
(let [addresses (rf/sub [:wallet/addresses])
input-value (reagent/atom nil)
validate (validate-message (set addresses))
validation-msg (reagent/atom (validate
@input-value))
clear-input (fn []
(reset! input-value nil)
(reset! validation-msg nil)
(rf/dispatch [:wallet/clean-scanned-address]))
customization-color (rf/sub [:profile/customization-color])]
(rf/dispatch [:wallet/clean-scanned-address])
(fn []
[rn/view
{:style {:flex 1}}
[quo/page-nav
{:type :no-title
:icon-name :i/close
:on-press #(rf/dispatch [:navigate-back])}]
:on-press (fn []
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:navigate-back]))}]
[quo/text-combinations
{:container-style style/header-container
:title (i18n/label :t/add-address)
:description (i18n/label :t/enter-eth)}]
[rn/view {:style style/input-container}
[quo/input
{:label (i18n/label :t/eth-or-ens)
:button {:on-press (fn [] (clipboard/get-string #(reset! input-value %)))
:text (i18n/label :t/paste)}
:placeholder (str "0x123abc... " (string/lower-case (i18n/label :t/or)) " bob.eth")
:container-style {:margin-right 12
:flex 1}
:weight :monospace
:on-change-text #(reset! input-value %)
:default-value @input-value}]
[quo/button
{:icon-only? true
:type :outline} :i/scan]]
[:f> address-input
{:input-value input-value
:validate validate
:validation-msg validation-msg
:clear-input clear-input}]
(when @validation-msg
[quo/info-message
{:accessibility-label :error-message
:size :default
:icon :i/info
:type :error
:style style/info-message}
@validation-msg])
[quo/button
{:customization-color customization-color
:disabled? (clojure.string/blank? @input-value)
:on-press #(re-frame/dispatch [:navigate-to
:confirm-address-to-watch
{:address @input-value}])
:disabled? (string/blank? @input-value)
:on-press #(rf/dispatch [:navigate-to
:confirm-address-to-watch
{:address @input-value}])
:container-style style/button-container}
(i18n/label :t/continue)]])))
(def view (quo.theme/with-theme view-internal))
@@ -1,9 +1,10 @@
(ns status-im2.contexts.wallet.common.activity-tab.view
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im2.common.resources :as resources]
[status-im2.contexts.wallet.common.empty-tab.view :as empty-tab]
[status-im2.contexts.wallet.common.temp :as temp]
[utils.i18n :as i18n]))
(defn activity-item
@@ -14,15 +15,17 @@
(merge {:on-press #(js/alert "Item pressed")}
item)]])
(defn view
[]
(let [activity-list temp/activity-list]
(defn- view-internal
[{:keys [theme]}]
(let [activity-list []]
(if (empty? activity-list)
[empty-tab/view
{:title (i18n/label :t/no-activity)
:description (i18n/label :t/empty-tab-description)
:placeholder? true}]
{:title (i18n/label :t/no-activity)
:description (i18n/label :t/empty-tab-description)
:image (resources/get-themed-image :no-activity theme)}]
[rn/flat-list
{:data activity-list
:style {:flex 1}
:render-fn activity-item}])))
(def view (quo.theme/with-theme view-internal))
@@ -1,19 +1,21 @@
(ns status-im2.contexts.wallet.common.collectibles-tab.view
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im2.common.resources :as resources]
[status-im2.contexts.wallet.common.empty-tab.view :as empty-tab]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(defn- view-internal
[{:keys [theme]}]
(let [collectible-list (rf/sub [:wallet/collectibles])]
(if (empty? collectible-list)
[empty-tab/view
{:title (i18n/label :t/no-collectibles)
:description (i18n/label :t/no-collectibles-description)
:placeholder? true}]
{:title (i18n/label :t/no-collectibles)
:description (i18n/label :t/no-collectibles-description)
:image (resources/get-themed-image :no-collectibles theme)}]
[rn/flat-list
{:data collectible-list
:style {:flex 1}
@@ -25,3 +27,5 @@
:on-press #(rf/dispatch
[:navigate-to
:wallet-collectible])}])}])))
(def view (quo.theme/with-theme view-internal))
@@ -1,6 +1,5 @@
(ns status-im2.contexts.wallet.common.screen-base.create-or-edit-account.view
(:require [quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im2.constants :as constants]
@@ -8,12 +7,12 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- view-internal
(defn view
[{:keys [margin-top? page-nav-right-side account-name account-color account-emoji on-change-name
on-change-color
on-change-emoji on-focus on-blur section-label bottom-action?
bottom-action-label bottom-action-props
custom-bottom-action]} & children]
custom-bottom-action watch-only?]} & children]
(let [{:keys [top bottom]} (safe-area/get-insets)
margin-top (if (false? margin-top?) 0 top)
{window-width :width} (rn/get-window)]
@@ -36,7 +35,7 @@
{:customization-color account-color
:size 80
:emoji account-emoji
:type :default}]
:type (if watch-only? :watch-only :default)}]
[quo/button
{:size 32
:type :grey
@@ -81,5 +80,3 @@
:type :primary}
bottom-action-props)
(i18n/label bottom-action-label)])])]))
(def view (quo.theme/with-theme view-internal))
@@ -1,13 +1,16 @@
(ns status-im2.contexts.wallet.common.sheets.account-options.view
(:require [quo.core :as quo]
[quo.foundations.colors :as colors]
quo.theme
[react-native.clipboard :as clipboard]
[react-native.core :as rn]
[status-im2.contexts.wallet.common.sheets.account-options.style :as style]
[status-im2.contexts.wallet.common.temp :as temp]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(defn- view-internal
[{:keys [theme]}]
(let [{:keys [name color emoji address]} (rf/sub [:wallet/current-viewing-account])]
[:<>
[quo/drawer-top
@@ -26,7 +29,13 @@
:on-press #(rf/dispatch [:navigate-to :wallet-edit-account])}
{:icon :i/copy
:accessibility-label :copy-address
:label (i18n/label :t/copy-address)}
:label (i18n/label :t/copy-address)
:on-press (fn []
(rf/dispatch [:toasts/upsert
{:icon :i/correct
:icon-color (colors/resolve-color :success theme)
:text (i18n/label :t/address-copied)}])
(clipboard/set-string address))}
{:icon :i/share
:accessibility-label :share-account
:label (i18n/label :t/share-account)}
@@ -42,3 +51,5 @@
{:data temp/other-accounts
:render-fn (fn [account] [quo/account-item {:account-props account}])
:style {:margin-horizontal 8}}]]))
(def view (quo.theme/with-theme view-internal))
@@ -22,7 +22,9 @@
[quo/button {:on-press #(rf/dispatch [:navigate-to :wallet-create-account])}
"Create Account"]
[quo/button {:on-press #(rf/dispatch [:navigate-to :wallet-saved-addresses])}
"Saved Addresses"]])
"Saved Addresses"]
[quo/button {:on-press #(rf/dispatch [:navigate-to :wallet-send-input-amount])}
"Send: input amount"]])
(defn wallet-overview-state
[networks]
@@ -0,0 +1,5 @@
(ns status-im2.contexts.wallet.common.validation
(:require [status-im2.constants :as constants]))
(defn ens-name? [s] (re-find constants/regx-ens s))
(defn eth-address? [s] (re-find constants/regx-address s))
@@ -7,7 +7,7 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im2.common.standard-authentication.standard-auth.view :as standard-auth]
[status-im2.common.standard-authentication.core :as standard-auth]
[status-im2.constants :as constants]
[status-im2.contexts.emoji-picker.utils :as emoji-picker.utils]
[status-im2.contexts.wallet.common.utils :as utils]
@@ -114,7 +114,7 @@
{:list-type :settings
:label (i18n/label :t/origin)
:data (get-keypair-data primary-name @derivation-path @account-color)}]
[standard-auth/view
[standard-auth/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-create-account)
:customization-color @account-color
@@ -30,7 +30,9 @@
(defn- new-account-card-data
[]
{:customization-color (rf/sub [:profile/customization-color])
:on-press #(rf/dispatch [:show-bottom-sheet {:content new-account}])
:on-press #(rf/dispatch [:navigate-to :wallet-send-input-amount
] ;[:show-bottom-sheet {:content new-account}]
)
:type :add-account})
(def tabs-data
@@ -53,10 +55,7 @@
[common.top-nav/view]
[rn/view {:style style/overview-container}
[quo/wallet-overview (temp/wallet-overview-state networks)]]
[rn/pressable
{:on-long-press #(rf/dispatch [:show-bottom-sheet
{:content temp/wallet-temporary-navigation}])}
[quo/wallet-graph {:time-frame :empty}]]
[quo/wallet-graph {:time-frame :empty}]
[rn/flat-list
{:style style/accounts-list
:content-container-style style/accounts-list-container
@@ -0,0 +1,80 @@
(ns status-im2.contexts.wallet.send.input-amount.component-spec
(:require
[re-frame.core :as re-frame]
[status-im2.contexts.wallet.send.input-amount.view :as input-amount]
[test-helpers.component :as h]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(def sub-mocks
{:profile/profile {:currency :usd}
:wallet/network-details [{:source 525
:short-name "eth"
:network-name :ethereum
:chain-id 1
:related-chain-id 5}]})
(h/describe "Send > input amount screen"
(h/test "Default render"
(setup-subs sub-mocks)
(h/render [input-amount/view {}])
(h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "ETH"))
(h/is-truthy (h/get-by-text "$0.00"))
(h/is-disabled (h/get-by-label-text :button-one)))
(h/test "Fill token input and confirm"
(setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render [input-amount/view {:on-confirm on-confirm}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-3))
(h/fire-event :press (h/query-by-label-text :keyboard-key-.))
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$1234.50")))
(h/is-truthy (h/get-by-label-text :button-one))
(h/fire-event :press (h/get-by-label-text :button-one))
(h/was-called on-confirm)))
(h/test "Try to fill more than limit"
(setup-subs sub-mocks)
(h/render [input-amount/view {}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$290.00")))
(h/fire-event :press (h/query-by-label-text :keyboard-key-backspace))
(h/fire-event :press (h/query-by-label-text :keyboard-key-8))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$2850.00"))))
(h/test "Switch from crypto to fiat and check limit"
(setup-subs sub-mocks)
(h/render [input-amount/view {}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-0))
(h/wait-for #(h/is-truthy (h/get-by-text "$200.00")))
(h/fire-event :press (h/query-by-label-text :reorder))
(h/wait-for #(h/is-truthy (h/get-by-text "2.00 ETH")))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "205.50 ETH")))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "205.50 ETH")))))
@@ -0,0 +1,8 @@
(ns status-im2.contexts.wallet.send.input-amount.style)
(def screen
{:flex 1})
(def input-container
{:padding-top 12
:padding-bottom 0})
@@ -0,0 +1,93 @@
(ns status-im2.contexts.wallet.send.input-amount.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.contexts.wallet.send.input-amount.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- make-limit-label
[{:keys [amount currency]}]
(str amount " " (string/upper-case (name currency))))
(defn- make-new-input
[current v]
(let [dot "."
max-length 12
length-owerflow? (>= (count current) max-length)
ignore-extra-dot? (and (= v dot) (string/includes? current dot))
ignore-value? (or
ignore-extra-dot?
length-owerflow?)]
(if ignore-value?
current
(str current v))))
(defn- view-internal
[_]
(let [token :eth
{:keys [currency]} (rf/sub [:profile/profile])
networks (rf/sub [:wallet/network-details])
conversion-rate 10
limit-crypto 100000000
limit-fiat (* limit-crypto conversion-rate)
input-value (reagent/atom "")
current-limit (reagent/atom {:amount limit-crypto
:currency token})
handle-swap (fn [crypto?]
(let [num-value (parse-double @input-value)]
(reset! current-limit (if crypto?
{:amount limit-crypto
:currency token}
{:amount limit-fiat
:currency currency}))
(when (> num-value (:amount @current-limit))
(reset! input-value ""))))
handle-keyboard-press (fn [v]
(let [current-value @input-value
new-value (make-new-input current-value v)
num-value (parse-double new-value)]
(when (<= num-value (:amount @current-limit))
(reset! input-value new-value))))
handle-delete (fn [_]
(swap! input-value #(subs % 0 (dec (count %)))))]
(fn [{:keys [on-confirm]
:or {on-confirm #(js/alert "Confirmed")}}]
(let [limit-label (make-limit-label @current-limit)]
[rn/view
{:style style/screen}
[quo/page-nav
{:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])
:right-side :account-switcher
:account-switcher {:customization-color :yellow
:emoji "🎮"
:on-press #(js/alert "Switch account")}}]
[quo/token-input
{:container-style style/input-container
:token token
:currency currency
:networks networks
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:on-swap handle-swap}]
;; Network routing content to be added
[rn/scroll-view]
[quo/bottom-actions
{:actions :1-action
:button-one-label (i18n/label :t/confirm)
:button-one-props {:disabled? (empty? @input-value)
:on-press on-confirm}}]
[quo/numbered-keyboard
{:left-action :dot
:delete-key? true
:on-press handle-keyboard-press
:on-delete handle-delete}]]))))
(def view (quo.theme/with-theme view-internal))
@@ -48,7 +48,7 @@
(fn []
(let [scanned-address (rf/sub [:wallet/scanned-address])
send-address (rf/sub [:wallet/send-address])
valid-ens-or-address? (boolean (rf/sub [:wallet/valid-ens-or-address?]))]
valid-ens-or-address? (rf/sub [:wallet/valid-ens-or-address?])]
[quo/address-input
{:on-focus #(reset! input-focused? true)
:on-blur #(reset! input-focused? false)
+4 -1
View File
@@ -1,5 +1,8 @@
(ns status-im2.core-spec
(:require
[status-im2.common.floating-button-page.component-spec]
[status-im2.contexts.chat.messages.content.audio.component-spec]
[status-im2.contexts.communities.actions.community-options.component-spec]
[status-im2.contexts.wallet.create-account.edit-derivation-path.component-spec]))
[status-im2.contexts.wallet.add-address-to-watch.component-spec]
[status-im2.contexts.wallet.create-account.edit-derivation-path.component-spec]
[status-im2.contexts.wallet.send.input-amount.component-spec]))
+27 -9
View File
@@ -32,6 +32,7 @@
[status-im2.contexts.shell.activity-center.view :as activity-center]
[status-im2.contexts.shell.jump-to.view :as shell]
[status-im2.contexts.shell.share.view :as share]
[status-im2.contexts.status-im-preview.main :as status-im-preview]
[status-im2.contexts.syncing.find-sync-code.view :as find-sync-code]
[status-im2.contexts.syncing.how-to-pair.view :as how-to-pair]
[status-im2.contexts.syncing.scan-sync-code-page.view :as scan-sync-code-page]
@@ -46,8 +47,9 @@
[status-im2.contexts.wallet.create-account.select-keypair.view :as wallet-select-keypair]
[status-im2.contexts.wallet.create-account.view :as wallet-create-account]
[status-im2.contexts.wallet.edit-account.view :as wallet-edit-account]
[status-im2.contexts.wallet.saved-address.view :as wallet-saved-address]
[status-im2.contexts.wallet.saved-addresses.view :as wallet-saved-addresses]
[status-im2.contexts.wallet.scan-account.view :as scan-address]
[status-im2.contexts.wallet.send.input-amount.view :as wallet-send-input-amount]
[status-im2.contexts.wallet.send.select-address.view :as wallet-select-address]
[status-im2.navigation.options :as options]
[status-im2.navigation.transitions :as transitions]))
@@ -161,11 +163,15 @@
:component create-password/create-password}
{:name :enable-biometrics
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations transitions/push-animations-for-transparent-background
:popGesture false}
:component enable-biometrics/enable-biometrics}
:options {:theme :dark
:layout options/onboarding-transparent-layout
:animations (merge transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)
:popGesture false
:modalPresentationStyle :overCurrentContext
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}}
:component enable-biometrics/view}
{:name :generating-keys
:options {:theme :dark
@@ -279,8 +285,13 @@
:options {:insets {:top? true}}
:component wallet-create-account/view}
{:name :wallet-saved-address
:component wallet-saved-address/view}
{:name :wallet-saved-addresses
:component wallet-saved-addresses/view}
{:name :wallet-send-input-amount
:options {:modalPresentationStyle :overCurrentContext
:insets {:top? true}}
:component wallet-send-input-amount/view}
{:name :wallet-select-address
:options {:modalPresentationStyle :overCurrentContext}
@@ -301,4 +312,11 @@
quo.preview/screens)
(when config/quo-preview-enabled?
quo.preview/main-screens)))
quo.preview/main-screens)
(when config/quo-preview-enabled?
status-im-preview/screens)
(when config/quo-preview-enabled?
status-im-preview/main-screens)))
+4 -1
View File
@@ -5,6 +5,7 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
schema.view
[status-im.bottom-sheet.sheets :as bottom-sheets-old]
[status-im.ui.screens.popover.views :as popover]
[status-im.ui.screens.profile.visibility-status.views :as visibility-status-views]
@@ -71,7 +72,9 @@
[bottom-sheet-screen/view {:content component}]
[component])]
(when js/goog.DEBUG
[reloader/reload-view])]))))
[:<>
[reloader/reload-view]
[schema.view/view]])]))))
(def bottom-sheet
(reagent/reactify-component
+9 -7
View File
@@ -2,6 +2,7 @@
(:require
["react-native" :refer (DevSettings LogBox)]
[react-native.platform :as platform]
[status-im2.setup.schema :as schema]
[utils.re-frame :as rf]))
;; Ignore all logs, because there are lots of temporary warnings when developing and hot reloading
@@ -45,10 +46,11 @@
:group-chats/extract-membership-signature
:utils/dispatch-later
:json-rpc/call})
(when (and js/goog.DEBUG platform/ios? DevSettings)
;;on Android this method doesn't work
(when-let [nm (.-_nativeModule DevSettings)]
;;there is a bug in RN, so we have to enable it first and then disable
(.setHotLoadingEnabled ^js nm true)
(js/setTimeout #(.setHotLoadingEnabled ^js nm false) 1000))))
(when ^:boolean js/goog.DEBUG
(schema/setup!)
(when (and platform/ios? DevSettings)
;;on Android this method doesn't work
(when-let [nm (.-_nativeModule DevSettings)]
;;there is a bug in RN, so we have to enable it first and then disable
(.setHotLoadingEnabled ^js nm true)
(js/setTimeout #(.setHotLoadingEnabled ^js nm false) 1000)))))

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