feat: integrate record audio component into composer
Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
parent
f67f205fa9
commit
7332f483a4
|
@ -147,7 +147,10 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
|||
:SvgXml #js {:render identity}
|
||||
:default #js {:render identity}})
|
||||
(def react-native-webview #js {:default {}})
|
||||
(def react-native-audio-toolkit #js {:MediaStates {}})
|
||||
(def react-native-audio-toolkit
|
||||
#js
|
||||
{:MediaStates {}
|
||||
:PlaybackCategories {}})
|
||||
(def net-info #js {})
|
||||
(def touchid #js {})
|
||||
(def react-native-image-viewing #js {:default {}})
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 0}})
|
||||
(-> (h/expect event)
|
||||
(.toHaveBeenCalledTimes 1))))
|
||||
|
||||
|
@ -36,19 +37,23 @@
|
|||
(h/render [record-audio/record-audio
|
||||
{:on-reviewing-audio event
|
||||
:record-audio-permission-granted true}])
|
||||
(with-redefs [audio/start-recording (fn [_ on-start _]
|
||||
(on-start))]
|
||||
(with-redefs [audio/start-recording (fn [_ on-start _]
|
||||
(on-start))
|
||||
audio/get-recorder-file-path (fn [] "file-path")]
|
||||
(h/fire-event
|
||||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 0}})
|
||||
(h/advance-timers-by-time 500)
|
||||
(h/fire-event
|
||||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 200}})
|
||||
(h/advance-timers-by-time 250)
|
||||
(-> (h/expect event)
|
||||
(.toHaveBeenCalledTimes 1)))))
|
||||
|
||||
|
@ -59,27 +64,40 @@
|
|||
:record-audio-permission-granted true}])
|
||||
(with-redefs [audio/start-recording (fn [_ on-start _]
|
||||
(on-start))
|
||||
audio/get-recorder-file-path (fn [] "audio-file-path")]
|
||||
audio/get-recorder-file-path (fn [] "audio-file-path")
|
||||
audio/get-player-duration (fn [] 5000)]
|
||||
(h/fire-event
|
||||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 0}})
|
||||
(h/advance-timers-by-time 500)
|
||||
(h/fire-event
|
||||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 200}})
|
||||
(h/fire-event
|
||||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70
|
||||
:timestamp 0}})
|
||||
(h/advance-timers-by-time 500)
|
||||
(h/fire-event
|
||||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 80
|
||||
:locationY 80}})
|
||||
:locationY 80
|
||||
:timestamp 200}})
|
||||
(h/advance-timers-by-time 250)
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalledTimes 1))
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalledWith "audio-file-path")))))
|
||||
(.toHaveBeenCalledWith {:file-path "audio-file-path"
|
||||
:duration 5000})))))
|
||||
|
||||
(h/test "record-audio on-send works after sliding to the send button"
|
||||
(let [event (js/jest.fn)]
|
||||
|
@ -95,7 +113,8 @@
|
|||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 0}})
|
||||
(h/advance-timers-by-time 500)
|
||||
(h/fire-event
|
||||
:on-responder-move
|
||||
|
@ -108,11 +127,14 @@
|
|||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 40
|
||||
:locationY 80}})
|
||||
:locationY 80
|
||||
:timestamp 200}})
|
||||
(h/advance-timers-by-time 250)
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalledTimes 1))
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalledWith "audio-file-path")))))
|
||||
(.toHaveBeenCalledWith {:file-path "audio-file-path"
|
||||
:duration 500})))))
|
||||
|
||||
(h/test "record-audio on-cancel works after reviewing audio"
|
||||
(let [event (js/jest.fn)]
|
||||
|
@ -125,18 +147,22 @@
|
|||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 0}})
|
||||
(h/advance-timers-by-time 500)
|
||||
(h/fire-event
|
||||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 200}})
|
||||
(h/fire-event
|
||||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 40
|
||||
:locationY 80}})
|
||||
:locationY 80
|
||||
:timestamp 200}})
|
||||
(h/advance-timers-by-time 250)
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalledTimes 1)))))
|
||||
|
||||
|
@ -153,7 +179,8 @@
|
|||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX 70
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 0}})
|
||||
(h/advance-timers-by-time 500)
|
||||
(h/fire-event
|
||||
:on-responder-move
|
||||
|
@ -166,6 +193,8 @@
|
|||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
{:nativeEvent {:locationX -10
|
||||
:locationY 70}})
|
||||
:locationY 70
|
||||
:timestamp 200}})
|
||||
(h/advance-timers-by-time 250)
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalledTimes 1))))))
|
||||
|
|
|
@ -4,58 +4,60 @@
|
|||
[quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.core :refer [use-effect]]
|
||||
[quo2.components.record-audio.record-audio.helpers :refer
|
||||
[animate-linear-with-delay
|
||||
animate-easing-with-delay
|
||||
animate-linear
|
||||
set-value]]))
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn delete-button
|
||||
[recording? ready-to-delete? reviewing-audio?]
|
||||
[recording? ready-to-delete? reviewing-audio? force-show-controls?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [opacity (reanimated/use-shared-value 0)
|
||||
translate-x (reanimated/use-shared-value 20)
|
||||
scale (reanimated/use-shared-value 1)
|
||||
(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 []
|
||||
(animate-linear-with-delay translate-x 12 50 133.33)
|
||||
(animate-easing-with-delay connector-opacity 1 0 93.33)
|
||||
(animate-easing-with-delay connector-width 56 83.33 80)
|
||||
(animate-easing-with-delay connector-height 56 83.33 80)
|
||||
(animate-easing-with-delay border-radius-first-half 28 83.33 80)
|
||||
(animate-easing-with-delay border-radius-second-half 28 83.33 80))
|
||||
(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 []
|
||||
(animate-linear translate-x 0 100)
|
||||
(set-value connector-opacity 0)
|
||||
(set-value connector-width 24)
|
||||
(set-value connector-height 12)
|
||||
(set-value border-radius-first-half 8)
|
||||
(set-value border-radius-second-half 16))
|
||||
(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 []
|
||||
(animate-linear translate-x 0 200)
|
||||
(animate-linear opacity 1 200))
|
||||
(helpers/animate-linear translate-x 0 200)
|
||||
(helpers/animate-linear opacity 1 200))
|
||||
fade-out-animation (fn []
|
||||
(animate-linear
|
||||
(helpers/animate-linear
|
||||
translate-x
|
||||
(if @reviewing-audio? 35 20)
|
||||
200)
|
||||
(if @reviewing-audio?
|
||||
(animate-linear scale 0.75 200)
|
||||
(animate-linear opacity 0 200))
|
||||
(set-value connector-opacity 0)
|
||||
(set-value connector-width 24)
|
||||
(set-value connector-height 12)
|
||||
(set-value border-radius-first-half 8)
|
||||
(set-value border-radius-second-half 16))
|
||||
(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 []
|
||||
(animate-linear opacity 0 200)
|
||||
(animate-linear-with-delay translate-x 20 0 200)
|
||||
(animate-linear-with-delay scale 1 0 200))]
|
||||
(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)
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
[quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.core :refer [use-effect]]
|
||||
[quo2.components.record-audio.record-audio.helpers :refer
|
||||
[animate-linear-with-delay
|
||||
animate-easing-with-delay
|
||||
animate-linear
|
||||
set-value]]))
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn lock-button
|
||||
[recording? ready-to-lock? locked?]
|
||||
|
@ -22,36 +18,36 @@
|
|||
border-radius-first-half (reanimated/use-shared-value 8)
|
||||
border-radius-second-half (reanimated/use-shared-value 8)
|
||||
start-x-y-animation (fn []
|
||||
(animate-linear-with-delay translate-x-y 8 50 116.66)
|
||||
(animate-easing-with-delay connector-opacity 1 0 80)
|
||||
(animate-easing-with-delay width 56 83.33 63.33)
|
||||
(animate-easing-with-delay height 56 83.33 63.33)
|
||||
(animate-easing-with-delay border-radius-first-half
|
||||
28
|
||||
83.33
|
||||
63.33)
|
||||
(animate-easing-with-delay border-radius-second-half
|
||||
28
|
||||
83.33
|
||||
63.33))
|
||||
(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 []
|
||||
(animate-linear translate-x-y 0 100)
|
||||
(set-value connector-opacity 0)
|
||||
(set-value width 24)
|
||||
(set-value height 12)
|
||||
(set-value border-radius-first-half 8)
|
||||
(set-value border-radius-second-half 16))
|
||||
(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 []
|
||||
(animate-linear translate-x-y 0 220)
|
||||
(animate-linear opacity 1 220))
|
||||
(helpers/animate-linear translate-x-y 0 220)
|
||||
(helpers/animate-linear opacity 1 220))
|
||||
fade-out-animation (fn []
|
||||
(animate-linear translate-x-y 20 200)
|
||||
(animate-linear opacity 0 200)
|
||||
(set-value connector-opacity 0)
|
||||
(set-value width 24)
|
||||
(set-value height 12)
|
||||
(set-value border-radius-first-half 8)
|
||||
(set-value border-radius-second-half 16))]
|
||||
(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)
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
[react-native.core :as rn :refer [use-effect]]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.record-audio.record-audio.helpers :refer [set-value]]))
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn record-button
|
||||
[recording? reviewing-audio?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [opacity (reanimated/use-shared-value 1)
|
||||
show-animation #(set-value opacity 1)
|
||||
hide-animation #(set-value opacity 0)]
|
||||
show-animation #(helpers/set-value opacity 1)
|
||||
hide-animation #(helpers/set-value opacity 0)]
|
||||
(use-effect (fn []
|
||||
(if (or @recording? @reviewing-audio?)
|
||||
(hide-animation)
|
||||
|
@ -25,4 +25,4 @@
|
|||
:size 32
|
||||
:width 32
|
||||
:accessibility-label :mic-button}
|
||||
[icons/icon :i/audio {:color (colors/theme-colors colors/neutral-100 colors/white)}]]]))])
|
||||
[icons/icon :i/audio {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]]))])
|
||||
|
|
|
@ -8,12 +8,7 @@
|
|||
[taoensso.timbre :as log]
|
||||
[cljs-bean.core :as bean]
|
||||
[reagent.core :as reagent]
|
||||
[quo2.components.record-audio.record-audio.helpers :refer
|
||||
[animate-linear
|
||||
animate-linear-with-delay
|
||||
animate-linear-with-delay-loop
|
||||
animate-easing
|
||||
set-value]]))
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(def ^:private scale-to-each 1.8)
|
||||
(def ^:private scale-to-total 2.6)
|
||||
|
@ -45,129 +40,131 @@
|
|||
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 (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)
|
||||
(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 (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 (audio/get-recorder-file-path @recorder-ref)))
|
||||
@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 []
|
||||
(set-value opacity 1)
|
||||
(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 []
|
||||
(set-value scale scale-to-each)
|
||||
(animate-linear-with-delay-loop scale
|
||||
scale-to-total
|
||||
signal-anim-duration-2
|
||||
0))
|
||||
signal-anim-duration)))
|
||||
stop-animation (fn []
|
||||
(set-value opacity 0)
|
||||
(reanimated/cancel-animation scale)
|
||||
(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)
|
||||
(animate-easing translate-y -64 250)
|
||||
(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 []
|
||||
(animate-easing translate-y 0 300)
|
||||
(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)
|
||||
(animate-easing translate-x -64 250)
|
||||
(animate-linear-with-delay icon-opacity 0 33.33 76.66)
|
||||
(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 []
|
||||
(animate-easing translate-x 0 300)
|
||||
(animate-linear icon-opacity 1 500)
|
||||
(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)
|
||||
(animate-easing translate-y -44 200)
|
||||
(animate-easing translate-x -44 200)
|
||||
(animate-linear-with-delay icon-opacity 0 33.33 33.33)
|
||||
(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 []
|
||||
(animate-easing translate-y 0 300)
|
||||
(animate-easing translate-x 0 300)
|
||||
(animate-linear icon-opacity 1 500)
|
||||
(animate-linear gray-overlay-opacity 0 800)
|
||||
(js/setTimeout (fn []
|
||||
(reset! record-button-at-initial-position? true))
|
||||
800))]
|
||||
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
|
||||
@recording?
|
||||
|
|
|
@ -4,60 +4,63 @@
|
|||
[quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.core :refer [use-effect]]
|
||||
[quo2.components.record-audio.record-audio.helpers :refer
|
||||
[animate-linear
|
||||
animate-linear-with-delay
|
||||
animate-easing-with-delay
|
||||
set-value]]))
|
||||
[quo2.components.record-audio.record-audio.helpers :as helpers]))
|
||||
|
||||
(defn send-button
|
||||
[recording? ready-to-send? reviewing-audio?]
|
||||
[recording? ready-to-send? reviewing-audio? force-show-controls?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [opacity (reanimated/use-shared-value 0)
|
||||
translate-y (reanimated/use-shared-value 20)
|
||||
(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 []
|
||||
(animate-linear-with-delay translate-y 12 50 133.33)
|
||||
(animate-easing-with-delay connector-opacity 1 0 93.33)
|
||||
(animate-easing-with-delay width 56 83.33 80)
|
||||
(animate-easing-with-delay height 56 83.33 80)
|
||||
(animate-easing-with-delay border-radius-first-half 28 83.33 80)
|
||||
(animate-easing-with-delay border-radius-second-half 28 83.33 80))
|
||||
(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 []
|
||||
(animate-linear translate-y 0 100)
|
||||
(set-value connector-opacity 0)
|
||||
(set-value width 12)
|
||||
(set-value height 24)
|
||||
(set-value border-radius-first-half 16)
|
||||
(set-value border-radius-second-half 8))
|
||||
(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 []
|
||||
(animate-linear translate-y 0 200)
|
||||
(animate-linear opacity 1 200))
|
||||
(helpers/animate-linear translate-y 0 200)
|
||||
(helpers/animate-linear opacity 1 200))
|
||||
fade-out-animation (fn []
|
||||
(animate-linear
|
||||
translate-y
|
||||
(if @reviewing-audio? 76 20)
|
||||
200)
|
||||
(when-not force-show-controls?
|
||||
(helpers/animate-linear
|
||||
translate-y
|
||||
(if @reviewing-audio? 76 20)
|
||||
200))
|
||||
(when-not @reviewing-audio?
|
||||
(animate-linear opacity 0 200))
|
||||
(set-value connector-opacity 0)
|
||||
(set-value width 24)
|
||||
(set-value height 12)
|
||||
(set-value border-radius-first-half 8)
|
||||
(set-value border-radius-second-half 16))
|
||||
(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 []
|
||||
(animate-linear opacity 0 200)
|
||||
(animate-linear-with-delay translate-y 20 0 200)
|
||||
(set-value connector-opacity 0)
|
||||
(set-value width 24)
|
||||
(set-value height 12)
|
||||
(set-value border-radius-first-half 8)
|
||||
(set-value border-radius-second-half 16))]
|
||||
(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)
|
||||
|
|
|
@ -14,19 +14,17 @@
|
|||
[quo2.components.record-audio.record-audio.buttons.send-button :as send-button]
|
||||
[quo2.components.record-audio.record-audio.buttons.lock-button :as lock-button]
|
||||
[quo2.components.record-audio.record-audio.buttons.delete-button :as delete-button]
|
||||
[quo2.components.record-audio.record-audio.buttons.record-button :as record-button]))
|
||||
[quo2.components.record-audio.record-audio.buttons.record-button :as record-button]
|
||||
[react-native.platform :as platform]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(def ^:private min-audio-duration-ms 500)
|
||||
(def ^:private max-audio-duration-ms 120000)
|
||||
(def ^:private metering-interval 100)
|
||||
(def ^:private base-filename "am.")
|
||||
(def ^:private default-format "aac")
|
||||
(def ^:private base-filename "am")
|
||||
(def ^:private default-format ".aac")
|
||||
|
||||
(def ^:private rec-options
|
||||
(merge
|
||||
audio/default-recorder-options
|
||||
{:filename (str base-filename default-format)
|
||||
:meteringInterval metering-interval}))
|
||||
(def min-touch-duration 150)
|
||||
|
||||
(def ^:private record-button-area-big
|
||||
{:width 56
|
||||
|
@ -149,8 +147,10 @@
|
|||
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]))])
|
||||
|
||||
(defn view
|
||||
[{:keys [on-start-recording on-send on-cancel on-reviewing-audio record-audio-permission-granted
|
||||
on-request-record-audio-permission on-check-audio-permissions]}]
|
||||
[{: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>
|
||||
(fn []
|
||||
(let [recording? (reagent/atom false)
|
||||
|
@ -163,30 +163,39 @@
|
|||
recording-length-ms (reagent/atom 0)
|
||||
audio-current-time-ms (reagent/atom 0)
|
||||
seeking-audio? (reagent/atom false)
|
||||
force-show-controls? (reagent/atom false)
|
||||
clear-timeout (atom nil)
|
||||
record-button-at-initial-position? (atom true)
|
||||
record-button-is-animating? (atom false)
|
||||
idle? (atom false)
|
||||
recorder-ref (atom nil)
|
||||
player-ref (atom nil)
|
||||
touch-active? (atom false)
|
||||
recording-timer (atom nil)
|
||||
playing-timer (atom nil)
|
||||
recorder-ref (atom nil)
|
||||
player-ref (atom nil)
|
||||
output-file (atom nil)
|
||||
reached-max-duration? (atom false)
|
||||
touch-timestamp (atom nil)
|
||||
disabled? (atom false)
|
||||
rec-options
|
||||
(merge
|
||||
audio/default-recorder-options
|
||||
{:filename (str base-filename (.now js/Date) default-format)
|
||||
:meteringInterval metering-interval})
|
||||
destroy-player
|
||||
(fn []
|
||||
(audio/destroy-player @player-ref)
|
||||
(reset! player-ref nil))
|
||||
reload-player
|
||||
(fn []
|
||||
(fn [audio-file]
|
||||
(when @player-ref
|
||||
(destroy-player))
|
||||
(reset! player-ref
|
||||
(audio/new-player
|
||||
(:filename rec-options)
|
||||
(or audio-file (:filename rec-options))
|
||||
{:autoDestroy false
|
||||
:continuesToPlayInBackground false}
|
||||
:continuesToPlayInBackground false
|
||||
:category audio/PLAYBACK}
|
||||
(fn []
|
||||
(reset! playing-audio? false)
|
||||
(when @playing-timer
|
||||
|
@ -210,9 +219,37 @@
|
|||
rec-options
|
||||
#(log/debug "[record-audio] new recorder - on meter")
|
||||
#(log/debug "[record-audio] new recorder - on ended"))))
|
||||
reset-recorder
|
||||
(fn []
|
||||
(reset! recording? false)
|
||||
(reset! reviewing-audio? false)
|
||||
(reset! locked? false)
|
||||
(reset! ready-to-send? false)
|
||||
(reset! ready-to-lock? false)
|
||||
(reset! ready-to-delete? false)
|
||||
(reset! audio-current-time-ms 0)
|
||||
(reset! recording-length-ms 0)
|
||||
(reset! seeking-audio? false)
|
||||
(reset! playing-audio? false)
|
||||
(reset! touch-active? false)
|
||||
(reset! reached-max-duration? false)
|
||||
(reset! output-file nil)
|
||||
(reset! idle? false)
|
||||
(reset! record-button-is-animating? false)
|
||||
(reset! record-button-at-initial-position? true)
|
||||
(when @clear-timeout
|
||||
(js/clearTimeout @clear-timeout)
|
||||
(reset! clear-timeout nil))
|
||||
(when @recording-timer
|
||||
(js/clearInterval @recording-timer)
|
||||
(reset! recording-timer nil))
|
||||
(when @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil))
|
||||
(reload-recorder))
|
||||
on-start-should-set-responder
|
||||
(fn [^js e]
|
||||
(when-not (or @locked? @idle? (nil? e))
|
||||
(when-not (or @locked? @idle? (nil? e) @disabled?)
|
||||
(let [pressed-record-button? (touch-inside-area?
|
||||
{:location-x (oops/oget e "nativeEvent.locationX")
|
||||
:location-y (oops/oget e "nativeEvent.locationY")
|
||||
|
@ -220,7 +257,12 @@
|
|||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? false}
|
||||
record-button-area)]
|
||||
record-button-area)
|
||||
new-recorder (audio/new-recorder
|
||||
rec-options
|
||||
#(log/debug "[record-audio] new recorder - on meter")
|
||||
#(log/debug "[record-audio] new recorder - on ended"))]
|
||||
(reset! touch-timestamp (oops/oget e "nativeEvent.timestamp"))
|
||||
(when-not @reviewing-audio?
|
||||
(if record-audio-permission-granted
|
||||
(do
|
||||
|
@ -231,8 +273,9 @@
|
|||
(when @recording-timer
|
||||
(js/clearInterval @recording-timer))
|
||||
(reset! output-file nil)
|
||||
(reset! recorder-ref new-recorder)
|
||||
(audio/start-recording
|
||||
@recorder-ref
|
||||
new-recorder
|
||||
(fn []
|
||||
(reset! audio-current-time-ms 0)
|
||||
(reset! recording-timer
|
||||
|
@ -251,28 +294,29 @@
|
|||
(reset! ready-to-send? false)
|
||||
(reset! ready-to-delete? false)
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
new-recorder
|
||||
(fn []
|
||||
(reset! output-file (audio/get-recorder-file-path
|
||||
@recorder-ref))
|
||||
(reload-recorder)
|
||||
(reload-player)
|
||||
new-recorder))
|
||||
(reload-player nil)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(js/clearInterval @recording-timer)
|
||||
(reset! recording-length-ms 0)
|
||||
(when on-reviewing-audio
|
||||
(on-reviewing-audio)))))
|
||||
(on-reviewing-audio (audio/get-recorder-file-path
|
||||
new-recorder))))))
|
||||
metering-interval))
|
||||
(log/debug "[record-audio] start recording - success"))
|
||||
#(log/error "[record-audio] start recording - error: " %))
|
||||
(when on-start-recording
|
||||
(on-start-recording))))
|
||||
(some-> on-request-record-audio-permission)))
|
||||
(when on-request-record-audio-permission
|
||||
(on-request-record-audio-permission))))
|
||||
(when record-audio-permission-granted
|
||||
(reset! touch-active? true))))
|
||||
(not @idle?))
|
||||
(and (not @idle?) (not @disabled?)))
|
||||
on-responder-move
|
||||
(fn [^js e]
|
||||
(when-not @locked?
|
||||
|
@ -349,43 +393,49 @@
|
|||
(when (and
|
||||
(not @idle?)
|
||||
(not @reached-max-duration?))
|
||||
(let [touch-area {:location-x (oops/oget e "nativeEvent.locationX")
|
||||
:location-y (oops/oget e "nativeEvent.locationY")
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? false}
|
||||
on-record-button? (touch-inside-area?
|
||||
touch-area
|
||||
record-button-area-big)
|
||||
on-send-button? (touch-inside-area?
|
||||
touch-area
|
||||
(send-button-area
|
||||
{:active? false
|
||||
:reviewing-audio? true}))
|
||||
on-delete-button? (touch-inside-area?
|
||||
touch-area
|
||||
(delete-button-area
|
||||
{:active? false
|
||||
:reviewing-audio? true}))]
|
||||
(let [touch-area {:location-x (oops/oget e "nativeEvent.locationX")
|
||||
:location-y (oops/oget e "nativeEvent.locationY")
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? false}
|
||||
on-record-button? (touch-inside-area?
|
||||
touch-area
|
||||
record-button-area-big)
|
||||
on-send-button? (touch-inside-area?
|
||||
touch-area
|
||||
(send-button-area
|
||||
{:active? false
|
||||
:reviewing-audio? true}))
|
||||
on-delete-button? (touch-inside-area?
|
||||
touch-area
|
||||
(delete-button-area
|
||||
{:active? false
|
||||
:reviewing-audio? true}))
|
||||
release-touch-timestamp (oops/oget e "nativeEvent.timestamp")
|
||||
touch-timestamp-diff (- release-touch-timestamp @touch-timestamp)
|
||||
audio-length @recording-length-ms]
|
||||
(cond
|
||||
(and @reviewing-audio? on-send-button?)
|
||||
(do
|
||||
(reset! reviewing-audio? false)
|
||||
(reset! audio-current-time-ms 0)
|
||||
(reset! force-show-controls? false)
|
||||
(when on-send
|
||||
(on-send {:file-path @output-file
|
||||
:duration (int (audio/get-player-duration @player-ref))}))
|
||||
(when @player-ref
|
||||
(audio/stop-playing
|
||||
@player-ref
|
||||
(fn []
|
||||
(destroy-player)
|
||||
(log/debug "[record-audio] stop playing - success"))
|
||||
#(log/error "[record-audio] stop playing - error: " %)))
|
||||
(when on-send
|
||||
(on-send @output-file)))
|
||||
#(log/error "[record-audio] stop playing - error: " %))))
|
||||
(and @reviewing-audio? on-delete-button?)
|
||||
(do
|
||||
(reset! reviewing-audio? false)
|
||||
(reset! audio-current-time-ms 0)
|
||||
(reset! force-show-controls? false)
|
||||
(destroy-player)
|
||||
(when on-cancel
|
||||
(on-cancel)))
|
||||
|
@ -393,61 +443,86 @@
|
|||
(do
|
||||
(reset! locked? true)
|
||||
(reset! ready-to-lock? false))
|
||||
(and (not @reviewing-audio?) on-record-button?)
|
||||
(and (not @reviewing-audio?)
|
||||
(or on-record-button?
|
||||
(and (not @ready-to-delete?)
|
||||
(not @ready-to-lock?)
|
||||
(not @ready-to-send?))))
|
||||
(do
|
||||
(if (>= @recording-length-ms min-audio-duration-ms)
|
||||
(do (reset! reviewing-audio? true)
|
||||
(reset! idle? false)
|
||||
(when on-reviewing-audio
|
||||
(on-reviewing-audio)))
|
||||
(do (when on-cancel
|
||||
(on-cancel))
|
||||
(reset! idle? true)))
|
||||
(reset! locked? false)
|
||||
(reset! recording? false)
|
||||
(reset! ready-to-lock? false)
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(reset! disabled? (<= touch-timestamp-diff min-touch-duration))
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(reset! output-file (audio/get-recorder-file-path @recorder-ref))
|
||||
(reload-recorder)
|
||||
(reload-player)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(js/clearInterval @recording-timer)
|
||||
(reset! recording-length-ms 0))
|
||||
(if (>= @recording-length-ms min-audio-duration-ms)
|
||||
(do (reset! reviewing-audio? true)
|
||||
(reset! idle? false)
|
||||
(when on-reviewing-audio
|
||||
(on-reviewing-audio (audio/get-recorder-file-path @recorder-ref))))
|
||||
(do (when on-cancel
|
||||
(on-cancel))
|
||||
(reset! idle? true)))
|
||||
(reset! locked? false)
|
||||
(reset! recording? false)
|
||||
(reset! ready-to-lock? false)
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(fn []
|
||||
(reset! output-file (audio/get-recorder-file-path @recorder-ref))
|
||||
(when (>= audio-length min-audio-duration-ms)
|
||||
(reload-player nil))
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(js/clearInterval @recording-timer)
|
||||
(reset! recording-length-ms 0)
|
||||
(reset! disabled? false))
|
||||
(if (> touch-timestamp-diff min-touch-duration) 0 250)))
|
||||
(and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?))
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(fn []
|
||||
(cond
|
||||
@ready-to-send?
|
||||
(when on-send
|
||||
(on-send (audio/get-recorder-file-path @recorder-ref)))
|
||||
@ready-to-delete?
|
||||
(when on-cancel
|
||||
(on-cancel)))
|
||||
(reload-recorder)
|
||||
(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: " %))))
|
||||
(do
|
||||
(reset! disabled? (<= touch-timestamp-diff min-touch-duration))
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(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)))
|
||||
(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)
|
||||
(reset! disabled? false)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %)))
|
||||
(if (> touch-timestamp-diff min-touch-duration) 0 250)))))
|
||||
(reset! touch-active? false))
|
||||
(when @reached-max-duration?
|
||||
(reset! reached-max-duration? false)))]
|
||||
(reset! reached-max-duration? false))
|
||||
(reset! touch-timestamp nil))]
|
||||
(fn []
|
||||
(use-effect (fn []
|
||||
(some-> on-check-audio-permissions)
|
||||
(reload-recorder)))
|
||||
(when on-check-audio-permissions
|
||||
(on-check-audio-permissions))
|
||||
(when on-init
|
||||
(on-init reset-recorder))
|
||||
(when audio-file
|
||||
(let [filename (last (string/split audio-file "/"))]
|
||||
(reload-player filename)
|
||||
(reset! output-file audio-file)
|
||||
(reset! reviewing-audio? true)
|
||||
(reset! force-show-controls? true)))))
|
||||
[rn/view
|
||||
{:style style/bar-container}
|
||||
{:style style/bar-container
|
||||
:pointer-events :box-none}
|
||||
(when @reviewing-audio?
|
||||
[:<>
|
||||
[play-button playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio?]
|
||||
|
@ -463,13 +538,18 @@
|
|||
[rn/view
|
||||
{:test-ID "record-audio"
|
||||
:style style/button-container
|
||||
:hit-slop {:top (if platform/ios? -70 0)
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0}
|
||||
:pointer-events :box-only
|
||||
: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?]
|
||||
[delete-button/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?]
|
||||
[send-button/send-button recording? ready-to-send? reviewing-audio? @force-show-controls?]
|
||||
[record-button-big/record-button-big
|
||||
recording?
|
||||
ready-to-send?
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
quo2.components.profile.profile-card.view
|
||||
quo2.components.profile.select-profile.view
|
||||
quo2.components.reactions.reaction
|
||||
quo2.components.record-audio.record-audio.view
|
||||
quo2.components.selectors.disclaimer.view
|
||||
quo2.components.selectors.filter.view
|
||||
quo2.components.selectors.selectors
|
||||
|
@ -162,6 +163,9 @@
|
|||
(def profile-card quo2.components.profile.profile-card.view/profile-card)
|
||||
(def select-profile quo2.components.profile.select-profile.view/view)
|
||||
|
||||
;;;; RECORD AUDIO
|
||||
(def record-audio quo2.components.record-audio.record-audio.view/record-audio)
|
||||
|
||||
;;;; SETTINGS
|
||||
(def privacy-option quo2.components.settings.privacy-option/card)
|
||||
(def account quo2.components.settings.accounts.view/account)
|
||||
|
|
|
@ -72,6 +72,9 @@
|
|||
(def neutral-80-opa-90 (alpha neutral-80 0.9))
|
||||
(def neutral-80-opa-95 (alpha neutral-80 0.95))
|
||||
|
||||
;;90 with transparency
|
||||
(def neutral-90-opa-0 (alpha neutral-90 0))
|
||||
|
||||
;;95 with transparency
|
||||
(def neutral-95-opa-60 (alpha neutral-95 0.6))
|
||||
(def neutral-95-opa-70 (alpha neutral-95 0.7))
|
||||
|
@ -98,6 +101,7 @@
|
|||
(def white "#ffffff")
|
||||
|
||||
;; with transparency
|
||||
(def white-opa-0 (alpha white 0))
|
||||
(def white-opa-5 (alpha white 0.05))
|
||||
(def white-opa-10 (alpha white 0.1))
|
||||
(def white-opa-20 (alpha white 0.2))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns react-native.audio-toolkit
|
||||
(:require ["@react-native-community/audio-toolkit" :refer (Player Recorder MediaStates)]))
|
||||
(:require ["@react-native-community/audio-toolkit" :refer
|
||||
(Player Recorder MediaStates PlaybackCategories)]))
|
||||
|
||||
;; get mediastates from react module
|
||||
(def PLAYING (.-PLAYING ^js MediaStates))
|
||||
|
@ -11,13 +12,17 @@
|
|||
(def DESTROYED (.-DESTROYED ^js MediaStates))
|
||||
(def SEEKING (.-SEEKING ^js MediaStates))
|
||||
|
||||
;; get PlaybackCategories from react module
|
||||
(def PLAYBACK (.-Playback ^js PlaybackCategories))
|
||||
|
||||
(def default-recorder-options
|
||||
{:filename "recording.aac"
|
||||
:bitrate 32000
|
||||
:channels 1
|
||||
:sampleRate 22050
|
||||
:quality "medium" ; ios only
|
||||
:meteringInterval 50})
|
||||
:meteringInterval 50
|
||||
:category PLAYBACK})
|
||||
|
||||
(defn get-state
|
||||
[player-recorder]
|
||||
|
|
|
@ -195,15 +195,21 @@
|
|||
(chat.message/send-messages messages)))))
|
||||
|
||||
(rf/defn send-audio-message
|
||||
[cofx audio-path duration current-chat-id]
|
||||
(when-not (string/blank? audio-path)
|
||||
(chat.message/send-message
|
||||
cofx
|
||||
{:chat-id current-chat-id
|
||||
:content-type constants/content-type-audio
|
||||
:audio-path audio-path
|
||||
:audio-duration-ms duration
|
||||
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})})))
|
||||
[{:keys [db] :as cofx} audio-path duration current-chat-id]
|
||||
(let [{:keys [message-id]}
|
||||
(get-in db [:chat/inputs current-chat-id :metadata :responding-to-message])]
|
||||
(when-not (string/blank? audio-path)
|
||||
(rf/merge
|
||||
{:db (assoc-in db [:chat/inputs current-chat-id :metadata :responding-to-message] nil)}
|
||||
(chat.message/send-message
|
||||
(merge
|
||||
{:chat-id current-chat-id
|
||||
:content-type constants/content-type-audio
|
||||
:audio-path audio-path
|
||||
:audio-duration-ms duration
|
||||
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})}
|
||||
(when message-id
|
||||
{:response-to message-id})))))))
|
||||
|
||||
(rf/defn send-sticker-message
|
||||
[cofx {:keys [hash packID pack]} current-chat-id]
|
||||
|
@ -293,5 +299,5 @@
|
|||
|
||||
(rf/defn chat-send-audio
|
||||
{:events [:chat/send-audio]}
|
||||
[{{:keys [current-chat-id]} :db :as cofx} audio-path duration]
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx} audio-path duration]
|
||||
(send-audio-message cofx audio-path duration current-chat-id))
|
||||
|
|
|
@ -7,11 +7,18 @@
|
|||
:flex-direction :row})
|
||||
|
||||
(defn quoted-message
|
||||
[pin?]
|
||||
[pin? in-chat-input?]
|
||||
(merge {:flex-direction :row
|
||||
:align-items :center
|
||||
:width "45%"}
|
||||
:width (if in-chat-input? "80%" "45%")}
|
||||
(when-not pin?
|
||||
{:position :absolute
|
||||
:left 34
|
||||
:top 3})))
|
||||
|
||||
(def gradient
|
||||
{:position :absolute
|
||||
:right 0
|
||||
:top 0
|
||||
:bottom 0
|
||||
:width "50%"})
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.ui2.screens.chat.components.reply.style :as style]))
|
||||
[status-im.ui2.screens.chat.components.reply.style :as style]
|
||||
[react-native.linear-gradient :as linear-gradient]))
|
||||
|
||||
(defn get-quoted-text-with-mentions
|
||||
[parsed-text]
|
||||
|
@ -65,7 +66,7 @@
|
|||
|
||||
(defn reply-message
|
||||
[{:keys [from identicon content-type contentType parsed-text content deleted? deleted-for-me?]}
|
||||
in-chat-input? pin?]
|
||||
in-chat-input? pin? recording-audio?]
|
||||
(let [contact-name (rf/sub [:contacts/contact-name-by-identity from])
|
||||
current-public-key (rf/sub [:multiaccount/public-key])
|
||||
content-type (or content-type contentType)]
|
||||
|
@ -80,9 +81,9 @@
|
|||
{:color (colors/theme-colors colors/neutral-40 colors/neutral-60)
|
||||
:container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}])
|
||||
(if (or deleted? deleted-for-me?)
|
||||
[rn/view {:style (style/quoted-message pin?)}
|
||||
[rn/view {:style (style/quoted-message pin? in-chat-input?)}
|
||||
[reply-deleted-message]]
|
||||
[rn/view {:style (style/quoted-message pin?)}
|
||||
[rn/view {:style (style/quoted-message pin? in-chat-input?)}
|
||||
[photos/member-photo from identicon 16]
|
||||
[quo2.text/text
|
||||
{:weight :semi-bold
|
||||
|
@ -109,7 +110,7 @@
|
|||
constants/content-type-sticker "Sticker"
|
||||
constants/content-type-audio "Audio"
|
||||
(get-quoted-text-with-mentions (or parsed-text (:parsed-text content))))]])]
|
||||
(when in-chat-input?
|
||||
(when (and in-chat-input? (not recording-audio?))
|
||||
[quo2.button/button
|
||||
{:width 24
|
||||
:size 24
|
||||
|
@ -120,4 +121,11 @@
|
|||
[icons/icon :main-icons/close
|
||||
{:width 16
|
||||
:height 16
|
||||
:color (colors/theme-colors colors/neutral-100 colors/neutral-40)}]])]))
|
||||
:color (colors/theme-colors colors/neutral-100 colors/neutral-40)}]])
|
||||
(when (and in-chat-input? recording-audio?)
|
||||
[linear-gradient/linear-gradient
|
||||
{:colors [(colors/theme-colors colors/white-opa-0 colors/neutral-90-opa-0)
|
||||
(colors/theme-colors colors/white colors/neutral-90)]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 0.7 :y 0}
|
||||
:style style/gradient}])]))
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
(defonce mentions-enabled? (reagent/atom {}))
|
||||
(defonce chat-input-key (reagent/atom 1))
|
||||
(defonce text-input-ref (reagent/atom nil))
|
||||
(defonce recording-audio? (reagent/atom false))
|
||||
(defonce reviewing-audio? (reagent/atom false))
|
||||
(defonce reviewing-audio-filepath (atom {}))
|
||||
(defonce record-audio-permission-granted (reagent/atom false))
|
||||
(defonce record-audio-reset-fn (atom nil))
|
||||
|
||||
(declare selectable-text-input)
|
||||
|
||||
|
@ -38,18 +43,25 @@
|
|||
.focus))
|
||||
|
||||
(defn show-send
|
||||
[{:keys [actions-ref send-ref sticker-ref]}]
|
||||
[{:keys [actions-ref send-ref sticker-ref record-ref]} chat-id]
|
||||
(when (and (or @recording-audio?
|
||||
(get @reviewing-audio-filepath chat-id))
|
||||
@record-audio-reset-fn)
|
||||
(@record-audio-reset-fn)
|
||||
(swap! reviewing-audio-filepath dissoc chat-id))
|
||||
(when actions-ref
|
||||
(quo.react/set-native-props actions-ref #js {:width 0 :left -88}))
|
||||
(quo.react/set-native-props send-ref #js {:width nil :right nil})
|
||||
(quo.react/set-native-props record-ref #js {:right nil :left -1000})
|
||||
(when sticker-ref
|
||||
(quo.react/set-native-props sticker-ref #js {:width 0 :right -100})))
|
||||
|
||||
(defn hide-send
|
||||
[{:keys [actions-ref send-ref sticker-ref]}]
|
||||
[{:keys [actions-ref send-ref sticker-ref record-ref]}]
|
||||
(when actions-ref
|
||||
(quo.react/set-native-props actions-ref #js {:width nil :left nil}))
|
||||
(quo.react/set-native-props send-ref #js {:width 0 :right -100})
|
||||
(quo.react/set-native-props record-ref #js {:left 0 :right 0})
|
||||
(when sticker-ref
|
||||
(quo.react/set-native-props sticker-ref #js {:width nil :right nil})))
|
||||
|
||||
|
@ -116,7 +128,9 @@
|
|||
(when (and (seq prev-text) (empty? text) (not sending-image))
|
||||
(hide-send refs))
|
||||
(when (and (empty? prev-text) (or (seq text) sending-image))
|
||||
(show-send refs))
|
||||
(show-send refs chat-id)
|
||||
(reset! recording-audio? false)
|
||||
(swap! reviewing-audio-filepath dissoc chat-id))
|
||||
|
||||
(when (and (not (get @mentions-enabled? chat-id)) (string/index-of text "@"))
|
||||
(swap! mentions-enabled? assoc chat-id true))
|
||||
|
@ -159,7 +173,7 @@
|
|||
(rf/dispatch [::mentions/calculate-suggestions mentionable-users]))))
|
||||
|
||||
(defn text-input-style
|
||||
[]
|
||||
[chat-id]
|
||||
(merge typography/font-regular
|
||||
typography/paragraph-1
|
||||
{:flex 1
|
||||
|
@ -172,7 +186,9 @@
|
|||
{:padding-vertical 8
|
||||
:text-align-vertical :top}
|
||||
{:margin-top 8
|
||||
:margin-bottom 8})))
|
||||
:margin-bottom 8})
|
||||
(when (or @recording-audio? (get @reviewing-audio-filepath chat-id))
|
||||
{:display :none})))
|
||||
|
||||
(defn text-input
|
||||
[{:keys [refs chat-id sending-image on-content-size-change]}]
|
||||
|
@ -182,7 +198,7 @@
|
|||
last-text-change (reagent/atom nil)
|
||||
mentions-enabled? (get @mentions-enabled? chat-id)
|
||||
props
|
||||
{:style (text-input-style)
|
||||
{:style (text-input-style chat-id)
|
||||
:ref (:text-input-ref refs)
|
||||
:max-font-size-multiplier 1
|
||||
:accessibility-label :chat-message-input
|
||||
|
|
|
@ -19,6 +19,19 @@
|
|||
(focus-input-on-reply reply had-reply text-input-ref)
|
||||
(when reply
|
||||
[rn/view
|
||||
{:style {:padding-horizontal 15
|
||||
:padding-vertical 8}}
|
||||
[reply/reply-message reply true]]))))
|
||||
{:style (merge
|
||||
{:padding-horizontal 15
|
||||
:padding-vertical 8}
|
||||
(when @input/recording-audio?
|
||||
{:position :absolute
|
||||
:top 12
|
||||
:left 0
|
||||
:right 0
|
||||
;;When recording an audio and replying at the same time,
|
||||
;;text input is overlapped by the reply component but
|
||||
;;text input still have priority over touches, so we need
|
||||
;;to force the reply component to receive the touches in this
|
||||
;;scenario, thus we increase its z-index
|
||||
:z-index 1}))}
|
||||
[reply/reply-message reply true false
|
||||
(and @input/recording-audio? (not @input/reviewing-audio?))]]))))
|
||||
|
|
|
@ -12,3 +12,18 @@
|
|||
:bottom 0
|
||||
:left 0
|
||||
:right 0})
|
||||
|
||||
(def buttons-container
|
||||
{:flex-direction :row
|
||||
:margin-top 12
|
||||
:min-height 32})
|
||||
|
||||
(defn record-audio-container
|
||||
[insets]
|
||||
{:align-items :center
|
||||
:background-color :transparent
|
||||
:flex-direction :row
|
||||
:position :absolute
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom (- (:bottom insets) 7)})
|
||||
|
|
|
@ -7,10 +7,13 @@
|
|||
[status-im2.common.not-implemented :as not-implemented]
|
||||
[status-im2.contexts.chat.messages.composer.controls.style :as style]
|
||||
[status-im2.contexts.chat.messages.list.view :as messages.list]
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[status-im.ui2.screens.chat.composer.images.view :as composer-images]
|
||||
[status-im.utils.utils :as utils-old]
|
||||
[status-im.ui2.screens.chat.composer.input :as input]))
|
||||
[status-im.ui2.screens.chat.composer.input :as input]
|
||||
[status-im2.common.alert.events :as alert]
|
||||
[react-native.permissions :as permissions]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[quo.react :as quo.react]))
|
||||
|
||||
(defn send-button
|
||||
[send-ref {:keys [chat-id images]} on-send]
|
||||
|
@ -56,13 +59,73 @@
|
|||
:size 32}
|
||||
:i/image])
|
||||
|
||||
(defn record-audio
|
||||
[record-ref chat-id]
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[rn/view
|
||||
{:ref record-ref
|
||||
:style (style/record-audio-container insets)
|
||||
:pointer-events :box-none}
|
||||
[quo/record-audio
|
||||
{:record-audio-permission-granted @input/record-audio-permission-granted
|
||||
:on-init (fn [init-fn]
|
||||
(reset! input/record-audio-reset-fn init-fn)
|
||||
(reset! input/recording-audio?
|
||||
(some? (get @input/reviewing-audio-filepath chat-id)))
|
||||
(when (seq (get @input/input-texts chat-id))
|
||||
(js/setTimeout #(quo.react/set-native-props
|
||||
record-ref
|
||||
#js {:right nil :left -1000}))))
|
||||
:on-start-recording #(reset! input/recording-audio? true)
|
||||
:audio-file (get @input/reviewing-audio-filepath chat-id)
|
||||
:on-reviewing-audio (fn [audio-file]
|
||||
(swap! input/reviewing-audio-filepath assoc
|
||||
chat-id
|
||||
audio-file)
|
||||
(reset! input/reviewing-audio? true))
|
||||
:on-send (fn
|
||||
[{:keys [file-path duration]}]
|
||||
(rf/dispatch [:chat/send-audio file-path duration])
|
||||
(reset! input/recording-audio? false)
|
||||
(reset! input/reviewing-audio? false)
|
||||
(swap! input/reviewing-audio-filepath dissoc chat-id))
|
||||
:on-cancel (fn []
|
||||
(reset! input/recording-audio? false)
|
||||
(reset! input/reviewing-audio? false)
|
||||
(swap! input/reviewing-audio-filepath dissoc chat-id))
|
||||
:on-check-audio-permissions (fn []
|
||||
(permissions/permission-granted?
|
||||
:record-audio
|
||||
#(reset! input/record-audio-permission-granted %)
|
||||
#(reset! input/record-audio-permission-granted false)))
|
||||
:on-request-record-audio-permission (fn []
|
||||
(rf/dispatch
|
||||
[:request-permissions
|
||||
{:permissions [:record-audio]
|
||||
:on-allowed
|
||||
#(reset! input/record-audio-permission-granted true)
|
||||
:on-denied
|
||||
#(js/setTimeout
|
||||
(fn []
|
||||
(alert/show-popup
|
||||
(i18n/label :t/audio-recorder-error)
|
||||
(i18n/label
|
||||
:t/audio-recorder-permissions-error)))
|
||||
50)}]))}]])])
|
||||
|
||||
(defn view
|
||||
[send-ref params insets chat-id images on-send]
|
||||
[send-ref record-ref params insets chat-id images edit on-send]
|
||||
[rn/view {:style (style/controls insets)}
|
||||
[composer-images/images-list images]
|
||||
[rn/view {:style {:flex-direction :row :margin-top 12}}
|
||||
[image-button chat-id]
|
||||
[rn/view {:width 12}]
|
||||
[reactions-button]
|
||||
[rn/view {:flex 1}]
|
||||
[send-button send-ref params on-send]]])
|
||||
[rn/view {:style style/buttons-container}
|
||||
(when (and (not @input/recording-audio?)
|
||||
(nil? (get @input/reviewing-audio-filepath chat-id)))
|
||||
[:<>
|
||||
[image-button chat-id]
|
||||
[rn/view {:width 12}]
|
||||
[reactions-button]
|
||||
[rn/view {:flex 1}]
|
||||
[send-button send-ref params on-send]])]
|
||||
(when (and (not edit) (not (seq images)))
|
||||
[record-audio record-ref chat-id])])
|
||||
|
|
|
@ -12,22 +12,26 @@
|
|||
[status-im2.contexts.chat.messages.composer.mentions.view :as mentions]
|
||||
[status-im.ui2.screens.chat.composer.edit.view :as edit]
|
||||
[status-im.ui2.screens.chat.composer.input :as input]
|
||||
[status-im.ui2.screens.chat.composer.reply :as reply]))
|
||||
[status-im.ui2.screens.chat.composer.reply :as reply]
|
||||
[quo.react :refer [set-native-props]]))
|
||||
|
||||
(def initial-content-height (atom nil))
|
||||
(def keyboard-hiding? (atom false))
|
||||
|
||||
(defn minimize
|
||||
[{:keys [min-y set-bg-opacity set-translate-y set-parent-height]}]
|
||||
[{:keys [min-y set-bg-opacity set-translate-y set-parent-height refs chat-id]}]
|
||||
(set-bg-opacity 0)
|
||||
(set-translate-y (- min-y))
|
||||
(set-parent-height min-y))
|
||||
(set-parent-height min-y)
|
||||
(when-not (seq (get @input/input-texts chat-id))
|
||||
(set-native-props (:record-ref refs) #js {:right 0 :left 0})))
|
||||
|
||||
(defn maximize
|
||||
[{:keys [max-y set-bg-opacity set-translate-y set-parent-height max-parent-height]}]
|
||||
[{:keys [max-y set-bg-opacity set-translate-y set-parent-height max-parent-height refs]}]
|
||||
(set-bg-opacity 1)
|
||||
(set-translate-y (- max-y))
|
||||
(set-parent-height max-parent-height))
|
||||
(set-parent-height max-parent-height)
|
||||
(set-native-props (:record-ref refs) #js {:right nil :left -1000}))
|
||||
|
||||
(defn clean-and-minimize
|
||||
[{:keys [chat-id refs] :as params}]
|
||||
|
@ -42,12 +46,12 @@
|
|||
(-> (gesture/gesture-pan)
|
||||
(gesture/on-start
|
||||
(fn [_]
|
||||
(if keyboard-shown
|
||||
(if (and keyboard-shown (not @input/recording-audio?))
|
||||
(swap! gesture-values assoc :pan-y (reanimated/get-shared-value translate-y))
|
||||
(input/input-focus text-input-ref))))
|
||||
(gesture/on-update
|
||||
(fn [evt]
|
||||
(when keyboard-shown
|
||||
(when (and keyboard-shown (not @input/recording-audio?))
|
||||
(let [tY (oget evt "translationY")]
|
||||
(swap! gesture-values assoc :dy (- tY (:pdy @gesture-values)))
|
||||
(swap! gesture-values assoc :pdy tY)
|
||||
|
@ -56,7 +60,7 @@
|
|||
(max (min (+ tY (:pan-y @gesture-values)) (- min-y)) (- max-y)))))))
|
||||
(gesture/on-end
|
||||
(fn [_]
|
||||
(when keyboard-shown
|
||||
(when (and keyboard-shown (not @input/recording-audio?))
|
||||
(if (< (:dy @gesture-values) 0)
|
||||
(maximize params)
|
||||
(do
|
||||
|
@ -153,8 +157,10 @@
|
|||
[_ _]
|
||||
(let [text-input-ref (rn/create-ref)
|
||||
send-ref (rn/create-ref)
|
||||
record-ref (rn/create-ref)
|
||||
refs {:send-ref send-ref
|
||||
:text-input-ref text-input-ref}]
|
||||
:text-input-ref text-input-ref
|
||||
:record-ref record-ref}]
|
||||
(fn [chat-id insets]
|
||||
[:f>
|
||||
(fn []
|
||||
|
@ -187,7 +193,10 @@
|
|||
(+ 16
|
||||
(* 46 (dec (count suggestions)))))
|
||||
(+ 0
|
||||
(when (or edit reply) 38)
|
||||
(when (and
|
||||
(or edit reply)
|
||||
(not @input/recording-audio?))
|
||||
38)
|
||||
(when (seq images) 80))))
|
||||
|
||||
parent-height (reanimated/use-shared-value min-y)
|
||||
|
@ -223,7 +232,8 @@
|
|||
:refs refs}]]]]
|
||||
(if suggestions?
|
||||
[mentions/mentions params insets]
|
||||
[controls/view send-ref params insets chat-id images #(clean-and-minimize params)])
|
||||
[controls/view send-ref record-ref params insets chat-id images
|
||||
edit #(clean-and-minimize params)])
|
||||
;;;;black background
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
|
|
|
@ -25,7 +25,10 @@
|
|||
{:keys [edit-enabled show-input? community? can-delete-message-for-everyone?
|
||||
message-pin-enabled group-chat group-admin?]}]
|
||||
(concat
|
||||
(when (and outgoing edit-enabled (not (or deleted? deleted-for-me?)))
|
||||
(when (and outgoing
|
||||
edit-enabled
|
||||
(not (or deleted? deleted-for-me?))
|
||||
(not= content-type constants/content-type-audio))
|
||||
[{:type :main
|
||||
:on-press #(rf/dispatch [:chat.ui/edit-message message-data])
|
||||
:label (i18n/label :t/edit-message)
|
||||
|
@ -37,7 +40,9 @@
|
|||
:label (i18n/label :t/message-reply)
|
||||
:icon :i/reply
|
||||
:id :reply}])
|
||||
(when (and (not (or deleted? deleted-for-me?)) (not= (get content :text) "placeholder"))
|
||||
(when (and (not (or deleted? deleted-for-me?))
|
||||
(not= (get content :text) "placeholder")
|
||||
(not= content-type constants/content-type-audio))
|
||||
[{:type :main
|
||||
:on-press #(react/copy-to-clipboard
|
||||
(components.reply/get-quoted-text-with-mentions
|
||||
|
|
|
@ -8,15 +8,36 @@
|
|||
[utils.i18n :as i18n]
|
||||
[react-native.permissions :as permissions]))
|
||||
|
||||
(defonce record-audio-permission-granted (reagent/atom false))
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [message (reagent/atom
|
||||
"Press & hold the mic button to start recording...")
|
||||
record-audio-permission-granted (reagent/atom false)
|
||||
on-send #(reset! message (str "onSend event triggered. File path: " %))
|
||||
on-start-recording #(reset! message "onStartRecording event triggered.")
|
||||
on-reviewing-audio #(reset! message "onReviewingAudio event triggered.")
|
||||
on-cancel #(reset! message "onCancel event triggered.")]
|
||||
(let [message (reagent/atom
|
||||
"Press & hold the mic button to start recording...")
|
||||
on-send #(reset! message (str "onSend event triggered. File path: "
|
||||
%))
|
||||
on-start-recording #(reset! message "onStartRecording event triggered.")
|
||||
on-reviewing-audio #(reset! message "onReviewingAudio event triggered.")
|
||||
on-cancel #(reset! message "onCancel event triggered.")
|
||||
on-check-audio-permissions (fn []
|
||||
(permissions/permission-granted?
|
||||
:record-audio
|
||||
#(reset! record-audio-permission-granted %)
|
||||
#(reset! record-audio-permission-granted false)))
|
||||
on-request-record-audio-permission (fn []
|
||||
(rf/dispatch
|
||||
[:request-permissions
|
||||
{:permissions [:record-audio]
|
||||
:on-allowed
|
||||
#(reset! record-audio-permission-granted true)
|
||||
:on-denied
|
||||
#(js/setTimeout
|
||||
(fn []
|
||||
(alert/show-popup
|
||||
(i18n/label :t/audio-recorder-error)
|
||||
(i18n/label
|
||||
:t/audio-recorder-permissions-error)))
|
||||
50)}]))]
|
||||
(fn []
|
||||
[rn/view
|
||||
[rn/view
|
||||
|
@ -30,25 +51,8 @@
|
|||
:on-start-recording on-start-recording
|
||||
:on-reviewing-audio on-reviewing-audio
|
||||
:on-cancel on-cancel
|
||||
:on-check-audio-permissions (fn []
|
||||
(permissions/permission-granted?
|
||||
:record-audio
|
||||
#(reset! record-audio-permission-granted %)
|
||||
#(reset! record-audio-permission-granted false)))
|
||||
:on-request-record-audio-permission (fn []
|
||||
(rf/dispatch
|
||||
[:request-permissions
|
||||
{:permissions [:record-audio]
|
||||
:on-allowed
|
||||
#(reset! record-audio-permission-granted true)
|
||||
:on-denied
|
||||
#(js/setTimeout
|
||||
(fn []
|
||||
(alert/show-popup
|
||||
(i18n/label :t/audio-recorder-error)
|
||||
(i18n/label
|
||||
:t/audio-recorder-permissions-error)))
|
||||
50)}]))}]]
|
||||
:on-check-audio-permissions on-check-audio-permissions
|
||||
:on-request-record-audio-permission on-request-record-audio-permission}]]
|
||||
[quo/text {:style {:margin-horizontal 20}} @message]])))
|
||||
|
||||
(defn preview-record-audio
|
||||
|
|
|
@ -58,6 +58,11 @@ jest.mock('@react-native-community/audio-toolkit', () => ({
|
|||
RECORDING: 4,
|
||||
PAUSED: 5,
|
||||
},
|
||||
PlaybackCategories: {
|
||||
Playback: 1,
|
||||
Ambient: 2,
|
||||
SoloAmbient: 3
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("i18n-js", () => ({
|
||||
|
|
Loading…
Reference in New Issue