diff options
author | Miguel Ángel Moreno <mail@migalmoreno.com> | 2024-04-29 01:32:31 +0200 |
---|---|---|
committer | Miguel Ángel Moreno <mail@migalmoreno.com> | 2024-05-29 11:16:14 +0200 |
commit | ee6eda82b113a7f1759b2926aa56216411fc0461 (patch) | |
tree | c5b772dbadd7b7398921425c518bed568418440a /src/frontend | |
parent | 8aacdfddb3d75c99a8b7c5784e3db32be2453d04 (diff) |
feat: add import and export of bookmark lists
Diffstat (limited to 'src/frontend')
-rw-r--r-- | src/frontend/tubo/events.cljs | 93 | ||||
-rw-r--r-- | src/frontend/tubo/views/bookmarks.cljs | 49 |
2 files changed, 127 insertions, 15 deletions
diff --git a/src/frontend/tubo/events.cljs b/src/frontend/tubo/events.cljs index c431daf..5a8bd9a 100644 --- a/src/frontend/tubo/events.cljs +++ b/src/frontend/tubo/events.cljs @@ -4,8 +4,10 @@ [day8.re-frame.http-fx] [goog.object :as gobj] [nano-id.core :refer [nano-id]] + [promesa.core :as p] [reagent.core :as r] [re-frame.core :as rf] + [re-promise.core] [reitit.frontend.easy :as rfe] [reitit.frontend.controllers :as rfc] [tubo.api :as api] @@ -506,6 +508,86 @@ (fn [_ [_ child]] {:fx [[:dispatch [::open-modal child]]]})) +(rf/reg-fx + ::download-file! + (fn [{:keys [data name mime-type]}] + (let [file (.createObjectURL js/URL (js/Blob. (array data) {:type mime-type})) + !link (.createElement js/document "a")] + (set! (.-href !link) file) + (set! (.-download !link) name) + (.click !link) + (.remove !link)))) + +(rf/reg-event-fx + ::add-imported-bookmark-list + (fn [{:keys [db]} [_ index bookmark]] + {:fx (if (= index 0) + (map (fn [s] [:dispatch [::add-to-likes s]]) + (:items bookmark)) + [[:dispatch [::add-bookmark-list bookmark]]])})) + +(rf/reg-event-fx + ::add-streams-to-imported-bookmark-lists + (fn [{:keys [db]} [_ bookmarks]] + {:fx (conj (map-indexed (fn [i b] [:dispatch [::add-imported-bookmark-list i b]]) bookmarks) + [:dispatch [::add-notification + {:status-text "Imported playlists successfully" + :failure :success}]])})) + +(defn fetch-imported-playlists-streams + [bookmarks] + (-> #(-> (p/all (map (fn [stream] + (p/then (js/fetch + (str "/api/v1/streams/" (js/encodeURIComponent stream))) + (fn [res] (.json res)))) + (:items %))) + (p/then (fn [results] + (assoc % :items results)))) + (map bookmarks) + p/all)) + +(rf/reg-event-fx + ::add-imported-bookmark-lists + (fn [{:keys [db]} [_ bookmarks]] + {:promise {:call #(-> (fetch-imported-playlists-streams bookmarks) + (p/then (fn [res] (js->clj res :keywordize-keys true)))) + :on-success-n [[::clear-notifications] + [::add-streams-to-imported-bookmark-lists]]} + :fx [[:dispatch [::add-notification {:status-text "Importing playlists..." :failure :success} false]]]})) + +(rf/reg-fx + ::import-bookmark-list + (fn [file] + (-> (.text file) + (p/then + #(let [res (js->clj (.parse js/JSON %) :keywordize-keys true)] + (if (= (:format res) "Tubo") + (rf/dispatch [::add-imported-bookmark-lists (:playlists res)]) + (throw (js/Error. "Format not supported"))))) + (p/catch js/Error + (fn [error] + (rf/dispatch [::add-notification {:status-text (.-message error) :failure :error}])))))) + +(rf/reg-event-fx + ::import-bookmark-lists + (fn [{:keys [db]} [_ files]] + {:fx (map (fn [file] [::import-bookmark-list file]) files)})) + +(rf/reg-event-fx + ::export-bookmark-lists + (fn [{:keys [db]} [_]] + {::download-file! + {:name "playlists.json" + :mime-type "application/json" + :data (.stringify js/JSON (clj->js {:format "Tubo" + :version 1 + :playlists + (map (fn [bookmark] + {:name (:name bookmark) + :items (map :url (:items bookmark))}) + (:bookmarks db))}))} + :fx [[:dispatch [::add-notification {:status-text "Exported playlists" :failure :success}]]]})) + (rf/reg-event-fx ::add-bookmark-list [(rf/inject-cofx :store)] @@ -545,6 +627,17 @@ [])}))) (rf/reg-event-fx + ::clear-bookmark-lists + (fn [{:keys [db]} _] + {:fx (apply merge + (map (fn [b] [:dispatch [::remove-bookmark-list (:id b)]]) (rest (:bookmarks db))) + (conj + (map (fn [s] [:dispatch [::remove-from-likes s]]) (:items (first (:bookmarks db)))) + [:dispatch [::add-notification + {:status-text "Cleared all playlists" + :failure :success}]]))})) + +(rf/reg-event-fx ::add-to-likes [(rf/inject-cofx :store)] (fn [{:keys [db store]} [_ bookmark notify?]] diff --git a/src/frontend/tubo/views/bookmarks.cljs b/src/frontend/tubo/views/bookmarks.cljs index e1a0ed8..2e14a3a 100644 --- a/src/frontend/tubo/views/bookmarks.cljs +++ b/src/frontend/tubo/views/bookmarks.cljs @@ -6,6 +6,7 @@ [tubo.components.items :as items] [tubo.components.layout :as layout] [tubo.components.modal :as modal] + [tubo.components.modals.bookmarks :as bookmarks] [tubo.events :as events])) (defn add-bookmark-modal @@ -18,25 +19,43 @@ [layout/secondary-button "Cancel" #(rf/dispatch [::events/close-modal])] [layout/primary-button "Create Playlist" - #(rf/dispatch [::events/add-bookmark-list {:name @!bookmark-name}])]]))) + #(rf/dispatch [::events/add-bookmark-list {:name @!bookmark-name} true])]]))) (defn bookmarks-page [] (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/popover-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]]))) + (fn [] + (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/popover-menu !menu-active? + [{:label "Add New" + :icon [:i.fa-solid.fa-plus] + :on-click #(rf/dispatch [::events/open-modal [add-bookmark-modal]])} + [:<> + [:input.hidden + {:id "file-selector" + :type "file" + :multiple "multiple" + :on-click #(reset! !menu-active? true) + :on-change #(rf/dispatch [::events/import-bookmark-lists (.. % -target -files)])}] + [:label.whitespace-nowrap.cursor-pointer.w-full.h-full.absolute.right-0.top-0 + {:for "file-selector"}] + [:span.text-xs.w-10.min-w-4.w-4.flex.items-center [:i.fa-solid.fa-file-import]] + [:span "Import"]] + {:label "Export" + :icon [:i.fa-solid.fa-file-export] + :on-click #(rf/dispatch [::events/export-bookmark-lists])} + {:label "Clear All" + :icon [:i.fa-solid.fa-trash] + :on-click #(rf/dispatch [::events/clear-bookmark-lists])}]]] + [items/related-streams items]])))) (defn bookmark-page [] |