Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88c56a4f2f | ||
|
|
91300b9ff8 | ||
|
|
d93b20242a | ||
|
|
0a43e5c97a |
+4
-12
@@ -16,18 +16,10 @@
|
||||
: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.string string
|
||||
clojure.set set
|
||||
clojure.walk walk
|
||||
taoensso.timbre log}}
|
||||
:deprecated-namespace {:level :warning}
|
||||
:docstring-blank {:level :error}
|
||||
:equals-true {:level :error}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
{:lint-as {malli.experimental/defn schema.core/defn}
|
||||
:linters {:unresolved-symbol {:exclude [(malli.core/=>)]}}}
|
||||
@@ -36,7 +36,6 @@
|
||||
"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?]]}]
|
||||
|
||||
@@ -311,14 +311,13 @@ endef
|
||||
lint: export TARGET := clojure
|
||||
lint: export CLJ_LINTER_PRINT_WARNINGS ?= false
|
||||
lint: ##@test Run code style checks
|
||||
@sh scripts/lint/re-frame-in-quo-components.sh && \
|
||||
sh scripts/lint/direct-require-component-outside-quo.sh && \
|
||||
sh scripts/lint/require-i18n-resource-first.sh && \
|
||||
@sh scripts/lint-re-frame-in-quo-components.sh && \
|
||||
sh scripts/lint-direct-require-component-outside-quo.sh && \
|
||||
clj-kondo --config .clj-kondo/config.edn --cache false --fail-level error --lint src $(if $(filter $(CLJ_LINTER_PRINT_WARNINGS),true),,| grep -v ': warning: ') && \
|
||||
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
|
||||
scripts/lint/translations.clj && \
|
||||
scripts/lint_translations.clj && \
|
||||
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES && \
|
||||
sh scripts/lint/trailing-newline.sh && \
|
||||
sh scripts/lint-trailing-newline.sh && \
|
||||
node_modules/.bin/prettier --write .
|
||||
|
||||
# NOTE: We run the linter twice because of https://github.com/kkinnear/zprint/issues/271
|
||||
@@ -328,7 +327,7 @@ lint-fix: ##@test Run code style checks and fix issues
|
||||
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
|
||||
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
|
||||
clojure-lsp --ns-exclude-regex ".*/src/status_im2/core\.cljs$$" clean-ns && \
|
||||
sh scripts/lint/trailing-newline.sh --fix && \
|
||||
sh scripts/lint-trailing-newline.sh --fix && \
|
||||
node_modules/.bin/prettier --write .
|
||||
|
||||
shadow-server: export TARGET := clojure
|
||||
|
||||
+12
-48
@@ -45,20 +45,11 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "borkdude/dynaload/0.3.5/dynaload-0.3.5",
|
||||
"path": "borkdude/edamame/1.1.17/edamame-1.1.17",
|
||||
"host": "https://repo.clojars.org",
|
||||
"jar": {
|
||||
"sha1": "accd696ba364b850b4d92e38f5a34d0e828a0ad1",
|
||||
"sha256": "0k62m1f29xfh3cp67w7kcvkp5aj35simi8kf95ycvkmgp76w11q8"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "borkdude/edamame/1.3.23/edamame-1.3.23",
|
||||
"host": "https://repo.clojars.org",
|
||||
"jar": {
|
||||
"sha1": "254d023e97ed438f0f44532b5a06d928d031ede4",
|
||||
"sha256": "0l7mxza2nimslhg0qh6jp7gmb8sd6l89fk5d1zvzq2xskscq2vly"
|
||||
"sha1": "9087f7abf0104e0354d7db7fc4576608eac558f4",
|
||||
"sha256": "1n1872i240lakn4pzsag4grf7bv7lcsipmqllxd9m4k1zp3dgla1"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -90,20 +81,20 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "cider/cider-nrepl/0.25.3/cider-nrepl-0.25.3",
|
||||
"path": "cider/cider-nrepl/0.29.0/cider-nrepl-0.29.0",
|
||||
"host": "https://repo.clojars.org",
|
||||
"jar": {
|
||||
"sha1": "5ae0efd9377a5e60c084bdaf4a2ce094f759ce23",
|
||||
"sha256": "0drxf9nm23i1pcgrkwbcr09msq37csilzww38709add0hz8spjhq"
|
||||
"sha1": "45f6034b26a14138e74145b7a4059628c0fedcd1",
|
||||
"sha256": "1dy1l6y8cb8xiqq97a4lf8giyiicq4wfl4s2lxn5fb6614cjxqx2"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "cider/piggieback/0.4.1/piggieback-0.4.1",
|
||||
"path": "cider/piggieback/0.5.2/piggieback-0.5.2",
|
||||
"host": "https://repo.clojars.org",
|
||||
"jar": {
|
||||
"sha1": "0a02a3e2ecd7a126ab60d8a44793342f20ced79b",
|
||||
"sha256": "142vl5np33akcrnn6pksi0rjfsmmi528villxsj6cwcndvybiw4m"
|
||||
"sha1": "ecfd5c286a85db3f059e75c37fca5722d9e26f79",
|
||||
"sha256": "1ps9yf3cxmlm447hqkidjb5xry90n0wl3jk0jn28fagq31lzylkl"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -548,24 +539,6 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"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",
|
||||
@@ -755,15 +728,6 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"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",
|
||||
@@ -945,11 +909,11 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0",
|
||||
"path": "refactor-nrepl/refactor-nrepl/3.6.0/refactor-nrepl-3.6.0",
|
||||
"host": "https://repo.clojars.org",
|
||||
"jar": {
|
||||
"sha1": "6bc3441afc94f7ca024e41a864ca75e05df7e207",
|
||||
"sha256": "0w8hax99y98l53mixxzx2ja0vcnhjv8dnsaz1zj3vqk775ns5w6i"
|
||||
"sha1": "2b3bb82da53b5db9c2b2aa298417816b81d0ed97",
|
||||
"sha256": "1ysqabmlnghki6x0636zngxza2d83c85276wp9ma9wk183mkv52a"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@ 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/dynaload/0.3.5/dynaload-0.3.5.jar
|
||||
borkdude/edamame/1.3.23/edamame-1.3.23.jar
|
||||
borkdude/edamame/1.1.17/edamame-1.1.17.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.25.3/cider-nrepl-0.25.3.jar
|
||||
cider/piggieback/0.4.1/piggieback-0.4.1.jar
|
||||
cider/cider-nrepl/0.29.0/cider-nrepl-0.29.0.jar
|
||||
cider/piggieback/0.5.2/piggieback-0.5.2.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
|
||||
@@ -59,8 +58,6 @@ 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
|
||||
@@ -82,7 +79,6 @@ 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
|
||||
@@ -103,7 +99,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/2.5.0/refactor-nrepl-2.5.0.jar
|
||||
refactor-nrepl/refactor-nrepl/3.6.0/refactor-nrepl-3.6.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
|
||||
|
||||
@@ -67,12 +67,9 @@ stdenv.mkDerivation {
|
||||
'';
|
||||
buildPhase = ''
|
||||
# Assemble CLASSPATH from available clojure dependencies.
|
||||
# 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' ':')"
|
||||
# We append 'src' so it can find the local sources.
|
||||
export CLASS_PATH="$(find ${deps.clojure} \
|
||||
-iname '*.jar' | tr '\n' ':')src"
|
||||
|
||||
# target must be one of the builds defined in shadow-cljs.edn
|
||||
java -cp "$CLASS_PATH" clojure.main \
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
if rg --quiet --multiline '^\(ns.*\n^\s*\(:require\n(^\s*(;|#_).*\n)*(^\s*\[status-im2\.setup\.i18n-resources\W)' src/status_im2/core.cljs; then
|
||||
exit 0
|
||||
elif [ $? -eq 1 ]; then
|
||||
echo "status-im2.setup.i18n-resources must be loaded first (be the first one in ns :require form) in status-im2.core"
|
||||
echo "For more info, check the comment here https://github.com/status-im/status-mobile/pull/17618#discussion_r1361275489"
|
||||
else
|
||||
exit $?
|
||||
fi
|
||||
+2
-18
@@ -9,7 +9,6 @@
|
||||
[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"]
|
||||
@@ -51,7 +50,6 @@
|
||||
: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
|
||||
@@ -73,10 +71,6 @@
|
||||
: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
|
||||
@@ -110,15 +104,13 @@
|
||||
: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
|
||||
{schema.core/throw-on-error? true
|
||||
status-im2.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
|
||||
{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"
|
||||
@@ -147,17 +139,9 @@
|
||||
:compiler-options {:optimizations :simple
|
||||
:source-map false}}
|
||||
:component-test {:target :npm-module
|
||||
: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]
|
||||
:entries [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,22 +3,18 @@
|
||||
[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"
|
||||
(render [step/view {} nil])
|
||||
(h/render [step/view {} nil])
|
||||
(-> (h/expect (h/query-by-label-text :step-counter))
|
||||
(h/is-truthy)))
|
||||
|
||||
(h/test "renders step with a string value"
|
||||
(render [step/view {} "1"])
|
||||
(h/render [step/view {} "1"])
|
||||
(-> (h/expect (h/get-by-text "1"))
|
||||
(h/is-truthy)))
|
||||
|
||||
(h/test "renders step with an integer value"
|
||||
(render [step/view {} 1])
|
||||
(h/render [step/view {} 1])
|
||||
(-> (h/expect (h/get-by-text "1"))
|
||||
(h/is-truthy))))
|
||||
|
||||
@@ -2,24 +2,10 @@
|
||||
(:require
|
||||
[quo.components.counter.step.style :as style]
|
||||
[quo.components.markdown.text :as text]
|
||||
quo.theme
|
||||
[quo.theme :as 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)
|
||||
@@ -37,9 +23,6 @@
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :label
|
||||
:style {:color (style/text-color type theme)}}
|
||||
label]]))
|
||||
:style {:color (style/text-color type theme)}} label]]))
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal ?schema)))
|
||||
(def view (theme/with-theme view-internal))
|
||||
|
||||
@@ -3,17 +3,22 @@
|
||||
[quo.components.icon :as quo.icons]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[quo.theme :as theme]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn get-color
|
||||
[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)))
|
||||
(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 view-internal
|
||||
(defn get-color
|
||||
[k]
|
||||
(get-in themes [(theme/get-theme) k]))
|
||||
|
||||
(defn info-message
|
||||
"[info-message opts \"message\"]
|
||||
opts
|
||||
{:type :default/:success/:error
|
||||
@@ -22,11 +27,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 theme icon text-color icon-color no-icon-color? style accessibility-label]} message]
|
||||
[{:keys [type size icon text-color icon-color no-icon-color? style]} 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 theme))
|
||||
text-color (or text-color (get-color type))
|
||||
icon-color (or icon-color text-color)]
|
||||
[rn/view
|
||||
{:style (merge {:flex-direction :row
|
||||
@@ -37,10 +42,7 @@
|
||||
:no-color no-icon-color?
|
||||
:size icon-size}]
|
||||
[text/text
|
||||
{: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))
|
||||
{:size size
|
||||
:weight weight
|
||||
:style {:color text-color
|
||||
:margin-horizontal 4}} message]]))
|
||||
|
||||
@@ -22,10 +22,9 @@
|
||||
(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-with-theme-provider [list/view
|
||||
{:type :step
|
||||
:step-number 1}]
|
||||
:dark)
|
||||
(h/render [list/view
|
||||
{:type :step
|
||||
:step-number 1}])
|
||||
(h/is-truthy (h/get-by-label-text :step-counter)))
|
||||
|
||||
(h/test "renders decription with a context tag component and description after the tag"
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
(ns quo.components.tags.summary-tag.component-spec
|
||||
(:require
|
||||
[quo.components.tags.summary-tag.view :as summary-tag]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Summary tag component tests"
|
||||
(h/test "Basic render"
|
||||
(h/render [summary-tag/view])
|
||||
(h/is-truthy (h/query-by-label-text :container)))
|
||||
|
||||
(h/test "Account view render"
|
||||
(h/render [summary-tag/view
|
||||
{:type :account
|
||||
:label "Alice's Account"
|
||||
:customization-color "#ff0000"
|
||||
:emoji "🎉"}])
|
||||
(h/is-truthy (h/get-by-text "Alice's Account")))
|
||||
|
||||
(h/test "Network view render"
|
||||
(h/render [summary-tag/view
|
||||
{:type :network
|
||||
:label "Ethereum Network"
|
||||
:image-source "path/to/ethereum-logo.png"}])
|
||||
(h/is-truthy (h/get-by-text "Ethereum Network")))
|
||||
|
||||
(h/test "Saved address view render"
|
||||
(h/render [summary-tag/view
|
||||
{:type :saved-address
|
||||
:label "Charlie's Wallet"
|
||||
:customization-color "#00ff00"}])
|
||||
(h/is-truthy (h/get-by-text "Charlie's Wallet")))
|
||||
|
||||
(h/test "Collectible view render"
|
||||
(h/render [summary-tag/view
|
||||
{:type :collectible
|
||||
:label "Rare Artifact"
|
||||
:image-source "path/to/artifact-image.png"}])
|
||||
(h/is-truthy (h/get-by-text "Rare Artifact")))
|
||||
|
||||
(h/test "User view render"
|
||||
(h/render [summary-tag/view
|
||||
{:type :user
|
||||
:label "Bob Smith"
|
||||
:image-source "path/to/profile-pic.png"
|
||||
:customization-color "#0000ff"}])
|
||||
(h/is-truthy (h/get-by-text "Bob Smith")))
|
||||
|
||||
(h/test "Token view render"
|
||||
(h/render [summary-tag/view
|
||||
{:type :token
|
||||
:label "1,000 SNT"
|
||||
:image-source "path/to/token-image.png"}])
|
||||
(h/is-truthy (h/get-by-text "1,000 SNT"))))
|
||||
@@ -1,29 +0,0 @@
|
||||
(ns quo.components.tags.summary-tag.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]))
|
||||
|
||||
(defn main
|
||||
[{:keys [type theme customization-color]}]
|
||||
{:justify-content :center
|
||||
:align-items :center
|
||||
:height 32
|
||||
:padding-left 4
|
||||
:padding-right 10
|
||||
:flex-direction :row
|
||||
:border-radius (if (#{:account :collectible} type) 10 16)
|
||||
:background-color (colors/resolve-color customization-color theme 10)})
|
||||
|
||||
(defn label
|
||||
[type theme]
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white theme)
|
||||
:margin-left (if (= type :address) 6 4)})
|
||||
|
||||
(def collectible-image
|
||||
{:width 24
|
||||
:height 24
|
||||
:border-radius 10})
|
||||
|
||||
(def token-image
|
||||
{:width 24
|
||||
:height 24
|
||||
:border-radius 12})
|
||||
@@ -1,67 +0,0 @@
|
||||
(ns quo.components.tags.summary-tag.view
|
||||
(:require
|
||||
[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.markdown.text :as text]
|
||||
[quo.components.tags.summary-tag.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- left-view
|
||||
[{:keys [label type customization-color emoji image-source]}]
|
||||
(case type
|
||||
:account
|
||||
[account-avatar/view
|
||||
{:customization-color customization-color
|
||||
:size 24
|
||||
:emoji emoji
|
||||
:type :default}]
|
||||
:network
|
||||
[rn/image
|
||||
{:source image-source
|
||||
:style style/token-image}]
|
||||
:saved-address
|
||||
[wallet-user-avatar/wallet-user-avatar
|
||||
{:full-name label
|
||||
:size :size-24
|
||||
:customization-color customization-color}]
|
||||
:collectible
|
||||
[rn/image
|
||||
{:source image-source
|
||||
:style style/collectible-image}]
|
||||
:user
|
||||
[user-avatar/user-avatar
|
||||
{:full-name label
|
||||
:size :xs
|
||||
:profile-picture image-source
|
||||
:customization-color customization-color}]
|
||||
:token
|
||||
[rn/image
|
||||
{:source image-source
|
||||
:style style/token-image}]
|
||||
nil))
|
||||
|
||||
(defn- view-internal
|
||||
"Options:
|
||||
- :label - string - tag label
|
||||
- :customization-color - color - It will be passed down to components that
|
||||
should vary based on a custom color.
|
||||
- :type - :token / :user / :collectible / :saved-address / :network / :account
|
||||
- :emoji - string - emoji used for displaying account avatar
|
||||
- :image-source - resource - image to display on :network, :collectible :user and :token
|
||||
- :theme - :light / :dark"
|
||||
[{:keys [label customization-color type theme]
|
||||
:as props
|
||||
:or {customization-color colors/neutral-80-opa-5}}]
|
||||
[rn/view
|
||||
{:accessibility-label :container
|
||||
:style (style/main (assoc props :customization-color customization-color))}
|
||||
[left-view props]
|
||||
[text/text
|
||||
{:style (style/label type theme)
|
||||
:weight :semi-bold
|
||||
:size :heading-1} label]])
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
+1
-3
@@ -131,7 +131,6 @@
|
||||
quo.components.tags.number-tag.view
|
||||
quo.components.tags.permission-tag
|
||||
quo.components.tags.status-tags
|
||||
quo.components.tags.summary-tag.view
|
||||
quo.components.tags.tag
|
||||
quo.components.tags.tags
|
||||
quo.components.tags.tiny-tag.view
|
||||
@@ -212,7 +211,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)
|
||||
@@ -361,7 +360,6 @@
|
||||
(def number-tag quo.components.tags.number-tag.view/view)
|
||||
(def permission-tag quo.components.tags.permission-tag/tag)
|
||||
(def status-tag quo.components.tags.status-tags/status-tag)
|
||||
(def summary-tag quo.components.tags.summary-tag.view/view)
|
||||
(def tag quo.components.tags.tag/tag)
|
||||
(def tags quo.components.tags.tags/tags)
|
||||
(def tiny-tag quo.components.tags.tiny-tag.view/view)
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
[quo.components.switchers.group-messaging-card.component-spec]
|
||||
[quo.components.tags.network-tags.component-spec]
|
||||
[quo.components.tags.status-tags-component-spec]
|
||||
[quo.components.tags.summary-tag.component-spec]
|
||||
[quo.components.tags.tiny-tag.component-spec]
|
||||
[quo.components.wallet.account-card.component-spec]
|
||||
[quo.components.wallet.account-origin.component-spec]
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
# 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]))
|
||||
```
|
||||
@@ -1,14 +0,0 @@
|
||||
(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))
|
||||
@@ -1,17 +0,0 @@
|
||||
(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))
|
||||
@@ -1,95 +0,0 @@
|
||||
(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)))
|
||||
@@ -1,26 +0,0 @@
|
||||
(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)
|
||||
@@ -1,11 +0,0 @@
|
||||
(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 #{}))
|
||||
@@ -1,28 +0,0 @@
|
||||
(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})
|
||||
@@ -1,18 +0,0 @@
|
||||
(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"]]]))
|
||||
@@ -107,15 +107,14 @@
|
||||
[{:keys [db] :as cofx} message]
|
||||
(let [current-chat-id (:current-chat-id db)
|
||||
text (get-in message [:content :text])]
|
||||
{:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
|
||||
message)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
|
||||
(update-in [:chat/inputs current-chat-id :metadata]
|
||||
dissoc
|
||||
:sending-image))
|
||||
:dispatch-n [[:chat.ui/set-chat-input-text nil current-chat-id]
|
||||
[:mention/to-input-field text current-chat-id]]}))
|
||||
{:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
|
||||
message)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
|
||||
(update-in [:chat/inputs current-chat-id :metadata]
|
||||
dissoc
|
||||
:sending-image))
|
||||
:dispatch [:mention/to-input-field text current-chat-id]}))
|
||||
|
||||
(rf/defn show-contact-request-input
|
||||
"Sets reference to previous chat message and focuses on input"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
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]))
|
||||
|
||||
@@ -119,7 +118,6 @@
|
||||
[& args]
|
||||
(reset-test-data!)
|
||||
(interceptors/register-global-interceptors)
|
||||
(schema/setup!)
|
||||
(rf/set-mergeable-keys #{:filters/load-filters
|
||||
:pairing/set-installation-metadata
|
||||
:dispatch-n
|
||||
|
||||
@@ -225,9 +225,7 @@
|
||||
(.. ^js % -nativeEvent -data)])
|
||||
:on-load #(re-frame/dispatch [:browser/loading-started])
|
||||
:on-error #(re-frame/dispatch [:browser/error-occured])
|
||||
:injected-java-script-before-content-loaded (js-res/ethereum-provider (str network-id))
|
||||
;; https://github.com/status-im/status-mobile/issues/17854
|
||||
:allows-inline-media-playback true}])]
|
||||
:injected-java-script-before-content-loaded (js-res/ethereum-provider (str network-id))}])]
|
||||
[navigation
|
||||
{:url url-original
|
||||
:name name
|
||||
|
||||
@@ -131,13 +131,6 @@
|
||||
: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)
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
(def test-dir (.mkdtempSync fs test-dir-prefix))
|
||||
|
||||
(def initialized? (atom false))
|
||||
|
||||
(defn signal-received-callback
|
||||
[a]
|
||||
(re-frame/dispatch [:signals/signal-received a]))
|
||||
@@ -24,12 +22,10 @@
|
||||
;; We poll for signals, could not get callback working
|
||||
(defn init!
|
||||
[]
|
||||
(when-not @initialized?
|
||||
(.setSignalEventCallback native-status)
|
||||
(reset! initialized? true)
|
||||
(js/setInterval (fn []
|
||||
(.pollSignal native-status signal-received-callback)
|
||||
100))))
|
||||
(.setSignalEventCallback native-status)
|
||||
(js/setInterval (fn []
|
||||
(.pollSignal native-status signal-received-callback)
|
||||
100)))
|
||||
|
||||
(def status
|
||||
(clj->js
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
(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"))))
|
||||
@@ -1,17 +0,0 @@
|
||||
(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})
|
||||
@@ -1,25 +0,0 @@
|
||||
(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))
|
||||
@@ -1,15 +0,0 @@
|
||||
(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})
|
||||
@@ -1,102 +0,0 @@
|
||||
(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
|
||||
:containstyle {:margin-top 8}}
|
||||
{:type :error
|
||||
:size :default
|
||||
:icon :i/info
|
||||
:style {:margin-top 8}}
|
||||
(i18n/label :t/oops-wrong-password)])
|
||||
[quo/button
|
||||
{:container-style {:margin-bottom 12 :margin-top 40}
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
:collapsed? collapsed?
|
||||
:page-nav-props page-nav-props
|
||||
:overlay-shown? overlay-shown?}]
|
||||
|
||||
[rn/scroll-view
|
||||
{:content-container-style {:flex-grow 1}
|
||||
:content-inset-adjustment-behavior :never
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
(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,44 +0,0 @@
|
||||
(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))
|
||||
@@ -1,43 +0,0 @@
|
||||
(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))
|
||||
+41
-1
@@ -1,6 +1,10 @@
|
||||
(ns status-im2.common.standard-authentication.standard-auth.authorize
|
||||
(ns status-im2.common.standard-authentication.standard-auth.view
|
||||
(: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]
|
||||
@@ -42,3 +46,39 @@
|
||||
{: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))
|
||||
@@ -1,41 +0,0 @@
|
||||
(ns status-im2.common.top-bar.style
|
||||
(:require [quo.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defonce ^:const cover-height 168)
|
||||
(defonce ^:const overscroll-cover-height 2000)
|
||||
(defonce ^:const header-avatar-top-offset -36)
|
||||
|
||||
(defn header-container
|
||||
[show? theme]
|
||||
{:display (if show? :flex :none)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-100 theme)
|
||||
:top (- overscroll-cover-height)
|
||||
:margin-bottom (- overscroll-cover-height)})
|
||||
|
||||
(defn header-cover
|
||||
[cover-bg-color theme]
|
||||
{:flex 1
|
||||
:height (+ overscroll-cover-height cover-height)
|
||||
:background-color (colors/theme-colors
|
||||
(colors/custom-color cover-bg-color 50 20)
|
||||
(colors/custom-color cover-bg-color 50 40)
|
||||
theme)})
|
||||
|
||||
(defn header-bottom-part
|
||||
[animation theme]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:border-top-right-radius animation
|
||||
:border-top-left-radius animation}
|
||||
{:top -16
|
||||
:margin-bottom -16
|
||||
:padding-bottom 24
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}))
|
||||
|
||||
(def header-avatar
|
||||
{:top header-avatar-top-offset
|
||||
:margin-horizontal 20
|
||||
:margin-bottom header-avatar-top-offset})
|
||||
|
||||
(def header-description-text
|
||||
{:margin-top 8})
|
||||
@@ -1,57 +0,0 @@
|
||||
(ns status-im2.common.top-bar.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im2.common.top-bar.style :as style]))
|
||||
|
||||
(def header-extrapolation-option
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "clamp"})
|
||||
|
||||
(defn- add-inverted-y-android
|
||||
[style]
|
||||
(cond-> style
|
||||
platform/android?
|
||||
(assoc :scale-y -1)))
|
||||
|
||||
;; TODO(alwx): status-im2.contexts.chat.messages.list.view/list-footer should be abstracted
|
||||
(defn f-abstract-list-header
|
||||
[{:keys [inverted-y-on-android?
|
||||
show?
|
||||
theme
|
||||
cover-bg-color
|
||||
scroll-y
|
||||
on-layout
|
||||
avatar-component
|
||||
title-component
|
||||
description
|
||||
actions-component
|
||||
loading-component]}]
|
||||
(let [border-animation (reanimated/interpolate scroll-y
|
||||
[30 125]
|
||||
[14 0]
|
||||
header-extrapolation-option)]
|
||||
[rn/view (cond-> {:flex 1}
|
||||
inverted-y-on-android?
|
||||
(add-inverted-y-android))
|
||||
[rn/view {:style (style/header-container show? theme)
|
||||
:on-layout on-layout}
|
||||
[rn/view {:style (style/header-cover cover-bg-color theme)}]
|
||||
[reanimated/view {:style (style/header-bottom-part border-animation theme)}
|
||||
[rn/view {:style style/header-avatar}
|
||||
[rn/view {:style {:align-items :flex-start}}
|
||||
[avatar-component]]
|
||||
[title-component]
|
||||
(when description
|
||||
[quo/text {:style style/header-description-text}
|
||||
description])
|
||||
(when actions-component
|
||||
[actions-component])]]]
|
||||
(when loading-component
|
||||
[loading-component])]))
|
||||
|
||||
(defn abstract-list-header
|
||||
[props]
|
||||
[:f> f-abstract-list-header props])
|
||||
@@ -11,7 +11,7 @@
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn edit-message
|
||||
[{:keys [text-value input-ref]}]
|
||||
[state]
|
||||
[rn/view
|
||||
{:style style/container
|
||||
:accessibility-label :edit-message}
|
||||
@@ -30,18 +30,20 @@
|
||||
{:size 24
|
||||
:icon-only? true
|
||||
:accessibility-label :edit-cancel-button
|
||||
:on-press #(utils/cancel-edit-message text-value input-ref)
|
||||
:on-press (fn []
|
||||
(utils/cancel-edit-message state)
|
||||
(rf/dispatch [:chat.ui/cancel-message-edit]))
|
||||
:type :outline}
|
||||
:i/close]])
|
||||
|
||||
(defn- f-view
|
||||
[props]
|
||||
[state]
|
||||
(let [edit (rf/sub [:chats/edit-message])
|
||||
height (reanimated/use-shared-value (if edit constants/edit-container-height 0))]
|
||||
(rn/use-effect #(reanimated/animate height (if edit constants/edit-container-height 0)) [edit])
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
|
||||
(when edit [edit-message props])]))
|
||||
(when edit [edit-message state])]))
|
||||
|
||||
(defn view
|
||||
[props]
|
||||
[:f> f-view props])
|
||||
[state]
|
||||
[:f> f-view state])
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.chat.composer.constants :as constants]
|
||||
[status-im2.contexts.chat.composer.keyboard :as kb]
|
||||
[status-im2.contexts.chat.composer.utils :as utils]
|
||||
@@ -106,62 +105,53 @@
|
||||
|
||||
(defn use-edit
|
||||
[{:keys [input-ref]}
|
||||
{:keys [text-value saved-cursor-position cursor-position]}
|
||||
{:keys [edit input-with-mentions]}
|
||||
messages-list-on-layout-finished?]
|
||||
(let [mention? (some #(= :mention (first %)) (seq input-with-mentions))
|
||||
composer-just-opened? (not @messages-list-on-layout-finished?)]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(let [mention-text (reduce (fn [acc item]
|
||||
(str acc (second item)))
|
||||
""
|
||||
input-with-mentions)
|
||||
edit-text (cond
|
||||
mention? mention-text
|
||||
;; NOTE: using text-value for cases when the user
|
||||
;; leaves the app with an unfinished edit and re-opens
|
||||
;; the chat.
|
||||
(and (seq @text-value) composer-just-opened?)
|
||||
@text-value
|
||||
:else (get-in edit [:content :text]))
|
||||
selection-pos (count edit-text)
|
||||
inject-edit-text (fn []
|
||||
(reset! text-value edit-text)
|
||||
(reset! cursor-position selection-pos)
|
||||
(reset! saved-cursor-position selection-pos)
|
||||
(when @input-ref
|
||||
(.setNativeProps ^js @input-ref
|
||||
(clj->js {:text edit-text}))))]
|
||||
|
||||
(when (and edit @input-ref)
|
||||
;; NOTE: A small setTimeout is necessary to ensure the focus is enqueued and is executed
|
||||
;; ASAP. Check https://github.com/software-mansion/react-native-screens/issues/472
|
||||
;;
|
||||
;; The nested setTimeout is necessary to avoid both `on-focus` and
|
||||
;; `on-content-size-change` handlers triggering the height animation simultaneously, as
|
||||
;; this causes a jump in the
|
||||
;; UI. This way, `on-focus` will trigger first without changing the height, after which
|
||||
;; `on-content-size-change` will animate the height of the input based on the injected
|
||||
;; text.
|
||||
(js/setTimeout #(do (when @messages-list-on-layout-finished? (.focus ^js @input-ref))
|
||||
(reagent/next-tick inject-edit-text))
|
||||
600))))
|
||||
[(:message-id edit)])))
|
||||
{:keys [text-value saved-cursor-position]}
|
||||
{:keys [edit]}]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(let [edit-text (get-in edit [:content :text])
|
||||
text-value-count (count @text-value)]
|
||||
(when (and edit @input-ref)
|
||||
;; A small setTimeout is necessary to ensure the statement is enqueued and will get executed
|
||||
;; ASAP.
|
||||
;; https://github.com/software-mansion/react-native-screens/issues/472
|
||||
(js/setTimeout #(.focus ^js @input-ref) 250)
|
||||
(.setNativeProps ^js @input-ref (clj->js {:text edit-text}))
|
||||
(reset! text-value edit-text)
|
||||
(reset! saved-cursor-position (if (zero? text-value-count)
|
||||
(count edit-text)
|
||||
text-value-count)))))
|
||||
[(:message-id edit)]))
|
||||
|
||||
(defn use-reply
|
||||
[{:keys [input-ref]}
|
||||
{:keys [container-opacity]}
|
||||
{:keys [reply]}
|
||||
messages-list-on-layout-finished?]
|
||||
{:keys [reply]}]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when reply
|
||||
(reanimated/animate container-opacity 1))
|
||||
(when (and reply @input-ref @messages-list-on-layout-finished?)
|
||||
(js/setTimeout #(.focus ^js @input-ref) 600)))
|
||||
(when (and reply @input-ref)
|
||||
(js/setTimeout #(.focus ^js @input-ref) 250)))
|
||||
[(:message-id reply)]))
|
||||
|
||||
(defn edit-mentions
|
||||
[{:keys [input-ref]} {:keys [text-value cursor-position]} {:keys [input-with-mentions]}]
|
||||
(rn/use-effect (fn []
|
||||
(let [input-text (reduce (fn [acc item]
|
||||
(str acc (second item)))
|
||||
""
|
||||
input-with-mentions)]
|
||||
(reset! text-value input-text)
|
||||
(reset! cursor-position (count input-text))
|
||||
(js/setTimeout #(when @input-ref
|
||||
(.setNativeProps ^js @input-ref
|
||||
(clj->js {:selection {:start (count input-text)
|
||||
:end (count
|
||||
input-text)}})))
|
||||
300)))
|
||||
[(some #(= :mention (first %)) (seq input-with-mentions))]))
|
||||
|
||||
(defn update-input-mention
|
||||
[{:keys [input-ref]}
|
||||
{:keys [text-value]}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
(reset! emoji-kb-extra-height nil))
|
||||
(reset! maximized? false)
|
||||
(rf/dispatch [:chat.ui/set-input-maximized false])
|
||||
(utils/blur-input input-ref))
|
||||
(when @input-ref
|
||||
(.blur ^js @input-ref)))
|
||||
|
||||
(defn bounce-back
|
||||
[{:keys [height saved-height opacity background-y]}
|
||||
@@ -95,13 +96,13 @@
|
||||
max-height
|
||||
bounded-height
|
||||
saved-height))
|
||||
; sheet at min-height, collapse keyboard
|
||||
(utils/blur-input input-ref))))))
|
||||
(when @input-ref ; sheet at min-height, collapse keyboard
|
||||
(.blur ^js @input-ref)))))))
|
||||
(gesture/on-end (fn []
|
||||
(let [diff (- (reanimated/get-shared-value height)
|
||||
(reanimated/get-shared-value saved-height))]
|
||||
(if @gesture-enabled?
|
||||
(if (and @expanding? (>= diff 0))
|
||||
(if (>= diff 0)
|
||||
(if (> diff constants/drag-threshold)
|
||||
(maximize state animations dimensions)
|
||||
(bounce-back animations dimensions starting-opacity))
|
||||
|
||||
@@ -23,14 +23,12 @@
|
||||
show-floating-scroll-down-button?]
|
||||
(reset! focused? true)
|
||||
(rf/dispatch [:chat.ui/set-input-focused true])
|
||||
(let [last-height-value (reanimated/get-shared-value last-height)]
|
||||
(reanimated/animate height last-height-value)
|
||||
(reanimated/set-shared-value saved-height last-height-value)
|
||||
(reanimated/animate container-opacity 1)
|
||||
(when (> last-height-value (* constants/background-threshold max-height))
|
||||
(reanimated/animate opacity 1)
|
||||
(reanimated/set-shared-value background-y 0)))
|
||||
|
||||
(reanimated/animate height (reanimated/get-shared-value last-height))
|
||||
(reanimated/set-shared-value saved-height (reanimated/get-shared-value last-height))
|
||||
(reanimated/animate container-opacity 1)
|
||||
(when (> (reanimated/get-shared-value last-height) (* constants/background-threshold max-height))
|
||||
(reanimated/animate opacity 1)
|
||||
(reanimated/set-shared-value background-y 0))
|
||||
(js/setTimeout #(reset! lock-selection? false) 300)
|
||||
(when (and (not-empty @text-value) @input-ref)
|
||||
(.setNativeProps ^js @input-ref
|
||||
@@ -74,7 +72,7 @@
|
||||
"Save new text height, expand composer if possible, show background overlay if needed"
|
||||
[event
|
||||
{:keys [maximized? lock-layout? text-value]}
|
||||
{:keys [height saved-height last-height opacity background-y]}
|
||||
{:keys [height saved-height opacity background-y]}
|
||||
{:keys [content-height window-height max-height]}
|
||||
keyboard-shown]
|
||||
(when keyboard-shown
|
||||
@@ -89,9 +87,8 @@
|
||||
max-height)
|
||||
new-height (min new-height max-height)]
|
||||
(reset! content-height content-size)
|
||||
(when (utils/update-height? content-size height max-height)
|
||||
(when (utils/update-height? content-size height max-height maximized?)
|
||||
(reanimated/animate height new-height)
|
||||
(reanimated/set-shared-value last-height new-height)
|
||||
(reanimated/set-shared-value saved-height new-height))
|
||||
(when (= new-height max-height)
|
||||
(reset! maximized? true)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
[react-native.async-storage :as async-storage]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im2.contexts.chat.composer.utils :as utils]))
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defn get-kb-height
|
||||
[curr-height default-height]
|
||||
@@ -21,24 +20,18 @@
|
||||
(async-storage/set-item! :kb-default-height (str height)))))
|
||||
|
||||
(defn handle-emoji-kb-ios
|
||||
"Opening emoji KB on iOS will cause a flicker up and down due to height differences.
|
||||
This method handles that by adding the extra difference between the keyboards. When the input is
|
||||
expanded to a point where the added difference will make the composer go beyond the screen causing a flicker,
|
||||
we're subtracting the difference so it only reaches the allowed max-height. We're not animating these
|
||||
changes to make it appear seamless during transitions between keyboard types when maximized."
|
||||
"Opening emoji KB on iOS while maximized will cause a flicker up and down. This method handles that."
|
||||
[event
|
||||
{:keys [emoji-kb-extra-height]}
|
||||
{:keys [text-value kb-height]}
|
||||
{:keys [text-value]}
|
||||
{:keys [height saved-height]}
|
||||
{:keys [max-height]}]
|
||||
(let [start-h (oops/oget event "startCoordinates.height")
|
||||
end-h (oops/oget event "endCoordinates.height")
|
||||
diff (- end-h start-h)
|
||||
max-height-diff (- max-height diff)
|
||||
curr-text @text-value
|
||||
bigger-than-default-kb? (> end-h @kb-height)
|
||||
almost-expanded? (> (reanimated/get-shared-value height) max-height-diff)]
|
||||
(if (and almost-expanded? bigger-than-default-kb? (pos? diff))
|
||||
(let [start-h (oops/oget event "startCoordinates.height")
|
||||
end-h (oops/oget event "endCoordinates.height")
|
||||
diff (- end-h start-h)
|
||||
max-height-diff (- max-height diff)
|
||||
curr-text @text-value]
|
||||
(if (> (reanimated/get-shared-value height) max-height-diff)
|
||||
(do
|
||||
(reanimated/set-shared-value height (- (reanimated/get-shared-value height) diff))
|
||||
(reanimated/set-shared-value saved-height (- (reanimated/get-shared-value saved-height) diff))
|
||||
@@ -65,8 +58,8 @@
|
||||
#(handle-emoji-kb-ios % props state animations dimensions)))
|
||||
(reset! keyboard-hide-listener (.addListener rn/keyboard
|
||||
"keyboardDidHide"
|
||||
#(when platform/android?
|
||||
(utils/blur-input input-ref)))))
|
||||
#(when (and platform/android? @input-ref)
|
||||
(.blur ^js @input-ref)))))
|
||||
|
||||
(defn handle-refocus-emoji-kb-ios
|
||||
[{:keys [saved-emoji-kb-extra-height]}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
[status-im2.constants :as constant]
|
||||
[status-im2.contexts.chat.composer.constants :as constants]
|
||||
[status-im2.contexts.chat.composer.reply.style :as style]
|
||||
[status-im2.contexts.chat.composer.utils :as utils]
|
||||
[utils.ens.stateofus :as stateofus]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
@@ -86,7 +85,7 @@
|
||||
(defn quoted-message
|
||||
[{:keys [from content-type contentType parsed-text content deleted? deleted-for-me?
|
||||
album-images-count]}
|
||||
in-chat-input? pin? recording-audio? input-ref]
|
||||
in-chat-input? pin? recording-audio?]
|
||||
(let [[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity from])
|
||||
current-public-key (rf/sub [:multiaccount/public-key])
|
||||
content-type (or content-type contentType)
|
||||
@@ -137,7 +136,7 @@
|
||||
{:icon-only? true
|
||||
:size 24
|
||||
:accessibility-label :reply-cancel-button
|
||||
:on-press #(utils/cancel-reply-message input-ref)
|
||||
:on-press #(rf/dispatch [:chat.ui/cancel-message-reply])
|
||||
:type :outline}
|
||||
:i/close])
|
||||
(when (and in-chat-input? recording-audio?)
|
||||
@@ -149,13 +148,13 @@
|
||||
:style style/gradient}])]))
|
||||
|
||||
(defn- f-view
|
||||
[recording? input-ref]
|
||||
[recording?]
|
||||
(let [reply (rf/sub [:chats/reply-message])
|
||||
height (reanimated/use-shared-value (if reply constants/reply-container-height 0))]
|
||||
(rn/use-effect #(reanimated/animate height (if reply constants/reply-container-height 0)) [reply])
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
|
||||
(when reply [quoted-message reply true false recording? input-ref])]))
|
||||
(when reply [quoted-message reply true false recording?])]))
|
||||
|
||||
(defn view
|
||||
[{:keys [recording?]} input-ref]
|
||||
[:f> f-view @recording? input-ref])
|
||||
[{:keys [recording?]}]
|
||||
[:f> f-view @recording?])
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
(max min-v (min v max-v)))
|
||||
|
||||
(defn update-height?
|
||||
[content-size height max-height]
|
||||
(let [diff (Math/abs (- content-size (reanimated/get-shared-value height)))]
|
||||
(and (not= (reanimated/get-shared-value height) max-height)
|
||||
(> diff constants/content-change-threshold))))
|
||||
[content-size height max-height maximized?]
|
||||
(when-not @maximized?
|
||||
(let [diff (Math/abs (- content-size (reanimated/get-shared-value height)))]
|
||||
(and (not= (reanimated/get-shared-value height) max-height)
|
||||
(> diff constants/content-change-threshold)))))
|
||||
|
||||
(defn show-top-gradient?
|
||||
[y lines max-lines gradient-opacity focused?]
|
||||
@@ -99,28 +100,10 @@
|
||||
(not reply?)
|
||||
(not audio?)))
|
||||
|
||||
(defn blur-input
|
||||
[input-ref]
|
||||
(when @input-ref
|
||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
||||
(.blur ^js @input-ref)))
|
||||
|
||||
(defn cancel-reply-message
|
||||
[input-ref]
|
||||
(js/setTimeout #(blur-input input-ref) 100)
|
||||
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height])
|
||||
(rf/dispatch [:chat.ui/cancel-message-reply]))
|
||||
|
||||
(defn cancel-edit-message
|
||||
[text-value input-ref]
|
||||
[{:keys [text-value]}]
|
||||
(reset! text-value "")
|
||||
;; NOTE: adding a timeout to assure the input is blurred on the next tick
|
||||
;; after the `text-value` was cleared. Otherwise the height will be calculated
|
||||
;; with the old `text-value`, leading to wrong composer height after blur.
|
||||
(js/setTimeout #(blur-input input-ref) 100)
|
||||
(.setNativeProps ^js @input-ref (clj->js {:text ""}))
|
||||
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height])
|
||||
(rf/dispatch [:chat.ui/cancel-message-edit]))
|
||||
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height]))
|
||||
|
||||
(defn count-lines
|
||||
[s]
|
||||
|
||||
@@ -33,8 +33,7 @@
|
||||
blur-height
|
||||
opacity
|
||||
background-y
|
||||
theme
|
||||
messages-list-on-layout-finished?]} props state]
|
||||
theme]} props state]
|
||||
(let [{:keys [chat-screen-loaded?]
|
||||
:as subscriptions} (utils/init-subs)
|
||||
content-height (reagent/atom (or (:input-content-height ; Actual text height
|
||||
@@ -74,9 +73,10 @@
|
||||
animations
|
||||
dimensions
|
||||
subscriptions)
|
||||
(effects/use-edit props state subscriptions messages-list-on-layout-finished?)
|
||||
(effects/use-reply props animations subscriptions messages-list-on-layout-finished?)
|
||||
(effects/use-edit props state subscriptions)
|
||||
(effects/use-reply props animations subscriptions)
|
||||
(effects/update-input-mention props state subscriptions)
|
||||
(effects/edit-mentions props state subscriptions)
|
||||
(effects/link-previews props state animations subscriptions)
|
||||
(effects/use-images props state animations subscriptions)
|
||||
[:<>
|
||||
@@ -98,10 +98,8 @@
|
||||
[sub-view/bar]
|
||||
(when chat-screen-loaded?
|
||||
[:<>
|
||||
[reply/view state (:input-ref props)]
|
||||
[edit/view
|
||||
{:text-value (:text-value state)
|
||||
:input-ref (:input-ref props)}]])
|
||||
[reply/view state]
|
||||
[edit/view state]])
|
||||
[reanimated/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:on-press (fn []
|
||||
@@ -149,13 +147,11 @@
|
||||
subscriptions]]]]]))
|
||||
|
||||
(defn composer
|
||||
[{:keys [insets scroll-to-bottom-fn show-floating-scroll-down-button?
|
||||
messages-list-on-layout-finished?]}]
|
||||
[{:keys [insets scroll-to-bottom-fn show-floating-scroll-down-button?]}]
|
||||
(let [window-height (:height (rn/get-window))
|
||||
theme (quo.theme/use-theme-value)
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
background-y (reanimated/use-shared-value (- window-height)) ; Y position of background
|
||||
; overlay
|
||||
background-y (reanimated/use-shared-value (- window-height)) ; Y position of background overlay
|
||||
blur-height (reanimated/use-shared-value (+ constants/composer-default-height
|
||||
(:bottom insets)))
|
||||
extra-params {:insets insets
|
||||
@@ -165,8 +161,7 @@
|
||||
:blur-height blur-height
|
||||
:opacity opacity
|
||||
:background-y background-y
|
||||
:theme theme
|
||||
:messages-list-on-layout-finished? messages-list-on-layout-finished?}
|
||||
:theme theme}
|
||||
props (utils/init-non-reactive-state)
|
||||
state (utils/init-reactive-state)]
|
||||
[rn/view (when platform/ios? {:style {:z-index 1}})
|
||||
|
||||
@@ -18,6 +18,37 @@
|
||||
(def list-container
|
||||
{:padding-vertical 16})
|
||||
|
||||
(defn header-container
|
||||
[show? theme]
|
||||
{:display (if show? :flex :none)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-100 theme)
|
||||
:top (- overscroll-cover-height)
|
||||
:margin-bottom (- overscroll-cover-height)})
|
||||
|
||||
(defn header-cover
|
||||
[cover-bg-color theme]
|
||||
{:flex 1
|
||||
:height (+ overscroll-cover-height cover-height)
|
||||
:background-color (colors/theme-colors
|
||||
(colors/custom-color cover-bg-color 50 20)
|
||||
(colors/custom-color cover-bg-color 50 40)
|
||||
theme)})
|
||||
|
||||
(defn header-bottom-part
|
||||
[animation theme]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:border-top-right-radius animation
|
||||
:border-top-left-radius animation}
|
||||
{:top -16
|
||||
:margin-bottom -16
|
||||
:padding-bottom 24
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}))
|
||||
|
||||
(def header-avatar
|
||||
{:top header-avatar-top-offset
|
||||
:margin-horizontal 20
|
||||
:margin-bottom header-avatar-top-offset})
|
||||
|
||||
(defn header-image
|
||||
[scale-animation top-margin-animation side-margin-animation]
|
||||
(reanimated/apply-animations-to-style
|
||||
@@ -26,3 +57,6 @@
|
||||
:margin-left side-margin-animation
|
||||
:margin-bottom side-margin-animation}
|
||||
{:align-items :flex-start}))
|
||||
|
||||
(def bio
|
||||
{:margin-top 8})
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.ui.screens.chat.group :as chat.group]
|
||||
[status-im.ui.screens.chat.message.gap :as message.gap]
|
||||
[status-im2.common.top-bar.view :as top-bar]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.chat.composer.constants :as composer.constants]
|
||||
[status-im2.contexts.chat.messages.content.view :as message]
|
||||
@@ -41,14 +40,14 @@
|
||||
[]
|
||||
(some-> ^js @messages-list-ref
|
||||
(.scrollToOffset #js
|
||||
{:animated true})))
|
||||
{:animated true})))
|
||||
|
||||
(defn on-scroll
|
||||
[evt show-floating-scroll-down-button?]
|
||||
(let [y (oops/oget evt "nativeEvent.contentOffset.y")
|
||||
layout-height (oops/oget evt "nativeEvent.layoutMeasurement.height")
|
||||
threshold-height (* (/ layout-height 100)
|
||||
threshold-percentage-to-show-floating-scroll-down-button)
|
||||
(let [y (oops/oget evt "nativeEvent.contentOffset.y")
|
||||
layout-height (oops/oget evt "nativeEvent.layoutMeasurement.height")
|
||||
threshold-height (* (/ layout-height 100)
|
||||
threshold-percentage-to-show-floating-scroll-down-button)
|
||||
reached-threshold? (> y threshold-height)]
|
||||
(when (not= reached-threshold? @show-floating-scroll-down-button?)
|
||||
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets))
|
||||
@@ -58,16 +57,16 @@
|
||||
[e]
|
||||
(when @messages-list-ref
|
||||
(reset! state/first-not-visible-item
|
||||
(when-let [last-visible-element (aget (oops/oget e "viewableItems")
|
||||
(dec (oops/oget e "viewableItems.length")))]
|
||||
(let [index (oops/oget last-visible-element "index")
|
||||
;; Get first not visible element, if it's a datemark/gap
|
||||
;; we might add messages on receiving as they do not have
|
||||
;; a clock value, but usually it will be a message
|
||||
first-not-visible (aget (oops/oget @messages-list-ref "props.data") (inc index))]
|
||||
(when (and first-not-visible
|
||||
(= :message (:type first-not-visible)))
|
||||
first-not-visible))))))
|
||||
(when-let [last-visible-element (aget (oops/oget e "viewableItems")
|
||||
(dec (oops/oget e "viewableItems.length")))]
|
||||
(let [index (oops/oget last-visible-element "index")
|
||||
;; Get first not visible element, if it's a datemark/gap
|
||||
;; we might add messages on receiving as they do not have
|
||||
;; a clock value, but usually it will be a message
|
||||
first-not-visible (aget (oops/oget @messages-list-ref "props.data") (inc index))]
|
||||
(when (and first-not-visible
|
||||
(= :message (:type first-not-visible)))
|
||||
first-not-visible))))))
|
||||
|
||||
(defn list-on-end-reached
|
||||
[scroll-y on-end-reached?]
|
||||
@@ -95,9 +94,9 @@
|
||||
{:no-color true
|
||||
:size 20
|
||||
:color (colors/theme-colors
|
||||
(colors/custom-color :success 50)
|
||||
(colors/custom-color :success 60)
|
||||
theme)}]
|
||||
(colors/custom-color :success 50)
|
||||
(colors/custom-color :success 60)
|
||||
theme)}]
|
||||
(when added?
|
||||
[quo/icon :i/contact
|
||||
{:no-color true
|
||||
@@ -116,17 +115,17 @@
|
||||
|
||||
(defn loading-view
|
||||
[chat-id messages-view-height messages-view-header-height]
|
||||
(let [loading-messages? (rf/sub [:chats/loading-messages? chat-id])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])
|
||||
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
|
||||
(let [loading-messages? (rf/sub [:chats/loading-messages? chat-id])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])
|
||||
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
|
||||
loading-first-page? (= (count messages) 0)
|
||||
top-spacing (if loading-first-page? 0 navigation.style/navigation-bar-height)
|
||||
parent-height (if loading-first-page?
|
||||
(- @messages-view-height
|
||||
@messages-view-header-height
|
||||
composer.constants/composer-default-height
|
||||
loading-indicator-extra-spacing)
|
||||
loading-indicator-page-loading-height)]
|
||||
top-spacing (if loading-first-page? 0 navigation.style/navigation-bar-height)
|
||||
parent-height (if loading-first-page?
|
||||
(- @messages-view-height
|
||||
@messages-view-header-height
|
||||
composer.constants/composer-default-height
|
||||
loading-indicator-extra-spacing)
|
||||
loading-indicator-page-loading-height)]
|
||||
(when (or loading-messages? (not all-loaded?))
|
||||
[rn/view {:padding-top top-spacing}
|
||||
;; Only use animated loading skeleton for ios
|
||||
@@ -147,14 +146,14 @@
|
||||
|
||||
(defn f-list-footer-avatar
|
||||
[{:keys [scroll-y display-name online? profile-picture]}]
|
||||
(let [image-scale-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[1 0.5]
|
||||
header-extrapolation-option)
|
||||
image-top-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 40]
|
||||
header-extrapolation-option)
|
||||
(let [image-scale-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[1 0.5]
|
||||
header-extrapolation-option)
|
||||
image-top-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 40]
|
||||
header-extrapolation-option)
|
||||
image-side-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 -20]
|
||||
@@ -177,13 +176,13 @@
|
||||
(defn- add-inverted-y-android
|
||||
[style]
|
||||
(cond-> style
|
||||
platform/android?
|
||||
(assoc :scale-y -1)))
|
||||
platform/android?
|
||||
(assoc :scale-y -1)))
|
||||
|
||||
(defn actions
|
||||
[chat-id cover-bg-color]
|
||||
(let [latest-pin-text (rf/sub [:chats/last-pinned-message-text chat-id])
|
||||
pins-count (rf/sub [:chats/pin-messages-count chat-id])]
|
||||
pins-count (rf/sub [:chats/pin-messages-count chat-id])]
|
||||
[quo/channel-actions
|
||||
{:style {:margin-top 16}
|
||||
:actions [{:accessibility-label :action-button-pinned
|
||||
@@ -202,46 +201,53 @@
|
||||
messages-view-header-height big-name-visible?]}]
|
||||
(let [{:keys [chat-id chat-name emoji chat-type
|
||||
group-chat]} chat
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])
|
||||
display-name (cond
|
||||
(= chat-type constants/one-to-one-chat-type)
|
||||
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
|
||||
(= chat-type constants/community-chat-type)
|
||||
(str (when emoji (str emoji " ")) "# " chat-name)
|
||||
:else (str emoji chat-name))
|
||||
{:keys [bio]} (rf/sub [:contacts/contact-by-identity chat-id])
|
||||
online? (rf/sub [:visibility-status-updates/online? chat-id])
|
||||
contact (when-not group-chat
|
||||
(rf/sub [:contacts/contact-by-address chat-id]))
|
||||
photo-path (rf/sub [:chats/photo-path chat-id])]
|
||||
[top-bar/abstract-list-header
|
||||
{:inverted-y-on-android? true
|
||||
:show? all-loaded?
|
||||
:theme theme
|
||||
:cover-bg-color cover-bg-color
|
||||
:scroll-y scroll-y
|
||||
:on-layout on-layout
|
||||
:avatar-component (fn [] (when-not group-chat
|
||||
[list-footer-avatar
|
||||
{:scroll-y scroll-y
|
||||
:display-name display-name
|
||||
:online? online?
|
||||
:profile-picture photo-path}]))
|
||||
:title-component (fn [] [rnio/view
|
||||
{:on-change (fn [view-visible?]
|
||||
(reset! big-name-visible? view-visible?))
|
||||
:style {:flex-direction :row
|
||||
:margin-top (if group-chat 54 12)}}
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:size :heading-1
|
||||
:style {:flex-shrink 1}
|
||||
:number-of-lines 1}
|
||||
display-name]
|
||||
[contact-icon contact theme]])
|
||||
:description bio
|
||||
:actions-component (fn [] [actions chat-id cover-bg-color])
|
||||
:loading-component (fn [] [loading-view chat-id messages-view-height messages-view-header-height])}]))
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])
|
||||
display-name (cond
|
||||
(= chat-type constants/one-to-one-chat-type)
|
||||
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
|
||||
(= chat-type constants/community-chat-type)
|
||||
(str (when emoji (str emoji " ")) "# " chat-name)
|
||||
:else (str emoji chat-name))
|
||||
{:keys [bio]} (rf/sub [:contacts/contact-by-identity chat-id])
|
||||
online? (rf/sub [:visibility-status-updates/online? chat-id])
|
||||
contact (when-not group-chat
|
||||
(rf/sub [:contacts/contact-by-address chat-id]))
|
||||
photo-path (rf/sub [:chats/photo-path chat-id])
|
||||
border-animation (reanimated/interpolate scroll-y
|
||||
[30 125]
|
||||
[14 0]
|
||||
header-extrapolation-option)]
|
||||
[rn/view (add-inverted-y-android {:flex 1})
|
||||
[rn/view
|
||||
{:style (style/header-container all-loaded? theme)
|
||||
:on-layout on-layout}
|
||||
[rn/view {:style (style/header-cover cover-bg-color theme)}]
|
||||
[reanimated/view {:style (style/header-bottom-part border-animation theme)}
|
||||
[rn/view {:style style/header-avatar}
|
||||
[rn/view {:style {:align-items :flex-start}}
|
||||
(when-not group-chat
|
||||
[list-footer-avatar
|
||||
{:scroll-y scroll-y
|
||||
:display-name display-name
|
||||
:online? online?
|
||||
:profile-picture photo-path}])]
|
||||
[rnio/view
|
||||
{:on-change (fn [view-visible?]
|
||||
(reset! big-name-visible? view-visible?))
|
||||
:style {:flex-direction :row
|
||||
:margin-top (if group-chat 54 12)}}
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:size :heading-1
|
||||
:style {:flex-shrink 1}
|
||||
:number-of-lines 1}
|
||||
display-name]
|
||||
[contact-icon contact theme]]
|
||||
(when bio
|
||||
[quo/text {:style style/bio}
|
||||
bio])
|
||||
[actions chat-id cover-bg-color]]]]
|
||||
[loading-view chat-id messages-view-height messages-view-header-height]]))
|
||||
|
||||
(defn list-footer
|
||||
[props]
|
||||
@@ -251,11 +257,10 @@
|
||||
[{:keys [chat-id invitation-admin]}]
|
||||
[rn/view
|
||||
[chat.group/group-chat-footer chat-id invitation-admin]])
|
||||
|
||||
(defn footer-on-layout
|
||||
[e messages-view-header-height]
|
||||
(let [height (oops/oget e "nativeEvent.layout.height")
|
||||
y (oops/oget e "nativeEvent.layout.y")]
|
||||
y (oops/oget e "nativeEvent.layout.y")]
|
||||
(reset! messages-view-header-height (+ height y))))
|
||||
|
||||
(defn render-fn
|
||||
@@ -264,7 +269,7 @@
|
||||
(when (not= content-type constants/content-type-contact-request)
|
||||
[rn/view
|
||||
(add-inverted-y-android
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
|
||||
(cond
|
||||
(= type :datemark)
|
||||
[quo/divider-date value]
|
||||
@@ -277,9 +282,9 @@
|
||||
|
||||
(defn scroll-handler
|
||||
[event scroll-y animate-topbar-opacity? on-end-reached? animate-topbar-name?]
|
||||
(let [content-size-y (- (oops/oget event "nativeEvent.contentSize.height")
|
||||
(oops/oget event "nativeEvent.layoutMeasurement.height"))
|
||||
current-y (oops/oget event "nativeEvent.contentOffset.y")
|
||||
(let [content-size-y (- (oops/oget event "nativeEvent.contentSize.height")
|
||||
(oops/oget event "nativeEvent.layoutMeasurement.height"))
|
||||
current-y (oops/oget event "nativeEvent.contentOffset.y")
|
||||
scroll-distance (- content-size-y current-y)]
|
||||
(when (and @on-end-reached? (pos? scroll-distance))
|
||||
(reset! on-end-reached? false))
|
||||
@@ -293,7 +298,7 @@
|
||||
|
||||
(defn f-messages-list-content
|
||||
[{:keys [chat insets scroll-y content-height cover-bg-color keyboard-shown? inner-state-atoms
|
||||
big-name-visible? animate-topbar-opacity? composer-active? messages-list-on-layout-finished?
|
||||
big-name-visible? animate-topbar-opacity? composer-active?
|
||||
on-end-reached? animate-topbar-name?]}]
|
||||
(rn/use-effect (fn []
|
||||
(if (and (not @on-end-reached?)
|
||||
@@ -301,13 +306,13 @@
|
||||
(reset! animate-topbar-opacity? true)
|
||||
(reset! animate-topbar-opacity? false)))
|
||||
[composer-active? @on-end-reached? @animate-topbar-opacity?])
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
{window-height :height} (rn/get-window)
|
||||
{:keys [keyboard-height]} (hooks/use-keyboard)
|
||||
context (rf/sub [:chats/current-chat-message-list-view-context])
|
||||
messages (rf/sub [:chats/raw-chat-messages-stream (:chat-id chat)])
|
||||
recording? (rf/sub [:chats/recording?])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? (:chat-id chat)])
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
{window-height :height} (rn/get-window)
|
||||
{:keys [keyboard-height]} (hooks/use-keyboard)
|
||||
context (rf/sub [:chats/current-chat-message-list-view-context])
|
||||
messages (rf/sub [:chats/raw-chat-messages-stream (:chat-id chat)])
|
||||
recording? (rf/sub [:chats/recording?])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? (:chat-id chat)])
|
||||
{:keys [show-floating-scroll-down-button?
|
||||
messages-view-height
|
||||
messages-view-header-height]} inner-state-atoms]
|
||||
@@ -317,20 +322,18 @@
|
||||
:key-fn list-key-fn
|
||||
:ref list-ref
|
||||
:bounces false
|
||||
;; TODO(alwx): list-header indicates offsets
|
||||
:header [:<>
|
||||
[list-header insets (:able-to-send-message? context) theme]
|
||||
(when (= (:chat-type chat) constants/private-group-chat-type)
|
||||
[list-group-chat-header chat])]
|
||||
;; TODO(alwx): list-footer is basically a header, yes
|
||||
:footer [list-footer
|
||||
{:theme theme
|
||||
:chat chat
|
||||
:scroll-y scroll-y
|
||||
:cover-bg-color cover-bg-color
|
||||
:on-layout #(footer-on-layout
|
||||
%
|
||||
messages-view-header-height)
|
||||
%
|
||||
messages-view-header-height)
|
||||
:messages-view-header-height messages-view-header-height
|
||||
:messages-view-height messages-view-height
|
||||
:big-name-visible? big-name-visible?}]
|
||||
@@ -343,26 +346,26 @@
|
||||
:on-viewable-items-changed on-viewable-items-changed
|
||||
:on-content-size-change (fn [_ y]
|
||||
(if (or
|
||||
(< minimum-scroll-y-topbar-overlaying-avatar
|
||||
(reanimated/get-shared-value scroll-y))
|
||||
(< topbar-visible-scroll-y-value
|
||||
(reanimated/get-shared-value scroll-y)))
|
||||
(< minimum-scroll-y-topbar-overlaying-avatar
|
||||
(reanimated/get-shared-value scroll-y))
|
||||
(< topbar-visible-scroll-y-value
|
||||
(reanimated/get-shared-value scroll-y)))
|
||||
(reset! animate-topbar-opacity? true)
|
||||
(reset! animate-topbar-opacity? false))
|
||||
(when-not (or
|
||||
(not @big-name-visible?)
|
||||
(= :initial-render @big-name-visible?)
|
||||
(pos? (reanimated/get-shared-value
|
||||
scroll-y)))
|
||||
(not @big-name-visible?)
|
||||
(= :initial-render @big-name-visible?)
|
||||
(pos? (reanimated/get-shared-value
|
||||
scroll-y)))
|
||||
(reset! on-end-reached? false))
|
||||
;; NOTE(alwx): here we set the initial value of
|
||||
;; `scroll-y` which is needed because by default the
|
||||
;; chat is scrolled to the bottom and no initial
|
||||
;; `on-scroll` event is getting triggered
|
||||
(let [scroll-y-shared (reanimated/get-shared-value
|
||||
scroll-y)
|
||||
(let [scroll-y-shared (reanimated/get-shared-value
|
||||
scroll-y)
|
||||
content-height-shared (reanimated/get-shared-value
|
||||
content-height)]
|
||||
content-height)]
|
||||
(when (or (= scroll-y-shared 0)
|
||||
(> (Math/abs (- content-height-shared y))
|
||||
min-message-height))
|
||||
@@ -393,31 +396,22 @@
|
||||
animate-topbar-name?)
|
||||
(on-scroll event show-floating-scroll-down-button?))
|
||||
:style (add-inverted-y-android
|
||||
{:background-color (if all-loaded?
|
||||
(colors/theme-colors
|
||||
(colors/custom-color cover-bg-color
|
||||
50
|
||||
20)
|
||||
(colors/custom-color cover-bg-color
|
||||
50
|
||||
40)
|
||||
theme)
|
||||
(colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-95
|
||||
theme))})
|
||||
{:background-color (if all-loaded?
|
||||
(colors/theme-colors
|
||||
(colors/custom-color cover-bg-color
|
||||
50
|
||||
20)
|
||||
(colors/custom-color cover-bg-color
|
||||
50
|
||||
40)
|
||||
theme)
|
||||
(colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-95
|
||||
theme))})
|
||||
;;TODO(rasom) https://github.com/facebook/react-native/issues/30034
|
||||
:inverted (when platform/ios? true)
|
||||
:on-layout (fn [e]
|
||||
;; FIXME: the 1s timeout is to assure all effects with
|
||||
;; timeouts that depend on the value are considered.
|
||||
;; Hacky, but we're heavily relying on timeouts in the
|
||||
;; composer and need to react to differently (e.g.
|
||||
;; inside effects/use-edit) when the chat has just
|
||||
;; opened and the subsequent times.
|
||||
(js/setTimeout #(reset! messages-list-on-layout-finished?
|
||||
true)
|
||||
1000)
|
||||
(let [layout-height (oops/oget e
|
||||
"nativeEvent.layout.height")]
|
||||
(reset! messages-view-height layout-height)))
|
||||
@@ -427,9 +421,9 @@
|
||||
(defn message-list-content-view
|
||||
[props]
|
||||
(let [chat-screen-loaded? (rf/sub [:shell/chat-screen-loaded?])
|
||||
window-height (:height (rn/get-window))
|
||||
content-height (- window-height composer.constants/composer-default-height)
|
||||
top-spacing (when (not chat-screen-loaded?) navigation.style/navigation-bar-height)]
|
||||
window-height (:height (rn/get-window))
|
||||
content-height (- window-height composer.constants/composer-default-height)
|
||||
top-spacing (when (not chat-screen-loaded?) navigation.style/navigation-bar-height)]
|
||||
(if chat-screen-loaded?
|
||||
[:f> f-messages-list-content props]
|
||||
[rn/view {:style {:padding-top top-spacing :flex 1}}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
(defn f-chat
|
||||
[{:keys [show-floating-scroll-down-button? animate-topbar-name?
|
||||
big-name-visible? animate-topbar-opacity? on-end-reached? messages-list-on-layout-finished?]
|
||||
big-name-visible? animate-topbar-opacity? on-end-reached?]
|
||||
:as inner-state-atoms}]
|
||||
(let [insets (safe-area/get-insets)
|
||||
scroll-y (reanimated/use-shared-value 0)
|
||||
@@ -53,21 +53,19 @@
|
||||
:keyboard-vertical-offset (- (:bottom insets))}
|
||||
|
||||
[list.view/message-list-content-view
|
||||
{:chat chat
|
||||
:insets insets
|
||||
:scroll-y scroll-y
|
||||
:content-height content-height
|
||||
:cover-bg-color :turquoise
|
||||
:keyboard-shown? keyboard-shown
|
||||
:inner-state-atoms inner-state-atoms
|
||||
:animate-topbar-name? animate-topbar-name?
|
||||
:big-name-visible? big-name-visible?
|
||||
:animate-topbar-opacity? animate-topbar-opacity?
|
||||
:composer-active? focused?
|
||||
:on-end-reached? on-end-reached?
|
||||
:messages-list-on-layout-finished? messages-list-on-layout-finished?}]
|
||||
{:chat chat
|
||||
:insets insets
|
||||
:scroll-y scroll-y
|
||||
:content-height content-height
|
||||
:cover-bg-color :turquoise
|
||||
:keyboard-shown? keyboard-shown
|
||||
:inner-state-atoms inner-state-atoms
|
||||
:animate-topbar-name? animate-topbar-name?
|
||||
:big-name-visible? big-name-visible?
|
||||
:animate-topbar-opacity? animate-topbar-opacity?
|
||||
:composer-active? focused?
|
||||
:on-end-reached? on-end-reached?}]
|
||||
|
||||
;; TODO(alwx): this part is basically the top bar itself, shown only when scrolled
|
||||
[messages.navigation/navigation-view
|
||||
{:scroll-y scroll-y
|
||||
:animate-topbar-name? animate-topbar-name?
|
||||
@@ -88,8 +86,7 @@
|
||||
[:f> composer.view/composer
|
||||
{:insets insets
|
||||
:scroll-to-bottom-fn list.view/scroll-to-bottom
|
||||
:show-floating-scroll-down-button? show-floating-scroll-down-button?
|
||||
:messages-list-on-layout-finished? messages-list-on-layout-finished?}]
|
||||
:show-floating-scroll-down-button? show-floating-scroll-down-button?}]
|
||||
[contact-requests.bottom-drawer/view chat-id contact-request-state group-chat]))]))
|
||||
|
||||
(defn chat
|
||||
@@ -102,6 +99,5 @@
|
||||
:animate-topbar-name? (reagent/atom false)
|
||||
:big-name-visible? (reagent/atom :initial-render)
|
||||
:animate-topbar-opacity? (reagent/atom false)
|
||||
:on-end-reached? (reagent/atom false)
|
||||
:messages-list-on-layout-finished? (reagent/atom false)}]
|
||||
:on-end-reached? (reagent/atom false)}]
|
||||
[:f> f-chat inner-state-atoms]))
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.home.actions.view :as actions]
|
||||
[status-im2.common.password-authentication.view :as password-authentication]
|
||||
[status-im2.common.scroll-page.style :as scroll-page.style]
|
||||
[status-im2.common.scroll-page.view :as scroll-page]
|
||||
[status-im2.common.top-bar.view :as top-bar]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.communities.actions.chat.view :as chat-actions]
|
||||
[status-im2.contexts.communities.actions.community-options.view :as options]
|
||||
@@ -39,8 +37,8 @@
|
||||
(defn add-category-height
|
||||
[categories-heights category height]
|
||||
(swap! categories-heights
|
||||
(fn [heights]
|
||||
(assoc heights category height))))
|
||||
(fn [heights]
|
||||
(assoc heights category height))))
|
||||
|
||||
(defn collapse-category
|
||||
[community-id category-id collapsed?]
|
||||
@@ -53,22 +51,22 @@
|
||||
(defn- channel-chat-item
|
||||
[community-id community-color
|
||||
{:keys [name emoji muted? id mentions-count unread-messages? on-press locked?] :as chat}]
|
||||
(let [sheet-content [actions/chat-actions
|
||||
(assoc chat
|
||||
:chat-type constants/community-chat-type
|
||||
:chat-id (str community-id id))
|
||||
false]
|
||||
notification (cond
|
||||
muted? :mute
|
||||
(> mentions-count 0) :mention
|
||||
unread-messages? :notification
|
||||
:else nil)
|
||||
channel-options {:name name
|
||||
:emoji emoji
|
||||
:customization-color community-color
|
||||
:mentions-count mentions-count
|
||||
:locked? locked?
|
||||
:notification notification}
|
||||
(let [sheet-content [actions/chat-actions
|
||||
(assoc chat
|
||||
:chat-type constants/community-chat-type
|
||||
:chat-id (str community-id id))
|
||||
false]
|
||||
notification (cond
|
||||
muted? :mute
|
||||
(> mentions-count 0) :mention
|
||||
unread-messages? :notification
|
||||
:else nil)
|
||||
channel-options {:name name
|
||||
:emoji emoji
|
||||
:customization-color community-color
|
||||
:mentions-count mentions-count
|
||||
:locked? locked?
|
||||
:notification notification}
|
||||
channel-sheet-data {:selected-item (fn [] [quo/channel channel-options])
|
||||
:content (fn [] sheet-content)}]
|
||||
[rn/view {:key id}
|
||||
@@ -82,8 +80,8 @@
|
||||
channels-list]
|
||||
[rn/view
|
||||
{:on-layout #(on-first-channel-height-changed
|
||||
(+ 38 (int (Math/ceil (layout-y %))))
|
||||
(into #{} (map (comp :name second) channels-list)))
|
||||
(+ 38 (int (Math/ceil (layout-y %))))
|
||||
(into #{} (map (comp :name second) channels-list)))
|
||||
:style {:margin-top 8 :flex 1}}
|
||||
(for [[category-id {:keys [chats name collapsed?]}] channels-list]
|
||||
[rn/view
|
||||
@@ -106,9 +104,9 @@
|
||||
(defn get-access-type
|
||||
[access]
|
||||
(condp = access
|
||||
constants/community-no-membership-access :open
|
||||
constants/community-no-membership-access :open
|
||||
constants/community-invitation-only-access :invite-only
|
||||
constants/community-on-request-access :request-access
|
||||
constants/community-on-request-access :request-access
|
||||
:unknown-access))
|
||||
|
||||
(defn join-gated-community
|
||||
@@ -124,15 +122,15 @@
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press
|
||||
#(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content
|
||||
(fn []
|
||||
[quo/documentation-drawers
|
||||
{:title (i18n/label :t/token-gated-communities)
|
||||
:show-button? true
|
||||
:button-label (i18n/label :t/read-more)
|
||||
:button-icon :info}
|
||||
[quo/text (i18n/label :t/token-gated-communities-info)]])}])}
|
||||
[:show-bottom-sheet
|
||||
{:content
|
||||
(fn []
|
||||
[quo/documentation-drawers
|
||||
{:title (i18n/label :t/token-gated-communities)
|
||||
:show-button? true
|
||||
:button-label (i18n/label :t/read-more)
|
||||
:button-icon :info}
|
||||
[quo/text (i18n/label :t/token-gated-communities-info)]])}])}
|
||||
[rn/view
|
||||
[quo/icon :i/info {:no-color true}]]])
|
||||
|
||||
@@ -175,9 +173,9 @@
|
||||
(defn join-community
|
||||
[{:keys [joined color permissions token-permissions] :as community}
|
||||
pending?]
|
||||
(let [access-type (get-access-type (:access permissions))
|
||||
(let [access-type (get-access-type (:access permissions))
|
||||
unknown-access? (= access-type :unknown-access)
|
||||
invite-only? (= access-type :invite-only)]
|
||||
invite-only? (= access-type :invite-only)]
|
||||
[:<>
|
||||
(when-not (or joined pending? invite-only? unknown-access?)
|
||||
(if (seq token-permissions)
|
||||
@@ -213,18 +211,18 @@
|
||||
:or {locked? false}
|
||||
:as chat}]
|
||||
(merge
|
||||
chat
|
||||
(when (and (not locked?) id)
|
||||
{:on-press (when joined
|
||||
(fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(debounce/dispatch-and-chill [:chat/navigate-to-chat (str community-id id)]
|
||||
1000)))
|
||||
:on-long-press #(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[chat-actions/actions chat false])}])
|
||||
:community-id community-id})))
|
||||
chat
|
||||
(when (and (not locked?) id)
|
||||
{:on-press (when joined
|
||||
(fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(debounce/dispatch-and-chill [:chat/navigate-to-chat (str community-id id)]
|
||||
1000)))
|
||||
:on-long-press #(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[chat-actions/actions chat false])}])
|
||||
:community-id community-id})))
|
||||
|
||||
(defn add-handlers-to-chats
|
||||
[community-id joined chats]
|
||||
@@ -248,16 +246,16 @@
|
||||
scroll-page.style/picture-border-width
|
||||
12))
|
||||
:margin-bottom 12}
|
||||
:avatar logo
|
||||
:title title
|
||||
:description description
|
||||
:title-accessibility-label :community-title
|
||||
:avatar logo
|
||||
:title title
|
||||
:description description
|
||||
:title-accessibility-label :community-title
|
||||
:description-accessibility-label :community-description}])
|
||||
|
||||
(defn community-content
|
||||
[community]
|
||||
(rf/dispatch [:communities/check-all-community-channels-permissions (:id community)])
|
||||
(fn [{:keys [name description joined images tags color id token-permissions] :as community}
|
||||
(fn [{:keys [name description joined images tags color id] :as community}
|
||||
pending?
|
||||
{:keys [on-category-layout
|
||||
collapsed?
|
||||
@@ -275,14 +273,12 @@
|
||||
:last-item-style style/last-community-tag
|
||||
:container-style style/community-tag-container}])
|
||||
[join-community community pending?]]
|
||||
(when (or (and (seq token-permissions) joined)
|
||||
(empty? token-permissions))
|
||||
[channel-list-component
|
||||
{:on-category-layout on-category-layout
|
||||
:community-id id
|
||||
:community-color color
|
||||
:on-first-channel-height-changed on-first-channel-height-changed}
|
||||
(add-handlers-to-categorized-chats id chats-by-category joined)])])))
|
||||
[channel-list-component
|
||||
{:on-category-layout on-category-layout
|
||||
:community-id id
|
||||
:community-color color
|
||||
:on-first-channel-height-changed on-first-channel-height-changed}
|
||||
(add-handlers-to-categorized-chats id chats-by-category joined)]])))
|
||||
|
||||
(defn sticky-category-header
|
||||
[_]
|
||||
@@ -302,8 +298,8 @@
|
||||
[{:icon-name :i/options
|
||||
:accessibility-label :community-options-for-community
|
||||
:on-press #(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [options/community-options-bottom-sheet id])}])}])
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [options/community-options-bottom-sheet id])}])}])
|
||||
|
||||
(defn pick-first-category-by-height
|
||||
[scroll-height first-channel-height categories-heights]
|
||||
@@ -315,18 +311,18 @@
|
||||
|
||||
(defn community-scroll-page
|
||||
[{:keys [joined]}]
|
||||
(let [scroll-height (reagent/atom 0)
|
||||
categories-heights (reagent/atom {})
|
||||
(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]
|
||||
initial-joined? joined]
|
||||
(fn [{:keys [id name images] :as community} pending?]
|
||||
(let [cover {:uri (get-in images [:banner :uri])}
|
||||
logo {:uri (get-in images [:thumbnail :uri])}
|
||||
collapsed? (and initial-joined? (:joined community))
|
||||
(let [cover {:uri (get-in images [:banner :uri])}
|
||||
logo {:uri (get-in images [:thumbnail :uri])}
|
||||
collapsed? (and initial-joined? (:joined community))
|
||||
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))]
|
||||
[scroll-page/scroll-page
|
||||
{:cover-image cover
|
||||
@@ -345,14 +341,14 @@
|
||||
:sticky-header [sticky-category-header
|
||||
{:enabled (> @scroll-height @first-channel-height)
|
||||
:label (pick-first-category-by-height
|
||||
@scroll-height
|
||||
@first-channel-height
|
||||
@categories-heights)}]}
|
||||
@scroll-height
|
||||
@first-channel-height
|
||||
@categories-heights)}]}
|
||||
[community-content
|
||||
community
|
||||
pending?
|
||||
{:on-category-layout (partial add-category-height categories-heights)
|
||||
:collapsed? collapsed?
|
||||
{:on-category-layout (partial add-category-height categories-heights)
|
||||
: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.
|
||||
@@ -364,12 +360,12 @@
|
||||
[id]
|
||||
(let [{:keys [id]
|
||||
:as community} (rf/sub [:communities/community id])
|
||||
pending? (rf/sub [:communities/my-pending-request-to-join id])]
|
||||
pending? (rf/sub [:communities/my-pending-request-to-join id])]
|
||||
[community-scroll-page community pending?]))
|
||||
|
||||
(defn overview
|
||||
[id]
|
||||
(let [id (or id (rf/sub [:get-screen-params :community-overview]))
|
||||
(let [id (or id (rf/sub [:get-screen-params :community-overview]))
|
||||
customization-color (rf/sub [:profile/customization-color])]
|
||||
[rn/view {:style style/community-overview-container}
|
||||
[community-card-page-view id]
|
||||
@@ -379,54 +375,3 @@
|
||||
:label (i18n/label :t/jump-to)}}
|
||||
{:position :absolute
|
||||
:bottom 34}]]))
|
||||
|
||||
(defn list-header [{:keys [scroll-y theme]}]
|
||||
[top-bar/abstract-list-header
|
||||
{:inverted-y-on-android? false
|
||||
:show? true
|
||||
:theme theme
|
||||
:cover-bg-color :red
|
||||
:scroll-y scroll-y
|
||||
:on-layout (fn []
|
||||
)
|
||||
:avatar-component (fn []
|
||||
)
|
||||
:title-component (fn []
|
||||
)
|
||||
:description "test"
|
||||
:actions-component (fn []
|
||||
)
|
||||
:loading-component (fn []
|
||||
)
|
||||
}])
|
||||
|
||||
(defn overview-content-view [{:keys [id community customization-color chats-by-category
|
||||
scroll-y content-height]}]
|
||||
(let [categories-heights (reagent/atom {})
|
||||
first-channel-height (reagent/atom 0)]
|
||||
[rn/scroll-view
|
||||
[channel-list-component
|
||||
{:on-category-layout (partial add-category-height categories-heights)
|
||||
:community-id id
|
||||
:community-color (:color community)
|
||||
:on-first-channel-height-changed (fn [height categories]
|
||||
(swap! categories-heights select-keys categories)
|
||||
(reset! first-channel-height height))}
|
||||
(add-handlers-to-categorized-chats id chats-by-category (:joined community))]]))
|
||||
|
||||
(defn overview-old
|
||||
[id]
|
||||
(let [scroll-y (reanimated/use-shared-value 0)
|
||||
content-height (reanimated/use-shared-value 0)
|
||||
id (or id (rf/sub [:get-screen-params :community-overview]))
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
community (rf/sub [:communities/community id])
|
||||
chats-by-category (rf/sub [:communities/categorized-channels id])]
|
||||
[rn/view {:style style/community-overview-container}
|
||||
[overview-content-view
|
||||
{:id id
|
||||
:customization-color customization-color
|
||||
:community community
|
||||
:chats-by-category chats-by-category
|
||||
:scroll-y scroll-y
|
||||
:content-height content-height}]]))
|
||||
@@ -1,20 +1,15 @@
|
||||
(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.security.core :as security]))
|
||||
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn page-title
|
||||
[]
|
||||
@@ -26,39 +21,22 @@
|
||||
:description-accessibility-label :enable-biometrics-sub-title}])
|
||||
|
||||
(defn enable-biometrics-buttons
|
||||
[insets theme]
|
||||
[insets]
|
||||
(let [supported-biometric-type (rf/sub [:biometric/supported-type])
|
||||
bio-type-label (biometric/get-label-by-type supported-biometric-type)
|
||||
profile-color (or (:color (rf/sub [:onboarding-2/profile]))
|
||||
(rf/sub [:profile/customization-color]))
|
||||
syncing-results? (= :syncing-results @state/root-id)]
|
||||
profile-color (:color (rf/sub [:onboarding-2/profile]))]
|
||||
[rn/view {:style (style/buttons insets)}
|
||||
[standard-auth/button
|
||||
(merge
|
||||
{:size 40
|
||||
:accessibility-label :enable-biometrics-button
|
||||
:icon-left :i/face-id
|
||||
:customization-color profile-color
|
||||
:button-label (i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})}
|
||||
(if syncing-results?
|
||||
{:theme theme
|
||||
:blur? true
|
||||
:on-enter-password (fn [entered-password]
|
||||
(rf/dispatch
|
||||
[:onboarding-2/authenticate-enable-biometrics
|
||||
(security/safe-unmask-data
|
||||
entered-password)])
|
||||
(rf/dispatch [:hide-bottom-sheet]))
|
||||
:auth-button-label (i18n/label :t/confirm)}
|
||||
{:on-press #(rf/dispatch [:onboarding-2/enable-biometrics])}))]
|
||||
[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})]
|
||||
[quo/button
|
||||
{:accessibility-label :maybe-later-button
|
||||
:background :blur
|
||||
:type :grey
|
||||
:on-press #(rf/dispatch (if syncing-results?
|
||||
[:navigate-to-within-stack
|
||||
[:enable-notifications :enable-biometrics]]
|
||||
[:onboarding-2/create-account-and-login]))
|
||||
:on-press #(rf/dispatch [:onboarding-2/create-account-and-login])
|
||||
:container-style {:margin-top 12}}
|
||||
(i18n/label :t/maybe-later)]]))
|
||||
|
||||
@@ -77,19 +55,12 @@
|
||||
:style (style/page-illustration width)
|
||||
:source (resources/get-image :biometrics)}]))
|
||||
|
||||
(defn f-enable-biometrics
|
||||
[{:keys [theme]}]
|
||||
(defn enable-biometrics
|
||||
[]
|
||||
(let [insets (safe-area/get-insets)]
|
||||
[rn/view {:style (style/page-container insets)}
|
||||
[page-title]
|
||||
(if whitelist/whitelisted?
|
||||
[enable-biometrics-parallax]
|
||||
[enable-biometrics-simple])
|
||||
[enable-biometrics-buttons insets theme]]))
|
||||
|
||||
|
||||
(defn- internale-enable-biometrics
|
||||
[params]
|
||||
[:f> f-enable-biometrics params])
|
||||
|
||||
(def view (quo.theme/with-theme internale-enable-biometrics))
|
||||
[enable-biometrics-buttons insets]]))
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
[quo/page-nav
|
||||
{:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press #(rf/dispatch [:navigate-back-within-stack :enable-biometrics])}]
|
||||
:on-press #(rf/dispatch [:navigate-back-within-stack :new-to-status])}]
|
||||
[page-title]
|
||||
[rn/view {:style style/page-illustration}
|
||||
[quo/text
|
||||
|
||||
@@ -35,30 +35,11 @@
|
||||
{: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]}]
|
||||
(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])}))
|
||||
{:db (assoc-in db [:onboarding-2/profile :auth-method] constants/auth-method-biometric)
|
||||
:dispatch [:onboarding-2/create-account-and-login]})
|
||||
|
||||
(rf/defn biometrics-fail
|
||||
{:events [:onboarding-2/biometrics-fail]}
|
||||
@@ -102,14 +83,6 @@
|
||||
[: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]
|
||||
@@ -147,7 +120,6 @@
|
||||
[{: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)}
|
||||
@@ -155,10 +127,7 @@
|
||||
(assoc :keychain/save-password-and-auth-method
|
||||
{:key-uid key-uid
|
||||
:masked-password masked-password
|
||||
:on-success (fn []
|
||||
(if syncing?
|
||||
(rf/dispatch [:onboarding-2/navigate-to-enable-notifications])
|
||||
(log/error "successfully saved biometrics")))
|
||||
:on-success #(log/debug "successfully saved biometric")
|
||||
: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 [:onboarding-2/navigate-to-enable-biometrics]))
|
||||
(rf/dispatch [:open-modal :enable-notifications]))
|
||||
:accessibility-label :continue-button
|
||||
:customization-color profile-color
|
||||
:container-style style/continue-button}
|
||||
|
||||
@@ -73,12 +73,12 @@
|
||||
(data-store.settings/rpc->settings settings)
|
||||
profile-overview (profile.rpc/rpc->profiles-overview account)]
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc :chats/loading? true
|
||||
:networks/current-network current-network
|
||||
:networks/networks (merge networks config/default-networks-by-id)
|
||||
:profile/profile (merge profile-overview settings))
|
||||
(assoc-in [:wallet :ui :tokens-loading?] true))}
|
||||
{:db (assoc db
|
||||
:chats/loading? true
|
||||
:networks/current-network current-network
|
||||
:wallet/tokens-loading? true
|
||||
:networks/networks (merge networks config/default-networks-by-id)
|
||||
:profile/profile (merge profile-overview settings))}
|
||||
(notifications/load-preferences)
|
||||
(data-store.chats/fetch-chats-preview
|
||||
{:on-success
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [theme title]}]
|
||||
[{:keys [theme]}]
|
||||
(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 title
|
||||
:title "quo components preview"
|
||||
: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 (dissoc @state :value) (:value @state)]])))
|
||||
[quo/step @state (:value @state)]])))
|
||||
|
||||
@@ -153,7 +153,6 @@
|
||||
[status-im2.contexts.quo-preview.tags.number-tag :as number-tag]
|
||||
[status-im2.contexts.quo-preview.tags.permission-tag :as permission-tag]
|
||||
[status-im2.contexts.quo-preview.tags.status-tags :as status-tags]
|
||||
[status-im2.contexts.quo-preview.tags.summary-tag :as summary-tag]
|
||||
[status-im2.contexts.quo-preview.tags.tag :as tag]
|
||||
[status-im2.contexts.quo-preview.tags.tags :as tags]
|
||||
[status-im2.contexts.quo-preview.tags.tiny-tag :as tiny-tag]
|
||||
@@ -434,8 +433,6 @@
|
||||
:component permission-tag/view}
|
||||
{:name :status-tags
|
||||
:component status-tags/view}
|
||||
{:name :summary-tag
|
||||
:component summary-tag/view}
|
||||
{:name :tag
|
||||
:component tag/view}
|
||||
{:name :tags
|
||||
@@ -487,7 +484,7 @@
|
||||
(defn- main-screen
|
||||
[]
|
||||
[:<>
|
||||
[common/navigation-bar {:title "Quo components preview"}]
|
||||
[common/navigation-bar]
|
||||
[rn/scroll-view {:style (style/main)}
|
||||
(for [category (sort screens-categories)]
|
||||
^{:key (first category)}
|
||||
|
||||
@@ -317,10 +317,10 @@
|
||||
children)])
|
||||
|
||||
(defn- f-preview-container
|
||||
[{:keys [title state descriptor blur? blur-dark-only?
|
||||
[{:keys [state descriptor blur? blur-dark-only?
|
||||
component-container-style
|
||||
blur-container-style blur-view-props blur-height show-blur-background?]
|
||||
:or {blur-height 200 title "quo component"}}
|
||||
:or {blur-height 200}}
|
||||
& 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 {:title title}]
|
||||
[common/navigation-bar]
|
||||
[rn/scroll-view
|
||||
{:style (style/panel-basic)
|
||||
:shows-vertical-scroll-indicator false}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
(ns status-im2.contexts.quo-preview.tags.summary-tag
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.resources :as quo.resources]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||
|
||||
(defn data
|
||||
[type]
|
||||
(case type
|
||||
:token
|
||||
{:customization-color "#9999991A"
|
||||
:label "150 ETH"
|
||||
:image-source (quo.resources/get-token :eth)}
|
||||
:address
|
||||
{:label "0x39c...Bd2"}
|
||||
:user
|
||||
{:image-source (resources/mock-images :user-picture-male4)
|
||||
:customization-color :blue
|
||||
:label "Mark Libot"}
|
||||
:collectible
|
||||
{:label "Isekai #1"
|
||||
:image-source (resources/mock-images :collectible2)
|
||||
:customization-color :yellow}
|
||||
:network
|
||||
{:image-source (quo.resources/get-network :ethereum)
|
||||
:label "Mainnet"}
|
||||
:saved-address
|
||||
{:customization-color :flamingo
|
||||
:label "Peter Lambo"}
|
||||
:account
|
||||
{:label "Account"
|
||||
:emoji "🍿"
|
||||
:customization-color :purple}))
|
||||
|
||||
(def descriptor
|
||||
[{:key :type
|
||||
:type :select
|
||||
:options [{:value "Token"
|
||||
:key :token}
|
||||
{:value "Address"
|
||||
:key :address}
|
||||
{:value "User"
|
||||
:key :user}
|
||||
{:value "Collectible"
|
||||
:key :collectible}
|
||||
{:value "Network"
|
||||
:key :network}
|
||||
{:value "Saved address"
|
||||
:key :saved-address}
|
||||
{:value "Account"
|
||||
:key :account}]}])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [state (reagent/atom
|
||||
(merge {:type :token}
|
||||
(data :token)))]
|
||||
(fn []
|
||||
[preview/preview-container
|
||||
{:state state
|
||||
:descriptor descriptor}
|
||||
[rn/view {:style {:align-items :center}}
|
||||
[quo/summary-tag (merge @state (data (:type @state)))]]])))
|
||||
@@ -1,22 +0,0 @@
|
||||
(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)})
|
||||
@@ -1,55 +0,0 @@
|
||||
(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"]]]])))
|
||||
@@ -1,59 +0,0 @@
|
||||
(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}])
|
||||
@@ -1,10 +0,0 @@
|
||||
(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.core :as standard-auth]
|
||||
[status-im2.common.standard-authentication.standard-auth.view :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/slide-button
|
||||
[standard-auth/view
|
||||
{:blur? true
|
||||
:size :size-40
|
||||
:track-text (i18n/label :t/slide-to-reveal-code)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
(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,7 +3,18 @@
|
||||
(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,6 +3,7 @@
|
||||
[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]
|
||||
@@ -13,12 +14,13 @@
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
(defn- view-internal
|
||||
[]
|
||||
(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 %)
|
||||
@@ -38,9 +40,8 @@
|
||||
: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/add-watched-address
|
||||
:bottom-action-label :t/create-account
|
||||
:bottom-action-props {:customization-color @account-color
|
||||
:disabled? (string/blank? @account-name)
|
||||
:on-press #(re-frame/dispatch [:navigate-to
|
||||
@@ -50,9 +51,11 @@
|
||||
:right-icon :i/advanced
|
||||
:icon-right? true
|
||||
:emoji @account-emoji
|
||||
:title (i18n/label :t/watched-address)
|
||||
:title address-title
|
||||
: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,24 +5,13 @@
|
||||
: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,114 +2,51 @@
|
||||
(: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 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
|
||||
(defn view-internal
|
||||
[]
|
||||
(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]))
|
||||
(let [input-value (reagent/atom "")
|
||||
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 (fn []
|
||||
(rf/dispatch [:wallet/clean-scanned-address])
|
||||
(rf/dispatch [:navigate-back]))}]
|
||||
:on-press #(rf/dispatch [:navigate-back])}]
|
||||
[quo/text-combinations
|
||||
{:container-style style/header-container
|
||||
:title (i18n/label :t/add-address)
|
||||
:description (i18n/label :t/enter-eth)}]
|
||||
[:f> address-input
|
||||
{:input-value input-value
|
||||
:validate validate
|
||||
:validation-msg validation-msg
|
||||
:clear-input clear-input}]
|
||||
(when @validation-msg
|
||||
[quo/info-message
|
||||
{:accessibility-label :error-message
|
||||
:size :default
|
||||
:icon :i/info
|
||||
:type :error
|
||||
:style style/info-message}
|
||||
@validation-msg])
|
||||
[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]]
|
||||
[quo/button
|
||||
{:customization-color customization-color
|
||||
:disabled? (string/blank? @input-value)
|
||||
:on-press #(rf/dispatch [:navigate-to
|
||||
:confirm-address-to-watch
|
||||
{:address @input-value}])
|
||||
:disabled? (clojure.string/blank? @input-value)
|
||||
:on-press #(re-frame/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,5 +1,6 @@
|
||||
(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]
|
||||
@@ -7,12 +8,12 @@
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
(defn- view-internal
|
||||
[{: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 watch-only?]} & children]
|
||||
custom-bottom-action]} & children]
|
||||
(let [{:keys [top bottom]} (safe-area/get-insets)
|
||||
margin-top (if (false? margin-top?) 0 top)
|
||||
{window-width :width} (rn/get-window)]
|
||||
@@ -35,7 +36,7 @@
|
||||
{:customization-color account-color
|
||||
:size 80
|
||||
:emoji account-emoji
|
||||
:type (if watch-only? :watch-only :default)}]
|
||||
:type :default}]
|
||||
[quo/button
|
||||
{:size 32
|
||||
:type :grey
|
||||
@@ -80,3 +81,5 @@
|
||||
:type :primary}
|
||||
bottom-action-props)
|
||||
(i18n/label bottom-action-label)])])]))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
(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-internal
|
||||
[{:keys [theme]}]
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [name color emoji address]} (rf/sub [:wallet/current-viewing-account])]
|
||||
[:<>
|
||||
[quo/drawer-top
|
||||
@@ -29,13 +26,7 @@
|
||||
:on-press #(rf/dispatch [:navigate-to :wallet-edit-account])}
|
||||
{:icon :i/copy
|
||||
:accessibility-label :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))}
|
||||
:label (i18n/label :t/copy-address)}
|
||||
{:icon :i/share
|
||||
:accessibility-label :share-account
|
||||
:label (i18n/label :t/share-account)}
|
||||
@@ -51,5 +42,3 @@
|
||||
{: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))
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
(ns status-im2.contexts.wallet.common.utils
|
||||
(:require [clojure.string :as string]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.number]))
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(defn get-first-name
|
||||
[full-name]
|
||||
(first (string/split full-name #" ")))
|
||||
|
||||
(defn get-balance-by-address
|
||||
[balances address]
|
||||
(->> balances
|
||||
(filter #(= (:address %) address))
|
||||
first
|
||||
:balance))
|
||||
|
||||
(defn get-account-by-address
|
||||
[accounts address]
|
||||
(some #(when (= (:address %) address) %) accounts))
|
||||
|
||||
(defn prettify-balance
|
||||
[balance]
|
||||
(str "$" (.toFixed (if (number? balance) balance 0) 2)))
|
||||
@@ -22,24 +32,3 @@
|
||||
[number-of-accounts]
|
||||
(let [path (get-derivation-path number-of-accounts)]
|
||||
(format-derivation-path path)))
|
||||
|
||||
(defn- calculate-raw-balance
|
||||
[raw-balance decimals]
|
||||
(if-let [n (utils.number/parse-int raw-balance nil)]
|
||||
(/ n (Math/pow 10 (utils.number/parse-int decimals)))
|
||||
0))
|
||||
|
||||
(defn- total-token-value-in-all-chains
|
||||
[{:keys [balances-per-chain decimals]}]
|
||||
(->> balances-per-chain
|
||||
(vals)
|
||||
(map #(calculate-raw-balance (:raw-balance %) decimals))
|
||||
(reduce +)))
|
||||
|
||||
(defn calculate-balance
|
||||
[tokens-in-account]
|
||||
(->> tokens-in-account
|
||||
(map (fn [token]
|
||||
(* (total-token-value-in-all-chains token)
|
||||
(-> token :market-values-per-currency :usd :price))))
|
||||
(reduce +)))
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
(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.core :as standard-auth]
|
||||
[status-im2.common.standard-authentication.standard-auth.view :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/slide-button
|
||||
[standard-auth/view
|
||||
{:size :size-48
|
||||
:track-text (i18n/label :t/slide-to-create-account)
|
||||
:customization-color @account-color
|
||||
|
||||
@@ -4,33 +4,29 @@
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.wallet.common.screen-base.create-or-edit-account.view
|
||||
:as create-or-edit-account]
|
||||
[status-im2.contexts.wallet.common.sheets.network-preferences.view
|
||||
:as network-preferences]
|
||||
[status-im2.contexts.wallet.common.screen-base.create-or-edit-account.view :as
|
||||
create-or-edit-account]
|
||||
[status-im2.contexts.wallet.common.sheets.network-preferences.view :as network-preferences]
|
||||
[status-im2.contexts.wallet.edit-account.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- show-save-account-toast
|
||||
[updated-key theme]
|
||||
(let [message (case updated-key
|
||||
:name :t/edit-wallet-account-name-updated-message
|
||||
:color :t/edit-wallet-account-colour-updated-message
|
||||
:emoji :t/edit-wallet-account-emoji-updated-message
|
||||
nil)]
|
||||
(rf/dispatch [:toasts/upsert
|
||||
{:id :edit-account
|
||||
:icon :i/correct
|
||||
:icon-color (colors/resolve-color :success theme)
|
||||
:text (i18n/label message)}])))
|
||||
|
||||
(defn- save-account
|
||||
[{:keys [account updated-key new-value theme]}]
|
||||
(let [edited-account-data (assoc account updated-key new-value)]
|
||||
[{:keys [theme type value account]}]
|
||||
(let [edited-account-data (assoc account type value)
|
||||
message (case type
|
||||
:name :t/edit-wallet-account-name-updated-message
|
||||
:color :t/edit-wallet-account-colour-updated-message
|
||||
:emoji :t/edit-wallet-account-emoji-updated-message
|
||||
nil)
|
||||
callback #(rf/dispatch [:toasts/upsert
|
||||
{:id :edit-account
|
||||
:icon :i/correct
|
||||
:icon-color (colors/resolve-color :success theme)
|
||||
:text (i18n/label message)}])]
|
||||
(rf/dispatch [:wallet/save-account
|
||||
{:account edited-account-data
|
||||
:on-success #(show-save-account-toast updated-key theme)}])))
|
||||
{:account edited-account-data
|
||||
:callback callback}])))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [theme]}]
|
||||
@@ -38,22 +34,22 @@
|
||||
show-confirm-button? (reagent/atom false)
|
||||
on-change-color (fn [edited-color {:keys [color] :as account}]
|
||||
(when (not= edited-color color)
|
||||
(save-account {:account account
|
||||
:updated-key :color
|
||||
:new-value edited-color
|
||||
:theme theme})))
|
||||
(save-account {:account account
|
||||
:type :color
|
||||
:value edited-color
|
||||
:theme theme})))
|
||||
on-change-emoji (fn [edited-emoji {:keys [emoji] :as account}]
|
||||
(when (not= edited-emoji emoji)
|
||||
(save-account {:account account
|
||||
:updated-key :emoji
|
||||
:new-value edited-emoji
|
||||
:theme theme})))
|
||||
(save-account {:account account
|
||||
:type :emoji
|
||||
:value edited-emoji
|
||||
:theme theme})))
|
||||
on-confirm-name (fn [account]
|
||||
(rn/dismiss-keyboard!)
|
||||
(save-account {:account account
|
||||
:updated-key :name
|
||||
:new-value @edited-account-name
|
||||
:theme theme}))]
|
||||
(save-account {:account account
|
||||
:type :name
|
||||
:value @edited-account-name
|
||||
:theme theme}))]
|
||||
(fn []
|
||||
(let [{:keys [name emoji address color]
|
||||
:as account} (rf/sub [:wallet/current-viewing-account])
|
||||
@@ -86,18 +82,17 @@
|
||||
:right-icon :i/advanced
|
||||
:card? true
|
||||
:title (i18n/label :t/address)
|
||||
:custom-subtitle (fn []
|
||||
[quo/address-text
|
||||
{:networks [{:network-name :ethereum :short-name "eth"}
|
||||
{:network-name :optimism :short-name "opt"}
|
||||
{:network-name :arbitrum :short-name "arb1"}]
|
||||
:address address
|
||||
:format :long}])
|
||||
:custom-subtitle (fn [] [quo/address-text
|
||||
{:networks [{:network-name :ethereum :short-name "eth"}
|
||||
{:network-name :optimism :short-name "opt"}
|
||||
{:network-name :arbitrum :short-name "arb1"}]
|
||||
:address address
|
||||
:format :long}])
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn []
|
||||
[network-preferences/view
|
||||
{:on-save #(js/alert "calling on save")}])}]))
|
||||
{:content (fn [] [network-preferences/view
|
||||
{:on-save #(js/alert
|
||||
"calling on save")}])}]))
|
||||
:container-style style/data-item}]]))))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
[taoensso.timbre :as log]
|
||||
[utils.ethereum.chain :as chain]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.number]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]
|
||||
[utils.transforms :as types]))
|
||||
@@ -41,8 +40,7 @@
|
||||
{:db (update db :wallet dissoc :current-viewing-account-address)
|
||||
:fx [[:dispatch [:navigate-back]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/get-accounts-success
|
||||
(rf/reg-event-fx :wallet/get-accounts-success
|
||||
(fn [{:keys [db]} [accounts]]
|
||||
(let [wallet-db (get db :wallet)
|
||||
new-account? (:new-account? wallet-db)
|
||||
@@ -63,8 +61,7 @@
|
||||
{:dispatch [:wallet/navigate-to-account navigate-to-account]
|
||||
:ms 300}]])))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/get-accounts
|
||||
(rf/reg-event-fx :wallet/get-accounts
|
||||
(fn [_]
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "accounts_getAccounts"
|
||||
@@ -75,54 +72,35 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/save-account
|
||||
(fn [_ [{:keys [account on-success]}]]
|
||||
(fn [_ [{:keys [account callback]}]]
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "accounts_saveAccount"
|
||||
:params [(data-store/<-account account)]
|
||||
:on-success (fn []
|
||||
(rf/dispatch [:wallet/get-accounts])
|
||||
(when (fn? on-success)
|
||||
(on-success)))
|
||||
(when (fn? callback)
|
||||
(callback)))
|
||||
:on-error #(log/info "failed to save account "
|
||||
{:error %
|
||||
:event :wallet/save-account})}]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/get-wallet-token
|
||||
(rf/reg-event-fx :wallet/get-wallet-token
|
||||
(fn [{:keys [db]}]
|
||||
(let [addresses (->> (get-in db [:wallet :accounts])
|
||||
vals
|
||||
(map :address))]
|
||||
(let [addresses (map :address (vals (get-in db [:wallet :accounts])))]
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "wallet_getWalletToken"
|
||||
:params [addresses]
|
||||
:on-success [:wallet/store-wallet-token]
|
||||
:on-success [:wallet/get-wallet-token-success]
|
||||
:on-error #(log/info "failed to get wallet token "
|
||||
{:error %
|
||||
:event :wallet/get-wallet-token
|
||||
:params addresses})}]]]})))
|
||||
|
||||
(defn- fix-chain-id-keys
|
||||
[token]
|
||||
(update token :balances-per-chain update-keys (comp utils.number/parse-int name)))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/store-wallet-token
|
||||
(fn [{:keys [db]} [raw-tokens-data]]
|
||||
(let [tokens (-> raw-tokens-data
|
||||
(update-keys name)
|
||||
(update-vals #(cske/transform-keys csk/->kebab-case %))
|
||||
(update-vals #(mapv fix-chain-id-keys %)))
|
||||
add-tokens (fn [stored-accounts tokens-per-account]
|
||||
(reduce-kv (fn [accounts address tokens-data]
|
||||
(if (accounts address)
|
||||
(update accounts address assoc :tokens tokens-data)
|
||||
accounts))
|
||||
stored-accounts
|
||||
tokens-per-account))]
|
||||
{:db (-> db
|
||||
(update-in [:wallet :accounts] add-tokens tokens)
|
||||
(assoc-in [:wallet :ui :tokens-loading?] false))})))
|
||||
(rf/reg-event-fx :wallet/get-wallet-token-success
|
||||
(fn [{:keys [db]} [tokens]]
|
||||
{:db (assoc db
|
||||
:wallet/tokens tokens
|
||||
:wallet/tokens-loading? false)}))
|
||||
|
||||
(rf/defn scan-address-success
|
||||
{:events [:wallet/scan-address-success]}
|
||||
@@ -144,8 +122,7 @@
|
||||
:on-success on-success
|
||||
:on-error #(log/info "failed to derive address " %)}]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/add-account
|
||||
(rf/reg-event-fx :wallet/add-account
|
||||
(fn [{:keys [db]} [password {:keys [emoji account-name color]} {:keys [public-key address path]}]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])
|
||||
sha3-pwd (native-module/sha3 (security/safe-unmask-data password))
|
||||
@@ -169,12 +146,11 @@
|
||||
:on-success [:wallet/get-accounts]
|
||||
:on-error #(log/info "failed to create account " %)}]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/derive-address-and-add-account
|
||||
(rf/reg-event-fx :wallet/derive-address-and-add-account
|
||||
(fn [_ [password account-details]]
|
||||
(let [on-success (fn [derived-address-details]
|
||||
(let [on-success (fn [derived-adress-details]
|
||||
(rf/dispatch [:wallet/add-account password account-details
|
||||
(first derived-address-details)]))]
|
||||
(first derived-adress-details)]))]
|
||||
{:fx [[:dispatch [:wallet/create-derived-addresses password account-details on-success]]]})))
|
||||
|
||||
(rf/defn get-ethereum-chains
|
||||
@@ -223,8 +199,7 @@
|
||||
|
||||
(rf/reg-event-fx :wallet/clear-stored-collectibles clear-stored-collectibles)
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/request-collectibles
|
||||
(rf/reg-event-fx :wallet/request-collectibles
|
||||
(fn [{:keys [db]} [{:keys [start-at-index new-request?]}]]
|
||||
(let [request-id 0
|
||||
collectibles-filter nil
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
[status-im2.contexts.wallet.common.activity-tab.view :as activity]
|
||||
[status-im2.contexts.wallet.common.collectibles-tab.view :as collectibles]
|
||||
[status-im2.contexts.wallet.common.temp :as temp]
|
||||
[status-im2.contexts.wallet.common.utils :as utils]
|
||||
[status-im2.contexts.wallet.home.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
@@ -27,9 +28,9 @@
|
||||
:on-press #(rf/dispatch [:navigate-to :add-address-to-watch])
|
||||
:add-divider? true}]]])
|
||||
|
||||
(defn- new-account-card-data
|
||||
[]
|
||||
{:customization-color (rf/sub [:profile/customization-color])
|
||||
(defn- add-account-placeholder
|
||||
[color]
|
||||
{:customization-color color
|
||||
:on-press #(rf/dispatch [:show-bottom-sheet {:content new-account}])
|
||||
:type :add-account})
|
||||
|
||||
@@ -38,6 +39,21 @@
|
||||
{:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}
|
||||
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}])
|
||||
|
||||
(defn account-cards
|
||||
[{:keys [accounts loading? balances profile-color]}]
|
||||
(let [accounts-with-balances
|
||||
(mapv
|
||||
(fn [{:keys [color address] :as account}]
|
||||
(assoc account
|
||||
:customization-color color
|
||||
:type :empty
|
||||
:on-press #(rf/dispatch [:wallet/navigate-to-account address])
|
||||
:loading? loading?
|
||||
:balance (utils/prettify-balance
|
||||
(utils/get-balance-by-address balances address))))
|
||||
accounts)]
|
||||
(conj accounts-with-balances (add-account-placeholder profile-color))))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(rf/dispatch [:wallet/request-collectibles
|
||||
@@ -46,10 +62,14 @@
|
||||
(let [top (safe-area/get-top)
|
||||
selected-tab (reagent/atom (:id (first tabs-data)))]
|
||||
(fn []
|
||||
(let [networks (rf/sub [:wallet/network-details])
|
||||
account-cards-data (rf/sub [:wallet/account-cards-data])
|
||||
cards (conj account-cards-data (new-account-card-data))]
|
||||
[rn/view {:style {:margin-top top :flex 1}}
|
||||
(let [accounts (rf/sub [:wallet/accounts])
|
||||
loading? (rf/sub [:wallet/tokens-loading?])
|
||||
balances (rf/sub [:wallet/balances])
|
||||
profile-color (rf/sub [:profile/customization-color])
|
||||
networks (rf/sub [:wallet/network-details])]
|
||||
[rn/view
|
||||
{:style {:margin-top top
|
||||
:flex 1}}
|
||||
[common.top-nav/view]
|
||||
[rn/view {:style style/overview-container}
|
||||
[quo/wallet-overview (temp/wallet-overview-state networks)]]
|
||||
@@ -60,7 +80,10 @@
|
||||
[rn/flat-list
|
||||
{:style style/accounts-list
|
||||
:content-container-style style/accounts-list-container
|
||||
:data cards
|
||||
:data (account-cards {:accounts accounts
|
||||
:loading? loading?
|
||||
:balances balances
|
||||
:profile-color profile-color})
|
||||
:horizontal true
|
||||
:separator [rn/view {:style {:width 12}}]
|
||||
:render-fn quo/account-card
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
(fn []
|
||||
(let [scanned-address (rf/sub [:wallet/scanned-address])
|
||||
send-address (rf/sub [:wallet/send-address])
|
||||
valid-ens-or-address? (rf/sub [:wallet/valid-ens-or-address?])]
|
||||
valid-ens-or-address? (boolean (rf/sub [:wallet/valid-ens-or-address?]))]
|
||||
[quo/address-input
|
||||
{:on-focus #(reset! input-focused? true)
|
||||
:on-blur #(reset! input-focused? false)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
(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.add-address-to-watch.component-spec]
|
||||
[status-im2.contexts.wallet.create-account.edit-derivation-path.component-spec]))
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
(ns status-im2.integration-test.chat-test
|
||||
(ns status-im2.integration-test.chat
|
||||
(:require
|
||||
[cljs.test :refer [deftest is]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
@@ -16,7 +16,7 @@
|
||||
"0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd")
|
||||
|
||||
(deftest one-to-one-chat-test
|
||||
(h/log-headline :one-to-one-chat-test)
|
||||
(h/log-headline one-to-one-chat-test)
|
||||
(rf-test/run-test-async
|
||||
(h/with-app-initialized
|
||||
(h/with-account
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(ns status-im2.integration-test.community-test
|
||||
(ns status-im2.integration-test.community
|
||||
(:require [cljs.test :refer [deftest]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
[re-frame.core :as rf]
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
(ns status-im2.integration-test.core-test
|
||||
(ns status-im2.integration-test.core
|
||||
(:require
|
||||
[cljs.test :refer [deftest]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
@@ -8,14 +8,17 @@
|
||||
status-im.subs.root
|
||||
[status-im.utils.test :as utils.test]
|
||||
status-im2.events
|
||||
status-im2.integration-test.chat
|
||||
status-im2.integration-test.wallet
|
||||
status-im2.navigation.core
|
||||
status-im2.subs.root
|
||||
[test-helpers.integration :as h]))
|
||||
|
||||
(utils.test/init!)
|
||||
|
||||
(deftest initialize-app-test
|
||||
(h/log-headline :initialize-app-test)
|
||||
(rf-test/run-test-async
|
||||
(utils.test/init!)
|
||||
(rf/dispatch [:app-started])
|
||||
(rf-test/wait-for
|
||||
;; use initialize-view because it has the longest avg. time and
|
||||
+4
-8
@@ -1,6 +1,6 @@
|
||||
(ns status-im2.integration-test.wallet-test
|
||||
(ns status-im2.integration-test.wallet
|
||||
(:require
|
||||
[cljs.test :refer [is]]
|
||||
[cljs.test :refer [deftest is]]
|
||||
[clojure.string :as string]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
[re-frame.core :as rf]
|
||||
@@ -12,9 +12,7 @@
|
||||
status-im2.subs.root
|
||||
[test-helpers.integration :as h]))
|
||||
|
||||
;; Workaround to skip test. Switch to `deftest` when test is fixed.
|
||||
(defn create-wallet-account-test
|
||||
[]
|
||||
(deftest create-wallet-account-test
|
||||
(h/log-headline :create-wallet-account-test)
|
||||
(rf-test/run-test-async
|
||||
(h/with-app-initialized
|
||||
@@ -26,9 +24,7 @@
|
||||
(h/logout)
|
||||
(rf-test/wait-for [::logout/logout-method]))))))
|
||||
|
||||
;; Workaround to skip test. Switch to `deftest` when test is fixed.
|
||||
(defn back-up-seed-phrase-test
|
||||
[]
|
||||
(deftest back-up-seed-phrase-test
|
||||
(h/log-headline :back-up-seed-phrase-test)
|
||||
(rf-test/run-test-async
|
||||
(h/with-app-initialized
|
||||
@@ -32,7 +32,6 @@
|
||||
[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]
|
||||
@@ -47,7 +46,7 @@
|
||||
[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-addresses.view :as wallet-saved-addresses]
|
||||
[status-im2.contexts.wallet.saved-address.view :as wallet-saved-address]
|
||||
[status-im2.contexts.wallet.scan-account.view :as scan-address]
|
||||
[status-im2.contexts.wallet.send.select-address.view :as wallet-select-address]
|
||||
[status-im2.navigation.options :as options]
|
||||
@@ -162,15 +161,11 @@
|
||||
:component create-password/create-password}
|
||||
|
||||
{:name :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}
|
||||
:options {:theme :dark
|
||||
:layout options/onboarding-transparent-layout
|
||||
:animations transitions/push-animations-for-transparent-background
|
||||
:popGesture false}
|
||||
:component enable-biometrics/enable-biometrics}
|
||||
|
||||
{:name :generating-keys
|
||||
:options {:theme :dark
|
||||
@@ -284,8 +279,8 @@
|
||||
:options {:insets {:top? true}}
|
||||
:component wallet-create-account/view}
|
||||
|
||||
{:name :wallet-saved-addresses
|
||||
:component wallet-saved-addresses/view}
|
||||
{:name :wallet-saved-address
|
||||
:component wallet-saved-address/view}
|
||||
|
||||
{:name :wallet-select-address
|
||||
:options {:modalPresentationStyle :overCurrentContext}
|
||||
@@ -306,11 +301,4 @@
|
||||
quo.preview/screens)
|
||||
|
||||
(when config/quo-preview-enabled?
|
||||
quo.preview/main-screens)
|
||||
|
||||
(when config/quo-preview-enabled?
|
||||
status-im-preview/screens)
|
||||
|
||||
(when config/quo-preview-enabled?
|
||||
status-im-preview/main-screens)))
|
||||
|
||||
quo.preview/main-screens)))
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[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]
|
||||
@@ -72,9 +71,7 @@
|
||||
[bottom-sheet-screen/view {:content component}]
|
||||
[component])]
|
||||
(when js/goog.DEBUG
|
||||
[:<>
|
||||
[reloader/reload-view]
|
||||
[schema.view/view]])]))))
|
||||
[reloader/reload-view])]))))
|
||||
|
||||
(def bottom-sheet
|
||||
(reagent/reactify-component
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
(: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
|
||||
@@ -46,11 +45,10 @@
|
||||
:group-chats/extract-membership-signature
|
||||
:utils/dispatch-later
|
||||
:json-rpc/call})
|
||||
(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)))))
|
||||
|
||||
(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))))
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
(:require
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
schema.state
|
||||
[status-im2.setup.schema :as schema]))
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defonce cnt (reagent/atom 0))
|
||||
(defonce reload-locked? (atom false))
|
||||
@@ -21,8 +19,6 @@
|
||||
(reset! visible true)
|
||||
(reset! label "reloading UI")
|
||||
(re-frame/clear-subscription-cache!)
|
||||
(schema/setup!)
|
||||
(schema.state/clear-errors)
|
||||
(swap! cnt inc))
|
||||
|
||||
(defn before-reload
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
(ns status-im2.setup.schema
|
||||
(:require
|
||||
[malli.core :as malli]
|
||||
[malli.dev.cljs :as malli.dev]
|
||||
[malli.dev.pretty :as malli.pretty]
|
||||
[malli.dev.virhe :as malli.virhe]
|
||||
malli.error
|
||||
malli.instrument
|
||||
malli.util
|
||||
schema.common
|
||||
[schema.core :as schema]
|
||||
schema.registry
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
;;;; Formatters
|
||||
;; These formatters replace the original ones provided by Malli. They are more
|
||||
;; compact (less line breaks) and don't show the "More Information" section.
|
||||
|
||||
(defn block
|
||||
"Same as `malli.dev.pretty/-block`, but adds only one line break between `text`
|
||||
and `body`."
|
||||
[text body printer]
|
||||
[:group (malli.virhe/-text text printer) :break [:align 2 body]])
|
||||
|
||||
(defmethod malli.virhe/-format ::malli/explain
|
||||
[_ _ {:keys [schema] :as explanation} printer]
|
||||
{:body
|
||||
[:group
|
||||
(block "Value:" (malli.virhe/-visit (malli.error/error-value explanation printer) printer) printer)
|
||||
:break :break
|
||||
(block "Errors:" (malli.virhe/-visit (malli.error/humanize explanation) printer) printer)
|
||||
:break :break
|
||||
(block "Schema:" (malli.virhe/-visit schema printer) printer)]})
|
||||
|
||||
(defmethod malli.virhe/-format ::malli/invalid-input
|
||||
[_ _ {:keys [args input fn-name]} printer]
|
||||
{:body
|
||||
(cond-> [:group
|
||||
(block "Invalid function arguments:" (malli.virhe/-visit args printer) printer)
|
||||
:break :break]
|
||||
fn-name
|
||||
(conj (block "Function Var:" (malli.virhe/-visit fn-name printer) printer)
|
||||
:break
|
||||
:break)
|
||||
:else
|
||||
(conj (block "Input Schema:" (malli.virhe/-visit input printer) printer)
|
||||
:break
|
||||
:break
|
||||
(block "Errors:" (malli.pretty/-explain input args printer) printer)))})
|
||||
|
||||
(defmethod malli.virhe/-format ::malli/invalid-output
|
||||
[_ _ {:keys [value args output fn-name]} printer]
|
||||
{:body
|
||||
(cond-> [:group
|
||||
(block "Invalid function return value:" (malli.virhe/-visit value printer) printer)
|
||||
:break :break]
|
||||
fn-name
|
||||
(conj (block "Function Var:" (malli.virhe/-visit fn-name printer) printer)
|
||||
:break
|
||||
:break)
|
||||
:else
|
||||
(conj (block "Function arguments:" (malli.virhe/-visit args printer) printer)
|
||||
:break
|
||||
:break
|
||||
(block "Output Schema:" (malli.virhe/-visit output printer) printer)
|
||||
:break
|
||||
:break
|
||||
(block "Errors:" (malli.pretty/-explain output value printer) printer)))})
|
||||
|
||||
(defn register-schemas
|
||||
"Register all global schemas in `schema.registry/registry`.
|
||||
|
||||
Since keys in a map are unique, remember to qualify keywords. Prefer to add to
|
||||
the global registry schemas for domain entities (e.g. message, chat,
|
||||
notification, etc) or unambiguously useful schemas, like
|
||||
`:schema.common/theme`."
|
||||
[]
|
||||
(schema.registry/merge (malli.util/schemas))
|
||||
(schema.common/register-schemas))
|
||||
|
||||
(defn setup!
|
||||
"Configure Malli and initializes instrumentation.
|
||||
|
||||
After evaluating an s-exp in the REPL that changes a function schema you'll
|
||||
need to either save the file where the schema is defined and hot reload or
|
||||
manually call `setup!`, otherwise you won't see any changes. It is safe and
|
||||
even expected you will call `setup!` multiple times in REPLs."
|
||||
[]
|
||||
(try
|
||||
(schema.registry/init-global-registry)
|
||||
(register-schemas)
|
||||
|
||||
;; In theory not necessary, but sometimes in a REPL session the dev needs to
|
||||
;; call unstrument! manually.
|
||||
(malli.instrument/unstrument!)
|
||||
|
||||
(malli.dev/start! {:report (schema/reporter)})
|
||||
(println "Schemas initialized.")
|
||||
|
||||
;; It is relatively easy to write invalid schemas, but we don't want to
|
||||
;; block the app from initializing if such errors happen, at least not until
|
||||
;; Malli matures in the project.
|
||||
(catch js/Error e
|
||||
(log/error "Failed to initialize schemas" {:error e}))))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user