diff --git a/src/cljs/airsonic_ui/components/collection/views.cljs b/src/cljs/airsonic_ui/components/collection/views.cljs index de43d33..3070731 100644 --- a/src/cljs/airsonic_ui/components/collection/views.cljs +++ b/src/cljs/airsonic_ui/components/collection/views.cljs @@ -54,29 +54,36 @@ [dropdown {:items [{:label "Play next" :event [:audio-player/enqueue-next song]} {:label "Play last" :event [:audio-player/enqueue-last song]}]}]) -(defn song-table [songs] +(defn default-thead [] + [:thead>tr + [:td.is-narrow] + [:td.song-artist "Artist"] + [:td.song-title "Title"] + [:td.song-duration "Duration"] + [:td.is-narrow]]) + +(defn default-tbody [{:keys [songs current-song]}] + [:tbody + (for [[idx song] (map-indexed vector songs)] + ^{:key idx} + [(if (= (:id song) (:id current-song)) :tr.is-playing :tr) + [:td.song-tracknr.is-narrow (:track song)] + [:td.song-artist [artist-link song]] + [:td.song-title [song-link {:songs songs + :song song + :idx idx}]] + [:td.song-duration (h/format-duration (:duration song) :brief? true)] + [:td.song-actions.is-narrow [song-actions song]]])]) + +(defn song-table [{:keys [songs thead tbody] + :or {thead default-thead, tbody default-tbody}}] ;; we subscribe here instead of one level higher up to make this a more ;; reusable component; this way we can for example get a list of all songs ;; in a search result and easily highlight the currently playing track (let [current-song @(subscribe [:audio/current-song])] [:table.song-listing-table.table.is-fullwidth - [:thead>tr - [:td.is-narrow] - [:td.song-artist "Artist"] - [:td.song-title "Title"] - [:td.song-duration "Duration"] - [:td.is-narrow]] - [:tbody - (for [[idx song] (map-indexed vector songs)] - ^{:key idx} - [(if (= (:id song) (:id current-song)) :tr.is-playing :tr) - [:td.song-tracknr.is-narrow (:track song)] - [:td.song-artist [artist-link song]] - [:td.song-title [song-link {:songs songs - :song song - :idx idx}]] - [:td.song-duration (h/format-duration (:duration song) :brief? true)] - [:td.song-actions.is-narrow [song-actions song]]])]])) + [thead] + [tbody {:songs songs, :current-song current-song}]])) (defn detail "Shows a detail view of a single album, listing all " @@ -91,4 +98,4 @@ [:h3.subtitle (:artist album)] [collection-info album]]]]] [:section.section>div.container - [song-table (:song album)]]]) + [song-table {:songs (:song album)}]]]) diff --git a/src/cljs/airsonic_ui/components/current_queue/views.cljs b/src/cljs/airsonic_ui/components/current_queue/views.cljs index 58f68b4..5ee6cee 100644 --- a/src/cljs/airsonic_ui/components/current_queue/views.cljs +++ b/src/cljs/airsonic_ui/components/current_queue/views.cljs @@ -5,6 +5,7 @@ [bulma.icon :refer [icon]] [bulma.dropdown.views :refer [dropdown]] [airsonic-ui.helpers :as helpers] + [airsonic-ui.components.collection.views :as collection] [airsonic-ui.components.sortable.views :as sortable] [airsonic-ui.routes :as routes] @@ -38,35 +39,45 @@ :on-click (helpers/muted-dispatch [:audio-player/set-current-song idx])} (:title song)]) +(defn song-table-head [] + [:thead>tr + [:td.is-narrow] + [:td.song-artist "Artist"] + [:td.song-title "Title"] + [:td.song-duration "Duration"] + [:td.song-actions.is-narrow]]) + +(defn song-table-sortable-tbdoy [{:keys [songs current-song-idx]}] + ;; we need this closure to pass in custom arguments (current-song-idx) + (fn [] + [sortable/sortable-component + {:items songs + :container [:tbody] + :helper-class "sortable-is-moving" + + :render-item + (fn [{[idx song] :value}] + [(if (= idx current-song-idx) :tr.is-playing :tr) + [:td.sortable-handle.is-narrow [:> SortHandle]] + [:td.song-artist [artist-link song]] + [:td.song-title [song-link song idx]] + [:td.song-duration (helpers/format-duration (:duration song) :brief? true)] + [:td.song-actions.is-narrow [song-actions]]]) + + :on-sort-end + (fn [{:keys [old-idx new-idx]}] + ;; if we don't dispatch-sync, the UI sometimes places the row back and + ;; resorts it a litle later + (dispatch-sync [:audio-player/move-song old-idx new-idx]))}])) + (defn song-table [{:keys [songs current-song-idx]}] - [:table.song-listing-table.table.is-fullwidth - [:thead>tr - [:td.is-narrow] - [:td.song-artist "Artist"] - [:td.song-title "Title"] - [:td.song-duration "Duration"] - [:td.song-actions.is-narrow]] - [sortable/sortable-component - {:items songs - :container [:tbody] - :helper-class "sortable-is-moving" + [collection/song-table + {:songs songs + :thead song-table-head + :tbody (song-table-sortable-tbdoy {:songs songs + :current-song-idx current-song-idx})}]) - :render-item - (fn [{[idx song] :value}] - [(if (= idx current-song-idx) :tr.is-playing :tr) - [:td.sortable-handle.is-narrow [:> SortHandle]] - [:td.song-artist [artist-link song]] - [:td.song-title [song-link song idx]] - [:td.song-duration (helpers/format-duration (:duration song) :brief? true)] - [:td.song-actions.is-narrow [song-actions]]]) - - :on-sort-end - (fn [{:keys [old-idx new-idx]}] - ;; if we don't dispatch-sync, the UI sometimes places the row back and - ;; resorts it a litle later - (dispatch-sync [:audio-player/move-song old-idx new-idx]))}]]) - -(defn collection-info [{:keys [playlist-info]}] +(defn queue-info [{:keys [playlist-info]}] [:ul.is-smaller.collection-info [:li [icon :audio-spectrum] (str (:count playlist-info) (if (pos? (:count playlist-info)) @@ -76,7 +87,7 @@ (defn playlist [props] [:div - [collection-info props] + [queue-info props] [song-table {:songs (get-in props [:current-playlist :items]) :current-song-idx (get-in props [:current-playlist :current-idx])}]]) diff --git a/src/cljs/airsonic_ui/components/search/views.cljs b/src/cljs/airsonic_ui/components/search/views.cljs index 6041711..4fb1d70 100644 --- a/src/cljs/airsonic_ui/components/search/views.cljs +++ b/src/cljs/airsonic_ui/components/search/views.cljs @@ -2,13 +2,15 @@ (:require [re-frame.core :refer [dispatch subscribe]] [goog.functions :refer [debounce]] [airsonic-ui.routes :as routes :refer [url-for]] - [airsonic-ui.components.collection.views :refer [song-table]] - [airsonic-ui.components.debug :refer [debug]] + [airsonic-ui.helpers :as h] + [airsonic-ui.components.collection.views :as collection] [airsonic-ui.views.cover :refer [card]])) +(def search + (debounce #(dispatch [:search/do-search (.. % -target -value)]) 100)) + (defn form [] - (let [search-term @(subscribe [:search/current-term]) - throttled-search (debounce #(dispatch [:search/do-search (.. % -target -value)]) 100)] + (let [search-term @(subscribe [:search/current-term])] (fn [] [:form {:on-submit #(.preventDefault %)} [:div.feld>p.control @@ -16,7 +18,7 @@ ;; the event might be gone when we the dispatched ;; function is fired, we need to persist it (.persist e) - (throttled-search e)) + (search e)) :default-value search-term :placeholder "Search"}]]]))) @@ -42,9 +44,34 @@ (defn album-results [{:keys [album]}] [result-cards (map (juxt album-url identity) album)]) +(defn song-table-thead [] + [:thead + [:td.song-artist "Artist"] + [:td.song-album "Album"] + [:td.song-title "Title"] + [:td.song-duration "Duration"] + [:td.song-actions.is-narrow]]) + +(defn album-link [{id :albumId :as song}] + [:a {:href (routes/url-for ::routes/album.detail {:id id})} (:album song)]) + +(defn song-table-tbody [{:keys [songs current-song]}] + [:tbody + (for [[idx song] (map-indexed vector songs)] + ^{:key idx} + [(if (= (:id song) (:id current-song)) :tr.is-playing :tr) + [:td.song-artist [collection/artist-link song]] + [:td.song-album [album-link song]] + [:td.song-title [collection/song-link {:songs songs + :song song + :idx idx}]] + [:td.song-duration (h/format-duration (:duration song) :brief? true)] + [:td.song-actions.is-narrow [collection/song-actions song]]])]) + (defn song-results [{songs :song}] - [] - [song-table songs]) + [collection/song-table {:songs songs + :thead song-table-thead + :tbody song-table-tbody}]) (defn results [{:keys [search]}] (let [term @(subscribe [:search/current-term])]