Skip to content

Commit 6c12394

Browse files
move component to status-im2 namespace
1 parent 97d5aec commit 6c12394

File tree

6 files changed

+302
-9
lines changed

6 files changed

+302
-9
lines changed

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

+12-6
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
[goog.string :as gstring]
44
[reagent.core :as reagent]
55
[react-native.audio-toolkit :as audio]
6-
[status-im.ui.components.react :as react]
76
[status-im.ui.screens.chat.styles.message.audio :as style]
8-
[status-im.utils.platform :as platform]
7+
[react-native.platform :as platform]
98
[taoensso.timbre :as log]
109
[quo2.foundations.colors :as colors]
1110
[quo2.core :as quo]
1211
[react-native.core :as rn]
1312
[utils.re-frame :as rf]))
1413

14+
(def ^:const media-server-uri-prefix "https://localhost:")
15+
(def ^:const audio-path "/messages/audio")
16+
(def ^:const uri-param "?messageId=")
17+
1518
(defonce current-player-message-id (atom nil))
1619
(defonce current-active-state-ref-ref (atom nil))
1720
(defonce progress-timer (atom nil))
@@ -131,8 +134,11 @@
131134
(defn play-pause
132135
[player-ref {:keys [message-id progress-ref progress-timer-ref] :as params} seeking-audio?]
133136
(let [mediaserver-port (rf/sub [:mediaserver/port])
134-
audio-uri (str "https://localhost:" mediaserver-port
135-
"/messages/audio?messageId=" message-id)]
137+
audio-uri (str media-server-uri-prefix
138+
mediaserver-port
139+
audio-path
140+
uri-param
141+
message-id)]
136142
(if (not= message-id @current-player-message-id)
137143
;; player has audio from another message, we need to reload
138144
(reload-player player-ref
@@ -179,7 +185,7 @@
179185

180186
(defn- play-pause-button
181187
[state-ref on-press]
182-
[react/touchable-opacity
188+
[rn/touchable-opacity
183189
{:on-press on-press
184190
:style (style/play-pause-container)}
185191
[quo/icon
@@ -229,7 +235,7 @@
229235
{:style {:typography :main-medium
230236
:margin-bottom 16}}
231237
(:error-msg @state)]
232-
[react/view {:style (style/container)}
238+
[rn/view {:style (style/container)}
233239
[play-pause-button state #(play-pause player-ref base-params seeking-audio?)]
234240
[quo/soundtrack
235241
{:style style/slider-container

src/status_im/ui2/screens/chat/messages/audio/style.cljs

-1
This file was deleted.

src/status_im/ui2/screens/chat/messages/audio/view.cljs

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(ns status-im2.contexts.chat.messages.content.audio.style
2+
(:require [quo2.foundations.colors :as colors]
3+
[quo2.theme :as theme]))
4+
5+
(defn container
6+
[]
7+
{:width 295
8+
:height 56
9+
:border-radius 12
10+
:border-width 1
11+
:padding 12
12+
:flex-direction :row
13+
:align-items :center
14+
:justify-content :space-between
15+
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)
16+
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80-opa-40)})
17+
18+
(def play-pause-slider-container
19+
{:flex-direction :row
20+
:align-items :center})
21+
22+
(def slider-container
23+
{:position :absolute
24+
:left 60
25+
:right 71
26+
:bottom nil})
27+
28+
(defn play-pause-container
29+
[]
30+
{:background-color (get-in colors/customization [:blue (if (theme/dark?) 60 50)])
31+
:width 32
32+
:height 32
33+
:border-radius 16
34+
:align-items :center
35+
:justify-content :center})
36+
37+
(def timestamp
38+
{:margin-left 4})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
(ns status-im2.contexts.chat.messages.content.audio.view
2+
(:require ["react-native-blob-util" :default ReactNativeBlobUtil]
3+
[goog.string :as gstring]
4+
[reagent.core :as reagent]
5+
[react-native.audio-toolkit :as audio]
6+
[status-im2.contexts.chat.messages.content.audio.style :as style]
7+
[react-native.platform :as platform]
8+
[taoensso.timbre :as log]
9+
[quo2.foundations.colors :as colors]
10+
[quo2.core :as quo]
11+
[react-native.core :as rn]
12+
[utils.re-frame :as rf]))
13+
14+
(def ^:const media-server-uri-prefix "https://localhost:")
15+
(def ^:const audio-path "/messages/audio")
16+
(def ^:const uri-param "?messageId=")
17+
18+
(defonce current-player-message-id (atom nil))
19+
(defonce current-active-state-ref-ref (atom nil))
20+
(defonce progress-timer (atom nil))
21+
22+
(defn update-state
23+
[player-ref
24+
{:keys [state-ref progress-ref message-id seek-to-ms audio-duration-ms
25+
slider-new-state-seeking? unloaded? error]}]
26+
(let [player-state (audio/get-state @player-ref)
27+
slider-seeking (if (some? slider-new-state-seeking?)
28+
slider-new-state-seeking?
29+
(:slider-seeking @state-ref))
30+
general (cond
31+
(some? error) :error
32+
(or unloaded? (not= message-id @current-player-message-id)) :not-loaded
33+
slider-seeking (:general
34+
@state-ref) ; persist
35+
; player
36+
; state
37+
; at
38+
; the
39+
; time
40+
; user
41+
; started
42+
; sliding
43+
(= player-state audio/PLAYING) :playing
44+
(= player-state audio/PAUSED) :paused
45+
(= player-state audio/SEEKING) :seeking
46+
(= player-state audio/PREPARED) :ready-to-play
47+
:else :preparing)
48+
new-state {:general general
49+
:error-msg error
50+
:duration (cond (not (#{:preparing :not-loaded :error} general))
51+
(audio/get-player-duration @player-ref)
52+
53+
audio-duration-ms audio-duration-ms
54+
55+
:else (:duration @state-ref))
56+
:progress-ref (or progress-ref (:progress-ref @state-ref))
57+
:slider-seeking slider-seeking
58+
59+
; persist seek-to-ms while seeking or audio is not loaded
60+
:seek-to-ms (when (or
61+
slider-seeking
62+
(#{:preparing :not-loaded :error} general))
63+
(or seek-to-ms (:seek-to-ms @state-ref)))}]
64+
; update state if needed
65+
(when (not= @state-ref new-state)
66+
(reset! state-ref new-state))
67+
68+
; update progress UI on slider release
69+
(when (and (some? slider-new-state-seeking?) (not slider-new-state-seeking?) (some? seek-to-ms))
70+
(reset! (:progress-ref new-state) seek-to-ms))))
71+
72+
(defn destroy-player
73+
[player-ref {:keys [message-id reloading?]}]
74+
(when (and @player-ref
75+
(or reloading?
76+
(= message-id @current-player-message-id)))
77+
(audio/destroy-player @player-ref)
78+
(reset! player-ref nil)
79+
(when @current-active-state-ref-ref
80+
(update-state player-ref {:state-ref @current-active-state-ref-ref :unloaded? true}))
81+
(reset! current-player-message-id nil)
82+
(reset! current-active-state-ref-ref nil)))
83+
84+
(defonce last-seek (atom (js/Date.now)))
85+
86+
(defn seek
87+
[player-ref {:keys [message-id] :as params} value immediate? on-success]
88+
(when (and @player-ref (= message-id @current-player-message-id))
89+
(let [now (js/Date.now)]
90+
(when (or immediate? (> (- now @last-seek) 200))
91+
(reset! last-seek (js/Date.now))
92+
(audio/seek-player
93+
@player-ref
94+
value
95+
#(do
96+
(update-state player-ref params)
97+
(when on-success (on-success)))
98+
#(update-state player-ref (merge params {:error (:message %)}))))))
99+
(update-state player-ref (merge params {:seek-to-ms value})))
100+
101+
(defn download-audio-http
102+
[base64-uri on-success]
103+
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?}))
104+
(.fetch "GET" (str base64-uri))
105+
(.then #(on-success (.base64 ^js %)))
106+
(.catch #(log/error "could not fetch audio"))))
107+
108+
(defn reload-player
109+
[player-ref {:keys [message-id state-ref progress-ref] :as params} audio-url on-success]
110+
;; to avoid reloading player while is initializing,
111+
;; we go ahead only if there is no player or
112+
;; if it is already prepared
113+
(when (or (nil? @player-ref) (audio/can-play? @player-ref))
114+
(when @player-ref
115+
(destroy-player player-ref (merge params {:reloading? true})))
116+
(download-audio-http
117+
audio-url
118+
(fn [base64-data]
119+
(reset! player-ref (audio/new-player
120+
(str "data:audio/acc;base64," base64-data)
121+
{:autoDestroy false
122+
:continuesToPlayInBackground false}
123+
(fn []
124+
(seek player-ref params 0 true nil)
125+
(reset! progress-ref 0))))
126+
(audio/prepare-player
127+
@player-ref
128+
#(when on-success (on-success))
129+
#(update-state player-ref (merge params {:error (:message %)})))
130+
(reset! current-player-message-id message-id)
131+
(reset! current-active-state-ref-ref state-ref)
132+
(update-state player-ref params)))))
133+
134+
(defn play-pause
135+
[player-ref {:keys [message-id progress-ref progress-timer-ref] :as params} seeking-audio?]
136+
(let [mediaserver-port (rf/sub [:mediaserver/port])
137+
audio-uri (str media-server-uri-prefix
138+
mediaserver-port
139+
audio-path
140+
uri-param
141+
message-id)]
142+
(if (not= message-id @current-player-message-id)
143+
;; player has audio from another message, we need to reload
144+
(reload-player player-ref
145+
params
146+
audio-uri
147+
;; on-success: audio is loaded, do we have an existing value to seek to?
148+
(fn []
149+
(reset! seeking-audio? false)
150+
(if (> @progress-ref 0)
151+
;; check seek time against real audio duration and play
152+
(let [audio-duration (audio/get-player-duration @player-ref)
153+
seek-time (* audio-duration @progress-ref)
154+
checked-seek-time (min audio-duration seek-time)]
155+
(seek player-ref
156+
params
157+
checked-seek-time
158+
true
159+
(fn [] (play-pause player-ref params seeking-audio?))))
160+
161+
;; nothing to seek to, play
162+
(play-pause player-ref params seeking-audio?))))
163+
164+
;; loaded audio corresponds to current message we can play
165+
(when @player-ref
166+
(audio/toggle-playpause-player
167+
@player-ref
168+
(fn []
169+
(update-state player-ref params)
170+
(reset! progress-timer-ref
171+
(js/setInterval
172+
(fn []
173+
(let [current-time (audio/get-player-current-time @player-ref)
174+
player-state (audio/get-state @player-ref)
175+
playing? (= player-state audio/PLAYING)]
176+
(when (and playing? (not @seeking-audio?) (> current-time 0))
177+
(reset! progress-ref current-time))))
178+
100)))
179+
(fn []
180+
(update-state player-ref params)
181+
(when @progress-timer-ref
182+
(js/clearInterval @progress-timer-ref)
183+
(reset! progress-timer-ref nil)))
184+
#(update-state player-ref (merge params {:error (:message %)})))))))
185+
186+
(defn- play-pause-button
187+
[state-ref on-press]
188+
[rn/touchable-opacity
189+
{:on-press on-press
190+
:style (style/play-pause-container)}
191+
[quo/icon
192+
(case (:general @state-ref)
193+
:preparing :i/loading
194+
:playing :i/pause-audio
195+
:i/play-audio)
196+
{:size 20
197+
:accessibility-label :play-pause-audio-message-button
198+
:color colors/white}]])
199+
200+
(defn audio-message
201+
[{:keys [audio-duration-ms message-id]}]
202+
(let [player-ref (atom nil)
203+
state (reagent/atom nil)
204+
progress (reagent/atom 0)
205+
progress-timer (atom nil)
206+
seeking-audio? (reagent/atom false)]
207+
[:f>
208+
(fn []
209+
(let [base-params {:state-ref state
210+
:message-id message-id
211+
:progress-ref progress
212+
:progress-timer-ref progress-timer}
213+
duration (:duration @state)
214+
time-secs (quot
215+
(if (or @seeking-audio? (#{:playing :seeking} (:general @state)))
216+
(if (and (> @progress 0) (<= @progress 1)) (* duration @progress) @progress)
217+
duration)
218+
1000)]
219+
(rn/use-effect
220+
(fn []
221+
(update-state player-ref
222+
{:state-ref state
223+
:audio-duration-ms audio-duration-ms
224+
:message-id message-id
225+
:unloaded? true
226+
:progress-ref progress})
227+
(fn []
228+
(destroy-player player-ref {:state-ref state :message-id message-id})
229+
(when (= @current-player-message-id message-id)
230+
(reset! current-active-state-ref-ref nil)
231+
(reset! current-player-message-id nil))
232+
(reset! state nil))))
233+
(if (= (:general @state) :error)
234+
[quo/text
235+
{:style {:typography :main-medium
236+
:margin-bottom 16}}
237+
(:error-msg @state)]
238+
[rn/view {:style (style/container)}
239+
[play-pause-button state #(play-pause player-ref base-params seeking-audio?)]
240+
[quo/soundtrack
241+
{:style style/slider-container
242+
:audio-current-time-ms progress
243+
:player-ref player-ref
244+
:seeking-audio? seeking-audio?}]
245+
[quo/text
246+
{:style style/timestamp
247+
:accessibility-label :audio-duration-label
248+
:weight :medium
249+
:size :paragraph-2}
250+
(gstring/format "%02d:%02d" (quot time-secs 60) (mod time-secs 60))]])))]))

src/status_im2/contexts/chat/messages/content/view.cljs

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
[status-im2.contexts.chat.messages.content.system.text.view :as system.text]
1313
[status-im2.contexts.chat.messages.content.album.view :as album]
1414
[status-im2.contexts.chat.messages.content.image.view :as image]
15+
[status-im2.contexts.chat.messages.content.audio.view :as audio]
1516
[quo2.core :as quo]
1617
[utils.re-frame :as rf]
1718
[status-im.ui2.screens.chat.messages.message :as old-message]
@@ -133,7 +134,7 @@
133134
[not-implemented/not-implemented [old-message/sticker message-data]]
134135

135136
constants/content-type-audio
136-
[old-message/audio message-data]
137+
[audio/audio-message message-data]
137138

138139
constants/content-type-image
139140
[image/image-message 0 message-data context on-long-press]

0 commit comments

Comments
 (0)