aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMiguel Ángel Moreno <mail@migalmoreno.com>2024-01-22 01:00:00 +0100
committerMiguel Ángel Moreno <mail@migalmoreno.com>2024-01-22 01:00:00 +0100
commit01b16e4186f0e664704668ffe067a0aef12e4c33 (patch)
treee5d49d3b082a2202dc9f773f6d7a3afdc96eab1b /src
parentc244c191123a96295ea9ce544b9a4e7b3bdaa9dd (diff)
feat(frontend): add common player components
Diffstat (limited to 'src')
-rw-r--r--src/frontend/tubo/components/audio_player.cljs117
-rw-r--r--src/frontend/tubo/components/play_queue.cljs97
-rw-r--r--src/frontend/tubo/components/player.cljs33
-rw-r--r--src/frontend/tubo/components/video_player.cljs2
4 files changed, 134 insertions, 115 deletions
diff --git a/src/frontend/tubo/components/audio_player.cljs b/src/frontend/tubo/components/audio_player.cljs
index ae4c4e4..4ecf9c8 100644
--- a/src/frontend/tubo/components/audio_player.cljs
+++ b/src/frontend/tubo/components/audio_player.cljs
@@ -4,6 +4,7 @@
[re-frame.core :as rf]
[reitit.frontend.easy :as rfe]
[tubo.components.loading :as loading]
+ [tubo.components.player :as player]
[tubo.events :as events]
[tubo.util :as util]))
@@ -55,77 +56,59 @@
(set! (.-src @!player) (:stream (nth media-queue idx)))
(.play @!player))))}]]
[:div.flex
- [:button:focus:ring-transparent.mx-2.cursor-pointer
- {:on-click #(rf/dispatch [::events/toggle-media-queue])}
- [:i.fa-solid.fa-list]]
- [:button.hidden.ml:block.focus:outline-none.mx-2
- {:class (when-not (and media-queue (not= media-queue-pos 0))
- "opacity-50 cursor-auto")
- :on-click #(when (and media-queue (not= media-queue-pos 0))
- (rf/dispatch [::events/change-media-queue-pos
- (- media-queue-pos 1)]))}
- [:i.fa-solid.fa-backward-step]]
- [:button.hidden.ml:block.focus:outline-none.mx-2
- {:on-click #(set! (.-currentTime @!player) (- @!elapsed-time 5))}
- [:i.fa-solid.fa-backward]]
- [:button.focus:outline-none.mx-2
- {:on-click #(if (.-paused @!player)
- (.play @!player)
- (.pause @!player))}
+ [player/button [:i.fa-solid.fa-list] #(rf/dispatch [::events/toggle-media-queue])
+ :show-on-mobile? true]
+ [player/button
+ [:i.fa-solid.fa-backward-step]
+ #(when (and media-queue (not= media-queue-pos 0))
+ (rf/dispatch [::events/change-media-queue-pos
+ (- media-queue-pos 1)]))
+ :disabled? (not (and media-queue (not= media-queue-pos 0)))]
+ [player/button [:i.fa-solid.fa-backward] #(set! (.-currentTime @!player) (- @!elapsed-time 5))]
+ [player/button
(if @!player
(if show-audio-player-loading?
[loading/loading-icon service-color "text-1xl"]
(if (.-paused @!player)
[:i.fa-solid.fa-play]
[:i.fa-solid.fa-pause]))
- [:i.fa-solid.fa-play])]
- [:button.hidden.ml:block.focus:outline-none.mx-2
- {:on-click #(set! (.-currentTime @!player) (+ @!elapsed-time 5))}
- [:i.fa-solid.fa-forward]]
- [:button.hidden.ml:block.focus:ring-transparent.mx-2
- {:class (when-not (and media-queue (< (+ media-queue-pos 1) (count media-queue)))
- "opacity-50 cursor-auto")
- :on-click #(when (and media-queue (< (+ media-queue-pos 1) (count media-queue)))
- (rf/dispatch [::events/change-media-queue-pos
- (+ media-queue-pos 1)]))}
- [:i.fa-solid.fa-forward-step]]
- [:div.flex.items-center
- [:span.hidden.ml:block.mx-2 (if @!elapsed-time (util/format-duration @!elapsed-time) "00:00")]
- [:input.hidden.ml:block.mx-2.w-20.ml:w-56.bg-gray-200.rounded-lg.cursor-pointer.focus:outline-none.h-1
- {:type "range"
- :on-input #(reset! !elapsed-time (.. % -target -value))
- :on-change #(and @!player (> (.-readyState @!player) 0)
- (set! (.-currentTime @!player) @!elapsed-time))
- :style {:accentColor service-color}
- :max (if (and @!player (> (.-readyState @!player) 0))
- (.floor js/Math (.-duration @!player))
- 100)
- :value @!elapsed-time}]
- [:span.hidden.ml:block.mx-2
+ [:i.fa-solid.fa-play])
+ #(if (.-paused @!player)
+ (.play @!player)
+ (.pause @!player))
+ :show-on-mobile? true]
+ [player/button [:i.fa-solid.fa-forward] #(set! (.-currentTime @!player) (+ @!elapsed-time 5))]
+ [player/button [:i.fa-solid.fa-forward-step]
+ #(when (and media-queue (< (+ media-queue-pos 1) (count media-queue)))
+ (rf/dispatch [::events/change-media-queue-pos
+ (+ media-queue-pos 1)]))
+ :disabled? (not (and media-queue (< (+ media-queue-pos 1) (count media-queue))))]
+ [:div.hidden.lg:flex.items-center
+ [:span.mx-2 (if @!elapsed-time (util/format-duration @!elapsed-time) "00:00")]
+ [:div.w-20.lg:w-56.mx-2
+ [player/time-slider !player !elapsed-time service-color]]
+ [:span.mx-2
(if player-ready? (util/format-duration (.-duration @!player)) "00:00")]
- [:button.hidden.ml:flex.focus:ring-transparent.mx-2
- {:on-click #(rf/dispatch [::events/toggle-loop-file])}
- [:i.fa-solid.fa-repeat
- {:style {:color (when loop-file? service-color)}}]]
- [:button.hidden.ml:flex.focus:ring-transparent.mx-2
- {:on-click #(rf/dispatch [::events/toggle-loop-playlist])}
- [:i.fa-solid.fa-retweet
- {:style {:color (when loop-playlist? service-color)}}]]
- [:div.hidden.ml:flex.items-center
- [:button.focus:outline-none.mx-2
- {:on-click #(rf/dispatch [::events/toggle-mute @!player])}
- (if (or (and @!player muted?))
- [:i.fa-solid.fa-volume-xmark]
- [:i.fa-solid.fa-volume-low])]
- [:input.w-20.bg-gray-200.rounded-lg.cursor-pointer.focus:outline-none.h-1.range-sm.mx-2
- {:type "range"
- :on-input #(rf/dispatch [::events/change-volume-level (.. % -target -value) @!player])
- :style {:accentColor service-color}
- :max 100
- :value volume-level}]]]
- [:div.mx-2
- [:i.fa-solid.fa-close.cursor-pointer
- {:on-click (fn []
- (rf/dispatch [::events/toggle-audio-player])
- (.pause @!player)
- (set! (.-currentTime @!player) 0))}]]]]])))
+ [player/button
+ [:div.relative
+ [:i.fa-solid.fa-repeat
+ {:style {:color (when loop-playback service-color)}}]
+ (when (= loop-playback :stream)
+ [:span.absolute.font-bold
+ {:style {:color (when loop-playback service-color)
+ :font-size "6px"
+ :right "6px"
+ :top "6.5px"}}
+ "1"])]
+ #(rf/dispatch [::events/toggle-loop-playback])]
+ [:div.hidden.lg:flex.items-center
+ [player/button
+ (if (or (and @!player muted?)) [:i.fa-solid.fa-volume-xmark] [:i.fa-solid.fa-volume-low])
+ #(rf/dispatch [::events/toggle-mute @!player])]
+ [player/volume-slider !player volume-level service-color]]]
+ [player/button [:i.fa-solid.fa-close]
+ (fn []
+ (rf/dispatch [::events/toggle-audio-player])
+ (.pause @!player)
+ (set! (.-currentTime @!player) 0))
+ :show-on-mobile? true]]]])))
diff --git a/src/frontend/tubo/components/play_queue.cljs b/src/frontend/tubo/components/play_queue.cljs
index bf9f433..19a49a3 100644
--- a/src/frontend/tubo/components/play_queue.cljs
+++ b/src/frontend/tubo/components/play_queue.cljs
@@ -4,6 +4,7 @@
[reitit.frontend.easy :as rfe]
[tubo.components.items :as items]
[tubo.components.loading :as loading]
+ [tubo.components.player :as player]
[tubo.events :as events]
[tubo.util :as util]))
@@ -60,55 +61,57 @@
[:a.text-sm.pt-2.text-neutral-600.dark:text-neutral-300.line-clamp-1
{:href (rfe/href :tubo.routes/channel nil {:url uploader-url})} uploader-name]]
[:div.flex.flex-auto.py-2.w-full.items-center
- [:span (if @!elapsed-time (util/format-duration @!elapsed-time) "00:00")]
- [:input.mx-2.bg-gray-200.rounded-lg.cursor-pointer.focus:outline-none.w-full.h-1
- {:type "range"
- :on-input #(reset! !elapsed-time (.. % -target -value))
- :on-change #(when player-ready?
- (set! (.-currentTime @!player) @!elapsed-time))
- :style {:accentColor service-color}
- :max (if player-ready?
- (.floor js/Math (.-duration @!player))
- 100)
- :value @!elapsed-time}]
- [:span (if player-ready?
- (util/format-duration (.-duration @!player))
- "00:00")]]
+ [:span.mr-2 (if @!elapsed-time (util/format-duration @!elapsed-time) "00:00")]
+ [player/time-slider !player !elapsed-time service-color]
+ [:span.ml-2 (if player-ready? (util/format-duration (.-duration @!player)) "00:00")]]
[:div.flex.justify-center.items-center
- [:button.focus:ring-transparent.mx-2
- {:on-click #(rf/dispatch [::events/toggle-loop-file])}
- [:i.fa-solid.fa-repeat
- {:style {:color (when loop-file? service-color)}}]]
- [:button.focus:outline-none.mx-2.text-xl
- {:class (when-not (and media-queue (not= media-queue-pos 0))
- "opacity-50 cursor-auto")
- :on-click #(when (and media-queue (not= media-queue-pos 0))
- (rf/dispatch [::events/change-media-queue-pos
- (- media-queue-pos 1)]))}
- [:i.fa-solid.fa-backward-step]]
- [:button.focus:outline-none.mx-2.text-xl
- {:on-click #(set! (.-currentTime @!player) (- @!elapsed-time 5))}
- [:i.fa-solid.fa-backward]]
- [:button.focus:outline-none.mx-2.text-3xl
- {:on-click #(if (.-paused @!player)
- (.play @!player)
- (.pause @!player))}
+ [player/button
+ [:div.relative
+ [:i.fa-solid.fa-repeat
+ {:style {:color (when loop-playback service-color)}}]
+ (when (= loop-playback :stream)
+ [:span.absolute.font-bold
+ {:style {:color (when loop-playback service-color)
+ :font-size "6px"
+ :right "6px"
+ :top "6.5px"}}
+ "1"])]
+ #(rf/dispatch [::events/toggle-loop-playback])
+ :show-on-mobile? true]
+ [player/button
+ [:i.fa-solid.fa-backward-step]
+ #(when (and media-queue (not= media-queue-pos 0))
+ (rf/dispatch [::events/change-media-queue-pos
+ (- media-queue-pos 1)]))
+ :disabled? (not (and media-queue (not= media-queue-pos 0)))
+ :extra-styles "text-xl"
+ :show-on-mobile? true]
+ [player/button
+ [:i.fa-solid.fa-backward]
+ #(set! (.-currentTime @!player) (- @!elapsed-time 5))
+ :extra-styles "text-xl"
+ :show-on-mobile? true]
+ [player/button
(if show-audio-player-loading?
[loading/loading-icon service-color "text-3xl"]
(if (.-paused @!player)
[:i.fa-solid.fa-play]
- [:i.fa-solid.fa-pause]))]
- [:button.focus:outline-none.mx-2.text-xl
- {:on-click #(set! (.-currentTime @!player) (+ @!elapsed-time 5))}
- [:i.fa-solid.fa-forward]]
- [:button.focus:ring-transparent.mx-2.text-xl
- {:class (when-not (and media-queue (< (+ media-queue-pos 1) (count media-queue)))
- "opacity-50 cursor-auto")
- :on-click #(when (and media-queue (< (+ media-queue-pos 1) (count media-queue)))
- (rf/dispatch [::events/change-media-queue-pos
- (+ media-queue-pos 1)]))}
- [:i.fa-solid.fa-forward-step]]
- [:button.focus:ring-transparent.mx-2
- {:on-click #(rf/dispatch [::events/toggle-loop-playlist])}
- [:i.fa-solid.fa-retweet
- {:style {:color (when loop-playlist? service-color)}}]]]]])))
+ [:i.fa-solid.fa-pause]))
+ #(if (.-paused @!player)
+ (.play @!player)
+ (.pause @!player))
+ :extra-styles "text-3xl"
+ :show-on-mobile? true]
+ [player/button
+ [:i.fa-solid.fa-forward]
+ #(set! (.-currentTime @!player) (+ @!elapsed-time 5))
+ :extra-styles "text-xl"
+ :show-on-mobile? true]
+ [player/button
+ [:i.fa-solid.fa-forward-step]
+ #(when (and media-queue (< (+ media-queue-pos 1) (count media-queue)))
+ (rf/dispatch [::events/change-media-queue-pos
+ (+ media-queue-pos 1)]))
+ :disabled? (not (and media-queue (< (+ media-queue-pos 1) (count media-queue))))
+ :extra-styles "text-xl"
+ :show-on-mobile? true]]]])))
diff --git a/src/frontend/tubo/components/player.cljs b/src/frontend/tubo/components/player.cljs
new file mode 100644
index 0000000..c851da8
--- /dev/null
+++ b/src/frontend/tubo/components/player.cljs
@@ -0,0 +1,33 @@
+(ns tubo.components.player
+ (:require
+ [re-frame.core :as rf]
+ [tubo.events :as events]))
+
+(defn time-slider [player elapsed-time service-color]
+ [:input.bg-gray-200.rounded-lg.cursor-pointer.focus:outline-none.w-full
+ {:type "range"
+ :on-input #(reset! elapsed-time (.. % -target -value))
+ :on-change #(and @player (> (.-readyState @player) 0)
+ (set! (.-currentTime @player) @elapsed-time))
+ :style {:accentColor service-color}
+ :max (if (and @player (> (.-readyState @player) 0))
+ (.floor js/Math (.-duration @player))
+ 100)
+ :value @elapsed-time}])
+
+(defn volume-slider [player volume-level service-color]
+ [:input.w-20.bg-gray-200.rounded-lg.cursor-pointer.focus:outline-none.range-sm.mx-2
+ {:type "range"
+ :on-input #(rf/dispatch [::events/change-volume-level (.. % -target -value) @player])
+ :style {:accentColor service-color}
+ :max 100
+ :value volume-level}])
+
+(defn button
+ [icon on-click-cb & {:keys [disabled? show-on-mobile? extra-styles]}]
+ [:button.outline-none.focus:ring-transparent.mx-2
+ {:class (let [styles (apply conj (when disabled? ["opacity-50" "cursor-auto"])
+ (when-not show-on-mobile? ["hidden" "lg:block"]))]
+ (apply str (if (> (count extra-styles) 1) (interpose " " (conj styles extra-styles)) (interpose " " styles))))
+ :on-click on-click-cb}
+ icon])
diff --git a/src/frontend/tubo/components/video_player.cljs b/src/frontend/tubo/components/video_player.cljs
index 0dd21d5..c94579a 100644
--- a/src/frontend/tubo/components/video_player.cljs
+++ b/src/frontend/tubo/components/video_player.cljs
@@ -8,7 +8,7 @@
[options url]
(let [!player (atom nil)]
(r/create-class
- {:display-name "StreamPlayer"
+ {:display-name "VideoPlayer"
:component-did-mount
(fn [this]
(reset! !player (videojs (rdom/dom-node this) (clj->js options))))