aboutsummaryrefslogtreecommitdiff
path: root/src/frontend
diff options
context:
space:
mode:
authorMiguel Ángel Moreno <mail@migalmoreno.com>2024-02-18 17:29:24 +0100
committerMiguel Ángel Moreno <mail@migalmoreno.com>2024-02-18 17:29:24 +0100
commit5cfde61805292f4b89c1dc244f8fbdf15c5c687d (patch)
tree8f9ab93d39fdb6a37556ebc72972bbfcb860c00d /src/frontend
parenta2c45c2747459a97766bff7ec731f2b73a03fed4 (diff)
feat(frontend): add support for bookmark lists (playlists)
Diffstat (limited to 'src/frontend')
-rw-r--r--src/frontend/tubo/components/modals/bookmarks.cljs44
-rw-r--r--src/frontend/tubo/components/navigation.cljs2
-rw-r--r--src/frontend/tubo/events.cljs81
-rw-r--r--src/frontend/tubo/routes.cljs7
-rw-r--r--src/frontend/tubo/views/bookmarks.cljs54
5 files changed, 174 insertions, 14 deletions
diff --git a/src/frontend/tubo/components/modals/bookmarks.cljs b/src/frontend/tubo/components/modals/bookmarks.cljs
new file mode 100644
index 0000000..c240d85
--- /dev/null
+++ b/src/frontend/tubo/components/modals/bookmarks.cljs
@@ -0,0 +1,44 @@
+(ns tubo.components.modals.bookmarks
+ (:require
+ [reagent.core :as r]
+ [re-frame.core :as rf]
+ [reitit.frontend.easy :as rfe]
+ [tubo.components.modal :as modal]
+ [tubo.components.layout :as layout]))
+
+(defn bookmark-list-item
+ [{:keys [items id name] :as bookmark} item]
+ [:div.flex.w-full.h-24.rounded.cursor-pointer.hover:bg-gray-100.dark:hover:bg-stone-800.px-2
+ {:on-click #(rf/dispatch [:tubo.events/add-to-bookmark-list bookmark item])}
+ [:div.w-24
+ [layout/thumbnail (-> items first :thumbnail-url) nil name nil
+ :classes "h-24"]]
+ [:div.flex.flex-col.py-4.px-4
+ [:h1.line-clamp-1.font-bold name]
+ [:span.text-sm (str (count items) " streams")]]])
+
+(defn add-bookmark-modal
+ [item]
+ (let [!bookmark-name (r/atom "")]
+ (fn []
+ [modal/modal-content "Create New Playlist?"
+ [layout/text-input "Title" :text-input @!bookmark-name
+ #(reset! !bookmark-name (.. % -target -value)) "Playlist name"]
+ [layout/secondary-button "Back"
+ #(rf/dispatch [:tubo.events/back-to-bookmark-list-modal item])]
+ [layout/primary-button "Create Playlist"
+ #(rf/dispatch [:tubo.events/add-bookmark-list-and-back {:name @!bookmark-name} item])
+ "fa-solid fa-plus"]])))
+
+(defn add-to-bookmark-list-modal
+ [item]
+ (let [bookmarks @(rf/subscribe [:bookmarks])]
+ [modal/modal-content "Add to Playlist"
+ [:div.flex-auto
+ [:div.flex.justify-center.items-center.pb-4
+ [layout/primary-button "Create New Playlist"
+ #(rf/dispatch [:tubo.events/open-modal [add-bookmark-modal item]])
+ "fa-solid fa-plus"]]
+ [:div.flex.flex-col.gap-y-2.pr-2
+ (for [[i bookmark] (map-indexed vector bookmarks)]
+ ^{:key i} [bookmark-list-item bookmark item])]]]))
diff --git a/src/frontend/tubo/components/navigation.cljs b/src/frontend/tubo/components/navigation.cljs
index eeeb665..3f5b508 100644
--- a/src/frontend/tubo/components/navigation.cljs
+++ b/src/frontend/tubo/components/navigation.cljs
@@ -130,7 +130,7 @@
[:div.relative.dark:border-neutral-800.border-gray-300.pt-4
{:class "border-t-[1px]"}
[:ul.flex.flex-col.font-roboto
- [mobile-nav-item (rfe/href ::routes/playlists) "fa-solid fa-bookmark" "Bookmarks"]
+ [mobile-nav-item (rfe/href ::routes/bookmarks) "fa-solid fa-bookmark" "Bookmarks"]
[mobile-nav-item (rfe/href ::routes/settings) "fa-solid fa-cog" "Settings"]
[mobile-nav-item "https://github.com/migalmoreno/tubo"
"fa-brands fa-github" "Source" :new-tab? true]]]]])
diff --git a/src/frontend/tubo/events.cljs b/src/frontend/tubo/events.cljs
index e155881..827dec5 100644
--- a/src/frontend/tubo/events.cljs
+++ b/src/frontend/tubo/events.cljs
@@ -3,10 +3,12 @@
[akiroz.re-frame.storage :refer [reg-co-fx!]]
[day8.re-frame.http-fx]
[goog.object :as gobj]
+ [nano-id.core :refer [nano-id]]
[re-frame.core :as rf]
[reitit.frontend.easy :as rfe]
[reitit.frontend.controllers :as rfc]
[tubo.api :as api]
+ [tubo.components.modals.bookmarks :as bookmarks]
[vimsical.re-frame.cofx.inject :as inject]))
(reg-co-fx! :tubo {:fx :store :cofx :store})
@@ -30,7 +32,11 @@
:media-queue (if (nil? media-queue) [] media-queue)
:media-queue-pos (if (nil? media-queue-pos) 0 media-queue-pos)
:volume-level (if (nil? volume-level) 100 volume-level)
- :bookmarks (if (nil? bookmarks) [] bookmarks)
+ :bookmarks (if (nil? bookmarks)
+ [{:id (nano-id)
+ :name "Liked Streams"
+ :items []}]
+ bookmarks)
:muted (if (nil? muted) false muted)
:current-match nil
:show-audio-player (if (nil? show-audio-player) false show-audio-player)
@@ -415,18 +421,77 @@
{:fx [[:dispatch [::modal {:show? true :child child}]]]
::body-overflow! true}))
+(rf/reg-event-fx
+ ::add-bookmark-list-modal
+ (fn [_ [_ child]]
+ {:fx [[:dispatch [::open-modal child]]]}))
+
+(rf/reg-event-fx
+ ::add-bookmark-list
+ [(rf/inject-cofx :store)]
+ (fn [{:keys [db store]} [_ bookmark]]
+ (let [updated-db (update db :bookmarks conj (assoc bookmark :id (nano-id)))]
+ {:db updated-db
+ :store (assoc store :bookmarks (:bookmarks updated-db))
+ :fx [[:dispatch [::close-modal]]]})))
+
+(rf/reg-event-fx
+ ::back-to-bookmark-list-modal
+ (fn [_ [_ item]]
+ {:fx [[:dispatch [::open-modal [bookmarks/add-to-bookmark-list-modal item]]]]}))
+
+(rf/reg-event-fx
+ ::add-bookmark-list-and-back
+ (fn [_ [_ bookmark item]]
+ {:fx [[:dispatch [::add-bookmark-list bookmark]]
+ [:dispatch [::back-to-bookmark-list-modal item]]]}))
+
+(rf/reg-event-fx
+ ::remove-bookmark-list
+ [(rf/inject-cofx :store)]
+ (fn [{:keys [db store]} [_ id]]
+ (let [updated-db (update db :bookmarks #(into [] (remove (fn [bookmark] (= (:id bookmark) id)) %)))]
+ {:db updated-db
+ :store (assoc store :bookmarks (:bookmarks updated-db))})))
+
+(rf/reg-event-fx
+ ::add-to-likes
[(rf/inject-cofx :store)]
(fn [{:keys [db store]} [_ bookmark]]
- (when-not (some #(= (:url %) (:url bookmark)) (:bookmarks db))
- (let [updated-db (update db :bookmarks conj bookmark)]
+ (when-not (some #(= (:url %) (:url bookmark)) (-> db :bookmarks first :items))
+ (let [updated-db (update-in db [:bookmarks 0 :items] #(into [] (conj (into [] %1) %2))
+ (assoc bookmark :bookmark-id (-> db :bookmarks first :id)))]
{:db updated-db
:store (assoc store :bookmarks (:bookmarks updated-db))}))))
(rf/reg-event-fx
- ::remove-from-bookmarks
+ ::remove-from-likes
[(rf/inject-cofx :store)]
(fn [{:keys [db store]} [_ bookmark]]
- (let [updated-db (update db :bookmarks #(remove (fn [item] (= (:url item) (:url bookmark))) %))]
+ (let [updated-db (update-in db [:bookmarks 0 :items] #(remove (fn [item] (= (:url item) (:url bookmark))) %))]
+ {:db updated-db
+ :store (assoc store :bookmarks (:bookmarks updated-db))})))
+
+(rf/reg-event-fx
+ ::add-to-bookmark-list
+ [(rf/inject-cofx :store)]
+ (fn [{:keys [db store]} [_ bookmark item]]
+ (let [bookmark-list (first (filter #(= (:id %) (:id bookmark)) (:bookmarks db)))
+ pos (.indexOf (:bookmarks db) bookmark-list)
+ updated-db (if (some #(= (:url %) (:url item)) (:items bookmark-list))
+ db
+ (update-in db [:bookmarks pos :items] #(into [] (conj (into [] %1) %2))
+ (assoc item :bookmark-id (:id bookmark))))]
+ {:db updated-db
+ :store (assoc store :bookmarks (:bookmarks updated-db))
+ :fx [[:dispatch [::close-modal]]]})))
+
+(rf/reg-event-fx
+ ::remove-from-bookmark-list
+ [(rf/inject-cofx :store)]
+ (fn [{:keys [db store]} [_ bookmark]]
+ (let [bookmark-list (.indexOf (:bookmarks db) (first (filter #(= (:id %) (:bookmark-id bookmark)) (:bookmarks db))))
+ updated-db (update-in db [:bookmarks bookmark-list :items] #(remove (fn [item] (= (:url item) (:url bookmark))) %))]
{:db updated-db
:store (assoc store :bookmarks (:bookmarks updated-db))})))
@@ -755,3 +820,9 @@
::get-bookmarks-page
(fn [_]
{::document-title! "Bookmarks"}))
+
+(rf/reg-event-fx
+ ::get-bookmark-page
+ (fn [{:keys [db]} [_ playlist-id]]
+ (let [playlist (first (filter #(= (:id %) playlist-id) (:bookmarks db)))]
+ {::document-title! (:name playlist)})))
diff --git a/src/frontend/tubo/routes.cljs b/src/frontend/tubo/routes.cljs
index 6bfb5dc..337a6d6 100644
--- a/src/frontend/tubo/routes.cljs
+++ b/src/frontend/tubo/routes.cljs
@@ -46,8 +46,13 @@
["/settings" {:view settings/settings-page
:name ::settings
:controllers [{:start #(rf/dispatch [::events/get-settings-page])}]}]
+ ["/bookmark" {:view bookmarks/bookmark-page
+ :name ::bookmark
+ :controllers [{:parameters {:query [:id]}
+ :start (fn [{{:keys [id]} :query}]
+ (rf/dispatch [::events/get-bookmark-page id]))}]}]
["/bookmarks" {:view bookmarks/bookmarks-page
- :name ::playlists
+ :name ::bookmarks
:controllers [{:start #(rf/dispatch [::events/get-bookmarks-page])}]}]]))
(defn on-navigate
diff --git a/src/frontend/tubo/views/bookmarks.cljs b/src/frontend/tubo/views/bookmarks.cljs
index 3f47c02..78959c4 100644
--- a/src/frontend/tubo/views/bookmarks.cljs
+++ b/src/frontend/tubo/views/bookmarks.cljs
@@ -1,16 +1,56 @@
(ns tubo.views.bookmarks
(:require
+ [reagent.core :as r]
[re-frame.core :as rf]
+ [reitit.frontend.easy :as rfe]
[tubo.components.items :as items]
[tubo.components.layout :as layout]
+ [tubo.components.modal :as modal]
[tubo.events :as events]))
+(defn add-bookmark-modal
+ []
+ (let [!bookmark-name (r/atom "")]
+ (fn []
+ [modal/modal-content "Create New Playlist?"
+ [layout/text-input "Title" :text-input @!bookmark-name
+ #(reset! !bookmark-name (.. % -target -value)) "Playlist name"]
+ [layout/secondary-button "Cancel"
+ #(rf/dispatch [::events/close-modal])]
+ [layout/primary-button "Create Playlist"
+ #(rf/dispatch [::events/add-bookmark-list {:name @!bookmark-name}])]])))
+
(defn bookmarks-page
[]
- (let [service-color @(rf/subscribe [:service-color])
- bookmarks @(rf/subscribe [:bookmarks])]
- [layout/content-container
- [layout/content-header "Bookmarks"
- [layout/primary-button "Enqueue"
- #(rf/dispatch [::events/enqueue-related-streams bookmarks service-color]) "fa-solid fa-headphones"]]
- [items/related-streams bookmarks]]))
+ (let [!menu-active? (r/atom nil)]
+ (let [service-color @(rf/subscribe [:service-color])
+ bookmarks @(rf/subscribe [:bookmarks])
+ items (map #(assoc %
+ :url (rfe/href :tubo.routes/bookmark nil {:id (:id %)})
+ :thumbnail-url (-> % :items first :thumbnail-url)
+ :stream-count (count (:items %))
+ :bookmark-id (:id %)) bookmarks)]
+ [layout/content-container
+ [layout/content-header "Bookmarks"
+ [layout/more-menu !menu-active?
+ [{:label "Create playlist"
+ :icon [:i.fa-solid.fa-plus]
+ :on-click #(rf/dispatch [::events/open-modal [add-bookmark-modal]])}]]]
+ [items/related-streams items]])))
+
+(defn bookmark-page
+ []
+ (let [!menu-active? (r/atom nil)]
+ (fn []
+ (let [bookmarks @(rf/subscribe [:bookmarks])
+ service-color @(rf/subscribe [:service-color])
+ {{:keys [id]} :query-params} @(rf/subscribe [:current-match])
+ {:keys [items name]} (first (filter #(= (:id %) id) bookmarks))]
+ [layout/content-container
+ [layout/content-header name
+ (when-not (empty? items)
+ [layout/more-menu !menu-active?
+ [{:label "Add to queue"
+ :icon [:i.fa-solid.fa-headphones]
+ :on-click #(rf/dispatch [::events/enqueue-related-streams items])}]])]
+ [items/related-streams (map #(assoc % :type "stream" :bookmark-id id) items)]]))))