Skip to content

Commit 97d5aec

Browse files
fix seeking audio, reproduce from a seek point and timer fix
1 parent 7a0bdd5 commit 97d5aec

File tree

2 files changed

+116
-89
lines changed

2 files changed

+116
-89
lines changed
+115-85
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
11
(ns status-im.ui.screens.chat.message.audio
2-
(:require-macros [status-im.utils.views :refer [defview letsubs]])
32
(:require ["react-native-blob-util" :default ReactNativeBlobUtil]
43
[goog.string :as gstring]
54
[reagent.core :as reagent]
65
[react-native.audio-toolkit :as audio]
7-
[status-im.ui.components.animation :as anim]
8-
[status-im.ui.components.icons.icons :as icons]
96
[status-im.ui.components.react :as react]
10-
[status-im.ui.components.slider :as slider]
117
[status-im.ui.screens.chat.styles.message.audio :as style]
128
[status-im.utils.platform :as platform]
13-
[status-im.utils.utils :as utils]
149
[taoensso.timbre :as log]
1510
[quo2.foundations.colors :as colors]
1611
[quo2.core :as quo]
17-
[react-native.core :as rn]))
12+
[react-native.core :as rn]
13+
[utils.re-frame :as rf]))
1814

19-
(defonce player-ref (atom nil))
2015
(defonce current-player-message-id (atom nil))
2116
(defonce current-active-state-ref-ref (atom nil))
2217
(defonce progress-timer (atom nil))
2318

2419
(defn update-state
25-
[{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms
20+
[player-ref
21+
{:keys [state-ref progress-ref message-id seek-to-ms audio-duration-ms
2622
slider-new-state-seeking? unloaded? error]}]
2723
(let [player-state (audio/get-state @player-ref)
2824
slider-seeking (if (some? slider-new-state-seeking?)
@@ -55,7 +51,6 @@
5551

5652
:else (:duration @state-ref))
5753
:progress-ref (or progress-ref (:progress-ref @state-ref))
58-
:progress-anim (or progress-anim (:progress-anim @state-ref))
5954
:slider-seeking slider-seeking
6055

6156
; persist seek-to-ms while seeking or audio is not loaded
@@ -72,21 +67,21 @@
7267
(reset! (:progress-ref new-state) seek-to-ms))))
7368

7469
(defn destroy-player
75-
[{:keys [message-id reloading?]}]
70+
[player-ref {:keys [message-id reloading?]}]
7671
(when (and @player-ref
7772
(or reloading?
7873
(= message-id @current-player-message-id)))
7974
(audio/destroy-player @player-ref)
8075
(reset! player-ref nil)
8176
(when @current-active-state-ref-ref
82-
(update-state {:state-ref @current-active-state-ref-ref :unloaded? true}))
77+
(update-state player-ref {:state-ref @current-active-state-ref-ref :unloaded? true}))
8378
(reset! current-player-message-id nil)
8479
(reset! current-active-state-ref-ref nil)))
8580

8681
(defonce last-seek (atom (js/Date.now)))
8782

8883
(defn seek
89-
[{:keys [message-id] :as params} value immediate? on-success]
84+
[player-ref {:keys [message-id] :as params} value immediate? on-success]
9085
(when (and @player-ref (= message-id @current-player-message-id))
9186
(let [now (js/Date.now)]
9287
(when (or immediate? (> (- now @last-seek) 200))
@@ -95,70 +90,92 @@
9590
@player-ref
9691
value
9792
#(do
98-
(update-state params)
93+
(update-state player-ref params)
9994
(when on-success (on-success)))
100-
#(update-state (merge params {:error (:message %)}))))))
101-
(update-state (merge params {:seek-to-ms value})))
95+
#(update-state player-ref (merge params {:error (:message %)}))))))
96+
(update-state player-ref (merge params {:seek-to-ms value})))
10297

10398
(defn download-audio-http
10499
[base64-uri on-success]
105-
(println base64-uri)
106100
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?}))
107101
(.fetch "GET" (str base64-uri))
108102
(.then #(on-success (.base64 ^js %)))
109-
(.catch #(println "could not fetch audio" %))))
103+
(.catch #(log/error "could not fetch audio"))))
110104

111105
(defn reload-player
112-
[{:keys [message-id state-ref] :as params} audio-url on-success]
106+
[player-ref {:keys [message-id state-ref progress-ref] :as params} audio-url on-success]
113107
;; to avoid reloading player while is initializing,
114108
;; we go ahead only if there is no player or
115109
;; if it is already prepared
116110
(when (or (nil? @player-ref) (audio/can-play? @player-ref))
117111
(when @player-ref
118-
(destroy-player (merge params {:reloading? true})))
112+
(destroy-player player-ref (merge params {:reloading? true})))
119113
(download-audio-http
120114
audio-url
121115
(fn [base64-data]
122116
(reset! player-ref (audio/new-player
123117
(str "data:audio/acc;base64," base64-data)
124118
{:autoDestroy false
125119
:continuesToPlayInBackground false}
126-
#(seek params 0 true nil)))
120+
(fn []
121+
(seek player-ref params 0 true nil)
122+
(reset! progress-ref 0))))
127123
(audio/prepare-player
128124
@player-ref
129125
#(when on-success (on-success))
130-
#(update-state (merge params {:error (:message %)})))
126+
#(update-state player-ref (merge params {:error (:message %)})))
131127
(reset! current-player-message-id message-id)
132128
(reset! current-active-state-ref-ref state-ref)
133-
(update-state params)))))
129+
(update-state player-ref params)))))
134130

135131
(defn play-pause
136-
[{:keys [message-id state-ref] :as params} audio seeking-audio?]
137-
(if (not= message-id @current-player-message-id)
138-
;; player has audio from another message, we need to reload
139-
(reload-player params
140-
audio
141-
;; on-success: audio is loaded, do we have an existing value to seek to?
142-
(fn []
143-
(reset! seeking-audio? false)
144-
(if-some [seek-time (:seek-to-ms @state-ref)]
145-
;; check seek time against real audio duration and play
146-
(let [checked-seek-time (min (audio/get-player-duration @player-ref) seek-time)]
147-
(seek params
148-
checked-seek-time
149-
true
150-
(fn [] (play-pause params audio))))
151-
152-
;; nothing to seek to, play
153-
(play-pause params audio))))
154-
155-
;; loaded audio corresponds to current message we can play
156-
(when @player-ref
157-
(audio/toggle-playpause-player
158-
@player-ref
159-
#(update-state params)
160-
#(update-state params)
161-
#(update-state (merge params {:error (:message %)}))))))
132+
[player-ref {:keys [message-id progress-ref progress-timer-ref] :as params} seeking-audio?]
133+
(let [mediaserver-port (rf/sub [:mediaserver/port])
134+
audio-uri (str "https://localhost:" mediaserver-port
135+
"/messages/audio?messageId=" message-id)]
136+
(if (not= message-id @current-player-message-id)
137+
;; player has audio from another message, we need to reload
138+
(reload-player player-ref
139+
params
140+
audio-uri
141+
;; on-success: audio is loaded, do we have an existing value to seek to?
142+
(fn []
143+
(reset! seeking-audio? false)
144+
(if (> @progress-ref 0)
145+
;; check seek time against real audio duration and play
146+
(let [audio-duration (audio/get-player-duration @player-ref)
147+
seek-time (* audio-duration @progress-ref)
148+
checked-seek-time (min audio-duration seek-time)]
149+
(seek player-ref
150+
params
151+
checked-seek-time
152+
true
153+
(fn [] (play-pause player-ref params seeking-audio?))))
154+
155+
;; nothing to seek to, play
156+
(play-pause player-ref params seeking-audio?))))
157+
158+
;; loaded audio corresponds to current message we can play
159+
(when @player-ref
160+
(audio/toggle-playpause-player
161+
@player-ref
162+
(fn []
163+
(update-state player-ref params)
164+
(reset! progress-timer-ref
165+
(js/setInterval
166+
(fn []
167+
(let [current-time (audio/get-player-current-time @player-ref)
168+
player-state (audio/get-state @player-ref)
169+
playing? (= player-state audio/PLAYING)]
170+
(when (and playing? (not @seeking-audio?) (> current-time 0))
171+
(reset! progress-ref current-time))))
172+
100)))
173+
(fn []
174+
(update-state player-ref params)
175+
(when @progress-timer-ref
176+
(js/clearInterval @progress-timer-ref)
177+
(reset! progress-timer-ref nil)))
178+
#(update-state player-ref (merge params {:error (:message %)})))))))
162179

163180
(defn- play-pause-button
164181
[state-ref on-press]
@@ -175,40 +192,53 @@
175192
:color colors/white}]])
176193

177194
(defn message-content
178-
[{:keys [audio audio-duration-ms message-id]}]
179-
[:f>
180-
(fn []
181-
(let [state (reagent/atom nil)
182-
progress (reagent/atom 0)
183-
seeking-audio? (reagent/atom false)
184-
base-params {:state-ref state
185-
:message-id message-id
186-
:progress-ref progress}
187-
time-ms (if (#{:playing :paused :seeking} (:general @state))
188-
@progress
189-
(:duration @state))
190-
time-secs (quot time-ms 1000)]
191-
(rn/use-effect
192-
(fn []
193-
(update-state {:state-ref state
194-
:audio-duration-ms audio-duration-ms
195-
:message-id message-id
196-
:unloaded? true
197-
:progress-ref progress})))
198-
(if (= (:general @state) :error)
199-
[react/text
200-
{:style {:typography :main-medium
201-
:margin-bottom 16}} (:error-msg @state)]
202-
[react/view {:style (style/container)}
203-
[play-pause-button state #(play-pause base-params audio seeking-audio?)]
204-
[quo/soundtrack
205-
{:style style/slider-container
206-
:audio-current-time-ms progress
207-
:player-ref player-ref
208-
:seeking-audio? seeking-audio?}]
209-
[quo/text
210-
{:style style/timestamp
211-
:accessibility-label :audio-duration-label
212-
:weight :medium
213-
:size :paragraph-2}
214-
(gstring/format "%02d:%02d" (quot time-secs 60) (mod time-secs 60))]])))])
195+
[{:keys [audio-duration-ms message-id]}]
196+
(let [player-ref (atom nil)
197+
state (reagent/atom nil)
198+
progress (reagent/atom 0)
199+
progress-timer (atom nil)
200+
seeking-audio? (reagent/atom false)]
201+
[:f>
202+
(fn []
203+
(let [base-params {:state-ref state
204+
:message-id message-id
205+
:progress-ref progress
206+
:progress-timer-ref progress-timer}
207+
duration (:duration @state)
208+
time-secs (quot
209+
(if (or @seeking-audio? (#{:playing :seeking} (:general @state)))
210+
(if (and (> @progress 0) (<= @progress 1)) (* duration @progress) @progress)
211+
duration)
212+
1000)]
213+
(rn/use-effect
214+
(fn []
215+
(update-state player-ref
216+
{:state-ref state
217+
:audio-duration-ms audio-duration-ms
218+
:message-id message-id
219+
:unloaded? true
220+
:progress-ref progress})
221+
(fn []
222+
(destroy-player player-ref {:state-ref state :message-id message-id})
223+
(when (= @current-player-message-id message-id)
224+
(reset! current-active-state-ref-ref nil)
225+
(reset! current-player-message-id nil))
226+
(reset! state nil))))
227+
(if (= (:general @state) :error)
228+
[quo/text
229+
{:style {:typography :main-medium
230+
:margin-bottom 16}}
231+
(:error-msg @state)]
232+
[react/view {:style (style/container)}
233+
[play-pause-button state #(play-pause player-ref base-params seeking-audio?)]
234+
[quo/soundtrack
235+
{:style style/slider-container
236+
:audio-current-time-ms progress
237+
:player-ref player-ref
238+
:seeking-audio? seeking-audio?}]
239+
[quo/text
240+
{:style style/timestamp
241+
:accessibility-label :audio-duration-label
242+
:weight :medium
243+
:size :paragraph-2}
244+
(gstring/format "%02d:%02d" (quot time-secs 60) (mod time-secs 60))]])))]))

src/status_im/ui/screens/chat/styles/message/audio.cljs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
(ns status-im.ui.screens.chat.styles.message.audio
2-
(:require [quo.design-system.colors :as colors-old]
3-
[status-im.ui.screens.chat.styles.message.message :as message.style]
4-
[status-im.utils.platform :as platform]
5-
[quo2.foundations.colors :as colors]
2+
(:require [quo2.foundations.colors :as colors]
63
[quo2.theme :as theme]))
74

85
(defn container

0 commit comments

Comments
 (0)