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.helpers :as helpers]
|
||||||
[quo.components.record-audio.record-audio.style :as style]
|
[quo.components.record-audio.record-audio.style :as style]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[react-native.core :refer [use-effect]]
|
[react-native.core :as rn]
|
||||||
[react-native.reanimated :as reanimated]))
|
[react-native.reanimated :as reanimated]))
|
||||||
|
|
||||||
(defn f-delete-button
|
(defn delete-button
|
||||||
[recording? ready-to-delete? reviewing-audio? force-show-controls?]
|
[recording? ready-to-delete? reviewing-audio? force-show-controls?]
|
||||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||||
translate-x (reanimated/use-shared-value (if force-show-controls? 35 20))
|
translate-x (reanimated/use-shared-value (if force-show-controls? 35 20))
|
||||||
|
@ -17,7 +17,8 @@
|
||||||
connector-height (reanimated/use-shared-value 12)
|
connector-height (reanimated/use-shared-value 12)
|
||||||
border-radius-first-half (reanimated/use-shared-value 8)
|
border-radius-first-half (reanimated/use-shared-value 8)
|
||||||
border-radius-second-half (reanimated/use-shared-value 8)
|
border-radius-second-half (reanimated/use-shared-value 8)
|
||||||
start-x-animation (fn []
|
start-x-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear-with-delay translate-x 12 50 133.33)
|
(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-opacity 1 0 93.33)
|
||||||
(helpers/animate-easing-with-delay connector-width 56 83.33 80)
|
(helpers/animate-easing-with-delay connector-width 56 83.33 80)
|
||||||
|
@ -29,23 +30,26 @@
|
||||||
(helpers/animate-easing-with-delay border-radius-second-half
|
(helpers/animate-easing-with-delay border-radius-second-half
|
||||||
28
|
28
|
||||||
83.33
|
83.33
|
||||||
80))
|
80)))
|
||||||
reset-x-animation (fn []
|
reset-x-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear translate-x 0 100)
|
(helpers/animate-linear translate-x 0 100)
|
||||||
(helpers/set-value connector-opacity 0)
|
(helpers/set-value connector-opacity 0)
|
||||||
(helpers/set-value connector-width 24)
|
(helpers/set-value connector-width 24)
|
||||||
(helpers/set-value connector-height 12)
|
(helpers/set-value connector-height 12)
|
||||||
(helpers/set-value border-radius-first-half 8)
|
(helpers/set-value border-radius-first-half 8)
|
||||||
(helpers/set-value border-radius-second-half 16))
|
(helpers/set-value border-radius-second-half 16)))
|
||||||
fade-in-animation (fn []
|
fade-in-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear translate-x 0 200)
|
(helpers/animate-linear translate-x 0 200)
|
||||||
(helpers/animate-linear opacity 1 200))
|
(helpers/animate-linear opacity 1 200)))
|
||||||
fade-out-animation (fn []
|
fade-out-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear
|
(helpers/animate-linear
|
||||||
translate-x
|
translate-x
|
||||||
(if @reviewing-audio? 35 20)
|
(if reviewing-audio? 35 20)
|
||||||
200)
|
200)
|
||||||
(if @reviewing-audio?
|
(if reviewing-audio?
|
||||||
(helpers/animate-linear scale 0.75 200)
|
(helpers/animate-linear scale 0.75 200)
|
||||||
(helpers/animate-linear opacity 0 200))
|
(helpers/animate-linear opacity 0 200))
|
||||||
(helpers/set-value connector-opacity 0)
|
(helpers/set-value connector-opacity 0)
|
||||||
|
@ -53,26 +57,28 @@
|
||||||
(helpers/set-value connector-height 12)
|
(helpers/set-value connector-height 12)
|
||||||
(helpers/set-value border-radius-first-half 8)
|
(helpers/set-value border-radius-first-half 8)
|
||||||
(helpers/set-value border-radius-second-half 16))
|
(helpers/set-value border-radius-second-half 16))
|
||||||
fade-out-reset-animation (fn []
|
[reviewing-audio?])
|
||||||
|
fade-out-reset-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear opacity 0 200)
|
(helpers/animate-linear opacity 0 200)
|
||||||
(helpers/animate-linear-with-delay translate-x 20 0 200)
|
(helpers/animate-linear-with-delay translate-x 20 0 200)
|
||||||
(helpers/animate-linear-with-delay scale 1 0 200))]
|
(helpers/animate-linear-with-delay scale 1 0 200)))]
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(if @recording?
|
(if recording?
|
||||||
(fade-in-animation)
|
(fade-in-animation)
|
||||||
(fade-out-animation)))
|
(fade-out-animation)))
|
||||||
[@recording?])
|
[recording?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(when-not @reviewing-audio?
|
(when-not reviewing-audio?
|
||||||
(fade-out-reset-animation)))
|
(fade-out-reset-animation)))
|
||||||
[@reviewing-audio?])
|
[reviewing-audio?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(cond
|
(cond
|
||||||
@ready-to-delete?
|
ready-to-delete?
|
||||||
(start-x-animation)
|
(start-x-animation)
|
||||||
@recording?
|
recording?
|
||||||
(reset-x-animation)))
|
(reset-x-animation)))
|
||||||
[@ready-to-delete?])
|
[ready-to-delete?])
|
||||||
[:<>
|
[:<>
|
||||||
[reanimated/view {:style (style/delete-button-container opacity)}
|
[reanimated/view {:style (style/delete-button-container opacity)}
|
||||||
[reanimated/view
|
[reanimated/view
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
[quo.components.record-audio.record-audio.style :as style]
|
[quo.components.record-audio.record-audio.style :as style]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[quo.theme :as quo.theme]
|
[quo.theme :as quo.theme]
|
||||||
[react-native.core :refer [use-effect]]
|
[react-native.core :as rn]
|
||||||
[react-native.reanimated :as reanimated]))
|
[react-native.reanimated :as reanimated]))
|
||||||
|
|
||||||
(defn f-lock-button
|
(defn lock-button
|
||||||
[recording? ready-to-lock? locked?]
|
[recording? ready-to-lock? locked?]
|
||||||
(let [theme (quo.theme/use-theme-value)
|
(let [theme (quo.theme/use-theme-value)
|
||||||
translate-x-y (reanimated/use-shared-value 20)
|
translate-x-y (reanimated/use-shared-value 20)
|
||||||
|
@ -18,7 +18,8 @@
|
||||||
height (reanimated/use-shared-value 12)
|
height (reanimated/use-shared-value 12)
|
||||||
border-radius-first-half (reanimated/use-shared-value 8)
|
border-radius-first-half (reanimated/use-shared-value 8)
|
||||||
border-radius-second-half (reanimated/use-shared-value 8)
|
border-radius-second-half (reanimated/use-shared-value 8)
|
||||||
start-x-y-animation (fn []
|
start-x-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear-with-delay translate-x-y 8 50 116.66)
|
(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 connector-opacity 1 0 80)
|
||||||
(helpers/animate-easing-with-delay width 56 83.33 63.33)
|
(helpers/animate-easing-with-delay width 56 83.33 63.33)
|
||||||
|
@ -30,42 +31,45 @@
|
||||||
(helpers/animate-easing-with-delay border-radius-second-half
|
(helpers/animate-easing-with-delay border-radius-second-half
|
||||||
28
|
28
|
||||||
83.33
|
83.33
|
||||||
63.33))
|
63.33)))
|
||||||
reset-x-y-animation (fn []
|
reset-x-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear translate-x-y 0 100)
|
(helpers/animate-linear translate-x-y 0 100)
|
||||||
(helpers/set-value connector-opacity 0)
|
(helpers/set-value connector-opacity 0)
|
||||||
(helpers/set-value width 24)
|
(helpers/set-value width 24)
|
||||||
(helpers/set-value height 12)
|
(helpers/set-value height 12)
|
||||||
(helpers/set-value border-radius-first-half 8)
|
(helpers/set-value border-radius-first-half 8)
|
||||||
(helpers/set-value border-radius-second-half 16))
|
(helpers/set-value border-radius-second-half 16)))
|
||||||
fade-in-animation (fn []
|
fade-in-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear translate-x-y 0 220)
|
(helpers/animate-linear translate-x-y 0 220)
|
||||||
(helpers/animate-linear opacity 1 220))
|
(helpers/animate-linear opacity 1 220)))
|
||||||
fade-out-animation (fn []
|
fade-out-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear translate-x-y 20 200)
|
(helpers/animate-linear translate-x-y 20 200)
|
||||||
(helpers/animate-linear opacity 0 200)
|
(helpers/animate-linear opacity 0 200)
|
||||||
(helpers/set-value connector-opacity 0)
|
(helpers/set-value connector-opacity 0)
|
||||||
(helpers/set-value width 24)
|
(helpers/set-value width 24)
|
||||||
(helpers/set-value height 12)
|
(helpers/set-value height 12)
|
||||||
(helpers/set-value border-radius-first-half 8)
|
(helpers/set-value border-radius-first-half 8)
|
||||||
(helpers/set-value border-radius-second-half 16))]
|
(helpers/set-value border-radius-second-half 16)))]
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(if @recording?
|
(if recording?
|
||||||
(fade-in-animation)
|
(fade-in-animation)
|
||||||
(fade-out-animation)))
|
(fade-out-animation)))
|
||||||
[@recording?])
|
[recording?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(cond
|
(cond
|
||||||
@ready-to-lock?
|
ready-to-lock?
|
||||||
(start-x-y-animation)
|
(start-x-y-animation)
|
||||||
(and @recording? (not @locked?))
|
(and recording? (not locked?))
|
||||||
(reset-x-y-animation)))
|
(reset-x-y-animation)))
|
||||||
[@ready-to-lock?])
|
[ready-to-lock?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(if @locked?
|
(if locked?
|
||||||
(fade-out-animation)
|
(fade-out-animation)
|
||||||
(reset-x-y-animation)))
|
(reset-x-y-animation)))
|
||||||
[@locked?])
|
[locked?])
|
||||||
[:<>
|
[:<>
|
||||||
[reanimated/view {:style (style/lock-button-container opacity)}
|
[reanimated/view {:style (style/lock-button-container opacity)}
|
||||||
[reanimated/view
|
[reanimated/view
|
||||||
|
@ -77,6 +81,6 @@
|
||||||
[reanimated/view
|
[reanimated/view
|
||||||
{:style (style/lock-button translate-x-y opacity)
|
{:style (style/lock-button translate-x-y opacity)
|
||||||
:pointer-events :none}
|
: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)
|
{:color (colors/theme-colors colors/black colors/white theme)
|
||||||
:size 20}]]]))
|
:size 20}]]]))
|
||||||
|
|
|
@ -3,19 +3,17 @@
|
||||||
[quo.components.buttons.button.view :as button]
|
[quo.components.buttons.button.view :as button]
|
||||||
[quo.components.record-audio.record-audio.helpers :as helpers]
|
[quo.components.record-audio.record-audio.helpers :as helpers]
|
||||||
[quo.components.record-audio.record-audio.style :as style]
|
[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]))
|
[react-native.reanimated :as reanimated]))
|
||||||
|
|
||||||
(defn f-record-button
|
(defn record-button
|
||||||
[recording? reviewing-audio?]
|
[recording? reviewing-audio?]
|
||||||
(let [opacity (reanimated/use-shared-value 1)
|
(let [opacity (reanimated/use-shared-value 1)]
|
||||||
show-animation #(helpers/set-value opacity 1)
|
(rn/use-effect (fn []
|
||||||
hide-animation #(helpers/set-value opacity 0)]
|
(if (or recording? reviewing-audio?)
|
||||||
(use-effect (fn []
|
(helpers/set-value opacity 0)
|
||||||
(if (or @recording? @reviewing-audio?)
|
(helpers/set-value opacity 1)))
|
||||||
(hide-animation)
|
[recording? reviewing-audio?])
|
||||||
(show-animation)))
|
|
||||||
[@recording? @reviewing-audio?])
|
|
||||||
[reanimated/view {:style (style/record-button-container opacity)}
|
[reanimated/view {:style (style/record-button-container opacity)}
|
||||||
[button/button
|
[button/button
|
||||||
{:type :outline
|
{:type :outline
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
[quo.components.record-audio.record-audio.helpers :as helpers]
|
[quo.components.record-audio.record-audio.helpers :as helpers]
|
||||||
[quo.components.record-audio.record-audio.style :as style]
|
[quo.components.record-audio.record-audio.style :as style]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
|
[quo.theme]
|
||||||
[react-native.audio-toolkit :as audio]
|
[react-native.audio-toolkit :as audio]
|
||||||
[react-native.core :as rn :refer [use-effect]]
|
[react-native.core :as rn]
|
||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
|
@ -28,13 +29,16 @@
|
||||||
(reagent/as-element
|
(reagent/as-element
|
||||||
[reanimated/view {:style (style/animated-circle scale opacity color)}]))))))
|
[reanimated/view {:style (style/animated-circle scale opacity color)}]))))))
|
||||||
|
|
||||||
(defn f-record-button-big
|
(defn record-button-big
|
||||||
[{:keys [recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating?
|
[{:keys [recording? set-recording ready-to-send? set-ready-to-send ready-to-lock? set-ready-to-lock
|
||||||
record-button-at-initial-position? locked? reviewing-audio? recording-length-ms
|
ready-to-delete? set-ready-to-delete record-button-is-animating?
|
||||||
clear-timeout touch-active? recorder-ref reload-recorder-fn idle? on-send on-cancel theme]}]
|
record-button-at-initial-position? locked? set-locked reviewing-audio? recording-length-ms
|
||||||
(let [scale (reanimated/use-shared-value 1)
|
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 (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
|
animations (map
|
||||||
(fn [index]
|
(fn [index]
|
||||||
(let [ring-scale (worklets.record-audio/ring-scale scale
|
(let [ring-scale (worklets.record-audio/ring-scale scale
|
||||||
|
@ -46,48 +50,52 @@
|
||||||
[opacity-from 0])}))
|
[opacity-from 0])}))
|
||||||
(range 0 5))
|
(range 0 5))
|
||||||
rings-color (cond
|
rings-color (cond
|
||||||
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
|
ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
|
||||||
colors/neutral-80
|
colors/neutral-80
|
||||||
theme)
|
theme)
|
||||||
@ready-to-delete? colors/danger-50
|
ready-to-delete? colors/danger-50
|
||||||
:else colors/primary-50)
|
:else colors/primary-50)
|
||||||
translate-y (reanimated/use-shared-value 0)
|
translate-y (reanimated/use-shared-value 0)
|
||||||
translate-x (reanimated/use-shared-value 0)
|
translate-x (reanimated/use-shared-value 0)
|
||||||
button-color colors/primary-50
|
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)
|
icon-opacity (reanimated/use-shared-value 1)
|
||||||
red-overlay-opacity (reanimated/use-shared-value 0)
|
red-overlay-opacity (reanimated/use-shared-value 0)
|
||||||
gray-overlay-opacity (reanimated/use-shared-value 0)
|
gray-overlay-opacity (reanimated/use-shared-value 0)
|
||||||
complete-animation
|
complete-animation
|
||||||
|
(rn/use-callback
|
||||||
(fn []
|
(fn []
|
||||||
(cond
|
(cond
|
||||||
(and @ready-to-lock? (not @record-button-is-animating?))
|
(and ready-to-lock? (not @record-button-is-animating?))
|
||||||
(do
|
(do
|
||||||
(reset! locked? true)
|
(set-locked true)
|
||||||
(reset! ready-to-lock? false))
|
(set-ready-to-lock false))
|
||||||
(and (not @locked?) (not @reviewing-audio?))
|
(and (not locked?) (not reviewing-audio?))
|
||||||
(audio/stop-recording
|
(audio/stop-recording
|
||||||
@recorder-ref
|
@recorder-ref
|
||||||
(fn []
|
(fn []
|
||||||
(cond
|
(cond
|
||||||
@ready-to-send?
|
ready-to-send?
|
||||||
(when on-send
|
(when on-send
|
||||||
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
|
(on-send {:file-path (audio/get-recorder-file-path @recorder-ref)
|
||||||
:duration @recording-length-ms}))
|
:duration recording-length-ms}))
|
||||||
@ready-to-delete?
|
ready-to-delete?
|
||||||
(when on-cancel
|
(when on-cancel
|
||||||
(on-cancel)))
|
(on-cancel)))
|
||||||
(reload-recorder-fn)
|
(reload-recorder-fn)
|
||||||
(reset! recording? false)
|
(set-recording false)
|
||||||
(reset! ready-to-send? false)
|
(set-ready-to-send false)
|
||||||
(reset! ready-to-delete? false)
|
(set-ready-to-delete false)
|
||||||
(reset! ready-to-lock? false)
|
(set-ready-to-lock false)
|
||||||
(reset! idle? true)
|
(reset! idle? true)
|
||||||
(js/setTimeout #(reset! idle? false) 1000)
|
(js/setTimeout #(reset! idle? false) 1000)
|
||||||
(reset! recording-length-ms 0)
|
(set-recording-length-ms 0)
|
||||||
(log/debug "[record-audio] stop recording - success"))
|
(log/debug "[record-audio] stop recording - success"))
|
||||||
#(log/error "[record-audio] stop recording - error: " %))))
|
#(log/error "[record-audio] stop recording - error: " %))))
|
||||||
start-animation (fn []
|
[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/set-value opacity 1)
|
||||||
(helpers/animate-linear scale 2.6 signal-anim-duration)
|
(helpers/animate-linear scale 2.6 signal-anim-duration)
|
||||||
;; TODO: Research if we can implement this with withSequence method
|
;; TODO: Research if we can implement this with withSequence method
|
||||||
|
@ -102,13 +110,15 @@
|
||||||
scale-to-total
|
scale-to-total
|
||||||
signal-anim-duration-2
|
signal-anim-duration-2
|
||||||
0))
|
0))
|
||||||
signal-anim-duration)))
|
signal-anim-duration))))
|
||||||
stop-animation (fn []
|
stop-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/set-value opacity 0)
|
(helpers/set-value opacity 0)
|
||||||
(reanimated/cancel-animation scale)
|
(reanimated/cancel-animation scale)
|
||||||
(helpers/set-value scale 1)
|
(helpers/set-value scale 1)
|
||||||
(when @clear-timeout (js/clearTimeout @clear-timeout)))
|
(when @clear-timeout (js/clearTimeout @clear-timeout))))
|
||||||
start-y-animation (fn []
|
start-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(reset! record-button-at-initial-position? false)
|
(reset! record-button-at-initial-position? false)
|
||||||
(reset! record-button-is-animating? true)
|
(reset! record-button-is-animating? true)
|
||||||
(helpers/animate-easing translate-y -64 250)
|
(helpers/animate-easing translate-y -64 250)
|
||||||
|
@ -117,13 +127,16 @@
|
||||||
(reset! record-button-is-animating? false)
|
(reset! record-button-is-animating? false)
|
||||||
(when-not @touch-active? (complete-animation)))
|
(when-not @touch-active? (complete-animation)))
|
||||||
250))
|
250))
|
||||||
reset-y-animation (fn []
|
[complete-animation])
|
||||||
|
reset-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-easing translate-y 0 300)
|
(helpers/animate-easing translate-y 0 300)
|
||||||
(helpers/animate-linear icon-opacity 1 500)
|
(helpers/animate-linear icon-opacity 1 500)
|
||||||
(js/setTimeout (fn []
|
(js/setTimeout (fn []
|
||||||
(reset! record-button-at-initial-position? true))
|
(reset! record-button-at-initial-position? true))
|
||||||
500))
|
500)))
|
||||||
start-x-animation (fn []
|
start-x-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(reset! record-button-at-initial-position? false)
|
(reset! record-button-at-initial-position? false)
|
||||||
(reset! record-button-is-animating? true)
|
(reset! record-button-is-animating? true)
|
||||||
(helpers/animate-easing translate-x -64 250)
|
(helpers/animate-easing translate-x -64 250)
|
||||||
|
@ -133,14 +146,17 @@
|
||||||
(reset! record-button-is-animating? false)
|
(reset! record-button-is-animating? false)
|
||||||
(when-not @touch-active? (complete-animation)))
|
(when-not @touch-active? (complete-animation)))
|
||||||
250))
|
250))
|
||||||
reset-x-animation (fn []
|
[complete-animation])
|
||||||
|
reset-x-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-easing translate-x 0 300)
|
(helpers/animate-easing translate-x 0 300)
|
||||||
(helpers/animate-linear icon-opacity 1 500)
|
(helpers/animate-linear icon-opacity 1 500)
|
||||||
(helpers/animate-linear red-overlay-opacity 0 100)
|
(helpers/animate-linear red-overlay-opacity 0 100)
|
||||||
(js/setTimeout (fn []
|
(js/setTimeout (fn []
|
||||||
(reset! record-button-at-initial-position? true))
|
(reset! record-button-at-initial-position? true))
|
||||||
500))
|
500)))
|
||||||
start-x-y-animation (fn []
|
start-x-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(reset! record-button-at-initial-position? false)
|
(reset! record-button-at-initial-position? false)
|
||||||
(reset! record-button-is-animating? true)
|
(reset! record-button-is-animating? true)
|
||||||
(helpers/animate-easing translate-y -44 200)
|
(helpers/animate-easing translate-y -44 200)
|
||||||
|
@ -151,36 +167,38 @@
|
||||||
(reset! record-button-is-animating? false)
|
(reset! record-button-is-animating? false)
|
||||||
(when-not @touch-active? (complete-animation)))
|
(when-not @touch-active? (complete-animation)))
|
||||||
200))
|
200))
|
||||||
reset-x-y-animation (fn []
|
[complete-animation])
|
||||||
|
reset-x-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-easing translate-y 0 300)
|
(helpers/animate-easing translate-y 0 300)
|
||||||
(helpers/animate-easing translate-x 0 300)
|
(helpers/animate-easing translate-x 0 300)
|
||||||
(helpers/animate-linear icon-opacity 1 500)
|
(helpers/animate-linear icon-opacity 1 500)
|
||||||
(helpers/animate-linear gray-overlay-opacity 0 800)
|
(helpers/animate-linear gray-overlay-opacity 0 800)
|
||||||
(js/setTimeout (fn []
|
(js/setTimeout (fn []
|
||||||
(reset! record-button-at-initial-position? true))
|
(reset! record-button-at-initial-position? true))
|
||||||
800))]
|
800)))]
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(cond
|
(cond
|
||||||
@recording?
|
recording?
|
||||||
(start-animation)
|
(start-animation)
|
||||||
(not @ready-to-lock?)
|
(not ready-to-lock?)
|
||||||
(stop-animation)))
|
(stop-animation)))
|
||||||
[@recording?])
|
[recording?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(if @ready-to-lock?
|
(if ready-to-lock?
|
||||||
(start-x-y-animation)
|
(start-x-y-animation)
|
||||||
(reset-x-y-animation)))
|
(reset-x-y-animation)))
|
||||||
[@ready-to-lock?])
|
[ready-to-lock?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(if @ready-to-send?
|
(if ready-to-send?
|
||||||
(start-y-animation)
|
(start-y-animation)
|
||||||
(reset-y-animation)))
|
(reset-y-animation)))
|
||||||
[@ready-to-send?])
|
[ready-to-send?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(if @ready-to-delete?
|
(if ready-to-delete?
|
||||||
(start-x-animation)
|
(start-x-animation)
|
||||||
(reset-x-animation)))
|
(reset-x-animation)))
|
||||||
[@ready-to-delete?])
|
[ready-to-delete?])
|
||||||
[reanimated/view
|
[reanimated/view
|
||||||
{:style (style/record-button-big-container translate-x translate-y opacity)
|
{:style (style/record-button-big-container translate-x translate-y opacity)
|
||||||
:pointer-events :none}
|
: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-red-overlay red-overlay-opacity)}]
|
||||||
[reanimated/view {:style (style/record-button-big-gray-overlay gray-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)}
|
[reanimated/view {:style (style/record-button-big-icon-container icon-opacity)}
|
||||||
(if @locked?
|
(if locked?
|
||||||
[rn/view {:style style/stop-icon}]
|
[rn/view {:style style/stop-icon}]
|
||||||
[icons/icon :i/audio {:color icon-color}])]]]))
|
[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.helpers :as helpers]
|
||||||
[quo.components.record-audio.record-audio.style :as style]
|
[quo.components.record-audio.record-audio.style :as style]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[react-native.core :refer [use-effect]]
|
[react-native.core :as rn]
|
||||||
[react-native.reanimated :as reanimated]))
|
[react-native.reanimated :as reanimated]))
|
||||||
|
|
||||||
(defn f-send-button
|
(defn send-button
|
||||||
[recording? ready-to-send? reviewing-audio? force-show-controls?]
|
[recording? ready-to-send? reviewing-audio? force-show-controls?]
|
||||||
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
(let [opacity (reanimated/use-shared-value (if force-show-controls? 1 0))
|
||||||
translate-y (reanimated/use-shared-value (if force-show-controls? 76 20))
|
translate-y (reanimated/use-shared-value (if force-show-controls? 76 20))
|
||||||
|
@ -16,7 +16,8 @@
|
||||||
height (reanimated/use-shared-value 24)
|
height (reanimated/use-shared-value 24)
|
||||||
border-radius-first-half (reanimated/use-shared-value 16)
|
border-radius-first-half (reanimated/use-shared-value 16)
|
||||||
border-radius-second-half (reanimated/use-shared-value 8)
|
border-radius-second-half (reanimated/use-shared-value 8)
|
||||||
start-y-animation (fn []
|
start-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear-with-delay translate-y 12 50 133.33)
|
(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 connector-opacity 1 0 93.33)
|
||||||
(helpers/animate-easing-with-delay width 56 83.33 80)
|
(helpers/animate-easing-with-delay width 56 83.33 80)
|
||||||
|
@ -28,53 +29,58 @@
|
||||||
(helpers/animate-easing-with-delay border-radius-second-half
|
(helpers/animate-easing-with-delay border-radius-second-half
|
||||||
28
|
28
|
||||||
83.33
|
83.33
|
||||||
80))
|
80)))
|
||||||
reset-y-animation (fn []
|
reset-y-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear translate-y 0 100)
|
(helpers/animate-linear translate-y 0 100)
|
||||||
(helpers/set-value connector-opacity 0)
|
(helpers/set-value connector-opacity 0)
|
||||||
(helpers/set-value width 12)
|
(helpers/set-value width 12)
|
||||||
(helpers/set-value height 24)
|
(helpers/set-value height 24)
|
||||||
(helpers/set-value border-radius-first-half 16)
|
(helpers/set-value border-radius-first-half 16)
|
||||||
(helpers/set-value border-radius-second-half 8))
|
(helpers/set-value border-radius-second-half 8)))
|
||||||
fade-in-animation (fn []
|
fade-in-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear translate-y 0 200)
|
(helpers/animate-linear translate-y 0 200)
|
||||||
(helpers/animate-linear opacity 1 200))
|
(helpers/animate-linear opacity 1 200)))
|
||||||
fade-out-animation (fn []
|
fade-out-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(when-not force-show-controls?
|
(when-not force-show-controls?
|
||||||
(helpers/animate-linear
|
(helpers/animate-linear
|
||||||
translate-y
|
translate-y
|
||||||
(if @reviewing-audio? 76 20)
|
(if reviewing-audio? 76 20)
|
||||||
200))
|
200))
|
||||||
(when-not @reviewing-audio?
|
(when-not reviewing-audio?
|
||||||
(helpers/animate-linear opacity 0 200))
|
(helpers/animate-linear opacity 0 200))
|
||||||
(helpers/set-value connector-opacity 0)
|
(helpers/set-value connector-opacity 0)
|
||||||
(helpers/set-value width 24)
|
(helpers/set-value width 24)
|
||||||
(helpers/set-value height 12)
|
(helpers/set-value height 12)
|
||||||
(helpers/set-value border-radius-first-half 8)
|
(helpers/set-value border-radius-first-half 8)
|
||||||
(helpers/set-value border-radius-second-half 16))
|
(helpers/set-value border-radius-second-half 16))
|
||||||
fade-out-reset-animation (fn []
|
[reviewing-audio? force-show-controls?])
|
||||||
|
fade-out-reset-animation (rn/use-callback
|
||||||
|
(fn []
|
||||||
(helpers/animate-linear opacity 0 200)
|
(helpers/animate-linear opacity 0 200)
|
||||||
(helpers/animate-linear-with-delay translate-y 20 0 200)
|
(helpers/animate-linear-with-delay translate-y 20 0 200)
|
||||||
(helpers/set-value connector-opacity 0)
|
(helpers/set-value connector-opacity 0)
|
||||||
(helpers/set-value width 24)
|
(helpers/set-value width 24)
|
||||||
(helpers/set-value height 12)
|
(helpers/set-value height 12)
|
||||||
(helpers/set-value border-radius-first-half 8)
|
(helpers/set-value border-radius-first-half 8)
|
||||||
(helpers/set-value border-radius-second-half 16))]
|
(helpers/set-value border-radius-second-half 16)))]
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(if @recording?
|
(if recording?
|
||||||
(fade-in-animation)
|
(fade-in-animation)
|
||||||
(fade-out-animation)))
|
(fade-out-animation)))
|
||||||
[@recording?])
|
[recording?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(when-not @reviewing-audio?
|
(when-not reviewing-audio?
|
||||||
(fade-out-reset-animation)))
|
(fade-out-reset-animation)))
|
||||||
[@reviewing-audio?])
|
[reviewing-audio?])
|
||||||
(use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
(cond
|
(cond
|
||||||
@ready-to-send?
|
ready-to-send?
|
||||||
(start-y-animation)
|
(start-y-animation)
|
||||||
@recording? (reset-y-animation)))
|
recording? (reset-y-animation)))
|
||||||
[@ready-to-send?])
|
[ready-to-send?])
|
||||||
[:<>
|
[:<>
|
||||||
[reanimated/view {:style (style/send-button-container opacity)}
|
[reanimated/view {:style (style/send-button-container opacity)}
|
||||||
[reanimated/view
|
[reanimated/view
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
(:require
|
(:require
|
||||||
[quo.components.record-audio.record-audio.view :as record-audio]
|
[quo.components.record-audio.record-audio.view :as record-audio]
|
||||||
[react-native.audio-toolkit :as audio]
|
[react-native.audio-toolkit :as audio]
|
||||||
[reagent.core :as reagent]
|
|
||||||
[test-helpers.component :as h]
|
[test-helpers.component :as h]
|
||||||
[utils.datetime :as datetime]))
|
[utils.datetime :as datetime]))
|
||||||
|
|
||||||
|
@ -11,8 +10,10 @@
|
||||||
|
|
||||||
(h/test "renders record-audio"
|
(h/test "renders record-audio"
|
||||||
(h/render [record-audio/record-audio])
|
(h/render [record-audio/record-audio])
|
||||||
|
(-> (h/wait-for #(h/get-by-test-id "record-audio"))
|
||||||
|
(.then (fn []
|
||||||
(-> (h/expect (h/get-by-test-id "record-audio"))
|
(-> (h/expect (h/get-by-test-id "record-audio"))
|
||||||
(.toBeTruthy)))
|
(.toBeTruthy))))))
|
||||||
|
|
||||||
(h/test "record-audio on-start-recording works"
|
(h/test "record-audio on-start-recording works"
|
||||||
(let [event (js/jest.fn)]
|
(let [event (js/jest.fn)]
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
|
|
||||||
(h/test "record-audio on-reviewing-audio works"
|
(h/test "record-audio on-reviewing-audio works"
|
||||||
(let [event (js/jest.fn)
|
(let [event (js/jest.fn)
|
||||||
on-meter (reagent/atom nil)]
|
on-meter (atom nil)]
|
||||||
(h/render [record-audio/record-audio
|
(h/render [record-audio/record-audio
|
||||||
{:on-reviewing-audio event
|
{:on-reviewing-audio event
|
||||||
:record-audio-permission-granted true}])
|
:record-audio-permission-granted true}])
|
||||||
|
@ -39,7 +40,7 @@
|
||||||
(reset! on-meter on-meter-fn))
|
(reset! on-meter on-meter-fn))
|
||||||
audio/start-recording (fn [_ on-start _]
|
audio/start-recording (fn [_ on-start _]
|
||||||
(on-start)
|
(on-start)
|
||||||
(js/setInterval #(@on-meter) 100))
|
(js/setInterval @on-meter 100))
|
||||||
audio/get-recorder-file-path (fn [] "file-path")]
|
audio/get-recorder-file-path (fn [] "file-path")]
|
||||||
(h/fire-event
|
(h/fire-event
|
||||||
:on-start-should-set-responder
|
:on-start-should-set-responder
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
:locationY 70
|
:locationY 70
|
||||||
:timestamp 0
|
:timestamp 0
|
||||||
:identifier 0}})
|
:identifier 0}})
|
||||||
|
|
||||||
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
|
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
|
||||||
(h/advance-timers-by-time 100)
|
(h/advance-timers-by-time 100)
|
||||||
(h/fire-event
|
(h/fire-event
|
||||||
|
@ -63,7 +65,7 @@
|
||||||
|
|
||||||
(h/test "record-audio on-send works after reviewing audio"
|
(h/test "record-audio on-send works after reviewing audio"
|
||||||
(let [event (js/jest.fn)
|
(let [event (js/jest.fn)
|
||||||
on-meter (reagent/atom nil)]
|
on-meter (atom nil)]
|
||||||
(h/render [record-audio/record-audio
|
(h/render [record-audio/record-audio
|
||||||
{:on-send event
|
{:on-send event
|
||||||
:record-audio-permission-granted true}])
|
:record-audio-permission-granted true}])
|
||||||
|
@ -114,14 +116,15 @@
|
||||||
|
|
||||||
(h/test "record-audio on-send works after sliding to the send button"
|
(h/test "record-audio on-send works after sliding to the send button"
|
||||||
(let [event (js/jest.fn)
|
(let [event (js/jest.fn)
|
||||||
on-meter (reagent/atom nil)
|
on-meter (atom nil)
|
||||||
last-now-ms (atom nil)
|
last-now-ms (atom nil)
|
||||||
duration-ms (atom nil)]
|
duration-ms (atom nil)]
|
||||||
(h/render [record-audio/record-audio
|
(h/render [record-audio/record-audio
|
||||||
{:on-send event
|
{:on-send event
|
||||||
:record-audio-permission-granted true}])
|
:record-audio-permission-granted true}])
|
||||||
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
|
(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 _]
|
audio/start-recording (fn [_ on-start _]
|
||||||
(on-start)
|
(on-start)
|
||||||
(js/setInterval #(@on-meter) 100))
|
(js/setInterval #(@on-meter) 100))
|
||||||
|
@ -170,15 +173,16 @@
|
||||||
|
|
||||||
(h/test "record-audio on-cancel works after reviewing audio"
|
(h/test "record-audio on-cancel works after reviewing audio"
|
||||||
(let [event (js/jest.fn)
|
(let [event (js/jest.fn)
|
||||||
on-meter (reagent/atom nil)]
|
on-meter (atom nil)]
|
||||||
(h/render [record-audio/record-audio
|
(h/render [record-audio/record-audio
|
||||||
{:on-cancel event
|
{:on-cancel event
|
||||||
:record-audio-permission-granted true}])
|
:record-audio-permission-granted true}])
|
||||||
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
|
(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 _]
|
audio/start-recording (fn [_ on-start _]
|
||||||
(on-start)
|
(on-start)
|
||||||
(js/setInterval #(@on-meter) 100))]
|
(js/setInterval @on-meter 100))]
|
||||||
(h/fire-event
|
(h/fire-event
|
||||||
:on-start-should-set-responder
|
:on-start-should-set-responder
|
||||||
(h/get-by-test-id "record-audio")
|
(h/get-by-test-id "record-audio")
|
||||||
|
@ -195,6 +199,7 @@
|
||||||
:locationY 70
|
:locationY 70
|
||||||
:timestamp 200
|
:timestamp 200
|
||||||
:identifier 0}})
|
:identifier 0}})
|
||||||
|
(h/advance-timers-by-time 250)
|
||||||
(h/fire-event
|
(h/fire-event
|
||||||
:on-responder-release
|
:on-responder-release
|
||||||
(h/get-by-test-id "record-audio")
|
(h/get-by-test-id "record-audio")
|
||||||
|
@ -202,13 +207,12 @@
|
||||||
:locationY 80
|
:locationY 80
|
||||||
:timestamp 200
|
:timestamp 200
|
||||||
:identifier 0}})
|
:identifier 0}})
|
||||||
(h/advance-timers-by-time 250)
|
|
||||||
(-> (js/expect event)
|
(-> (js/expect event)
|
||||||
(.toHaveBeenCalledTimes 1))))))
|
(.toHaveBeenCalledTimes 1))))))
|
||||||
|
|
||||||
(h/test "record-audio on-cancel works after sliding to the cancel button"
|
(h/test "record-audio on-cancel works after sliding to the cancel button"
|
||||||
(let [event (js/jest.fn)
|
(let [event (js/jest.fn)
|
||||||
on-meter (reagent/atom nil)]
|
on-meter (atom nil)]
|
||||||
(h/render [record-audio/record-audio
|
(h/render [record-audio/record-audio
|
||||||
{:on-cancel event
|
{:on-cancel event
|
||||||
:record-audio-permission-granted true}])
|
: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
|
(:require
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[goog.string :as gstring]
|
[goog.string :as gstring]
|
||||||
[oops.core :as oops]
|
|
||||||
[quo.components.icon :as icons]
|
[quo.components.icon :as icons]
|
||||||
[quo.components.markdown.text :as text]
|
[quo.components.markdown.text :as text]
|
||||||
[quo.components.record-audio.record-audio.buttons.delete-button :as delete-button]
|
[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 :as record-button]
|
||||||
[quo.components.record-audio.record-audio.buttons.record-button-big :as record-button-big]
|
[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.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.record-audio.style :as style]
|
||||||
[quo.components.record-audio.soundtrack.view :as soundtrack]
|
[quo.components.record-audio.soundtrack.view :as soundtrack]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[quo.theme :as quo.theme]
|
|
||||||
[react-native.audio-toolkit :as audio]
|
[react-native.audio-toolkit :as audio]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
|
||||||
[reagent.core :as reagent]
|
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[utils.datetime :as datetime]))
|
[utils.datetime :as datetime]))
|
||||||
|
|
||||||
(def ^:private min-audio-duration-ms 1000)
|
(defn- recording-bar
|
||||||
(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
|
|
||||||
[recording-length-ms ready-to-delete?]
|
[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-container)}
|
||||||
[rn/view {:style (style/recording-bar fill-percentage ready-to-delete?)}]]))
|
[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]
|
[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)
|
(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))]
|
time-str (gstring/format "%02d:%02d" (quot s 60) (mod s 60))]
|
||||||
|
@ -111,10 +40,12 @@
|
||||||
{:style (style/timer-text)}))
|
{:style (style/timer-text)}))
|
||||||
time-str]]))
|
time-str]]))
|
||||||
|
|
||||||
(defn- f-play-button
|
(defn- play-button
|
||||||
[playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio? max-duration-ms]
|
[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 []
|
(let [on-play (fn []
|
||||||
(reset! playing-audio? true)
|
(set-playing-audio true)
|
||||||
(reset! playing-timer
|
(reset! playing-timer
|
||||||
(js/setInterval
|
(js/setInterval
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -122,24 +53,24 @@
|
||||||
player-state (audio/get-state @player-ref)
|
player-state (audio/get-state @player-ref)
|
||||||
playing? (= player-state audio/PLAYING)]
|
playing? (= player-state audio/PLAYING)]
|
||||||
(when (and playing?
|
(when (and playing?
|
||||||
(not @seeking-audio?)
|
(not seeking-audio?)
|
||||||
(> current-time 0)
|
(> current-time 0)
|
||||||
(< current-time max-duration-ms))
|
(< 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)
|
(when (>= current-time max-duration-ms)
|
||||||
(audio/stop-playing
|
(audio/stop-playing
|
||||||
@player-ref
|
@player-ref
|
||||||
(fn []
|
(fn []
|
||||||
(reset! playing-audio? false)
|
(set-playing-audio false)
|
||||||
(when @playing-timer
|
(when @playing-timer
|
||||||
(js/clearInterval @playing-timer)
|
(js/clearInterval @playing-timer)
|
||||||
(reset! playing-timer nil)
|
(reset! playing-timer nil)
|
||||||
(reset! audio-current-time-ms 0)
|
(set-audio-current-time-ms 0)
|
||||||
(reset! seeking-audio? false)))
|
(set-seeking-audio false)))
|
||||||
#(log/error "[record-audio] stop player - error: " %)))))
|
#(log/error "[record-audio] stop player - error: " %)))))
|
||||||
100)))
|
100)))
|
||||||
on-pause (fn []
|
on-pause (fn []
|
||||||
(reset! playing-audio? false)
|
(set-playing-audio false)
|
||||||
(when @playing-timer
|
(when @playing-timer
|
||||||
(js/clearInterval @playing-timer)
|
(js/clearInterval @playing-timer)
|
||||||
(reset! playing-timer nil))
|
(reset! playing-timer nil))
|
||||||
|
@ -154,130 +85,111 @@
|
||||||
{:style (style/play-button)
|
{:style (style/play-button)
|
||||||
:on-press on-press}
|
:on-press on-press}
|
||||||
[icons/icon
|
[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)}]]))
|
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]))
|
||||||
|
|
||||||
(defn- record-audio-internal
|
(defn record-audio
|
||||||
[{:keys [on-init on-start-recording on-send on-cancel on-reviewing-audio
|
[{:keys [on-init on-start-recording on-send on-cancel on-reviewing-audio audio-file on-lock
|
||||||
record-audio-permission-granted
|
max-duration-ms on-check-audio-permissions record-audio-permission-granted
|
||||||
on-request-record-audio-permission on-check-audio-permissions
|
on-request-record-audio-permission]}]
|
||||||
audio-file on-lock max-duration-ms theme]}]
|
(let [;;STATE
|
||||||
[:f>
|
[recording? set-recording] (rn/use-state false)
|
||||||
;; TODO we need to refactor this, and use :f> with defined function, currenly state is reseted each
|
[locked? set-locked] (rn/use-state false)
|
||||||
;; time parent component
|
[ready-to-send? set-ready-to-send] (rn/use-state false)
|
||||||
;; is re-rendered
|
[ready-to-lock? set-ready-to-lock] (rn/use-state false)
|
||||||
(fn []
|
[ready-to-delete?
|
||||||
(let [recording? (reagent/atom false)
|
set-ready-to-delete] (rn/use-state false)
|
||||||
locked? (reagent/atom false)
|
[reviewing-audio?
|
||||||
ready-to-send? (reagent/atom false)
|
set-reviewing-audio] (rn/use-state (some? audio-file))
|
||||||
ready-to-lock? (reagent/atom false)
|
[playing-audio?
|
||||||
ready-to-delete? (reagent/atom false)
|
set-playing-audio] (rn/use-state false)
|
||||||
reviewing-audio? (reagent/atom (some? audio-file))
|
[recording-length-ms
|
||||||
playing-audio? (reagent/atom false)
|
set-recording-length-ms] (rn/use-state 0)
|
||||||
recording-length-ms (reagent/atom 0)
|
[audio-current-time-ms
|
||||||
audio-current-time-ms (reagent/atom 0)
|
set-audio-current-time-ms] (rn/use-state 0)
|
||||||
seeking-audio? (reagent/atom false)
|
[seeking-audio? set-seeking-audio] (rn/use-state false)
|
||||||
force-show-controls? (reagent/atom (some? audio-file))
|
[force-show-controls?
|
||||||
recording-start-ms (atom (datetime/timestamp))
|
set-force-show-controls] (rn/use-state (some? audio-file))
|
||||||
clear-timeout (atom nil)
|
;;ATOMS
|
||||||
record-button-at-initial-position? (atom true)
|
recording-start-ms (rn/use-ref-atom (datetime/timestamp))
|
||||||
record-button-is-animating? (atom false)
|
clear-timeout (rn/use-ref-atom nil)
|
||||||
idle? (atom false)
|
record-button-at-initial-position? (rn/use-ref-atom true)
|
||||||
recorder-ref (atom nil)
|
record-button-is-animating? (rn/use-ref-atom false)
|
||||||
player-ref (atom nil)
|
idle? (rn/use-ref-atom false)
|
||||||
touch-active? (atom false)
|
recorder-ref (rn/use-ref-atom nil)
|
||||||
playing-timer (atom nil)
|
player-ref (rn/use-ref-atom nil)
|
||||||
output-file (atom audio-file)
|
touch-active? (rn/use-ref-atom false)
|
||||||
reached-max-duration? (atom false)
|
playing-timer (rn/use-ref-atom nil)
|
||||||
touch-timestamp (atom nil)
|
output-file (rn/use-ref-atom audio-file)
|
||||||
touch-identifier (atom nil)
|
reached-max-duration? (rn/use-ref-atom false)
|
||||||
disabled? (atom false)
|
touch-timestamp (rn/use-ref-atom nil)
|
||||||
app-state-listener (atom nil)
|
touch-identifier (rn/use-ref-atom nil)
|
||||||
rec-options
|
disabled? (rn/use-ref-atom false)
|
||||||
(merge
|
app-state-listener (rn/use-ref-atom nil)
|
||||||
audio/default-recorder-options
|
;;HANDLERS
|
||||||
{:filename (str base-filename (datetime/timestamp) default-format)
|
destroy-player (rn/use-callback
|
||||||
:meteringInterval metering-interval})
|
|
||||||
destroy-player
|
|
||||||
(fn []
|
(fn []
|
||||||
(audio/destroy-player @player-ref)
|
(audio/destroy-player @player-ref)
|
||||||
(reset! player-ref nil))
|
(reset! player-ref nil)))
|
||||||
reload-player
|
reload-player (handlers/get-reload-player
|
||||||
(fn [audio-file]
|
[player-ref destroy-player set-playing-audio playing-timer
|
||||||
(when @player-ref
|
set-audio-current-time-ms set-seeking-audio])
|
||||||
(destroy-player))
|
recorder-on-meter (handlers/get-recorder-on-meter
|
||||||
(reset! player-ref
|
[recording-start-ms set-recording-length-ms
|
||||||
(audio/new-player
|
reached-max-duration?
|
||||||
(or audio-file (:filename rec-options))
|
locked? set-locked set-reviewing-audio idle? set-recording
|
||||||
{:autoDestroy false
|
set-ready-to-lock set-ready-to-send set-ready-to-delete
|
||||||
:continuesToPlayInBackground false
|
recorder-ref
|
||||||
:category audio/PLAYBACK}
|
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?])]
|
||||||
|
|
||||||
|
;;ON MOUNT
|
||||||
|
(rn/use-mount
|
||||||
(fn []
|
(fn []
|
||||||
(reset! playing-audio? false)
|
(when on-check-audio-permissions (on-check-audio-permissions))
|
||||||
(when @playing-timer
|
(when on-init
|
||||||
(js/clearInterval @playing-timer)
|
(on-init
|
||||||
(reset! playing-timer nil)
|
(fn reset-recorder []
|
||||||
(reset! audio-current-time-ms 0)
|
(set-recording false)
|
||||||
(reset! seeking-audio? false)))))
|
(set-reviewing-audio false)
|
||||||
(audio/prepare-player
|
(set-locked false)
|
||||||
@player-ref
|
(set-ready-to-send false)
|
||||||
#(log/debug "[record-audio] prepare player - success")
|
(set-ready-to-lock false)
|
||||||
#(log/error "[record-audio] prepare player - error: " %)))
|
(set-ready-to-delete false)
|
||||||
destroy-recorder
|
(set-audio-current-time-ms 0)
|
||||||
(fn []
|
(set-recording-length-ms 0)
|
||||||
(audio/destroy-recorder @recorder-ref)
|
(set-seeking-audio false)
|
||||||
(reset! recorder-ref nil))
|
(set-playing-audio false)
|
||||||
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! touch-active? false)
|
||||||
(reset! reached-max-duration? false)
|
(reset! reached-max-duration? false)
|
||||||
(reset! output-file nil)
|
(reset! output-file nil)
|
||||||
|
@ -290,249 +202,7 @@
|
||||||
(when @playing-timer
|
(when @playing-timer
|
||||||
(js/clearInterval @playing-timer)
|
(js/clearInterval @playing-timer)
|
||||||
(reset! playing-timer nil))
|
(reset! playing-timer nil))
|
||||||
(reload-recorder))
|
(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
|
(when audio-file
|
||||||
(let [filename (last (string/split audio-file "/"))]
|
(let [filename (last (string/split audio-file "/"))]
|
||||||
(reload-player filename)))
|
(reload-player filename)))
|
||||||
|
@ -540,59 +210,61 @@
|
||||||
(.addEventListener rn/app-state
|
(.addEventListener rn/app-state
|
||||||
"change"
|
"change"
|
||||||
#(when (= % "background")
|
#(when (= % "background")
|
||||||
(reset! playing-audio? false))))
|
(set-playing-audio false))))))
|
||||||
#(.remove @app-state-listener)))
|
;;ON UNMOUNT
|
||||||
|
(rn/use-unmount #(.remove @app-state-listener))
|
||||||
|
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style style/bar-container
|
{:style style/bar-container
|
||||||
:pointer-events :box-none}
|
:pointer-events :box-none}
|
||||||
(when @reviewing-audio?
|
(when reviewing-audio?
|
||||||
[:<>
|
[:<>
|
||||||
[:f> f-play-button playing-audio? player-ref playing-timer audio-current-time-ms
|
[play-button playing-audio? set-playing-audio player-ref playing-timer set-audio-current-time-ms
|
||||||
seeking-audio? max-duration-ms]
|
seeking-audio? set-seeking-audio max-duration-ms]
|
||||||
[:f> soundtrack/f-soundtrack
|
[soundtrack/soundtrack
|
||||||
{:audio-current-time-ms audio-current-time-ms
|
{:audio-current-time-ms audio-current-time-ms
|
||||||
|
:set-audio-current-time-ms set-audio-current-time-ms
|
||||||
:player-ref @player-ref
|
:player-ref @player-ref
|
||||||
:seeking-audio? seeking-audio?
|
:seeking-audio? seeking-audio?
|
||||||
|
:set-seeking-audio set-seeking-audio
|
||||||
:max-audio-duration-ms max-duration-ms}]])
|
:max-audio-duration-ms max-duration-ms}]])
|
||||||
(when (or @recording? @reviewing-audio?)
|
(when (or recording? reviewing-audio?)
|
||||||
[:f> f-time-counter @recording? @recording-length-ms @ready-to-delete? @reviewing-audio?
|
[time-counter recording? recording-length-ms ready-to-delete? reviewing-audio?
|
||||||
@audio-current-time-ms])
|
audio-current-time-ms])
|
||||||
(when @recording?
|
(when recording?
|
||||||
[:f> f-recording-bar @recording-length-ms @ready-to-delete?])
|
[recording-bar recording-length-ms ready-to-delete?])
|
||||||
[rn/view
|
[rn/view
|
||||||
{:test-ID "record-audio"
|
{:test-ID "record-audio"
|
||||||
:style style/button-container
|
:style style/button-container
|
||||||
:hit-slop {:top -70
|
:hit-slop {:top -70 :bottom 0 :left 0 :right 0}
|
||||||
:bottom 0
|
|
||||||
:left 0
|
|
||||||
:right 0}
|
|
||||||
:pointer-events :box-only
|
:pointer-events :box-only
|
||||||
:on-start-should-set-responder on-start-should-set-responder
|
:on-start-should-set-responder on-start-should-set-responder
|
||||||
:on-responder-move on-responder-move
|
:on-responder-move on-responder-move
|
||||||
:on-responder-release on-responder-release}
|
:on-responder-release on-responder-release}
|
||||||
[:f> delete-button/f-delete-button recording? ready-to-delete? reviewing-audio?
|
[delete-button/delete-button recording? ready-to-delete? reviewing-audio? force-show-controls?]
|
||||||
@force-show-controls?]
|
[lock-button/lock-button recording? ready-to-lock? locked?]
|
||||||
[:f> lock-button/f-lock-button recording? ready-to-lock? locked?]
|
[send-button/send-button recording? ready-to-send? reviewing-audio? force-show-controls?]
|
||||||
[:f> send-button/f-send-button recording? ready-to-send? reviewing-audio?
|
[record-button-big/record-button-big
|
||||||
@force-show-controls?]
|
|
||||||
[:f> record-button-big/f-record-button-big
|
|
||||||
{:recording? recording?
|
{:recording? recording?
|
||||||
|
:set-recording set-recording
|
||||||
:ready-to-send? ready-to-send?
|
:ready-to-send? ready-to-send?
|
||||||
|
:set-ready-to-send set-ready-to-send
|
||||||
:ready-to-lock? ready-to-lock?
|
:ready-to-lock? ready-to-lock?
|
||||||
|
:set-ready-to-lock set-ready-to-lock
|
||||||
:ready-to-delete? ready-to-delete?
|
:ready-to-delete? ready-to-delete?
|
||||||
|
:set-ready-to-delete set-ready-to-delete
|
||||||
:record-button-is-animating? record-button-is-animating?
|
:record-button-is-animating? record-button-is-animating?
|
||||||
:record-button-at-initial-position? record-button-at-initial-position?
|
:record-button-at-initial-position? record-button-at-initial-position?
|
||||||
:locked? locked?
|
:locked? locked?
|
||||||
|
:set-locked set-locked
|
||||||
:reviewing-audio? reviewing-audio?
|
:reviewing-audio? reviewing-audio?
|
||||||
:recording-length-ms recording-length-ms
|
:recording-length-ms recording-length-ms
|
||||||
|
:set-recording-length-ms set-recording-length-ms
|
||||||
:clear-timeout clear-timeout
|
:clear-timeout clear-timeout
|
||||||
:touch-active? touch-active?
|
:touch-active? touch-active?
|
||||||
:recorder-ref recorder-ref
|
:recorder-ref recorder-ref
|
||||||
:reload-recorder-fn reload-recorder
|
:reload-recorder-fn reload-recorder
|
||||||
:idle? idle?
|
:idle? idle?
|
||||||
:on-send on-send
|
:on-send on-send
|
||||||
:on-cancel on-cancel
|
:on-cancel on-cancel}]
|
||||||
:theme theme}]
|
[record-button/record-button recording? reviewing-audio?]]]))
|
||||||
[:f> record-button/f-record-button recording? reviewing-audio?]]])))])
|
|
||||||
|
|
||||||
(def record-audio (quo.theme/with-theme record-audio-internal))
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
(:require
|
(:require
|
||||||
[quo.components.record-audio.soundtrack.view :as soundtrack]
|
[quo.components.record-audio.soundtrack.view :as soundtrack]
|
||||||
[react-native.audio-toolkit :as audio]
|
[react-native.audio-toolkit :as audio]
|
||||||
[reagent.core :as reagent]
|
|
||||||
[test-helpers.component :as h]))
|
[test-helpers.component :as h]))
|
||||||
|
|
||||||
(h/describe "soundtrack component"
|
(h/describe "soundtrack component"
|
||||||
|
@ -10,23 +9,21 @@
|
||||||
|
|
||||||
(h/test "renders soundtrack"
|
(h/test "renders soundtrack"
|
||||||
(with-redefs [audio/get-player-duration (fn [] 2000)]
|
(with-redefs [audio/get-player-duration (fn [] 2000)]
|
||||||
(let [player-ref (reagent/atom {})
|
(h/render [soundtrack/soundtrack
|
||||||
audio-current-time-ms (reagent/atom 0)]
|
{:player-ref {}
|
||||||
(h/render [:f> soundtrack/f-soundtrack
|
:audio-current-time-ms 0}])
|
||||||
{:player-ref @player-ref
|
|
||||||
:audio-current-time-ms audio-current-time-ms}])
|
|
||||||
(-> (h/expect (h/get-by-test-id "soundtrack"))
|
(-> (h/expect (h/get-by-test-id "soundtrack"))
|
||||||
(.toBeTruthy)))))
|
(.toBeTruthy))))
|
||||||
|
|
||||||
(h/test "soundtrack on-sliding-start works"
|
(h/test "soundtrack on-sliding-start works"
|
||||||
(with-redefs [audio/get-player-duration (fn [] 2000)]
|
(with-redefs [audio/get-player-duration (fn [] 2000)]
|
||||||
(let [seeking-audio? (reagent/atom false)
|
(let [seeking-audio? (atom false)]
|
||||||
player-ref (reagent/atom {})
|
(h/render [soundtrack/soundtrack
|
||||||
audio-current-time-ms (reagent/atom 0)]
|
|
||||||
(h/render [:f> soundtrack/f-soundtrack
|
|
||||||
{:seeking-audio? seeking-audio?
|
{:seeking-audio? seeking-audio?
|
||||||
:player-ref @player-ref
|
:set-seeking-audio #(reset! seeking-audio? %)
|
||||||
:audio-current-time-ms audio-current-time-ms}])
|
:player-ref {}
|
||||||
|
:audio-current-time-ms 0
|
||||||
|
:set-audio-current-time-ms #()}])
|
||||||
(h/fire-event
|
(h/fire-event
|
||||||
:on-sliding-start
|
:on-sliding-start
|
||||||
(h/get-by-test-id "soundtrack"))
|
(h/get-by-test-id "soundtrack"))
|
||||||
|
@ -36,13 +33,13 @@
|
||||||
(h/test "soundtrack on-sliding-complete works"
|
(h/test "soundtrack on-sliding-complete works"
|
||||||
(with-redefs [audio/get-player-duration (fn [] 2000)
|
(with-redefs [audio/get-player-duration (fn [] 2000)
|
||||||
audio/seek-player (js/jest.fn)]
|
audio/seek-player (js/jest.fn)]
|
||||||
(let [seeking-audio? (reagent/atom false)
|
(let [seeking-audio? (atom false)]
|
||||||
player-ref (reagent/atom {})
|
(h/render [soundtrack/soundtrack
|
||||||
audio-current-time-ms (reagent/atom 0)]
|
|
||||||
(h/render [:f> soundtrack/f-soundtrack
|
|
||||||
{:seeking-audio? seeking-audio?
|
{:seeking-audio? seeking-audio?
|
||||||
:player-ref @player-ref
|
:set-seeking-audio #(reset! seeking-audio? %)
|
||||||
:audio-current-time-ms audio-current-time-ms}])
|
:player-ref {}
|
||||||
|
:audio-current-time-ms 0
|
||||||
|
:set-audio-current-time-ms #()}])
|
||||||
(h/fire-event
|
(h/fire-event
|
||||||
:on-sliding-start
|
:on-sliding-start
|
||||||
(h/get-by-test-id "soundtrack"))
|
(h/get-by-test-id "soundtrack"))
|
||||||
|
@ -58,13 +55,14 @@
|
||||||
(h/test "soundtrack on-value-change when seeking audio works"
|
(h/test "soundtrack on-value-change when seeking audio works"
|
||||||
(with-redefs [audio/get-player-duration (fn [] 2000)
|
(with-redefs [audio/get-player-duration (fn [] 2000)
|
||||||
audio/seek-player (js/jest.fn)]
|
audio/seek-player (js/jest.fn)]
|
||||||
(let [seeking-audio? (reagent/atom false)
|
(let [seeking-audio? (atom false)
|
||||||
player-ref (reagent/atom {})
|
audio-current-time-ms (atom 0)]
|
||||||
audio-current-time-ms (reagent/atom 0)]
|
(h/render [soundtrack/soundtrack
|
||||||
(h/render [:f> soundtrack/f-soundtrack
|
|
||||||
{:seeking-audio? seeking-audio?
|
{:seeking-audio? seeking-audio?
|
||||||
:player-ref @player-ref
|
:set-seeking-audio #(reset! seeking-audio? %)
|
||||||
:audio-current-time-ms audio-current-time-ms}])
|
:player-ref {}
|
||||||
|
:audio-current-time-ms audio-current-time-ms
|
||||||
|
:set-audio-current-time-ms #(reset! audio-current-time-ms %)}])
|
||||||
(h/fire-event
|
(h/fire-event
|
||||||
:on-sliding-start
|
:on-sliding-start
|
||||||
(h/get-by-test-id "soundtrack"))
|
(h/get-by-test-id "soundtrack"))
|
||||||
|
|
|
@ -1,42 +1,47 @@
|
||||||
(ns quo.components.record-audio.soundtrack.view
|
(ns quo.components.record-audio.soundtrack.view
|
||||||
(:require
|
(:require
|
||||||
|
[quo.components.icons.icons :as icons]
|
||||||
[quo.components.record-audio.soundtrack.style :as style]
|
[quo.components.record-audio.soundtrack.style :as style]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[quo.theme :as quo.theme]
|
[quo.theme :as quo.theme]
|
||||||
[react-native.audio-toolkit :as audio]
|
[react-native.audio-toolkit :as audio]
|
||||||
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
[react-native.platform :as platform]
|
||||||
[react-native.slider :as slider]
|
[react-native.slider :as slider]
|
||||||
[taoensso.timbre :as log]))
|
[taoensso.timbre :as log]))
|
||||||
|
|
||||||
(def ^:private thumb-light (js/require "../resources/images/icons2/12x12/thumb-light.png"))
|
(def ^:private thumb-light (icons/icon-source :thumb-light12))
|
||||||
(def ^:private thumb-dark (js/require "../resources/images/icons2/12x12/thumb-dark.png"))
|
(def ^:private thumb-dark (icons/icon-source :thumb-dark12))
|
||||||
|
|
||||||
(defn f-soundtrack
|
(defn soundtrack
|
||||||
[{:keys [audio-current-time-ms player-ref style seeking-audio? max-audio-duration-ms]}]
|
[{: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))
|
(let [audio-duration-ms (min max-audio-duration-ms (audio/get-player-duration player-ref))
|
||||||
theme (quo.theme/use-theme-value)]
|
theme (quo.theme/use-theme-value)
|
||||||
[:<>
|
on-sliding-start (rn/use-callback #(set-seeking-audio true))
|
||||||
[slider/slider
|
on-sliding-complete (rn/use-callback
|
||||||
{:test-ID "soundtrack"
|
(fn [seek-time]
|
||||||
:style (merge
|
(set-seeking-audio false)
|
||||||
(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
|
(audio/seek-player
|
||||||
player-ref
|
player-ref
|
||||||
seek-time
|
seek-time
|
||||||
#(log/debug "[record-audio] on seek - seek time: " seek-time)
|
#(log/debug "[record-audio] on seek - seek time: " seek-time)
|
||||||
#(log/error "[record-audio] on seek - error: " %)))
|
#(log/error "[record-audio] on seek - error: " %)))
|
||||||
:on-value-change #(when @seeking-audio?
|
[player-ref])
|
||||||
(reset! audio-current-time-ms %))
|
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)
|
: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)
|
:minimum-track-tint-color (colors/theme-colors colors/primary-50 colors/primary-60 theme)
|
||||||
:maximum-track-tint-color (colors/theme-colors
|
:maximum-track-tint-color (colors/theme-colors
|
||||||
(if platform/ios? colors/neutral-20 colors/neutral-40)
|
(if platform/ios? colors/neutral-20 colors/neutral-40)
|
||||||
(if platform/ios? colors/neutral-80 colors/neutral-60)
|
(if platform/ios? colors/neutral-80 colors/neutral-60)
|
||||||
theme)}]]))
|
theme)}]))
|
||||||
|
|
|
@ -357,7 +357,7 @@
|
||||||
|
|
||||||
;;;; Record Audio
|
;;;; Record Audio
|
||||||
(def record-audio quo.components.record-audio.record-audio.view/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
|
;;;; Selectors
|
||||||
(def disclaimer quo.components.selectors.disclaimer.view/view)
|
(def disclaimer quo.components.selectors.disclaimer.view/view)
|
||||||
|
|
|
@ -38,7 +38,8 @@
|
||||||
(when on-meter
|
(when on-meter
|
||||||
(.on ^js recorder "meter" on-meter))
|
(.on ^js recorder "meter" on-meter))
|
||||||
(when on-ended
|
(when on-ended
|
||||||
(.on ^js recorder "ended" on-ended))))
|
(.on ^js recorder "ended" on-ended))
|
||||||
|
recorder))
|
||||||
|
|
||||||
(defn new-player
|
(defn new-player
|
||||||
[audio options on-ended]
|
[audio options on-ended]
|
||||||
|
@ -46,7 +47,8 @@
|
||||||
audio
|
audio
|
||||||
(clj->js options))]
|
(clj->js options))]
|
||||||
(when on-ended
|
(when on-ended
|
||||||
(.on ^js player "ended" on-ended))))
|
(.on ^js player "ended" on-ended))
|
||||||
|
player))
|
||||||
|
|
||||||
(defn prepare-player
|
(defn prepare-player
|
||||||
[player on-prepared on-error]
|
[player on-prepared on-error]
|
||||||
|
@ -157,20 +159,16 @@
|
||||||
(on-error {:error (.-err err) :message (.-message err)})
|
(on-error {:error (.-err err) :message (.-message err)})
|
||||||
(on-seek))))))
|
(on-seek))))))
|
||||||
|
|
||||||
(defn can-play?
|
|
||||||
[player]
|
|
||||||
(and player (.-canPlay ^js player)))
|
|
||||||
|
|
||||||
(defn destroy-recorder
|
(defn destroy-recorder
|
||||||
[recorder]
|
[recorder]
|
||||||
(stop-recording 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))
|
(.destroy ^js recorder))
|
||||||
#()))
|
#()))
|
||||||
|
|
||||||
(defn destroy-player
|
(defn destroy-player
|
||||||
[player]
|
[player]
|
||||||
(stop-playing 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))
|
(.destroy ^js player))
|
||||||
#()))
|
#()))
|
||||||
|
|
|
@ -210,11 +210,13 @@
|
||||||
:else :i/play-audio)
|
:else :i/play-audio)
|
||||||
{:size 20
|
{:size 20
|
||||||
:color colors/white}]]
|
:color colors/white}]]
|
||||||
[:f> quo/soundtrack
|
[quo/soundtrack
|
||||||
{:style style/slider-container
|
{:style style/slider-container
|
||||||
:audio-current-time-ms progress
|
:audio-current-time-ms @progress
|
||||||
|
:set-audio-current-time-ms #(reset! progress %)
|
||||||
:player-ref (@active-players player-key)
|
:player-ref (@active-players player-key)
|
||||||
:seeking-audio? seeking-audio?
|
:seeking-audio? @seeking-audio?
|
||||||
|
:set-seeking-audio? #(reset! seeking-audio? %)
|
||||||
:max-audio-duration-ms constants/audio-max-duration-ms}]
|
:max-audio-duration-ms constants/audio-max-duration-ms}]
|
||||||
[quo/text
|
[quo/text
|
||||||
{:style style/timestamp
|
{:style style/timestamp
|
||||||
|
|
Loading…
Reference in New Issue