migrate reagent record audio component part5 (#19212)
This commit is contained in:
parent
296d868797
commit
1a8bca56e0
|
@ -4,10 +4,10 @@
|
|||
[quo.components.record-audio.record-audio.helpers :as helpers]
|
||||
[quo.components.record-audio.record-audio.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :refer [use-effect]]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defn f-delete-button
|
||||
(defn delete-button
|
||||
[recording? ready-to-delete? reviewing-audio? force-show-controls?]
|
||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||
translate-x (reanimated/use-shared-value (if force-show-controls? 35 20))
|
||||
|
@ -17,62 +17,68 @@
|
|||
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?])
|
||||
start-x-animation (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(fn []
|
||||
(helpers/animate-linear translate-x 0 200)
|
||||
(helpers/animate-linear opacity 1 200)))
|
||||
fade-out-animation (rn/use-callback
|
||||
(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))
|
||||
[reviewing-audio?])
|
||||
fade-out-reset-animation (rn/use-callback
|
||||
(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)))]
|
||||
(rn/use-effect (fn []
|
||||
(if recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[recording?])
|
||||
(rn/use-effect (fn []
|
||||
(when-not reviewing-audio?
|
||||
(fade-out-reset-animation)))
|
||||
[reviewing-audio?])
|
||||
(rn/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
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
[quo.components.record-audio.record-audio.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :refer [use-effect]]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defn f-lock-button
|
||||
(defn lock-button
|
||||
[recording? ready-to-lock? locked?]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
translate-x-y (reanimated/use-shared-value 20)
|
||||
|
@ -18,54 +18,58 @@
|
|||
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?])
|
||||
start-x-y-animation (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(fn []
|
||||
(helpers/animate-linear translate-x-y 0 220)
|
||||
(helpers/animate-linear opacity 1 220)))
|
||||
fade-out-animation (rn/use-callback
|
||||
(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)))]
|
||||
(rn/use-effect (fn []
|
||||
(if recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[recording?])
|
||||
(rn/use-effect (fn []
|
||||
(cond
|
||||
ready-to-lock?
|
||||
(start-x-y-animation)
|
||||
(and recording? (not locked?))
|
||||
(reset-x-y-animation)))
|
||||
[ready-to-lock?])
|
||||
(rn/use-effect (fn []
|
||||
(if locked?
|
||||
(fade-out-animation)
|
||||
(reset-x-y-animation)))
|
||||
[locked?])
|
||||
[:<>
|
||||
[reanimated/view {:style (style/lock-button-container opacity)}
|
||||
[reanimated/view
|
||||
|
@ -77,6 +81,6 @@
|
|||
[reanimated/view
|
||||
{:style (style/lock-button translate-x-y opacity)
|
||||
:pointer-events :none}
|
||||
[icons/icon (if @ready-to-lock? :i/locked :i/unlocked)
|
||||
[icons/icon (if ready-to-lock? :i/locked :i/unlocked)
|
||||
{:color (colors/theme-colors colors/black colors/white theme)
|
||||
:size 20}]]]))
|
||||
|
|
|
@ -3,19 +3,17 @@
|
|||
[quo.components.buttons.button.view :as button]
|
||||
[quo.components.record-audio.record-audio.helpers :as helpers]
|
||||
[quo.components.record-audio.record-audio.style :as style]
|
||||
[react-native.core :as rn :refer [use-effect]]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defn f-record-button
|
||||
(defn record-button
|
||||
[recording? reviewing-audio?]
|
||||
(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?])
|
||||
(let [opacity (reanimated/use-shared-value 1)]
|
||||
(rn/use-effect (fn []
|
||||
(if (or recording? reviewing-audio?)
|
||||
(helpers/set-value opacity 0)
|
||||
(helpers/set-value opacity 1)))
|
||||
[recording? reviewing-audio?])
|
||||
[reanimated/view {:style (style/record-button-container opacity)}
|
||||
[button/button
|
||||
{:type :outline
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
[quo.components.record-audio.record-audio.helpers :as helpers]
|
||||
[quo.components.record-audio.record-audio.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.audio-toolkit :as audio]
|
||||
[react-native.core :as rn :refer [use-effect]]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[taoensso.timbre :as log]
|
||||
|
@ -28,13 +29,16 @@
|
|||
(reagent/as-element
|
||||
[reanimated/view {:style (style/animated-circle scale opacity color)}]))))))
|
||||
|
||||
(defn f-record-button-big
|
||||
[{:keys [recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating?
|
||||
record-button-at-initial-position? locked? reviewing-audio? recording-length-ms
|
||||
clear-timeout touch-active? recorder-ref reload-recorder-fn idle? on-send on-cancel theme]}]
|
||||
(let [scale (reanimated/use-shared-value 1)
|
||||
(defn record-button-big
|
||||
[{:keys [recording? set-recording ready-to-send? set-ready-to-send ready-to-lock? set-ready-to-lock
|
||||
ready-to-delete? set-ready-to-delete record-button-is-animating?
|
||||
record-button-at-initial-position? locked? set-locked reviewing-audio? recording-length-ms
|
||||
set-recording-length-ms
|
||||
clear-timeout touch-active? recorder-ref reload-recorder-fn idle? on-send on-cancel]}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
scale (reanimated/use-shared-value 1)
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
opacity-from (if @ready-to-lock? opacity-from-lock opacity-from-default)
|
||||
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
|
||||
|
@ -46,141 +50,155 @@
|
|||
[opacity-from 0])}))
|
||||
(range 0 5))
|
||||
rings-color (cond
|
||||
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
|
||||
colors/neutral-80
|
||||
theme)
|
||||
@ready-to-delete? colors/danger-50
|
||||
:else colors/primary-50)
|
||||
ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
|
||||
colors/neutral-80
|
||||
theme)
|
||||
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 (= :dark theme)) @ready-to-lock?) colors/black colors/white)
|
||||
icon-color (if (and (not (= :dark theme)) 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)
|
||||
(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
|
||||
(rn/use-callback
|
||||
(fn []
|
||||
(cond
|
||||
(and ready-to-lock? (not @record-button-is-animating?))
|
||||
(do
|
||||
(set-locked true)
|
||||
(set-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)
|
||||
(set-recording false)
|
||||
(set-ready-to-send false)
|
||||
(set-ready-to-delete false)
|
||||
(set-ready-to-lock false)
|
||||
(reset! idle? true)
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(set-recording-length-ms 0)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))))
|
||||
[ready-to-lock? locked? reviewing-audio? ready-to-send? recording-length-ms ready-to-delete?
|
||||
idle?])
|
||||
start-animation (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(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))
|
||||
[complete-animation])
|
||||
reset-y-animation (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(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))
|
||||
[complete-animation])
|
||||
reset-x-animation (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(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?])
|
||||
(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))
|
||||
[complete-animation])
|
||||
reset-x-y-animation (rn/use-callback
|
||||
(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)))]
|
||||
(rn/use-effect (fn []
|
||||
(cond
|
||||
recording?
|
||||
(start-animation)
|
||||
(not ready-to-lock?)
|
||||
(stop-animation)))
|
||||
[recording?])
|
||||
(rn/use-effect (fn []
|
||||
(if ready-to-lock?
|
||||
(start-x-y-animation)
|
||||
(reset-x-y-animation)))
|
||||
[ready-to-lock?])
|
||||
(rn/use-effect (fn []
|
||||
(if ready-to-send?
|
||||
(start-y-animation)
|
||||
(reset-y-animation)))
|
||||
[ready-to-send?])
|
||||
(rn/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}
|
||||
|
@ -197,6 +215,6 @@
|
|||
[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?
|
||||
(if locked?
|
||||
[rn/view {:style style/stop-icon}]
|
||||
[icons/icon :i/audio {:color icon-color}])]]]))
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
[quo.components.record-audio.record-audio.helpers :as helpers]
|
||||
[quo.components.record-audio.record-audio.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :refer [use-effect]]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defn f-send-button
|
||||
(defn send-button
|
||||
[recording? ready-to-send? reviewing-audio? force-show-controls?]
|
||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||
translate-y (reanimated/use-shared-value (if force-show-controls? 76 20))
|
||||
|
@ -16,65 +16,71 @@
|
|||
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?])
|
||||
start-y-animation (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(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 (rn/use-callback
|
||||
(fn []
|
||||
(helpers/animate-linear translate-y 0 200)
|
||||
(helpers/animate-linear opacity 1 200)))
|
||||
fade-out-animation (rn/use-callback
|
||||
(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))
|
||||
[reviewing-audio? force-show-controls?])
|
||||
fade-out-reset-animation (rn/use-callback
|
||||
(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)))]
|
||||
(rn/use-effect (fn []
|
||||
(if recording?
|
||||
(fade-in-animation)
|
||||
(fade-out-animation)))
|
||||
[recording?])
|
||||
(rn/use-effect (fn []
|
||||
(when-not reviewing-audio?
|
||||
(fade-out-reset-animation)))
|
||||
[reviewing-audio?])
|
||||
(rn/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
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
(:require
|
||||
[quo.components.record-audio.record-audio.view :as record-audio]
|
||||
[react-native.audio-toolkit :as audio]
|
||||
[reagent.core :as reagent]
|
||||
[test-helpers.component :as h]
|
||||
[utils.datetime :as datetime]))
|
||||
|
||||
|
@ -11,8 +10,10 @@
|
|||
|
||||
(h/test "renders record-audio"
|
||||
(h/render [record-audio/record-audio])
|
||||
(-> (h/expect (h/get-by-test-id "record-audio"))
|
||||
(.toBeTruthy)))
|
||||
(-> (h/wait-for #(h/get-by-test-id "record-audio"))
|
||||
(.then (fn []
|
||||
(-> (h/expect (h/get-by-test-id "record-audio"))
|
||||
(.toBeTruthy))))))
|
||||
|
||||
(h/test "record-audio on-start-recording works"
|
||||
(let [event (js/jest.fn)]
|
||||
|
@ -31,7 +32,7 @@
|
|||
|
||||
(h/test "record-audio on-reviewing-audio works"
|
||||
(let [event (js/jest.fn)
|
||||
on-meter (reagent/atom nil)]
|
||||
on-meter (atom nil)]
|
||||
(h/render [record-audio/record-audio
|
||||
{:on-reviewing-audio event
|
||||
:record-audio-permission-granted true}])
|
||||
|
@ -39,7 +40,7 @@
|
|||
(reset! on-meter on-meter-fn))
|
||||
audio/start-recording (fn [_ on-start _]
|
||||
(on-start)
|
||||
(js/setInterval #(@on-meter) 100))
|
||||
(js/setInterval @on-meter 100))
|
||||
audio/get-recorder-file-path (fn [] "file-path")]
|
||||
(h/fire-event
|
||||
:on-start-should-set-responder
|
||||
|
@ -48,6 +49,7 @@
|
|||
:locationY 70
|
||||
:timestamp 0
|
||||
:identifier 0}})
|
||||
|
||||
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
|
||||
(h/advance-timers-by-time 100)
|
||||
(h/fire-event
|
||||
|
@ -63,7 +65,7 @@
|
|||
|
||||
(h/test "record-audio on-send works after reviewing audio"
|
||||
(let [event (js/jest.fn)
|
||||
on-meter (reagent/atom nil)]
|
||||
on-meter (atom nil)]
|
||||
(h/render [record-audio/record-audio
|
||||
{:on-send event
|
||||
:record-audio-permission-granted true}])
|
||||
|
@ -114,14 +116,15 @@
|
|||
|
||||
(h/test "record-audio on-send works after sliding to the send button"
|
||||
(let [event (js/jest.fn)
|
||||
on-meter (reagent/atom nil)
|
||||
on-meter (atom nil)
|
||||
last-now-ms (atom nil)
|
||||
duration-ms (atom nil)]
|
||||
(h/render [record-audio/record-audio
|
||||
{:on-send event
|
||||
:record-audio-permission-granted true}])
|
||||
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
|
||||
(reset! on-meter on-meter-fn))
|
||||
(reset! on-meter on-meter-fn)
|
||||
#js {:destroy #()})
|
||||
audio/start-recording (fn [_ on-start _]
|
||||
(on-start)
|
||||
(js/setInterval #(@on-meter) 100))
|
||||
|
@ -170,15 +173,16 @@
|
|||
|
||||
(h/test "record-audio on-cancel works after reviewing audio"
|
||||
(let [event (js/jest.fn)
|
||||
on-meter (reagent/atom nil)]
|
||||
on-meter (atom nil)]
|
||||
(h/render [record-audio/record-audio
|
||||
{:on-cancel event
|
||||
:record-audio-permission-granted true}])
|
||||
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
|
||||
(reset! on-meter on-meter-fn))
|
||||
(reset! on-meter on-meter-fn)
|
||||
#js {:destroy #()})
|
||||
audio/start-recording (fn [_ on-start _]
|
||||
(on-start)
|
||||
(js/setInterval #(@on-meter) 100))]
|
||||
(js/setInterval @on-meter 100))]
|
||||
(h/fire-event
|
||||
:on-start-should-set-responder
|
||||
(h/get-by-test-id "record-audio")
|
||||
|
@ -195,6 +199,7 @@
|
|||
:locationY 70
|
||||
:timestamp 200
|
||||
:identifier 0}})
|
||||
(h/advance-timers-by-time 250)
|
||||
(h/fire-event
|
||||
:on-responder-release
|
||||
(h/get-by-test-id "record-audio")
|
||||
|
@ -202,13 +207,12 @@
|
|||
:locationY 80
|
||||
:timestamp 200
|
||||
:identifier 0}})
|
||||
(h/advance-timers-by-time 250)
|
||||
(-> (js/expect event)
|
||||
(.toHaveBeenCalledTimes 1))))))
|
||||
|
||||
(h/test "record-audio on-cancel works after sliding to the cancel button"
|
||||
(let [event (js/jest.fn)
|
||||
on-meter (reagent/atom nil)]
|
||||
on-meter (atom nil)]
|
||||
(h/render [record-audio/record-audio
|
||||
{:on-cancel event
|
||||
:record-audio-permission-granted true}])
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
(ns quo.components.record-audio.record-audio.constants
|
||||
(:require [react-native.audio-toolkit :as audio]
|
||||
[react-native.platform :as platform]
|
||||
[utils.datetime :as datetime]))
|
||||
|
||||
(def min-audio-duration-ms 1000)
|
||||
(def max-audio-duration-ms (if platform/ios? 120800 120500))
|
||||
(def metering-interval 25)
|
||||
(def base-filename "am")
|
||||
(def default-format ".aac")
|
||||
|
||||
(def min-touch-duration 150)
|
||||
|
||||
(def record-button-area-big
|
||||
{:width 56
|
||||
:height 56
|
||||
:x 64
|
||||
:y 64})
|
||||
|
||||
(def record-button-area
|
||||
{:width 48
|
||||
:height 48
|
||||
:x 68
|
||||
:y 68})
|
||||
|
||||
(defn delete-button-area
|
||||
[{:keys [active? reviewing-audio?]}]
|
||||
{:width (cond
|
||||
active? 72
|
||||
reviewing-audio? 32
|
||||
:else 82)
|
||||
:height (if reviewing-audio? 32 56)
|
||||
:x (cond
|
||||
active? -16
|
||||
reviewing-audio? 36
|
||||
:else -32)
|
||||
:y (cond
|
||||
active? 64
|
||||
reviewing-audio? 76
|
||||
:else 70)})
|
||||
|
||||
(defn lock-button-area
|
||||
[{:keys [active?]}]
|
||||
{:width (if active? 72 100)
|
||||
:height (if active? 72 102)
|
||||
:x -32
|
||||
:y -32})
|
||||
|
||||
(defn send-button-area
|
||||
[{:keys [active? reviewing-audio?]}]
|
||||
{:width (if reviewing-audio? 32 56)
|
||||
:height (cond
|
||||
active? 72
|
||||
reviewing-audio? 47
|
||||
:else 92)
|
||||
:x (if reviewing-audio? 76 32)
|
||||
:y (cond
|
||||
active? -16
|
||||
reviewing-audio? 76
|
||||
:else -32)})
|
||||
|
||||
(defn touch-inside-area?
|
||||
[{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]}
|
||||
{:keys [width height x y]}]
|
||||
(let [max-x (+ x width)
|
||||
max-y (+ y height)]
|
||||
(and
|
||||
(and
|
||||
(or ignore-min-x? (>= location-x x))
|
||||
(or ignore-max-x? (<= location-x max-x)))
|
||||
(and
|
||||
(or ignore-min-y? (>= location-y y))
|
||||
(or ignore-max-y? (<= location-y max-y))))))
|
||||
|
||||
(def rec-options
|
||||
(merge
|
||||
audio/default-recorder-options
|
||||
{:filename (str base-filename
|
||||
(datetime/timestamp)
|
||||
default-format)
|
||||
:meteringInterval metering-interval}))
|
|
@ -0,0 +1,345 @@
|
|||
(ns quo.components.record-audio.record-audio.handlers
|
||||
(:require [oops.core :as oops]
|
||||
[quo.components.record-audio.record-audio.constants :as record-audio.constants]
|
||||
[react-native.audio-toolkit :as audio]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.datetime :as datetime]))
|
||||
|
||||
(def get-reload-player
|
||||
(memoize
|
||||
(fn
|
||||
[[player-ref destroy-player set-playing-audio playing-timer set-audio-current-time-ms
|
||||
set-seeking-audio]]
|
||||
(fn [audio-file]
|
||||
(when @player-ref
|
||||
(destroy-player))
|
||||
(reset! player-ref
|
||||
(audio/new-player
|
||||
(or audio-file (:filename record-audio.constants/rec-options))
|
||||
{:autoDestroy false
|
||||
:continuesToPlayInBackground false
|
||||
:category audio/PLAYBACK}
|
||||
(fn []
|
||||
(set-playing-audio false)
|
||||
(when @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil)
|
||||
(set-audio-current-time-ms 0)
|
||||
(set-seeking-audio false)))))
|
||||
(audio/prepare-player
|
||||
@player-ref
|
||||
#(log/debug "[record-audio] prepare player - success")
|
||||
#(log/error "[record-audio] prepare player - error: " %))))))
|
||||
|
||||
(def get-recorder-on-meter
|
||||
(memoize
|
||||
(fn
|
||||
[[recording-start-ms set-recording-length-ms reached-max-duration? locked? set-locked
|
||||
set-reviewing-audio
|
||||
idle? set-recording
|
||||
set-ready-to-lock set-ready-to-send set-ready-to-delete recorder-ref output-file reload-player
|
||||
on-reviewing-audio]]
|
||||
(fn []
|
||||
(when @recording-start-ms
|
||||
(let [now-ms (datetime/timestamp)
|
||||
recording-duration (- now-ms @recording-start-ms)]
|
||||
(set-recording-length-ms recording-duration)
|
||||
(when (>= recording-duration record-audio.constants/max-audio-duration-ms)
|
||||
(reset! reached-max-duration? (not locked?))
|
||||
(set-reviewing-audio true)
|
||||
(reset! idle? false)
|
||||
(set-locked false)
|
||||
(set-recording false)
|
||||
(set-ready-to-lock false)
|
||||
(set-ready-to-send false)
|
||||
(set-ready-to-delete false)
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(fn []
|
||||
(reset! output-file (audio/get-recorder-file-path @recorder-ref))
|
||||
(reload-player nil)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(set-recording-length-ms 0)
|
||||
(reset! recording-start-ms nil)
|
||||
(when on-reviewing-audio
|
||||
(on-reviewing-audio (audio/get-recorder-file-path @recorder-ref))))
|
||||
(log/debug "[record-audio] new recorder - on meter")))))))
|
||||
|
||||
(def get-reload-recorder
|
||||
(memoize
|
||||
(fn
|
||||
[[recorder-ref recorder-on-meter]]
|
||||
(fn []
|
||||
(when @recorder-ref
|
||||
(audio/destroy-recorder @recorder-ref)
|
||||
(reset! recorder-ref nil))
|
||||
(reset! recorder-ref (audio/new-recorder
|
||||
record-audio.constants/rec-options
|
||||
recorder-on-meter
|
||||
#(log/debug "[record-audio] new recorder - on ended")))))))
|
||||
|
||||
(def get-on-start-should-set-responder
|
||||
(memoize
|
||||
(fn
|
||||
[[locked? idle? disabled? recorder-on-meter touch-timestamp touch-identifier
|
||||
reviewing-audio? record-audio-permission-granted set-recording set-playing-audio output-file
|
||||
recorder-ref recording-start-ms set-audio-current-time-ms on-start-recording
|
||||
on-request-record-audio-permission touch-active?]]
|
||||
(fn [e]
|
||||
(when-not (or locked? @idle? (nil? e) @disabled?)
|
||||
(let [pressed-record-button? (record-audio.constants/touch-inside-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}
|
||||
record-audio.constants/record-button-area)
|
||||
new-recorder (audio/new-recorder
|
||||
record-audio.constants/rec-options
|
||||
recorder-on-meter
|
||||
#(log/debug "[record-audio] new recorder - on ended"))]
|
||||
(reset! touch-timestamp (oops/oget e "nativeEvent.timestamp"))
|
||||
(reset! touch-identifier (oops/oget e "nativeEvent.identifier"))
|
||||
(when-not reviewing-audio?
|
||||
(if record-audio-permission-granted
|
||||
(do
|
||||
(when (not @idle?)
|
||||
(set-recording pressed-record-button?))
|
||||
(when pressed-record-button?
|
||||
(set-playing-audio false)
|
||||
(reset! output-file nil)
|
||||
(reset! recorder-ref new-recorder)
|
||||
(audio/start-recording
|
||||
new-recorder
|
||||
(fn []
|
||||
(reset! recording-start-ms (datetime/timestamp))
|
||||
(set-audio-current-time-ms 0)
|
||||
(log/debug "[record-audio] start recording - success"))
|
||||
#(log/error "[record-audio] start recording - error: " %))
|
||||
(when on-start-recording
|
||||
(on-start-recording))))
|
||||
(when on-request-record-audio-permission
|
||||
(on-request-record-audio-permission))))
|
||||
(when record-audio-permission-granted
|
||||
(reset! touch-active? true))))
|
||||
(and (not @idle?) (not @disabled?))))))
|
||||
|
||||
(def get-on-responder-move
|
||||
(memoize
|
||||
(fn
|
||||
[[locked? ready-to-send? set-ready-to-send ready-to-delete? set-ready-to-delete ready-to-lock?
|
||||
set-ready-to-lock touch-identifier
|
||||
record-button-at-initial-position? recording?]]
|
||||
(fn [^js e]
|
||||
(when-not locked?
|
||||
(let [location-x (oops/oget e "nativeEvent.locationX")
|
||||
location-y (oops/oget e "nativeEvent.locationY")
|
||||
page-x (oops/oget e "nativeEvent.pageX")
|
||||
page-y (oops/oget e "nativeEvent.pageY")
|
||||
identifier (oops/oget e "nativeEvent.identifier")
|
||||
moved-to-send-button? (record-audio.constants/touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? true
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? true}
|
||||
(record-audio.constants/send-button-area
|
||||
{:active? ready-to-send?
|
||||
:reviewing-audio? false}))
|
||||
moved-to-delete-button? (record-audio.constants/touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? true
|
||||
:ignore-min-x? true
|
||||
:ignore-max-x? false}
|
||||
(record-audio.constants/delete-button-area
|
||||
{:active? ready-to-delete?
|
||||
:reviewing-audio? false}))
|
||||
moved-to-lock-button? (record-audio.constants/touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? false}
|
||||
(record-audio.constants/lock-button-area {:active?
|
||||
ready-to-lock?}))
|
||||
moved-to-record-button? (and
|
||||
(record-audio.constants/touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? false}
|
||||
record-audio.constants/record-button-area-big)
|
||||
(not= location-x page-x)
|
||||
(not= location-y page-y))]
|
||||
(when (= identifier @touch-identifier)
|
||||
(cond
|
||||
(and
|
||||
(or
|
||||
(and moved-to-record-button? ready-to-lock?)
|
||||
(and (not locked?) moved-to-lock-button? @record-button-at-initial-position?))
|
||||
(not ready-to-delete?)
|
||||
(not ready-to-send?)
|
||||
recording?)
|
||||
(set-ready-to-lock moved-to-lock-button?)
|
||||
(and
|
||||
(or
|
||||
(and moved-to-record-button? ready-to-delete?)
|
||||
(and moved-to-delete-button? @record-button-at-initial-position?))
|
||||
(not ready-to-lock?)
|
||||
(not ready-to-send?)
|
||||
recording?)
|
||||
(set-ready-to-delete moved-to-delete-button?)
|
||||
(and
|
||||
(or
|
||||
(and moved-to-record-button? ready-to-send?)
|
||||
(and moved-to-send-button? @record-button-at-initial-position?))
|
||||
(not ready-to-lock?)
|
||||
(not ready-to-delete?)
|
||||
recording?)
|
||||
(set-ready-to-send moved-to-send-button?)))))))))
|
||||
|
||||
(def get-on-responder-release
|
||||
(memoize
|
||||
(fn
|
||||
[[idle? reached-max-duration? touch-timestamp recording-length-ms set-recording-length-ms
|
||||
reviewing-audio? set-reviewing-audio
|
||||
set-audio-current-time-ms set-force-show-controls on-send output-file player-ref
|
||||
destroy-player on-cancel record-button-is-animating? ready-to-lock? set-ready-to-lock
|
||||
locked? set-locked on-lock ready-to-delete? set-ready-to-delete ready-to-send? set-ready-to-send
|
||||
disabled? on-reviewing-audio recorder-ref set-recording reload-player recording-start-ms
|
||||
touch-active?]]
|
||||
(fn [^js e]
|
||||
(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? (record-audio.constants/touch-inside-area?
|
||||
touch-area
|
||||
record-audio.constants/record-button-area-big)
|
||||
on-send-button? (record-audio.constants/touch-inside-area?
|
||||
touch-area
|
||||
(record-audio.constants/send-button-area
|
||||
{:active? false
|
||||
:reviewing-audio? true}))
|
||||
on-delete-button? (record-audio.constants/touch-inside-area?
|
||||
touch-area
|
||||
(record-audio.constants/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
|
||||
(set-reviewing-audio false)
|
||||
(set-audio-current-time-ms 0)
|
||||
(set-force-show-controls false)
|
||||
(when on-send
|
||||
(on-send {:file-path @output-file
|
||||
:duration (min record-audio.constants/max-audio-duration-ms
|
||||
(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: " %))))
|
||||
|
||||
(and reviewing-audio? on-delete-button?)
|
||||
(do
|
||||
(set-reviewing-audio false)
|
||||
(set-audio-current-time-ms 0)
|
||||
(set-force-show-controls false)
|
||||
(destroy-player)
|
||||
(when on-cancel
|
||||
(on-cancel)))
|
||||
|
||||
(and ready-to-lock? (not @record-button-is-animating?))
|
||||
(do
|
||||
(set-locked true)
|
||||
(set-ready-to-lock false)
|
||||
(when on-lock
|
||||
(on-lock)))
|
||||
|
||||
(and (not reviewing-audio?)
|
||||
(or on-record-button?
|
||||
(and (not ready-to-delete?)
|
||||
(not ready-to-lock?)
|
||||
(not ready-to-send?))))
|
||||
(do
|
||||
(reset! disabled? (<= touch-timestamp-diff record-audio.constants/min-touch-duration))
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(if (>= recording-length-ms record-audio.constants/min-audio-duration-ms)
|
||||
(do (set-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)))
|
||||
(set-locked false)
|
||||
(set-recording false)
|
||||
(set-ready-to-lock false)
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(fn []
|
||||
(reset! output-file (audio/get-recorder-file-path @recorder-ref))
|
||||
(when (>= audio-length record-audio.constants/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)
|
||||
(set-recording-length-ms 0)
|
||||
(reset! recording-start-ms nil)
|
||||
(reset! disabled? false))
|
||||
(if (> touch-timestamp-diff record-audio.constants/min-touch-duration) 0 250)))
|
||||
|
||||
(and (not locked?) (not reviewing-audio?) (not @record-button-is-animating?))
|
||||
(do
|
||||
(reset! disabled? (<= touch-timestamp-diff record-audio.constants/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)))
|
||||
(set-recording false)
|
||||
(set-ready-to-send false)
|
||||
(set-ready-to-delete false)
|
||||
(set-ready-to-lock false)
|
||||
(reset! idle? true)
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(set-recording-length-ms 0)
|
||||
(reset! recording-start-ms nil)
|
||||
(reset! disabled? false)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %)))
|
||||
(if (> touch-timestamp-diff record-audio.constants/min-touch-duration) 0 250)))))
|
||||
(reset! touch-active? false))
|
||||
(when @reached-max-duration?
|
||||
(reset! reached-max-duration? false))
|
||||
(reset! touch-timestamp nil)))))
|
|
@ -2,7 +2,6 @@
|
|||
(:require
|
||||
[clojure.string :as string]
|
||||
[goog.string :as gstring]
|
||||
[oops.core :as oops]
|
||||
[quo.components.icon :as icons]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.components.record-audio.record-audio.buttons.delete-button :as delete-button]
|
||||
|
@ -10,93 +9,23 @@
|
|||
[quo.components.record-audio.record-audio.buttons.record-button :as record-button]
|
||||
[quo.components.record-audio.record-audio.buttons.record-button-big :as record-button-big]
|
||||
[quo.components.record-audio.record-audio.buttons.send-button :as send-button]
|
||||
[quo.components.record-audio.record-audio.constants :as record-audio.constants]
|
||||
[quo.components.record-audio.record-audio.handlers :as handlers]
|
||||
[quo.components.record-audio.record-audio.style :as style]
|
||||
[quo.components.record-audio.soundtrack.view :as soundtrack]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.audio-toolkit :as audio]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[reagent.core :as reagent]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.datetime :as datetime]))
|
||||
|
||||
(def ^:private min-audio-duration-ms 1000)
|
||||
(def ^:private max-audio-duration-ms (if platform/ios? 120800 120500))
|
||||
(def ^:private metering-interval 25)
|
||||
(def ^:private base-filename "am")
|
||||
(def ^:private default-format ".aac")
|
||||
|
||||
(def min-touch-duration 150)
|
||||
|
||||
(def ^:private record-button-area-big
|
||||
{:width 56
|
||||
:height 56
|
||||
:x 64
|
||||
:y 64})
|
||||
|
||||
(def ^:private record-button-area
|
||||
{:width 48
|
||||
:height 48
|
||||
:x 68
|
||||
:y 68})
|
||||
|
||||
(defn- delete-button-area
|
||||
[{:keys [active? reviewing-audio?]}]
|
||||
{:width (cond
|
||||
active? 72
|
||||
reviewing-audio? 32
|
||||
:else 82)
|
||||
:height (if reviewing-audio? 32 56)
|
||||
:x (cond
|
||||
active? -16
|
||||
reviewing-audio? 36
|
||||
:else -32)
|
||||
:y (cond
|
||||
active? 64
|
||||
reviewing-audio? 76
|
||||
:else 70)})
|
||||
|
||||
(defn- lock-button-area
|
||||
[{:keys [active?]}]
|
||||
{:width (if active? 72 100)
|
||||
:height (if active? 72 102)
|
||||
:x -32
|
||||
:y -32})
|
||||
|
||||
(defn- send-button-area
|
||||
[{:keys [active? reviewing-audio?]}]
|
||||
{:width (if reviewing-audio? 32 56)
|
||||
:height (cond
|
||||
active? 72
|
||||
reviewing-audio? 47
|
||||
:else 92)
|
||||
:x (if reviewing-audio? 76 32)
|
||||
:y (cond
|
||||
active? -16
|
||||
reviewing-audio? 76
|
||||
:else -32)})
|
||||
|
||||
(defn touch-inside-area?
|
||||
[{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]}
|
||||
{:keys [width height x y]}]
|
||||
(let [max-x (+ x width)
|
||||
max-y (+ y height)]
|
||||
(and
|
||||
(and
|
||||
(or ignore-min-x? (>= location-x x))
|
||||
(or ignore-max-x? (<= location-x max-x)))
|
||||
(and
|
||||
(or ignore-min-y? (>= location-y y))
|
||||
(or ignore-max-y? (<= location-y max-y))))))
|
||||
|
||||
(defn- f-recording-bar
|
||||
(defn- recording-bar
|
||||
[recording-length-ms ready-to-delete?]
|
||||
(let [fill-percentage (/ (* recording-length-ms 100) max-audio-duration-ms)]
|
||||
(let [fill-percentage (/ (* recording-length-ms 100) record-audio.constants/max-audio-duration-ms)]
|
||||
[rn/view {:style (style/recording-bar-container)}
|
||||
[rn/view {:style (style/recording-bar fill-percentage ready-to-delete?)}]]))
|
||||
|
||||
(defn- f-time-counter
|
||||
(defn- time-counter
|
||||
[recording? recording-length-ms ready-to-delete? reviewing-audio? audio-current-time-ms]
|
||||
(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))]
|
||||
|
@ -111,10 +40,12 @@
|
|||
{:style (style/timer-text)}))
|
||||
time-str]]))
|
||||
|
||||
(defn- f-play-button
|
||||
[playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio? max-duration-ms]
|
||||
(defn- play-button
|
||||
[playing-audio? set-playing-audio player-ref playing-timer set-audio-current-time-ms seeking-audio?
|
||||
set-seeking-audio
|
||||
max-duration-ms]
|
||||
(let [on-play (fn []
|
||||
(reset! playing-audio? true)
|
||||
(set-playing-audio true)
|
||||
(reset! playing-timer
|
||||
(js/setInterval
|
||||
(fn []
|
||||
|
@ -122,24 +53,24 @@
|
|||
player-state (audio/get-state @player-ref)
|
||||
playing? (= player-state audio/PLAYING)]
|
||||
(when (and playing?
|
||||
(not @seeking-audio?)
|
||||
(not seeking-audio?)
|
||||
(> current-time 0)
|
||||
(< current-time max-duration-ms))
|
||||
(reset! audio-current-time-ms current-time))
|
||||
(set-audio-current-time-ms current-time))
|
||||
(when (>= current-time max-duration-ms)
|
||||
(audio/stop-playing
|
||||
@player-ref
|
||||
(fn []
|
||||
(reset! playing-audio? false)
|
||||
(set-playing-audio false)
|
||||
(when @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil)
|
||||
(reset! audio-current-time-ms 0)
|
||||
(reset! seeking-audio? false)))
|
||||
(set-audio-current-time-ms 0)
|
||||
(set-seeking-audio false)))
|
||||
#(log/error "[record-audio] stop player - error: " %)))))
|
||||
100)))
|
||||
on-pause (fn []
|
||||
(reset! playing-audio? false)
|
||||
(set-playing-audio false)
|
||||
(when @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil))
|
||||
|
@ -154,445 +85,186 @@
|
|||
{:style (style/play-button)
|
||||
:on-press on-press}
|
||||
[icons/icon
|
||||
(if @playing-audio? :i/pause :i/play)
|
||||
(if playing-audio? :i/pause :i/play)
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]))
|
||||
|
||||
(defn- record-audio-internal
|
||||
[{: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 on-lock max-duration-ms theme]}]
|
||||
[: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)
|
||||
ready-to-send? (reagent/atom false)
|
||||
ready-to-lock? (reagent/atom false)
|
||||
ready-to-delete? (reagent/atom false)
|
||||
reviewing-audio? (reagent/atom (some? audio-file))
|
||||
playing-audio? (reagent/atom false)
|
||||
recording-length-ms (reagent/atom 0)
|
||||
audio-current-time-ms (reagent/atom 0)
|
||||
seeking-audio? (reagent/atom false)
|
||||
force-show-controls? (reagent/atom (some? audio-file))
|
||||
recording-start-ms (atom (datetime/timestamp))
|
||||
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)
|
||||
playing-timer (atom nil)
|
||||
output-file (atom audio-file)
|
||||
reached-max-duration? (atom false)
|
||||
touch-timestamp (atom nil)
|
||||
touch-identifier (atom nil)
|
||||
disabled? (atom false)
|
||||
app-state-listener (atom nil)
|
||||
rec-options
|
||||
(merge
|
||||
audio/default-recorder-options
|
||||
{:filename (str base-filename (datetime/timestamp) default-format)
|
||||
:meteringInterval metering-interval})
|
||||
destroy-player
|
||||
(fn []
|
||||
(audio/destroy-player @player-ref)
|
||||
(reset! player-ref nil))
|
||||
reload-player
|
||||
(fn [audio-file]
|
||||
(when @player-ref
|
||||
(destroy-player))
|
||||
(reset! player-ref
|
||||
(audio/new-player
|
||||
(or audio-file (:filename rec-options))
|
||||
{:autoDestroy false
|
||||
:continuesToPlayInBackground false
|
||||
:category audio/PLAYBACK}
|
||||
(fn []
|
||||
(reset! playing-audio? false)
|
||||
(when @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil)
|
||||
(reset! audio-current-time-ms 0)
|
||||
(reset! seeking-audio? false)))))
|
||||
(audio/prepare-player
|
||||
@player-ref
|
||||
#(log/debug "[record-audio] prepare player - success")
|
||||
#(log/error "[record-audio] prepare player - error: " %)))
|
||||
destroy-recorder
|
||||
(fn []
|
||||
(audio/destroy-recorder @recorder-ref)
|
||||
(reset! recorder-ref nil))
|
||||
recorder-on-meter
|
||||
(fn []
|
||||
(when @recording-start-ms
|
||||
(let [now-ms (datetime/timestamp)
|
||||
recording-duration (- now-ms
|
||||
@recording-start-ms)]
|
||||
(reset! recording-length-ms recording-duration)
|
||||
(when (>= recording-duration max-audio-duration-ms)
|
||||
(reset! reached-max-duration? (not @locked?))
|
||||
(reset! reviewing-audio? true)
|
||||
(reset! idle? false)
|
||||
(reset! locked? false)
|
||||
(reset! recording? false)
|
||||
(reset! ready-to-lock? false)
|
||||
(reset! ready-to-send? false)
|
||||
(reset! ready-to-delete? false)
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
(fn []
|
||||
(reset! output-file (audio/get-recorder-file-path
|
||||
@recorder-ref))
|
||||
(reload-player nil)
|
||||
(log/debug "[record-audio] stop recording - success"))
|
||||
#(log/error "[record-audio] stop recording - error: " %))
|
||||
(js/setTimeout #(reset! idle? false) 1000)
|
||||
(reset! recording-length-ms 0)
|
||||
(reset! recording-start-ms nil)
|
||||
(when on-reviewing-audio
|
||||
(on-reviewing-audio (audio/get-recorder-file-path
|
||||
@recorder-ref))))
|
||||
(log/debug "[record-audio] new recorder - on meter"))))
|
||||
reload-recorder
|
||||
(fn []
|
||||
(when @recorder-ref
|
||||
(destroy-recorder))
|
||||
(reset! recorder-ref (audio/new-recorder
|
||||
rec-options
|
||||
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 @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) @disabled?)
|
||||
(let [pressed-record-button? (touch-inside-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}
|
||||
record-button-area)
|
||||
new-recorder (audio/new-recorder
|
||||
rec-options
|
||||
recorder-on-meter
|
||||
#(log/debug "[record-audio] new recorder - on ended"))]
|
||||
(reset! touch-timestamp (oops/oget e "nativeEvent.timestamp"))
|
||||
(reset! touch-identifier (oops/oget e "nativeEvent.identifier"))
|
||||
(when-not @reviewing-audio?
|
||||
(if record-audio-permission-granted
|
||||
(do
|
||||
(when (not @idle?)
|
||||
(reset! recording? pressed-record-button?))
|
||||
(when pressed-record-button?
|
||||
(reset! playing-audio? false)
|
||||
(reset! output-file nil)
|
||||
(reset! recorder-ref new-recorder)
|
||||
(audio/start-recording
|
||||
new-recorder
|
||||
(fn []
|
||||
(reset! recording-start-ms (datetime/timestamp))
|
||||
(reset! audio-current-time-ms 0)
|
||||
(log/debug "[record-audio] start recording - success"))
|
||||
#(log/error "[record-audio] start recording - error: " %))
|
||||
(when on-start-recording
|
||||
(on-start-recording))))
|
||||
(when on-request-record-audio-permission
|
||||
(on-request-record-audio-permission))))
|
||||
(when record-audio-permission-granted
|
||||
(reset! touch-active? true))))
|
||||
(and (not @idle?) (not @disabled?)))
|
||||
on-responder-move
|
||||
(fn [^js e]
|
||||
(when-not @locked?
|
||||
(let [location-x (oops/oget e "nativeEvent.locationX")
|
||||
location-y (oops/oget e "nativeEvent.locationY")
|
||||
page-x (oops/oget e "nativeEvent.pageX")
|
||||
page-y (oops/oget e "nativeEvent.pageY")
|
||||
identifier (oops/oget e "nativeEvent.identifier")
|
||||
moved-to-send-button? (touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? true
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? true}
|
||||
(send-button-area
|
||||
{:active? @ready-to-send?
|
||||
:reviewing-audio? false}))
|
||||
moved-to-delete-button? (touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? true
|
||||
:ignore-min-x? true
|
||||
:ignore-max-x? false}
|
||||
(delete-button-area
|
||||
{:active? @ready-to-delete?
|
||||
:reviewing-audio? false}))
|
||||
moved-to-lock-button? (touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? false}
|
||||
(lock-button-area {:active? @ready-to-lock?}))
|
||||
moved-to-record-button? (and
|
||||
(touch-inside-area?
|
||||
{:location-x location-x
|
||||
:location-y location-y
|
||||
:ignore-min-y? false
|
||||
:ignore-max-y? false
|
||||
:ignore-min-x? false
|
||||
:ignore-max-x? false}
|
||||
record-button-area-big)
|
||||
(not= location-x page-x)
|
||||
(not= location-y page-y))]
|
||||
(when (= identifier @touch-identifier)
|
||||
(cond
|
||||
(and
|
||||
(or
|
||||
(and moved-to-record-button? @ready-to-lock?)
|
||||
(and (not @locked?) moved-to-lock-button? @record-button-at-initial-position?))
|
||||
(not @ready-to-delete?)
|
||||
(not @ready-to-send?)
|
||||
@recording?)
|
||||
(reset! ready-to-lock? moved-to-lock-button?)
|
||||
(and
|
||||
(or
|
||||
(and moved-to-record-button? @ready-to-delete?)
|
||||
(and moved-to-delete-button? @record-button-at-initial-position?))
|
||||
(not @ready-to-lock?)
|
||||
(not @ready-to-send?)
|
||||
@recording?)
|
||||
(reset! ready-to-delete? moved-to-delete-button?)
|
||||
(and
|
||||
(or
|
||||
(and moved-to-record-button? @ready-to-send?)
|
||||
(and moved-to-send-button? @record-button-at-initial-position?))
|
||||
(not @ready-to-lock?)
|
||||
(not @ready-to-delete?)
|
||||
@recording?)
|
||||
(reset! ready-to-send? moved-to-send-button?))))))
|
||||
on-responder-release
|
||||
(fn [^js e]
|
||||
(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}))
|
||||
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 (min max-audio-duration-ms
|
||||
(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: " %))))
|
||||
(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)))
|
||||
(and @ready-to-lock? (not @record-button-is-animating?))
|
||||
(do
|
||||
(reset! locked? true)
|
||||
(reset! ready-to-lock? false)
|
||||
(when on-lock
|
||||
(on-lock)))
|
||||
(and (not @reviewing-audio?)
|
||||
(or on-record-button?
|
||||
(and (not @ready-to-delete?)
|
||||
(not @ready-to-lock?)
|
||||
(not @ready-to-send?))))
|
||||
(do
|
||||
(reset! disabled? (<= touch-timestamp-diff min-touch-duration))
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(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)
|
||||
(reset! recording-length-ms 0)
|
||||
(reset! recording-start-ms nil)
|
||||
(reset! disabled? false))
|
||||
(if (> touch-timestamp-diff min-touch-duration) 0 250)))
|
||||
(and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?))
|
||||
(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)
|
||||
(reset! recording-length-ms 0)
|
||||
(reset! recording-start-ms nil)
|
||||
(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! touch-timestamp nil))]
|
||||
(fn []
|
||||
(rn/use-mount (fn []
|
||||
(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! app-state-listener
|
||||
(.addEventListener rn/app-state
|
||||
"change"
|
||||
#(when (= % "background")
|
||||
(reset! playing-audio? false))))
|
||||
#(.remove @app-state-listener)))
|
||||
[rn/view
|
||||
{:style style/bar-container
|
||||
:pointer-events :box-none}
|
||||
(when @reviewing-audio?
|
||||
[:<>
|
||||
[:f> f-play-button playing-audio? player-ref playing-timer audio-current-time-ms
|
||||
seeking-audio? max-duration-ms]
|
||||
[:f> soundtrack/f-soundtrack
|
||||
{:audio-current-time-ms audio-current-time-ms
|
||||
:player-ref @player-ref
|
||||
:seeking-audio? seeking-audio?
|
||||
:max-audio-duration-ms max-duration-ms}]])
|
||||
(when (or @recording? @reviewing-audio?)
|
||||
[:f> f-time-counter @recording? @recording-length-ms @ready-to-delete? @reviewing-audio?
|
||||
@audio-current-time-ms])
|
||||
(when @recording?
|
||||
[:f> f-recording-bar @recording-length-ms @ready-to-delete?])
|
||||
[rn/view
|
||||
{:test-ID "record-audio"
|
||||
:style style/button-container
|
||||
:hit-slop {:top -70
|
||||
: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}
|
||||
[:f> delete-button/f-delete-button recording? ready-to-delete? reviewing-audio?
|
||||
@force-show-controls?]
|
||||
[: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? recording?
|
||||
:ready-to-send? ready-to-send?
|
||||
:ready-to-lock? ready-to-lock?
|
||||
:ready-to-delete? ready-to-delete?
|
||||
:record-button-is-animating? record-button-is-animating?
|
||||
:record-button-at-initial-position? record-button-at-initial-position?
|
||||
:locked? locked?
|
||||
:reviewing-audio? reviewing-audio?
|
||||
:recording-length-ms recording-length-ms
|
||||
:clear-timeout clear-timeout
|
||||
:touch-active? touch-active?
|
||||
:recorder-ref recorder-ref
|
||||
:reload-recorder-fn reload-recorder
|
||||
:idle? idle?
|
||||
:on-send on-send
|
||||
:on-cancel on-cancel
|
||||
:theme theme}]
|
||||
[:f> record-button/f-record-button recording? reviewing-audio?]]])))])
|
||||
(defn record-audio
|
||||
[{:keys [on-init on-start-recording on-send on-cancel on-reviewing-audio audio-file on-lock
|
||||
max-duration-ms on-check-audio-permissions record-audio-permission-granted
|
||||
on-request-record-audio-permission]}]
|
||||
(let [;;STATE
|
||||
[recording? set-recording] (rn/use-state false)
|
||||
[locked? set-locked] (rn/use-state false)
|
||||
[ready-to-send? set-ready-to-send] (rn/use-state false)
|
||||
[ready-to-lock? set-ready-to-lock] (rn/use-state false)
|
||||
[ready-to-delete?
|
||||
set-ready-to-delete] (rn/use-state false)
|
||||
[reviewing-audio?
|
||||
set-reviewing-audio] (rn/use-state (some? audio-file))
|
||||
[playing-audio?
|
||||
set-playing-audio] (rn/use-state false)
|
||||
[recording-length-ms
|
||||
set-recording-length-ms] (rn/use-state 0)
|
||||
[audio-current-time-ms
|
||||
set-audio-current-time-ms] (rn/use-state 0)
|
||||
[seeking-audio? set-seeking-audio] (rn/use-state false)
|
||||
[force-show-controls?
|
||||
set-force-show-controls] (rn/use-state (some? audio-file))
|
||||
;;ATOMS
|
||||
recording-start-ms (rn/use-ref-atom (datetime/timestamp))
|
||||
clear-timeout (rn/use-ref-atom nil)
|
||||
record-button-at-initial-position? (rn/use-ref-atom true)
|
||||
record-button-is-animating? (rn/use-ref-atom false)
|
||||
idle? (rn/use-ref-atom false)
|
||||
recorder-ref (rn/use-ref-atom nil)
|
||||
player-ref (rn/use-ref-atom nil)
|
||||
touch-active? (rn/use-ref-atom false)
|
||||
playing-timer (rn/use-ref-atom nil)
|
||||
output-file (rn/use-ref-atom audio-file)
|
||||
reached-max-duration? (rn/use-ref-atom false)
|
||||
touch-timestamp (rn/use-ref-atom nil)
|
||||
touch-identifier (rn/use-ref-atom nil)
|
||||
disabled? (rn/use-ref-atom false)
|
||||
app-state-listener (rn/use-ref-atom nil)
|
||||
;;HANDLERS
|
||||
destroy-player (rn/use-callback
|
||||
(fn []
|
||||
(audio/destroy-player @player-ref)
|
||||
(reset! player-ref nil)))
|
||||
reload-player (handlers/get-reload-player
|
||||
[player-ref destroy-player set-playing-audio playing-timer
|
||||
set-audio-current-time-ms set-seeking-audio])
|
||||
recorder-on-meter (handlers/get-recorder-on-meter
|
||||
[recording-start-ms set-recording-length-ms
|
||||
reached-max-duration?
|
||||
locked? set-locked set-reviewing-audio idle? set-recording
|
||||
set-ready-to-lock set-ready-to-send set-ready-to-delete
|
||||
recorder-ref
|
||||
output-file reload-player
|
||||
on-reviewing-audio])
|
||||
reload-recorder (handlers/get-reload-recorder [recorder-ref
|
||||
recorder-on-meter])
|
||||
on-start-should-set-responder (handlers/get-on-start-should-set-responder
|
||||
[locked? idle? disabled? recorder-on-meter touch-timestamp
|
||||
touch-identifier
|
||||
reviewing-audio? record-audio-permission-granted
|
||||
set-recording set-playing-audio output-file
|
||||
recorder-ref recording-start-ms set-audio-current-time-ms
|
||||
on-start-recording
|
||||
on-request-record-audio-permission touch-active?])
|
||||
on-responder-move (handlers/get-on-responder-move
|
||||
[locked? ready-to-send? set-ready-to-send
|
||||
ready-to-delete? set-ready-to-delete
|
||||
ready-to-lock? set-ready-to-lock
|
||||
touch-identifier
|
||||
record-button-at-initial-position?
|
||||
recording?])
|
||||
on-responder-release (handlers/get-on-responder-release
|
||||
[idle? reached-max-duration? touch-timestamp
|
||||
recording-length-ms set-recording-length-ms
|
||||
reviewing-audio? set-reviewing-audio
|
||||
set-audio-current-time-ms set-force-show-controls on-send
|
||||
output-file player-ref
|
||||
destroy-player on-cancel record-button-is-animating?
|
||||
ready-to-lock? set-ready-to-lock
|
||||
locked? set-locked on-lock ready-to-delete?
|
||||
set-ready-to-delete ready-to-send? set-ready-to-send
|
||||
disabled? on-reviewing-audio
|
||||
recorder-ref set-recording reload-player recording-start-ms
|
||||
touch-active?])]
|
||||
|
||||
(def record-audio (quo.theme/with-theme record-audio-internal))
|
||||
;;ON MOUNT
|
||||
(rn/use-mount
|
||||
(fn []
|
||||
(when on-check-audio-permissions (on-check-audio-permissions))
|
||||
(when on-init
|
||||
(on-init
|
||||
(fn reset-recorder []
|
||||
(set-recording false)
|
||||
(set-reviewing-audio false)
|
||||
(set-locked false)
|
||||
(set-ready-to-send false)
|
||||
(set-ready-to-lock false)
|
||||
(set-ready-to-delete false)
|
||||
(set-audio-current-time-ms 0)
|
||||
(set-recording-length-ms 0)
|
||||
(set-seeking-audio false)
|
||||
(set-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 @playing-timer
|
||||
(js/clearInterval @playing-timer)
|
||||
(reset! playing-timer nil))
|
||||
(reload-recorder))))
|
||||
(when audio-file
|
||||
(let [filename (last (string/split audio-file "/"))]
|
||||
(reload-player filename)))
|
||||
(reset! app-state-listener
|
||||
(.addEventListener rn/app-state
|
||||
"change"
|
||||
#(when (= % "background")
|
||||
(set-playing-audio false))))))
|
||||
;;ON UNMOUNT
|
||||
(rn/use-unmount #(.remove @app-state-listener))
|
||||
|
||||
[rn/view
|
||||
{:style style/bar-container
|
||||
:pointer-events :box-none}
|
||||
(when reviewing-audio?
|
||||
[:<>
|
||||
[play-button playing-audio? set-playing-audio player-ref playing-timer set-audio-current-time-ms
|
||||
seeking-audio? set-seeking-audio max-duration-ms]
|
||||
[soundtrack/soundtrack
|
||||
{:audio-current-time-ms audio-current-time-ms
|
||||
:set-audio-current-time-ms set-audio-current-time-ms
|
||||
:player-ref @player-ref
|
||||
:seeking-audio? seeking-audio?
|
||||
:set-seeking-audio set-seeking-audio
|
||||
:max-audio-duration-ms max-duration-ms}]])
|
||||
(when (or recording? reviewing-audio?)
|
||||
[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?])
|
||||
[rn/view
|
||||
{:test-ID "record-audio"
|
||||
:style style/button-container
|
||||
:hit-slop {:top -70 :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? 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
|
||||
{:recording? recording?
|
||||
:set-recording set-recording
|
||||
:ready-to-send? ready-to-send?
|
||||
:set-ready-to-send set-ready-to-send
|
||||
:ready-to-lock? ready-to-lock?
|
||||
:set-ready-to-lock set-ready-to-lock
|
||||
:ready-to-delete? ready-to-delete?
|
||||
:set-ready-to-delete set-ready-to-delete
|
||||
:record-button-is-animating? record-button-is-animating?
|
||||
:record-button-at-initial-position? record-button-at-initial-position?
|
||||
:locked? locked?
|
||||
:set-locked set-locked
|
||||
:reviewing-audio? reviewing-audio?
|
||||
:recording-length-ms recording-length-ms
|
||||
:set-recording-length-ms set-recording-length-ms
|
||||
:clear-timeout clear-timeout
|
||||
:touch-active? touch-active?
|
||||
:recorder-ref recorder-ref
|
||||
:reload-recorder-fn reload-recorder
|
||||
:idle? idle?
|
||||
:on-send on-send
|
||||
:on-cancel on-cancel}]
|
||||
[record-button/record-button recording? reviewing-audio?]]]))
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
(:require
|
||||
[quo.components.record-audio.soundtrack.view :as soundtrack]
|
||||
[react-native.audio-toolkit :as audio]
|
||||
[reagent.core :as reagent]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "soundtrack component"
|
||||
|
@ -10,23 +9,21 @@
|
|||
|
||||
(h/test "renders soundtrack"
|
||||
(with-redefs [audio/get-player-duration (fn [] 2000)]
|
||||
(let [player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(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"))
|
||||
(.toBeTruthy)))))
|
||||
(h/render [soundtrack/soundtrack
|
||||
{:player-ref {}
|
||||
:audio-current-time-ms 0}])
|
||||
(-> (h/expect (h/get-by-test-id "soundtrack"))
|
||||
(.toBeTruthy))))
|
||||
|
||||
(h/test "soundtrack on-sliding-start works"
|
||||
(with-redefs [audio/get-player-duration (fn [] 2000)]
|
||||
(let [seeking-audio? (reagent/atom false)
|
||||
player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(h/render [:f> soundtrack/f-soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:player-ref @player-ref
|
||||
:audio-current-time-ms audio-current-time-ms}])
|
||||
(let [seeking-audio? (atom false)]
|
||||
(h/render [soundtrack/soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:set-seeking-audio #(reset! seeking-audio? %)
|
||||
:player-ref {}
|
||||
:audio-current-time-ms 0
|
||||
:set-audio-current-time-ms #()}])
|
||||
(h/fire-event
|
||||
:on-sliding-start
|
||||
(h/get-by-test-id "soundtrack"))
|
||||
|
@ -36,13 +33,13 @@
|
|||
(h/test "soundtrack on-sliding-complete works"
|
||||
(with-redefs [audio/get-player-duration (fn [] 2000)
|
||||
audio/seek-player (js/jest.fn)]
|
||||
(let [seeking-audio? (reagent/atom false)
|
||||
player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(h/render [:f> soundtrack/f-soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:player-ref @player-ref
|
||||
:audio-current-time-ms audio-current-time-ms}])
|
||||
(let [seeking-audio? (atom false)]
|
||||
(h/render [soundtrack/soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:set-seeking-audio #(reset! seeking-audio? %)
|
||||
:player-ref {}
|
||||
:audio-current-time-ms 0
|
||||
:set-audio-current-time-ms #()}])
|
||||
(h/fire-event
|
||||
:on-sliding-start
|
||||
(h/get-by-test-id "soundtrack"))
|
||||
|
@ -58,13 +55,14 @@
|
|||
(h/test "soundtrack on-value-change when seeking audio works"
|
||||
(with-redefs [audio/get-player-duration (fn [] 2000)
|
||||
audio/seek-player (js/jest.fn)]
|
||||
(let [seeking-audio? (reagent/atom false)
|
||||
player-ref (reagent/atom {})
|
||||
audio-current-time-ms (reagent/atom 0)]
|
||||
(h/render [:f> soundtrack/f-soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:player-ref @player-ref
|
||||
:audio-current-time-ms audio-current-time-ms}])
|
||||
(let [seeking-audio? (atom false)
|
||||
audio-current-time-ms (atom 0)]
|
||||
(h/render [soundtrack/soundtrack
|
||||
{:seeking-audio? seeking-audio?
|
||||
:set-seeking-audio #(reset! seeking-audio? %)
|
||||
:player-ref {}
|
||||
:audio-current-time-ms audio-current-time-ms
|
||||
:set-audio-current-time-ms #(reset! audio-current-time-ms %)}])
|
||||
(h/fire-event
|
||||
:on-sliding-start
|
||||
(h/get-by-test-id "soundtrack"))
|
||||
|
|
|
@ -1,42 +1,47 @@
|
|||
(ns quo.components.record-audio.soundtrack.view
|
||||
(:require
|
||||
[quo.components.icons.icons :as icons]
|
||||
[quo.components.record-audio.soundtrack.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.audio-toolkit :as audio]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.slider :as slider]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(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"))
|
||||
(def ^:private thumb-light (icons/icon-source :thumb-light12))
|
||||
(def ^:private thumb-dark (icons/icon-source :thumb-dark12))
|
||||
|
||||
(defn f-soundtrack
|
||||
[{:keys [audio-current-time-ms player-ref style seeking-audio? max-audio-duration-ms]}]
|
||||
(let [audio-duration-ms (min max-audio-duration-ms (audio/get-player-duration player-ref))
|
||||
theme (quo.theme/use-theme-value)]
|
||||
[:<>
|
||||
[slider/slider
|
||||
{:test-ID "soundtrack"
|
||||
:style (merge
|
||||
(style/player-slider-container)
|
||||
(or style {}))
|
||||
: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 (quo.theme/theme-value thumb-light thumb-dark theme)
|
||||
:minimum-track-tint-color (colors/theme-colors colors/primary-50 colors/primary-60 theme)
|
||||
: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)
|
||||
theme)}]]))
|
||||
(defn soundtrack
|
||||
[{:keys [audio-current-time-ms set-audio-current-time-ms player-ref style
|
||||
seeking-audio? set-seeking-audio max-audio-duration-ms]}]
|
||||
(let [audio-duration-ms (min max-audio-duration-ms (audio/get-player-duration player-ref))
|
||||
theme (quo.theme/use-theme-value)
|
||||
on-sliding-start (rn/use-callback #(set-seeking-audio true))
|
||||
on-sliding-complete (rn/use-callback
|
||||
(fn [seek-time]
|
||||
(set-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: " %)))
|
||||
[player-ref])
|
||||
on-value-change (rn/use-callback #(when seeking-audio? (set-audio-current-time-ms %))
|
||||
[seeking-audio?])]
|
||||
[slider/slider
|
||||
{:test-ID "soundtrack"
|
||||
:style (merge (style/player-slider-container) style)
|
||||
:minimum-value 0
|
||||
:maximum-value audio-duration-ms
|
||||
:value audio-current-time-ms
|
||||
:on-sliding-start on-sliding-start
|
||||
:on-sliding-complete on-sliding-complete
|
||||
:on-value-change on-value-change
|
||||
:thumb-image (quo.theme/theme-value thumb-light thumb-dark theme)
|
||||
:minimum-track-tint-color (colors/theme-colors colors/primary-50 colors/primary-60 theme)
|
||||
: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)
|
||||
theme)}]))
|
||||
|
|
|
@ -357,7 +357,7 @@
|
|||
|
||||
;;;; Record Audio
|
||||
(def record-audio quo.components.record-audio.record-audio.view/record-audio)
|
||||
(def soundtrack quo.components.record-audio.soundtrack.view/f-soundtrack)
|
||||
(def soundtrack quo.components.record-audio.soundtrack.view/soundtrack)
|
||||
|
||||
;;;; Selectors
|
||||
(def disclaimer quo.components.selectors.disclaimer.view/view)
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
(when on-meter
|
||||
(.on ^js recorder "meter" on-meter))
|
||||
(when on-ended
|
||||
(.on ^js recorder "ended" on-ended))))
|
||||
(.on ^js recorder "ended" on-ended))
|
||||
recorder))
|
||||
|
||||
(defn new-player
|
||||
[audio options on-ended]
|
||||
|
@ -46,7 +47,8 @@
|
|||
audio
|
||||
(clj->js options))]
|
||||
(when on-ended
|
||||
(.on ^js player "ended" on-ended))))
|
||||
(.on ^js player "ended" on-ended))
|
||||
player))
|
||||
|
||||
(defn prepare-player
|
||||
[player on-prepared on-error]
|
||||
|
@ -157,20 +159,16 @@
|
|||
(on-error {:error (.-err err) :message (.-message err)})
|
||||
(on-seek))))))
|
||||
|
||||
(defn can-play?
|
||||
[player]
|
||||
(and player (.-canPlay ^js player)))
|
||||
|
||||
(defn destroy-recorder
|
||||
[recorder]
|
||||
(stop-recording recorder
|
||||
#(when (and recorder (not= (get-state recorder) DESTROYED))
|
||||
#(when (and recorder (.-destroy ^js recorder) (not= (get-state recorder) DESTROYED))
|
||||
(.destroy ^js recorder))
|
||||
#()))
|
||||
|
||||
(defn destroy-player
|
||||
[player]
|
||||
(stop-playing player
|
||||
#(when (and player (not= (get-state player) IDLE))
|
||||
#(when (and player (.-destroy ^js player) (not= (get-state player) IDLE))
|
||||
(.destroy ^js player))
|
||||
#()))
|
||||
|
|
|
@ -210,12 +210,14 @@
|
|||
:else :i/play-audio)
|
||||
{:size 20
|
||||
:color colors/white}]]
|
||||
[:f> quo/soundtrack
|
||||
{:style style/slider-container
|
||||
:audio-current-time-ms progress
|
||||
:player-ref (@active-players player-key)
|
||||
:seeking-audio? seeking-audio?
|
||||
:max-audio-duration-ms constants/audio-max-duration-ms}]
|
||||
[quo/soundtrack
|
||||
{:style style/slider-container
|
||||
:audio-current-time-ms @progress
|
||||
:set-audio-current-time-ms #(reset! progress %)
|
||||
:player-ref (@active-players player-key)
|
||||
:seeking-audio? @seeking-audio?
|
||||
:set-seeking-audio? #(reset! seeking-audio? %)
|
||||
:max-audio-duration-ms constants/audio-max-duration-ms}]
|
||||
[quo/text
|
||||
{:style style/timestamp
|
||||
:accessibility-label :audio-duration-label
|
||||
|
|
Loading…
Reference in New Issue