Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b9d7110f9 | ||
|
|
cc6aca6c6a | ||
|
|
3c9316698d | ||
|
|
c62204121d | ||
|
|
a4bc18ee3f | ||
|
|
68e53e05bd | ||
|
|
d142d58677 | ||
|
|
56dbb77ee5 | ||
|
|
0dd3cb51fd | ||
|
|
9960a5c958 | ||
|
|
fb4d484d7d | ||
|
|
98067f8c8e | ||
|
|
9dad4716d6 | ||
|
|
2b701f9af0 | ||
|
|
e8b956d4f4 | ||
|
|
f30d5b59ca | ||
|
|
863dac6e6a | ||
|
|
9a4f8f97b9 | ||
|
|
d395ca6cc6 | ||
|
|
15211865fb | ||
|
|
65eb0af191 | ||
|
|
bbdac4e8c6 | ||
|
|
abcfaef1a1 | ||
|
|
2d1b37ab7b | ||
|
|
a18cf2f786 | ||
|
|
e14ed7a637 |
@@ -40,9 +40,6 @@ pipeline {
|
||||
BUILD_ENV = 'prod'
|
||||
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
|
||||
FASTLANE_DISABLE_COLORS = 1
|
||||
/* coverage report identification */
|
||||
COVERALLS_SERVICE_NAME = "jenkins"
|
||||
COVERALLS_SERVICE_JOB_ID = "${JOB_NAME}#${BUILD_NUMBER}"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
@@ -61,10 +61,9 @@ pipeline {
|
||||
}
|
||||
stage('Build android jsbundle') {
|
||||
steps { script {
|
||||
/* build/fetch things required to produce a js-bundle for android
|
||||
* (e.g. maven and node repos) */
|
||||
/* Build/fetch deps required for jsbundle build. */
|
||||
nix.build(
|
||||
attr: 'targets.mobile.android.jsbundle',
|
||||
attr: 'targets.mobile.jsbundle',
|
||||
sandbox: false,
|
||||
pure: false,
|
||||
link: false
|
||||
@@ -73,7 +72,7 @@ pipeline {
|
||||
}
|
||||
stage('Build android deps') {
|
||||
steps { script {
|
||||
/* build/fetch things required to build jsbundle and android */
|
||||
/* Build/fetch deps required to build android release. */
|
||||
nix.build(
|
||||
attr: 'targets.mobile.android.release.buildInputs',
|
||||
sandbox: false,
|
||||
@@ -84,7 +83,7 @@ pipeline {
|
||||
}
|
||||
stage('Build nix shell deps') {
|
||||
steps { script {
|
||||
/* build/fetch things required to instantiate shell.nix for TARGET=all */
|
||||
/* Build/fetch deps required to start default Nix shell. */
|
||||
nix.build(
|
||||
attr: 'shells.default.buildInputs',
|
||||
sandbox: false,
|
||||
+15
-54
@@ -103,45 +103,6 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0",
|
||||
"host": "https://repo.clojars.org",
|
||||
"pom": {
|
||||
"sha1": "ff154f318e1f63061b5bd42d9a38fc8547360cb4",
|
||||
"sha256": "166ynlrph7rx3nql0c85c37vcbcsnwbjamk71hw74rz451pgnncr"
|
||||
},
|
||||
"jar": {
|
||||
"sha1": "d5c53eca9f3884402c76268b82e3828223dcbf90",
|
||||
"sha256": "1530hwqpxzxfjm6dy77whqwjzzx79jpnd9xvnkxy1lnk7l7lj494"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "cljsjs/react-dom/17.0.1-0/react-dom-17.0.1-0",
|
||||
"host": "https://repo.clojars.org",
|
||||
"pom": {
|
||||
"sha1": "077d1a0973c23cbbff52dea7365dca4cf9de5ad5",
|
||||
"sha256": "1ccm3l19cgy30hr03n8jgkgzmswdbg520zmg97xk8b16ncn964jn"
|
||||
},
|
||||
"jar": {
|
||||
"sha1": "a98b94cbc84d264ba9892c971cfaf5920c2152f1",
|
||||
"sha256": "0ym20h0h7mw1mxzm1r7smbj65x8nsqj3cszg82mw3aw4a8j32c29"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "cljsjs/react/17.0.1-0/react-17.0.1-0",
|
||||
"host": "https://repo.clojars.org",
|
||||
"pom": {
|
||||
"sha1": "d5d3681281585d981375fe7bb29362bfcd0d4449",
|
||||
"sha256": "0rskh6mfq5h0l0wybs68w9v585a6zlnnfgrvsf5k3h51fk2p28da"
|
||||
},
|
||||
"jar": {
|
||||
"sha1": "726da8a9305a865df90d81e9a8d58f87ff0ee8bd",
|
||||
"sha256": "0bfmvw8gk98vdf9azll6041r1jaz1pmb0dyb35cfc8dr3mmr4ag7"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "clout/clout/2.1.2/clout-2.1.2",
|
||||
"host": "https://repo.clojars.org",
|
||||
@@ -1014,15 +975,15 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/clojure/tools.logging/0.4.1/tools.logging-0.4.1",
|
||||
"path": "org/clojure/tools.logging/1.1.0/tools.logging-1.1.0",
|
||||
"host": "https://repo1.maven.org/maven2",
|
||||
"pom": {
|
||||
"sha1": "483bbfc82aee3c005bb1ad36dec77ba5d6c5f95c",
|
||||
"sha256": "1qhfigr9am90fx527ax6f7z7xahgsd5hzb4pmi40n79nx3jsc9hf"
|
||||
"sha1": "44b6bd9ac74cb5e2254e1f0b258d53c1829b865d",
|
||||
"sha256": "093jy29w5gm9rp9va6qlhb6096jpf5higyk7sgmbwb5nf53qi3qb"
|
||||
},
|
||||
"jar": {
|
||||
"sha1": "991bebc6e3633b85091ae929d62c116a2584aee7",
|
||||
"sha256": "001sx1ddgx0ys0z1qphzpxjf2w35rikrfjc0h5bvvqbw8ra0vvja"
|
||||
"sha1": "84cb5d00caa9df2ee504d46f6107f4708271f619",
|
||||
"sha256": "0x2zzivn38z179lxkw9wbi9n9qwsf466lrd9y27khdz7wbxhscb5"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1352,15 +1313,15 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "re-frame/re-frame/0.12.0/re-frame-0.12.0",
|
||||
"path": "re-frame/re-frame/1.3.0/re-frame-1.3.0",
|
||||
"host": "https://repo.clojars.org",
|
||||
"pom": {
|
||||
"sha1": "41fd34c15c1cf68eec81435e09d0a79fd8a88276",
|
||||
"sha256": "08c3sxi3sdkay3550vb3gbv5czzdh2kyin4mb8cpd2wd5g2c88sq"
|
||||
"sha1": "b1f70463fba018c5464d3fc0843232fe955c1372",
|
||||
"sha256": "0yqq1kg1si0akr7kx7w1l4vvkdai20na6h6627ibqkgwzivkc5n5"
|
||||
},
|
||||
"jar": {
|
||||
"sha1": "3ff92169fe7588ad62dad2d6ea3bf0e5d3e2d224",
|
||||
"sha256": "1m5sjskgjx028s1zpa631bp52y9hjn8l1nr8hrwiwdh09i2kpnk2"
|
||||
"sha1": "b7135a76432b8141027ba1f4eb6bb15a36acfb7c",
|
||||
"sha256": "16wi01z0j4wn04kldwzxvj0pd9dianjyb000nv5611ikhxk1qrps"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1391,15 +1352,15 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "reagent/reagent/1.0.0/reagent-1.0.0",
|
||||
"path": "reagent/reagent/1.2.0/reagent-1.2.0",
|
||||
"host": "https://repo.clojars.org",
|
||||
"pom": {
|
||||
"sha1": "93ad5ccb0101108fb0843a3f2c50da28f8fd8336",
|
||||
"sha256": "1l4xzgc4wbd8b29mhj5j336cf8w1zccaia0w413zf3bnj6jf5m38"
|
||||
"sha1": "d2279922777793edfc0ae9e01d68a832eb5ce52d",
|
||||
"sha256": "1h1gk4cykidm1jx75dq426lrgf8r7900fcir5jz59zp1ka2a0ywb"
|
||||
},
|
||||
"jar": {
|
||||
"sha1": "a1dd4d40425bb2e1afad0fe72780b43ff394ccfa",
|
||||
"sha256": "1g607s1ymns85923bvhwrsj0y5czyqqq1jmxx0a58fnsm1b318fa"
|
||||
"sha1": "1b9a181b5c7ed3557768d2ea0c66f5616aef5e97",
|
||||
"sha256": "0scvkzfqjs613z10rngh7427v3pxdqablf0fcl65pbpkzz16wgav"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ camel-snake-kebab/camel-snake-kebab/0.4.3/camel-snake-kebab-0.4.3.jar
|
||||
cider/cider-nrepl/0.25.3/cider-nrepl-0.25.3.jar
|
||||
cider/piggieback/0.4.1/piggieback-0.4.1.jar
|
||||
cljs-bean/cljs-bean/1.3.0/cljs-bean-1.3.0.jar
|
||||
cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0.jar
|
||||
cljsjs/react-dom/17.0.1-0/react-dom-17.0.1-0.jar
|
||||
cljsjs/react/17.0.1-0/react-17.0.1-0.jar
|
||||
clout/clout/2.1.2/clout-2.1.2.jar
|
||||
com/andrewmcveigh/cljs-time/0.5.2/cljs-time-0.5.2.jar
|
||||
com/bhauman/cljs-test-display/0.1.1/cljs-test-display-0.1.1.jar
|
||||
@@ -76,7 +73,7 @@ org/clojure/test.check/1.1.0/test.check-1.1.0.jar
|
||||
org/clojure/tools.analyzer.jvm/1.1.0/tools.analyzer.jvm-1.1.0.jar
|
||||
org/clojure/tools.analyzer/1.0.0/tools.analyzer-1.0.0.jar
|
||||
org/clojure/tools.cli/1.0.194/tools.cli-1.0.194.jar
|
||||
org/clojure/tools.logging/0.4.1/tools.logging-0.4.1.jar
|
||||
org/clojure/tools.logging/1.1.0/tools.logging-1.1.0.jar
|
||||
org/clojure/tools.macro/0.1.5/tools.macro-0.1.5.jar
|
||||
org/clojure/tools.reader/1.3.3/tools.reader-1.3.3.jar
|
||||
org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar
|
||||
@@ -102,10 +99,10 @@ 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
|
||||
quoin/quoin/0.1.2/quoin-0.1.2.jar
|
||||
re-com/re-com/2.8.0/re-com-2.8.0.jar
|
||||
re-frame/re-frame/0.12.0/re-frame-0.12.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
|
||||
reagent/reagent/1.0.0/reagent-1.0.0.jar
|
||||
reagent/reagent/1.2.0/reagent-1.2.0.jar
|
||||
refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0.jar
|
||||
ring-cors/ring-cors/0.1.8/ring-cors-0.1.8.jar
|
||||
ring/ring-codec/1.1.2/ring-codec-1.1.2.jar
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1011 B After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
+2
-2
@@ -1,8 +1,8 @@
|
||||
;; shadow-cljs configuration
|
||||
{:source-paths ["src" "test/cljs"]
|
||||
|
||||
:dependencies [[reagent "1.0.0"]
|
||||
[re-frame "0.12.0"]
|
||||
:dependencies [[reagent "1.2.0"]
|
||||
[re-frame "1.3.0"]
|
||||
[binaryage/oops "0.7.0"]
|
||||
[com.andrewmcveigh/cljs-time "0.5.2"]
|
||||
[status-im/timbre "4.10.0-2-status"]
|
||||
|
||||
@@ -220,10 +220,10 @@
|
||||
only icon
|
||||
[button {:icon true} :i/close-circle]"
|
||||
[_ _]
|
||||
(let [pressed (reagent/atom false)]
|
||||
(let [pressed-in (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press disabled type size before after above
|
||||
width customization-color override-theme override-background-color
|
||||
[{:keys [on-press disabled type size before after above icon-secondary-no-color
|
||||
width customization-color override-theme override-background-color pressed
|
||||
on-long-press accessibility-label icon icon-no-color style inner-style test-ID]
|
||||
:or {type :primary
|
||||
size 40
|
||||
@@ -234,17 +234,17 @@
|
||||
[(or
|
||||
override-theme
|
||||
(theme/get-theme)) type])
|
||||
state (cond disabled :disabled
|
||||
@pressed :pressed
|
||||
:else :default)
|
||||
state (cond disabled :disabled
|
||||
(or @pressed-in pressed) :pressed
|
||||
:else :default)
|
||||
icon-size (when (= 24 size) 12)
|
||||
icon-secondary-color (or icon-secondary-color icon-color)]
|
||||
[rn/touchable-without-feedback
|
||||
(merge {:test-ID test-ID
|
||||
:disabled disabled
|
||||
:accessibility-label accessibility-label
|
||||
:on-press-in #(reset! pressed true)
|
||||
:on-press-out #(reset! pressed nil)}
|
||||
:on-press-in #(reset! pressed-in true)
|
||||
:on-press-out #(reset! pressed-in nil)}
|
||||
(when on-press
|
||||
{:on-press on-press})
|
||||
(when on-long-press
|
||||
@@ -306,5 +306,6 @@
|
||||
[quo2.icons/icon after
|
||||
{:container-style {:margin-left 4
|
||||
:margin-right (if (= size 40) 12 8)}
|
||||
:no-color icon-secondary-no-color
|
||||
:color icon-secondary-color
|
||||
:size icon-size}]])]]]))))
|
||||
|
||||
@@ -68,7 +68,8 @@
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press
|
||||
:active-opacity 1
|
||||
:style {:padding 5}
|
||||
:hit-slop {:top 5 :bottom 5 :left 5 :right 5}
|
||||
:pointer-events :auto
|
||||
:accessibility-label type}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
(case type
|
||||
:words
|
||||
[rn/flat-list
|
||||
{:data words
|
||||
{:keyboard-should-persist-taps :always
|
||||
:data words
|
||||
:content-container-style style/word-list
|
||||
:render-fn word-component
|
||||
:render-data {:blur? blur?
|
||||
|
||||
@@ -1,53 +1,62 @@
|
||||
(ns quo2.components.drawers.action-drawers.component-spec
|
||||
(:require ["@testing-library/react-native" :as rtl]
|
||||
[quo2.components.drawers.action-drawers.view :as action-drawer]
|
||||
[reagent.core :as reagent]))
|
||||
(:require [quo2.components.drawers.action-drawers.view :as action-drawer]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(defn render-action-drawer
|
||||
([options]
|
||||
(rtl/render (reagent/as-element [action-drawer/action-drawer options]))))
|
||||
(h/describe "action drawer"
|
||||
(h/test "default render"
|
||||
(h/render [action-drawer/action-drawer
|
||||
[[{:icon :i/friend
|
||||
:label "a sample label"}]]])
|
||||
(h/is-truthy (h/get-by-text "a sample label")))
|
||||
|
||||
(js/global.test "action-drawer renders with elements label displaying"
|
||||
(fn []
|
||||
(render-action-drawer [[{:icon :i/friend
|
||||
:label "a sample label"}]])
|
||||
(-> (js/expect (rtl/screen.getByText "a sample label"))
|
||||
(.toBeTruthy))))
|
||||
(h/test "renders with elements sub-label displaying"
|
||||
(h/render [action-drawer/action-drawer
|
||||
[[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:sub-label "a sample sub label"}]]])
|
||||
(h/is-truthy (h/get-by-text "a sample sub label")))
|
||||
|
||||
(js/global.test "action-drawer renders with elements sub-label displaying"
|
||||
(fn []
|
||||
(render-action-drawer [[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:sub-label "a sample sub label"}]])
|
||||
(-> (js/expect (rtl/screen.getByText "a sample sub label"))
|
||||
(.toBeTruthy))))
|
||||
(h/test "on click action works on element"
|
||||
(let [on-press (js/jest.fn)]
|
||||
(h/render [action-drawer/action-drawer
|
||||
[[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:on-press on-press}]]])
|
||||
(h/fire-event :press (h/get-by-text "a sample label"))
|
||||
(h/was-called on-press)))
|
||||
|
||||
(js/global.test "action-drawer on click action works on element"
|
||||
(let [event (js/jest.fn)]
|
||||
(fn []
|
||||
(render-action-drawer [[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:on-press event}]])
|
||||
(rtl/fireEvent.press (rtl/screen.getByText "a sample label"))
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalled)))))
|
||||
(h/test "disabled state"
|
||||
(let [on-press-muted (js/jest.fn)
|
||||
on-press-friend (js/jest.fn)
|
||||
label-mute "Mute"
|
||||
label-friend "View member details"]
|
||||
(h/render [action-drawer/action-drawer
|
||||
[[{:icon :i/muted
|
||||
:label label-mute
|
||||
:on-press on-press-muted}
|
||||
{:icon :i/friend
|
||||
:label label-friend
|
||||
:disabled? true
|
||||
:on-press on-press-friend}]]])
|
||||
(h/fire-event :press (h/get-by-text label-mute))
|
||||
(h/was-called on-press-muted)
|
||||
|
||||
(js/global.test "action-drawer renders two icons when set"
|
||||
(fn []
|
||||
(render-action-drawer [[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:right-icon :i/friend
|
||||
:accessibility-label :first-element}]])
|
||||
(-> (js/expect (rtl/screen.getByLabelText "right-icon-for-action"))
|
||||
(.toBeTruthy))
|
||||
(-> (js/expect (rtl/screen.queryByLabelText "left-icon-for-action"))
|
||||
(.toBeTruthy))))
|
||||
(h/fire-event :press (h/get-by-text label-friend))
|
||||
(h/was-not-called on-press-friend)))
|
||||
|
||||
(js/global.test "action-drawer renders a divider when the add-divider? prop is true"
|
||||
(fn []
|
||||
(render-action-drawer [[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:add-divider? true
|
||||
:accessibility-label :first-element}]])
|
||||
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
|
||||
(.toBeTruthy))))
|
||||
(h/test "renders two icons when set"
|
||||
(h/render [action-drawer/action-drawer
|
||||
[[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:right-icon :i/friend
|
||||
:accessibility-label :first-element}]]])
|
||||
(h/is-truthy (h/get-by-label-text "right-icon-for-action"))
|
||||
(h/is-truthy (h/query-by-label-text "left-icon-for-action")))
|
||||
|
||||
(h/test "renders a divider when the add-divider? prop is true"
|
||||
(h/render [action-drawer/action-drawer
|
||||
[[{:icon :i/friend
|
||||
:label "a sample label"
|
||||
:add-divider? true
|
||||
:accessibility-label :first-element}]]])
|
||||
(h/is-truthy (h/get-all-by-label-text "divider"))))
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
:flex-direction :row})
|
||||
|
||||
(defn container
|
||||
[sub-label]
|
||||
[sub-label disabled?]
|
||||
{:border-radius 12
|
||||
:height (if sub-label 58 50)
|
||||
:opacity (when disabled? 0.3)
|
||||
:margin-horizontal 8})
|
||||
|
||||
(defn row-container
|
||||
|
||||
@@ -18,12 +18,19 @@
|
||||
:accessible true
|
||||
:accessibility-label :divider}])
|
||||
|
||||
(defn action
|
||||
(defn- maybe-pressable
|
||||
[disabled? props child]
|
||||
(if disabled?
|
||||
[rn/view (dissoc props :on-press) child]
|
||||
[rn/touchable-highlight props child]))
|
||||
|
||||
(defn- action
|
||||
[{:keys [icon
|
||||
label
|
||||
sub-label
|
||||
right-icon
|
||||
danger?
|
||||
disabled?
|
||||
on-press
|
||||
add-divider?
|
||||
override-theme
|
||||
@@ -33,9 +40,9 @@
|
||||
[:<> {:key label}
|
||||
(when add-divider?
|
||||
[divider])
|
||||
[rn/touchable-highlight
|
||||
[maybe-pressable disabled?
|
||||
{:accessibility-label accessibility-label
|
||||
:style (style/container sub-label)
|
||||
:style (style/container sub-label disabled?)
|
||||
:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90 override-theme)
|
||||
:on-press on-press}
|
||||
[rn/view
|
||||
|
||||
@@ -1,165 +1,14 @@
|
||||
(ns quo2.components.dropdowns.dropdown
|
||||
(:require [quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn apply-anim
|
||||
[dd-height val]
|
||||
(reanimated/animate-shared-value-with-delay dd-height
|
||||
val
|
||||
300
|
||||
:easing1
|
||||
0))
|
||||
|
||||
(def sizes
|
||||
{:big {:icon-size 20
|
||||
:font {:font-size :paragraph-1}
|
||||
:height 40
|
||||
:padding {:padding-with-icon {:padding-vertical 9
|
||||
:padding-horizontal 12}
|
||||
:padding-with-no-icon {:padding-vertical 9
|
||||
:padding-left 12
|
||||
:padding-right 8}}}
|
||||
:medium {:icon-size 20
|
||||
:font {:font-size :paragraph-1}
|
||||
:height 32
|
||||
:padding {:padding-with-icon {:padding-vertical 5
|
||||
:padding-horizontal 8}
|
||||
:padding-with-no-icon {:padding-vertical 5
|
||||
:padding-left 12
|
||||
:padding-right 8}}}
|
||||
:small {:icon-size 15
|
||||
:font {:font-size :paragraph-2}
|
||||
:height 24
|
||||
:padding {:padding-with-icon {:padding-vertical 3
|
||||
:padding-horizontal 6}
|
||||
:padding-with-no-icon {:padding-vertical 3
|
||||
:padding-horizontal 6}}}})
|
||||
(defn color-by-10
|
||||
[color]
|
||||
(colors/alpha color 0.6))
|
||||
|
||||
(defn dropdown-comp
|
||||
[{:keys [icon dd-height size disabled? dd-color use-border? border-color]}]
|
||||
(let [dark? (colors/dark?)
|
||||
{:keys [width height width-with-icon padding font icon-size]} (size sizes)
|
||||
{:keys [padding-with-icon padding-with-no-icon]} padding
|
||||
font-size (:font-size font)
|
||||
spacing (case size
|
||||
:big 4
|
||||
:medium 2
|
||||
:small 2)
|
||||
open? (reagent/atom false)]
|
||||
(fn []
|
||||
[rn/touchable-opacity
|
||||
(cond->
|
||||
{:on-press (fn []
|
||||
(if (swap! open? not)
|
||||
(apply-anim dd-height 120)
|
||||
(apply-anim dd-height 0)))
|
||||
:style (cond->
|
||||
(merge
|
||||
(if icon
|
||||
padding-with-icon
|
||||
padding-with-no-icon)
|
||||
{:width (if icon
|
||||
width-with-icon
|
||||
width)
|
||||
:height height
|
||||
:border-radius (case size
|
||||
:big 12
|
||||
:medium 10
|
||||
:small 8)
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:background-color (if @open?
|
||||
dd-color
|
||||
(color-by-10 dd-color))})
|
||||
use-border? (assoc :border-width 1
|
||||
:border-color (if @open?
|
||||
border-color
|
||||
(color-by-10 border-color))))}
|
||||
disabled? (assoc-in [:style :opacity] 0.3)
|
||||
disabled? (assoc :disabled true))
|
||||
(when icon
|
||||
[icons/icon icon
|
||||
{:no-color true
|
||||
:size 20
|
||||
:container-style {:margin-right spacing
|
||||
:margin-top 1
|
||||
:width icon-size
|
||||
:height icon-size}}])
|
||||
[text/text
|
||||
{:size font-size
|
||||
:weight :medium
|
||||
:font :font-medium
|
||||
:color :main} "Dropdown"]
|
||||
[icons/icon
|
||||
(if @open?
|
||||
(if dark?
|
||||
:main-icons/pullup-dark
|
||||
:main-icons/pullup)
|
||||
(if dark?
|
||||
:main-icons/dropdown-dark
|
||||
:main-icons/dropdown))
|
||||
{:size 20
|
||||
:no-color true
|
||||
:container-style {:width (+ icon-size 3)
|
||||
:border-radius 20
|
||||
:margin-left (if (= :small size)
|
||||
2
|
||||
4)
|
||||
:margin-top 1
|
||||
:height (+ icon-size 4)}}]])))
|
||||
|
||||
(defn items-comp
|
||||
[{:keys [items on-select]}]
|
||||
(let [items-count (count items)]
|
||||
[rn/scroll-view
|
||||
{:horizontal false
|
||||
:nestedScrollEnabled true}
|
||||
(doall
|
||||
(map-indexed (fn [index item]
|
||||
[rn/touchable-opacity
|
||||
{:key (str item index)
|
||||
:style {:padding 4
|
||||
:border-bottom-width (if (= index (- items-count 1))
|
||||
0
|
||||
1)
|
||||
:border-color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white)
|
||||
:text-align :center}
|
||||
:on-press #(on-select item)}
|
||||
[text/text {:style {:text-align :center}} item]])
|
||||
items))]))
|
||||
|
||||
(defn- f-dropdown
|
||||
[{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}]
|
||||
(let [dd-height (reanimated/use-shared-value 0)]
|
||||
[rn/view {:style {:flex-grow 1}}
|
||||
[dropdown-comp
|
||||
{:items items
|
||||
:icon icon
|
||||
:disabled? disabled?
|
||||
:size size
|
||||
:dd-color dd-color
|
||||
:text text
|
||||
:border-color (colors/custom-color-by-theme border-color 50 60)
|
||||
:use-border? use-border?
|
||||
:default-item default-item
|
||||
:dd-height dd-height}]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:height dd-height}
|
||||
{})}
|
||||
[items-comp
|
||||
{:items items
|
||||
:on-select on-select}]]]))
|
||||
(:require [quo2.components.buttons.button :as button]))
|
||||
|
||||
(defn dropdown
|
||||
[params]
|
||||
[:f> f-dropdown params])
|
||||
[_ _]
|
||||
(fn [{:keys [on-change selected] :as opts} children]
|
||||
[button/button
|
||||
(merge
|
||||
opts
|
||||
{:after (if selected :i/pullup :i/dropdown)
|
||||
:icon-secondary-no-color true
|
||||
:pressed selected
|
||||
:on-press #(when on-change (on-change selected))})
|
||||
children]))
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
(h/describe "Error text"
|
||||
(h/test "Marked when words doesn't satisfy a predicate"
|
||||
(h/render [recovery-phrase/recovery-phrase-input
|
||||
{:mark-errors? true
|
||||
:error-pred #(>= (count %) 5)}
|
||||
{:mark-errors? true
|
||||
:error-pred-current-word #(>= (count %) 5)
|
||||
:error-pred-written-words #(>= (count %) 5)}
|
||||
"Text with some error words that don't satisfy the predicate"])
|
||||
(let [children-text-nodes (-> (h/get-by-label-text :recovery-phrase-input)
|
||||
(oops/oget "props" "children" "props" "children")
|
||||
|
||||
@@ -15,15 +15,27 @@
|
||||
text])
|
||||
|
||||
(defn- mark-error-words
|
||||
[pred text word-limit]
|
||||
(let [word-limit (or word-limit ##Inf)]
|
||||
(into [:<>]
|
||||
(comp (map-indexed (fn [idx word]
|
||||
(if (or (pred word) (>= idx word-limit))
|
||||
[error-word word]
|
||||
word)))
|
||||
(interpose " "))
|
||||
(string/split text #" "))))
|
||||
[pred-last-word pred-previous-words text word-limit]
|
||||
(let [last-index (dec (count (string/split text #"\s+")))
|
||||
words (map #(apply str %)
|
||||
(partition-by #(= " " %) text))]
|
||||
(->> words
|
||||
(reduce (fn [{:keys [idx] :as acc} word]
|
||||
(let [error-pred (if (= last-index idx) pred-last-word pred-previous-words)
|
||||
invalid-word? (and (or (error-pred word)
|
||||
(>= idx word-limit))
|
||||
(not (string/blank? word)))
|
||||
not-blank-spaces? (not (string/blank? word))]
|
||||
(cond-> acc
|
||||
not-blank-spaces? (update :idx inc)
|
||||
:always (update :result
|
||||
conj
|
||||
(if invalid-word?
|
||||
[error-word word]
|
||||
word)))))
|
||||
{:result [:<>]
|
||||
:idx 0})
|
||||
:result)))
|
||||
|
||||
(defn recovery-phrase-input
|
||||
[_ _]
|
||||
@@ -31,9 +43,11 @@
|
||||
set-focused #(reset! state :focused)
|
||||
set-default #(reset! state :default)]
|
||||
(fn [{:keys [customization-color override-theme blur? on-focus on-blur mark-errors?
|
||||
error-pred word-limit]
|
||||
:or {customization-color :blue
|
||||
error-pred (constantly false)}
|
||||
error-pred-current-word error-pred-written-words word-limit]
|
||||
:or {customization-color :blue
|
||||
word-limit ##Inf
|
||||
error-pred-current-word (constantly false)
|
||||
error-pred-written-words (constantly false)}
|
||||
:as props}
|
||||
text]
|
||||
(let [extra-props (apply dissoc props custom-props)]
|
||||
@@ -53,5 +67,5 @@
|
||||
(when on-blur (on-blur)))}
|
||||
extra-props)
|
||||
(if mark-errors?
|
||||
(mark-error-words error-pred text word-limit)
|
||||
(mark-error-words error-pred-current-word error-pred-written-words text word-limit)
|
||||
text)]]))))
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
:style style
|
||||
:customization-color customization-color}]))
|
||||
|
||||
(defn- section
|
||||
[children]
|
||||
[rn/view {:style {:flex 1} :pointer-events :box-none} children])
|
||||
|
||||
(defn- f-floating-shell-button
|
||||
[dynamic-buttons style opacity-anim]
|
||||
(let [original-style (merge {:flex-direction :row
|
||||
@@ -27,14 +31,14 @@
|
||||
original-style)]
|
||||
[reanimated/view {:style animated-style}
|
||||
;; Left Section
|
||||
[rn/view {:style {:flex 1}}
|
||||
[section
|
||||
[dynamic-button-view :search dynamic-buttons
|
||||
{:position :absolute
|
||||
:right 8}]]
|
||||
;; Mid Section (jump-to)
|
||||
[dynamic-button-view :jump-to dynamic-buttons nil]
|
||||
;; Right Section
|
||||
[rn/view {:style {:flex 1}}
|
||||
[section
|
||||
[rn/view
|
||||
{:style {:position :absolute
|
||||
:flex-direction :row
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{:on-press on-press
|
||||
:accessibility-label :emoji-reaction-add
|
||||
:style (style/add-reaction)}
|
||||
[icons/icon :i/add
|
||||
[icons/icon :i/add-reaction
|
||||
{:size 20
|
||||
:color (if dark?
|
||||
colors/white
|
||||
@@ -21,10 +21,11 @@
|
||||
|
||||
(defn reaction
|
||||
"Add your emoji as a param here"
|
||||
[{:keys [emoji clicks neutral? on-press accessibility-label]}]
|
||||
[{:keys [emoji clicks neutral? on-press accessibility-label on-long-press]}]
|
||||
(let [numeric-value (int clicks)]
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-press
|
||||
:on-long-press on-long-press
|
||||
:accessibility-label accessibility-label
|
||||
:style (style/reaction neutral?)}
|
||||
[icons/icon emoji
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
[react-native.masked-view :as masked-view]
|
||||
[reagent.core :as reagent]
|
||||
[utils.collection :as utils.collection]
|
||||
[utils.number :as utils.number]))
|
||||
[utils.number :as utils.number]
|
||||
[react-native.gesture :as gesture]))
|
||||
|
||||
(def default-tab-size 32)
|
||||
(def unread-count-offset 3)
|
||||
@@ -131,7 +132,8 @@
|
||||
style
|
||||
size
|
||||
blur?
|
||||
override-theme]
|
||||
override-theme
|
||||
in-scroll-view?]
|
||||
:or {fade-end-percentage fade-end-percentage
|
||||
fade-end? false
|
||||
scrollable? false
|
||||
@@ -142,7 +144,9 @@
|
||||
[rn/view {:style {:margin-top (- (dec unread-count-offset))}}
|
||||
[masked-view-wrapper
|
||||
{:fade-end-percentage (get @fading :fade-end-percentage) :fade-end? fade-end?}
|
||||
[rn/flat-list
|
||||
[(if in-scroll-view?
|
||||
gesture/flat-list
|
||||
rn/flat-list)
|
||||
(merge
|
||||
(dissoc props
|
||||
:default-active
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
(let [text-params {:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (assoc text-style :justify-content :center)}
|
||||
empty-photo? (empty? photo)
|
||||
empty-photo? (nil? photo)
|
||||
avatar-size :xxs
|
||||
avatar-outer-size (get-in user-avatar-style/sizes [avatar-size :outer])]
|
||||
[rn/view {:flex-direction :row}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
(ns react-native.cameraroll
|
||||
(:require ["@react-native-community/cameraroll" :as CameraRoll]
|
||||
[utils.transforms :as transforms]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(defn get-photos
|
||||
[opts callback]
|
||||
(-> (.getPhotos CameraRoll (clj->js opts))
|
||||
(.then #(callback (transforms/js->clj %)))
|
||||
(.catch #(log/warn "could not get camera roll photos" %))))
|
||||
|
||||
(defn get-albums
|
||||
[opts callback]
|
||||
(-> (.getAlbums CameraRoll (clj->js opts))
|
||||
(.then #(callback (transforms/js->clj %)))
|
||||
(.catch #(log/warn "could not get camera roll albums" %))))
|
||||
@@ -17,6 +17,7 @@
|
||||
(def view (reagent/adapt-react-class (.-View ^js react-native)))
|
||||
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js react-native)))
|
||||
(def image (reagent/adapt-react-class (.-Image ^js react-native)))
|
||||
(defn image-get-size [uri callback] (.getSize ^js (.-Image ^js react-native) uri callback))
|
||||
(def text (reagent/adapt-react-class (.-Text ^js react-native)))
|
||||
(def text-input (reagent/adapt-react-class (.-TextInput ^js react-native)))
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
[flat-list
|
||||
(merge props
|
||||
{:data data
|
||||
:render-fn (fn [item]
|
||||
(if (:header? item)
|
||||
(render-section-header-fn item)
|
||||
(render-fn item)))})]))
|
||||
:render-fn (fn [p1 p2 p3 p4]
|
||||
(if (:header? p1)
|
||||
[render-section-header-fn p1 p2 p3 p4]
|
||||
[render-fn p1 p2 p3 p4]))})]))
|
||||
|
||||
@@ -1,47 +1,17 @@
|
||||
(ns status-im.chat.models.images
|
||||
(:require ["@react-native-community/cameraroll" :as CameraRoll]
|
||||
["react-native-blob-util" :default ReactNativeBlobUtil]
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[utils.i18n :as i18n]
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[react-native.permissions :as permissions]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im2.config :as config]
|
||||
[status-im.utils.fs :as fs]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.utils.image-processing :as image-processing]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.utils.utils :as utils]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(def maximum-image-size-px 2000)
|
||||
|
||||
(defn- resize-and-call
|
||||
[uri cb]
|
||||
(react/image-get-size
|
||||
uri
|
||||
(fn [width height]
|
||||
(let [resize? (> (max width height) maximum-image-size-px)]
|
||||
(image-processing/resize
|
||||
uri
|
||||
(if resize? maximum-image-size-px width)
|
||||
(if resize? maximum-image-size-px height)
|
||||
60
|
||||
(fn [^js resized-image]
|
||||
(let [path (.-path resized-image)
|
||||
path (if (string/starts-with? path "file") path (str "file://" path))]
|
||||
(cb {:resized-uri path
|
||||
:width width
|
||||
:height height})))
|
||||
#(log/error "could not resize image" %))))))
|
||||
|
||||
(defn result->id
|
||||
[^js result]
|
||||
(if platform/ios?
|
||||
(.-localIdentifier result)
|
||||
(.-path result)))
|
||||
|
||||
(def temp-image-url (str (fs/cache-dir) "/StatusIm_Image.jpeg"))
|
||||
|
||||
(defn download-image-http
|
||||
@@ -78,108 +48,6 @@
|
||||
#(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)])
|
||||
{})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::chat-open-image-picker
|
||||
(fn [chat-id]
|
||||
(react/show-image-picker
|
||||
(fn [^js images]
|
||||
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
|
||||
;; gallery, we just clean previous state and set all newly picked images
|
||||
(when (and platform/ios? (pos? (count images)))
|
||||
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
|
||||
(doseq [^js result (if platform/ios?
|
||||
(take config/max-images-batch images)
|
||||
[images])]
|
||||
(resize-and-call (.-path result)
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %]))))
|
||||
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
|
||||
;; selects too many images the app crashes.
|
||||
{:media-type "photo"
|
||||
:multiple platform/ios?})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::image-selected
|
||||
(fn [[image chat-id]]
|
||||
(resize-and-call
|
||||
(:uri image)
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id image %]))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::camera-roll-get-photos
|
||||
(fn [[num end-cursor album]]
|
||||
(permissions/request-permissions
|
||||
{:permissions [:read-external-storage]
|
||||
:on-allowed (fn []
|
||||
(-> (if end-cursor
|
||||
(.getPhotos
|
||||
CameraRoll
|
||||
#js
|
||||
{:first num
|
||||
:after end-cursor
|
||||
:assetType "Photos"
|
||||
:groupTypes (if (= album (i18n/label :t/recent)) "All" "Albums")
|
||||
:groupName (when (not= album (i18n/label :t/recent)) album)
|
||||
:include (clj->js ["imageSize"])})
|
||||
(.getPhotos
|
||||
CameraRoll
|
||||
#js
|
||||
{:first num
|
||||
:assetType "Photos"
|
||||
:groupTypes (if (= album (i18n/label :t/recent)) "All" "Albums")
|
||||
:groupName (when (not= album (i18n/label :t/recent)) album)
|
||||
:include (clj->js ["imageSize"])}))
|
||||
(.then #(let [response (types/js->clj %)]
|
||||
(re-frame/dispatch [:on-camera-roll-get-photos (:edges response)
|
||||
(:page_info response) end-cursor])))
|
||||
(.catch #(log/warn "could not get camera roll photos"))))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:chat.ui/camera-roll-get-albums
|
||||
(fn []
|
||||
(let [albums (atom [{:title :smart-albums :data []}
|
||||
{:title (i18n/label :t/my-albums) :data []}])]
|
||||
;; Get the "recent" album first
|
||||
(->
|
||||
(.getPhotos CameraRoll
|
||||
#js
|
||||
{:first 1
|
||||
:groupTypes "All"})
|
||||
(.then
|
||||
(fn [res]
|
||||
(let [recent-album {:title (i18n/label :t/recent)
|
||||
:count "--"
|
||||
:uri (get-in (first (:edges (types/js->clj res))) [:node :image :uri])}]
|
||||
(swap! albums update-in [0 :data] conj recent-album)
|
||||
;; Get albums, then loop over albums and get each one's cover (first photo)
|
||||
(->
|
||||
(.getAlbums CameraRoll #js {:assetType "All"})
|
||||
(.then
|
||||
(fn [response]
|
||||
(let [response (types/js->clj response)]
|
||||
(reduce
|
||||
(fn [_ album]
|
||||
(->
|
||||
(.getPhotos
|
||||
CameraRoll
|
||||
#js
|
||||
{:first 1
|
||||
:groupTypes "Albums"
|
||||
:groupName (:title album)})
|
||||
(.then (fn [res]
|
||||
(let [uri (get-in (first (:edges (types/js->clj res)))
|
||||
[:node :image :uri])]
|
||||
(swap! albums update-in [1 :data] conj (merge album {:uri uri}))
|
||||
(when (= (count (get-in @albums [1 :data])) (count response))
|
||||
(swap! albums update-in
|
||||
[1 :data]
|
||||
#(->> %
|
||||
(sort-by :title)))
|
||||
(re-frame/dispatch [:on-camera-roll-get-albums @albums])))))))
|
||||
nil
|
||||
response))))
|
||||
(.catch #(log/warn "could not get camera roll albums"))))))))))
|
||||
|
||||
|
||||
(rf/defn image-captured
|
||||
{:events [:chat.ui/image-captured]}
|
||||
[{:keys [db]} chat-id uri]
|
||||
@@ -189,81 +57,17 @@
|
||||
(not (get images uri)))
|
||||
{::image-selected [uri current-chat-id]})))
|
||||
|
||||
(rf/defn on-end-reached
|
||||
{:events [:camera-roll/on-end-reached]}
|
||||
[_ end-cursor selected-album loading? has-next-page?]
|
||||
(when (and (not loading?) has-next-page?)
|
||||
(re-frame/dispatch [:chat.ui/camera-roll-loading-more true])
|
||||
(re-frame/dispatch [:chat.ui/camera-roll-get-photos 20 end-cursor selected-album])))
|
||||
|
||||
(rf/defn camera-roll-get-photos
|
||||
{:events [:chat.ui/camera-roll-get-photos]}
|
||||
[_ num end-cursor selected-album]
|
||||
{::camera-roll-get-photos [num end-cursor selected-album]})
|
||||
|
||||
(rf/defn camera-roll-get-albums
|
||||
{:events [:chat.ui/camera-roll-get-albums]}
|
||||
[_]
|
||||
{:chat.ui/camera-roll-get-albums []})
|
||||
|
||||
(rf/defn camera-roll-loading-more
|
||||
{:events [:chat.ui/camera-roll-loading-more]}
|
||||
[{:keys [db]} is-loading]
|
||||
{:db (assoc db :camera-roll/loading-more is-loading)})
|
||||
|
||||
(rf/defn on-camera-roll-get-photos
|
||||
{:events [:on-camera-roll-get-photos]}
|
||||
[{:keys [db] :as cofx} photos page-info end-cursor]
|
||||
(let [photos_x (when end-cursor (:camera-roll/photos db))]
|
||||
{:db (-> db
|
||||
(assoc :camera-roll/photos (concat photos_x (map #(get-in % [:node :image]) photos)))
|
||||
(assoc :camera-roll/end-cursor (:end_cursor page-info))
|
||||
(assoc :camera-roll/has-next-page (:has_next_page page-info))
|
||||
(assoc :camera-roll/loading-more false))}))
|
||||
|
||||
(rf/defn on-camera-roll-get-albums
|
||||
{:events [:on-camera-roll-get-albums]}
|
||||
[{:keys [db]} albums]
|
||||
{:db (-> db
|
||||
(assoc :camera-roll/albums albums)
|
||||
(assoc :camera-roll/loading-albums false))})
|
||||
|
||||
(rf/defn clear-sending-images
|
||||
{:events [:chat.ui/clear-sending-images]}
|
||||
[{:keys [db]}]
|
||||
{:db (update-in db [:chat/inputs (:current-chat-id db) :metadata] assoc :sending-image {})})
|
||||
|
||||
(rf/defn cancel-sending-image
|
||||
{:events [:chat.ui/cancel-sending-image]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(clear-sending-images cofx))
|
||||
|
||||
(rf/defn image-selected
|
||||
{:events [:chat.ui/image-selected]}
|
||||
[{:keys [db]} current-chat-id original {:keys [resized-uri width height]}]
|
||||
{:db
|
||||
(update-in db
|
||||
[:chat/inputs current-chat-id :metadata :sending-image (:uri original)]
|
||||
merge
|
||||
original
|
||||
{:resized-uri resized-uri
|
||||
:width width
|
||||
:height height})})
|
||||
|
||||
(rf/defn image-unselected
|
||||
{:events [:chat.ui/image-unselected]}
|
||||
[{:keys [db]} original]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
{:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image] dissoc (:uri original))}))
|
||||
|
||||
(rf/defn chat-open-image-picker
|
||||
{:events [:chat.ui/open-image-picker]}
|
||||
[{:keys [db]} chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(when (< (count images) config/max-images-batch)
|
||||
{::chat-open-image-picker current-chat-id})))
|
||||
|
||||
(rf/defn chat-show-image-picker-camera
|
||||
{:events [:chat.ui/show-image-picker-camera]}
|
||||
[{:keys [db]} chat-id]
|
||||
@@ -271,25 +75,3 @@
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(when (< (count images) config/max-images-batch)
|
||||
{::chat-open-image-picker-camera current-chat-id})))
|
||||
|
||||
(rf/defn camera-roll-pick
|
||||
{:events [:chat.ui/camera-roll-pick]}
|
||||
[{:keys [db]} image chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(if (get-in db [:chats current-chat-id :timeline?])
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :metadata :sending-image] {})
|
||||
::image-selected [image current-chat-id]}
|
||||
(when (and (< (count images) config/max-images-batch)
|
||||
(not (some #(= (:uri image) (:uri %)) images)))
|
||||
{::image-selected [image current-chat-id]}))))
|
||||
|
||||
(rf/defn camera-roll-select-album
|
||||
{:events [:chat.ui/camera-roll-select-album]}
|
||||
[{:keys [db]} album]
|
||||
{:db (assoc db :camera-roll/selected-album album)})
|
||||
|
||||
(rf/defn save-image-to-gallery
|
||||
{:events [:chat.ui/save-image-to-gallery]}
|
||||
[_ base64-uri]
|
||||
{::save-image-to-gallery base64-uri})
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
[status-im.data-store.reactions :as data-store.reactions]
|
||||
[status-im.transport.message.protocol :as message.protocol]
|
||||
[utils.re-frame :as rf]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(defn update-reaction
|
||||
[acc retracted chat-id message-id emoji-id emoji-reaction-id reaction]
|
||||
@@ -94,3 +95,42 @@
|
||||
acc))
|
||||
[]
|
||||
reactions))
|
||||
|
||||
(defn- <-rpc
|
||||
[{compressed-key :compressedKey
|
||||
emoji-id :emojiId
|
||||
:keys [from]}]
|
||||
{:compressed-key compressed-key
|
||||
:emoji-id emoji-id
|
||||
:from from})
|
||||
|
||||
|
||||
(defn- format-response
|
||||
[response]
|
||||
(->> (transforms/js->clj response)
|
||||
(map <-rpc)
|
||||
(group-by :emoji-id)))
|
||||
|
||||
(rf/defn save-emoji-reaction-details
|
||||
{:events [:chat/save-emoji-reaction-details]}
|
||||
[{:keys [db]} message-reactions long-pressed-emoji]
|
||||
{:db (assoc db
|
||||
:chat/reactions-authors
|
||||
{:reaction-authors-list message-reactions
|
||||
:selected-reaction long-pressed-emoji})})
|
||||
|
||||
(rf/defn clear-emoji-reaction-details
|
||||
{:events [:chat/clear-emoji-reaction-author-details]}
|
||||
[{:keys [db]} message-reactions]
|
||||
{:db (update db dissoc :chat/reactions-authors)})
|
||||
|
||||
(rf/defn emoji-reactions-by-message-id
|
||||
{:events [:chat.ui/emoji-reactions-by-message-id]}
|
||||
[{:keys [db]} {:keys [message-id long-pressed-emoji]}]
|
||||
{:json-rpc/call [{:method "wakuext_emojiReactionsByChatIDMessageID"
|
||||
:params [(:current-chat-id db) message-id]
|
||||
:js-response true
|
||||
:on-error #(log/error "failed to fetch emoji reaction by message-id: "
|
||||
{:message-id message-id :error %})
|
||||
:on-success #(rf/dispatch [:chat/save-emoji-reaction-details
|
||||
(format-response %) long-pressed-emoji])}]})
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
status-im.signals.core
|
||||
status-im.stickers.core
|
||||
status-im.transport.core
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[react-native.permissions :as permissions]
|
||||
[status-im.ui.components.react :as react]
|
||||
status-im.ui.screens.privacy-and-security-settings.events
|
||||
[status-im.utils.dimensions :as dimensions]
|
||||
|
||||
@@ -165,6 +165,15 @@
|
||||
(wallet/request-current-block-update))
|
||||
(prices/update-prices)))
|
||||
|
||||
(rf/defn login-local-paired-user
|
||||
{:events [:multiaccounts.login/local-paired-user]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [key-uid name password]} (get-in db [:syncing :multiaccount])]
|
||||
{::login [key-uid
|
||||
(types/clj->json {:name name
|
||||
:key-uid key-uid})
|
||||
password]}))
|
||||
|
||||
(rf/defn login
|
||||
{:events [:multiaccounts.login.ui/password-input-submitted]}
|
||||
[{:keys [db]}]
|
||||
@@ -294,7 +303,7 @@
|
||||
(re-frame/reg-fx
|
||||
;;TODO: this could be replaced by a single API call on status-go side
|
||||
::initialize-wallet
|
||||
(fn [[network-id callback]]
|
||||
(fn [[network-id network callback]]
|
||||
(-> (js/Promise.all
|
||||
(clj->js
|
||||
[(js/Promise.
|
||||
@@ -302,12 +311,31 @@
|
||||
(json-rpc/call {:method "accounts_getAccounts"
|
||||
:on-success resolve
|
||||
:on-error reject})))
|
||||
(js/Promise.
|
||||
(fn [resolve _]
|
||||
(json-rpc/call
|
||||
{:method "wallet_addEthereumChain"
|
||||
:params
|
||||
[{:isTest false
|
||||
:tokenOverrides []
|
||||
:rpcUrl (get-in network [:config :UpstreamConfig :URL])
|
||||
:chainColor "green"
|
||||
:chainName (:name network)
|
||||
:nativeCurrencyDecimals 10
|
||||
:shortName "erc20"
|
||||
:layer 1
|
||||
:chainId (int network-id)
|
||||
:enabled false
|
||||
:fallbackURL (get-in network [:config :UpstreamConfig :URL])}]
|
||||
:on-success resolve
|
||||
:on-error (fn [_] (resolve nil))})))
|
||||
(js/Promise.
|
||||
(fn [resolve _]
|
||||
(json-rpc/call {:method "wallet_getTokens"
|
||||
:params [(int network-id)]
|
||||
:on-success resolve
|
||||
:on-error #(resolve nil)})))
|
||||
:on-error (fn [_]
|
||||
(resolve nil))})))
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(json-rpc/call {:method "wallet_getCustomTokens"
|
||||
@@ -318,7 +346,7 @@
|
||||
(json-rpc/call {:method "wallet_getSavedAddresses"
|
||||
:on-success resolve
|
||||
:on-error reject})))]))
|
||||
(.then (fn [[accounts tokens custom-tokens favourites]]
|
||||
(.then (fn [[accounts _ tokens custom-tokens favourites]]
|
||||
(callback accounts
|
||||
(normalize-tokens network-id tokens)
|
||||
(mapv #(update % :symbol keyword) custom-tokens)
|
||||
@@ -365,22 +393,27 @@
|
||||
(rf/defn redirect-to-root
|
||||
"Decides which root should be initialised depending on user and app state"
|
||||
[{:keys [db] :as cofx}]
|
||||
(cond
|
||||
(get db :onboarding-2/new-account?)
|
||||
{:dispatch [:onboarding-2/finalize-setup]}
|
||||
(let [pairing-completed? (= (get-in db [:syncing :pairing-status]) :completed)]
|
||||
(cond
|
||||
pairing-completed?
|
||||
{:db (dissoc db :syncing)
|
||||
:dispatch [:init-root :syncing-results]}
|
||||
|
||||
(get db :tos/accepted?)
|
||||
(rf/merge
|
||||
cofx
|
||||
(multiaccounts/switch-theme nil :shell-stack)
|
||||
(navigation/init-root :shell-stack))
|
||||
(get db :onboarding-2/new-account?)
|
||||
{:dispatch [:onboarding-2/finalize-setup]}
|
||||
|
||||
:else
|
||||
{:dispatch [:init-root :tos]}))
|
||||
(get db :tos/accepted?)
|
||||
(rf/merge
|
||||
cofx
|
||||
(multiaccounts/switch-theme nil :shell-stack)
|
||||
(navigation/init-root :shell-stack))
|
||||
|
||||
:else
|
||||
{:dispatch [:init-root :tos]})))
|
||||
|
||||
(rf/defn get-settings-callback
|
||||
{:events [::get-settings-callback]}
|
||||
[{:keys [db] :as cofx} settings pairing-in-progress?]
|
||||
[{:keys [db] :as cofx} settings]
|
||||
(let [{:networks/keys [current-network networks]
|
||||
:as settings}
|
||||
(data-store.settings/rpc->settings settings)
|
||||
@@ -412,8 +445,7 @@
|
||||
(activity-center/notifications-fetch-pending-contact-requests)
|
||||
(activity-center/update-seen-state)
|
||||
(activity-center/notifications-fetch-unread-count)
|
||||
(when-not pairing-in-progress?
|
||||
(redirect-to-root)))))
|
||||
(redirect-to-root))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::open-last-chat
|
||||
@@ -449,6 +481,7 @@
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:networks/keys [current-network networks]} db
|
||||
notifications-enabled? (get-in db [:multiaccount :notifications-enabled?])
|
||||
current-network-config (get networks current-network)
|
||||
network-id (str (get-in networks
|
||||
[current-network :config :NetworkId]))
|
||||
remote-push-notifications-enabled?
|
||||
@@ -460,6 +493,7 @@
|
||||
:on-disabled #(log/info "eip1559 is not activated")}
|
||||
::initialize-wallet
|
||||
[network-id
|
||||
current-network-config
|
||||
(fn [accounts tokens custom-tokens favourites]
|
||||
(re-frame/dispatch [::initialize-wallet
|
||||
accounts tokens custom-tokens favourites]))]
|
||||
@@ -490,9 +524,8 @@
|
||||
|
||||
(rf/defn login-only-events
|
||||
[{:keys [db] :as cofx} key-uid password save-password?]
|
||||
(let [auth-method (:auth-method db)
|
||||
new-auth-method (get-new-auth-method auth-method save-password?)
|
||||
pairing-in-progress? (get-in db [:syncing :pairing-in-progress?])]
|
||||
(let [auth-method (:auth-method db)
|
||||
new-auth-method (get-new-auth-method auth-method save-password?)]
|
||||
(log/debug "[login] login-only-events"
|
||||
"auth-method" auth-method
|
||||
"new-auth-method" new-auth-method)
|
||||
@@ -500,7 +533,7 @@
|
||||
{:db (assoc db :chats/loading? true)
|
||||
:json-rpc/call
|
||||
[{:method "settings_getSettings"
|
||||
:on-success #(re-frame/dispatch [::get-settings-callback % pairing-in-progress?])}]}
|
||||
:on-success #(re-frame/dispatch [::get-settings-callback %])}]}
|
||||
(notifications/load-notification-preferences)
|
||||
(when save-password?
|
||||
(keychain/save-user-password key-uid password))
|
||||
|
||||
@@ -89,8 +89,9 @@
|
||||
{:events [:chat.ui/show-profile]}
|
||||
[{:keys [db]} identity ens-name]
|
||||
(let [my-public-key (get-in db [:multiaccount :public-key])]
|
||||
(when (not= my-public-key identity)
|
||||
(if (not= my-public-key identity)
|
||||
{:db (-> db
|
||||
(assoc :contacts/identity identity)
|
||||
(assoc :contacts/ens-name ens-name))
|
||||
:dispatch [:contacts/build-contact identity ens-name true]})))
|
||||
:dispatch [:contacts/build-contact identity ens-name true]}
|
||||
{:dispatch [:navigate-to :my-profile]})))
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[status-im2.contexts.chat.messages.link-preview.events :as link-preview]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]))
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(rf/defn status-node-started
|
||||
[{db :db :as cofx} {:keys [error]}]
|
||||
@@ -56,36 +56,58 @@
|
||||
:peers-count (count (:peers peer-stats)))}))
|
||||
|
||||
(rf/defn handle-local-pairing-signals
|
||||
[{:keys [db] :as cofx} event]
|
||||
[{:keys [db] :as cofx} {:keys [type action data error] :as event}]
|
||||
(log/info "local pairing signal received"
|
||||
{:event event})
|
||||
(let [connection-success? (and (= (:type event)
|
||||
(let [{:keys [account password]} data
|
||||
role (get-in db [:syncing :role])
|
||||
receiver? (= role constants/local-pairing-role-receiver)
|
||||
sender? (= role constants/local-pairing-role-sender)
|
||||
connection-success? (and (= type
|
||||
constants/local-pairing-event-connection-success)
|
||||
(= (:action event)
|
||||
(= action
|
||||
constants/local-pairing-action-connect))
|
||||
error-on-pairing? (contains? constants/local-pairing-event-errors (:type event))
|
||||
completed-pairing? (and (= (:type event)
|
||||
error-on-pairing? (contains? constants/local-pairing-event-errors type)
|
||||
completed-pairing? (and (= type
|
||||
constants/local-pairing-event-transfer-success)
|
||||
(= (:action event)
|
||||
(= action
|
||||
constants/local-pairing-action-pairing-installation))
|
||||
logged-in? (multiaccounts.model/logged-in? db)
|
||||
;; since `connection-success` event is received on both sender and receiver devices
|
||||
;; we check the `logged-in?` status to identify the receiver and take the user to next screen
|
||||
navigate-to-syncing-devices? (and connection-success? (not logged-in?))
|
||||
received-account? (and (= type
|
||||
constants/local-pairing-event-received-account)
|
||||
(= action
|
||||
constants/local-pairing-action-pairing-account)
|
||||
(and (some? account) (some? password)))
|
||||
multiaccount-data (when received-account?
|
||||
(merge account {:password password}))
|
||||
navigate-to-syncing-devices? (and (or connection-success? error-on-pairing?) receiver?)
|
||||
user-in-syncing-devices-screen? (= (:view-id db) :syncing-progress)]
|
||||
(merge {:db (cond-> db
|
||||
connection-success?
|
||||
(assoc-in [:syncing :pairing-in-progress?] :connected)
|
||||
(assoc-in [:syncing :pairing-status] :connected)
|
||||
|
||||
received-account?
|
||||
(assoc-in [:syncing :multiaccount] multiaccount-data)
|
||||
|
||||
error-on-pairing?
|
||||
(assoc-in [:syncing :pairing-in-progress?] :error)
|
||||
(assoc-in [:syncing :pairing-status] :error)
|
||||
|
||||
completed-pairing?
|
||||
(assoc-in [:syncing :pairing-in-progress?] :completed))}
|
||||
(when (and navigate-to-syncing-devices? (not user-in-syncing-devices-screen?))
|
||||
{:dispatch [:navigate-to :syncing-progress]})
|
||||
(when completed-pairing?
|
||||
{:dispatch [:syncing/pairing-completed]}))))
|
||||
(assoc-in [:syncing :pairing-status] :completed))}
|
||||
(cond
|
||||
(and navigate-to-syncing-devices? (not user-in-syncing-devices-screen?))
|
||||
{:dispatch [:navigate-to :syncing-progress]}
|
||||
|
||||
(and completed-pairing? sender?)
|
||||
{:dispatch [:syncing/clear-states]}
|
||||
|
||||
(and completed-pairing? receiver?)
|
||||
{:dispatch [:multiaccounts.login/local-paired-user]}
|
||||
|
||||
error-on-pairing?
|
||||
{:dispatch [:toasts/upsert
|
||||
{:icon :i/alert
|
||||
:icon-color colors/danger-50
|
||||
:text error}]}))))
|
||||
|
||||
(rf/defn process
|
||||
{:events [:signals/signal-received]}
|
||||
|
||||
@@ -219,9 +219,10 @@
|
||||
(rf/defn update-estimated-gas-success
|
||||
{:events [:signing/update-estimated-gas-success]}
|
||||
[{db :db} gas]
|
||||
{:db (-> db
|
||||
(assoc-in [:signing/tx :gas] gas)
|
||||
(assoc-in [:signing/edit-fee :gas-loading?] false))})
|
||||
(when (contains? db :signing/tx)
|
||||
{:db (-> db
|
||||
(assoc-in [:signing/tx :gas] gas)
|
||||
(assoc-in [:signing/edit-fee :gas-loading?] false))}))
|
||||
|
||||
(rf/defn update-gas-price-success
|
||||
{:events [:signing/update-gas-price-success]}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
(ns status-im.ui.components.permissions
|
||||
(:require ["react-native-permissions" :refer (requestMultiple PERMISSIONS RESULTS)]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(def permissions-map
|
||||
{:read-external-storage (cond
|
||||
platform/android? (.-READ_EXTERNAL_STORAGE (.-ANDROID PERMISSIONS)))
|
||||
:write-external-storage (cond
|
||||
platform/low-device? (.-WRITE_EXTERNAL_STORAGE (.-ANDROID PERMISSIONS)))
|
||||
:camera (cond
|
||||
platform/android? (.-CAMERA (.-ANDROID PERMISSIONS))
|
||||
platform/ios? (.-CAMERA (.-IOS PERMISSIONS)))
|
||||
:record-audio (cond
|
||||
platform/android? (.-RECORD_AUDIO (.-ANDROID PERMISSIONS))
|
||||
platform/ios? (.-MICROPHONE (.-IOS PERMISSIONS)))})
|
||||
|
||||
(defn all-granted?
|
||||
[permissions]
|
||||
(let [permission-vals (distinct (vals permissions))]
|
||||
(and (= (count permission-vals) 1)
|
||||
(not (#{(.-BLOCKED RESULTS) (.-DENIED RESULTS)} (first permission-vals))))))
|
||||
|
||||
(defn request-permissions
|
||||
[{:keys [permissions on-allowed on-denied]
|
||||
:or {on-allowed #()
|
||||
on-denied #()}}]
|
||||
(let [permissions (remove nil? (mapv #(get permissions-map %) permissions))]
|
||||
(if (empty? permissions)
|
||||
(on-allowed)
|
||||
(-> (requestMultiple (clj->js permissions))
|
||||
(.then #(if (all-granted? (js->clj %))
|
||||
(on-allowed)
|
||||
(on-denied)))
|
||||
(.catch on-denied)))))
|
||||
@@ -11,7 +11,7 @@
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.connectivity.view :as connectivity]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.permissions :as components.permissions]
|
||||
[react-native.permissions :as components.permissions]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.tooltip.views :as tooltip]
|
||||
[status-im.ui.components.webview :as components.webview]
|
||||
|
||||
@@ -384,8 +384,7 @@
|
||||
(fn [prices wallet-currency amount amount-error display-symbol fee-display-symbol prices-loading?]
|
||||
(let [converted-value (* amount
|
||||
(get-in prices
|
||||
[(keyword display-symbol) (keyword (:code wallet-currency))
|
||||
:price]))]
|
||||
[(keyword display-symbol) (keyword (:code wallet-currency))]))]
|
||||
[quo/list-item
|
||||
{:size :small
|
||||
:title (if amount-error
|
||||
@@ -412,8 +411,8 @@
|
||||
show-error (reagent/atom false)]
|
||||
(let [converted-fee-value (* fee
|
||||
(get-in prices
|
||||
[(keyword fee-display-symbol) (keyword (:code wallet-currency))
|
||||
:price]))]
|
||||
[(keyword fee-display-symbol)
|
||||
(keyword (:code wallet-currency))]))]
|
||||
[quo/list-item
|
||||
{:size :small
|
||||
:title (if (and (not (or gas-price-loading? gas-loading?)) gas-error)
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
(ns status-im.ui.screens.wallet.account.styles
|
||||
(:require [quo.design-system.colors :as colors]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.animation :as animation]))
|
||||
|
||||
(defn card
|
||||
[window-width color]
|
||||
{:width (- window-width 30)
|
||||
:height 161
|
||||
:background-color color
|
||||
:background-color (if (string/blank? color)
|
||||
colors/blue
|
||||
color)
|
||||
:shadow-offset {:width 0 :height 2}
|
||||
:shadow-radius 8
|
||||
:shadow-opacity 1
|
||||
|
||||
@@ -92,4 +92,4 @@
|
||||
:accessibility-label (str (:symbol token) "-asset-value")
|
||||
:icon (if icon
|
||||
[wallet.components/token-icon icon]
|
||||
[chat-icon/custom-icon-view-list (:name token) color])}])
|
||||
[chat-icon/custom-icon-view-list (:name token) color])}])
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.ui.screens.wallet.accounts.styles
|
||||
(:require [quo.animated :as animated]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.animated :as animated]
|
||||
[quo.design-system.colors :as colors]))
|
||||
|
||||
(def dot-size 6)
|
||||
@@ -68,7 +69,9 @@
|
||||
(defn card
|
||||
[color card-width]
|
||||
(merge (card-common card-width)
|
||||
{:background-color color
|
||||
{:background-color (if (string/blank? color)
|
||||
colors/blue
|
||||
color)
|
||||
:justify-content :space-between
|
||||
:padding-horizontal 12
|
||||
:padding-top 12
|
||||
|
||||
@@ -120,7 +120,6 @@
|
||||
{:horizontal true
|
||||
:deceleration-rate "fast"
|
||||
:snap-to-interval page-width
|
||||
:snap-to-alignment "left"
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:scroll-event-throttle 64
|
||||
:on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)]
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
[react/scroll-view
|
||||
{:horizontal true
|
||||
:deceleration-rate "fast"
|
||||
:snap-to-alignment "left"
|
||||
:snap-to-alignment "start"
|
||||
:shows-horizontal-scroll-indicator
|
||||
false
|
||||
:scroll-event-throttle 64
|
||||
|
||||
@@ -8,16 +8,22 @@
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.wallet.accounts.common :as common]))
|
||||
|
||||
(defn asset
|
||||
[currency token]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(re-frame/dispatch [:wallet.send/set-symbol (:symbol token)])}
|
||||
[common/render-asset token nil nil (:code currency)]])
|
||||
|
||||
(views/defview assets
|
||||
[address]
|
||||
(views/letsubs [{:keys [tokens]} [:wallet/visible-assets-with-values address]
|
||||
currency [:wallet/currency]]
|
||||
[:<>
|
||||
(for [token tokens]
|
||||
^{:key (str (:symbol token))}
|
||||
[react/touchable-highlight
|
||||
{:on-press #(re-frame/dispatch [:wallet.send/set-symbol (:symbol token)])}
|
||||
[common/render-asset token nil nil (:code currency)]])]))
|
||||
[react/view
|
||||
{:style {:height 300}}
|
||||
[list/flat-list
|
||||
{:data tokens
|
||||
:key-fn :symbol
|
||||
:render-fn (partial asset currency)}]]))
|
||||
|
||||
(defn render-account
|
||||
[account _ _ {:keys [field event]}]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im.ui.screens.wallet.send.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs] :as views])
|
||||
(:require [quo.core :as quo]
|
||||
[quo2.core :as quo2]
|
||||
[quo.design-system.colors :as colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.commands.core :as commands]
|
||||
@@ -234,6 +235,17 @@
|
||||
(let [to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))]
|
||||
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[:<>
|
||||
[quo2/page-nav
|
||||
{:align-mid? true
|
||||
:mid-section {:type :text-only
|
||||
:main-text (i18n/label :t/send-transaction)}
|
||||
|
||||
:left-section {:on-press #(do
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
(re-frame/dispatch
|
||||
[:wallet/cancel-transaction-command]))
|
||||
:icon :i/arrow-left
|
||||
:accessibility-label :back-button}}]
|
||||
[react/scroll-view
|
||||
{:style {:flex 1}
|
||||
:keyboard-should-persist-taps :handled}
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
(-> amount-str
|
||||
(js/parseFloat)
|
||||
bignumber
|
||||
(crypto->fiat (get-in prices [from to :price] ^js (bignumber 0)))
|
||||
(crypto->fiat (get-in prices [from to] ^js (bignumber 0)))
|
||||
(with-precision 2)
|
||||
str))
|
||||
|
||||
|
||||
@@ -4,22 +4,14 @@
|
||||
(defn- format-price-resp
|
||||
[resp to mainnet?]
|
||||
(if mainnet?
|
||||
(when (seq resp)
|
||||
(into {}
|
||||
(for [[from price] resp]
|
||||
{from {(keyword to) {:from (name from)
|
||||
:to to
|
||||
:price price}}})))
|
||||
resp
|
||||
{:ETH (into {}
|
||||
(for [[_ price] resp]
|
||||
{(keyword to) {:from "ETH"
|
||||
:to to
|
||||
:price price}}))}))
|
||||
{(keyword to) price}))}))
|
||||
|
||||
(defn get-prices
|
||||
[from to mainnet? on-success on-error]
|
||||
(let [to (first to)]
|
||||
(json-rpc/call {:method "wallet_fetchPrices"
|
||||
:params [from to]
|
||||
:on-success (fn [resp] (on-success (format-price-resp resp to mainnet?)))
|
||||
:on-error on-error})))
|
||||
(json-rpc/call {:method "wallet_fetchPrices"
|
||||
:params [from to]
|
||||
:on-success (fn [resp] (on-success (format-price-resp resp to mainnet?)))
|
||||
:on-error on-error}))
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
(defn derive-and-store-account
|
||||
[key-uid path hashed-password type accounts]
|
||||
(fn [value]
|
||||
(let [{:keys [id error]} (types/json->clj value)]
|
||||
(let [{:keys [id error keyUid]} (types/json->clj value)]
|
||||
(if error
|
||||
(re-frame/dispatch [::new-account-error :password-error error])
|
||||
(native-module/multiaccount-derive-addresses
|
||||
@@ -96,6 +96,7 @@
|
||||
[:wallet.accounts/account-stored
|
||||
{:address address
|
||||
:public-key publicKey
|
||||
:key-uid keyUid
|
||||
:type type
|
||||
:path (normalize-path path)}])))))))))))))
|
||||
|
||||
@@ -192,13 +193,33 @@
|
||||
(rf/defn save-new-account
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [latest-derived-path]} (:multiaccount db)
|
||||
{:keys [account type]} (:add-account db)
|
||||
accounts (:multiaccount/accounts db)
|
||||
new-accounts (conj accounts account)]
|
||||
{:keys [account type]} (:add-account db)
|
||||
{:keys [address name key-uid]} account
|
||||
main-key-uid (or key-uid (get-in db [:multiaccount :key-uid]))
|
||||
accounts (:multiaccount/accounts db)
|
||||
new-accounts (conj accounts account)
|
||||
;; Note(rasom): in case if a new account is created using a mnemonic or
|
||||
;; a private key we add a new keypair to the database, otherwise we just
|
||||
;; keep using "profile" keypair
|
||||
[method params]
|
||||
(if key-uid
|
||||
["accounts_saveKeypair"
|
||||
[{:key-uid main-key-uid
|
||||
:name name
|
||||
:type (if (= type :seed)
|
||||
"seed"
|
||||
"key")
|
||||
:derived-from address
|
||||
:last-used-derivation-index 0
|
||||
:synced-from ""
|
||||
:clock 0
|
||||
:accounts [account]}]]
|
||||
["accounts_saveAccount"
|
||||
[(assoc account :key-uid key-uid)]])]
|
||||
(when account
|
||||
(rf/merge cofx
|
||||
{:json-rpc/call [{:method "accounts_saveAccounts"
|
||||
:params [[account]]
|
||||
{:json-rpc/call [{:method method
|
||||
:params params
|
||||
:on-success #(re-frame/dispatch [::wallet/restart])}]
|
||||
:db (-> db
|
||||
(assoc :multiaccount/accounts new-accounts)
|
||||
@@ -293,8 +314,8 @@
|
||||
color (assoc :color color)
|
||||
(not (nil? hidden)) (assoc :hidden hidden))
|
||||
new-accounts (replace {account new-account} accounts)]
|
||||
{:json-rpc/call [{:method "accounts_saveAccounts"
|
||||
:params [[new-account]]
|
||||
{:json-rpc/call [{:method "accounts_saveAccount"
|
||||
:params [new-account]
|
||||
:on-success #()}]
|
||||
:db (assoc db :multiaccount/accounts new-accounts)}))
|
||||
|
||||
|
||||
@@ -381,30 +381,31 @@
|
||||
(rf/defn wallet-send-gas-price-success
|
||||
{:events [:wallet.send/update-gas-price-success]}
|
||||
[{db :db} tx-entry price {:keys [maxFeePerGas maxPriorityFeePerGas gasPrice]}]
|
||||
(if (eip1559/sync-enabled?)
|
||||
(let [{:keys [slow-base-fee normal-base-fee fast-base-fee
|
||||
current-base-fee max-priority-fee]}
|
||||
price
|
||||
max-priority-fee-bn (money/with-precision (signing.gas/get-suggested-tip max-priority-fee) 0)
|
||||
fee-cap (-> normal-base-fee
|
||||
money/bignumber
|
||||
(money/add max-priority-fee-bn)
|
||||
money/to-hex)
|
||||
tip-cap (money/to-hex max-priority-fee-bn)]
|
||||
(when (contains? db tx-entry)
|
||||
(if (eip1559/sync-enabled?)
|
||||
(let [{:keys [slow-base-fee normal-base-fee fast-base-fee
|
||||
current-base-fee max-priority-fee]}
|
||||
price
|
||||
max-priority-fee-bn (money/with-precision (signing.gas/get-suggested-tip max-priority-fee) 0)
|
||||
fee-cap (-> normal-base-fee
|
||||
money/bignumber
|
||||
(money/add max-priority-fee-bn)
|
||||
money/to-hex)
|
||||
tip-cap (money/to-hex max-priority-fee-bn)]
|
||||
{:db (-> db
|
||||
(update tx-entry
|
||||
assoc
|
||||
:maxFeePerGas (or maxFeePerGas fee-cap)
|
||||
:maxPriorityFeePerGas (or maxPriorityFeePerGas tip-cap))
|
||||
(assoc :wallet/current-base-fee current-base-fee
|
||||
:wallet/normal-base-fee normal-base-fee
|
||||
:wallet/slow-base-fee slow-base-fee
|
||||
:wallet/fast-base-fee fast-base-fee
|
||||
:wallet/current-priority-fee max-priority-fee)
|
||||
(assoc-in [:signing/edit-fee :gas-price-loading?] false))})
|
||||
{:db (-> db
|
||||
(update tx-entry
|
||||
assoc
|
||||
:maxFeePerGas (or maxFeePerGas fee-cap)
|
||||
:maxPriorityFeePerGas (or maxPriorityFeePerGas tip-cap))
|
||||
(assoc :wallet/current-base-fee current-base-fee
|
||||
:wallet/normal-base-fee normal-base-fee
|
||||
:wallet/slow-base-fee slow-base-fee
|
||||
:wallet/fast-base-fee fast-base-fee
|
||||
:wallet/current-priority-fee max-priority-fee)
|
||||
(assoc-in [:signing/edit-fee :gas-price-loading?] false))})
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet/prepare-transaction :gasPrice] (or gasPrice price))
|
||||
(assoc-in [:signing/edit-fee :gas-price-loading?] false))}))
|
||||
(assoc-in [:wallet/prepare-transaction :gasPrice] (or gasPrice price))
|
||||
(assoc-in [:signing/edit-fee :gas-price-loading?] false))})))
|
||||
|
||||
(rf/defn set-max-amount
|
||||
{:events [:wallet.send/set-max-amount]}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
:margin-vertical 8})
|
||||
|
||||
(defn sheet
|
||||
[{:keys [top bottom]} window-height override-theme shell?]
|
||||
[{:keys [top bottom]} window-height override-theme padding-bottom-override shell?]
|
||||
{:position :absolute
|
||||
:max-height (- window-height top 20)
|
||||
:z-index 1
|
||||
@@ -22,9 +22,9 @@
|
||||
:right 0
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:overflow :hidden
|
||||
:overflow (when shell? :hidden)
|
||||
:flex 1
|
||||
:padding-bottom (max 20 bottom)
|
||||
:padding-bottom (or padding-bottom-override (max 20 bottom))
|
||||
:background-color (if shell?
|
||||
:transparent
|
||||
(colors/theme-colors colors/white colors/neutral-90 override-theme))})
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
(def timing-options #js {:duration duration})
|
||||
|
||||
(defn hide
|
||||
[translate-y bg-opacity window-height]
|
||||
[translate-y bg-opacity window-height on-close]
|
||||
(when (fn? on-close)
|
||||
(on-close))
|
||||
;; it will be better to use animation callback, but it doesn't work
|
||||
;; so we have to use timeout, also we add 50ms for safety
|
||||
(js/setTimeout #(rf/dispatch [:bottom-sheet-hidden]) (+ duration 50))
|
||||
@@ -29,7 +31,7 @@
|
||||
(def gesture-values (atom {}))
|
||||
|
||||
(defn get-sheet-gesture
|
||||
[translate-y bg-opacity window-height]
|
||||
[translate-y bg-opacity window-height on-close]
|
||||
(-> (gesture/gesture-pan)
|
||||
(gesture/on-start
|
||||
(fn [_]
|
||||
@@ -47,17 +49,22 @@
|
||||
(fn [_]
|
||||
(if (< (:dy @gesture-values) 0)
|
||||
(show translate-y bg-opacity)
|
||||
(hide translate-y bg-opacity window-height))))))
|
||||
(hide translate-y bg-opacity window-height on-close))))))
|
||||
|
||||
(defn view
|
||||
[{:keys [hide? insets]} {:keys [content override-theme selected-item shell?]}]
|
||||
[{:keys [hide? insets]}
|
||||
{:keys [content override-theme selected-item padding-bottom-override on-close shell?]}]
|
||||
(let [{window-height :height} (rn/get-window)
|
||||
bg-opacity (reanimated/use-shared-value 0)
|
||||
translate-y (reanimated/use-shared-value window-height)
|
||||
sheet-gesture (get-sheet-gesture translate-y bg-opacity window-height)]
|
||||
(rn/use-effect #(if hide? (hide translate-y bg-opacity window-height) (show translate-y bg-opacity))
|
||||
[hide?])
|
||||
(hooks/use-back-handler #(do (rf/dispatch [:hide-bottom-sheet]) true))
|
||||
sheet-gesture (get-sheet-gesture translate-y bg-opacity window-height on-close)]
|
||||
(rn/use-effect
|
||||
#(if hide? (hide translate-y bg-opacity window-height on-close) (show translate-y bg-opacity))
|
||||
[hide?])
|
||||
(hooks/use-back-handler #(do (when (fn? on-close)
|
||||
(on-close))
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
true))
|
||||
[rn/view {:flex 1}
|
||||
;; backdrop
|
||||
[rn/touchable-without-feedback {:on-press #(rf/dispatch [:hide-bottom-sheet])}
|
||||
@@ -70,7 +77,7 @@
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateY translate-y}]}
|
||||
(styles/sheet insets window-height override-theme shell?))}
|
||||
(styles/sheet insets window-height override-theme padding-bottom-override shell?))}
|
||||
|
||||
(when shell?
|
||||
[blur/ios-view
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
:right 0}))
|
||||
|
||||
(defn main-view
|
||||
[translate-y]
|
||||
[translate-y override-theme]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:translate-y translate-y}]}
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95 override-theme)
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:flex 1
|
||||
@@ -36,8 +36,8 @@
|
||||
:align-items :center})
|
||||
|
||||
(defn handle
|
||||
[]
|
||||
[override-theme]
|
||||
{:width 32
|
||||
:height 4
|
||||
:border-radius 100
|
||||
:background-color (colors/theme-colors colors/neutral-100-opa-30 colors/white-opa-30)})
|
||||
:background-color (colors/theme-colors colors/neutral-100-opa-30 colors/white-opa-30 override-theme)})
|
||||
|
||||
@@ -45,8 +45,9 @@
|
||||
|
||||
(defn f-view
|
||||
[content skip-background?]
|
||||
(let [scroll-enabled (reagent/atom true)
|
||||
curr-scroll (atom 0)]
|
||||
(let [scroll-enabled (reagent/atom true)
|
||||
curr-scroll (atom 0)
|
||||
{:keys [override-theme]} (rf/sub [:get-screen-params])]
|
||||
(fn []
|
||||
(let [insets (safe-area/get-insets)
|
||||
{:keys [height]} (rn/get-window)
|
||||
@@ -70,9 +71,9 @@
|
||||
[reanimated/view {:style (style/background opacity)}])
|
||||
[gesture/gesture-detector
|
||||
{:gesture (drag-gesture translate-y opacity scroll-enabled curr-scroll close)}
|
||||
[reanimated/view {:style (style/main-view translate-y)}
|
||||
[reanimated/view {:style (style/main-view translate-y override-theme)}
|
||||
[rn/view {:style style/handle-container}
|
||||
[rn/view {:style (style/handle)}]]
|
||||
[rn/view {:style (style/handle override-theme)}]]
|
||||
[content
|
||||
{:insets insets
|
||||
:close close
|
||||
|
||||
@@ -277,9 +277,10 @@
|
||||
(def ^:const local-pairing-event-transfer-error "transfer-error")
|
||||
|
||||
;; receiver events
|
||||
(def ^:const local-pairing-event-received-amount "received-account")
|
||||
(def ^:const local-pairing-event-received-account "received-account")
|
||||
(def ^:const local-pairing-event-process-success "process-success")
|
||||
(def ^:const local-pairing-event-process-error "process-error")
|
||||
(def ^:const local-pairing-event-received-installation "received-installation")
|
||||
|
||||
(def ^:const local-pairing-event-errors
|
||||
#{local-pairing-event-connection-error
|
||||
|
||||
@@ -38,22 +38,18 @@
|
||||
[]
|
||||
(let [unread-count (rf/sub [:activity-center/unread-count])]
|
||||
[quo/action-drawer
|
||||
[[{:icon :i/check
|
||||
[[{:icon :i/mark-as-read
|
||||
:override-theme :dark
|
||||
:label (i18n/label :t/mark-all-notifications-as-read)
|
||||
:disabled? (zero? unread-count)
|
||||
:on-press (fn []
|
||||
(if (pos? unread-count)
|
||||
(rf/dispatch [:activity-center.notifications/mark-all-as-read-locally
|
||||
(fn []
|
||||
{:icon :up-to-date
|
||||
:icon-color colors/success-50
|
||||
:text (i18n/label :t/notifications-marked-as-read
|
||||
{:count unread-count})
|
||||
:override-theme :dark})])
|
||||
;; Need design improvements if there is NO unread
|
||||
;; notifications to mark as read
|
||||
;; https://github.com/status-im/status-mobile/issues/14983
|
||||
(js/alert "No unread notifications to mark as read"))
|
||||
(rf/dispatch [:activity-center.notifications/mark-all-as-read-locally
|
||||
(fn []
|
||||
{:icon :up-to-date
|
||||
:icon-color colors/success-50
|
||||
:text (i18n/label :t/notifications-marked-as-read
|
||||
{:count unread-count})
|
||||
:override-theme :dark})])
|
||||
(rf/dispatch [:hide-bottom-sheet]))}]]]))
|
||||
|
||||
(defn empty-tab
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
(ns status-im2.contexts.add-new-contact.style
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.typography :as typography]
|
||||
[react-native.platform :as platform]
|
||||
[quo2.foundations.typography :as typography]))
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(defn container-outer
|
||||
[]
|
||||
{:style {:flex (if platform/ios? 4.5 5)
|
||||
{:style {:flex 1
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:justify-content :space-between
|
||||
:align-items :center
|
||||
@@ -171,7 +172,7 @@
|
||||
:size 40
|
||||
:width 335
|
||||
:style {:margin-top 24
|
||||
:margin-bottom 24}
|
||||
:margin-bottom (+ (safe-area/get-bottom) 12)}
|
||||
:accessibility-label :new-contact-button
|
||||
:before :i/profile
|
||||
:disabled (not= state :valid)})
|
||||
|
||||
@@ -51,64 +51,68 @@
|
||||
show-paste-button? (and (not (string/blank? @clipboard))
|
||||
(string/blank? @default-value)
|
||||
(string/blank? input))]
|
||||
|
||||
[rn/view (style/container-outer)
|
||||
[rn/view style/container-inner
|
||||
[quo/button
|
||||
(merge (style/button-close)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back]))}) :i/close]
|
||||
[quo/text (style/text-title)
|
||||
(i18n/label :t/add-a-contact)]
|
||||
[quo/text (style/text-subtitle)
|
||||
(i18n/label :t/find-your-friends)]
|
||||
[quo/text (style/text-description)
|
||||
(i18n/label :t/ens-or-chat-key)]
|
||||
[rn/view style/container-text-input
|
||||
[rn/view (style/text-input-container invalid?)
|
||||
[rn/text-input
|
||||
(merge (style/text-input)
|
||||
{:default-value (or scanned @default-value input)
|
||||
:placeholder (i18n/label :t/type-some-chat-key)
|
||||
:on-change-text (fn [v]
|
||||
(reset! default-value v)
|
||||
(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity v nil]
|
||||
600))})]
|
||||
(when show-paste-button?
|
||||
[quo/button
|
||||
(merge style/button-paste
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! default-value @clipboard)
|
||||
(rf/dispatch
|
||||
[:contacts/set-new-identity @clipboard nil]))})
|
||||
(i18n/label :t/paste)])]
|
||||
[rn/keyboard-avoiding-view
|
||||
{:style {:flex 1}}
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press rn/dismiss-keyboard!}
|
||||
[rn/view (style/container-outer)
|
||||
[rn/view style/container-inner
|
||||
[quo/button
|
||||
(merge (style/button-close)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back]))}) :i/close]
|
||||
[quo/text (style/text-title)
|
||||
(i18n/label :t/add-a-contact)]
|
||||
[quo/text (style/text-subtitle)
|
||||
(i18n/label :t/find-your-friends)]
|
||||
[quo/text (style/text-description)
|
||||
(i18n/label :t/ens-or-chat-key)]
|
||||
[rn/view style/container-text-input
|
||||
[rn/view (style/text-input-container invalid?)
|
||||
[rn/text-input
|
||||
(merge (style/text-input)
|
||||
{:default-value (or scanned @default-value input)
|
||||
:placeholder (i18n/label :t/type-some-chat-key)
|
||||
:on-change-text (fn [v]
|
||||
(reset! default-value v)
|
||||
(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity v nil]
|
||||
600))
|
||||
:blur-on-submit true
|
||||
:return-key-type :done})]
|
||||
(when show-paste-button?
|
||||
[quo/button
|
||||
(merge style/button-paste
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! default-value @clipboard)
|
||||
(rf/dispatch
|
||||
[:contacts/set-new-identity @clipboard nil]))})
|
||||
(i18n/label :t/paste)])]
|
||||
[quo/button
|
||||
(merge style/button-qr
|
||||
{:on-press #(rf/dispatch
|
||||
[::qr-scanner/scan-code
|
||||
{:handler :contacts/qr-code-scanned}])})
|
||||
:i/scan]]
|
||||
(when invalid?
|
||||
[rn/view style/container-invalid
|
||||
[quo/icon :i/alert style/icon-invalid]
|
||||
[quo/text style/text-invalid
|
||||
(i18n/label (or msg :t/invalid-ens-or-key))]])
|
||||
(when (= state :valid)
|
||||
[found-contact public-key])]
|
||||
[quo/button
|
||||
(merge style/button-qr
|
||||
{:on-press #(rf/dispatch
|
||||
[::qr-scanner/scan-code
|
||||
{:handler :contacts/qr-code-scanned}])})
|
||||
:i/scan]]
|
||||
(when invalid?
|
||||
[rn/view style/container-invalid
|
||||
[quo/icon :i/alert style/icon-invalid]
|
||||
[quo/text style/text-invalid
|
||||
(i18n/label (or msg :t/invalid-ens-or-key))]])
|
||||
(when (= state :valid)
|
||||
[found-contact public-key])]
|
||||
[rn/view
|
||||
[quo/button
|
||||
(merge (style/button-view-profile state)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:chat.ui/show-profile public-key ens]))})
|
||||
(i18n/label :t/view-profile)]]]))))
|
||||
(merge (style/button-view-profile state)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:chat.ui/show-profile public-key ens]))})
|
||||
(i18n/label :t/view-profile)]]]]))))
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
[status-im2.contexts.chat.composer.actions.style :as style]))
|
||||
|
||||
(defn send-message
|
||||
[{:keys [text-value focused? maximized?]}
|
||||
[{:keys [sending-images? sending-links?]}
|
||||
{:keys [text-value focused? maximized?]}
|
||||
{:keys [height saved-height last-height opacity background-y container-opacity]}
|
||||
window-height]
|
||||
(reanimated/animate height constants/input-height)
|
||||
@@ -32,10 +33,12 @@
|
||||
(rf/dispatch [:chat.ui/set-chat-input-text nil])
|
||||
(reset! maximized? false)
|
||||
(reset! text-value "")
|
||||
(reset! sending-links? false)
|
||||
(reset! sending-images? false)
|
||||
(messages.list/scroll-to-bottom))
|
||||
|
||||
(defn f-send-button
|
||||
[{:keys [text-value] :as state}
|
||||
[props {:keys [text-value] :as state}
|
||||
animations window-height images?
|
||||
btn-opacity z-index]
|
||||
(rn/use-effect (fn []
|
||||
@@ -54,14 +57,14 @@
|
||||
{:icon true
|
||||
:size 32
|
||||
:accessibility-label :send-message-button
|
||||
:on-press #(send-message state animations window-height)}
|
||||
:on-press #(send-message props state animations window-height)}
|
||||
:i/arrow-up]])
|
||||
|
||||
(defn send-button
|
||||
[{:keys [text-value] :as state} animations window-height images?]
|
||||
[props {:keys [text-value] :as state} animations window-height images?]
|
||||
(let [btn-opacity (reanimated/use-shared-value 0)
|
||||
z-index (reagent/atom (if (and (empty? @text-value) (not images?)) 0 1))]
|
||||
[:f> f-send-button state animations window-height images? btn-opacity z-index]))
|
||||
[:f> f-send-button props state animations window-height images? btn-opacity z-index]))
|
||||
|
||||
(defn audio-button
|
||||
[{:keys [record-reset-fn input-ref]}
|
||||
@@ -197,6 +200,6 @@
|
||||
[image-button props animations insets]
|
||||
[reaction-button]
|
||||
[format-button]]
|
||||
[:f> send-button state animations window-height images]
|
||||
[:f> send-button props state animations window-height images]
|
||||
(when (and (not edit) (not images))
|
||||
[audio-button props state animations])])
|
||||
|
||||
@@ -61,18 +61,6 @@
|
||||
(when-not (string/blank? text)
|
||||
(rf/dispatch [:link-preview/unfurl-urls text]))))
|
||||
|
||||
(defn images-effect
|
||||
[{:keys [sending-images? input-ref]}
|
||||
{:keys [container-opacity]}
|
||||
images?]
|
||||
(when images?
|
||||
(reanimated/animate container-opacity 1))
|
||||
(when (and (not @sending-images?) images? @input-ref)
|
||||
(.focus ^js @input-ref)
|
||||
(reset! sending-images? true))
|
||||
(when-not images?
|
||||
(reset! sending-images? false)))
|
||||
|
||||
(defn audio-effect
|
||||
[{:keys [recording? gesture-enabled?]}
|
||||
{:keys [container-opacity]}
|
||||
@@ -83,9 +71,10 @@
|
||||
(reanimated/animate container-opacity 1)))
|
||||
|
||||
(defn empty-effect
|
||||
[{:keys [container-opacity]}
|
||||
[{:keys [focused?]}
|
||||
{:keys [container-opacity]}
|
||||
{:keys [input-text images link-previews? reply audio]}]
|
||||
(when (utils/empty-input? input-text images link-previews? reply audio)
|
||||
(when (and (not @focused?) (utils/empty-input? input-text images link-previews? reply audio))
|
||||
(reanimated/animate-delay container-opacity constants/empty-opacity 200)))
|
||||
|
||||
(defn component-will-unmount
|
||||
@@ -94,26 +83,9 @@
|
||||
(.remove ^js @keyboard-hide-listener)
|
||||
(.remove ^js @keyboard-frame-listener))
|
||||
|
||||
(defn max-height-effect
|
||||
[{:keys [focused?]}
|
||||
{:keys [max-height]}
|
||||
{:keys [height saved-height last-height]}]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
;; Some subscriptions can arrive after the composer if focused (esp. link
|
||||
;; previews), so we need to react to changes in `max-height` outside of the
|
||||
;; `on-focus` handler
|
||||
(when @focused?
|
||||
(let [new-height (min max-height (reanimated/get-shared-value last-height))]
|
||||
(reanimated/set-shared-value last-height new-height)
|
||||
(reanimated/animate height new-height)
|
||||
(reanimated/set-shared-value saved-height new-height))))
|
||||
[max-height @focused?]))
|
||||
|
||||
(defn initialize
|
||||
[props state animations {:keys [max-height] :as dimensions}
|
||||
{:keys [chat-input images audio] :as subs}]
|
||||
(max-height-effect state dimensions animations)
|
||||
{:keys [chat-input audio] :as subs}]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(maximized-effect state animations dimensions chat-input)
|
||||
@@ -121,10 +93,9 @@
|
||||
(layout-effect state)
|
||||
(kb-default-height-effect state)
|
||||
(background-effect state animations dimensions chat-input)
|
||||
(images-effect props animations images)
|
||||
(link-preview-effect state)
|
||||
(audio-effect state animations audio)
|
||||
(empty-effect animations subs)
|
||||
(empty-effect state animations subs)
|
||||
(kb/add-kb-listeners props state animations dimensions)
|
||||
#(component-will-unmount props))
|
||||
[max-height]))
|
||||
@@ -185,6 +156,56 @@
|
||||
(rf/dispatch [:mention/on-change-text input-text])))
|
||||
[input-text]))
|
||||
|
||||
(defn link-previews
|
||||
[{:keys [sending-links?]}
|
||||
{:keys [text-value maximized?]}
|
||||
{:keys [height saved-height last-height]}
|
||||
{:keys [link-previews?]}]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(if-not @maximized?
|
||||
(let [value (if link-previews?
|
||||
constants/links-container-height
|
||||
(- constants/links-container-height))]
|
||||
(when (not= @sending-links? link-previews?)
|
||||
(reanimated/animate height (+ (reanimated/get-shared-value saved-height) value))
|
||||
(reanimated/set-shared-value saved-height
|
||||
(+ (reanimated/get-shared-value saved-height) value))
|
||||
(reanimated/set-shared-value last-height
|
||||
(+ (reanimated/get-shared-value last-height) value))))
|
||||
(let [curr-text @text-value]
|
||||
(reset! text-value (str @text-value " "))
|
||||
(js/setTimeout #(reset! text-value curr-text) 10)))
|
||||
(reset! sending-links? link-previews?))
|
||||
[link-previews?]))
|
||||
|
||||
(defn images
|
||||
[{:keys [sending-images? input-ref]}
|
||||
{:keys [text-value maximized?]}
|
||||
{:keys [container-opacity height saved-height last-height]}
|
||||
{:keys [images]}]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when images
|
||||
(reanimated/animate container-opacity 1))
|
||||
(when (and (not @sending-images?) (seq images) @input-ref)
|
||||
(.focus ^js @input-ref))
|
||||
(if-not @maximized?
|
||||
(let [value (if (seq images)
|
||||
constants/images-container-height
|
||||
(- constants/images-container-height))]
|
||||
(when (not= @sending-images? (boolean (seq images)))
|
||||
(reanimated/animate height (+ (reanimated/get-shared-value saved-height) value))
|
||||
(reanimated/set-shared-value saved-height
|
||||
(+ (reanimated/get-shared-value saved-height) value))
|
||||
(reanimated/set-shared-value last-height
|
||||
(+ (reanimated/get-shared-value last-height) value))))
|
||||
(let [curr-text @text-value]
|
||||
(reset! text-value (str @text-value " "))
|
||||
(js/setTimeout #(reset! text-value curr-text) 10)))
|
||||
(reset! sending-images? (boolean (seq images))))
|
||||
[(boolean (seq images))]))
|
||||
|
||||
(defn did-mount
|
||||
[{:keys [selectable-input-ref input-ref selection-manager]}]
|
||||
(rn/use-effect
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
[{:keys [input-ref] :as props}
|
||||
{:keys [gesture-enabled?] :as state}
|
||||
{:keys [height saved-height last-height opacity background-y container-opacity] :as animations}
|
||||
{:keys [images link-previews?]}
|
||||
{:keys [max-height lines] :as dimensions}
|
||||
keyboard-shown]
|
||||
(let [expanding? (atom true)
|
||||
@@ -80,7 +81,7 @@
|
||||
(gesture/on-update
|
||||
(fn [event]
|
||||
(let [translation (oops/oget event "translationY")
|
||||
min-height (utils/get-min-height lines)
|
||||
min-height (utils/get-min-height lines images link-previews?)
|
||||
new-height (- (reanimated/get-shared-value saved-height) translation)
|
||||
bounded-height (utils/bounded-val new-height min-height max-height)]
|
||||
(when keyboard-shown
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
(defn focus
|
||||
[{:keys [input-ref] :as props}
|
||||
{:keys [text-value focused? lock-selection? saved-cursor-position gradient-z-index]}
|
||||
{:keys [last-height opacity background-y gradient-opacity container-opacity] :as animations}
|
||||
{:keys [height saved-height last-height opacity background-y gradient-opacity container-opacity]
|
||||
:as animations}
|
||||
{:keys [max-height] :as dimensions}]
|
||||
(reset! focused? true)
|
||||
(rf/dispatch [:chat.ui/set-input-focused true])
|
||||
(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)
|
||||
@@ -40,8 +43,14 @@
|
||||
{:keys [images link-previews? reply]}]
|
||||
(when-not @recording?
|
||||
(let [lines (utils/calc-lines (- @content-height constants/extra-content-offset))
|
||||
min-height (utils/get-min-height lines)
|
||||
reopen-height (utils/calc-reopen-height text-value min-height content-height saved-height)]
|
||||
min-height (utils/get-min-height lines images link-previews?)
|
||||
reopen-height (utils/calc-reopen-height text-value
|
||||
min-height
|
||||
max-height
|
||||
content-height
|
||||
saved-height
|
||||
images
|
||||
link-previews?)]
|
||||
(reset! focused? false)
|
||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
||||
(reanimated/set-shared-value last-height reopen-height)
|
||||
@@ -63,16 +72,19 @@
|
||||
[event
|
||||
{:keys [maximized? lock-layout? text-value]}
|
||||
{:keys [height saved-height opacity background-y]}
|
||||
{:keys [images link-previews?]}
|
||||
{:keys [content-height window-height max-height]}
|
||||
keyboard-shown]
|
||||
(when keyboard-shown
|
||||
(let [event-size (oops/oget event "nativeEvent.contentSize.height")
|
||||
content-size (+ event-size constants/extra-content-offset)
|
||||
lines (utils/calc-lines event-size)
|
||||
content-size (if (or (= lines 1) (empty? @text-value))
|
||||
constants/input-height
|
||||
(if (= lines 2) constants/multiline-minimized-height content-size))
|
||||
new-height (utils/bounded-val content-size constants/input-height max-height)]
|
||||
(let [event-size (oops/oget event "nativeEvent.contentSize.height")
|
||||
content-size (+ event-size constants/extra-content-offset)
|
||||
lines (utils/calc-lines event-size)
|
||||
content-size (if (or (= lines 1) (empty? @text-value))
|
||||
constants/input-height
|
||||
(if (= lines 2) constants/multiline-minimized-height content-size))
|
||||
new-height (utils/bounded-val content-size constants/input-height max-height)
|
||||
bottom-content-height (utils/calc-bottom-content-height images link-previews?)
|
||||
new-height (min (+ new-height bottom-content-height) max-height)]
|
||||
(reset! content-height content-size)
|
||||
(when (utils/update-height? content-size height max-height maximized?)
|
||||
(reanimated/animate height new-height)
|
||||
@@ -87,7 +99,7 @@
|
||||
(when (= (reanimated/get-shared-value opacity) 1)
|
||||
(reanimated/animate opacity 0)
|
||||
(js/setTimeout #(reanimated/set-shared-value background-y (- window-height)) 300)))
|
||||
(rf/dispatch [:chat.ui/set-input-content-height new-height])
|
||||
(rf/dispatch [:chat.ui/set-input-content-height content-size])
|
||||
(reset! lock-layout? (> lines 2)))))
|
||||
|
||||
(defn scroll
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
(if (seq images) constants/images-container-height 0)))
|
||||
[images])
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style {:height height} {:margin-horizontal -20})}
|
||||
{:style (reanimated/apply-animations-to-style {:height height}
|
||||
{:margin-horizontal -20
|
||||
:z-index 1})}
|
||||
[gesture/flat-list
|
||||
{:key-fn first
|
||||
:render-fn image
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
(let [previews (rf/sub [:chats/link-previews-unfurled])
|
||||
height (use-animated-height (boolean (seq previews)))]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style {:height height} {})}
|
||||
{:style (reanimated/apply-animations-to-style {:height height} {:z-index 1})}
|
||||
[quo/url-preview-list
|
||||
{:key-fn :url
|
||||
:preview-width (- (:width (rn/get-window))
|
||||
|
||||
@@ -63,7 +63,9 @@
|
||||
|
||||
(defn input-text
|
||||
[{:keys [saved-emoji-kb-extra-height]}
|
||||
{:keys [focused? maximized?]}]
|
||||
{:keys [focused? maximized?]}
|
||||
{:keys [link-previews? images]}
|
||||
max-height]
|
||||
(merge typography/paragraph-1
|
||||
{:color (colors/theme-colors :black :white)
|
||||
:text-align-vertical :top
|
||||
@@ -71,6 +73,9 @@
|
||||
:top 0
|
||||
:left 0
|
||||
:right (when (or focused? platform/ios?) 0)
|
||||
:max-height (- max-height
|
||||
(if link-previews? constants/links-container-height 0)
|
||||
(if (seq images) constants/images-container-height 0))
|
||||
:padding-bottom (when @maximized? 0)}))
|
||||
(defn background
|
||||
[opacity background-y window-height]
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
(defn- f-shell-button
|
||||
[{:keys [maximized?]} {:keys [height]} {:keys [images link-previews? reply edit]}]
|
||||
(let [insets (safe-area/get-insets)
|
||||
extra-height (utils/calc-extra-content-height images link-previews? reply edit)
|
||||
extra-height (utils/calc-top-content-height reply edit)
|
||||
translate-y (reanimated/use-shared-value
|
||||
(utils/calc-shell-neg-y insets maximized? extra-height))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(let [extra-height (utils/calc-extra-content-height images link-previews? reply edit)]
|
||||
(let [extra-height (utils/calc-top-content-height reply edit)]
|
||||
(reanimated/animate translate-y
|
||||
(utils/calc-shell-neg-y insets maximized? extra-height))))
|
||||
[@maximized? images link-previews? reply edit])
|
||||
|
||||
@@ -14,16 +14,6 @@
|
||||
[val min-val max-val]
|
||||
(max min-val (min val max-val)))
|
||||
|
||||
(defn get-min-height
|
||||
[lines]
|
||||
(if (> lines 1) constants/multiline-minimized-height constants/input-height))
|
||||
|
||||
(defn calc-reopen-height
|
||||
[text-value min-height content-height saved-height]
|
||||
(if (empty? @text-value)
|
||||
min-height
|
||||
(Math/min @content-height (reanimated/get-shared-value saved-height))))
|
||||
|
||||
(defn update-height?
|
||||
[content-size height max-height maximized?]
|
||||
(when-not @maximized?
|
||||
@@ -64,23 +54,44 @@
|
||||
[height]
|
||||
(Math/floor (/ height constants/line-height)))
|
||||
|
||||
(defn calc-extra-content-height
|
||||
[images? link-previews? reply? edit?]
|
||||
(let [height (if images? constants/images-container-height 0)
|
||||
height (if link-previews? (+ height constants/links-container-height) height)
|
||||
height (if reply? (+ height constants/reply-container-height) height)
|
||||
(defn calc-top-content-height
|
||||
[reply? edit?]
|
||||
(let [height (if reply? constants/reply-container-height 0)
|
||||
height (if edit? (+ height constants/edit-container-height) height)]
|
||||
height))
|
||||
|
||||
(defn calc-bottom-content-height
|
||||
[images link-previews?]
|
||||
(let [height (if (seq images) constants/images-container-height 0)
|
||||
height (if link-previews? (+ height constants/links-container-height) height)]
|
||||
height))
|
||||
|
||||
(defn calc-reopen-height
|
||||
[text-value min-height max-height content-height saved-height images link-previews?]
|
||||
(if (empty? @text-value)
|
||||
min-height
|
||||
(let [bottom-content-height (calc-bottom-content-height images link-previews?)
|
||||
input-height (min (+ @content-height bottom-content-height)
|
||||
(reanimated/get-shared-value saved-height))]
|
||||
(min max-height input-height))))
|
||||
|
||||
(defn get-min-height
|
||||
[lines images link-previews?]
|
||||
(let [input-height (if (> lines 1)
|
||||
constants/multiline-minimized-height
|
||||
constants/input-height)
|
||||
bottom-content-height (calc-bottom-content-height images link-previews?)]
|
||||
(+ input-height bottom-content-height)))
|
||||
|
||||
(defn calc-max-height
|
||||
[{:keys [images link-previews? reply edit]} window-height kb-height insets]
|
||||
[{:keys [reply edit]} window-height kb-height insets]
|
||||
(let [margin-top (if platform/ios? (:top insets) (+ 10 (:top insets)))
|
||||
max-height (- window-height
|
||||
margin-top
|
||||
kb-height
|
||||
constants/bar-container-height
|
||||
constants/actions-container-height)
|
||||
max-height (- max-height (calc-extra-content-height images link-previews? reply edit))]
|
||||
max-height (- max-height (calc-top-content-height reply edit))]
|
||||
max-height))
|
||||
|
||||
(defn empty-input?
|
||||
@@ -114,7 +125,7 @@
|
||||
|
||||
(defn calc-shell-neg-y
|
||||
[insets maximized? extra-height]
|
||||
(let [padding 6
|
||||
(let [padding 12
|
||||
neg-y (if @maximized? -50 0)]
|
||||
(- (+ constants/bar-container-height
|
||||
constants/actions-container-height
|
||||
@@ -126,10 +137,10 @@
|
||||
(defn calc-suggestions-position
|
||||
[cursor-pos max-height size
|
||||
{:keys [maximized?]}
|
||||
{:keys [insets curr-height window-height keyboard-height images link-previews? reply edit]}]
|
||||
{:keys [insets curr-height window-height keyboard-height reply edit]}]
|
||||
(let [base (+ constants/composer-default-height (:bottom insets) 8)
|
||||
base (+ base (- curr-height constants/input-height))
|
||||
base (+ base (calc-extra-content-height images link-previews? reply edit))
|
||||
base (+ base (calc-top-content-height reply edit))
|
||||
view-height (- window-height keyboard-height (:top insets))
|
||||
container-height (bounded-val
|
||||
(* (/ constants/mentions-max-height 4) size)
|
||||
@@ -153,6 +164,7 @@
|
||||
:emoji-kb-extra-height (atom nil)
|
||||
:saved-emoji-kb-extra-height (atom nil)
|
||||
:sending-images? (atom false)
|
||||
:sending-links? (atom false)
|
||||
:record-reset-fn (atom nil)
|
||||
:scroll-y (atom 0)
|
||||
:selection-event (atom nil)
|
||||
@@ -191,9 +203,10 @@
|
||||
(defn init-animations
|
||||
[{:keys [input-text images link-previews? reply audio]}
|
||||
lines content-height max-height opacity background-y]
|
||||
(let [initial-height (if (> lines 1)
|
||||
constants/multiline-minimized-height
|
||||
constants/input-height)]
|
||||
(let [initial-height (if (> lines 1)
|
||||
constants/multiline-minimized-height
|
||||
constants/input-height)
|
||||
bottom-content-height 0]
|
||||
{:gradient-opacity (reanimated/use-shared-value 0)
|
||||
:container-opacity (reanimated/use-shared-value
|
||||
(if (empty-input?
|
||||
@@ -210,7 +223,7 @@
|
||||
initial-height)
|
||||
:last-height (reanimated/use-shared-value
|
||||
(bounded-val
|
||||
@content-height
|
||||
(+ @content-height bottom-content-height)
|
||||
constants/input-height
|
||||
max-height))
|
||||
:opacity opacity
|
||||
|
||||
@@ -62,6 +62,8 @@
|
||||
(effects/reply props animations subs)
|
||||
(effects/update-input-mention props state subs)
|
||||
(effects/edit-mentions props state subs)
|
||||
(effects/link-previews props state animations subs)
|
||||
(effects/images props state animations subs)
|
||||
[:<>
|
||||
[sub-view/shell-button state animations subs]
|
||||
[mentions/view props state animations max-height cursor-pos
|
||||
@@ -70,7 +72,7 @@
|
||||
(:reply subs)
|
||||
(:edit subs)]
|
||||
[gesture/gesture-detector
|
||||
{:gesture (drag-gesture/drag-gesture props state animations dimensions keyboard-shown)}
|
||||
{:gesture (drag-gesture/drag-gesture props state animations subs dimensions keyboard-shown)}
|
||||
[reanimated/view
|
||||
{:style (style/sheet-container insets state animations)
|
||||
:on-layout #(handler/layout % state blur-height)}
|
||||
@@ -94,6 +96,7 @@
|
||||
:on-content-size-change #(handler/content-size-change %
|
||||
state
|
||||
animations
|
||||
subs
|
||||
dimensions
|
||||
(or keyboard-shown (:edit subs)))
|
||||
:on-scroll #(handler/scroll % props state animations dimensions)
|
||||
@@ -106,12 +109,12 @@
|
||||
:multiline true
|
||||
:placeholder (i18n/label :t/type-something)
|
||||
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50)
|
||||
:style (style/input-text props state)
|
||||
:style (style/input-text props state subs max-height)
|
||||
:max-length constants/max-text-size
|
||||
:accessibility-label :chat-message-input}]]
|
||||
[gradients/view props state animations show-bottom-gradient?]]
|
||||
[link-preview/view]
|
||||
[images/images-list]
|
||||
[gradients/view props state animations show-bottom-gradient?]
|
||||
[link-preview/view]
|
||||
[images/images-list]]
|
||||
[actions/view props state animations window-height insets subs]]]]))
|
||||
|
||||
(defn composer
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
(ns status-im2.contexts.chat.events-test
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im2.contexts.chat.events :as chat]
|
||||
[status-im.chat.models.images :as images]
|
||||
[status-im.utils.clocks :as utils.clocks]))
|
||||
|
||||
(deftest clear-history-test
|
||||
@@ -90,8 +89,3 @@
|
||||
(testing "Pagination info should be reset on navigation"
|
||||
(let [res (chat/navigate-to-chat {:db db} chat-id)]
|
||||
(is (nil? (get-in res [:db :pagination-info chat-id :all-loaded?])))))))
|
||||
|
||||
(deftest camera-roll-loading-more-test
|
||||
(let [cofx {:db {:camera-roll/loading-more false}}]
|
||||
(is (= {:db {:camera-roll/loading-more true}}
|
||||
(images/camera-roll-loading-more cofx true)))))
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im2.contexts.chat.home.style :as style]
|
||||
[status-im2.common.contact-list.view :as contact-list]
|
||||
[status-im2.common.home.view :as common.home]
|
||||
@@ -106,6 +107,7 @@
|
||||
(let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests])
|
||||
selected-tab (or (rf/sub [:messages-home/selected-tab]) :tab/recent)
|
||||
{:keys [key-uid]} (rf/sub [:multiaccount])
|
||||
account (rf/sub [:profile/multiaccount])
|
||||
profile-color (:color (rf/sub [:onboarding-2/profile]))
|
||||
customization-color (if profile-color
|
||||
profile-color
|
||||
@@ -123,7 +125,9 @@
|
||||
:style style/blur}]
|
||||
[common.home/top-nav
|
||||
{:type :grey
|
||||
:avatar {:customization-color customization-color}}]
|
||||
:avatar {:customization-color customization-color
|
||||
:full-name (multiaccounts/displayed-name account)
|
||||
:profile-picture (multiaccounts/displayed-photo account)}}]
|
||||
[common.home/title-column
|
||||
{:label (i18n/label :t/messages)
|
||||
:handler #(rf/dispatch [:show-bottom-sheet {:content home.sheet/new-chat}])
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.navigation :as navigation]
|
||||
[react-native.orientation :as orientation]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.chat.lightbox.animations :as anim]
|
||||
@@ -83,27 +81,6 @@
|
||||
(when (and enabled? (not= result orientation/landscape-right))
|
||||
(handle-orientation result props state animations))))))))
|
||||
|
||||
(defn toggle-opacity
|
||||
[index {:keys [opacity-value border-value transparent? props]} portrait?]
|
||||
(let [{:keys [small-list-ref]} props
|
||||
opacity (reanimated/get-shared-value opacity-value)]
|
||||
(if (= opacity 1)
|
||||
(do
|
||||
(when platform/ios?
|
||||
;; status-bar issue: https://github.com/status-im/status-mobile/issues/15343
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible false}}) 75))
|
||||
(anim/animate opacity-value 0)
|
||||
(js/setTimeout #(reset! transparent? (not @transparent?)) 400))
|
||||
(do
|
||||
(reset! transparent? (not @transparent?))
|
||||
(js/setTimeout #(anim/animate opacity-value 1) 50)
|
||||
(js/setTimeout #(when @small-list-ref
|
||||
(.scrollToIndex ^js @small-list-ref #js {:animated false :index index}))
|
||||
100)
|
||||
(when (and platform/ios? portrait?)
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible true}}) 150))))
|
||||
(anim/animate border-value (if (= opacity 1) 0 12))))
|
||||
|
||||
(defn drag-gesture
|
||||
[{:keys [pan-x pan-y background-color opacity layout]} x? set-full-height?]
|
||||
(->
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
[message index _ {:keys [screen-width screen-height] :as args}]
|
||||
[rn/view
|
||||
{:style (style/image (+ screen-width constants/separator-width) screen-height)}
|
||||
[zoomable-image/zoomable-image message index args
|
||||
#(utils/toggle-opacity index args %)]
|
||||
[:f> zoomable-image/zoomable-image message index args]
|
||||
[rn/view {:style {:width constants/separator-width}}]])
|
||||
|
||||
(defn lightbox-content
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
(ns status-im2.contexts.chat.lightbox.zoomable-image.utils
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[react-native.navigation :as navigation]
|
||||
[react-native.orientation :as orientation]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.chat.lightbox.zoomable-image.constants :as c]
|
||||
[status-im2.contexts.chat.lightbox.animations :as anim]
|
||||
[utils.re-frame :as rf]))
|
||||
@@ -36,9 +39,9 @@
|
||||
exit?
|
||||
{:keys [x-threshold-scale y-threshold-scale]}
|
||||
{:keys [scale saved-scale] :as animations}
|
||||
{:keys [pan-x-enabled? pan-y-enabled?] :as props}]
|
||||
{:keys [pan-x-enabled? pan-y-enabled?] :as state}]
|
||||
(when (= value c/min-scale)
|
||||
(reset-values exit? animations props))
|
||||
(reset-values exit? animations state))
|
||||
(anim/animate scale value (if exit? 100 c/default-duration))
|
||||
(anim/set-val saved-scale value)
|
||||
(reset! pan-x-enabled? (> value x-threshold-scale))
|
||||
@@ -102,6 +105,27 @@
|
||||
(when (and (= zoom-out-signal index) (> scale c/min-scale))
|
||||
(rescale c/min-scale true)))
|
||||
|
||||
(defn toggle-opacity
|
||||
[index {:keys [opacity-value border-value transparent? props]} portrait?]
|
||||
(let [{:keys [small-list-ref]} props
|
||||
opacity (reanimated/get-shared-value opacity-value)]
|
||||
(if (= opacity 1)
|
||||
(do
|
||||
(when platform/ios?
|
||||
;; status-bar issue: https://github.com/status-im/status-mobile/issues/15343
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible false}}) 75))
|
||||
(anim/animate opacity-value 0)
|
||||
(js/setTimeout #(reset! transparent? (not @transparent?)) 400))
|
||||
(do
|
||||
(reset! transparent? (not @transparent?))
|
||||
(js/setTimeout #(anim/animate opacity-value 1) 50)
|
||||
(js/setTimeout #(when @small-list-ref
|
||||
(.scrollToIndex ^js @small-list-ref #js {:animated false :index index}))
|
||||
100)
|
||||
(when (and platform/ios? portrait?)
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible true}}) 150))))
|
||||
(anim/animate border-value (if (= opacity 1) 0 12))))
|
||||
|
||||
;;; Dimensions
|
||||
(defn get-dimensions
|
||||
"Calculates all required dimensions. Dimensions calculations are different on iOS and Android because landscape
|
||||
@@ -162,3 +186,28 @@
|
||||
(if (or (> focal max) (< focal min))
|
||||
(/ screen-size 2)
|
||||
focal)))
|
||||
|
||||
;;; INITIALIZATIONS
|
||||
(defn init-animations
|
||||
[]
|
||||
{:scale (anim/use-val c/min-scale)
|
||||
:saved-scale (anim/use-val c/min-scale)
|
||||
:pan-x-start (anim/use-val c/init-offset)
|
||||
:pan-x (anim/use-val c/init-offset)
|
||||
:pan-y-start (anim/use-val c/init-offset)
|
||||
:pan-y (anim/use-val c/init-offset)
|
||||
:pinch-x-start (anim/use-val c/init-offset)
|
||||
:pinch-x (anim/use-val c/init-offset)
|
||||
:pinch-y-start (anim/use-val c/init-offset)
|
||||
:pinch-y (anim/use-val c/init-offset)
|
||||
:pinch-x-max (anim/use-val js/Infinity)
|
||||
:pinch-y-max (anim/use-val js/Infinity)
|
||||
:rotate (anim/use-val c/init-rotation)
|
||||
:rotate-scale (anim/use-val c/min-scale)})
|
||||
|
||||
(defn init-state
|
||||
[]
|
||||
{:pan-x-enabled? (reagent/atom false)
|
||||
:pan-y-enabled? (reagent/atom false)
|
||||
:focal-x (reagent/atom nil)
|
||||
:focal-y (reagent/atom nil)})
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[utils.re-frame :as rf]
|
||||
[oops.core :refer [oget]]
|
||||
[react-native.orientation :as orientation]
|
||||
@@ -80,7 +79,7 @@
|
||||
[{:keys [width height screen-height screen-width x-threshold-scale y-threshold-scale] :as dimensions}
|
||||
{:keys [saved-scale scale pinch-x pinch-y pinch-x-start pinch-y-start pinch-x-max pinch-y-max]
|
||||
:as animations}
|
||||
{:keys [focal-x focal-y] :as props}
|
||||
{:keys [focal-x focal-y] :as state}
|
||||
rescale
|
||||
transparent?
|
||||
toggle-opacity]
|
||||
@@ -135,7 +134,7 @@
|
||||
(utils/center-x animations false))
|
||||
(when (< (anim/get-val scale) y-threshold-scale)
|
||||
(utils/center-y animations false))))
|
||||
(finalize-pinch dimensions animations props)))))
|
||||
(finalize-pinch dimensions animations state)))))
|
||||
|
||||
(defn pan-x-gesture
|
||||
[{:keys [width screen-width x-threshold-scale]}
|
||||
@@ -204,76 +203,61 @@
|
||||
(anim/animate-decay pan-y velocity [lower-bound upper-bound])
|
||||
(anim/animate-decay pan-y-start velocity [lower-bound upper-bound]))))))))
|
||||
|
||||
(defn- f-zoomable-image
|
||||
[dimensions animations state rescale curr-orientation content focused? index render-data]
|
||||
(let [{:keys [transparent? set-full-height?]} render-data
|
||||
portrait? (= curr-orientation orientation/portrait)
|
||||
on-tap #(utils/toggle-opacity index render-data portrait?)
|
||||
tap (tap-gesture on-tap)
|
||||
double-tap (double-tap-gesture dimensions animations rescale transparent? on-tap)
|
||||
pinch (pinch-gesture dimensions animations state rescale transparent? on-tap)
|
||||
pan-x (pan-x-gesture dimensions animations state rescale)
|
||||
pan-y (pan-y-gesture dimensions animations state rescale)
|
||||
composed-gestures (gesture/exclusive
|
||||
(gesture/simultaneous pinch pan-x pan-y)
|
||||
(gesture/exclusive double-tap tap))]
|
||||
[gesture/gesture-detector {:gesture composed-gestures}
|
||||
[reanimated/view
|
||||
{:style (style/container dimensions
|
||||
animations
|
||||
@set-full-height?
|
||||
(= curr-orientation orientation/portrait))}
|
||||
[reanimated/fast-image
|
||||
{:source {:uri (:image content)}
|
||||
:native-ID (when focused? :shared-element)
|
||||
:style (style/image dimensions animations (:border-value render-data))}]]]))
|
||||
|
||||
(defn zoomable-image
|
||||
[{:keys [image-width image-height content message-id]} index args on-tap]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [{:keys [transparent? set-full-height?]} args
|
||||
shared-element-id (rf/sub [:shared-element-id])
|
||||
exit-lightbox-signal (rf/sub [:lightbox/exit-signal])
|
||||
zoom-out-signal (rf/sub [:lightbox/zoom-out-signal])
|
||||
focused? (= shared-element-id message-id)
|
||||
curr-orientation (or (rf/sub [:lightbox/orientation])
|
||||
orientation/portrait)
|
||||
portrait? (= curr-orientation orientation/portrait)
|
||||
dimensions (utils/get-dimensions
|
||||
(or image-width c/default-dimension)
|
||||
(or image-height c/default-duration)
|
||||
curr-orientation
|
||||
args)
|
||||
animations {:scale (anim/use-val c/min-scale)
|
||||
:saved-scale (anim/use-val c/min-scale)
|
||||
:pan-x-start (anim/use-val c/init-offset)
|
||||
:pan-x (anim/use-val c/init-offset)
|
||||
:pan-y-start (anim/use-val c/init-offset)
|
||||
:pan-y (anim/use-val c/init-offset)
|
||||
:pinch-x-start (anim/use-val c/init-offset)
|
||||
:pinch-x (anim/use-val c/init-offset)
|
||||
:pinch-y-start (anim/use-val c/init-offset)
|
||||
:pinch-y (anim/use-val c/init-offset)
|
||||
:pinch-x-max (anim/use-val js/Infinity)
|
||||
:pinch-y-max (anim/use-val js/Infinity)
|
||||
:rotate (anim/use-val c/init-rotation)
|
||||
:rotate-scale (anim/use-val c/min-scale)}
|
||||
props {:pan-x-enabled? (reagent/atom false)
|
||||
:pan-y-enabled? (reagent/atom false)
|
||||
:focal-x (reagent/atom nil)
|
||||
:focal-y (reagent/atom nil)}
|
||||
rescale (fn [value exit?]
|
||||
(utils/rescale-image value
|
||||
exit?
|
||||
dimensions
|
||||
animations
|
||||
props))]
|
||||
(rn/use-effect (fn []
|
||||
(js/setTimeout #(reset! set-full-height? true) 500)))
|
||||
(when platform/ios?
|
||||
(utils/handle-orientation-change curr-orientation focused? dimensions animations props)
|
||||
(utils/handle-exit-lightbox-signal exit-lightbox-signal
|
||||
index
|
||||
(anim/get-val (:scale animations))
|
||||
rescale
|
||||
set-full-height?))
|
||||
(utils/handle-zoom-out-signal zoom-out-signal index (anim/get-val (:scale animations)) rescale)
|
||||
[:f>
|
||||
(fn []
|
||||
(let [tap (tap-gesture #(on-tap portrait?))
|
||||
double-tap
|
||||
(double-tap-gesture dimensions animations rescale transparent? #(on-tap portrait?))
|
||||
pinch
|
||||
(pinch-gesture dimensions animations props rescale transparent? #(on-tap portrait?))
|
||||
pan-x (pan-x-gesture dimensions animations props rescale)
|
||||
pan-y (pan-y-gesture dimensions animations props rescale)
|
||||
composed-gestures (gesture/exclusive
|
||||
(gesture/simultaneous pinch pan-x pan-y)
|
||||
(gesture/exclusive double-tap tap))]
|
||||
[gesture/gesture-detector {:gesture composed-gestures}
|
||||
[reanimated/view
|
||||
{:style (style/container dimensions
|
||||
animations
|
||||
@set-full-height?
|
||||
(= curr-orientation orientation/portrait))}
|
||||
[reanimated/fast-image
|
||||
{:source {:uri (:image content)}
|
||||
:native-ID (when focused? :shared-element)
|
||||
:style (style/image dimensions animations (:border-value args))}]]]))]))])
|
||||
[{:keys [image-width image-height content message-id]} index render-data]
|
||||
(let [state (utils/init-state)
|
||||
shared-element-id (rf/sub [:shared-element-id])
|
||||
exit-lightbox-signal (rf/sub [:lightbox/exit-signal])
|
||||
zoom-out-signal (rf/sub [:lightbox/zoom-out-signal])
|
||||
curr-orientation (or (rf/sub [:lightbox/orientation])
|
||||
orientation/portrait)
|
||||
{:keys [set-full-height?]} render-data
|
||||
focused? (= shared-element-id message-id)
|
||||
dimensions (utils/get-dimensions
|
||||
(or image-width c/default-dimension)
|
||||
(or image-height c/default-duration)
|
||||
curr-orientation
|
||||
render-data)
|
||||
animations (utils/init-animations)
|
||||
rescale (fn [value exit?]
|
||||
(utils/rescale-image value
|
||||
exit?
|
||||
dimensions
|
||||
animations
|
||||
state))]
|
||||
(rn/use-effect (fn []
|
||||
(js/setTimeout #(reset! set-full-height? true) 500)))
|
||||
(when platform/ios?
|
||||
(utils/handle-orientation-change curr-orientation focused? dimensions animations state)
|
||||
(utils/handle-exit-lightbox-signal exit-lightbox-signal
|
||||
index
|
||||
(anim/get-val (:scale animations))
|
||||
rescale
|
||||
set-full-height?))
|
||||
(utils/handle-zoom-out-signal zoom-out-signal index (anim/get-val (:scale animations)) rescale)
|
||||
[:f> f-zoomable-image dimensions animations state rescale curr-orientation content focused?
|
||||
index render-data]))
|
||||
|
||||
@@ -5,31 +5,69 @@
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.contexts.chat.messages.drawers.view :as drawers]))
|
||||
|
||||
(defn- on-press
|
||||
[own message-id emoji-id emoji-reaction-id]
|
||||
(if own
|
||||
(rf/dispatch [:models.reactions/send-emoji-reaction-retraction
|
||||
{:message-id message-id
|
||||
:emoji-id emoji-id
|
||||
:emoji-reaction-id emoji-reaction-id}])
|
||||
(rf/dispatch [:models.reactions/send-emoji-reaction
|
||||
{:message-id message-id
|
||||
:emoji-id emoji-id}])))
|
||||
|
||||
(defn- on-long-press
|
||||
[message-id emoji-id]
|
||||
(rf/dispatch [:chat.ui/emoji-reactions-by-message-id
|
||||
{:message-id message-id
|
||||
:long-pressed-emoji emoji-id}]))
|
||||
|
||||
(defn show-authors-sheet
|
||||
[user-message-content reactions]
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:on-close (fn []
|
||||
(rf/dispatch
|
||||
[:chat/clear-emoji-reaction-author-details]))
|
||||
:content (fn []
|
||||
[drawers/reaction-authors
|
||||
(map :emoji-id reactions)])
|
||||
:selected-item (fn []
|
||||
user-message-content)
|
||||
:padding-bottom-override 0}]))
|
||||
|
||||
(defn message-reactions-row
|
||||
[chat-id message-id]
|
||||
[{:keys [message-id chat-id]} user-message-content]
|
||||
(let [reactions (rf/sub [:chats/message-reactions message-id chat-id])]
|
||||
(when (seq reactions)
|
||||
[rn/view {:margin-left 52 :margin-bottom 12 :flex-direction :row}
|
||||
(for [{:keys [own emoji-id quantity emoji-reaction-id] :as emoji-reaction} reactions]
|
||||
^{:key (str emoji-reaction)}
|
||||
[rn/view {:style {:margin-right 6}}
|
||||
[quo/reaction
|
||||
{:emoji (get constants/reactions emoji-id)
|
||||
:neutral? own
|
||||
:clicks quantity
|
||||
:on-press (if own
|
||||
#(rf/dispatch [:models.reactions/send-emoji-reaction-retraction
|
||||
{:message-id message-id
|
||||
:emoji-id emoji-id
|
||||
:emoji-reaction-id emoji-reaction-id}])
|
||||
#(rf/dispatch [:models.reactions/send-emoji-reaction
|
||||
{:message-id message-id
|
||||
:emoji-id emoji-id}]))
|
||||
:accessibility-label (str "emoji-reaction-" emoji-id)}]])
|
||||
[quo/add-reaction
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [drawers/reactions
|
||||
{:chat-id chat-id :message-id message-id}])}]))}]])))
|
||||
[:<>
|
||||
(when (seq reactions)
|
||||
[rn/view
|
||||
{:style {:margin-left 52
|
||||
:margin-bottom 12
|
||||
:flex-direction :row}}
|
||||
(for [{:keys [own emoji-id quantity emoji-reaction-id]
|
||||
:as emoji-reaction} reactions]
|
||||
^{:key emoji-reaction}
|
||||
[rn/view {:style {:margin-right 6}}
|
||||
[quo/reaction
|
||||
{:emoji (get constants/reactions emoji-id)
|
||||
:neutral? own
|
||||
:clicks quantity
|
||||
:on-press #(on-press own message-id emoji-id emoji-reaction-id)
|
||||
:on-long-press (fn []
|
||||
(on-long-press message-id
|
||||
emoji-id)
|
||||
(show-authors-sheet user-message-content
|
||||
reactions))
|
||||
:accessibility-label (str "emoji-reaction-" emoji-id)}]])
|
||||
[quo/add-reaction
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [drawers/reactions
|
||||
{:chat-id chat-id
|
||||
:message-id message-id}])
|
||||
:selected-item (fn []
|
||||
user-message-content)}]))}]])]))
|
||||
|
||||
@@ -23,16 +23,18 @@
|
||||
[status-im2.common.not-implemented :as not-implemented]
|
||||
[utils.datetime :as datetime]
|
||||
[reagent.core :as reagent]
|
||||
[utils.address :as address]))
|
||||
[utils.address :as address]
|
||||
[react-native.gesture :as gesture]))
|
||||
|
||||
(def delivery-state-showing-time-ms 3000)
|
||||
|
||||
(defn avatar-container
|
||||
[{:keys [content last-in-group? pinned-by quoted-message from]}]
|
||||
[{:keys [content last-in-group? pinned-by quoted-message from]} message-reaction-view?]
|
||||
(if (or (and (seq (:response-to content))
|
||||
quoted-message)
|
||||
last-in-group?
|
||||
pinned-by)
|
||||
pinned-by
|
||||
message-reaction-view?)
|
||||
[avatar/avatar from :small]
|
||||
[rn/view {:padding-top 2 :width 32}]))
|
||||
|
||||
@@ -43,8 +45,9 @@
|
||||
pinned-by
|
||||
quoted-message
|
||||
from
|
||||
timestamp]}]
|
||||
(when (or (and (seq response-to) quoted-message) last-in-group? pinned-by)
|
||||
timestamp]}
|
||||
message-reaction-view?]
|
||||
(when (or (and (seq response-to) quoted-message) last-in-group? pinned-by message-reaction-view?)
|
||||
(let [[primary-name secondary-name] (rf/sub [:contacts/contact-two-names-by-identity from])
|
||||
{:keys [ens-verified added?]} (rf/sub [:contacts/contact-by-address from])]
|
||||
[quo/author
|
||||
@@ -82,7 +85,8 @@
|
||||
(let [show-delivery-state? (reagent/atom false)]
|
||||
(fn [{:keys [content-type quoted-message content outgoing outgoing-status] :as message-data}
|
||||
context
|
||||
keyboard-shown]
|
||||
keyboard-shown
|
||||
message-reaction-view?]
|
||||
(let [first-image (first (:album message-data))
|
||||
outgoing-status (if (= content-type constants/content-type-album)
|
||||
(:outgoing-status first-image)
|
||||
@@ -91,7 +95,8 @@
|
||||
(:outgoing first-image)
|
||||
outgoing)
|
||||
context (assoc context :on-long-press #(on-long-press message-data context))
|
||||
response-to (:response-to content)]
|
||||
response-to (:response-to content)
|
||||
height (rf/sub [:dimensions/window-height])]
|
||||
[rn/touchable-highlight
|
||||
{:accessibility-label (if (and outgoing (= outgoing-status :sending))
|
||||
:message-sending
|
||||
@@ -115,37 +120,41 @@
|
||||
[rn/view
|
||||
{:style {:padding-horizontal 12
|
||||
:flex-direction :row}}
|
||||
[avatar-container message-data]
|
||||
[rn/view
|
||||
{:style {:margin-left 8
|
||||
:flex 1}}
|
||||
[author message-data]
|
||||
(case content-type
|
||||
[avatar-container message-data message-reaction-view?]
|
||||
(into
|
||||
(if message-reaction-view?
|
||||
[gesture/scroll-view]
|
||||
[rn/view])
|
||||
[{:style {:margin-left 8
|
||||
:flex 1
|
||||
:max-height (when message-reaction-view? (* 0.4 height))}}
|
||||
[author message-data message-reaction-view?]
|
||||
(case content-type
|
||||
|
||||
constants/content-type-text [content.text/text-content message-data]
|
||||
constants/content-type-text [content.text/text-content message-data context]
|
||||
|
||||
constants/content-type-emoji
|
||||
[not-implemented/not-implemented [old-message/emoji message-data]]
|
||||
constants/content-type-emoji
|
||||
[not-implemented/not-implemented [old-message/emoji message-data]]
|
||||
|
||||
constants/content-type-sticker
|
||||
[not-implemented/not-implemented [old-message/sticker message-data]]
|
||||
constants/content-type-sticker
|
||||
[not-implemented/not-implemented [old-message/sticker message-data]]
|
||||
|
||||
constants/content-type-audio
|
||||
[audio/audio-message message-data context]
|
||||
constants/content-type-audio
|
||||
[audio/audio-message message-data context]
|
||||
|
||||
constants/content-type-image
|
||||
[image/image-message 0 message-data on-long-press]
|
||||
constants/content-type-image
|
||||
[image/image-message 0 message-data context on-long-press]
|
||||
|
||||
constants/content-type-album
|
||||
[album/album-message message-data context on-long-press]
|
||||
constants/content-type-album
|
||||
[album/album-message message-data context on-long-press]
|
||||
|
||||
[not-implemented/not-implemented [content.unknown/unknown-content message-data]])
|
||||
(when @show-delivery-state?
|
||||
[status/status outgoing-status])]]]]))))
|
||||
[not-implemented/not-implemented [content.unknown/unknown-content message-data]])
|
||||
(when @show-delivery-state?
|
||||
[status/status outgoing-status])])]]]))))
|
||||
|
||||
(defn message-with-reactions
|
||||
[{:keys [pinned-by mentioned in-pinned-view? content-type last-in-group? message-id] :as message-data}
|
||||
{:keys [chat-id] :as context}
|
||||
[{:keys [pinned-by mentioned in-pinned-view? content-type last-in-group?] :as message-data}
|
||||
context
|
||||
keyboard-shown]
|
||||
[rn/view
|
||||
{:style (style/message-container in-pinned-view? pinned-by mentioned last-in-group?)
|
||||
@@ -153,9 +162,10 @@
|
||||
(when pinned-by
|
||||
[pin/pinned-by-view pinned-by])
|
||||
(if (#{constants/content-type-system-text constants/content-type-community
|
||||
constants/content-type-system-pinned-message
|
||||
constants/content-type-contact-request}
|
||||
constants/content-type-contact-request
|
||||
constants/content-type-system-pinned-message}
|
||||
content-type)
|
||||
[system-message-content message-data]
|
||||
[user-message-content message-data context keyboard-shown])
|
||||
[reactions/message-reactions-row chat-id message-id]])
|
||||
[user-message-content message-data context keyboard-shown false])
|
||||
[reactions/message-reactions-row message-data
|
||||
[user-message-content message-data context keyboard-shown true]]])
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
(ns status-im2.contexts.chat.messages.drawers.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def tab
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center})
|
||||
|
||||
(def tab-icon {:margin-right 4})
|
||||
|
||||
(defn tab-count
|
||||
[active?]
|
||||
{:color (if (or active? (colors/dark?)) colors/white colors/neutral-100)})
|
||||
|
||||
(def tabs-container
|
||||
{:flex 1
|
||||
:align-self :stretch
|
||||
:padding-left 20
|
||||
:padding-right 8
|
||||
:margin-bottom 12})
|
||||
|
||||
(def authors-list
|
||||
{:height 320
|
||||
:flex 1})
|
||||
@@ -6,7 +6,72 @@
|
||||
[status-im2.common.not-implemented :as not-implemented]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.re-frame :as rf]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.contact-list-item.view :as contact-list-item]
|
||||
[status-im2.contexts.chat.messages.drawers.style :as style]
|
||||
[react-native.gesture :as gesture]))
|
||||
|
||||
(defn contact-list-item-fn
|
||||
[{:keys [from compressed-key]}]
|
||||
(let [[primary-name secondary-name] (rf/sub [:contacts/contact-two-names-by-identity
|
||||
from])
|
||||
{:keys [ens-verified added?]} (rf/sub [:contacts/contact-by-address from])]
|
||||
^{:key compressed-key}
|
||||
[contact-list-item/contact-list-item
|
||||
{:on-press #(rf/dispatch [:chat.ui/show-profile from])}
|
||||
{:primary-name primary-name
|
||||
:secondary-name secondary-name
|
||||
:public-key from
|
||||
:compressed-key compressed-key
|
||||
:ens-verified ens-verified
|
||||
:added? added?}]))
|
||||
|
||||
(defn get-tabs-data
|
||||
[reaction-authors selected-tab reactions-order]
|
||||
(map (fn [reaction-type-int]
|
||||
(let [author-details (get reaction-authors reaction-type-int)]
|
||||
{:id reaction-type-int
|
||||
:accessibility-label (keyword (str "authors-for-reaction-" reaction-type-int))
|
||||
:label [rn/view {:style style/tab}
|
||||
[quo/icon
|
||||
(get constants/reactions reaction-type-int)
|
||||
{:no-color true
|
||||
:container-style style/tab-icon}]
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:size :paragraph-1
|
||||
:style (style/tab-count (= selected-tab
|
||||
reaction-type-int))}
|
||||
(count author-details)]]}))
|
||||
reactions-order))
|
||||
|
||||
(defn reaction-authors-comp
|
||||
[selected-tab reaction-authors reactions-order]
|
||||
[:<>
|
||||
[rn/view style/tabs-container
|
||||
[quo/tabs
|
||||
{:size 32
|
||||
:scrollable? true
|
||||
:in-scroll-view? true
|
||||
:on-change #(reset! selected-tab %)
|
||||
:default-active @selected-tab
|
||||
:data (get-tabs-data reaction-authors @selected-tab reactions-order)}]]
|
||||
[gesture/flat-list
|
||||
{:data (for [contact (get reaction-authors @selected-tab)]
|
||||
contact)
|
||||
:render-fn contact-list-item-fn
|
||||
:key-fn :from
|
||||
:style style/authors-list}]])
|
||||
|
||||
(defn reaction-authors
|
||||
[reactions-order]
|
||||
(let [{:keys [reaction-authors-list
|
||||
selected-reaction]} (rf/sub [:chat/reactions-authors])
|
||||
selected-tab (reagent/atom (or selected-reaction
|
||||
(first (keys reaction-authors-list))))]
|
||||
(fn []
|
||||
[reaction-authors-comp selected-tab reaction-authors-list reactions-order])))
|
||||
|
||||
(defn pin-message
|
||||
[{:keys [chat-id pinned pinned-by] :as message-data}]
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
deleted-for-me? (i18n/label :t/message-deleted-for-you)
|
||||
(#{constants/content-type-text
|
||||
constants/content-type-image
|
||||
constants/content-type-sticker}
|
||||
constants/content-type-sticker
|
||||
constants/content-type-emoji}
|
||||
(:content-type pinned-message))
|
||||
(resolver/resolve-message latest-pin-text)
|
||||
:else latest-pin-text)]
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im2.contexts.chat.photo-selector.view :refer [album-title]]
|
||||
[status-im2.contexts.chat.photo-selector.album-selector.style :as style]))
|
||||
|
||||
(defn album
|
||||
[{:keys [title count uri]} index _ selected-album]
|
||||
(defn render-album
|
||||
[{:keys [title count uri]} index _ {:keys [album? selected-album]}]
|
||||
(let [selected? (= selected-album title)]
|
||||
[rn/touchable-opacity
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:chat.ui/camera-roll-select-album title])
|
||||
(rf/dispatch [:navigate-back]))
|
||||
(rf/dispatch [:photo-selector/get-photos-for-selected-album])
|
||||
(reset! album? false))
|
||||
:style (style/album-container selected?)
|
||||
:accessibility-label (str "album-" index)}
|
||||
[rn/image
|
||||
@@ -31,7 +31,7 @@
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
(str count " " (i18n/label :t/images))]]
|
||||
(when count (str count " " (i18n/label :t/images)))]]
|
||||
(when selected?
|
||||
[rn/view
|
||||
{:style {:position :absolute
|
||||
@@ -39,9 +39,11 @@
|
||||
[quo/icon :i/check
|
||||
{:size 20 :color (colors/theme-colors colors/primary-50 colors/primary-60)}]])]))
|
||||
|
||||
(def no-title "no-title")
|
||||
|
||||
(defn section-header
|
||||
[{:keys [title]}]
|
||||
(when (not= title "smart-albums")
|
||||
(when-not (= title no-title)
|
||||
[quo/divider-label
|
||||
{:label title
|
||||
:container-style style/divider}]))
|
||||
@@ -51,22 +53,19 @@
|
||||
(str (:title item) index))
|
||||
|
||||
(defn album-selector
|
||||
[{:keys [on-scroll]}]
|
||||
(rf/dispatch [:chat.ui/camera-roll-get-albums])
|
||||
(fn [{:keys [scroll-enabled]}]
|
||||
(let [albums (rf/sub [:camera-roll/albums])
|
||||
selected-album (or (rf/sub [:camera-roll/selected-album]) (i18n/label :t/recent))]
|
||||
[rn/view {:style {:padding-top 20}}
|
||||
[album-title false]
|
||||
[gesture/section-list
|
||||
{:data albums
|
||||
:render-fn album
|
||||
:render-data selected-album
|
||||
:sections albums
|
||||
:sticky-section-headers-enabled false
|
||||
:render-section-header-fn section-header
|
||||
:style {:margin-top 12}
|
||||
:content-container-style {:padding-bottom 40}
|
||||
:key-fn key-fn
|
||||
:scroll-enabled @scroll-enabled
|
||||
:on-scroll on-scroll}]])))
|
||||
[{:keys [scroll-enabled on-scroll]} album? selected-album]
|
||||
(let [albums (rf/sub [:camera-roll/albums])
|
||||
albums-sections [{:title no-title :data (:smart-albums albums)}
|
||||
{:title (i18n/label :t/my-albums) :data (:my-albums albums)}]]
|
||||
[gesture/section-list
|
||||
{:data albums-sections
|
||||
:sections albums-sections
|
||||
:render-data {:album? album? :selected-album selected-album}
|
||||
:render-fn render-album
|
||||
:sticky-section-headers-enabled false
|
||||
:render-section-header-fn section-header
|
||||
:content-container-style {:padding-top 64
|
||||
:padding-bottom 40}
|
||||
:key-fn key-fn
|
||||
:scroll-enabled @scroll-enabled
|
||||
:on-scroll on-scroll}]))
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
(ns status-im2.contexts.chat.photo-selector.events
|
||||
(:require [react-native.cameraroll :as cameraroll]
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[utils.i18n :as i18n]
|
||||
[react-native.permissions :as permissions]
|
||||
[status-im2.config :as config]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.utils.image-processing :as image-processing]
|
||||
[taoensso.timbre :as log]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def maximum-image-size-px 2000)
|
||||
|
||||
(defn- resize-photo
|
||||
[uri callback]
|
||||
(rn/image-get-size
|
||||
uri
|
||||
(fn [width height]
|
||||
(let [resize? (> (max width height) maximum-image-size-px)]
|
||||
(image-processing/resize
|
||||
uri
|
||||
(if resize? maximum-image-size-px width)
|
||||
(if resize? maximum-image-size-px height)
|
||||
60
|
||||
(fn [^js resized-image]
|
||||
(let [path (.-path resized-image)
|
||||
path (if (string/starts-with? path "file") path (str "file://" path))]
|
||||
(callback {:resized-uri path
|
||||
:width width
|
||||
:height height})))
|
||||
#(log/error "could not resize image" %))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:camera-roll-request-permissions-and-get-photos
|
||||
(fn [[num end-cursor album]]
|
||||
(permissions/request-permissions
|
||||
{:permissions [:read-external-storage]
|
||||
:on-allowed
|
||||
(fn []
|
||||
(cameraroll/get-photos
|
||||
(merge {:first num
|
||||
:assetType "Photos"
|
||||
:groupTypes (if (= album (i18n/label :t/recent)) "All" "Albums")
|
||||
:groupName (when (not= album (i18n/label :t/recent)) album)
|
||||
:include ["imageSize"]}
|
||||
(when end-cursor
|
||||
{:after end-cursor}))
|
||||
#(re-frame/dispatch [:on-camera-roll-get-photos (:edges %) (:page_info %) end-cursor])))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:camera-roll-image-selected
|
||||
(fn [[image chat-id]]
|
||||
(resize-photo (:uri image) #(re-frame/dispatch [:photo-selector/image-selected chat-id image %]))))
|
||||
|
||||
(defn get-albums
|
||||
[callback]
|
||||
(let [albums (atom {:smart-albums []
|
||||
:my-albums []})]
|
||||
;; Get the "recent" album first
|
||||
(cameraroll/get-photos
|
||||
{:first 1 :groupTypes "All"}
|
||||
(fn [res-recent]
|
||||
(swap! albums assoc
|
||||
:smart-albums
|
||||
[{:title (i18n/label :t/recent)
|
||||
:uri (get-in (first (:edges res-recent)) [:node :image :uri])}])
|
||||
;; Get albums, then loop over albums and get each one's cover (first photo)
|
||||
(cameraroll/get-albums
|
||||
{:assetType "All"}
|
||||
(fn [res-albums]
|
||||
(let [response-count (count res-albums)]
|
||||
(if (pos? response-count)
|
||||
(doseq [album res-albums]
|
||||
(cameraroll/get-photos
|
||||
{:first 1 :groupTypes "Albums" :groupName (:title album)}
|
||||
(fn [res]
|
||||
(let [uri (get-in (first (:edges res)) [:node :image :uri])]
|
||||
(swap! albums update :my-albums conj (merge album {:uri uri}))
|
||||
(when (= (count (:my-albums @albums)) response-count)
|
||||
(swap! albums update :my-albums #(sort-by :title %))
|
||||
(callback @albums))))))
|
||||
(callback @albums)))))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:camera-roll-get-albums
|
||||
(fn []
|
||||
(get-albums #(re-frame/dispatch [:on-camera-roll-get-albums %]))))
|
||||
|
||||
(rf/defn on-camera-roll-get-albums
|
||||
{:events [:on-camera-roll-get-albums]}
|
||||
[{:keys [db]} albums]
|
||||
{:db (assoc db :camera-roll/albums albums)})
|
||||
|
||||
(rf/defn camera-roll-get-albums
|
||||
{:events [:photo-selector/camera-roll-get-albums]}
|
||||
[_]
|
||||
{:camera-roll-get-albums nil})
|
||||
|
||||
(rf/defn camera-roll-select-album
|
||||
{:events [:chat.ui/camera-roll-select-album]}
|
||||
[{:keys [db]} album]
|
||||
{:db (assoc db :camera-roll/selected-album album)})
|
||||
|
||||
(rf/defn image-selected
|
||||
{:events [:photo-selector/image-selected]}
|
||||
[{:keys [db]} current-chat-id original {:keys [resized-uri width height]}]
|
||||
{:db
|
||||
(update-in db
|
||||
[:chat/inputs current-chat-id :metadata :sending-image (:uri original)]
|
||||
merge
|
||||
original
|
||||
{:resized-uri resized-uri
|
||||
:width width
|
||||
:height height})})
|
||||
|
||||
(rf/defn on-camera-roll-get-photos
|
||||
{:events [:on-camera-roll-get-photos]}
|
||||
[{:keys [db]} photos page-info end-cursor]
|
||||
(let [photos_x (when end-cursor (:camera-roll/photos db))]
|
||||
{:db (-> db
|
||||
(assoc :camera-roll/photos (concat photos_x (map #(get-in % [:node :image]) photos)))
|
||||
(assoc :camera-roll/end-cursor (:end_cursor page-info))
|
||||
(assoc :camera-roll/has-next-page (:has_next_page page-info))
|
||||
(assoc :camera-roll/loading-more false))}))
|
||||
|
||||
(rf/defn get-photos-for-selected-album
|
||||
{:events [:photo-selector/get-photos-for-selected-album]}
|
||||
[{:keys [db]} end-cursor]
|
||||
{:camera-roll-request-permissions-and-get-photos [21 end-cursor
|
||||
(or (:camera-roll/selected-album db)
|
||||
(i18n/label :t/recent))]})
|
||||
|
||||
(rf/defn camera-roll-loading-more
|
||||
{:events [:photo-selector/camera-roll-loading-more]}
|
||||
[{:keys [db]} is-loading]
|
||||
{:db (assoc db :camera-roll/loading-more is-loading)})
|
||||
|
||||
(rf/defn camera-roll-pick
|
||||
{:events [:photo-selector/camera-roll-pick]}
|
||||
[{:keys [db]} image chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
|
||||
(when (and (< (count images) config/max-images-batch)
|
||||
(not (some #(= (:uri image) (:uri %)) images)))
|
||||
{:camera-roll-image-selected [image current-chat-id]})))
|
||||
@@ -15,18 +15,13 @@
|
||||
:flex-direction :row
|
||||
:left 0
|
||||
:right 0
|
||||
:top 0
|
||||
:top 20
|
||||
:justify-content :center
|
||||
:z-index 1})
|
||||
|
||||
(defn clear-container
|
||||
[]
|
||||
{:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:padding-horizontal 12
|
||||
:padding-vertical 5
|
||||
:border-radius 10
|
||||
:position :absolute
|
||||
:right 20})
|
||||
(def clear-container
|
||||
{:position :absolute
|
||||
:right 20})
|
||||
|
||||
(defn close-button-container
|
||||
[]
|
||||
|
||||
@@ -1,150 +1,126 @@
|
||||
(ns status-im2.contexts.chat.photo-selector.view
|
||||
(:require
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[react-native.platform :as platform]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[quo2.components.notifications.info-count :as info-count]
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.linear-gradient :as linear-gradient]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.chat.photo-selector.style :as style]
|
||||
[status-im.utils.core :as utils]
|
||||
[quo.react]
|
||||
[utils.re-frame :as rf]))
|
||||
[status-im2.contexts.chat.photo-selector.album-selector.view :as album-selector]
|
||||
utils.collection))
|
||||
|
||||
(defn show-toast
|
||||
[]
|
||||
(rf/dispatch [:toasts/upsert
|
||||
{:id :random-id
|
||||
:icon :info
|
||||
:icon-color colors/danger-50-opa-40
|
||||
:container-style {:top (when platform/ios? 20)}
|
||||
:text (i18n/label :t/only-6-images)}]))
|
||||
|
||||
(defn on-press-confirm-selection
|
||||
[selected]
|
||||
[selected close]
|
||||
(rf/dispatch [:chat.ui/clear-sending-images])
|
||||
(doseq [item @selected]
|
||||
(rf/dispatch [:chat.ui/camera-roll-pick item]))
|
||||
(reset! selected [])
|
||||
(rf/dispatch [:navigate-back]))
|
||||
(doseq [item selected]
|
||||
(rf/dispatch [:photo-selector/camera-roll-pick item]))
|
||||
(close))
|
||||
|
||||
(defn bottom-gradient
|
||||
[selected-images insets selected]
|
||||
(when (or (seq @selected) (seq selected-images))
|
||||
(defn confirm-button
|
||||
[selected-images sending-image close]
|
||||
(when (not= selected-images sending-image)
|
||||
[linear-gradient/linear-gradient
|
||||
{:colors [:black :transparent]
|
||||
:start {:x 0 :y 1}
|
||||
:end {:x 0 :y 0}
|
||||
:style (style/gradient-container (:bottom insets))}
|
||||
:style (style/gradient-container (safe-area/get-bottom))}
|
||||
[quo/button
|
||||
{:style {:align-self :stretch
|
||||
:margin-horizontal 20
|
||||
:margin-top 12}
|
||||
:on-press #(on-press-confirm-selection selected)
|
||||
:on-press #(on-press-confirm-selection selected-images close)
|
||||
:accessibility-label :confirm-selection}
|
||||
(i18n/label :t/confirm-selection)]]))
|
||||
|
||||
(defn clear-button
|
||||
[selected]
|
||||
(when (seq @selected)
|
||||
[rn/touchable-opacity
|
||||
{:on-press #(reset! selected [])
|
||||
:style (style/clear-container)
|
||||
:accessibility-label :clear}
|
||||
[quo/text {:weight :medium} (i18n/label :t/clear)]]))
|
||||
[album? selected]
|
||||
(when (and (not album?) (seq @selected))
|
||||
[rn/view {:style style/clear-container}
|
||||
[quo/button
|
||||
{:type :grey
|
||||
:size 32
|
||||
:accessibility-label :clear
|
||||
:on-press #(reset! selected [])}
|
||||
(i18n/label :t/clear)]]))
|
||||
|
||||
(defn remove-selected
|
||||
[coll item]
|
||||
(vec (remove #(= (:uri item) (:uri %)) coll)))
|
||||
|
||||
(defn image
|
||||
(defn render-image
|
||||
[item index _ {:keys [window-width selected]}]
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:on-press (fn []
|
||||
(if (some #(= (:uri item) (:uri %)) @selected)
|
||||
(swap! selected remove-selected item)
|
||||
(if (>= (count @selected) constants/max-album-photos)
|
||||
(rf/dispatch [:toasts/upsert
|
||||
{:id :random-id
|
||||
:icon :info
|
||||
:icon-color colors/danger-50-opa-40
|
||||
:container-style {:top (when platform/ios? 20)}
|
||||
:text (i18n/label :t/only-6-images)}])
|
||||
(swap! selected conj item))))
|
||||
:accessibility-label (str "image-" index)}
|
||||
[rn/image
|
||||
{:source {:uri (:uri item)}
|
||||
:style (style/image window-width index)}]
|
||||
(when (some #(= (:uri item) (:uri %)) @selected)
|
||||
[rn/view {:style (style/overlay window-width)}])
|
||||
(when (some #(= (:uri item) (:uri %)) @selected)
|
||||
[info-count/info-count
|
||||
{:style style/image-count
|
||||
:accessibility-label (str "count-" index)}
|
||||
(inc (utils/first-index #(= (:uri item) (:uri %)) @selected))])])
|
||||
|
||||
(defn album-title
|
||||
[photos? selected temporary-selected insets close]
|
||||
(fn []
|
||||
(let [selected-album (or (rf/sub [:camera-roll/selected-album]) (i18n/label :t/recent))]
|
||||
[rn/touchable-opacity
|
||||
{:style (style/title-container)
|
||||
:active-opacity 1
|
||||
:accessibility-label :album-title
|
||||
:on-press (fn []
|
||||
;; TODO: album-selector issue:
|
||||
;; https://github.com/status-im/status-mobile/issues/15398
|
||||
(if photos?
|
||||
(do
|
||||
(reset! temporary-selected @selected)
|
||||
(rf/dispatch [:open-modal :album-selector {:insets insets}]))
|
||||
(close)))}
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:style {:max-width 150}}
|
||||
selected-album]
|
||||
[rn/view {:style (style/chevron-container)}
|
||||
[quo/icon (if photos? :i/chevron-down :i/chevron-up)
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]])))
|
||||
(let [item-selected? (some #(= (:uri item) (:uri %)) @selected)]
|
||||
[rn/touchable-opacity
|
||||
{:on-press (fn []
|
||||
(if item-selected?
|
||||
(swap! selected remove-selected item)
|
||||
(if (>= (count @selected) constants/max-album-photos)
|
||||
(show-toast)
|
||||
(swap! selected conj item))))
|
||||
:accessibility-label (str "image-" index)}
|
||||
[rn/image
|
||||
{:source {:uri (:uri item)}
|
||||
:style (style/image window-width index)}]
|
||||
(when item-selected?
|
||||
[:<>
|
||||
[rn/view {:style (style/overlay window-width)}]
|
||||
[info-count/info-count
|
||||
{:style style/image-count
|
||||
:accessibility-label (str "count-" index)}
|
||||
(inc (utils.collection/first-index #(= (:uri item) (:uri %)) @selected))]])]))
|
||||
|
||||
(defn photo-selector
|
||||
[{:keys [scroll-enabled on-scroll close]}]
|
||||
[:f>
|
||||
(let [temporary-selected (reagent/atom [])
|
||||
{:keys [insets]} (rf/sub [:get-screen-params])] ; used when switching albums
|
||||
(fn []
|
||||
(let [selected (reagent/atom []) ; currently selected
|
||||
selected-images (rf/sub [:chats/sending-image])
|
||||
selected-album (or (rf/sub [:camera-roll/selected-album]) (i18n/label :t/recent))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(rf/dispatch [:chat.ui/camera-roll-get-photos 20 nil selected-album])
|
||||
(if (seq selected-images)
|
||||
(reset! selected (vec (vals selected-images)))
|
||||
(reset! selected @temporary-selected)))
|
||||
[selected-album])
|
||||
[:f>
|
||||
(fn []
|
||||
(let [window-width (:width (rn/get-window))
|
||||
camera-roll-photos (rf/sub [:camera-roll/photos])
|
||||
end-cursor (rf/sub [:camera-roll/end-cursor])
|
||||
loading? (rf/sub [:camera-roll/loading-more])
|
||||
has-next-page? (rf/sub [:camera-roll/has-next-page])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[rn/view
|
||||
{:style style/buttons-container}
|
||||
[album-title true selected temporary-selected insets close]
|
||||
[clear-button selected]]
|
||||
[gesture/flat-list
|
||||
{:key-fn identity
|
||||
:render-fn image
|
||||
:render-data {:window-width window-width :selected selected}
|
||||
:data camera-roll-photos
|
||||
:num-columns 3
|
||||
:content-container-style {:width "100%"
|
||||
:padding-bottom (+ (:bottom insets) 100)
|
||||
:padding-top 64}
|
||||
:on-scroll on-scroll
|
||||
:scroll-enabled @scroll-enabled
|
||||
:on-end-reached #(rf/dispatch [:camera-roll/on-end-reached end-cursor
|
||||
selected-album loading?
|
||||
has-next-page?])}]
|
||||
[bottom-gradient selected-images insets selected]]))])))])
|
||||
[{:keys [scroll-enabled on-scroll close] :as sheet}]
|
||||
(rf/dispatch [:photo-selector/get-photos-for-selected-album])
|
||||
(rf/dispatch [:photo-selector/camera-roll-get-albums])
|
||||
(let [album? (reagent/atom false)
|
||||
sending-image (into [] (vals (rf/sub [:chats/sending-image])))
|
||||
selected-images (reagent/atom sending-image)
|
||||
window-width (:width (rn/get-window))]
|
||||
(fn []
|
||||
(let [camera-roll-photos (rf/sub [:camera-roll/photos])
|
||||
end-cursor (rf/sub [:camera-roll/end-cursor])
|
||||
loading? (rf/sub [:camera-roll/loading-more])
|
||||
has-next-page? (rf/sub [:camera-roll/has-next-page])
|
||||
selected-album (or (rf/sub [:camera-roll/selected-album]) (i18n/label :t/recent))]
|
||||
[rn/view {:style {:flex 1 :margin-top -20}}
|
||||
[rn/view {:style style/buttons-container}
|
||||
[quo/dropdown {:type :grey :size 32 :on-change #(swap! album? not) :selected @album?}
|
||||
selected-album]
|
||||
[clear-button @album? selected-images]]
|
||||
(if @album?
|
||||
[album-selector/album-selector sheet album? selected-album]
|
||||
[:<>
|
||||
[gesture/flat-list
|
||||
{:key-fn identity
|
||||
:render-fn render-image
|
||||
:render-data {:window-width window-width :selected selected-images}
|
||||
:data camera-roll-photos
|
||||
:num-columns 3
|
||||
:content-container-style {:width "100%"
|
||||
:padding-bottom (+ (safe-area/get-bottom) 100)
|
||||
:padding-top 64}
|
||||
:on-scroll on-scroll
|
||||
:scroll-enabled @scroll-enabled
|
||||
:on-end-reached (fn []
|
||||
(when (and (not loading?) has-next-page?)
|
||||
(rf/dispatch [:photo-selector/camera-roll-loading-more true])
|
||||
(rf/dispatch [:photo-selector/get-photos-for-selected-album
|
||||
end-cursor])))}]
|
||||
[confirm-button @selected-images sending-image close]])]))))
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
[react-native.safe-area :as safe-area]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im2.common.home.view :as common.home]
|
||||
[status-im2.contexts.communities.actions.community-options.view :as options]
|
||||
[status-im2.contexts.communities.home.style :as style]
|
||||
@@ -54,6 +55,7 @@
|
||||
(let [selected-tab (or (rf/sub [:communities/selected-tab]) :joined)
|
||||
{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status])
|
||||
{:keys [key-uid]} (rf/sub [:multiaccount])
|
||||
account (rf/sub [:profile/multiaccount])
|
||||
profile-color (:color (rf/sub [:onboarding-2/profile]))
|
||||
customization-color (if profile-color
|
||||
profile-color
|
||||
@@ -80,7 +82,9 @@
|
||||
:style style/blur}]
|
||||
[common.home/top-nav
|
||||
{:type :grey
|
||||
:avatar {:customization-color customization-color}}]
|
||||
:avatar {:customization-color customization-color
|
||||
:full-name (multiaccounts/displayed-name account)
|
||||
:profile-picture (multiaccounts/displayed-photo account)}}]
|
||||
[common.home/title-column
|
||||
{:label (i18n/label :t/communities)
|
||||
:handler #(rf/dispatch [:show-bottom-sheet {:content actions.home-plus/view}])
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
(ns status-im2.contexts.onboarding.enter-seed-phrase.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(def full-layout {:flex 1})
|
||||
|
||||
(def page-container
|
||||
{:position :absolute
|
||||
@@ -9,6 +12,27 @@
|
||||
:right 0
|
||||
:background-color colors/neutral-80-opa-80-blur})
|
||||
|
||||
(def form-container
|
||||
{:flex 1
|
||||
:padding-horizontal 20
|
||||
:padding-vertical 12})
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:justify-content :space-between})
|
||||
|
||||
(def word-count-container
|
||||
{:justify-content :flex-end
|
||||
:margin-bottom 2})
|
||||
|
||||
(def input-container
|
||||
{:height 120
|
||||
:margin-top 12
|
||||
:margin-horizontal -20})
|
||||
|
||||
(defn continue-button
|
||||
[keyboard-shown?]
|
||||
{:margin-top :auto
|
||||
:margin-bottom (when-not keyboard-shown? (safe-area/get-bottom))})
|
||||
|
||||
(def keyboard-container {:margin-top :auto})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im2.contexts.onboarding.enter-seed-phrase.view
|
||||
(:require [clojure.string :as string]
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
@@ -13,57 +14,152 @@
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(def button-disabled?
|
||||
(comp not constants/seed-phrase-valid-length mnemonic/words-count))
|
||||
(def ^:private max-seed-phrase-length
|
||||
(apply max constants/seed-phrase-valid-length))
|
||||
|
||||
(defn clean-seed-phrase
|
||||
[s]
|
||||
(as-> s $
|
||||
(string/lower-case $)
|
||||
(string/split $ #"\s")
|
||||
(filter #(not (string/blank? %)) $)
|
||||
(string/join " " $)))
|
||||
(defn- partial-word-in-dictionary?
|
||||
[partial-word]
|
||||
(some #(string/starts-with? % partial-word) mnemonic/dictionary))
|
||||
|
||||
(defn page
|
||||
[{:keys [navigation-bar-top]}]
|
||||
(let [seed-phrase (reagent/atom "")
|
||||
error-message (reagent/atom "")
|
||||
on-invalid-seed-phrase #(reset! error-message (i18n/label :t/custom-seed-phrase))]
|
||||
(fn []
|
||||
[rn/view {:style style/page-container}
|
||||
[navigation-bar/navigation-bar {:top navigation-bar-top}]
|
||||
[rn/view {:style {:padding-horizontal 20}}
|
||||
[quo/text
|
||||
{:weight :bold
|
||||
:align :center}
|
||||
(i18n/label :t/use-recovery-phrase)]
|
||||
[quo/text
|
||||
(i18n/label-pluralize (mnemonic/words-count @seed-phrase) :t/words-n)]
|
||||
[rn/view {:style style/input-container}
|
||||
[quo/recovery-phrase-input
|
||||
{:on-change-text (fn [t]
|
||||
(reset! seed-phrase (clean-seed-phrase t))
|
||||
(reset! error-message ""))
|
||||
:mark-errors? true
|
||||
:error-pred (constantly false)
|
||||
:word-limit 24
|
||||
:auto-focus true
|
||||
:accessibility-label :passphrase-input
|
||||
:placeholder (i18n/label :t/seed-phrase-placeholder)
|
||||
:auto-correct false}
|
||||
@seed-phrase]]
|
||||
[quo/button
|
||||
{:disabled (button-disabled? @seed-phrase)
|
||||
:on-press #(rf/dispatch [:onboarding-2/seed-phrase-entered
|
||||
(security/mask-data @seed-phrase)
|
||||
on-invalid-seed-phrase])}
|
||||
(i18n/label :t/continue)]
|
||||
(when (seq @error-message)
|
||||
[quo/text @error-message])]])))
|
||||
(defn- word-in-dictionary?
|
||||
[word]
|
||||
(some #(= % word) mnemonic/dictionary))
|
||||
|
||||
(def ^:private partial-word-not-in-dictionary?
|
||||
(comp not partial-word-in-dictionary?))
|
||||
|
||||
(def ^:private word-not-in-dictionary?
|
||||
(comp not word-in-dictionary?))
|
||||
|
||||
(defn- header
|
||||
[seed-phrase-count]
|
||||
[rn/view {:style style/header-container}
|
||||
[quo/text {:weight :semi-bold :size :heading-1}
|
||||
(i18n/label :t/use-recovery-phrase)]
|
||||
[rn/view {:style style/word-count-container}
|
||||
[quo/text
|
||||
{:style {:color colors/white-opa-40}
|
||||
:weight :regular
|
||||
:size :paragraph-2}
|
||||
(i18n/label-pluralize seed-phrase-count :t/words-n)]]])
|
||||
|
||||
(defn- clean-seed-phrase
|
||||
[seed-phrase]
|
||||
(-> seed-phrase
|
||||
(string/lower-case)
|
||||
(string/replace #", " " ")
|
||||
(string/replace #"," " ")
|
||||
(string/replace #"\s+" " ")
|
||||
(string/trim)))
|
||||
|
||||
(defn- recovery-form
|
||||
[{:keys [seed-phrase word-count error-state? all-words-valid? on-change-seed-phrase
|
||||
keyboard-shown? on-submit]}]
|
||||
(let [button-disabled? (or error-state?
|
||||
(not (constants/seed-phrase-valid-length word-count))
|
||||
(not all-words-valid?))]
|
||||
[rn/view {:style style/form-container}
|
||||
[header word-count]
|
||||
[rn/view {:style style/input-container}
|
||||
[quo/recovery-phrase-input
|
||||
{:accessibility-label :passphrase-input
|
||||
:placeholder (i18n/label :t/seed-phrase-placeholder)
|
||||
:auto-capitalize :none
|
||||
:auto-correct false
|
||||
:auto-focus true
|
||||
:mark-errors? true
|
||||
:word-limit max-seed-phrase-length
|
||||
:error-pred-current-word partial-word-not-in-dictionary?
|
||||
:error-pred-written-words word-not-in-dictionary?
|
||||
:on-change-text on-change-seed-phrase}
|
||||
seed-phrase]]
|
||||
[quo/button
|
||||
{:style (style/continue-button keyboard-shown?)
|
||||
:disabled button-disabled?
|
||||
:on-press on-submit}
|
||||
(i18n/label :t/continue)]]))
|
||||
|
||||
(defn keyboard-suggestions
|
||||
[current-word]
|
||||
(->> mnemonic/dictionary
|
||||
(filter #(string/starts-with? % current-word))
|
||||
(take 7)))
|
||||
|
||||
(defn screen
|
||||
[]
|
||||
(reagent/with-let [keyboard-shown? (reagent/atom false)
|
||||
keyboard-show-listener (.addListener rn/keyboard
|
||||
"keyboardDidShow"
|
||||
#(reset! keyboard-shown? true))
|
||||
keyboard-hide-listener (.addListener rn/keyboard
|
||||
"keyboardDidHide"
|
||||
#(reset! keyboard-shown? false))
|
||||
invalid-seed-phrase? (reagent/atom false)
|
||||
set-invalid-seed-phrase #(reset! invalid-seed-phrase? true)
|
||||
seed-phrase (reagent/atom "")
|
||||
on-change-seed-phrase (fn [new-phrase]
|
||||
(when @invalid-seed-phrase?
|
||||
(reset! invalid-seed-phrase? false))
|
||||
(reset! seed-phrase new-phrase))
|
||||
on-submit (fn []
|
||||
(swap! seed-phrase clean-seed-phrase)
|
||||
(rf/dispatch [:onboarding-2/seed-phrase-entered
|
||||
(security/mask-data @seed-phrase)
|
||||
set-invalid-seed-phrase]))]
|
||||
(let [words-coll (mnemonic/passphrase->words @seed-phrase)
|
||||
last-word (peek words-coll)
|
||||
pick-suggested-word (fn [pressed-word]
|
||||
(swap! seed-phrase str (subs pressed-word (count last-word)) " "))
|
||||
;; Last word doesn't exist in dictionary while being written by the user, so
|
||||
;; it's validated checking whether is a substring of a dictionary word or not.
|
||||
last-partial-word-valid? (partial-word-in-dictionary? last-word)
|
||||
last-word-valid? (word-in-dictionary? last-word)
|
||||
butlast-words-valid? (every? word-in-dictionary? (butlast words-coll))
|
||||
all-words-valid? (and butlast-words-valid? last-word-valid?)
|
||||
word-count (mnemonic/words-count @seed-phrase)
|
||||
words-exceeded? (> word-count max-seed-phrase-length)
|
||||
error-in-words? (or (not last-partial-word-valid?)
|
||||
(not butlast-words-valid?))
|
||||
upper-case? (boolean (re-find #"[A-Z]" @seed-phrase))
|
||||
suggestions-state (cond
|
||||
(or error-in-words?
|
||||
words-exceeded?
|
||||
@invalid-seed-phrase?) :error
|
||||
(string/blank? @seed-phrase) :info
|
||||
(string/ends-with? @seed-phrase " ") :empty
|
||||
:else :words)
|
||||
suggestions-text (cond
|
||||
upper-case? (i18n/label :t/seed-phrase-words-uppercase)
|
||||
words-exceeded? (i18n/label :t/seed-phrase-words-exceeded)
|
||||
error-in-words? (i18n/label :t/seed-phrase-error)
|
||||
@invalid-seed-phrase? (i18n/label :t/seed-phrase-invalid)
|
||||
:else (i18n/label :t/seed-phrase-info))]
|
||||
[:<>
|
||||
[recovery-form
|
||||
{:seed-phrase @seed-phrase
|
||||
:error-state? (= suggestions-state :error)
|
||||
:all-words-valid? all-words-valid?
|
||||
:on-change-seed-phrase on-change-seed-phrase
|
||||
:word-count word-count
|
||||
:on-submit on-submit
|
||||
:keyboard-shown? @keyboard-shown?}]
|
||||
(when @keyboard-shown?
|
||||
[rn/view {:style style/keyboard-container}
|
||||
[quo/predictive-keyboard
|
||||
{:type suggestions-state
|
||||
:blur? true
|
||||
:text suggestions-text
|
||||
:words (keyboard-suggestions last-word)
|
||||
:on-press pick-suggested-word}]])])
|
||||
(finally
|
||||
(.remove keyboard-show-listener)
|
||||
(.remove keyboard-hide-listener))))
|
||||
|
||||
(defn enter-seed-phrase
|
||||
[]
|
||||
(let [{:keys [top]} (safe-area/get-insets)]
|
||||
[rn/view {:style {:flex 1}}
|
||||
(let [{navigation-bar-top :top} (safe-area/get-insets)]
|
||||
[rn/view {:style style/full-layout}
|
||||
[background/view true]
|
||||
[page {:navigation-bar-top top}]]))
|
||||
[rn/keyboard-avoiding-view {:style style/page-container}
|
||||
[navigation-bar/navigation-bar {:top navigation-bar-top}]
|
||||
[screen]]]))
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
(defn try-again-button
|
||||
[profile-color]
|
||||
[quo/button
|
||||
{:on-press #(rf/dispatch [:navigate-back])
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:syncing/clear-states])
|
||||
(rf/dispatch [:navigate-back]))
|
||||
:accessibility-label :try-again-later-button
|
||||
:override-background-color (colors/custom-color profile-color 60)
|
||||
:style style/try-again-button}
|
||||
@@ -39,7 +41,7 @@
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [pairing-status (rf/sub [:pairing/pairing-in-progress])
|
||||
(let [pairing-status (rf/sub [:pairing/pairing-status])
|
||||
profile-color (:color (rf/sub [:onboarding-2/profile]))]
|
||||
[rn/view {:style style/page-container}
|
||||
[background/view true]
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
[{:label "Muted?"
|
||||
:key :muted?
|
||||
:type :boolean}
|
||||
{:label "Mark as read disabled?"
|
||||
:key :mark-as-read-disabled?
|
||||
:type :boolean}
|
||||
{:label "Show red options?"
|
||||
:key :show-red-options?
|
||||
:type :boolean}])
|
||||
@@ -31,9 +34,10 @@
|
||||
[[{:icon :i/friend
|
||||
:label "View channel members and details"
|
||||
:on-press #(js/alert "View channel members and details")}
|
||||
{:icon :i/communities
|
||||
:label "Mark as read"
|
||||
:on-press #(js/alert "Mark as read")}
|
||||
{:icon :i/communities
|
||||
:label "Mark as read"
|
||||
:disabled? (:mark-as-read-disabled? @state)
|
||||
:on-press #(js/alert "Mark as read")}
|
||||
{:icon :i/muted
|
||||
:label (if (:muted? @state) "Unmute channel" "Mute channel")
|
||||
:on-press #(js/alert (if (:muted? @state) "Unmute channel" "Mute channel"))
|
||||
|
||||
@@ -1,76 +1,78 @@
|
||||
(ns status-im2.contexts.quo-preview.dropdowns.dropdown
|
||||
(:require [quo2.components.dropdowns.dropdown :as quo2]
|
||||
[quo2.foundations.colors :as colors]
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||
[status-im2.contexts.quo-preview.preview :as preview]
|
||||
[quo2.core :as quo]))
|
||||
|
||||
(def descriptor
|
||||
[{:label "Icon"
|
||||
:key :icon
|
||||
[{:label "Type:"
|
||||
:key :type
|
||||
:type :select
|
||||
:options [{:key :main-icons/placeholder
|
||||
:value "Placeholder"}
|
||||
{:key :main-icons/locked
|
||||
:value "Wallet"}]}
|
||||
{:label "Disabled"
|
||||
:key :disabled?
|
||||
:type :boolean}
|
||||
{:label "Default item"
|
||||
:key :default-item
|
||||
:type :text}
|
||||
{:label "Use border?"
|
||||
:key :use-border?
|
||||
:type :boolean}
|
||||
{:label "Border color"
|
||||
:key :border-color
|
||||
:type :select
|
||||
:options (map
|
||||
(fn [c]
|
||||
{:key c
|
||||
:value c})
|
||||
(keys colors/customization))}
|
||||
{:label "DD color"
|
||||
:key :dd-color
|
||||
:type :text}
|
||||
{:label "Size"
|
||||
:options [{:key :primary
|
||||
:value "Primary"}
|
||||
{:key :secondary
|
||||
:value "Secondary"}
|
||||
{:key :grey
|
||||
:value "Grey"}
|
||||
{:key :dark-grey
|
||||
:value "Dark Grey"}
|
||||
{:key :outline
|
||||
:value "Outline"}
|
||||
{:key :ghost
|
||||
:value "Ghost"}
|
||||
{:key :danger
|
||||
:value "Danger"}
|
||||
{:key :positive
|
||||
:value "Positive"}]}
|
||||
{:label "Size:"
|
||||
:key :size
|
||||
:type :select
|
||||
:options [{:key :big
|
||||
:value "big"}
|
||||
{:key :medium
|
||||
:value "medium"}
|
||||
{:key :small
|
||||
:value "small"}]}])
|
||||
:options [{:key 56
|
||||
:value "56"}
|
||||
{:key 40
|
||||
:value "40"}
|
||||
{:key 32
|
||||
:value "32"}
|
||||
{:key 24
|
||||
:value "24"}]}
|
||||
{:label "Icon:"
|
||||
:key :icon
|
||||
:type :boolean}
|
||||
{:label "Before icon:"
|
||||
:key :before
|
||||
:type :boolean}
|
||||
{:label "Disabled:"
|
||||
:key :disabled
|
||||
:type :boolean}
|
||||
{:label "Label"
|
||||
:key :label
|
||||
:type :text}])
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [items ["Banana"
|
||||
"Apple"
|
||||
"COVID +18"
|
||||
"Orange"
|
||||
"Kryptonite"
|
||||
"BMW"
|
||||
"Meh"]
|
||||
state (reagent/atom {:icon :main-icons/placeholder
|
||||
:default-item "item1"
|
||||
:use-border? false
|
||||
:dd-color (colors/custom-color :purple 50)
|
||||
:size :big})
|
||||
selected-item (reagent/cursor state [:default-item])
|
||||
on-select #(reset! selected-item %)]
|
||||
(let [state (reagent/atom {:label "Press Me"
|
||||
:size 40})
|
||||
label (reagent/cursor state [:label])
|
||||
before (reagent/cursor state [:before])
|
||||
icon (reagent/cursor state [:icon])]
|
||||
(fn []
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view
|
||||
{:padding-vertical 60
|
||||
:align-items :center}
|
||||
[rn/text (str "Selected item: " @selected-item)]
|
||||
[quo2/dropdown
|
||||
(merge @state
|
||||
{:on-select on-select
|
||||
:items items})]]]])))
|
||||
:flex-direction :row
|
||||
:justify-content :center}
|
||||
[quo/dropdown
|
||||
(merge (dissoc @state
|
||||
:theme
|
||||
:before
|
||||
:after)
|
||||
{:on-press #(println "Hello world!")}
|
||||
(when @before
|
||||
{:before :i/placeholder}))
|
||||
(if @icon :i/placeholder @label)]]]])))
|
||||
|
||||
(defn preview-dropdown
|
||||
[]
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
(case stack-id
|
||||
:communities-stack [communities/home]
|
||||
:chats-stack [chat/home]
|
||||
:wallet-stack [wallet.accounts/accounts-overview]
|
||||
:wallet-stack [wallet.accounts/accounts-overview-old]
|
||||
:browser-stack [browser.stack/browser-stack])])
|
||||
|
||||
(defn lazy-screen
|
||||
|
||||
@@ -129,8 +129,7 @@
|
||||
:label (i18n/label :t/jump-to)
|
||||
:customization-color customization-color}}
|
||||
{:position :absolute
|
||||
:bottom (+ (shell.constants/bottom-tabs-container-height) 7)} ;; bottom offset is 12 = 7 +
|
||||
;; 5(padding on button)
|
||||
:bottom (+ (shell.constants/bottom-tabs-container-height) 12)}
|
||||
(:home-stack-opacity shared-values)]]))
|
||||
|
||||
(defn shell-stack
|
||||
|
||||
@@ -10,20 +10,16 @@
|
||||
[status-im.utils.platform :as utils.platform]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(rf/defn local-pairing-completed
|
||||
{:events [:syncing/pairing-completed]}
|
||||
[{:keys [db]}]
|
||||
(let [receiver? (= (get-in db [:syncing :role]) constants/local-pairing-role-receiver)]
|
||||
(merge
|
||||
{:db (dissoc db :syncing)}
|
||||
(when receiver?
|
||||
{:dispatch [:init-root :syncing-results]}))))
|
||||
|
||||
(rf/defn local-pairing-update-role
|
||||
{:events [:syncing/update-role]}
|
||||
[{:keys [db]} role]
|
||||
{:db (assoc-in db [:syncing :role] role)})
|
||||
|
||||
(rf/defn local-pairing-clear-states
|
||||
{:events [:syncing/clear-states]}
|
||||
[{:keys [db]} role]
|
||||
{:db (dissoc db :syncing)})
|
||||
|
||||
(defn- get-default-node-config
|
||||
[installation-id]
|
||||
(let [db {:networks/current-network config/default-network
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
(i18n/label (:label value))]))
|
||||
|
||||
(defn render-item
|
||||
[i item]
|
||||
[i list-item]
|
||||
^{:key i}
|
||||
[rn/view
|
||||
{:margin-vertical 6
|
||||
:style style/numbered-list-item}
|
||||
@@ -60,7 +61,10 @@
|
||||
{:size :label
|
||||
:weight :medium
|
||||
:style style/list-icon-text} i]]
|
||||
(map #(render-element %) item)])
|
||||
(map-indexed (fn [idx item]
|
||||
^{:key idx}
|
||||
[render-element item])
|
||||
list-item)])
|
||||
|
||||
(defn render-list
|
||||
[{:keys [title image list]}]
|
||||
|
||||
@@ -201,19 +201,23 @@
|
||||
:text (i18n/label
|
||||
:t/camera-permission-denied)}])}]))]
|
||||
(fn []
|
||||
(let [camera-ref (atom nil)
|
||||
read-qr-once? (atom false)
|
||||
on-read-code (fn [data]
|
||||
(when-not @read-qr-once?
|
||||
(reset! read-qr-once? true)
|
||||
(js/setTimeout (fn []
|
||||
(reset! read-qr-once? false))
|
||||
3000)
|
||||
(check-qr-code-data data)))
|
||||
scan-qr-code-tab? (= @active-tab 1)
|
||||
show-camera? (and scan-qr-code-tab? @camera-permission-granted?)
|
||||
show-holes? (and show-camera?
|
||||
(boolean (not-empty @qr-view-finder)))]
|
||||
(let [camera-ref (atom nil)
|
||||
read-qr-once? (atom false)
|
||||
;; The below check is to prevent scanning of any QR code
|
||||
;; when the user is in syncing progress screen
|
||||
user-in-syncing-progress-screen? (= (rf/sub [:view-id]) :syncing-progress)
|
||||
on-read-code (fn [data]
|
||||
(when (and (not @read-qr-once?)
|
||||
(not user-in-syncing-progress-screen?))
|
||||
(reset! read-qr-once? true)
|
||||
(js/setTimeout (fn []
|
||||
(reset! read-qr-once? false))
|
||||
3000)
|
||||
(check-qr-code-data data)))
|
||||
scan-qr-code-tab? (= @active-tab 1)
|
||||
show-camera? (and scan-qr-code-tab? @camera-permission-granted?)
|
||||
show-holes? (and show-camera?
|
||||
(boolean (not-empty @qr-view-finder)))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when-not @camera-permission-granted?
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
(ns status-im2.contexts.syncing.setup-syncing.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn container-main
|
||||
[top]
|
||||
(def container-main
|
||||
{:background-color colors/neutral-95
|
||||
:flex 1
|
||||
:padding-top top})
|
||||
:flex 1})
|
||||
|
||||
(def page-container
|
||||
{:margin-horizontal 20})
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[react-native.hooks :as hooks]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im2.contexts.syncing.utils :as sync-utils]))
|
||||
|
||||
(def code-valid-for-ms 120000)
|
||||
@@ -32,7 +31,8 @@
|
||||
:label (i18n/label :t/how-to-scan)
|
||||
:icon :i/info
|
||||
:icon-override-theme :dark
|
||||
:on-press #(rf/dispatch [:open-modal :how-to-pair])}]}]])
|
||||
:on-press #(rf/dispatch [:open-modal :how-to-pair
|
||||
{:override-theme :dark}])}]}]])
|
||||
|
||||
(defn f-use-interval
|
||||
[clock cleanup-clock delay]
|
||||
@@ -66,7 +66,7 @@
|
||||
(reset! valid-for-ms code-valid-for-ms))]
|
||||
|
||||
(fn []
|
||||
[rn/view {:style (style/container-main (safe-area/get-top))}
|
||||
[rn/view {:style style/container-main}
|
||||
[:f> f-use-interval clock cleanup-clock @delay]
|
||||
[rn/scroll-view {}
|
||||
[navigation-bar]
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
status-im2.contexts.syncing.events
|
||||
status-im2.contexts.chat.events
|
||||
status-im2.common.password-authentication.events
|
||||
status-im2.contexts.communities.overview.events))
|
||||
status-im2.contexts.communities.overview.events
|
||||
status-im2.contexts.chat.photo-selector.events))
|
||||
|
||||
(re-frame/reg-cofx
|
||||
:now
|
||||
|
||||
@@ -90,6 +90,12 @@
|
||||
:animations {:showModal {:alpha {:from 1 :to 1 :duration 300}}
|
||||
:dismissModal {:alpha {:from 1 :to 1 :duration 300}}}})
|
||||
|
||||
(def dark-screen
|
||||
(merge (statusbar true)
|
||||
{:layout {:componentBackgroundColor colors/neutral-95
|
||||
:orientation ["portrait"]
|
||||
:backgroundColor colors/neutral-95}}))
|
||||
|
||||
(def lightbox
|
||||
{:topBar {:visible false}
|
||||
:statusBar {:backgroundColor :transparent
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[status-im2.contexts.add-new-contact.views :as add-new-contact]
|
||||
[status-im2.contexts.chat.lightbox.view :as lightbox]
|
||||
[status-im2.contexts.chat.messages.view :as chat]
|
||||
[status-im2.contexts.chat.photo-selector.album-selector.view :as album-selector]
|
||||
[status-im2.contexts.chat.photo-selector.view :as photo-selector]
|
||||
[status-im2.contexts.communities.discover.view :as communities.discover]
|
||||
[status-im2.contexts.communities.overview.view :as communities.overview]
|
||||
@@ -53,7 +52,8 @@
|
||||
:component shell/shell-stack}
|
||||
|
||||
{:name :chat
|
||||
:options {:insets {:top? true}}
|
||||
:options {:insets {:top? true}
|
||||
:popGesture false}
|
||||
:component chat/chat}
|
||||
|
||||
{:name :start-a-new-chat
|
||||
@@ -76,10 +76,6 @@
|
||||
:options {:sheet? true}
|
||||
:component photo-selector/photo-selector}
|
||||
|
||||
{:name :album-selector
|
||||
:options {:sheet? true}
|
||||
:component album-selector/album-selector}
|
||||
|
||||
{:name :new-contact
|
||||
:options {:sheet? true}
|
||||
:component add-new-contact/new-contact}
|
||||
@@ -95,12 +91,11 @@
|
||||
:component communities.overview/overview}
|
||||
|
||||
{:name :settings-syncing
|
||||
:options (options/statusbar true)
|
||||
:options (merge options/dark-screen {:insets {:top? true}})
|
||||
:component settings-syncing/view}
|
||||
|
||||
{:name :settings-setup-syncing
|
||||
:options (options/statusbar true)
|
||||
|
||||
:options (merge options/dark-screen {:insets {:top? true}})
|
||||
:component settings-setup-syncing/view}
|
||||
|
||||
;; Onboarding
|
||||
@@ -160,7 +155,8 @@
|
||||
:component sign-in/view}
|
||||
|
||||
{:name :syncing-progress
|
||||
:options {:layout options/onboarding-layout}
|
||||
:options {:layout options/onboarding-layout
|
||||
:popGesture false}
|
||||
:component syncing-devices/view}
|
||||
|
||||
{:name :syncing-results
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
(fn [multiaccount] (:installation-name multiaccount)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:pairing/pairing-in-progress
|
||||
:pairing/pairing-status
|
||||
:<- [:syncing]
|
||||
(fn [syncing]
|
||||
(:pairing-in-progress? syncing)))
|
||||
(:pairing-status syncing)))
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
(reg-root-key-sub :chat/inputs-with-mentions :chat/inputs-with-mentions)
|
||||
(reg-root-key-sub :chats-home-list :chats-home-list)
|
||||
(reg-root-key-sub :chats/recording? :chats/recording?)
|
||||
(reg-root-key-sub :chat/reactions-authors :chat/reactions-authors)
|
||||
|
||||
;;lightbox
|
||||
(reg-root-key-sub :lightbox/exit-signal :lightbox/exit-signal)
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
(fn [[prices {:keys [code]} {:keys [symbol]}]]
|
||||
[(name symbol)
|
||||
code
|
||||
(get-in prices [symbol (keyword code) :price])]))
|
||||
(get-in prices [symbol (keyword code)])]))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:signing/priority-fee-suggestions-range
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
:price
|
||||
:<- [:prices]
|
||||
(fn [prices [_ fsym tsym]]
|
||||
(get-in prices [fsym tsym :price])))
|
||||
(get-in prices [fsym tsym])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:last-day
|
||||
@@ -62,7 +62,7 @@
|
||||
(defn get-balance-total-value
|
||||
[balance prices currency token->decimals]
|
||||
(reduce-kv (fn [acc symbol value]
|
||||
(if-let [price (get-in prices [symbol currency :price])]
|
||||
(if-let [price (get-in prices [symbol currency])]
|
||||
(+ acc
|
||||
(or (some-> (money/internal->formatted value symbol (token->decimals symbol))
|
||||
^js (money/crypto->fiat price)
|
||||
@@ -194,7 +194,8 @@
|
||||
(defn update-value
|
||||
[prices currency]
|
||||
(fn [{:keys [symbol decimals amount] :as token}]
|
||||
(let [price (get-in prices [symbol (-> currency :code keyword) :price])]
|
||||
(let [currency-kw (-> currency :code keyword)
|
||||
price (get-in prices [symbol currency-kw])]
|
||||
(assoc token
|
||||
:price price
|
||||
:value (when (and amount price)
|
||||
|
||||
@@ -32,12 +32,8 @@
|
||||
:max-block 10}}})
|
||||
|
||||
(def prices
|
||||
{:ETH {:USD {:from "ETH"
|
||||
:to "USD"
|
||||
:price 1282.23}}
|
||||
:SNT {:USD {:from "SNT"
|
||||
:to "USD"
|
||||
:price 0.0232}}})
|
||||
{:ETH {:USD 1282.23}
|
||||
:SNT {:USD 0.0232}})
|
||||
|
||||
(def tokens
|
||||
{"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
||||
@@ -76,9 +72,9 @@
|
||||
{:ETH (money/bignumber 1000000000000000000)
|
||||
:SNT (money/bignumber 100000000000000000000)
|
||||
:AST (money/bignumber 10000)}
|
||||
{:ETH {:USD {:from "ETH" :to "USD" :price 677.91 :last-day 658.688}}
|
||||
:SNT {:USD {:from "SNT" :to "USD" :price 0.1562 :last-day 0.15}}
|
||||
:AST {:USD {:from "AST" :to "USD" :price 4 :last-day 3}}}
|
||||
{:ETH {:USD 677.91}
|
||||
:SNT {:USD 0.1562}
|
||||
:AST {:USD 4}}
|
||||
:USD
|
||||
{:ETH 18
|
||||
:SNT 18
|
||||
|
||||
@@ -207,3 +207,7 @@
|
||||
(defn was-called-times
|
||||
[^js mock number-of-times]
|
||||
(.toHaveBeenCalledTimes (js/expect mock) number-of-times))
|
||||
|
||||
(defn was-not-called
|
||||
[mock]
|
||||
(was-called-times mock 0))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.151.15",
|
||||
"commit-sha1": "75864528b130f60f9fdd58d1d404b02bab5e1a3c",
|
||||
"src-sha256": "1mr26p154187jsgi0jgwakfxgnqnhzy8g0fxb118pzy3d9ax5awh"
|
||||
"version": "v0.154.1",
|
||||
"commit-sha1": "1c51df20e002e9b93203a9128882d60deed6f47a",
|
||||
"src-sha256": "0mhznnkdx7946mkz8bzlsv70iv2aynirh9dsm5cyanjjap123qgv"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user