diff options
Diffstat (limited to 'src/frontend')
-rw-r--r-- | src/frontend/tubo/components/items.cljs | 170 | ||||
-rw-r--r-- | src/frontend/tubo/components/layout.cljs | 19 |
2 files changed, 99 insertions, 90 deletions
diff --git a/src/frontend/tubo/components/items.cljs b/src/frontend/tubo/components/items.cljs index c207a04..3b9b7ca 100644 --- a/src/frontend/tubo/components/items.cljs +++ b/src/frontend/tubo/components/items.cljs @@ -1,99 +1,89 @@ (ns tubo.components.items (:require + [reagent.core :as r] [re-frame.core :as rf] [reitit.frontend.easy :as rfe] [tubo.components.layout :as layout] + [tubo.components.modal :as modal] + [tubo.components.modals.bookmarks :as bookmarks] [tubo.events :as events] [tubo.util :as util])) -(defn thumbnail - [thumbnail-url route url name duration & {:keys [classes] :or {classes "h-44 xs:h-28"}}] - [:div.flex.py-2.box-border {:class classes} - [:div.relative.min-w-full - [:a.absolute.min-w-full.min-h-full.z-10 {:href route :title name}] - [:img.rounded.object-cover.min-h-full.max-h-full.min-w-full {:src thumbnail-url}] - (when duration - [:div.rounded.p-2.absolute {:style {:bottom 5 :right 5 :background "rgba(0,0,0,.7)" :zIndex "0"}} - [:p.text-white {:style {:fontSize "14px"}} - (if (= duration 0) - "LIVE" - (util/format-duration duration))]])]]) - (defn item-content - [{:keys [type url name thumbnail-url description subscriber-count - stream-count verified? key uploader-name uploader-url - uploader-avatar upload-date short-description view-count - duration audio-streams video-streams] :as item} - item-route service-color bookmarks] - (let [stream? (or (= type "stream") audio-streams video-streams)] - [:<> - (when name - [:div.flex.items-center.my-2 - [:a {:href item-route :title name} - [:h1.line-clamp-2.my-1 {:class "[overflow-wrap:anywhere]"} name]] - (when (and verified? (not uploader-url)) - [:i.fa-solid.fa-circle-check.pl-2])]) - [:div.flex.justify-between - [:div.flex.items-center.my-2 - (if uploader-url - [:a {:href (rfe/href :tubo.routes/channel nil {:url uploader-url}) :title uploader-name} - [:h1.line-clamp-1.text-neutral-800.dark:text-gray-300.font-bold.pr-2.break-all - {:class "[overflow-wrap:anywhere]"} - uploader-name]] - [:h1.line-clamp-1.text-neutral-800.dark:text-gray-300.font-bold.pr-2 uploader-name]) - (when (and uploader-url verified?) - [:i.fa-solid.fa-circle-check])] - [:div.flex.items-center - (when stream? - [:button.ml-2.focus:outline-none - {:on-click #(rf/dispatch [::events/switch-to-audio-player item service-color])} - [:i.fa-solid.fa-headphones]]) - (when (some #{(dissoc item :key)} bookmarks) - [:button.ml-4.focus:outline-none - {:on-click #(rf/dispatch [::events/remove-from-bookmarks (dissoc item :key)])} - [:i.fa-solid.fa-trash]])]] - (when (and subscriber-count (not stream?)) - [:div.flex.items-center - [:i.fa-solid.fa-users.text-xs] - [:p.mx-2 (util/format-quantity subscriber-count)]]) - (when stream-count - [:div.flex.items-center - [:i.fa-solid.fa-video.text-xs] - [:p.mx-2 (util/format-quantity stream-count)]]) - [:div.flex.my-1.justify-between - [:p (util/format-date-ago upload-date)] - (when view-count - [:div.flex.items-center.h-full.pl-2 - [:i.fa-solid.fa-eye.text-xs] - [:p.pl-1.5 (util/format-quantity view-count)]])]])) - -(defn stream-item - [{:keys [url name thumbnail-url duration] :as item} service-color bookmarks] - [:<> - [thumbnail thumbnail-url (rfe/href :tubo.routes/stream nil {:url url}) url name duration] - [item-content item (rfe/href :tubo.routes/stream nil {:url url}) service-color bookmarks]]) - -(defn channel-item - [{:keys [url name thumbnail-url] :as item} service-color bookmarks] - [:<> - [thumbnail thumbnail-url (rfe/href :tubo.routes/channel nil {:url url}) url name nil] - [item-content item (rfe/href :tubo.routes/channel nil {:url url}) service-color bookmarks]]) - -(defn playlist-item - [{:keys [url name thumbnail-url] :as item} service-color bookmarks] - [:<> - [thumbnail thumbnail-url (rfe/href :tubo.routes/playlist nil {:url url}) url name nil] - [item-content item (rfe/href :tubo.routes/playlist nil {:url url}) service-color bookmarks]]) + [{:keys [audio-streams video-streams type service-id bookmark-id url] :as item} item-route bookmarks] + (let [!menu-active? (r/atom false)] + (fn [{:keys [type service-id url name thumbnail-url description subscriber-count + stream-count verified? uploader-name uploader-url + uploader-avatar upload-date short-description view-count + duration audio-streams video-streams bookmark-id] :as item} item-route bookmarks] + (let [stream? (or (= type "stream") audio-streams video-streams) + liked? (some #(= (:url %) url) (-> bookmarks first :items)) + items (if stream? + [{:label "Add to queue" + :icon [:i.fa-solid.fa-headphones] + :on-click #(rf/dispatch [::events/switch-to-audio-player item])} + {: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) item])} + (if (some #(= (:url %) url) (:items (first (filter #(= (:id %) bookmark-id) bookmarks)))) + {:label "Remove from playlist" + :icon [:i.fa-solid.fa-trash] + :on-click #(rf/dispatch [::events/remove-from-bookmark-list item])} + {: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 item]])})] + [(when (and bookmarks (some #(= (:id %) bookmark-id) (rest bookmarks))) + {:label "Remove playlist" + :icon [:i.fa-solid.fa-trash] + :on-click #(rf/dispatch [::events/remove-bookmark-list bookmark-id])})])] + [:<> + (when name + [:div.flex.items-center.my-2 + [:a {:href item-route :title name} + [:h1.line-clamp-2.my-1 {:class "[overflow-wrap:anywhere]"} name]] + (when (and verified? (not uploader-url)) + [:i.fa-solid.fa-circle-check.pl-2])]) + [:div.flex.justify-between + [:div.flex.items-center.my-2 + (if uploader-url + [:a {:href (rfe/href :tubo.routes/channel nil {:url uploader-url}) :title uploader-name} + [:h1.line-clamp-1.text-neutral-800.dark:text-gray-300.font-bold.pr-2.break-all + {:class "[overflow-wrap:anywhere]"} + uploader-name]] + [:h1.line-clamp-1.text-neutral-800.dark:text-gray-300.font-bold.pr-2 uploader-name]) + (when (and uploader-url verified?) + [:i.fa-solid.fa-circle-check])] + (when-not (empty? (remove nil? items)) + [layout/more-menu !menu-active? items])] + (when (and subscriber-count (not stream?)) + [:div.flex.items-center + [:i.fa-solid.fa-users.text-xs] + [:p.mx-2 (util/format-quantity subscriber-count)]]) + (when stream-count + [:div.flex.items-center + [:i.fa-solid.fa-video.text-xs] + [:p.mx-2 (util/format-quantity stream-count)]]) + [:div.flex.my-1.justify-between + [:p (util/format-date-ago upload-date)] + (when view-count + [:div.flex.items-center.h-full.pl-2 + [:i.fa-solid.fa-eye.text-xs] + [:p.pl-1.5 (util/format-quantity view-count)]])]])))) (defn generic-item - [item service-color bookmarks] - [:div.w-full.h-80.xs:h-72 {:key key} - [:div.flex.flex-col.max-w-full.min-h-full.max-h-full - (case (:type item) - "stream" [stream-item item service-color bookmarks] - "channel" [channel-item item service-color bookmarks] - "playlist" [playlist-item item service-color bookmarks] - [stream-item item service-color bookmarks])]]) + [{:keys [url name thumbnail-url duration] :as item} bookmarks] + (let [item-url (case (:type item) + "stream" (rfe/href :tubo.routes/stream nil {:url url}) + "channel" (rfe/href :tubo.routes/channel nil {:url url}) + "playlist" (rfe/href :tubo.routes/playlist nil {:url url}) + url)] + [:div.w-full + [:div.flex.flex-col.max-w-full.min-h-full.max-h-full + [layout/thumbnail thumbnail-url item-url name duration] + [item-content item item-url bookmarks]]])) (defn related-streams [related-streams next-page-url] @@ -101,12 +91,14 @@ pagination-loading? @(rf/subscribe [:show-pagination-loading]) bookmarks @(rf/subscribe [:bookmarks])] [:div.flex.flex-col.items-center.flex-auto.my-2.md:my-8 + [modal/modal] (if (empty? related-streams) - [:div.flex.items-center.flex-auto - [:p "No available streams"]] - [:div.grid.w-full.gap-x-10.gap-y-5 + [:div.flex.items-center.flex-auto.flex-col.justify-center.gap-y-4 + [:i.fa-solid.fa-ghost.text-3xl] + [:p.text-lg "No available streams"]] + [:div.grid.w-full.gap-x-10.gap-y-6 {:class "xs:grid-cols-[repeat(auto-fill,_minmax(165px,_1fr))]"} (for [[i item] (map-indexed vector related-streams)] - ^{:key i} [generic-item item service-color bookmarks])]) + ^{:key i} [generic-item item bookmarks])]) (when-not (empty? next-page-url) [layout/loading-icon service-color "text-2xl" (when-not pagination-loading? "invisible")])])) diff --git a/src/frontend/tubo/components/layout.cljs b/src/frontend/tubo/components/layout.cljs index 2dc552f..19adcc1 100644 --- a/src/frontend/tubo/components/layout.cljs +++ b/src/frontend/tubo/components/layout.cljs @@ -1,7 +1,24 @@ (ns tubo.components.layout (:require [reagent.core :as r] - [re-frame.core :as rf])) + [re-frame.core :as rf] + [tubo.util :as util])) + +(defn thumbnail + [thumbnail-url route name duration & {:keys [classes] :or {classes "h-44 xs:h-28"}}] + [:div.flex.py-2.box-border {:class classes} + [:div.relative.min-w-full + [:a.absolute.min-w-full.min-h-full.z-10 {:href route :title name}] + (if thumbnail-url + [:img.rounded.object-cover.min-h-full.max-h-full.min-w-full {:src thumbnail-url}] + [:div.bg-gray-300.flex.min-h-full.min-w-full.justify-center.items-center.rounded + [:i.fa-solid.fa-image.text-3xl.text-white]]) + (when duration + [:div.rounded.p-2.absolute {:style {:bottom 5 :right 5 :background "rgba(0,0,0,.7)" :zIndex "0"}} + [:p.text-white {:style {:fontSize "14px"}} + (if (= duration 0) + "LIVE" + (util/format-duration duration))]])]]) (defn logo [] [:img.mb-1 |