diff options
Diffstat (limited to 'src/frontend')
-rw-r--r-- | src/frontend/tubo/components/audio_player.cljs | 92 | ||||
-rw-r--r-- | src/frontend/tubo/components/play_queue.cljs | 150 | ||||
-rw-r--r-- | src/frontend/tubo/views/stream.cljs | 236 |
3 files changed, 251 insertions, 227 deletions
diff --git a/src/frontend/tubo/components/audio_player.cljs b/src/frontend/tubo/components/audio_player.cljs index 40f05b1..8935a0d 100644 --- a/src/frontend/tubo/components/audio_player.cljs +++ b/src/frontend/tubo/components/audio_player.cljs @@ -6,6 +6,7 @@ [re-frame.core :as rf] [reitit.frontend.easy :as rfe] [tubo.components.layout :as layout] + [tubo.components.modals.bookmarks :as bookmarks] [tubo.components.player :as player] [tubo.events :as events] [tubo.util :as util])) @@ -66,7 +67,7 @@ [:i.fa-solid.fa-pause])) #(rf/dispatch [::events/set-player-paused (not paused?)]) :show-on-mobile? true - :extra-styles "lg:text-2xl"] + :extra-classes "lg:text-2xl"] [player/button [:i.fa-solid.fa-forward] #(rf/dispatch [::events/set-player-time (+ @!elapsed-time 5)])] [player/button [:i.fa-solid.fa-forward-step] @@ -83,39 +84,56 @@ (defn player [] - (let [{:keys - [uploader-name uploader-url thumbnail-url - name stream url service-color] :as current-stream} - @(rf/subscribe [:media-queue-stream]) - show-audio-player? @(rf/subscribe [:show-audio-player]) - show-media-queue? @(rf/subscribe [:show-media-queue]) - volume-level @(rf/subscribe [:volume-level]) - muted? @(rf/subscribe [:muted]) - !player @(rf/subscribe [:player]) - {:keys [theme]} @(rf/subscribe [:settings]) - bg-color (str "rgba(" (if (= theme "dark") "23, 23, 23" "255, 255, 255") ", 0.95)")] - (when show-audio-player? - [:div.sticky.bottom-0.z-40.p-3.absolute.box-border.m-0 - {:style - {:display (when show-media-queue? "none") - :background-image (str "linear-gradient(0deg, " bg-color "," bg-color "), url(\"" thumbnail-url "\")") - :backgroundSize "cover" - :backgroundPosition "center" - :backgroundRepeat "no-repeat"}} - [:div.flex.items-center.justify-between - [:div.flex.items-center.lg:flex-1 - [:div {:style {:height "40px" :width "70px" :maxWidth "70px" :minWidth "70px"}} - [:img.min-h-full.max-h-full.object-cover.min-w-full.max-w-full.w-full {:src thumbnail-url}]] - [:div.flex.flex-col.px-2 - [:a.text-xs.line-clamp-1 - {:href (rfe/href :tubo.routes/stream nil {:url url})} name] - [:a.text-xs.pt-2.text-neutral-600.dark:text-neutral-300.line-clamp-1 - {:href (rfe/href :tubo.routes/channel nil {:url uploader-url})} uploader-name]] - [audio-source !player]] - [main-controls service-color] - [:div.flex.lg:justify-end.lg:flex-1 - [player/volume-slider !player volume-level muted? service-color] - [player/button [:i.fa-solid.fa-list] #(rf/dispatch [::events/toggle-media-queue]) - :show-on-mobile? true] - [player/button [:i.fa-solid.fa-close] #(rf/dispatch [::events/dispose-audio-player]) - :show-on-mobile? true]]]]))) + (let [!menu-active? (r/atom nil)] + (fn [] + (let [{:keys + [uploader-name uploader-url thumbnail-url + name stream url service-id] :as current-stream} + @(rf/subscribe [:media-queue-stream]) + show-audio-player? @(rf/subscribe [:show-audio-player]) + show-media-queue? @(rf/subscribe [:show-media-queue]) + volume-level @(rf/subscribe [:volume-level]) + muted? @(rf/subscribe [:muted]) + bookmarks @(rf/subscribe [:bookmarks]) + !player @(rf/subscribe [:player]) + {:keys [theme]} @(rf/subscribe [:settings]) + service-color (and service-id (util/get-service-color service-id)) + bg-color (str "rgba(" (if (= theme "dark") "23, 23, 23" "255, 255, 255") ", 0.95)") + liked? (some #(= (:url %) url) (-> bookmarks first :items))] + (when show-audio-player? + [:div.sticky.bottom-0.z-10.p-3.absolute.box-border.m-0 + {:style + {:display (when show-media-queue? "none") + :background-image (str "linear-gradient(0deg, " bg-color "," bg-color "), url(\"" thumbnail-url "\")") + :backgroundSize "cover" + :backgroundPosition "center" + :backgroundRepeat "no-repeat"}} + [:div.flex.items-center.justify-between + [:div.flex.items-center.lg:flex-1 + [:div {:style {:height "40px" :width "70px" :maxWidth "70px" :minWidth "70px"}} + [:img.min-h-full.max-h-full.object-cover.min-w-full.max-w-full.w-full {:src thumbnail-url}]] + [:div.flex.flex-col.px-2 + [:a.text-xs.line-clamp-1 + {:href (rfe/href :tubo.routes/stream nil {:url url})} name] + [:a.text-xs.pt-2.text-neutral-600.dark:text-neutral-300.line-clamp-1 + {:href (rfe/href :tubo.routes/channel nil {:url uploader-url})} uploader-name]] + [audio-source !player]] + [main-controls service-color] + [:div.flex.lg:justify-end.lg:flex-1 + [player/volume-slider !player volume-level muted? service-color] + [player/button [:i.fa-solid.fa-list] #(rf/dispatch [::events/toggle-media-queue]) + :show-on-mobile? true] + [layout/more-menu !menu-active? + [{:label (if liked? "Remove favorite" "Favorite") + :icon (if liked? + [:i.fa-solid.fa-heart {:style {:color service-color}}] + [:i.fa-regular.fa-heart]) + :on-click #(rf/dispatch [(if liked? ::events/remove-from-likes ::events/add-to-likes) current-stream])} + {:label "Add to playlist" + :icon [:i.fa-solid.fa-plus] + :on-click #(rf/dispatch [::events/add-bookmark-list-modal + [bookmarks/add-to-bookmark-list-modal current-stream]])}] + :menu-styles {:bottom "0px" :top nil} + :extra-classes "pt-1 !pl-2 pr-2 "] + [player/button [:i.fa-solid.fa-close] #(rf/dispatch [::events/dispose-audio-player]) + :show-on-mobile? true]]]]))))) diff --git a/src/frontend/tubo/components/play_queue.cljs b/src/frontend/tubo/components/play_queue.cljs index a2b1190..8c77d7d 100644 --- a/src/frontend/tubo/components/play_queue.cljs +++ b/src/frontend/tubo/components/play_queue.cljs @@ -9,25 +9,19 @@ [tubo.util :as util])) (defn play-queue-item - [{:keys [uploader-name uploader-url name duration + [{:keys [service-id uploader-name uploader-url name duration stream url service-color thumbnail-url]} media-queue-pos i] - (let [service-name (case service-color - "#cc0000" "YouTube" - "#ff7700" "SoundCloud" - "#333333" "media.ccc.de" - "#F2690D" "PeerTube" - "#629aa9" "Bandcamp")] - [:div.flex.w-full.h-24.rounded.cursor-pointer.px-2.my-1 - {:class (when (= i media-queue-pos) "bg-[#f0f0f0] dark:bg-stone-800") - :on-click #(rf/dispatch [::events/change-media-queue-pos i])} - [:div.w-56 - [items/thumbnail thumbnail-url nil url name duration {:classes "h-24"}]] - [:div.flex.flex-col.px-4.py-2.w-full - [:h1.line-clamp-1 name] - [:div.text-neutral-600.dark:text-neutral-300.text-sm.flex.flex-col.xs:flex-row - [:span.line-clamp-1 uploader-name] - [:span.px-2.hidden.xs:inline-block {:dangerouslySetInnerHTML {:__html "•"}}] - [:span service-name]]]])) + [:div.flex.w-full.h-24.rounded.cursor-pointer.px-2.my-1 + {:class (when (= i media-queue-pos) "bg-[#f0f0f0] dark:bg-stone-800") + :on-click #(rf/dispatch [::events/change-media-queue-pos i])} + [:div.w-56 + [layout/thumbnail thumbnail-url url name duration {:classes "h-24"}]] + [:div.flex.flex-col.px-4.py-2.w-full + [:h1.line-clamp-1 name] + [:div.text-neutral-600.dark:text-neutral-300.text-sm.flex.flex-col.xs:flex-row + [:span.line-clamp-1 uploader-name] + [:span.px-2.hidden.xs:inline-block {:dangerouslySetInnerHTML {:__html "•"}}] + [:span (util/get-service-name service-id)]]]]) (defn queue [] @@ -36,72 +30,72 @@ paused? @(rf/subscribe [:paused]) media-queue @(rf/subscribe [:media-queue]) media-queue-pos @(rf/subscribe [:media-queue-pos]) - {:keys [uploader-name uploader-url - name stream url service-color] + {:keys [uploader-name uploader-url name stream url service-id] :as current-stream} @(rf/subscribe [:media-queue-stream]) + service-color (and service-id (util/get-service-color service-id)) !elapsed-time @(rf/subscribe [:elapsed-time]) !player @(rf/subscribe [:player]) loop-playback @(rf/subscribe [:loop-playback]) player-ready? (and @!player (> (.-readyState @!player) 0))] (when (and show-media-queue media-queue) - [:div.fixed.flex.flex-col.items-center.px-5.py-2.min-w-full.w-full.z-30 + [:div.fixed.flex.flex-col.items-center.px-5.py-2.min-w-full.w-full.z-10 {:style {:minHeight "calc(100dvh - 56px)" :height "calc(100dvh - 56px)"} :class "dark:bg-neutral-900/90 bg-white/90 backdrop-blur"} - [:div.flex.justify-between.items-center.w-full.shrink-0 + [layout/focus-overlay #(rf/dispatch [::events/toggle-media-queue]) show-media-queue true] + [:div.z-20.w-full.flex.flex-col.flex-auto.h-full {:class "lg:w-4/5 xl:w-3/5"} - [:h1.text-2xl.font-bold.py-6 "Play Queue"] - [:button.mx-2 - [:i.fa-solid.fa-close - {:on-click #(rf/dispatch [::events/toggle-media-queue])}]]] - [:div.flex.flex-col.pr-2.w-full.overflow-y-auto.flex-auto - {:class "lg:w-4/5 xl:w-3/5"} - (for [[i item] (map-indexed vector media-queue)] - ^{:key i} [play-queue-item item media-queue-pos i])] - [:div.flex.flex-col.py-4.w-full.shrink-0 - {:class "lg:w-4/5 xl:w-3/5"} - [:div.flex.flex-col.w-full.py-2 - [:a.text-md.line-clamp-1 - {:href (rfe/href :tubo.routes/stream nil {:url url})} name] - [: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.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 - [player/loop-button loop-playback service-color 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 (or loading? (not @!player)) - [layout/loading-icon service-color "text-3xl"] - (if paused? - [:i.fa-solid.fa-play] - [:i.fa-solid.fa-pause])) - #(rf/dispatch [::events/set-player-paused (not paused?)]) - :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]]]]))) + [:div.flex.justify-between.items-center.shrink-0 + [:h1.text-2xl.font-bold.py-6 "Play Queue"] + [:button.mx-2 + [:i.fa-solid.fa-close + {:on-click #(rf/dispatch [::events/toggle-media-queue])}]]] + [:div.flex.flex-col.pr-2.overflow-y-auto.flex-auto + (for [[i item] (map-indexed vector media-queue)] + ^{:key i} [play-queue-item item media-queue-pos i])] + [:div.flex.flex-col.py-4.shrink-0 + [:div.flex.flex-col.w-full.py-2 + [:a.text-md.line-clamp-1 + {:href (rfe/href :tubo.routes/stream nil {:url url})} name] + [: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.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 + [player/loop-button loop-playback service-color 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-classes "text-xl" + :show-on-mobile? true] + [player/button + [:i.fa-solid.fa-backward] + #(set! (.-currentTime @!player) (- @!elapsed-time 5)) + :extra-classes "text-xl" + :show-on-mobile? true] + [player/button + (if (or loading? (not @!player)) + [layout/loading-icon service-color "text-3xl"] + (if paused? + [:i.fa-solid.fa-play] + [:i.fa-solid.fa-pause])) + #(rf/dispatch [::events/set-player-paused (not paused?)]) + :extra-classes "text-3xl" + :show-on-mobile? true] + [player/button + [:i.fa-solid.fa-forward] + #(set! (.-currentTime @!player) (+ @!elapsed-time 5)) + :extra-classes "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-classes "text-xl" + :show-on-mobile? true]]]]]))) diff --git a/src/frontend/tubo/views/stream.cljs b/src/frontend/tubo/views/stream.cljs index 29f4bb0..93cd831 100644 --- a/src/frontend/tubo/views/stream.cljs +++ b/src/frontend/tubo/views/stream.cljs @@ -1,125 +1,137 @@ (ns tubo.views.stream (:require + [reagent.core :as r] [re-frame.core :as rf] [reitit.frontend.easy :as rfe] [tubo.events :as events] [tubo.components.items :as items] [tubo.components.layout :as layout] [tubo.components.comments :as comments] + [tubo.components.modals.bookmarks :as bookmarks] [tubo.components.video-player :as player] [tubo.util :as util])) (defn stream [match] - (let [{:keys [name url video-streams audio-streams view-count - subscriber-count like-count dislike-count - description uploader-avatar uploader-name - uploader-url upload-date related-streams - thumbnail-url show-comments-loading comments-page - show-comments show-related show-description service-id] - :as stream} @(rf/subscribe [:stream]) - {show-comments? :show-comments - show-related? :show-related - show-description? :show-description} - @(rf/subscribe [:settings]) - available-streams (apply conj audio-streams video-streams) - page-loading? @(rf/subscribe [:show-page-loading]) - service-color @(rf/subscribe [:service-color]) - bookmarks @(rf/subscribe [:bookmarks]) - sources (reverse (map (fn [{:keys [content format resolution averageBitrate]}] - {:src content - :type "video/mp4" - :label (str (or resolution "audio-only") " " - format - (when-not resolution - (str " " averageBitrate "kbit/s")))}) - available-streams)) - player-elements ["playToggle" "progressControl" - "volumePanel" "playbackRateMenuButton" - "QualitySelector" "fullscreenToggle"]] - [layout/content-container - [:div.flex.justify-center.relative - {:class "h-[300px] md:h-[450px] lg:h-[600px]"} - [player/player - {:sources sources - :poster thumbnail-url - :controls true - :controlBar {:children player-elements} - :preload "metadata" - :responsive true - :fill true - :playbackRates [0.5 1 1.5 2]}]] - [:div - [:div.flex.flex-col - [:div.flex.items-center.justify-between.pt-4 - [:div.flex-auto - [:h1.text-lg.sm:text-2xl.font-extrabold.line-clamp-1 name]] - [:div.flex.flex-auto.justify-end.items-center.my-3.ml-4.gap-x-5 - [:button - {:on-click #(rf/dispatch [::events/switch-to-audio-player stream service-color])} - [:i.fa-solid.fa-headphones]] - [:button - [:a.block.sm:inline-block {:href url :target "__blank"} - [:i.fa-solid.fa-external-link-alt]]] - (if (some #(= (:url %) url) bookmarks) - [:button - {:on-click #(rf/dispatch [::events/remove-from-bookmarks stream])} - [:i.fa-solid.fa-bookmark {:style {:color service-color}}]] - [:button - {:on-click #(rf/dispatch [::events/add-to-bookmarks stream])} - [:i.fa-regular.fa-bookmark]])]] - [:div.flex.justify-between.py-2.flex-nowrap - [:div.flex.items-center - [layout/uploader-avatar uploader-avatar uploader-name - (rfe/href :tubo.routes/channel nil {:url uploader-url})] - [:div.mx-3 - [:a.line-clamp-1 {:href (rfe/href :tubo.routes/channel nil {:url uploader-url})} uploader-name] - (when subscriber-count - [:div.flex.my-2.items-center - [:i.fa-solid.fa-users.text-xs] - [:p.mx-2 (util/format-quantity subscriber-count)]])]] - [:div.flex.flex-col.items-end.flex-auto.justify-center - (when view-count - [:div.sm:text-base.text-sm.mb-1 - [:i.fa-solid.fa-eye] - [:span.ml-2 (.toLocaleString view-count)]]) - [:div.flex - (when like-count - [:div.items-center.sm:text-base.text-sm - [:i.fa-solid.fa-thumbs-up] - [:span.ml-2 (.toLocaleString like-count)]]) - (when dislike-count - [:div.ml-2.items-center.sm:text-base.text-sm - [:i.fa-solid.fa-thumbs-down] - [:span.ml-2 dislike-count]])] - (when upload-date - [:div.sm:text-base.text-sm.mt-1.whitespace-nowrap - [:i.fa-solid.fa-calendar] - [:span.ml-2 (util/format-date-string upload-date)]])]] - (when (and show-description? (not (empty? description))) - [layout/show-more-container show-description description - #(rf/dispatch [::events/toggle-stream-layout :show-description])]) - (when (and comments-page (not (empty? (:comments comments-page))) show-comments?) - [layout/accordeon - {:label "Comments" - :on-open #(if show-comments - (rf/dispatch [::events/toggle-stream-layout :show-comments]) - (if comments-page - (rf/dispatch [::events/toggle-stream-layout :show-comments]) - (rf/dispatch [::events/get-comments url]))) - :open? show-comments - :left-icon "fa-solid fa-comments"} - (if show-comments-loading - [layout/loading-icon service-color "text-2xl"] - (when (and show-comments comments-page) - [comments/comments comments-page uploader-name uploader-avatar url]))]) - (when (and show-related? (not (empty? related-streams))) - [layout/accordeon - {:label "Suggested" - :on-open #(rf/dispatch [::events/toggle-stream-layout :show-related]) - :open? (not show-related) - :left-icon "fa-solid fa-list" - :right-button [layout/primary-button "Enqueue" - #(rf/dispatch [::events/enqueue-related-streams related-streams service-color]) - "fa-solid fa-headphones"]} - [items/related-streams related-streams nil]])]]])) + (let [!stream-menu-active? (r/atom nil) + !suggested-menu-active? (r/atom nil)] + (fn [] + (let [{:keys [name url video-streams audio-streams view-count + subscriber-count like-count dislike-count + description uploader-avatar uploader-name + uploader-url upload-date related-streams + thumbnail-url show-comments-loading comments-page + show-comments show-related show-description service-id] + :as stream} @(rf/subscribe [:stream]) + {show-comments? :show-comments + show-related? :show-related + show-description? :show-description} + @(rf/subscribe [:settings]) + available-streams (apply conj audio-streams video-streams) + page-loading? @(rf/subscribe [:show-page-loading]) + service-color @(rf/subscribe [:service-color]) + bookmarks @(rf/subscribe [:bookmarks]) + liked? (some #(= (:url %) url) (-> bookmarks first :items)) + sources (reverse (map (fn [{:keys [content format resolution averageBitrate]}] + {:src content + :type "video/mp4" + :label (str (or resolution "audio-only") " " + format + (when-not resolution + (str " " averageBitrate "kbit/s")))}) + available-streams)) + player-elements ["playToggle" "progressControl" + "volumePanel" "playbackRateMenuButton" + "QualitySelector" "fullscreenToggle"]] + [layout/content-container + [:div.flex.justify-center.relative + {:class "h-[300px] md:h-[450px] lg:h-[600px]"} + [player/player + {:sources sources + :poster thumbnail-url + :controls true + :controlBar {:children player-elements} + :preload "metadata" + :responsive true + :fill true + :playbackRates [0.5 1 1.5 2]}]] + [:div + [:div.flex.flex-col + [:div.flex.items-center.justify-between.pt-4.my-3 + [:div.flex-auto + [:h1.text-lg.sm:text-2xl.font-nunito-bold.line-clamp-1 name]] + [:div.flex.flex-auto.justify-end.items-center + [layout/more-menu !stream-menu-active? + [{:label "Add to queue" + :icon [:i.fa-solid.fa-headphones] + :on-click #(rf/dispatch [::events/switch-to-audio-player stream])} + {:label (if liked? "Remove favorite" "Favorite") + :icon (if liked? + [:i.fa-solid.fa-heart {:style {:color (util/get-service-color service-id)}}] + [:i.fa-regular.fa-heart]) + :on-click #(rf/dispatch [(if liked? ::events/remove-from-likes ::events/add-to-likes) stream])} + {:label "Original" + :link {:route url :external? true} + :icon [:i.fa-solid.fa-external-link-alt]} + {:label "Add to playlist" + :icon [:i.fa-solid.fa-plus] + :on-click #(rf/dispatch [::events/add-bookmark-list-modal + [bookmarks/add-to-bookmark-list-modal stream]])}]]]] + [:div.flex.justify-between.py-2.flex-nowrap + [:div.flex.items-center + [layout/uploader-avatar uploader-avatar uploader-name + (rfe/href :tubo.routes/channel nil {:url uploader-url})] + [:div.mx-3 + [:a.line-clamp-1.font-nunito-semibold + {:href (rfe/href :tubo.routes/channel nil {:url uploader-url})} + uploader-name] + (when subscriber-count + [:div.flex.my-2.items-center + [:i.fa-solid.fa-users.text-xs] + [:p.mx-2 (util/format-quantity subscriber-count)]])]] + [:div.flex.flex-col.items-end.flex-auto.justify-center + (when view-count + [:div.sm:text-base.text-sm.mb-1 + [:i.fa-solid.fa-eye] + [:span.ml-2 (.toLocaleString view-count)]]) + [:div.flex + (when like-count + [:div.items-center.sm:text-base.text-sm + [:i.fa-solid.fa-thumbs-up] + [:span.ml-2 (.toLocaleString like-count)]]) + (when dislike-count + [:div.ml-2.items-center.sm:text-base.text-sm + [:i.fa-solid.fa-thumbs-down] + [:span.ml-2 dislike-count]])] + (when upload-date + [:div.sm:text-base.text-sm.mt-1.whitespace-nowrap + [:i.fa-solid.fa-calendar] + [:span.ml-2 (util/format-date-string upload-date)]])]] + (when (and show-description? (not (empty? description))) + [layout/show-more-container show-description description + #(rf/dispatch [::events/toggle-stream-layout :show-description])]) + (when (and comments-page (not (empty? (:comments comments-page))) show-comments?) + [layout/accordeon + {:label "Comments" + :on-open #(if show-comments + (rf/dispatch [::events/toggle-stream-layout :show-comments]) + (if comments-page + (rf/dispatch [::events/toggle-stream-layout :show-comments]) + (rf/dispatch [::events/get-comments url]))) + :open? show-comments + :left-icon "fa-solid fa-comments"} + (if show-comments-loading + [layout/loading-icon service-color "text-2xl"] + (when (and show-comments comments-page) + [comments/comments comments-page uploader-name uploader-avatar url]))]) + (when (and show-related? (not (empty? related-streams))) + [layout/accordeon + {:label "Suggested" + :on-open #(rf/dispatch [::events/toggle-stream-layout :show-related]) + :open? (not show-related) + :left-icon "fa-solid fa-list" + :right-button [layout/more-menu !suggested-menu-active? + [{:label "Add to queue" + :icon [:i.fa-solid.fa-headphones] + :on-click #(rf/dispatch [::events/enqueue-related-streams related-streams])}]]} + [items/related-streams related-streams nil]])]]])))) |