aboutsummaryrefslogtreecommitdiff
path: root/src/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend')
-rw-r--r--src/frontend/tubo/components/items.cljs170
-rw-r--r--src/frontend/tubo/components/layout.cljs19
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