Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f357e67529 | ||
|
|
a7b27a0ef3 | ||
|
|
88aeb0ccfb | ||
|
|
b54d8ad5f3 | ||
|
|
7666026764 | ||
|
|
963745ecb6 | ||
|
|
c689b81e01 | ||
|
|
b0f0e29706 | ||
|
|
b035c4621b | ||
|
|
10eb4330e6 | ||
|
|
c5d472ab9b | ||
|
|
1ae8dae700 | ||
|
|
35fb6354c6 | ||
|
|
f9eae4ef82 | ||
|
|
68f360bbf6 | ||
|
|
30f0b436e9 | ||
|
|
80bab6d338 | ||
|
|
2438af4e00 | ||
|
|
fd7797322f | ||
|
|
cd69d0423a | ||
|
|
2fe4956f4d | ||
|
|
1e4a49fafe | ||
|
|
faa29a2946 | ||
|
|
5bf58bbaf8 | ||
|
|
4a42d20b0c | ||
|
|
e676b3dab4 |
@@ -7,6 +7,7 @@
|
||||
|
||||
# Xcode
|
||||
#
|
||||
/ios/.xcode.env.local
|
||||
/component-spec
|
||||
result/
|
||||
build/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
agent { label 'linux' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
agent { label params.AGENT_LABEL }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
agent { label 'macos' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
agent { label 'linux' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.9'
|
||||
library 'status-jenkins-lib@v1.7.2'
|
||||
|
||||
pipeline {
|
||||
agent {
|
||||
|
||||
@@ -36,6 +36,50 @@ Pay special attention to:
|
||||
|
||||
## Dos and don'ts
|
||||
|
||||
### Hiccup
|
||||
|
||||
Never use anonymous inline function in hiccup, this will lead to reinitialization of component on each render of parent component
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
(defn checkbox-view
|
||||
[{:keys [size]}]
|
||||
[rn/view
|
||||
[(fn [] [rn/view])]])
|
||||
|
||||
;; good
|
||||
(defn comp []
|
||||
[rn/view])
|
||||
|
||||
(defn checkbox-view
|
||||
[{:keys [size]}]
|
||||
[rn/view
|
||||
[comp]])
|
||||
```
|
||||
|
||||
This mistake mostly happens with functional components
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
(fn []
|
||||
(let [atom (rf/sub [:sub])]
|
||||
(fn []
|
||||
[:f>
|
||||
(fn []
|
||||
[rn/text atom]
|
||||
|
||||
;; good
|
||||
(defn f-comp [atom]
|
||||
[rn/text atom])
|
||||
|
||||
(fn []
|
||||
(let [atom (rf/sub [:sub])]
|
||||
(fn []
|
||||
[:f> f-comp atom])))
|
||||
```
|
||||
|
||||
it's important to name functional components with `f-` prefix
|
||||
|
||||
### Component styles
|
||||
|
||||
Prefer to define styles in a separate file named `style.cljs`, colocated with
|
||||
@@ -90,11 +134,38 @@ their styles in the top-level of the properties map, prefer to add them inside t
|
||||
]
|
||||
```
|
||||
|
||||
### Always apply animated styles in the style file
|
||||
Also its fine to keep one liner styles in view
|
||||
|
||||
```clojure
|
||||
;; ok
|
||||
[rn/view {:style {:flex 1 :padding-top 5}}]
|
||||
```
|
||||
|
||||
### Don't define properties in styles ns
|
||||
|
||||
Properties must be set on view level
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
{:style {:position :absolute
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0}
|
||||
:blur-amount 30
|
||||
:blur-radius 25
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}
|
||||
|
||||
;; good
|
||||
{:position :absolute
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0}
|
||||
```
|
||||
|
||||
|
||||
### Apply animated styles in the style file
|
||||
|
||||
When implementing styles for reanimated views, we should always define
|
||||
them in the style file and apply animations with reanimated/apply-animations-to-style
|
||||
in the style definition.
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
|
||||
@@ -256,8 +256,6 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-randombytes (3.6.1):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (2.0.0):
|
||||
- React
|
||||
- react-native-shake (3.4.0):
|
||||
- React
|
||||
- react-native-slider (3.0.0):
|
||||
@@ -412,6 +410,8 @@ PODS:
|
||||
- Yoga
|
||||
- RNShare (7.0.1):
|
||||
- React-Core
|
||||
- RNStaticSafeAreaInsets (2.2.0):
|
||||
- React-Core
|
||||
- RNSVG (9.13.6):
|
||||
- React
|
||||
- SDWebImage (5.11.1):
|
||||
@@ -468,7 +468,6 @@ DEPENDENCIES:
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
|
||||
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-shake (from `../node_modules/react-native-shake`)
|
||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||
- react-native-status (from `../modules/react-native-status`)
|
||||
@@ -503,6 +502,7 @@ DEPENDENCIES:
|
||||
- RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`)
|
||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||
- RNShare (from `../node_modules/react-native-share`)
|
||||
- RNStaticSafeAreaInsets (from `../node_modules/react-native-static-safe-area-insets`)
|
||||
- RNSVG (from `../node_modules/react-native-svg`)
|
||||
- secp256k1 (from `https://github.com/status-im/secp256k1.swift.git`)
|
||||
- SQLCipher (~> 3.0)
|
||||
@@ -589,8 +589,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-orientation-locker"
|
||||
react-native-randombytes:
|
||||
:path: "../node_modules/react-native-randombytes"
|
||||
react-native-safe-area-context:
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-shake:
|
||||
:path: "../node_modules/react-native-shake"
|
||||
react-native-slider:
|
||||
@@ -659,6 +657,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-reanimated"
|
||||
RNShare:
|
||||
:path: "../node_modules/react-native-share"
|
||||
RNStaticSafeAreaInsets:
|
||||
:path: "../node_modules/react-native-static-safe-area-insets"
|
||||
RNSVG:
|
||||
:path: "../node_modules/react-native-svg"
|
||||
secp256k1:
|
||||
@@ -682,11 +682,11 @@ SPEC CHECKSUMS:
|
||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
||||
CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082
|
||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
||||
FBLazyVector: d2db9d00883282819d03bbd401b2ad4360d47580
|
||||
FBReactNativeSpec: 94da4d84ba3b1acf459103320882daa481a2b62d
|
||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||
glog: 9e3310013355e9221591364060e841c28041dfe3
|
||||
glog: 997518ea2aa2d8cd5df9797b641b758d52ecf2bc
|
||||
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
|
||||
Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8
|
||||
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
|
||||
@@ -716,7 +716,6 @@ SPEC CHECKSUMS:
|
||||
react-native-netinfo: ddaca8bbb9e6e914b1a23787ccb879bc642931c9
|
||||
react-native-orientation-locker: 851f6510d8046ea2f14aa169b1e01fcd309a94ba
|
||||
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
|
||||
react-native-safe-area-context: 60f654e00b6cc416573f6d5dbfce3839958eb57a
|
||||
react-native-shake: de052eaa3eadc4a326b8ddd7ac80c06e8d84528c
|
||||
react-native-slider: 12bd76d3d568c9c5500825db54123d44b48e4ad4
|
||||
react-native-status: 21f75d492fd311dc111303da38a7a2b23a8a8466
|
||||
@@ -751,6 +750,7 @@ SPEC CHECKSUMS:
|
||||
RNReactNativeHapticFeedback: 2566b468cc8d0e7bb2f84b23adc0f4614594d071
|
||||
RNReanimated: 3ad6ec4e147462206be9d1c925df10b6ea850b0e
|
||||
RNShare: 2dc2fcac3f7321cfd6b60a23ed4bf4d549f86f5f
|
||||
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
|
||||
RNSVG: 8ba35cbeb385a52fd960fd28db9d7d18b4c2974f
|
||||
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
||||
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
||||
@@ -763,4 +763,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: c29de3b14e3275299c51aa95520622f09d084bcb
|
||||
|
||||
COCOAPODS: 1.12.0
|
||||
COCOAPODS: 1.12.1
|
||||
|
||||
@@ -63,9 +63,9 @@
|
||||
"react-native-randombytes": "^3.6.1",
|
||||
"react-native-reanimated": "2.3.3",
|
||||
"react-native-redash": "^16.0.11",
|
||||
"react-native-safe-area-context": "^2.0.0",
|
||||
"react-native-shake": "^3.3.1",
|
||||
"react-native-share": "^7.0.1",
|
||||
"react-native-static-safe-area-insets": "^2.2.0",
|
||||
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.5.39",
|
||||
"react-native-svg": "^9.8.4",
|
||||
"react-native-touch-id": "^4.4.1",
|
||||
|
||||
|
After Width: | Height: | Size: 528 B |
|
After Width: | Height: | Size: 769 B |
|
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 628 B |
|
Before Width: | Height: | Size: 759 B After Width: | Height: | Size: 913 B |
|
Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 869 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 843 B After Width: | Height: | Size: 946 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 986 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -141,6 +141,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
{:ClipPath #js {:render identity}
|
||||
:Circle #js {:render identity}
|
||||
:Defs #js {:render identity}
|
||||
:G #js {:render identity}
|
||||
:Path #js {:render identity}
|
||||
:Rect #js {:render identity}
|
||||
:SvgUri #js {:render identity}
|
||||
@@ -154,10 +155,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
(def net-info #js {})
|
||||
(def touchid #js {})
|
||||
(def react-native-image-viewing #js {:default {}})
|
||||
(def safe-area-context
|
||||
(clj->js {:SafeAreaProvider {:_reactNativeIphoneXHelper {:getStatusBarHeight (fn [])}}
|
||||
:SafeAreaInsetsContext {:Consumer (fn [])}
|
||||
:SafeAreaView {}}))
|
||||
(def react-native-static-safe-area-insets #js {:default {}})
|
||||
|
||||
(def back-handler
|
||||
#js
|
||||
@@ -366,7 +364,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
"react-native-background-timer" background-timer
|
||||
"react-native-image-crop-picker" image-crop-picker
|
||||
"react-native-gesture-handler" react-native-gesture-handler
|
||||
"react-native-safe-area-context" safe-area-context
|
||||
"react-native-static-safe-area-insets" react-native-static-safe-area-insets
|
||||
"react-native-config" config
|
||||
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
|
||||
:getBottomSpace (fn [])})
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.animated :as animated]
|
||||
[quo.components.header :as header]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.platform :as platform]
|
||||
[reagent.core :as reagent]))
|
||||
[reagent.core :as reagent]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(defn header-wrapper-style
|
||||
[{:keys [value offset]}]
|
||||
@@ -95,11 +95,9 @@
|
||||
(defn header
|
||||
[{:keys [use-insets] :as props} & children]
|
||||
(if use-insets
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[header-container
|
||||
(-> props
|
||||
(dissoc :use-insets)
|
||||
(assoc :insets insets))
|
||||
children])]
|
||||
[header-container
|
||||
(-> props
|
||||
(dissoc :use-insets)
|
||||
(assoc :insets (safe-area/get-insets)))
|
||||
children]
|
||||
[header-container props children]))
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
(:require [cljs-bean.core :as bean]
|
||||
[quo.animated :as animated]
|
||||
[quo.components.bottom-sheet.style :as styles]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.gesture-handler :as gesture-handler]
|
||||
[quo.platform :as platform]
|
||||
[quo.react :as react]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[reagent.core :as reagent]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(def opacity-coeff 0.8)
|
||||
(def close-duration 150)
|
||||
@@ -43,7 +43,7 @@
|
||||
(rn/use-keyboard)
|
||||
keyboard-height-android-delta
|
||||
(if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0)
|
||||
safe-area (safe-area/use-safe-area)
|
||||
safe-area (safe-area/get-insets)
|
||||
window-height (- window-height
|
||||
(if platform/android?
|
||||
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
(ns quo.components.safe-area
|
||||
(:require ["react-native-safe-area-context" :as safe-area-context :refer
|
||||
(SafeAreaView SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def provider (reagent/adapt-react-class SafeAreaProvider))
|
||||
(def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext)))
|
||||
(def view (reagent/adapt-react-class SafeAreaView))
|
||||
|
||||
(defn consumer
|
||||
[component]
|
||||
[consumer-raw
|
||||
(fn [insets]
|
||||
(reagent/as-element
|
||||
[component (js->clj insets :keywordize-keys true)]))])
|
||||
|
||||
(defn use-safe-area
|
||||
[]
|
||||
(let [insets (useSafeAreaInsets)]
|
||||
{:top (.-top ^js insets)
|
||||
:bottom (.-bottom ^js insets)
|
||||
:left (.-left ^js insets)
|
||||
:right (.-right ^js insets)}))
|
||||
@@ -8,7 +8,6 @@
|
||||
[quo.components.list.header :as list-header]
|
||||
[quo.components.list.index :as list-index]
|
||||
[quo.components.list.item :as list-item]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo.components.separator :as separator]
|
||||
[quo.components.text :as text]
|
||||
[quo.components.text-input :as text-input]
|
||||
@@ -29,8 +28,5 @@
|
||||
(def switch controls/switch)
|
||||
(def radio controls/radio)
|
||||
(def checkbox controls/checkbox)
|
||||
(def safe-area-provider safe-area/provider)
|
||||
(def safe-area-consumer safe-area/consumer)
|
||||
(def safe-area-view safe-area/view)
|
||||
(def separator separator/separator)
|
||||
(def get-color colors/get-color)
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
(reanimated/set-shared-value scroll-y current-y)))
|
||||
|
||||
(defn header
|
||||
[{:keys [theme-color display-picture-comp cover-uri title-comp]} top-inset scroll-y]
|
||||
[{:keys [theme-color f-display-picture-comp cover-uri title-comp]} top-inset scroll-y]
|
||||
(let [input-range [0 (* threshold 0.33)]
|
||||
picture-scale-down 0.4
|
||||
size-animation (interpolate scroll-y input-range [80 (* 80 picture-scale-down)])
|
||||
@@ -48,50 +48,49 @@
|
||||
[reanimated/view {:style (style/header-bottom-part border-animation)}
|
||||
[title-comp]]
|
||||
[reanimated/view {:style (style/entity-picture size-animation)}
|
||||
[display-picture-comp image-animation]]]))
|
||||
[:f> f-display-picture-comp image-animation]]]))
|
||||
|
||||
(defn- f-animated-header-list
|
||||
[{:keys [header-comp main-comp back-button-on-press] :as params}]
|
||||
(let [window-height (:height (rn/get-window))
|
||||
{:keys [top bottom]} (safe-area/get-insets)
|
||||
;; view height calculation is different because window height is different on iOS and Android:
|
||||
view-height (if platform/ios?
|
||||
(- window-height bottom)
|
||||
(+ window-height top))
|
||||
initial-y (if platform/ios? (- top) 0)
|
||||
scroll-y (reanimated/use-shared-value initial-y)
|
||||
opacity-animation (interpolate scroll-y
|
||||
[(* threshold 0.33) (* threshold 0.66)]
|
||||
[0 1])
|
||||
translate-animation (interpolate scroll-y [(* threshold 0.66) threshold] [100 56])
|
||||
title-opacity-animation (interpolate scroll-y [(* threshold 0.66) threshold] [0 1])]
|
||||
[rn/view {:style (style/container-view view-height)}
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:on-press back-button-on-press
|
||||
:style (style/button-container {:left 20})}
|
||||
[quo/icon :i/arrow-left {:size 20 :color (colors/theme-colors colors/black colors/white)}]]
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:style (style/button-container {:right 20})}
|
||||
[quo/icon :i/options {:size 20 :color (colors/theme-colors colors/black colors/white)}]]
|
||||
[reanimated/blur-view
|
||||
{:blurAmount 32
|
||||
:blurType :light
|
||||
:overlayColor (if platform/ios? colors/white-opa-70 :transparent)
|
||||
:style (style/blur-view opacity-animation)}
|
||||
[reanimated/view {:style (style/header-comp translate-animation title-opacity-animation)}
|
||||
[header-comp]]]
|
||||
[reanimated/flat-list
|
||||
{:data [nil]
|
||||
:render-fn main-comp
|
||||
:key-fn str
|
||||
:header (reagent/as-element (header params top scroll-y))
|
||||
;; TODO: https://github.com/status-im/status-mobile/issues/14924
|
||||
:scroll-event-throttle 8
|
||||
:on-scroll (fn [event] (scroll-handler event initial-y scroll-y))}]]))
|
||||
|
||||
(defn animated-header-list
|
||||
[{:keys [header-comp main-comp back-button-on-press] :as parameters}]
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
(let [window-height (:height (rn/get-window))
|
||||
status-bar-height (rn/status-bar-height)
|
||||
bottom-inset (:bottom insets)
|
||||
;; view height calculation is different because window height is different on iOS and Android:
|
||||
view-height (if platform/ios?
|
||||
(- window-height bottom-inset)
|
||||
(+ window-height status-bar-height))
|
||||
initial-y (if platform/ios? (- (:top insets)) 0)]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [scroll-y (reanimated/use-shared-value initial-y)
|
||||
opacity-animation (interpolate scroll-y
|
||||
[(* threshold 0.33) (* threshold 0.66)]
|
||||
[0 1])
|
||||
translate-animation (interpolate scroll-y [(* threshold 0.66) threshold] [100 56])
|
||||
title-opacity-animation (interpolate scroll-y [(* threshold 0.66) threshold] [0 1])]
|
||||
[rn/view {:style (style/container-view view-height)}
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:on-press back-button-on-press
|
||||
:style (style/button-container {:left 20})}
|
||||
[quo/icon :i/arrow-left {:size 20 :color (colors/theme-colors colors/black colors/white)}]]
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:style (style/button-container {:right 20})}
|
||||
[quo/icon :i/options {:size 20 :color (colors/theme-colors colors/black colors/white)}]]
|
||||
[reanimated/blur-view
|
||||
{:blurAmount 32
|
||||
:blurType :light
|
||||
:overlayColor (if platform/ios? colors/white-opa-70 :transparent)
|
||||
:style (style/blur-view opacity-animation)}
|
||||
[reanimated/view {:style (style/header-comp translate-animation title-opacity-animation)}
|
||||
[header-comp]]]
|
||||
[reanimated/flat-list
|
||||
{:data [nil]
|
||||
:render-fn main-comp
|
||||
:key-fn str
|
||||
:header (reagent/as-element (header parameters (:top insets) scroll-y))
|
||||
;; TODO: https://github.com/status-im/status-mobile/issues/14924
|
||||
:scroll-event-throttle 8
|
||||
:on-scroll (fn [event] (scroll-handler event initial-y scroll-y))}]]))]))])
|
||||
[params]
|
||||
[:f> f-animated-header-list params])
|
||||
|
||||
@@ -65,10 +65,10 @@
|
||||
18 ;; ~ 9 is char width, 18 is width used in Figma.
|
||||
(* 9 max-line-digits font-scale))))
|
||||
|
||||
(defn- native-renderer
|
||||
(defn- f-native-renderer
|
||||
[{:keys [rows max-lines on-copy-press]
|
||||
:or {max-lines ##Inf}}]
|
||||
(let [font-scale (:font-scale (rn/use-window-dimensions))
|
||||
(let [font-scale (:font-scale (rn/get-window))
|
||||
total-rows (count rows)
|
||||
number-rows-to-show (min (count rows) max-lines)
|
||||
line-number-width (calc-line-number-width font-scale number-rows-to-show)
|
||||
@@ -100,7 +100,7 @@
|
||||
{:language language
|
||||
:renderer (fn [^js/Object props]
|
||||
(reagent/as-element
|
||||
[:f> native-renderer
|
||||
[:f> f-native-renderer
|
||||
{:rows (-> props .-rows bean/->clj)
|
||||
:on-copy-press #(when on-copy-press (on-copy-press children))
|
||||
:max-lines max-lines}]))
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
(colors/alpha color 0.6))
|
||||
|
||||
(defn dropdown-comp
|
||||
[{:keys [icon open? dd-height size disabled? dd-color use-border? border-color]}]
|
||||
[{: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
|
||||
@@ -51,74 +51,75 @@
|
||||
spacing (case size
|
||||
:big 4
|
||||
:medium 2
|
||||
:small 2)]
|
||||
[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)}}]]))
|
||||
: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
|
||||
{:style {:height "100%"}
|
||||
:horizontal false
|
||||
{:horizontal false
|
||||
:nestedScrollEnabled true}
|
||||
(doall
|
||||
(map-indexed (fn [index item]
|
||||
@@ -136,29 +137,29 @@
|
||||
[text/text {:style {:text-align :center}} item]])
|
||||
items))]))
|
||||
|
||||
(defn dropdown
|
||||
(defn- f-dropdown
|
||||
[{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [open? (reagent/atom false)
|
||||
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
|
||||
:open? open?
|
||||
:dd-height dd-height}]
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:height dd-height}
|
||||
{:height dd-height})}
|
||||
[items-comp
|
||||
{:items items
|
||||
:on-select on-select}]]]))])
|
||||
(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}]]]))
|
||||
|
||||
(defn dropdown
|
||||
[params]
|
||||
[:f> f-dropdown params])
|
||||
|
||||
@@ -1,29 +1,43 @@
|
||||
(ns quo2.components.icon
|
||||
(:require [clojure.string :as string]
|
||||
[quo2.components.icons.icons :as icons]
|
||||
[quo2.components.icons.svg :as icons.svg]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- valid-color?
|
||||
[color]
|
||||
(or (keyword? color)
|
||||
(and (string? color)
|
||||
(not (string/blank? color)))))
|
||||
|
||||
(defn memo-icon-fn
|
||||
([icon-name] (memo-icon-fn icon-name nil))
|
||||
([icon-name
|
||||
{:keys [color container-style size
|
||||
accessibility-label no-color]
|
||||
{:keys [color color-2 no-color
|
||||
container-style size accessibility-label]
|
||||
:or {accessibility-label :icon}}]
|
||||
(let [size (or size 20)]
|
||||
^{:key icon-name}
|
||||
[rn/image
|
||||
{:style
|
||||
(merge {:width size
|
||||
:height size}
|
||||
(if-let [svg-icon (icons.svg/get-icon icon-name size)]
|
||||
[svg-icon
|
||||
{:size size
|
||||
:color (when (valid-color? color) color)
|
||||
:color-2 (when (valid-color? color-2) color-2)
|
||||
:accessibility-label accessibility-label
|
||||
:style container-style}]
|
||||
[rn/image
|
||||
{:style
|
||||
(merge {:width size
|
||||
:height size}
|
||||
|
||||
(when (not no-color)
|
||||
{:tint-color (if (and (string? color) (not (string/blank? color)))
|
||||
color
|
||||
(colors/theme-colors colors/neutral-100 colors/white))})
|
||||
(when (not no-color)
|
||||
{:tint-color (if (and (string? color) (not (string/blank? color)))
|
||||
color
|
||||
(colors/theme-colors colors/neutral-100 colors/white))})
|
||||
|
||||
container-style)
|
||||
:accessibility-label accessibility-label
|
||||
:source (icons/icon-source (str (name icon-name) size))}])))
|
||||
container-style)
|
||||
:accessibility-label accessibility-label
|
||||
:source (icons/icon-source (str (name icon-name) size))}]))))
|
||||
|
||||
(def icon (memoize memo-icon-fn))
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(def icon-path "./resources/images/icons2/")
|
||||
(def ^:private icon-path "./resources/images/icons2/")
|
||||
|
||||
(defn require-icon
|
||||
(defn- require-icon
|
||||
[size path]
|
||||
(fn [el]
|
||||
(let [s (str "." path el ".png")
|
||||
@@ -15,7 +15,7 @@
|
||||
(str size))]
|
||||
[k `(js/require ~s)])))
|
||||
|
||||
(defn get-files
|
||||
(defn- get-files
|
||||
[path]
|
||||
(->> (io/file path)
|
||||
file-seq
|
||||
@@ -23,7 +23,7 @@
|
||||
(map #(first (string/split (.getName %) #"@")))
|
||||
distinct))
|
||||
|
||||
(defn get-icons
|
||||
(defn- get-icons
|
||||
[size]
|
||||
(let [path (str icon-path size "x" size "/")]
|
||||
(into {} (map (require-icon size path) (get-files path)))))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require-macros [quo2.components.icons.icons :as icons])
|
||||
(:require [taoensso.timbre :as log]))
|
||||
|
||||
(def icons (icons/resolve-icons))
|
||||
(def ^:private icons (icons/resolve-icons))
|
||||
|
||||
(defn icon-source
|
||||
[icon]
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
(ns quo2.components.icons.svg
|
||||
"Declare icons in this namespace when they have two possible colors, because the
|
||||
ReactNative `:tint-color` prop affects all non-transparent pixels of PNGs. If
|
||||
the icon has only one color, prefer a PNG.
|
||||
|
||||
Keep all SVG components private and expose them by name in the `icons` var."
|
||||
(:require [react-native.svg :as svg]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn- container
|
||||
[{:keys [size accessibility-label style]
|
||||
:or {size 20}}
|
||||
& children]
|
||||
(into [svg/svg
|
||||
{:accessibility-label accessibility-label
|
||||
:style style
|
||||
:width size
|
||||
:height size
|
||||
:view-box (str "0 0 " size " " size)
|
||||
:fill :none}]
|
||||
children))
|
||||
|
||||
(defn- clear-20
|
||||
[{:keys [color color-2] :as props}]
|
||||
(let [color (or color colors/neutral-100)
|
||||
color-2 (or color-2 colors/white)]
|
||||
[container props
|
||||
[svg/path
|
||||
{:d
|
||||
"M3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10Z"
|
||||
:fill color}]
|
||||
[svg/path
|
||||
{:d
|
||||
"M9.15142 9.99998L7.07566 12.0757L7.9242 12.9243L9.99994 10.8485L12.0757 12.9242L12.9242 12.0757L10.8485 9.99998L12.9242 7.92421L12.0757 7.07568L9.99994 9.15145L7.92421 7.07572L7.07568 7.92425L9.15142 9.99998Z"
|
||||
:fill color-2}]]))
|
||||
|
||||
(def ^:private icons
|
||||
{:i/clear-20 clear-20})
|
||||
|
||||
(defn- append-to-keyword
|
||||
[k & xs]
|
||||
(keyword (apply str
|
||||
(subs (str k) 1)
|
||||
xs)))
|
||||
|
||||
(defn get-icon
|
||||
[icon-name size]
|
||||
(get icons (append-to-keyword icon-name "-" size)))
|
||||
@@ -0,0 +1,8 @@
|
||||
(ns quo2.components.inputs.recovery-phrase.component-spec
|
||||
(:require [quo2.components.inputs.recovery-phrase.view :as recovery-phrase]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Recovery phrase input"
|
||||
(h/test "Default render"
|
||||
(h/render [recovery-phrase/recovery-phrase-input {}])
|
||||
(h/is-truthy (h/get-by-label-text :recovery-phrase-input))))
|
||||
@@ -0,0 +1,45 @@
|
||||
(ns quo2.components.inputs.recovery-phrase.style
|
||||
(:require [quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(def container
|
||||
{:min-height 40
|
||||
:flex 1
|
||||
:padding-vertical 4
|
||||
:padding-horizontal 20})
|
||||
|
||||
(defn input
|
||||
[]
|
||||
(assoc (text/text-style {})
|
||||
:height 32
|
||||
:flex-grow 1
|
||||
:padding-vertical 5
|
||||
:text-align-vertical :top))
|
||||
|
||||
(defn placeholder-color
|
||||
[input-state override-theme blur?]
|
||||
(cond
|
||||
(and (= input-state :focused) blur?)
|
||||
(colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 override-theme)
|
||||
|
||||
(= input-state :focused) ; Not blur
|
||||
(colors/theme-colors colors/neutral-30 colors/neutral-60 override-theme)
|
||||
|
||||
blur? ; :default & blur
|
||||
(colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-30 override-theme)
|
||||
|
||||
:else ; :default & not blur
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-50 override-theme)))
|
||||
|
||||
(defn cursor-color
|
||||
[customization-color override-theme]
|
||||
(colors/theme-colors (colors/custom-color customization-color 50)
|
||||
(colors/custom-color customization-color 60)
|
||||
override-theme))
|
||||
|
||||
(defn error-word
|
||||
[]
|
||||
{:height 22
|
||||
:padding-horizontal 20
|
||||
:background-color colors/danger-50-opa-10
|
||||
:color (colors/theme-colors colors/danger-50 colors/danger-60)})
|
||||
@@ -0,0 +1,54 @@
|
||||
(ns quo2.components.inputs.recovery-phrase.view
|
||||
(:require [clojure.string :as string]
|
||||
[quo2.components.inputs.recovery-phrase.style :as style]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def ^:private custom-props
|
||||
[:customization-color :override-theme :blur? :cursor-color :multiline :on-focus :on-blur
|
||||
:placeholder-text-color :mark-errors? :error-pred :word-limit])
|
||||
|
||||
(defn- error-word
|
||||
[text]
|
||||
[rn/text {:style (style/error-word)}
|
||||
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 #" "))))
|
||||
|
||||
(defn recovery-phrase-input
|
||||
[_ _]
|
||||
(let [state (reagent/atom :default)
|
||||
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}
|
||||
:as props}
|
||||
text]
|
||||
(let [extra-props (apply dissoc props custom-props)]
|
||||
[rn/view {:style style/container}
|
||||
[rn/text-input
|
||||
(merge {:accessibility-label :recovery-phrase-input
|
||||
:style (style/input)
|
||||
:placeholder-text-color (style/placeholder-color @state override-theme blur?)
|
||||
:cursor-color (style/cursor-color customization-color override-theme)
|
||||
:multiline true
|
||||
:on-focus (fn []
|
||||
(set-focused)
|
||||
(when on-focus (on-focus)))
|
||||
:on-blur (fn []
|
||||
(set-default)
|
||||
(when on-blur (on-blur)))}
|
||||
extra-props)
|
||||
(if mark-errors?
|
||||
(mark-error-words error-pred text word-limit)
|
||||
text)]]))))
|
||||
@@ -28,7 +28,7 @@
|
||||
[blur? override-theme]
|
||||
(if blur?
|
||||
(colors/theme-colors colors/neutral-80-opa-30 colors/white-opa-10 override-theme)
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-50 override-theme)))
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-60 override-theme)))
|
||||
|
||||
(defn cursor
|
||||
[customization-color override-theme]
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
(ns quo2.components.links.link-preview.component-spec
|
||||
(:require [quo2.components.links.link-preview.view :as view]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(def props
|
||||
{:title "Some title"
|
||||
:description "Some description"
|
||||
:link "status.im"
|
||||
:thumbnail "data:image/png,whatever"})
|
||||
|
||||
(h/describe "Links - Link Preview"
|
||||
(h/test "default render"
|
||||
(h/render [view/view])
|
||||
(h/is-truthy (h/query-by-label-text :link-preview))
|
||||
(h/is-null (h/query-by-label-text :button-enable-preview)))
|
||||
|
||||
(h/test "renders with most common props"
|
||||
(h/render [view/view props])
|
||||
(h/is-truthy (h/query-by-text (:title props)))
|
||||
(h/is-truthy (h/query-by-text (:description props)))
|
||||
(h/is-truthy (h/query-by-text (:link props)))
|
||||
(h/is-truthy (h/query-by-label-text :thumbnail)))
|
||||
|
||||
(h/test "does not render thumbnail if prop is not present"
|
||||
(h/render [view/view (dissoc props :thumbnail)])
|
||||
(h/is-null (h/query-by-label-text :thumbnail)))
|
||||
|
||||
(h/test "shows button to enable preview when preview is disabled"
|
||||
(h/render [view/view
|
||||
(assoc props
|
||||
:enabled? false
|
||||
:disabled-text "I'm disabled")])
|
||||
(h/is-truthy (h/query-by-label-text :button-enable-preview))
|
||||
(h/is-truthy (h/query-by-text "I'm disabled")))
|
||||
|
||||
(h/test "on-enable event"
|
||||
(let [on-enable (h/mock-fn)]
|
||||
(h/render [view/view
|
||||
(assoc props
|
||||
:enabled? false
|
||||
:on-enable on-enable)])
|
||||
(h/fire-event :press (h/get-by-label-text :button-enable-preview))
|
||||
(h/was-called on-enable))))
|
||||
@@ -0,0 +1,43 @@
|
||||
(ns quo2.components.links.link-preview.style
|
||||
(:require
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn container
|
||||
[preview-enabled?]
|
||||
(merge {:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40)
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:padding-top 10
|
||||
:padding-bottom 12}
|
||||
(when-not preview-enabled?
|
||||
{:height 139
|
||||
:align-items :center
|
||||
:justify-content :center})))
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def title
|
||||
{:flex 1
|
||||
:margin-bottom 2})
|
||||
|
||||
(defn link
|
||||
[]
|
||||
{:margin-top 8
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})
|
||||
|
||||
(defn thumbnail
|
||||
[size]
|
||||
{:width "100%"
|
||||
:height (if (= size :large) 271 139)
|
||||
:margin-top 12
|
||||
:border-radius 12})
|
||||
|
||||
(def logo
|
||||
{:margin-right 6
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 8})
|
||||
@@ -0,0 +1,77 @@
|
||||
(ns quo2.components.links.link-preview.view
|
||||
(:require [quo2.components.buttons.button :as button]
|
||||
[quo2.components.links.link-preview.style :as style]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- button-disabled
|
||||
[disabled-text on-enable]
|
||||
[button/button
|
||||
{:before :i/reveal
|
||||
:size 32
|
||||
:type :grey
|
||||
:on-press on-enable
|
||||
:accessibility-label :button-enable-preview}
|
||||
disabled-text])
|
||||
|
||||
(defn- description-comp
|
||||
[description]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 3
|
||||
:accessibility-label :description}
|
||||
description])
|
||||
|
||||
(defn- link-comp
|
||||
[link]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/link)
|
||||
:accessibility-label :link}
|
||||
link])
|
||||
|
||||
(defn- title-comp
|
||||
[title]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
title])
|
||||
|
||||
(defn- thumbnail-comp
|
||||
[thumbnail size]
|
||||
[rn/image
|
||||
{:style (style/thumbnail size)
|
||||
:source (if (string? thumbnail)
|
||||
{:uri thumbnail}
|
||||
thumbnail)
|
||||
:accessibility-label :thumbnail}])
|
||||
|
||||
(defn- logo-comp
|
||||
[logo]
|
||||
[rn/image
|
||||
{:accessibility-label :logo
|
||||
:source logo
|
||||
:style style/logo}])
|
||||
|
||||
(defn view
|
||||
[{:keys [title logo description link thumbnail
|
||||
enabled? on-enable disabled-text
|
||||
container-style thumbnail-size]
|
||||
:or {enabled? true}}]
|
||||
[rn/view
|
||||
{:style (merge (style/container enabled?) container-style)
|
||||
:accessibility-label :link-preview}
|
||||
(if enabled?
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
[logo-comp logo]
|
||||
[title-comp title]]
|
||||
[description-comp description]
|
||||
[link-comp link]
|
||||
(when thumbnail
|
||||
[thumbnail-comp thumbnail thumbnail-size])]
|
||||
[button-disabled disabled-text on-enable])])
|
||||
@@ -48,10 +48,6 @@
|
||||
{:text-transform :lowercase
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})
|
||||
|
||||
(def clear-button
|
||||
{:border-color colors/danger-50
|
||||
:border-width 1})
|
||||
|
||||
(def clear-button-container
|
||||
{:width 20
|
||||
:height 20
|
||||
|
||||
@@ -39,9 +39,8 @@
|
||||
:hit-slop {:top 3 :right 3 :bottom 3 :left 3}
|
||||
:accessibility-label :button-clear-preview}
|
||||
[icon/icon :i/clear
|
||||
{:size 20
|
||||
:container-style style/clear-button
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]])
|
||||
{:size 20
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]])
|
||||
|
||||
(defn view
|
||||
[{:keys [title body logo on-clear loading? loading-message container-style]}]
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
(- (oops/oget e "nativeEvent.layout.width")
|
||||
(* 2 horizontal-spacing))))
|
||||
|
||||
(defn- view-component
|
||||
(defn- f-view
|
||||
[]
|
||||
(let [preview-width (reagent/atom 0)
|
||||
flat-list-ref (atom nil)]
|
||||
@@ -83,4 +83,4 @@
|
||||
|
||||
(defn view
|
||||
[props]
|
||||
[:f> view-component props])
|
||||
[:f> f-view props])
|
||||
|
||||
@@ -20,61 +20,59 @@
|
||||
:message 144}])
|
||||
|
||||
;; Standlone message skeleton
|
||||
(defn message-skeleton
|
||||
(defn- f-message-skeleton
|
||||
[]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [color (colors/theme-colors colors/neutral-5 colors/neutral-70)
|
||||
loading-color (colors/theme-colors colors/neutral-10 colors/neutral-60)
|
||||
content-width (rand-nth message-content-width)
|
||||
author-width (content-width :author)
|
||||
message-width (content-width :message)
|
||||
{window-width :width} (rn/use-window-dimensions)
|
||||
translate-x (reanimated/use-shared-value (- window-width))
|
||||
animated-gradient-style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateX translate-x}]}
|
||||
{:width window-width
|
||||
:height "100%"})]
|
||||
(reanimated/animate-shared-value-with-repeat translate-x window-width 1000 :linear (- 1) false)
|
||||
[masked-view/masked-view
|
||||
{:style {:height message-skeleton-height}
|
||||
:maskElement (reagent/as-element
|
||||
[rn/view
|
||||
{:style {:height message-skeleton-height
|
||||
:flex-direction :row
|
||||
:padding-vertical 11
|
||||
:background-color :transparent
|
||||
:padding-left 21}}
|
||||
[rn/view
|
||||
{:style {:height avatar-skeleton-size
|
||||
:width avatar-skeleton-size
|
||||
:border-radius (/ avatar-skeleton-size 2)
|
||||
:background-color color
|
||||
:overflow :hidden}}]
|
||||
[rn/view
|
||||
{:style {:padding-left 8
|
||||
:background-color :transparent}}
|
||||
[rn/view
|
||||
{:style {:height 8
|
||||
:width author-width
|
||||
:border-radius 6
|
||||
:background-color color
|
||||
:margin-bottom 8
|
||||
:overflow :hidden}}]
|
||||
[rn/view
|
||||
{:style {:height 16
|
||||
:width message-width
|
||||
:border-radius 6
|
||||
:overflow :hidden
|
||||
:background-color color}}]]])}
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:background-color color}}
|
||||
[reanimated/linear-gradient
|
||||
{:colors [color color loading-color color color]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 1 :y 0}
|
||||
:style animated-gradient-style}]]]))])
|
||||
(let [color (colors/theme-colors colors/neutral-5 colors/neutral-70)
|
||||
loading-color (colors/theme-colors colors/neutral-10 colors/neutral-60)
|
||||
content-width (rand-nth message-content-width)
|
||||
author-width (content-width :author)
|
||||
message-width (content-width :message)
|
||||
{window-width :width} (rn/get-window)
|
||||
translate-x (reanimated/use-shared-value (- window-width))
|
||||
animated-gradient-style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateX translate-x}]}
|
||||
{:width window-width
|
||||
:height "100%"})]
|
||||
(reanimated/animate-shared-value-with-repeat translate-x window-width 1000 :linear (- 1) false)
|
||||
[masked-view/masked-view
|
||||
{:style {:height message-skeleton-height}
|
||||
:maskElement (reagent/as-element
|
||||
[rn/view
|
||||
{:style {:height message-skeleton-height
|
||||
:flex-direction :row
|
||||
:padding-vertical 11
|
||||
:background-color :transparent
|
||||
:padding-left 21}}
|
||||
[rn/view
|
||||
{:style {:height avatar-skeleton-size
|
||||
:width avatar-skeleton-size
|
||||
:border-radius (/ avatar-skeleton-size 2)
|
||||
:background-color color
|
||||
:overflow :hidden}}]
|
||||
[rn/view
|
||||
{:style {:padding-left 8
|
||||
:background-color :transparent}}
|
||||
[rn/view
|
||||
{:style {:height 8
|
||||
:width author-width
|
||||
:border-radius 6
|
||||
:background-color color
|
||||
:margin-bottom 8
|
||||
:overflow :hidden}}]
|
||||
[rn/view
|
||||
{:style {:height 16
|
||||
:width message-width
|
||||
:border-radius 6
|
||||
:overflow :hidden
|
||||
:background-color color}}]]])}
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:background-color color}}
|
||||
[reanimated/linear-gradient
|
||||
{:colors [color color loading-color color color]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 1 :y 0}
|
||||
:style animated-gradient-style}]]]))
|
||||
|
||||
(defn skeleton
|
||||
[parent-height]
|
||||
@@ -84,5 +82,6 @@
|
||||
colors/white
|
||||
colors/neutral-90)
|
||||
:flex 1}}
|
||||
(for [n (range number-of-skeletons)]
|
||||
[message-skeleton {:key n}])]))
|
||||
(doall
|
||||
(for [n (range number-of-skeletons)]
|
||||
[:f> f-message-skeleton {:key n}]))]))
|
||||
|
||||
@@ -10,64 +10,62 @@
|
||||
|
||||
(defn author
|
||||
[{:keys [primary-name secondary-name short-chat-key time-str contact? verified? untrustworthy?]}]
|
||||
[:f>
|
||||
(fn []
|
||||
[rn/view {:style style/container}
|
||||
[rn/view {:style style/container}
|
||||
[:<>
|
||||
[text/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style {:color (colors/theme-colors colors/neutral-100 colors/white)}}
|
||||
primary-name]
|
||||
(when (not (string/blank? secondary-name))
|
||||
[:<>
|
||||
[text/text
|
||||
{:weight :semi-bold
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style style/middle-dot-nickname}
|
||||
middle-dot]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style {:color (colors/theme-colors colors/neutral-100 colors/white)}}
|
||||
primary-name]
|
||||
(when (not (string/blank? secondary-name))
|
||||
[:<>
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style style/middle-dot-nickname}
|
||||
middle-dot]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style {:color (colors/theme-colors colors/neutral-60 colors/neutral-40)}}
|
||||
secondary-name]])]
|
||||
(when contact?
|
||||
[icons/icon :main-icons2/contact
|
||||
{:size 12
|
||||
:no-color true
|
||||
:container-style style/icon-container}])
|
||||
(cond
|
||||
verified?
|
||||
[icons/icon :main-icons2/verified
|
||||
{:size 12
|
||||
:no-color true
|
||||
:container-style style/icon-container}]
|
||||
untrustworthy?
|
||||
[icons/icon :main-icons2/untrustworthy
|
||||
{:size 12
|
||||
:no-color true
|
||||
:container-style style/icon-container}])
|
||||
(when (and (not verified?) short-chat-key)
|
||||
[text/text
|
||||
{:monospace true
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style style/chat-key-text}
|
||||
short-chat-key])
|
||||
(when (and (not verified?) time-str)
|
||||
[text/text
|
||||
{:monospace true
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style style/middle-dot-chat-key}
|
||||
middle-dot])
|
||||
(when time-str
|
||||
[text/text
|
||||
{:monospace true
|
||||
:size :paragraph-2
|
||||
:accessibility-label :message-timestamp
|
||||
:number-of-lines 1
|
||||
:style (style/time-text verified?)}
|
||||
time-str])])])
|
||||
:style {:color (colors/theme-colors colors/neutral-60 colors/neutral-40)}}
|
||||
secondary-name]])]
|
||||
(when contact?
|
||||
[icons/icon :main-icons2/contact
|
||||
{:size 12
|
||||
:no-color true
|
||||
:container-style style/icon-container}])
|
||||
(cond
|
||||
verified?
|
||||
[icons/icon :main-icons2/verified
|
||||
{:size 12
|
||||
:no-color true
|
||||
:container-style style/icon-container}]
|
||||
untrustworthy?
|
||||
[icons/icon :main-icons2/untrustworthy
|
||||
{:size 12
|
||||
:no-color true
|
||||
:container-style style/icon-container}])
|
||||
(when (and (not verified?) short-chat-key)
|
||||
[text/text
|
||||
{:monospace true
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style style/chat-key-text}
|
||||
short-chat-key])
|
||||
(when (and (not verified?) time-str)
|
||||
[text/text
|
||||
{:monospace true
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:style style/middle-dot-chat-key}
|
||||
middle-dot])
|
||||
(when time-str
|
||||
[text/text
|
||||
{:monospace true
|
||||
:size :paragraph-2
|
||||
:accessibility-label :message-timestamp
|
||||
:number-of-lines 1
|
||||
:style (style/time-text verified?)}
|
||||
time-str])])
|
||||
|
||||
@@ -172,31 +172,33 @@
|
||||
:style {:color (get-color :time)}}
|
||||
(utils/truncate-str (:info content) 24)])]]]])
|
||||
|
||||
(defn system-message
|
||||
(defn- f-system-message
|
||||
[{:keys [type style non-pressable? animate-landing? labels on-long-press] :as message}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [sv-color (reanimated/use-shared-value
|
||||
(get-color :bg (if animate-landing? :landed :default) type))]
|
||||
(when animate-landing?
|
||||
(reanimated/animate-shared-value-with-delay
|
||||
sv-color
|
||||
(get-color :bg :default type)
|
||||
0
|
||||
:linear
|
||||
1000))
|
||||
[reanimated/touchable-opacity
|
||||
{:on-press #(when-not non-pressable?
|
||||
(reanimated/set-shared-value sv-color (get-color :bg :pressed type)))
|
||||
:on-long-press on-long-press
|
||||
:style (reanimated/apply-animations-to-style
|
||||
{:background-color sv-color}
|
||||
(merge
|
||||
{:flex-direction :row
|
||||
:flex 1
|
||||
:border-radius 16
|
||||
:padding-vertical 9
|
||||
:padding-horizontal 11
|
||||
:background-color sv-color}
|
||||
style))}
|
||||
[sm-render message labels]]))])
|
||||
(let [sv-color (reanimated/use-shared-value
|
||||
(get-color :bg (if animate-landing? :landed :default) type))]
|
||||
(when animate-landing?
|
||||
(reanimated/animate-shared-value-with-delay
|
||||
sv-color
|
||||
(get-color :bg :default type)
|
||||
0
|
||||
:linear
|
||||
1000))
|
||||
[reanimated/touchable-opacity
|
||||
{:on-press #(when-not non-pressable?
|
||||
(reanimated/set-shared-value sv-color (get-color :bg :pressed type)))
|
||||
:on-long-press on-long-press
|
||||
:style (reanimated/apply-animations-to-style
|
||||
{:background-color sv-color}
|
||||
(merge
|
||||
{:flex-direction :row
|
||||
:flex 1
|
||||
:border-radius 16
|
||||
:padding-vertical 9
|
||||
:padding-horizontal 11
|
||||
:background-color sv-color}
|
||||
style))}
|
||||
[sm-render message labels]]))
|
||||
|
||||
(defn system-message
|
||||
[message]
|
||||
[:f> f-system-message message])
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
pass-through? colors/white-opa-5
|
||||
:else colors/neutral-70)))
|
||||
|
||||
(defn bottom-nav-tab
|
||||
(defn- f-bottom-nav-tab
|
||||
"[bottom-nav-tab opts]
|
||||
opts
|
||||
{:icon :i/communities
|
||||
@@ -29,66 +29,68 @@
|
||||
"
|
||||
[{:keys [icon new-notifications? notification-indicator counter-label
|
||||
on-press pass-through? icon-color-anim accessibility-label test-ID]}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [icon-animated-style (reanimated/apply-animations-to-style
|
||||
{:tint-color icon-color-anim}
|
||||
{:width 24
|
||||
:height 24
|
||||
:margin-left 33
|
||||
:margin-top 8})
|
||||
background-color (reanimated/use-shared-value "transparent")
|
||||
background-animated-style (reanimated/apply-animations-to-style
|
||||
{:background-color background-color}
|
||||
{:width 90
|
||||
:height 40
|
||||
:border-radius 10})]
|
||||
[rn/touchable-without-feedback
|
||||
{:test-ID test-ID
|
||||
:on-press on-press
|
||||
:on-press-in #(toggle-background-color background-color false pass-through?)
|
||||
:on-press-out #(toggle-background-color background-color true pass-through?)
|
||||
:accessibility-label accessibility-label}
|
||||
[reanimated/view {:style background-animated-style}
|
||||
;; In android animations are not working for the animated components which are nested by
|
||||
;; hole-view,
|
||||
;; Interestingly this only happens when hole view and blur view are used together
|
||||
;; Similar behavior is also seen while removing holes, and to fix that we used key and
|
||||
;; force-rendered view
|
||||
;; But we need animations faster for tab clicks, so we can't rely on reagent atoms,
|
||||
;; so for now only using hole view for the ios tab icon notification boundary
|
||||
(if platform/ios?
|
||||
[hole-view/hole-view
|
||||
{:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
(let [icon-animated-style (reanimated/apply-animations-to-style
|
||||
{:tint-color icon-color-anim}
|
||||
{:width 24
|
||||
:height 24
|
||||
:margin-left 33
|
||||
:margin-top 8})
|
||||
background-color (reanimated/use-shared-value "transparent")
|
||||
background-animated-style (reanimated/apply-animations-to-style
|
||||
{:background-color background-color}
|
||||
{:width 90
|
||||
:height 40
|
||||
:border-radius 10})]
|
||||
[rn/touchable-without-feedback
|
||||
{:test-ID test-ID
|
||||
:on-press on-press
|
||||
:on-press-in #(toggle-background-color background-color false pass-through?)
|
||||
:on-press-out #(toggle-background-color background-color true pass-through?)
|
||||
:accessibility-label accessibility-label}
|
||||
[reanimated/view {:style background-animated-style}
|
||||
;; In android animations are not working for the animated components which are nested by
|
||||
;; hole-view,
|
||||
;; Interestingly this only happens when hole view and blur view are used together
|
||||
;; Similar behavior is also seen while removing holes, and to fix that we used key and
|
||||
;; force-rendered view
|
||||
;; But we need animations faster for tab clicks, so we can't rely on reagent atoms,
|
||||
;; so for now only using hole view for the ios tab icon notification boundary
|
||||
(if platform/ios?
|
||||
[hole-view/hole-view
|
||||
{:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
|
||||
|
||||
:else
|
||||
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
|
||||
[reanimated/image
|
||||
{:style icon-animated-style
|
||||
:source (icons/icon-source (keyword (str icon 24)))}]]
|
||||
[reanimated/image
|
||||
{:style icon-animated-style
|
||||
:source (icons/icon-source (keyword (str icon 24)))}])
|
||||
(when new-notifications?
|
||||
(if (= notification-indicator :counter)
|
||||
[counter/counter
|
||||
{:override-text-color colors/white
|
||||
:override-bg-color colors/primary-50
|
||||
:style {:position :absolute
|
||||
:left 48
|
||||
:top 2}}
|
||||
counter-label]
|
||||
[rn/view
|
||||
{:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:top 6
|
||||
:left 51
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]]))])
|
||||
:else
|
||||
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
|
||||
[reanimated/image
|
||||
{:style icon-animated-style
|
||||
:source (icons/icon-source (keyword (str icon 24)))}]]
|
||||
[reanimated/image
|
||||
{:style icon-animated-style
|
||||
:source (icons/icon-source (keyword (str icon 24)))}])
|
||||
(when new-notifications?
|
||||
(if (= notification-indicator :counter)
|
||||
[counter/counter
|
||||
{:override-text-color colors/white
|
||||
:override-bg-color colors/primary-50
|
||||
:style {:position :absolute
|
||||
:left 48
|
||||
:top 2}}
|
||||
counter-label]
|
||||
[rn/view
|
||||
{:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:top 6
|
||||
:left 51
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]]))
|
||||
|
||||
(defn bottom-nav-tab
|
||||
[opts]
|
||||
[:f> f-bottom-nav-tab opts])
|
||||
|
||||
@@ -14,40 +14,42 @@
|
||||
:style style
|
||||
:customization-color customization-color}]))
|
||||
|
||||
(defn- f-floating-shell-button
|
||||
[dynamic-buttons style opacity-anim]
|
||||
(let [original-style (merge {:flex-direction :row
|
||||
:margin-horizontal 12
|
||||
:pointer-events :box-none}
|
||||
style)
|
||||
animated-style (reanimated/apply-animations-to-style
|
||||
(if opacity-anim
|
||||
{:opacity opacity-anim}
|
||||
{})
|
||||
original-style)]
|
||||
[reanimated/view {:style animated-style}
|
||||
;; Left Section
|
||||
[rn/view {:style {:flex 1}}
|
||||
[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}}
|
||||
[rn/view
|
||||
{:style {:position :absolute
|
||||
:flex-direction :row
|
||||
:right 0}}
|
||||
[dynamic-button-view :mention dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :scroll-to-bottom dynamic-buttons {:margin-left 8}]]]]))
|
||||
|
||||
(defn floating-shell-button
|
||||
"[floating-shell-button dynamic-buttons style opacity-anim pointer-anim]
|
||||
dynamic-buttons {:button-type {:on-press on-press :count count}}
|
||||
style override style
|
||||
opacity-anim reanimated value (optional)"
|
||||
([dynamic-button style]
|
||||
(floating-shell-button dynamic-button style nil))
|
||||
([dynamic-buttons style]
|
||||
[:f> f-floating-shell-button dynamic-buttons style nil])
|
||||
([dynamic-buttons style opacity-anim]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [original-style (merge {:flex-direction :row
|
||||
:margin-horizontal 12
|
||||
:pointer-events :box-none}
|
||||
style)
|
||||
animated-style (reanimated/apply-animations-to-style
|
||||
(if opacity-anim
|
||||
{:opacity opacity-anim}
|
||||
{})
|
||||
original-style)]
|
||||
[reanimated/view {:style animated-style}
|
||||
;; Left Section
|
||||
[rn/view {:style {:flex 1}}
|
||||
[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}}
|
||||
[rn/view
|
||||
{:style {:position :absolute
|
||||
:flex-direction :row
|
||||
:right 0}}
|
||||
[dynamic-button-view :mention dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :scroll-to-bottom dynamic-buttons {:margin-left 8}]]]]))]))
|
||||
[:f> f-floating-shell-button dynamic-buttons style opacity-anim]))
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[quo2.components.profile.profile-card.style :as style]
|
||||
[quo2.components.avatars.user-avatar.view :as user-avatar]))
|
||||
|
||||
(defn- profile-card-component
|
||||
(defn- f-profile-card-component
|
||||
[{:keys [keycard-account? profile-picture name hash
|
||||
customization-color emoji-hash on-options-press
|
||||
show-emoji-hash? show-options-button? show-user-hash?
|
||||
@@ -25,7 +25,7 @@
|
||||
last-item? false
|
||||
card-style {:padding-horizontal 20
|
||||
:flex 1}}}]
|
||||
(let [{:keys [width]} (rn/use-window-dimensions)
|
||||
(let [{:keys [width]} (rn/get-window)
|
||||
padding-bottom (cond
|
||||
login-card? 38
|
||||
show-emoji-hash? 12
|
||||
@@ -104,4 +104,4 @@
|
||||
|
||||
(defn profile-card
|
||||
[props]
|
||||
[:f> profile-card-component props])
|
||||
[:f> f-profile-card-component props])
|
||||
|
||||
@@ -6,85 +6,83 @@
|
||||
[react-native.core :refer [use-effect]]
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn delete-button
|
||||
(defn f-delete-button
|
||||
[recording? ready-to-delete? reviewing-audio? force-show-controls?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||
translate-x (reanimated/use-shared-value (if force-show-controls? 35 20))
|
||||
scale (reanimated/use-shared-value (if force-show-controls? 0.75 1))
|
||||
connector-opacity (reanimated/use-shared-value 0)
|
||||
connector-width (reanimated/use-shared-value 24)
|
||||
connector-height (reanimated/use-shared-value 12)
|
||||
border-radius-first-half (reanimated/use-shared-value 8)
|
||||
border-radius-second-half (reanimated/use-shared-value 8)
|
||||
start-x-animation (fn []
|
||||
(helpers/animate-linear-with-delay translate-x 12 50 133.33)
|
||||
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
|
||||
(helpers/animate-easing-with-delay connector-width 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay connector-height 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay border-radius-first-half
|
||||
28
|
||||
83.33
|
||||
80)
|
||||
(helpers/animate-easing-with-delay border-radius-second-half
|
||||
28
|
||||
83.33
|
||||
80))
|
||||
reset-x-animation (fn []
|
||||
(helpers/animate-linear translate-x 0 100)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value connector-width 24)
|
||||
(helpers/set-value connector-height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-in-animation (fn []
|
||||
(helpers/animate-linear translate-x 0 200)
|
||||
(helpers/animate-linear opacity 1 200))
|
||||
fade-out-animation (fn []
|
||||
(helpers/animate-linear
|
||||
translate-x
|
||||
(if @reviewing-audio? 35 20)
|
||||
200)
|
||||
(if @reviewing-audio?
|
||||
(helpers/animate-linear scale 0.75 200)
|
||||
(helpers/animate-linear opacity 0 200))
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value connector-width 24)
|
||||
(helpers/set-value connector-height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-out-reset-animation (fn []
|
||||
(helpers/animate-linear opacity 0 200)
|
||||
(helpers/animate-linear-with-delay translate-x 20 0 200)
|
||||
(helpers/animate-linear-with-delay scale 1 0 200))]
|
||||
(use-effect (fn []
|
||||
(if @recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(when-not @reviewing-audio?
|
||||
(fade-out-reset-animation)))
|
||||
[@reviewing-audio?])
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@ready-to-delete?
|
||||
(start-x-animation)
|
||||
@recording?
|
||||
(reset-x-animation)))
|
||||
[@ready-to-delete?])
|
||||
[:<>
|
||||
[reanimated/view {:style (style/delete-button-container opacity)}
|
||||
[reanimated/view
|
||||
{:style (style/delete-button-connector connector-opacity
|
||||
connector-width
|
||||
connector-height
|
||||
border-radius-first-half
|
||||
border-radius-second-half)}]]
|
||||
[reanimated/view
|
||||
{:style (style/delete-button scale translate-x opacity)
|
||||
:pointer-events :none}
|
||||
[icons/icon :i/delete
|
||||
{:color colors/white
|
||||
:size 20}]]]))])
|
||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||
translate-x (reanimated/use-shared-value (if force-show-controls? 35 20))
|
||||
scale (reanimated/use-shared-value (if force-show-controls? 0.75 1))
|
||||
connector-opacity (reanimated/use-shared-value 0)
|
||||
connector-width (reanimated/use-shared-value 24)
|
||||
connector-height (reanimated/use-shared-value 12)
|
||||
border-radius-first-half (reanimated/use-shared-value 8)
|
||||
border-radius-second-half (reanimated/use-shared-value 8)
|
||||
start-x-animation (fn []
|
||||
(helpers/animate-linear-with-delay translate-x 12 50 133.33)
|
||||
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
|
||||
(helpers/animate-easing-with-delay connector-width 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay connector-height 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay border-radius-first-half
|
||||
28
|
||||
83.33
|
||||
80)
|
||||
(helpers/animate-easing-with-delay border-radius-second-half
|
||||
28
|
||||
83.33
|
||||
80))
|
||||
reset-x-animation (fn []
|
||||
(helpers/animate-linear translate-x 0 100)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value connector-width 24)
|
||||
(helpers/set-value connector-height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-in-animation (fn []
|
||||
(helpers/animate-linear translate-x 0 200)
|
||||
(helpers/animate-linear opacity 1 200))
|
||||
fade-out-animation (fn []
|
||||
(helpers/animate-linear
|
||||
translate-x
|
||||
(if @reviewing-audio? 35 20)
|
||||
200)
|
||||
(if @reviewing-audio?
|
||||
(helpers/animate-linear scale 0.75 200)
|
||||
(helpers/animate-linear opacity 0 200))
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value connector-width 24)
|
||||
(helpers/set-value connector-height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-out-reset-animation (fn []
|
||||
(helpers/animate-linear opacity 0 200)
|
||||
(helpers/animate-linear-with-delay translate-x 20 0 200)
|
||||
(helpers/animate-linear-with-delay scale 1 0 200))]
|
||||
(use-effect (fn []
|
||||
(if @recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(when-not @reviewing-audio?
|
||||
(fade-out-reset-animation)))
|
||||
[@reviewing-audio?])
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@ready-to-delete?
|
||||
(start-x-animation)
|
||||
@recording?
|
||||
(reset-x-animation)))
|
||||
[@ready-to-delete?])
|
||||
[:<>
|
||||
[reanimated/view {:style (style/delete-button-container opacity)}
|
||||
[reanimated/view
|
||||
{:style (style/delete-button-connector connector-opacity
|
||||
connector-width
|
||||
connector-height
|
||||
border-radius-first-half
|
||||
border-radius-second-half)}]]
|
||||
[reanimated/view
|
||||
{:style (style/delete-button scale translate-x opacity)
|
||||
:pointer-events :none}
|
||||
[icons/icon :i/delete
|
||||
{:color colors/white
|
||||
:size 20}]]]))
|
||||
|
||||
@@ -6,76 +6,74 @@
|
||||
[react-native.core :refer [use-effect]]
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn lock-button
|
||||
(defn f-lock-button
|
||||
[recording? ready-to-lock? locked?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [translate-x-y (reanimated/use-shared-value 20)
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
connector-opacity (reanimated/use-shared-value 0)
|
||||
width (reanimated/use-shared-value 24)
|
||||
height (reanimated/use-shared-value 12)
|
||||
border-radius-first-half (reanimated/use-shared-value 8)
|
||||
border-radius-second-half (reanimated/use-shared-value 8)
|
||||
start-x-y-animation (fn []
|
||||
(helpers/animate-linear-with-delay translate-x-y 8 50 116.66)
|
||||
(helpers/animate-easing-with-delay connector-opacity 1 0 80)
|
||||
(helpers/animate-easing-with-delay width 56 83.33 63.33)
|
||||
(helpers/animate-easing-with-delay height 56 83.33 63.33)
|
||||
(helpers/animate-easing-with-delay border-radius-first-half
|
||||
28
|
||||
83.33
|
||||
63.33)
|
||||
(helpers/animate-easing-with-delay border-radius-second-half
|
||||
28
|
||||
83.33
|
||||
63.33))
|
||||
reset-x-y-animation (fn []
|
||||
(helpers/animate-linear translate-x-y 0 100)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-in-animation (fn []
|
||||
(helpers/animate-linear translate-x-y 0 220)
|
||||
(helpers/animate-linear opacity 1 220))
|
||||
fade-out-animation (fn []
|
||||
(helpers/animate-linear translate-x-y 20 200)
|
||||
(helpers/animate-linear opacity 0 200)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))]
|
||||
(use-effect (fn []
|
||||
(if @recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@ready-to-lock?
|
||||
(start-x-y-animation)
|
||||
(and @recording? (not @locked?))
|
||||
(reset-x-y-animation)))
|
||||
[@ready-to-lock?])
|
||||
(use-effect (fn []
|
||||
(if @locked?
|
||||
(fade-out-animation)
|
||||
(reset-x-y-animation)))
|
||||
[@locked?])
|
||||
[:<>
|
||||
[reanimated/view {:style (style/lock-button-container opacity)}
|
||||
[reanimated/view
|
||||
{:style (style/lock-button-connector connector-opacity
|
||||
width
|
||||
height
|
||||
border-radius-first-half
|
||||
border-radius-second-half)}]]
|
||||
[reanimated/view
|
||||
{:style (style/lock-button translate-x-y opacity)
|
||||
:pointer-events :none}
|
||||
[icons/icon (if @ready-to-lock? :i/locked :i/unlocked)
|
||||
{:color (colors/theme-colors colors/black colors/white)
|
||||
:size 20}]]]))])
|
||||
(let [translate-x-y (reanimated/use-shared-value 20)
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
connector-opacity (reanimated/use-shared-value 0)
|
||||
width (reanimated/use-shared-value 24)
|
||||
height (reanimated/use-shared-value 12)
|
||||
border-radius-first-half (reanimated/use-shared-value 8)
|
||||
border-radius-second-half (reanimated/use-shared-value 8)
|
||||
start-x-y-animation (fn []
|
||||
(helpers/animate-linear-with-delay translate-x-y 8 50 116.66)
|
||||
(helpers/animate-easing-with-delay connector-opacity 1 0 80)
|
||||
(helpers/animate-easing-with-delay width 56 83.33 63.33)
|
||||
(helpers/animate-easing-with-delay height 56 83.33 63.33)
|
||||
(helpers/animate-easing-with-delay border-radius-first-half
|
||||
28
|
||||
83.33
|
||||
63.33)
|
||||
(helpers/animate-easing-with-delay border-radius-second-half
|
||||
28
|
||||
83.33
|
||||
63.33))
|
||||
reset-x-y-animation (fn []
|
||||
(helpers/animate-linear translate-x-y 0 100)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-in-animation (fn []
|
||||
(helpers/animate-linear translate-x-y 0 220)
|
||||
(helpers/animate-linear opacity 1 220))
|
||||
fade-out-animation (fn []
|
||||
(helpers/animate-linear translate-x-y 20 200)
|
||||
(helpers/animate-linear opacity 0 200)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))]
|
||||
(use-effect (fn []
|
||||
(if @recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@ready-to-lock?
|
||||
(start-x-y-animation)
|
||||
(and @recording? (not @locked?))
|
||||
(reset-x-y-animation)))
|
||||
[@ready-to-lock?])
|
||||
(use-effect (fn []
|
||||
(if @locked?
|
||||
(fade-out-animation)
|
||||
(reset-x-y-animation)))
|
||||
[@locked?])
|
||||
[:<>
|
||||
[reanimated/view {:style (style/lock-button-container opacity)}
|
||||
[reanimated/view
|
||||
{:style (style/lock-button-connector connector-opacity
|
||||
width
|
||||
height
|
||||
border-radius-first-half
|
||||
border-radius-second-half)}]]
|
||||
[reanimated/view
|
||||
{:style (style/lock-button translate-x-y opacity)
|
||||
:pointer-events :none}
|
||||
[icons/icon (if @ready-to-lock? :i/locked :i/unlocked)
|
||||
{:color (colors/theme-colors colors/black colors/white)
|
||||
:size 20}]]]))
|
||||
|
||||
@@ -7,22 +7,20 @@
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn record-button
|
||||
(defn f-record-button
|
||||
[recording? reviewing-audio?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [opacity (reanimated/use-shared-value 1)
|
||||
show-animation #(helpers/set-value opacity 1)
|
||||
hide-animation #(helpers/set-value opacity 0)]
|
||||
(use-effect (fn []
|
||||
(if (or @recording? @reviewing-audio?)
|
||||
(hide-animation)
|
||||
(show-animation)))
|
||||
[@recording? @reviewing-audio?])
|
||||
[reanimated/view {:style (style/record-button-container opacity)}
|
||||
[button/button
|
||||
{:type :outline
|
||||
:size 32
|
||||
:width 32
|
||||
:accessibility-label :mic-button}
|
||||
[icons/icon :i/audio {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]]))])
|
||||
(let [opacity (reanimated/use-shared-value 1)
|
||||
show-animation #(helpers/set-value opacity 1)
|
||||
hide-animation #(helpers/set-value opacity 0)]
|
||||
(use-effect (fn []
|
||||
(if (or @recording? @reviewing-audio?)
|
||||
(hide-animation)
|
||||
(show-animation)))
|
||||
[@recording? @reviewing-audio?])
|
||||
[reanimated/view {:style (style/record-button-container opacity)}
|
||||
[button/button
|
||||
{:type :outline
|
||||
:size 32
|
||||
:width 32
|
||||
:accessibility-label :mic-button}
|
||||
[icons/icon :i/audio {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]]))
|
||||
|
||||
@@ -27,177 +27,175 @@
|
||||
(reagent/as-element
|
||||
[reanimated/view {:style (style/animated-circle scale opacity color)}]))))))
|
||||
|
||||
(defn record-button-big
|
||||
(defn f-record-button-big
|
||||
[recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating?
|
||||
record-button-at-initial-position? locked? reviewing-audio? recording-timer recording-length-ms
|
||||
clear-timeout touch-active? recorder-ref reload-recorder-fn idle? on-send on-cancel]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [scale (reanimated/use-shared-value 1)
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
opacity-from (if @ready-to-lock? opacity-from-lock opacity-from-default)
|
||||
animations (map
|
||||
(fn [index]
|
||||
(let [ring-scale (worklets.record-audio/ring-scale scale
|
||||
(* scale-padding
|
||||
index))]
|
||||
{:scale ring-scale
|
||||
:opacity (reanimated/interpolate ring-scale
|
||||
[1 scale-to-each]
|
||||
[opacity-from 0])}))
|
||||
(range 0 5))
|
||||
rings-color (cond
|
||||
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
|
||||
colors/neutral-80)
|
||||
@ready-to-delete? colors/danger-50
|
||||
:else colors/primary-50)
|
||||
translate-y (reanimated/use-shared-value 0)
|
||||
translate-x (reanimated/use-shared-value 0)
|
||||
button-color colors/primary-50
|
||||
icon-color (if (and (not (colors/dark?)) @ready-to-lock?) colors/black colors/white)
|
||||
icon-opacity (reanimated/use-shared-value 1)
|
||||
red-overlay-opacity (reanimated/use-shared-value 0)
|
||||
gray-overlay-opacity (reanimated/use-shared-value 0)
|
||||
complete-animation
|
||||
(fn []
|
||||
(cond
|
||||
(and @ready-to-lock? (not @record-button-is-animating?))
|
||||
(do
|
||||
(reset! locked? true)
|
||||
(reset! ready-to-lock? false))
|
||||
(and (not @locked?) (not @reviewing-audio?))
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(fn []
|
||||
(let [scale (reanimated/use-shared-value 1)
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
opacity-from (if @ready-to-lock? opacity-from-lock opacity-from-default)
|
||||
animations (map
|
||||
(fn [index]
|
||||
(let [ring-scale (worklets.record-audio/ring-scale scale
|
||||
(* scale-padding
|
||||
index))]
|
||||
{:scale ring-scale
|
||||
:opacity (reanimated/interpolate ring-scale
|
||||
[1 scale-to-each]
|
||||
[opacity-from 0])}))
|
||||
(range 0 5))
|
||||
rings-color (cond
|
||||
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
|
||||
colors/neutral-80)
|
||||
@ready-to-delete? colors/danger-50
|
||||
:else colors/primary-50)
|
||||
translate-y (reanimated/use-shared-value 0)
|
||||
translate-x (reanimated/use-shared-value 0)
|
||||
button-color colors/primary-50
|
||||
icon-color (if (and (not (colors/dark?)) @ready-to-lock?) colors/black colors/white)
|
||||
icon-opacity (reanimated/use-shared-value 1)
|
||||
red-overlay-opacity (reanimated/use-shared-value 0)
|
||||
gray-overlay-opacity (reanimated/use-shared-value 0)
|
||||
complete-animation
|
||||
(fn []
|
||||
(cond
|
||||
(and @ready-to-lock? (not @record-button-is-animating?))
|
||||
(do
|
||||
(reset! locked? true)
|
||||
(reset! ready-to-lock? false))
|
||||
(and (not @locked?) (not @reviewing-audio?))
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(fn []
|
||||
(cond
|
||||
@ready-to-send?
|
||||
(when on-send
|
||||
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
|
||||
:duration @recording-length-ms}))
|
||||
@ready-to-delete?
|
||||
(when on-cancel
|
||||
(on-cancel)))
|
||||
(reload-recorder-fn)
|
||||
(reset! recording? false)
|
||||
(reset! ready-to-send? false)
|
||||
(reset! ready-to-delete? false)
|
||||
(reset! ready-to-lock? false)
|
||||
(reset! idle? true)
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(js/clearInterval @recording-timer)
|
||||
(reset! recording-length-ms 0)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))))
|
||||
start-animation (fn []
|
||||
(helpers/set-value opacity 1)
|
||||
(helpers/animate-linear scale 2.6 signal-anim-duration)
|
||||
;; TODO: Research if we can implement this with withSequence method
|
||||
;; from Reanimated 2
|
||||
;; GitHub issue [#14561]:
|
||||
;; https://github.com/status-im/status-mobile/issues/14561
|
||||
(reset! clear-timeout
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(helpers/set-value scale scale-to-each)
|
||||
(helpers/animate-linear-with-delay-loop scale
|
||||
scale-to-total
|
||||
signal-anim-duration-2
|
||||
0))
|
||||
signal-anim-duration)))
|
||||
stop-animation (fn []
|
||||
(helpers/set-value opacity 0)
|
||||
(reanimated/cancel-animation scale)
|
||||
(helpers/set-value scale 1)
|
||||
(when @clear-timeout (js/clearTimeout @clear-timeout)))
|
||||
start-y-animation (fn []
|
||||
(reset! record-button-at-initial-position? false)
|
||||
(reset! record-button-is-animating? true)
|
||||
(helpers/animate-easing translate-y -64 250)
|
||||
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-is-animating? false)
|
||||
(when-not @touch-active? (complete-animation)))
|
||||
250))
|
||||
reset-y-animation (fn []
|
||||
(helpers/animate-easing translate-y 0 300)
|
||||
(helpers/animate-linear icon-opacity 1 500)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-at-initial-position? true))
|
||||
500))
|
||||
start-x-animation (fn []
|
||||
(reset! record-button-at-initial-position? false)
|
||||
(reset! record-button-is-animating? true)
|
||||
(helpers/animate-easing translate-x -64 250)
|
||||
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
|
||||
(helpers/animate-linear red-overlay-opacity 1 33.33)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-is-animating? false)
|
||||
(when-not @touch-active? (complete-animation)))
|
||||
250))
|
||||
reset-x-animation (fn []
|
||||
(helpers/animate-easing translate-x 0 300)
|
||||
(helpers/animate-linear icon-opacity 1 500)
|
||||
(helpers/animate-linear red-overlay-opacity 0 100)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-at-initial-position? true))
|
||||
500))
|
||||
start-x-y-animation (fn []
|
||||
(reset! record-button-at-initial-position? false)
|
||||
(reset! record-button-is-animating? true)
|
||||
(helpers/animate-easing translate-y -44 200)
|
||||
(helpers/animate-easing translate-x -44 200)
|
||||
(helpers/animate-linear-with-delay icon-opacity 0 33.33 33.33)
|
||||
(helpers/animate-linear gray-overlay-opacity 1 33.33)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-is-animating? false)
|
||||
(when-not @touch-active? (complete-animation)))
|
||||
200))
|
||||
reset-x-y-animation (fn []
|
||||
(helpers/animate-easing translate-y 0 300)
|
||||
(helpers/animate-easing translate-x 0 300)
|
||||
(helpers/animate-linear icon-opacity 1 500)
|
||||
(helpers/animate-linear gray-overlay-opacity 0 800)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-at-initial-position? true))
|
||||
800))]
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@ready-to-send?
|
||||
(when on-send
|
||||
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
|
||||
:duration @recording-length-ms}))
|
||||
@ready-to-delete?
|
||||
(when on-cancel
|
||||
(on-cancel)))
|
||||
(reload-recorder-fn)
|
||||
(reset! recording? false)
|
||||
(reset! ready-to-send? false)
|
||||
(reset! ready-to-delete? false)
|
||||
(reset! ready-to-lock? false)
|
||||
(reset! idle? true)
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(js/clearInterval @recording-timer)
|
||||
(reset! recording-length-ms 0)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))))
|
||||
start-animation (fn []
|
||||
(helpers/set-value opacity 1)
|
||||
(helpers/animate-linear scale 2.6 signal-anim-duration)
|
||||
;; TODO: Research if we can implement this with withSequence method
|
||||
;; from Reanimated 2
|
||||
;; GitHub issue [#14561]:
|
||||
;; https://github.com/status-im/status-mobile/issues/14561
|
||||
(reset! clear-timeout
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(helpers/set-value scale scale-to-each)
|
||||
(helpers/animate-linear-with-delay-loop scale
|
||||
scale-to-total
|
||||
signal-anim-duration-2
|
||||
0))
|
||||
signal-anim-duration)))
|
||||
stop-animation (fn []
|
||||
(helpers/set-value opacity 0)
|
||||
(reanimated/cancel-animation scale)
|
||||
(helpers/set-value scale 1)
|
||||
(when @clear-timeout (js/clearTimeout @clear-timeout)))
|
||||
start-y-animation (fn []
|
||||
(reset! record-button-at-initial-position? false)
|
||||
(reset! record-button-is-animating? true)
|
||||
(helpers/animate-easing translate-y -64 250)
|
||||
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-is-animating? false)
|
||||
(when-not @touch-active? (complete-animation)))
|
||||
250))
|
||||
reset-y-animation (fn []
|
||||
(helpers/animate-easing translate-y 0 300)
|
||||
(helpers/animate-linear icon-opacity 1 500)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-at-initial-position? true))
|
||||
500))
|
||||
start-x-animation (fn []
|
||||
(reset! record-button-at-initial-position? false)
|
||||
(reset! record-button-is-animating? true)
|
||||
(helpers/animate-easing translate-x -64 250)
|
||||
(helpers/animate-linear-with-delay icon-opacity 0 33.33 76.66)
|
||||
(helpers/animate-linear red-overlay-opacity 1 33.33)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-is-animating? false)
|
||||
(when-not @touch-active? (complete-animation)))
|
||||
250))
|
||||
reset-x-animation (fn []
|
||||
(helpers/animate-easing translate-x 0 300)
|
||||
(helpers/animate-linear icon-opacity 1 500)
|
||||
(helpers/animate-linear red-overlay-opacity 0 100)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-at-initial-position? true))
|
||||
500))
|
||||
start-x-y-animation (fn []
|
||||
(reset! record-button-at-initial-position? false)
|
||||
(reset! record-button-is-animating? true)
|
||||
(helpers/animate-easing translate-y -44 200)
|
||||
(helpers/animate-easing translate-x -44 200)
|
||||
(helpers/animate-linear-with-delay icon-opacity 0 33.33 33.33)
|
||||
(helpers/animate-linear gray-overlay-opacity 1 33.33)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-is-animating? false)
|
||||
(when-not @touch-active? (complete-animation)))
|
||||
200))
|
||||
reset-x-y-animation (fn []
|
||||
(helpers/animate-easing translate-y 0 300)
|
||||
(helpers/animate-easing translate-x 0 300)
|
||||
(helpers/animate-linear icon-opacity 1 500)
|
||||
(helpers/animate-linear gray-overlay-opacity 0 800)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-at-initial-position? true))
|
||||
800))]
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@recording?
|
||||
(start-animation)
|
||||
(not @ready-to-lock?)
|
||||
(stop-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(if @ready-to-lock?
|
||||
(start-x-y-animation)
|
||||
(reset-x-y-animation)))
|
||||
[@ready-to-lock?])
|
||||
(use-effect (fn []
|
||||
(if @ready-to-send?
|
||||
(start-y-animation)
|
||||
(reset-y-animation)))
|
||||
[@ready-to-send?])
|
||||
(use-effect (fn []
|
||||
(if @ready-to-delete?
|
||||
(start-x-animation)
|
||||
(reset-x-animation)))
|
||||
[@ready-to-delete?])
|
||||
[reanimated/view
|
||||
{:style (style/record-button-big-container translate-x translate-y opacity)
|
||||
:pointer-events :none}
|
||||
[:<>
|
||||
(map-indexed
|
||||
(fn [id animation]
|
||||
^{:key id}
|
||||
[animated-ring
|
||||
{:scale (:scale animation)
|
||||
:opacity (:opacity animation)
|
||||
:color rings-color}])
|
||||
animations)]
|
||||
[rn/view {:style (style/record-button-big-body button-color)}
|
||||
[reanimated/view {:style (style/record-button-big-red-overlay red-overlay-opacity)}]
|
||||
[reanimated/view {:style (style/record-button-big-gray-overlay gray-overlay-opacity)}]
|
||||
[reanimated/view {:style (style/record-button-big-icon-container icon-opacity)}
|
||||
(if @locked?
|
||||
[rn/view {:style style/stop-icon}]
|
||||
[icons/icon :i/audio {:color icon-color}])]]]))])
|
||||
@recording?
|
||||
(start-animation)
|
||||
(not @ready-to-lock?)
|
||||
(stop-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(if @ready-to-lock?
|
||||
(start-x-y-animation)
|
||||
(reset-x-y-animation)))
|
||||
[@ready-to-lock?])
|
||||
(use-effect (fn []
|
||||
(if @ready-to-send?
|
||||
(start-y-animation)
|
||||
(reset-y-animation)))
|
||||
[@ready-to-send?])
|
||||
(use-effect (fn []
|
||||
(if @ready-to-delete?
|
||||
(start-x-animation)
|
||||
(reset-x-animation)))
|
||||
[@ready-to-delete?])
|
||||
[reanimated/view
|
||||
{:style (style/record-button-big-container translate-x translate-y opacity)
|
||||
:pointer-events :none}
|
||||
[:<>
|
||||
(map-indexed
|
||||
(fn [id animation]
|
||||
^{:key id}
|
||||
[animated-ring
|
||||
{:scale (:scale animation)
|
||||
:opacity (:opacity animation)
|
||||
:color rings-color}])
|
||||
animations)]
|
||||
[rn/view {:style (style/record-button-big-body button-color)}
|
||||
[reanimated/view {:style (style/record-button-big-red-overlay red-overlay-opacity)}]
|
||||
[reanimated/view {:style (style/record-button-big-gray-overlay gray-overlay-opacity)}]
|
||||
[reanimated/view {:style (style/record-button-big-icon-container icon-opacity)}
|
||||
(if @locked?
|
||||
[rn/view {:style style/stop-icon}]
|
||||
[icons/icon :i/audio {:color icon-color}])]]]))
|
||||
|
||||
@@ -6,88 +6,86 @@
|
||||
[react-native.core :refer [use-effect]]
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn send-button
|
||||
(defn f-send-button
|
||||
[recording? ready-to-send? reviewing-audio? force-show-controls?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||
translate-y (reanimated/use-shared-value (if force-show-controls? 76 20))
|
||||
connector-opacity (reanimated/use-shared-value 0)
|
||||
width (reanimated/use-shared-value 12)
|
||||
height (reanimated/use-shared-value 24)
|
||||
border-radius-first-half (reanimated/use-shared-value 16)
|
||||
border-radius-second-half (reanimated/use-shared-value 8)
|
||||
start-y-animation (fn []
|
||||
(helpers/animate-linear-with-delay translate-y 12 50 133.33)
|
||||
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
|
||||
(helpers/animate-easing-with-delay width 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay height 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay border-radius-first-half
|
||||
28
|
||||
83.33
|
||||
80)
|
||||
(helpers/animate-easing-with-delay border-radius-second-half
|
||||
28
|
||||
83.33
|
||||
80))
|
||||
reset-y-animation (fn []
|
||||
(helpers/animate-linear translate-y 0 100)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 12)
|
||||
(helpers/set-value height 24)
|
||||
(helpers/set-value border-radius-first-half 16)
|
||||
(helpers/set-value border-radius-second-half 8))
|
||||
fade-in-animation (fn []
|
||||
(helpers/animate-linear translate-y 0 200)
|
||||
(helpers/animate-linear opacity 1 200))
|
||||
fade-out-animation (fn []
|
||||
(when-not force-show-controls?
|
||||
(helpers/animate-linear
|
||||
translate-y
|
||||
(if @reviewing-audio? 76 20)
|
||||
200))
|
||||
(when-not @reviewing-audio?
|
||||
(helpers/animate-linear opacity 0 200))
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-out-reset-animation (fn []
|
||||
(helpers/animate-linear opacity 0 200)
|
||||
(helpers/animate-linear-with-delay translate-y 20 0 200)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))]
|
||||
(use-effect (fn []
|
||||
(if @recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(when-not @reviewing-audio?
|
||||
(fade-out-reset-animation)))
|
||||
[@reviewing-audio?])
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@ready-to-send?
|
||||
(start-y-animation)
|
||||
@recording? (reset-y-animation)))
|
||||
[@ready-to-send?])
|
||||
[:<>
|
||||
[reanimated/view {:style (style/send-button-container opacity)}
|
||||
[reanimated/view
|
||||
{:style (style/send-button-connector connector-opacity
|
||||
width
|
||||
height
|
||||
border-radius-first-half
|
||||
border-radius-second-half)}]]
|
||||
[reanimated/view
|
||||
{:style (style/send-button translate-y opacity)
|
||||
:pointer-events :none}
|
||||
[icons/icon :i/arrow-up
|
||||
{:color colors/white
|
||||
:size 20
|
||||
:container-style style/send-icon-container}]]]))])
|
||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||
translate-y (reanimated/use-shared-value (if force-show-controls? 76 20))
|
||||
connector-opacity (reanimated/use-shared-value 0)
|
||||
width (reanimated/use-shared-value 12)
|
||||
height (reanimated/use-shared-value 24)
|
||||
border-radius-first-half (reanimated/use-shared-value 16)
|
||||
border-radius-second-half (reanimated/use-shared-value 8)
|
||||
start-y-animation (fn []
|
||||
(helpers/animate-linear-with-delay translate-y 12 50 133.33)
|
||||
(helpers/animate-easing-with-delay connector-opacity 1 0 93.33)
|
||||
(helpers/animate-easing-with-delay width 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay height 56 83.33 80)
|
||||
(helpers/animate-easing-with-delay border-radius-first-half
|
||||
28
|
||||
83.33
|
||||
80)
|
||||
(helpers/animate-easing-with-delay border-radius-second-half
|
||||
28
|
||||
83.33
|
||||
80))
|
||||
reset-y-animation (fn []
|
||||
(helpers/animate-linear translate-y 0 100)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 12)
|
||||
(helpers/set-value height 24)
|
||||
(helpers/set-value border-radius-first-half 16)
|
||||
(helpers/set-value border-radius-second-half 8))
|
||||
fade-in-animation (fn []
|
||||
(helpers/animate-linear translate-y 0 200)
|
||||
(helpers/animate-linear opacity 1 200))
|
||||
fade-out-animation (fn []
|
||||
(when-not force-show-controls?
|
||||
(helpers/animate-linear
|
||||
translate-y
|
||||
(if @reviewing-audio? 76 20)
|
||||
200))
|
||||
(when-not @reviewing-audio?
|
||||
(helpers/animate-linear opacity 0 200))
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))
|
||||
fade-out-reset-animation (fn []
|
||||
(helpers/animate-linear opacity 0 200)
|
||||
(helpers/animate-linear-with-delay translate-y 20 0 200)
|
||||
(helpers/set-value connector-opacity 0)
|
||||
(helpers/set-value width 24)
|
||||
(helpers/set-value height 12)
|
||||
(helpers/set-value border-radius-first-half 8)
|
||||
(helpers/set-value border-radius-second-half 16))]
|
||||
(use-effect (fn []
|
||||
(if @recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[@recording?])
|
||||
(use-effect (fn []
|
||||
(when-not @reviewing-audio?
|
||||
(fade-out-reset-animation)))
|
||||
[@reviewing-audio?])
|
||||
(use-effect (fn []
|
||||
(cond
|
||||
@ready-to-send?
|
||||
(start-y-animation)
|
||||
@recording? (reset-y-animation)))
|
||||
[@ready-to-send?])
|
||||
[:<>
|
||||
[reanimated/view {:style (style/send-button-container opacity)}
|
||||
[reanimated/view
|
||||
{:style (style/send-button-connector connector-opacity
|
||||
width
|
||||
height
|
||||
border-radius-first-half
|
||||
border-radius-second-half)}]]
|
||||
[reanimated/view
|
||||
{:style (style/send-button translate-y opacity)
|
||||
:pointer-events :none}
|
||||
[icons/icon :i/arrow-up
|
||||
{:color colors/white
|
||||
:size 20
|
||||
:container-style style/send-icon-container}]]]))
|
||||
|
||||
@@ -86,71 +86,68 @@
|
||||
(or ignore-min-y? (>= location-y y))
|
||||
(or ignore-max-y? (<= location-y max-y))))))
|
||||
|
||||
(defn- recording-bar
|
||||
(defn- f-recording-bar
|
||||
[recording-length-ms ready-to-delete?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [fill-percentage (/ (* recording-length-ms 100) max-audio-duration-ms)]
|
||||
[rn/view {:style (style/recording-bar-container)}
|
||||
[rn/view {:style (style/recording-bar fill-percentage ready-to-delete?)}]]))])
|
||||
(let [fill-percentage (/ (* recording-length-ms 100) max-audio-duration-ms)]
|
||||
[rn/view {:style (style/recording-bar-container)}
|
||||
[rn/view {:style (style/recording-bar fill-percentage ready-to-delete?)}]]))
|
||||
|
||||
(defn- time-counter
|
||||
(defn- f-time-counter
|
||||
[recording? recording-length-ms ready-to-delete? reviewing-audio? audio-current-time-ms]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [s (quot (if recording? recording-length-ms audio-current-time-ms) 1000)
|
||||
time-str (gstring/format "%02d:%02d" (quot s 60) (mod s 60))]
|
||||
[rn/view {:style (style/timer-container reviewing-audio?)}
|
||||
(when-not reviewing-audio?
|
||||
[rn/view {:style (style/timer-circle)}])
|
||||
[text/text
|
||||
(merge
|
||||
{:size :label
|
||||
:weight :semi-bold}
|
||||
(when ready-to-delete?
|
||||
{:style (style/timer-text)}))
|
||||
time-str]]))])
|
||||
(let [s (quot (if recording? recording-length-ms audio-current-time-ms) 1000)
|
||||
time-str (gstring/format "%02d:%02d" (quot s 60) (mod s 60))]
|
||||
[rn/view {:style (style/timer-container reviewing-audio?)}
|
||||
(when-not reviewing-audio?
|
||||
[rn/view {:style (style/timer-circle)}])
|
||||
[text/text
|
||||
(merge
|
||||
{:size :label
|
||||
:weight :semi-bold}
|
||||
(when ready-to-delete?
|
||||
{:style (style/timer-text)}))
|
||||
time-str]]))
|
||||
|
||||
(defn- play-button
|
||||
(defn- f-play-button
|
||||
[playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [on-play (fn []
|
||||
(reset! playing-audio? true)
|
||||
(reset! playing-timer
|
||||
(js/setInterval
|
||||
(fn []
|
||||
(let [current-time (audio/get-player-current-time @player-ref)
|
||||
player-state (audio/get-state @player-ref)
|
||||
playing? (= player-state audio/PLAYING)]
|
||||
(when (and playing? (not @seeking-audio?) (> current-time 0))
|
||||
(reset! audio-current-time-ms current-time))))
|
||||
100)))
|
||||
on-pause (fn []
|
||||
(reset! playing-audio? false)
|
||||
(when @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil))
|
||||
(log/debug "[record-audio] toggle play / pause - success"))
|
||||
on-press (fn []
|
||||
(audio/toggle-playpause-player
|
||||
@player-ref
|
||||
on-play
|
||||
on-pause
|
||||
#(log/error "[record-audio] toggle play / pause - error: " %)))]
|
||||
[rn/touchable-opacity
|
||||
{:style (style/play-button)
|
||||
:on-press on-press}
|
||||
[icons/icon
|
||||
(if @playing-audio? :i/pause :i/play)
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]))])
|
||||
(let [on-play (fn []
|
||||
(reset! playing-audio? true)
|
||||
(reset! playing-timer
|
||||
(js/setInterval
|
||||
(fn []
|
||||
(let [current-time (audio/get-player-current-time @player-ref)
|
||||
player-state (audio/get-state @player-ref)
|
||||
playing? (= player-state audio/PLAYING)]
|
||||
(when (and playing? (not @seeking-audio?) (> current-time 0))
|
||||
(reset! audio-current-time-ms current-time))))
|
||||
100)))
|
||||
on-pause (fn []
|
||||
(reset! playing-audio? false)
|
||||
(when @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil))
|
||||
(log/debug "[record-audio] toggle play / pause - success"))
|
||||
on-press (fn []
|
||||
(audio/toggle-playpause-player
|
||||
@player-ref
|
||||
on-play
|
||||
on-pause
|
||||
#(log/error "[record-audio] toggle play / pause - error: " %)))]
|
||||
[rn/touchable-opacity
|
||||
{:style (style/play-button)
|
||||
:on-press on-press}
|
||||
[icons/icon
|
||||
(if @playing-audio? :i/pause :i/play)
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]))
|
||||
|
||||
(defn view
|
||||
(defn record-audio
|
||||
[{:keys [on-init on-start-recording on-send on-cancel on-reviewing-audio
|
||||
record-audio-permission-granted
|
||||
on-request-record-audio-permission on-check-audio-permissions
|
||||
audio-file]}]
|
||||
[:f>
|
||||
;; TODO we need to refactor this, and use :f> with defined function, currenly state is reseted each
|
||||
;; time parent component
|
||||
;; is re-rendered
|
||||
(fn []
|
||||
(let [recording? (reagent/atom false)
|
||||
locked? (reagent/atom false)
|
||||
@@ -524,16 +521,17 @@
|
||||
:pointer-events :box-none}
|
||||
(when @reviewing-audio?
|
||||
[:<>
|
||||
[play-button playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio?]
|
||||
[soundtrack/soundtrack
|
||||
[:f> f-play-button playing-audio? player-ref playing-timer audio-current-time-ms
|
||||
seeking-audio?]
|
||||
[:f> soundtrack/f-soundtrack
|
||||
{:audio-current-time-ms audio-current-time-ms
|
||||
:player-ref player-ref
|
||||
:seeking-audio? seeking-audio?}]])
|
||||
(when (or @recording? @reviewing-audio?)
|
||||
[time-counter @recording? @recording-length-ms @ready-to-delete? @reviewing-audio?
|
||||
[:f> f-time-counter @recording? @recording-length-ms @ready-to-delete? @reviewing-audio?
|
||||
@audio-current-time-ms])
|
||||
(when @recording?
|
||||
[recording-bar @recording-length-ms @ready-to-delete?])
|
||||
[:f> f-recording-bar @recording-length-ms @ready-to-delete?])
|
||||
[rn/view
|
||||
{:test-ID "record-audio"
|
||||
:style style/button-container
|
||||
@@ -545,11 +543,12 @@
|
||||
:on-start-should-set-responder on-start-should-set-responder
|
||||
:on-responder-move on-responder-move
|
||||
:on-responder-release on-responder-release}
|
||||
[delete-button/delete-button recording? ready-to-delete? reviewing-audio?
|
||||
[:f> delete-button/f-delete-button recording? ready-to-delete? reviewing-audio?
|
||||
@force-show-controls?]
|
||||
[lock-button/lock-button recording? ready-to-lock? locked?]
|
||||
[send-button/send-button recording? ready-to-send? reviewing-audio? @force-show-controls?]
|
||||
[record-button-big/record-button-big
|
||||
[:f> lock-button/f-lock-button recording? ready-to-lock? locked?]
|
||||
[:f> send-button/f-send-button recording? ready-to-send? reviewing-audio?
|
||||
@force-show-controls?]
|
||||
[:f> record-button-big/f-record-button-big
|
||||
recording?
|
||||
ready-to-send?
|
||||
ready-to-lock?
|
||||
@@ -567,6 +566,4 @@
|
||||
idle?
|
||||
on-send
|
||||
on-cancel]
|
||||
[record-button/record-button recording? reviewing-audio?]]])))])
|
||||
|
||||
(def record-audio view)
|
||||
[:f> record-button/f-record-button recording? reviewing-audio?]]])))])
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
(with-redefs [audio/get-player-duration (fn [] 2000)]
|
||||
(let [player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(h/render [soundtrack/soundtrack
|
||||
(h/render [:f> soundtrack/f-soundtrack
|
||||
{:player-ref player-ref
|
||||
:audio-current-time-ms audio-current-time-ms}])
|
||||
(-> (h/expect (h/get-by-test-id "soundtrack"))
|
||||
@@ -29,7 +29,7 @@
|
||||
(let [seeking-audio? (reagent/atom false)
|
||||
player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(h/render [soundtrack/soundtrack
|
||||
(h/render [:f> soundtrack/f-soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:player-ref player-ref
|
||||
:audio-current-time-ms audio-current-time-ms}])
|
||||
@@ -45,7 +45,7 @@
|
||||
(let [seeking-audio? (reagent/atom false)
|
||||
player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(h/render [soundtrack/soundtrack
|
||||
(h/render [:f> soundtrack/f-soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:player-ref player-ref
|
||||
:audio-current-time-ms audio-current-time-ms}])
|
||||
@@ -67,7 +67,7 @@
|
||||
(let [seeking-audio? (reagent/atom false)
|
||||
player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(h/render [soundtrack/soundtrack
|
||||
(h/render [:f> soundtrack/f-soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:player-ref player-ref
|
||||
:audio-current-time-ms audio-current-time-ms}])
|
||||
|
||||
@@ -9,30 +9,28 @@
|
||||
(def ^:private thumb-light (js/require "../resources/images/icons2/12x12/thumb-light.png"))
|
||||
(def ^:private thumb-dark (js/require "../resources/images/icons2/12x12/thumb-dark.png"))
|
||||
|
||||
(defn soundtrack
|
||||
(defn f-soundtrack
|
||||
[{:keys [audio-current-time-ms player-ref seeking-audio?]}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [audio-duration-ms (audio/get-player-duration @player-ref)]
|
||||
[:<>
|
||||
[slider/slider
|
||||
{:test-ID "soundtrack"
|
||||
:style (style/player-slider-container)
|
||||
:minimum-value 0
|
||||
:maximum-value audio-duration-ms
|
||||
:value @audio-current-time-ms
|
||||
:on-sliding-start #(reset! seeking-audio? true)
|
||||
:on-sliding-complete (fn [seek-time]
|
||||
(reset! seeking-audio? false)
|
||||
(audio/seek-player
|
||||
@player-ref
|
||||
seek-time
|
||||
#(log/debug "[record-audio] on seek - seek time: " seek-time)
|
||||
#(log/error "[record-audio] on seek - error: " %)))
|
||||
:on-value-change #(when @seeking-audio?
|
||||
(reset! audio-current-time-ms %))
|
||||
:thumb-image (if (colors/dark?) thumb-dark thumb-light)
|
||||
:minimum-track-tint-color (colors/theme-colors colors/primary-50 colors/primary-60)
|
||||
:maximum-track-tint-color (colors/theme-colors
|
||||
(if platform/ios? colors/neutral-20 colors/neutral-40)
|
||||
(if platform/ios? colors/neutral-80 colors/neutral-60))}]]))])
|
||||
(let [audio-duration-ms (audio/get-player-duration @player-ref)]
|
||||
[:<>
|
||||
[slider/slider
|
||||
{:test-ID "soundtrack"
|
||||
:style (style/player-slider-container)
|
||||
:minimum-value 0
|
||||
:maximum-value audio-duration-ms
|
||||
:value @audio-current-time-ms
|
||||
:on-sliding-start #(reset! seeking-audio? true)
|
||||
:on-sliding-complete (fn [seek-time]
|
||||
(reset! seeking-audio? false)
|
||||
(audio/seek-player
|
||||
@player-ref
|
||||
seek-time
|
||||
#(log/debug "[record-audio] on seek - seek time: " seek-time)
|
||||
#(log/error "[record-audio] on seek - error: " %)))
|
||||
:on-value-change #(when @seeking-audio?
|
||||
(reset! audio-current-time-ms %))
|
||||
:thumb-image (if (colors/dark?) thumb-dark thumb-light)
|
||||
:minimum-track-tint-color (colors/theme-colors colors/primary-50 colors/primary-60)
|
||||
:maximum-track-tint-color (colors/theme-colors
|
||||
(if platform/ios? colors/neutral-20 colors/neutral-40)
|
||||
(if platform/ios? colors/neutral-80 colors/neutral-60))}]]))
|
||||
|
||||
@@ -33,10 +33,12 @@
|
||||
quo2.components.info.information-box
|
||||
quo2.components.inputs.input.view
|
||||
quo2.components.inputs.profile-input.view
|
||||
quo2.components.inputs.recovery-phrase.view
|
||||
quo2.components.inputs.search-input.view
|
||||
quo2.components.inputs.title-input.view
|
||||
quo2.components.links.url-preview-list.view
|
||||
quo2.components.links.url-preview.view
|
||||
quo2.components.links.link-preview.view
|
||||
quo2.components.list-items.channel
|
||||
quo2.components.list-items.menu-item
|
||||
quo2.components.list-items.preview-list
|
||||
@@ -154,8 +156,9 @@
|
||||
|
||||
;;;; INPUTS
|
||||
(def input quo2.components.inputs.input.view/input)
|
||||
(def search-input quo2.components.inputs.search-input.view/search-input)
|
||||
(def profile-input quo2.components.inputs.profile-input.view/profile-input)
|
||||
(def recovery-phrase-input quo2.components.inputs.recovery-phrase.view/recovery-phrase-input)
|
||||
(def search-input quo2.components.inputs.search-input.view/search-input)
|
||||
(def title-input quo2.components.inputs.title-input.view/title-input)
|
||||
|
||||
;;;; LIST ITEMS
|
||||
@@ -208,3 +211,4 @@
|
||||
;;;; LINKS
|
||||
(def url-preview quo2.components.links.url-preview.view/view)
|
||||
(def url-preview-list quo2.components.links.url-preview-list.view/view)
|
||||
(def link-preview quo2.components.links.link-preview.view/view)
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
[quo2.components.drawers.permission-context.component-spec]
|
||||
[quo2.components.inputs.input.component-spec]
|
||||
[quo2.components.inputs.profile-input.component-spec]
|
||||
[quo2.components.inputs.recovery-phrase.component-spec]
|
||||
[quo2.components.inputs.title-input.component-spec]
|
||||
[quo2.components.links.url-preview-list.component-spec]
|
||||
[quo2.components.links.url-preview.component-spec]
|
||||
[quo2.components.links.link-preview.component-spec]
|
||||
[quo2.components.markdown.--tests--.text-component-spec]
|
||||
[quo2.components.notifications.notification.component-spec]
|
||||
[quo2.components.onboarding.small-option-card.component-spec]
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
(def neutral-90-opa-0 (alpha neutral-90 0))
|
||||
|
||||
;;95 with transparency
|
||||
(def neutral-95-opa-0 (alpha neutral-95 0))
|
||||
(def neutral-95-opa-60 (alpha neutral-95 0.6))
|
||||
(def neutral-95-opa-70 (alpha neutral-95 0.7))
|
||||
(def neutral-95-opa-80 (alpha neutral-95 0.8))
|
||||
@@ -84,6 +85,7 @@
|
||||
|
||||
;;100 with transparency
|
||||
(def neutral-100-opa-0 (alpha neutral-100 0))
|
||||
(def neutral-100-opa-5 (alpha neutral-100 0.05))
|
||||
(def neutral-100-opa-10 (alpha neutral-100 0.1))
|
||||
(def neutral-100-opa-30 (alpha neutral-100 0.3))
|
||||
(def neutral-100-opa-60 (alpha neutral-100 0.6))
|
||||
|
||||
@@ -37,14 +37,6 @@
|
||||
|
||||
(def dismiss-keyboard! #(.dismiss keyboard))
|
||||
|
||||
(defn use-window-dimensions
|
||||
[]
|
||||
(let [window ^js (react-native/useWindowDimensions)]
|
||||
{:font-scale (.-fontScale window)
|
||||
:height (.-height window)
|
||||
:scale (.-scale window)
|
||||
:width (.-width window)}))
|
||||
|
||||
(defn hide-splash-screen
|
||||
[]
|
||||
(.hide ^js (-> react-native .-NativeModules .-SplashScreen)))
|
||||
@@ -63,17 +55,15 @@
|
||||
[handler]
|
||||
(.addChangeListener appearance handler))
|
||||
|
||||
(defn get-window
|
||||
[]
|
||||
(js->clj (.get (.-Dimensions ^js react-native) "window") :keywordize-keys true))
|
||||
(def get-window
|
||||
(memoize
|
||||
(fn []
|
||||
(js->clj (.get (.-Dimensions ^js react-native) "window") :keywordize-keys true))))
|
||||
|
||||
(def status-bar (.-StatusBar ^js react-native))
|
||||
|
||||
(def style-sheet (.-StyleSheet ^js react-native))
|
||||
|
||||
(defn status-bar-height
|
||||
[]
|
||||
(.-currentHeight ^js status-bar))
|
||||
(def get-screen
|
||||
(memoize
|
||||
(fn []
|
||||
(js->clj (.get (.-Dimensions ^js react-native) "screen") :keywordize-keys true))))
|
||||
|
||||
(defn hw-back-add-listener
|
||||
[callback]
|
||||
|
||||
@@ -76,3 +76,22 @@
|
||||
[gesture-flat-list (rn-flat-list/base-list-props props)])
|
||||
|
||||
(def scroll-view (reagent/adapt-react-class ScrollView))
|
||||
|
||||
|
||||
;;; Custom gesture section-list
|
||||
(defn- flatten-sections
|
||||
[sections]
|
||||
(mapcat (fn [{:keys [title data]}]
|
||||
(into [{:title title :header? true}] data))
|
||||
sections))
|
||||
|
||||
(defn section-list
|
||||
[{:keys [sections render-section-header-fn render-fn] :as props}]
|
||||
(let [data (flatten-sections sections)]
|
||||
[flat-list
|
||||
(merge props
|
||||
{:data data
|
||||
:render-fn (fn [item]
|
||||
(if (:header? item)
|
||||
(render-section-header-fn item)
|
||||
(render-fn item)))})]))
|
||||
|
||||
@@ -171,6 +171,15 @@
|
||||
(with-decay (clj->js {:velocity velocity
|
||||
:clamp clamp}))))
|
||||
|
||||
(defn animate
|
||||
([animation value]
|
||||
(animate animation value default-duration))
|
||||
([animation value duration]
|
||||
(set-shared-value animation
|
||||
(with-timing value
|
||||
(clj->js {:duration duration
|
||||
:easing (default-easing)})))))
|
||||
|
||||
(defn with-timing-duration
|
||||
[val duration]
|
||||
(with-timing val
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
(ns react-native.safe-area
|
||||
(:require ["react-native-safe-area-context" :as safe-area-context :refer
|
||||
(SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)]
|
||||
[reagent.core :as reagent]))
|
||||
(:require ["react-native-static-safe-area-insets" :default StaticSafeAreaInsets]
|
||||
[oops.core :as oops]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.navigation :as navigation]))
|
||||
|
||||
(def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext)))
|
||||
|
||||
(def provider (reagent/adapt-react-class SafeAreaProvider))
|
||||
|
||||
(defn consumer
|
||||
[component]
|
||||
[consumer-raw
|
||||
(fn [^js insets]
|
||||
(reagent/as-element
|
||||
[component (js->clj insets :keywordize-keys true)]))])
|
||||
|
||||
(defn use-safe-area
|
||||
(defn- get-static-top
|
||||
[]
|
||||
(let [insets ^js (useSafeAreaInsets)]
|
||||
{:top (.-top insets)
|
||||
:bottom (.-bottom insets)
|
||||
:left (.-left insets)
|
||||
:right (.-right insets)}))
|
||||
(oops/oget StaticSafeAreaInsets "safeAreaInsetsTop"))
|
||||
|
||||
(defn- get-static-bottom
|
||||
[]
|
||||
(oops/oget StaticSafeAreaInsets "safeAreaInsetsBottom"))
|
||||
|
||||
(defn get-top
|
||||
[]
|
||||
(if platform/ios?
|
||||
(get-static-top)
|
||||
(navigation/status-bar-height)))
|
||||
|
||||
(defn get-bottom
|
||||
[]
|
||||
(if platform/ios?
|
||||
(get-static-bottom)
|
||||
0))
|
||||
|
||||
(defn get-insets
|
||||
[]
|
||||
{:top (get-top)
|
||||
:bottom (get-bottom)})
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
(def defs (reagent/adapt-react-class Svg/Defs))
|
||||
(def circle (reagent/adapt-react-class Svg/Circle))
|
||||
(def svgxml (reagent/adapt-react-class Svg/SvgXml))
|
||||
(def g (reagent/adapt-react-class Svg/G))
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
(if-not validation-result
|
||||
(if new-contact?
|
||||
(rf/merge cofx
|
||||
(contact/add-contact chat-key nickname ens-name)
|
||||
(contact/send-contact-request chat-key)
|
||||
(navigation/navigate-to :contacts-list {}))
|
||||
(chat/start-chat cofx chat-key ens-name))
|
||||
{:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code)
|
||||
|
||||
@@ -115,126 +115,125 @@
|
||||
(re-frame/dispatch [:bottom-sheet/hide-old-navigation-overlay])
|
||||
(reset-atoms))
|
||||
animation-delay))]
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [{height :height
|
||||
window-width :width}
|
||||
(rn/use-window-dimensions)
|
||||
window-height (if selected-item (- height 72) height)
|
||||
{:keys [keyboard-shown]} (hooks/use-keyboard)
|
||||
bg-height-expanded (- window-height (:top insets))
|
||||
bg-height (max (min @content-height bg-height-expanded) 109)
|
||||
bottom-sheet-dy (reanimated/use-shared-value 0)
|
||||
pan-y (reanimated/use-shared-value 0)
|
||||
translate-y (worklets.bottom-sheet/use-translate-y window-height bottom-sheet-dy pan-y)
|
||||
bg-opacity
|
||||
(worklets.bottom-sheet/use-background-opacity translate-y bg-height window-height 0.7)
|
||||
on-content-layout (fn [evt]
|
||||
(let [height (oget evt "nativeEvent" "layout" "height")]
|
||||
(reset! content-height height)))
|
||||
on-expanded (fn []
|
||||
(reanimated/set-shared-value bottom-sheet-dy bg-height-expanded)
|
||||
(reanimated/set-shared-value pan-y 0))
|
||||
on-collapsed (fn []
|
||||
(reanimated/set-shared-value bottom-sheet-dy bg-height)
|
||||
(reanimated/set-shared-value pan-y 0))
|
||||
bottom-sheet-gesture (get-bottom-sheet-gesture
|
||||
pan-y
|
||||
translate-y
|
||||
bg-height
|
||||
bg-height-expanded
|
||||
window-height
|
||||
keyboard-shown
|
||||
disable-drag?
|
||||
expandable?
|
||||
show-bottom-sheet?
|
||||
expanded?
|
||||
close-bottom-sheet
|
||||
gesture-running?)
|
||||
handle-comp [gesture/gesture-detector {:gesture bottom-sheet-gesture}
|
||||
[handle-comp window-width override-theme]]]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [{height :height
|
||||
window-width :width}
|
||||
(rn/get-window)
|
||||
window-height (if selected-item (- height 72) height)
|
||||
{:keys [keyboard-shown]} (hooks/use-keyboard)
|
||||
insets (safe-area/get-insets)
|
||||
bg-height-expanded (- window-height (:top insets))
|
||||
bg-height (max (min @content-height bg-height-expanded) 109)
|
||||
bottom-sheet-dy (reanimated/use-shared-value 0)
|
||||
pan-y (reanimated/use-shared-value 0)
|
||||
translate-y (worklets.bottom-sheet/use-translate-y window-height bottom-sheet-dy pan-y)
|
||||
bg-opacity
|
||||
(worklets.bottom-sheet/use-background-opacity translate-y bg-height window-height 0.7)
|
||||
on-content-layout (fn [evt]
|
||||
(let [height (oget evt "nativeEvent" "layout" "height")]
|
||||
(reset! content-height height)))
|
||||
on-expanded (fn []
|
||||
(reanimated/set-shared-value bottom-sheet-dy bg-height-expanded)
|
||||
(reanimated/set-shared-value pan-y 0))
|
||||
on-collapsed (fn []
|
||||
(reanimated/set-shared-value bottom-sheet-dy bg-height)
|
||||
(reanimated/set-shared-value pan-y 0))
|
||||
bottom-sheet-gesture (get-bottom-sheet-gesture
|
||||
pan-y
|
||||
translate-y
|
||||
bg-height
|
||||
bg-height-expanded
|
||||
window-height
|
||||
keyboard-shown
|
||||
disable-drag?
|
||||
expandable?
|
||||
show-bottom-sheet?
|
||||
expanded?
|
||||
close-bottom-sheet
|
||||
gesture-running?)
|
||||
handle-comp [gesture/gesture-detector {:gesture bottom-sheet-gesture}
|
||||
[handle-comp window-width override-theme]]]
|
||||
|
||||
(react/effect! #(do
|
||||
(cond
|
||||
(and
|
||||
(nil? @show-bottom-sheet?)
|
||||
visible?
|
||||
(some? @content-height)
|
||||
(> @content-height 0))
|
||||
(reset! show-bottom-sheet? true)
|
||||
(react/effect! #(do
|
||||
(cond
|
||||
(and
|
||||
(nil? @show-bottom-sheet?)
|
||||
visible?
|
||||
(some? @content-height)
|
||||
(> @content-height 0))
|
||||
(reset! show-bottom-sheet? true)
|
||||
|
||||
(and @show-bottom-sheet? (not visible?))
|
||||
(close-bottom-sheet)))
|
||||
[@show-bottom-sheet? @content-height visible?])
|
||||
(react/effect! #(do
|
||||
(when @show-bottom-sheet?
|
||||
(cond
|
||||
keyboard-shown
|
||||
(do
|
||||
(reset! keyboard-was-shown? true)
|
||||
(reset! expanded? true))
|
||||
(and @keyboard-was-shown? (not keyboard-shown))
|
||||
(reset! expanded? false))))
|
||||
[@show-bottom-sheet? @keyboard-was-shown? keyboard-shown])
|
||||
(react/effect! #(do
|
||||
(when-not @gesture-running?
|
||||
(cond
|
||||
@show-bottom-sheet?
|
||||
(if @expanded?
|
||||
(do
|
||||
(reanimated/set-shared-value
|
||||
bottom-sheet-dy
|
||||
(with-animation (+ bg-height-expanded (.-value pan-y))))
|
||||
;; Workaround for
|
||||
;; https://github.com/software-mansion/react-native-reanimated/issues/1758#issue-817145741
|
||||
;; withTiming/withSpring callback not working
|
||||
;; on-expanded should be called as a callback of
|
||||
;; with-animation instead, once this issue has been resolved
|
||||
(timer/set-timeout on-expanded animation-delay))
|
||||
(do
|
||||
(reanimated/set-shared-value
|
||||
bottom-sheet-dy
|
||||
(with-animation (+ bg-height (.-value pan-y))))
|
||||
;; Workaround for
|
||||
;; https://github.com/software-mansion/react-native-reanimated/issues/1758#issue-817145741
|
||||
;; withTiming/withSpring callback not working
|
||||
;; on-collapsed should be called as a callback of
|
||||
;; with-animation instead, once this issue has been resolved
|
||||
(timer/set-timeout on-collapsed animation-delay)))
|
||||
(and @show-bottom-sheet? (not visible?))
|
||||
(close-bottom-sheet)))
|
||||
[@show-bottom-sheet? @content-height visible?])
|
||||
(react/effect! #(do
|
||||
(when @show-bottom-sheet?
|
||||
(cond
|
||||
keyboard-shown
|
||||
(do
|
||||
(reset! keyboard-was-shown? true)
|
||||
(reset! expanded? true))
|
||||
(and @keyboard-was-shown? (not keyboard-shown))
|
||||
(reset! expanded? false))))
|
||||
[@show-bottom-sheet? @keyboard-was-shown? keyboard-shown])
|
||||
(react/effect! #(do
|
||||
(when-not @gesture-running?
|
||||
(cond
|
||||
@show-bottom-sheet?
|
||||
(if @expanded?
|
||||
(do
|
||||
(reanimated/set-shared-value
|
||||
bottom-sheet-dy
|
||||
(with-animation (+ bg-height-expanded (.-value pan-y))))
|
||||
;; Workaround for
|
||||
;; https://github.com/software-mansion/react-native-reanimated/issues/1758#issue-817145741
|
||||
;; withTiming/withSpring callback not working
|
||||
;; on-expanded should be called as a callback of
|
||||
;; with-animation instead, once this issue has been resolved
|
||||
(timer/set-timeout on-expanded animation-delay))
|
||||
(do
|
||||
(reanimated/set-shared-value
|
||||
bottom-sheet-dy
|
||||
(with-animation (+ bg-height (.-value pan-y))))
|
||||
;; Workaround for
|
||||
;; https://github.com/software-mansion/react-native-reanimated/issues/1758#issue-817145741
|
||||
;; withTiming/withSpring callback not working
|
||||
;; on-collapsed should be called as a callback of
|
||||
;; with-animation instead, once this issue has been resolved
|
||||
(timer/set-timeout on-collapsed animation-delay)))
|
||||
|
||||
(= @show-bottom-sheet? false)
|
||||
(reanimated/set-shared-value bottom-sheet-dy (with-animation 0)))))
|
||||
[@show-bottom-sheet? @expanded? @gesture-running?])
|
||||
(= @show-bottom-sheet? false)
|
||||
(reanimated/set-shared-value bottom-sheet-dy (with-animation 0)))))
|
||||
[@show-bottom-sheet? @expanded? @gesture-running?])
|
||||
|
||||
[:<>
|
||||
[rn/touchable-without-feedback {:on-press (when backdrop-dismiss? close-bottom-sheet)}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity bg-opacity}
|
||||
styles/backdrop)}]]
|
||||
(cond->> [reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateY translate-y}]}
|
||||
{:width window-width
|
||||
:height window-height})}
|
||||
[rn/view {:style styles/container}
|
||||
(when selected-item
|
||||
[rn/view {:style (styles/selected-background override-theme)}
|
||||
[selected-item]])
|
||||
[rn/view {:style (styles/background override-theme)}
|
||||
[rn/keyboard-avoiding-view
|
||||
{:behaviour (if platform/ios? :padding :height)
|
||||
:style {:flex 1}}
|
||||
[rn/view
|
||||
{:style (styles/content-style insets bottom-safe-area-spacing?)
|
||||
:on-layout (when-not (and
|
||||
(some? @content-height)
|
||||
(> @content-height 0))
|
||||
on-content-layout)}
|
||||
children]]
|
||||
(when show-handle?
|
||||
handle-comp)]]]
|
||||
(not show-handle?)
|
||||
(conj [gesture/gesture-detector {:gesture bottom-sheet-gesture}]))]))])]))
|
||||
[:<>
|
||||
[rn/touchable-without-feedback {:on-press (when backdrop-dismiss? close-bottom-sheet)}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity bg-opacity}
|
||||
styles/backdrop)}]]
|
||||
(cond->> [reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateY translate-y}]}
|
||||
{:width window-width
|
||||
:height window-height})}
|
||||
[rn/view {:style styles/container}
|
||||
(when selected-item
|
||||
[rn/view {:style (styles/selected-background override-theme)}
|
||||
[selected-item]])
|
||||
[rn/view {:style (styles/background override-theme)}
|
||||
[rn/keyboard-avoiding-view
|
||||
{:behaviour (if platform/ios? :padding :height)
|
||||
:style {:flex 1}}
|
||||
[rn/view
|
||||
{:style (styles/content-style insets bottom-safe-area-spacing?)
|
||||
:on-layout (when-not (and
|
||||
(some? @content-height)
|
||||
(> @content-height 0))
|
||||
on-content-layout)}
|
||||
children]]
|
||||
(when show-handle?
|
||||
handle-comp)]]]
|
||||
(not show-handle?)
|
||||
(conj [gesture/gesture-detector {:gesture bottom-sheet-gesture}]))]))]))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
[status-im.chat.models.mentions :as mentions]
|
||||
[status-im.chat.models.message :as chat.message]
|
||||
[status-im.chat.models.message-content :as message-content]
|
||||
[status-im2.config :as config]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.i18n :as i18n]
|
||||
@@ -39,6 +40,18 @@
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))]
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))}))
|
||||
|
||||
(rf/defn set-input-content-height
|
||||
{:events [:chat.ui/set-input-content-height]}
|
||||
[{db :db} content-height chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))]
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :input-content-height] content-height)}))
|
||||
|
||||
(rf/defn set-input-maximized
|
||||
{:events [:chat.ui/set-input-maximized]}
|
||||
[{db :db} maximized? chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))]
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :input-maximized?] maximized?)}))
|
||||
|
||||
(rf/defn select-mention
|
||||
{:events [:chat.ui/select-mention]}
|
||||
[{:keys [db] :as cofx} text-input-ref {:keys [primary-name searched-text match public-key] :as user}]
|
||||
@@ -88,7 +101,8 @@
|
||||
(update-in [:chat/inputs current-chat-id :metadata]
|
||||
dissoc
|
||||
:sending-image))}
|
||||
(input/set-input-text text current-chat-id))))
|
||||
(when-not config/new-composer-enabled?
|
||||
(input/set-input-text text current-chat-id)))))
|
||||
|
||||
(rf/defn show-contact-request-input
|
||||
"Sets reference to previous chat message and focuses on input"
|
||||
@@ -163,7 +177,8 @@
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
(rf/merge cofx
|
||||
{:set-text-input-value [current-chat-id ""]}
|
||||
(when-not config/new-composer-enabled?
|
||||
{:set-text-input-value [current-chat-id ""]})
|
||||
(clean-input current-chat-id)
|
||||
(mentions/clear-mentions))))
|
||||
|
||||
@@ -207,25 +222,21 @@
|
||||
|
||||
(rf/defn send-edited-message
|
||||
[{:keys [db] :as cofx} text {:keys [message-id quoted-message chat-id]}]
|
||||
(let [pinned-message (get-in db [:pin-messages chat-id message-id])]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:json-rpc/call [{:method "wakuext_editMessage"
|
||||
:params [{:id message-id
|
||||
:text text
|
||||
:content-type (if (message-content/emoji-only-content?
|
||||
{:text text :response-to quoted-message})
|
||||
constants/content-type-emoji
|
||||
constants/content-type-text)}]
|
||||
:js-response true
|
||||
:on-error #(log/error "failed to edit message " %)
|
||||
:on-success (fn [result]
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response
|
||||
result])
|
||||
(when pinned-message
|
||||
(re-frame/dispatch [:pin-message/load-pin-messages
|
||||
chat-id])))}]}
|
||||
(cancel-message-edit))))
|
||||
(rf/merge
|
||||
cofx
|
||||
{:json-rpc/call [{:method "wakuext_editMessage"
|
||||
:params [{:id message-id
|
||||
:text text
|
||||
:content-type (if (message-content/emoji-only-content?
|
||||
{:text text :response-to quoted-message})
|
||||
constants/content-type-emoji
|
||||
constants/content-type-text)}]
|
||||
:js-response true
|
||||
:on-error #(log/error "failed to edit message " %)
|
||||
:on-success (fn [result]
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response
|
||||
result]))}]}
|
||||
(cancel-message-edit)))
|
||||
|
||||
(rf/defn send-current-message
|
||||
"Sends message from current chat input"
|
||||
|
||||
@@ -32,16 +32,30 @@
|
||||
;; TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
|
||||
(message-list/rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id))
|
||||
|
||||
(defn add-pinned-message
|
||||
[acc chat-id message-id message]
|
||||
(let [{:keys [pinned-by pinned-at] :as pinned-message}
|
||||
(get-in acc [:db :pin-messages chat-id message-id])]
|
||||
(if pinned-message
|
||||
(assoc-in acc
|
||||
[:db :pin-messages chat-id message-id]
|
||||
(assoc
|
||||
message
|
||||
:pinned-by pinned-by
|
||||
:pinned-at pinned-at))
|
||||
acc)))
|
||||
|
||||
(defn add-message
|
||||
[{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value]
|
||||
(let [{:keys [replace from clock-value] :as message}
|
||||
(data-store.messages/<-rpc (types/js->clj message-js))]
|
||||
(data-store.messages/<-rpc (types/js->clj message-js))
|
||||
acc-with-pinned-message (add-pinned-message acc chat-id message-id message)]
|
||||
(if (message-loaded? db chat-id message-id)
|
||||
;; If the message is already loaded, it means it's an update, that
|
||||
;; happens when a message that was missing a reply had the reply
|
||||
;; coming through, in which case we just insert the new message
|
||||
(assoc-in acc [:db :messages chat-id message-id] message)
|
||||
(cond-> acc
|
||||
(assoc-in acc-with-pinned-message [:db :messages chat-id message-id] message)
|
||||
(cond-> acc-with-pinned-message
|
||||
;;add new message to db
|
||||
:always
|
||||
(update-in [:db :messages chat-id] assoc message-id message)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(ns status-im.contact.chat
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im2.contexts.contacts.events :as contact]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.navigation.events :as navigation]))
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/defn contact-code-submitted
|
||||
{:events [:contact.ui/contact-code-submitted]
|
||||
@@ -11,7 +11,7 @@
|
||||
(let [{:keys [public-key ens-name]} new-identity]
|
||||
(rf/merge cofx
|
||||
#(if new-contact?
|
||||
(contact/add-contact % public-key nickname ens-name)
|
||||
(contact/send-contact-request % public-key)
|
||||
{:dispatch [:chat.ui/start-chat public-key ens-name]})
|
||||
#(when new-contact?
|
||||
(navigation/navigate-back %)))))
|
||||
|
||||
@@ -46,7 +46,10 @@
|
||||
|
||||
(update :quoted-message
|
||||
set/rename-keys
|
||||
{:parsedText :parsed-text :communityId :community-id})
|
||||
{:parsedText :parsed-text
|
||||
:deleted :deleted?
|
||||
:deletedForMe :deleted-for-me?
|
||||
:communityId :community-id})
|
||||
(update :outgoing-status keyword)
|
||||
(update :command-parameters
|
||||
set/rename-keys
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
(rf/defn send-pin-message
|
||||
[cofx pin-message]
|
||||
{:json-rpc/call [{:method "wakuext_sendPinMessage"
|
||||
:params [(messages/->rpc pin-message)]
|
||||
:on-success #(log/debug "successfully pinned message" pin-message)
|
||||
:on-error #(log/error "failed to pin message" % pin-message)}]})
|
||||
{:json-rpc/call [{:method "wakuext_sendPinMessage"
|
||||
:params [(messages/->rpc pin-message)]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to pin message" %)}]})
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
[status-im.data-store.activities :as data-store.activities]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.data-store.invitations :as data-store.invitations]
|
||||
[status-im.data-store.messages :as data-store.messages]
|
||||
[status-im.data-store.reactions :as data-store.reactions]
|
||||
[status-im.group-chats.core :as models.group]
|
||||
[status-im.multiaccounts.login.core :as multiaccounts.login]
|
||||
@@ -44,7 +43,7 @@
|
||||
^js removed-chats (.-removedChats response-js)
|
||||
^js activity-notifications (.-activityCenterNotifications response-js)
|
||||
^js activity-center-state (.-activityCenterState response-js)
|
||||
^js pin-messages (.-pinMessages response-js)
|
||||
^js pin-messages-js (.-pinMessages response-js)
|
||||
^js removed-messages (.-removedMessages response-js)
|
||||
^js visibility-status-updates (.-statusUpdates response-js)
|
||||
^js current-visibility-status (.-currentStatus response-js)
|
||||
@@ -109,12 +108,12 @@
|
||||
(process-next response-js sync-handler)
|
||||
(browser/handle-bookmarks bookmarks-clj)))
|
||||
|
||||
(seq pin-messages)
|
||||
(let [pin-messages (types/js->clj pin-messages)]
|
||||
(seq pin-messages-js)
|
||||
(do
|
||||
(js-delete response-js "pinMessages")
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(messages.pin/receive-signal (map data-store.messages/<-rpc pin-messages))))
|
||||
(messages.pin/receive-signal pin-messages-js)))
|
||||
|
||||
(seq removed-chats)
|
||||
(let [removed-chats-clj (types/js->clj removed-chats)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns status-im.ui.components.keyboard-avoid-presentation
|
||||
(:require [oops.core :refer [oget]]
|
||||
[reagent.core :as reagent]
|
||||
(:require [reagent.core :as reagent]
|
||||
[status-im.ui.components.react :as react]))
|
||||
|
||||
(defn keyboard-avoiding-view
|
||||
@@ -8,16 +7,11 @@
|
||||
(let [this (reagent/current-component)
|
||||
props (reagent/props this)
|
||||
children (reagent/children this)]
|
||||
[react/safe-area-consumer
|
||||
(fn [insets]
|
||||
(let [vertical-offset (+ (oget insets "top")
|
||||
;; 20 is the margin-top for presentation modal
|
||||
20)]
|
||||
(reagent/as-element
|
||||
(into [react/keyboard-avoiding-view
|
||||
(update props
|
||||
:keyboardVerticalOffset
|
||||
+
|
||||
vertical-offset
|
||||
(if (:ignore-offset props) 44 0))]
|
||||
children))))]))
|
||||
(reagent/as-element
|
||||
(into [react/keyboard-avoiding-view
|
||||
(update props
|
||||
:keyboardVerticalOffset
|
||||
+
|
||||
20
|
||||
(if (:ignore-offset props) 44 0))]
|
||||
children))))
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
["react-native-image-crop-picker" :default image-picker]
|
||||
["react-native-linear-gradient" :default LinearGradient]
|
||||
["react-native-navigation" :refer (Navigation)]
|
||||
["react-native-safe-area-context" :as safe-area-context :refer
|
||||
(SafeAreaProvider SafeAreaInsetsContext)]
|
||||
[quo.design-system.colors :as colors]
|
||||
[reagent.core :as reagent]
|
||||
[utils.i18n :as i18n]
|
||||
@@ -308,9 +306,6 @@
|
||||
[activity-indicator {:animating true}]])
|
||||
comp)))
|
||||
|
||||
(def safe-area-provider (reagent/adapt-react-class SafeAreaProvider))
|
||||
(def safe-area-consumer (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext)))
|
||||
|
||||
(defn hw-back-add-listener
|
||||
[callback]
|
||||
(.addEventListener BackHandler "hardwareBackPress" callback))
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
(ns status-im.ui.components.topbar
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[quo.core :as quo]
|
||||
[quo2.foundations.colors :as quo2.colors]))
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(def default-button-width 48)
|
||||
|
||||
@@ -32,18 +33,16 @@
|
||||
(let [navigation (if (= navigation :none)
|
||||
nil
|
||||
[(default-navigation modal? navigation)])]
|
||||
[quo/safe-area-consumer
|
||||
(fn [insets]
|
||||
[quo/header
|
||||
(merge {:left-accessories navigation
|
||||
:title-component content
|
||||
:insets (when use-insets insets)
|
||||
:left-width (when navigation
|
||||
default-button-width)
|
||||
:border-bottom border-bottom?}
|
||||
props
|
||||
(when (seq right-accessories)
|
||||
{:right-accessories right-accessories})
|
||||
(when new-ui?
|
||||
{:background (quo2.colors/theme-colors quo2.colors/neutral-5
|
||||
quo2.colors/neutral-95)}))])]))
|
||||
[quo/header
|
||||
(merge {:left-accessories navigation
|
||||
:title-component content
|
||||
:insets (when use-insets (safe-area/get-insets))
|
||||
:left-width (when navigation
|
||||
default-button-width)
|
||||
:border-bottom border-bottom?}
|
||||
props
|
||||
(when (seq right-accessories)
|
||||
{:right-accessories right-accessories})
|
||||
(when new-ui?
|
||||
{:background (quo2.colors/theme-colors quo2.colors/neutral-5
|
||||
quo2.colors/neutral-95)}))]))
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
(defn browser-stack
|
||||
[]
|
||||
(let [screen-id (rf/sub [:browser/screen-id])]
|
||||
[safe-area/consumer
|
||||
(fn [{:keys [top]}]
|
||||
[rn/view {:padding-top top :flex 1}
|
||||
(case screen-id
|
||||
:empty-tab [empty-tab/empty-tab]
|
||||
:browser [browser/browser]
|
||||
:browser-tabs [tabs/tabs]
|
||||
[empty-tab/empty-tab])])]))
|
||||
[rn/view {:padding-top (safe-area/get-top) :flex 1}
|
||||
(case screen-id
|
||||
:empty-tab [empty-tab/empty-tab]
|
||||
:browser [browser/browser]
|
||||
:browser-tabs [tabs/tabs]
|
||||
[empty-tab/empty-tab])]))
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
(ns status-im.ui.screens.chat.components.accessory
|
||||
(:require [cljs-bean.core :as bean]
|
||||
[quo.animated :as animated]
|
||||
[quo.components.safe-area :refer [use-safe-area]]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.platform :as platform]
|
||||
[quo.react :as react]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.tabbar.core :as tabbar]
|
||||
[status-im.ui.screens.chat.components.hooks :refer [use-keyboard-dimension]]))
|
||||
[status-im.ui.screens.chat.components.hooks :refer [use-keyboard-dimension]]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(def duration 250)
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
keyboard-max-height :max-height
|
||||
keyboard-end-position :end-position}
|
||||
(use-keyboard-dimension)
|
||||
{:keys [bottom]} (use-safe-area)
|
||||
bottom (safe-area/get-bottom)
|
||||
{on-layout :on-layout
|
||||
bar-height :height}
|
||||
(rn/use-layout)
|
||||
@@ -121,7 +121,7 @@
|
||||
children :children}
|
||||
(bean/bean props)
|
||||
{keyboard-max-height :max-height} (use-keyboard-dimension)
|
||||
{:keys [bottom]} (use-safe-area)
|
||||
bottom (safe-area/get-bottom)
|
||||
{on-layout :on-layout
|
||||
bar-height :height}
|
||||
(rn/use-layout)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(ns status-im.ui.screens.chat.components.hooks
|
||||
(:require [quo.components.safe-area :refer [use-safe-area]]
|
||||
[quo.platform :as platform]
|
||||
(:require [quo.platform :as platform]
|
||||
[quo.react :as react]
|
||||
[quo.react-native :refer [use-window-dimensions] :as rn]))
|
||||
[quo.react-native :refer [use-window-dimensions] :as rn]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(def ^:private keyboard-change-event (if platform/android? "keyboardDidShow" "keyboardWillChangeFrame"))
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
(defn use-keyboard-dimension
|
||||
[]
|
||||
(let [{:keys [height]} (use-window-dimensions)
|
||||
{:keys [bottom]} (use-safe-area)
|
||||
bottom (safe-area/get-bottom)
|
||||
keyboard-listener (atom nil)
|
||||
keyboard (react/state
|
||||
{:height 0
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns status-im.ui.screens.chat.image.preview.views
|
||||
(:require ["react-native-image-viewing" :default image-viewing]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.platform :as platform]
|
||||
[re-frame.core :as re-frame]
|
||||
@@ -9,7 +8,8 @@
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.share :as share]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[react-native.safe-area :as safe-area]))
|
||||
|
||||
(defn share
|
||||
[path]
|
||||
@@ -53,29 +53,27 @@
|
||||
|
||||
(defn header
|
||||
[{:keys [on-close] :as props}]
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[react/view
|
||||
{:style {:padding-horizontal 15
|
||||
:padding-top (+ (safe-area/get-bottom) 50)}}
|
||||
[react/view {:style {:justify-content :center}}
|
||||
[react/touchable-opacity
|
||||
{:on-press on-close
|
||||
:style {:padding-vertical 11
|
||||
:border-radius 44}
|
||||
:accessibility-label :close-button}
|
||||
[react/view
|
||||
{:style {:padding-horizontal 15
|
||||
:padding-top (+ (:bottom insets) 50)}}
|
||||
[react/view {:style {:justify-content :center}}
|
||||
[react/touchable-opacity
|
||||
{:on-press on-close
|
||||
:style {:padding-vertical 11
|
||||
:border-radius 44}
|
||||
:accessibility-label :close-button}
|
||||
[react/view
|
||||
{:style {:background-color colors/black-transparent-86
|
||||
:border-radius 20
|
||||
:width 40
|
||||
:height 40
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
[icons/icon :main-icons/close
|
||||
{:container-style {:width 24
|
||||
:height 24}
|
||||
:color colors/white-persist}]]]
|
||||
[header-options props]]])])
|
||||
{:style {:background-color colors/black-transparent-86
|
||||
:border-radius 20
|
||||
:width 40
|
||||
:height 40
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
[icons/icon :main-icons/close
|
||||
{:container-style {:width 24
|
||||
:height 24}
|
||||
:color colors/white-persist}]]]
|
||||
[header-options props]]])
|
||||
|
||||
(defn preview-image
|
||||
[{{:keys [content] :as message} :message
|
||||
|
||||
@@ -258,8 +258,9 @@
|
||||
(merge
|
||||
(when in-popover? {:number-of-lines 2})
|
||||
(cond
|
||||
(= content-type constants/content-type-system-text) (system-text-style)
|
||||
:else (default-text-style))))
|
||||
(= content-type constants/content-type-system-text) (system-text-style)
|
||||
(= content-type constants/content-type-system-pinned-message) (system-text-style)
|
||||
:else (default-text-style))))
|
||||
|
||||
(defn emph-text-style
|
||||
[]
|
||||
|
||||
@@ -36,8 +36,9 @@
|
||||
:disabled blocked?
|
||||
:accessibility-label :add-to-contacts-button
|
||||
:action (when-not blocked?
|
||||
#(re-frame/dispatch [:contact.ui/add-contact-pressed public-key
|
||||
nil ens-name]))}])
|
||||
(fn []
|
||||
(re-frame/dispatch [:contact.ui/send-contact-request
|
||||
public-key])))}])
|
||||
[{:label (i18n/label (if (or muted? blocked?) :t/unmute :t/mute))
|
||||
:icon :main-icons/notification
|
||||
:accessibility-label :mute-chat
|
||||
|
||||
@@ -299,39 +299,36 @@
|
||||
(defn accounts-overview
|
||||
[]
|
||||
(let [mnemonic @(re-frame/subscribe [:mnemonic])
|
||||
;mainnet? @(re-frame/subscribe [:mainnet?])
|
||||
selected-account-atom (reagent/atom nil)]
|
||||
(fn []
|
||||
[safe-area/consumer
|
||||
(fn [{:keys [top]}]
|
||||
[react/view
|
||||
{:style {:flex 1
|
||||
:padding-top top
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-5
|
||||
quo2.colors/neutral-95)}}
|
||||
[react/view {:padding-horizontal 20}
|
||||
[react/view {:flex-direction :row :height 56 :align-items :center :justify-content :flex-end}
|
||||
[quo2.button/button
|
||||
{:icon true
|
||||
:size 32
|
||||
:type :grey
|
||||
:accessibility-label :accounts-qr-code
|
||||
:on-press #(re-frame/dispatch
|
||||
[::qr-scanner/scan-code
|
||||
{:handler :wallet.send/qr-scanner-result}])}
|
||||
:i/placeholder]
|
||||
[react/view {:width 12}]
|
||||
[quo2.button/button
|
||||
{:icon true
|
||||
:size 32
|
||||
:type :grey
|
||||
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
|
||||
{:content (sheets/accounts-options mnemonic)}])
|
||||
:accessibility-label :accounts-more-options}
|
||||
:i/placeholder]]
|
||||
[total-value]
|
||||
[accounts selected-account-atom]]
|
||||
[account.views/account-new @selected-account-atom]])])))
|
||||
[react/view
|
||||
{:style {:flex 1
|
||||
:padding-top (safe-area/get-top)
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-5
|
||||
quo2.colors/neutral-95)}}
|
||||
[react/view {:padding-horizontal 20}
|
||||
[react/view {:flex-direction :row :height 56 :align-items :center :justify-content :flex-end}
|
||||
[quo2.button/button
|
||||
{:icon true
|
||||
:size 32
|
||||
:type :grey
|
||||
:accessibility-label :accounts-qr-code
|
||||
:on-press #(re-frame/dispatch
|
||||
[::qr-scanner/scan-code
|
||||
{:handler :wallet.send/qr-scanner-result}])}
|
||||
:i/placeholder]
|
||||
[react/view {:width 12}]
|
||||
[quo2.button/button
|
||||
{:icon true
|
||||
:size 32
|
||||
:type :grey
|
||||
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet-old
|
||||
{:content (sheets/accounts-options mnemonic)}])
|
||||
:accessibility-label :accounts-more-options}
|
||||
:i/placeholder]]
|
||||
[total-value]
|
||||
[accounts selected-account-atom]]
|
||||
[account.views/account-new @selected-account-atom]])))
|
||||
|
||||
(defn accounts-overview-old
|
||||
[]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[quo2.core :as quo2]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.gesture :as gesture]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[react-native.core :as rn]
|
||||
@@ -58,61 +59,61 @@
|
||||
item])))
|
||||
|
||||
(defn contact-selection-list
|
||||
[]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [contacts (rf/sub [:contacts/sorted-and-grouped-by-first-letter])
|
||||
selected-contacts-count (rf/sub [:selected-contacts-count])
|
||||
selected-contacts (rf/sub [:group/selected-contacts])
|
||||
one-contact-selected? (= selected-contacts-count 1)
|
||||
contacts-selected? (pos? selected-contacts-count)
|
||||
{:keys [primary-name public-key]} (when one-contact-selected?
|
||||
(rf/sub [:contacts/contact-by-identity
|
||||
(first selected-contacts)]))
|
||||
no-contacts? (empty? contacts)]
|
||||
[:<>
|
||||
[quo2/button
|
||||
{:type :grey
|
||||
:icon true
|
||||
:on-press #(rf/dispatch [:navigate-back])
|
||||
:style style/contact-selection-close
|
||||
:override-background-color (quo2.colors/theme-colors quo2.colors/neutral-10
|
||||
quo2.colors/neutral-90)}
|
||||
:i/close]
|
||||
[rn/view style/contact-selection-heading
|
||||
[quo2/text
|
||||
{:weight :semi-bold
|
||||
:size :heading-1
|
||||
:style {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}}
|
||||
(i18n/label :t/new-chat)]
|
||||
(when-not no-contacts?
|
||||
[quo2/text
|
||||
{:size :paragraph-2
|
||||
:weight :regular
|
||||
:style {:color (quo2.colors/theme-colors quo2.colors/neutral-40 quo2.colors/neutral-50)}}
|
||||
(i18n/label :t/selected-count-from-max
|
||||
{:selected selected-contacts-count
|
||||
:max constants/max-group-chat-participants})])]
|
||||
[rn/view
|
||||
{:style {:flex 1}}
|
||||
(if no-contacts?
|
||||
[no-contacts-view]
|
||||
[rn/section-list
|
||||
{:key-fn :title
|
||||
:sticky-section-headers-enabled false
|
||||
:sections (rf/sub [:contacts/filtered-active-sections])
|
||||
:render-section-header-fn contact-list/contacts-section-header
|
||||
:content-container-style {:padding-bottom 70}
|
||||
:render-fn contact-item-render}])]
|
||||
(when contacts-selected?
|
||||
[button/button
|
||||
{:type :primary
|
||||
:style style/chat-button
|
||||
:accessibility-label :next-button
|
||||
:on-press (fn []
|
||||
(if one-contact-selected?
|
||||
(rf/dispatch [:chat.ui/start-chat public-key])
|
||||
(rf/dispatch [:navigate-to :new-group])))}
|
||||
(if one-contact-selected?
|
||||
(i18n/label :t/chat-with {:selected-user primary-name})
|
||||
(i18n/label :t/setup-group-chat))])]))])
|
||||
[{:keys [scroll-enabled on-scroll]}]
|
||||
(let [contacts (rf/sub [:contacts/sorted-and-grouped-by-first-letter])
|
||||
selected-contacts-count (rf/sub [:selected-contacts-count])
|
||||
selected-contacts (rf/sub [:group/selected-contacts])
|
||||
one-contact-selected? (= selected-contacts-count 1)
|
||||
contacts-selected? (pos? selected-contacts-count)
|
||||
{:keys [primary-name public-key]} (when one-contact-selected?
|
||||
(rf/sub [:contacts/contact-by-identity
|
||||
(first selected-contacts)]))
|
||||
no-contacts? (empty? contacts)]
|
||||
[:<>
|
||||
[quo2/button
|
||||
{:type :grey
|
||||
:icon true
|
||||
:on-press #(rf/dispatch [:navigate-back])
|
||||
:style style/contact-selection-close
|
||||
:override-background-color (quo2.colors/theme-colors quo2.colors/neutral-10
|
||||
quo2.colors/neutral-90)}
|
||||
:i/close]
|
||||
[rn/view style/contact-selection-heading
|
||||
[quo2/text
|
||||
{:weight :semi-bold
|
||||
:size :heading-1
|
||||
:style {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}}
|
||||
(i18n/label :t/new-chat)]
|
||||
(when-not no-contacts?
|
||||
[quo2/text
|
||||
{:size :paragraph-2
|
||||
:weight :regular
|
||||
:style {:color (quo2.colors/theme-colors quo2.colors/neutral-40 quo2.colors/neutral-50)}}
|
||||
(i18n/label :t/selected-count-from-max
|
||||
{:selected selected-contacts-count
|
||||
:max constants/max-group-chat-participants})])]
|
||||
[rn/view
|
||||
{:style {:flex 1}}
|
||||
(if no-contacts?
|
||||
[no-contacts-view]
|
||||
[gesture/section-list
|
||||
{:key-fn :title
|
||||
:sticky-section-headers-enabled false
|
||||
:sections (rf/sub [:contacts/filtered-active-sections])
|
||||
:render-section-header-fn contact-list/contacts-section-header
|
||||
:content-container-style {:padding-bottom 70}
|
||||
:render-fn contact-item-render
|
||||
:scroll-enabled @scroll-enabled
|
||||
:on-scroll on-scroll}])]
|
||||
(when contacts-selected?
|
||||
[button/button
|
||||
{:type :primary
|
||||
:style style/chat-button
|
||||
:accessibility-label :next-button
|
||||
:on-press (fn []
|
||||
(if one-contact-selected?
|
||||
(rf/dispatch [:chat.ui/start-chat public-key])
|
||||
(rf/dispatch [:navigate-to :new-group])))}
|
||||
(if one-contact-selected?
|
||||
(i18n/label :t/chat-with {:selected-user primary-name})
|
||||
(i18n/label :t/setup-group-chat))])]))
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[status-im2.constants :as constants]
|
||||
[status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im2.contexts.chat.messages.avatar.view :as avatar]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.ui2.screens.chat.components.reply.style :as style]
|
||||
[react-native.linear-gradient :as linear-gradient]))
|
||||
@@ -65,9 +65,9 @@
|
||||
(i18n/label :t/message-deleted)]])
|
||||
|
||||
(defn reply-from
|
||||
[{:keys [from identicon contact-name current-public-key]}]
|
||||
[{:keys [from contact-name current-public-key]}]
|
||||
[rn/view {:style style/reply-from}
|
||||
[photos/member-photo from identicon 16]
|
||||
[avatar/avatar from :xxxs]
|
||||
[quo2.text/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-2
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
(ns status-im.ui2.screens.chat.composer.images.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
(ns status-im.ui2.screens.chat.composer.images.style)
|
||||
|
||||
(def remove-photo-container
|
||||
{:width 14
|
||||
:height 14
|
||||
:border-radius 7
|
||||
:background-color colors/neutral-50
|
||||
:position :absolute
|
||||
:top 5
|
||||
:right 5
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
{:position :absolute
|
||||
:top 5
|
||||
:right 5})
|
||||
|
||||
(def small-image
|
||||
{:width 56
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
:left 5
|
||||
:top 10
|
||||
:bottom 10}}
|
||||
[quo/icon :i/close {:color colors/white :size 12}]]])
|
||||
[quo/icon :i/clear
|
||||
{:size 20
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]]])
|
||||
|
||||
(defn images-list
|
||||
[images]
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
|
||||
(defn system-text?
|
||||
[content-type]
|
||||
(= content-type constants/content-type-system-text))
|
||||
(or
|
||||
(= content-type constants/content-type-system-text)
|
||||
(= content-type constants/content-type-system-pinned-message)))
|
||||
|
||||
(defn mention-element
|
||||
[from]
|
||||
@@ -142,11 +144,9 @@
|
||||
(:parsed-text content))))
|
||||
|
||||
(defn quoted-message
|
||||
[{:keys [message-id chat-id]} pin?]
|
||||
(let [quoted-message (get @(re-frame/subscribe [:chats/chat-messages chat-id])
|
||||
message-id)]
|
||||
[rn/view {:style (when-not pin? (style/quoted-message-container))}
|
||||
[components.reply/reply-message quoted-message false pin?]]))
|
||||
[quoted-message pin?]
|
||||
[rn/view {:style (when-not pin? (style/quoted-message-container))}
|
||||
[components.reply/reply-message quoted-message false pin?]])
|
||||
|
||||
(defn message-not-sent-text
|
||||
[chat-id message-id]
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
(ns status-im.ui2.screens.chat.pin-limit-popover.view
|
||||
(:require [utils.i18n :as i18n]
|
||||
[quo.react :as react]
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.ui2.screens.chat.pin-limit-popover.style :as style]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
;; TODO (flexsurfer) this should be an in-app notification component in quo2
|
||||
;; https://github.com/status-im/status-mobile/issues/14527
|
||||
(defn pin-limit-popover
|
||||
[chat-id]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [width (rf/sub [:dimensions/window-width])
|
||||
show-pin-limit-modal? (rf/sub [:chats/pin-modal chat-id])
|
||||
opacity-animation (reanimated/use-shared-value 0)
|
||||
z-index-animation (reanimated/use-shared-value -1)]
|
||||
(react/effect!
|
||||
#(do
|
||||
(reanimated/set-shared-value opacity-animation
|
||||
(reanimated/with-timing (if show-pin-limit-modal? 1 0)))
|
||||
(reanimated/set-shared-value z-index-animation
|
||||
(reanimated/with-timing (if show-pin-limit-modal? 10 -1)))))
|
||||
(when show-pin-limit-modal?
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity opacity-animation
|
||||
:z-index z-index-animation}
|
||||
(style/pin-popover width))
|
||||
:accessibility-label :pin-limit-popover}
|
||||
[rn/view {:style (style/pin-alert-container)}
|
||||
[rn/view {:style style/pin-alert-circle}
|
||||
[rn/text {:style {:color colors/danger-50}} "!"]]]
|
||||
[rn/view {:style {:margin-left 8}}
|
||||
[quo/text {:weight :semi-bold :color (colors/theme-colors colors/white colors/neutral-100)}
|
||||
(i18n/label :t/cannot-pin-title)]
|
||||
[quo/text {:size :paragraph-2 :color (colors/theme-colors colors/white colors/neutral-100)}
|
||||
(i18n/label :t/cannot-pin-desc)]
|
||||
[rn/touchable-opacity
|
||||
{:accessibility-label :view-pinned-messages
|
||||
:active-opacity 1
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:pin-message/hide-pin-limit-modal chat-id])
|
||||
(rf/dispatch [:pin-message/show-pins-bottom-sheet chat-id])
|
||||
(rf/dispatch [:dismiss-keyboard]))
|
||||
:style style/view-pinned-messages}
|
||||
[quo/text {:size :paragraph-2 :weight :medium :color colors/white}
|
||||
(i18n/label :t/view-pinned-messages)]]]
|
||||
[rn/touchable-opacity
|
||||
{:accessibility-label :close-pin-limit-popover
|
||||
:active-opacity 1
|
||||
:on-press #(rf/dispatch [:pin-message/hide-pin-limit-modal chat-id])
|
||||
:style {:position :absolute
|
||||
:top 16
|
||||
:right 16}}
|
||||
[quo/icon :i/close
|
||||
{:color (colors/theme-colors colors/white colors/neutral-100)
|
||||
:size 12}]]])))])
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
(defn view
|
||||
[{:keys [hide? insets]} {:keys [content override-theme selected-item]}]
|
||||
(let [{window-height :height} (rn/use-window-dimensions)
|
||||
(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)]
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
(:require
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.hooks :as hooks]
|
||||
[react-native.navigation :as navigation]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[oops.core :as oops]
|
||||
@@ -46,38 +45,35 @@
|
||||
(let [y (oops/oget e "nativeEvent.contentOffset.y")]
|
||||
(reset! curr-scroll y)))
|
||||
|
||||
(defn view
|
||||
(defn f-view
|
||||
[content skip-background?]
|
||||
[:f>
|
||||
(let [scroll-enabled (reagent/atom true)
|
||||
curr-scroll (atom 0)]
|
||||
(fn []
|
||||
(let [sb-height (navigation/status-bar-height)
|
||||
insets (safe-area/use-safe-area)
|
||||
padding-top (Math/max sb-height (:top insets))
|
||||
padding-top (if platform/ios? padding-top (+ padding-top 10))
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
translate-y (reanimated/use-shared-value 0)
|
||||
close (fn []
|
||||
(reanimated/set-shared-value opacity (reanimated/with-timing-duration 0 100))
|
||||
(rf/dispatch [:navigate-back]))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reanimated/animate-delay opacity 1 (if platform/ios? 300 100))))
|
||||
(hooks/use-back-handler close)
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:padding-top padding-top}}
|
||||
(when-not skip-background?
|
||||
[reanimated/view {:style (style/background opacity)}])
|
||||
|
||||
(let [scroll-enabled (reagent/atom true)
|
||||
curr-scroll (atom 0)]
|
||||
(fn []
|
||||
(let [insets (safe-area/get-insets)
|
||||
padding-top (:top insets)
|
||||
padding-top (if platform/ios? padding-top (+ padding-top 10))
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
translate-y (reanimated/use-shared-value 0)
|
||||
close (fn []
|
||||
(reanimated/set-shared-value opacity (reanimated/with-timing-duration 0 100))
|
||||
(rf/dispatch [:navigate-back]))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reanimated/animate-delay opacity 1 (if platform/ios? 300 100))))
|
||||
(hooks/use-back-handler close)
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:padding-top padding-top}}
|
||||
(when-not skip-background?
|
||||
[reanimated/view {:style (style/background opacity)}])
|
||||
[gesture/gesture-detector
|
||||
{:gesture (drag-gesture translate-y opacity scroll-enabled curr-scroll)}
|
||||
[reanimated/view {:style (style/main-view translate-y)}
|
||||
[gesture/gesture-detector
|
||||
{:gesture (drag-gesture translate-y opacity scroll-enabled curr-scroll)}
|
||||
[rn/view {:style style/handle-container}
|
||||
[rn/view {:style (style/handle)}]]]
|
||||
[rn/view {:style style/handle-container}
|
||||
[rn/view {:style (style/handle)}]]
|
||||
[content
|
||||
{:insets insets
|
||||
:close close
|
||||
:scroll-enabled @scroll-enabled
|
||||
:on-scroll #(on-scroll % curr-scroll)}]]])))])
|
||||
:scroll-enabled scroll-enabled
|
||||
:on-scroll #(on-scroll % curr-scroll)}]]]]))))
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
(max minimum)
|
||||
(min maximum)))
|
||||
|
||||
(defn scroll-page-header
|
||||
(defn f-scroll-page-header
|
||||
[scroll-height height name page-nav logo sticky-header top-nav title-colum navigate-back?]
|
||||
(let [input-range (if platform/ios? [-47 10] [0 10])
|
||||
output-range (if platform/ios? [-208 0] [-208 -45])
|
||||
@@ -88,7 +88,7 @@
|
||||
sticky-header]]))
|
||||
|
||||
|
||||
(defn display-picture
|
||||
(defn f-display-picture
|
||||
[scroll-height cover]
|
||||
(let [input-range (if platform/ios? [-67 10] [0 150])
|
||||
y (reanimated/use-shared-value scroll-height)
|
||||
@@ -115,7 +115,7 @@
|
||||
sticky-header
|
||||
children]
|
||||
[:<>
|
||||
[:f> scroll-page-header @scroll-height height name page-nav-right-section-buttons
|
||||
[:f> f-scroll-page-header @scroll-height height name page-nav-right-section-buttons
|
||||
logo sticky-header top-nav title-colum navigate-back?]
|
||||
[rn/scroll-view
|
||||
{:content-container-style (style/scroll-view-container
|
||||
@@ -144,5 +144,5 @@
|
||||
:border-radius (diff-with-max-min @scroll-height 16 0)
|
||||
:background-color background-color}
|
||||
(when cover-image
|
||||
[:f> display-picture @scroll-height logo])
|
||||
[:f> f-display-picture @scroll-height logo])
|
||||
children])]])))
|
||||
|
||||