1
0
Fork 0
mirror of https://github.com/heyarne/airsonic-ui.git synced 2026-05-06 18:33:38 +02:00

Add list of recent albums

This commit is contained in:
Arne Schlüter 2018-04-18 19:19:07 +02:00
commit 8e799f651d
4 changed files with 85 additions and 28 deletions

View file

@ -7,15 +7,14 @@
[airsonic-ui.db :as db] [airsonic-ui.db :as db]
[clojure.string :as string])) [clojure.string :as string]))
;; TODO:
;; TODO: Remove impurities
;; api related functions ;; api related functions
(defn ^:private uri-escape [s] (defn ^:private uri-escape [s]
(js/encodeURIComponent s)) (js/encodeURIComponent s))
(defn api-url [endpoint params] (defn ^:private api-url [endpoint params]
(let [query (->> (assoc params (let [query (->> (assoc params
:f "json" :f "json"
:c "airsonic-ui-cljs" :c "airsonic-ui-cljs"
@ -25,13 +24,13 @@
(string/join "&"))] (string/join "&"))]
(str config/server "/rest/" endpoint "?" query))) (str config/server "/rest/" endpoint "?" query)))
(defn api-error? (defn ^:private api-error?
"We need to look at the message body because the subsonic api always responds "We need to look at the message body because the subsonic api always responds
with status 200" with status 200"
[response] [response]
(= "failed" (-> response :subsonic-response :status))) (= "failed" (-> response :subsonic-response :status)))
(defn error-message (defn ^:private error-message
[response] [response]
(let [{:keys [code message]} (-> response :subsonic-response :error)] (let [{:keys [code message]} (-> response :subsonic-response :error)]
(str "Code " code ": " message))) (str "Code " code ": " message)))
@ -43,17 +42,38 @@
:http-xhrio {:method :get :http-xhrio {:method :get
:uri (api-url "ping" {:u user :p pass}) :uri (api-url "ping" {:u user :p pass})
:response-format (ajax/json-response-format {:keywords? true}) :response-format (ajax/json-response-format {:keywords? true})
:on-success [::auth-successful user pass] :on-success [::auth-success user pass]
:on-failure [::api-failure]}})) :on-failure [::api-failure]}}))
;; TODO: Test that credentials are associated
(re-frame/reg-event-fx (re-frame/reg-event-fx
::auth-successful ::auth-success
(fn [{:keys [db]} [_ user pass response]] (fn [{:keys [db]} [_ user pass response]]
;; TODO: Handle failures differently ;; TODO: Handle failures differently
(let [login {:u user :p pass}] (let [login {:u user :p pass}]
{:navigate [login ::routes/main] {:navigate [login ::routes/main]
:db (-> (update db :active-requests dec) :db (-> (update db :active-requests #(max (dec %) 0))
(assoc :login login))}))) (assoc :login login))
:dispatch [::api-request "getAlbumList2" :albumList2 {:type "recent"}]})))
;; TODO: Test that credentials are actually taken
(re-frame/reg-event-fx
::api-request
(fn [{:keys [db]} [_ endpoint k params]]
{:http-xhrio {:method :get
:uri (api-url endpoint (merge params (:login db)))
:response-format (ajax/json-response-format {:keywords? true})
:on-success [::api-success k]
:on-failure [::api-failure]}}))
(re-frame/reg-event-db
::api-success
(fn [db [_ k response]]
(println "api response" response)
;; we "unwrap" the responses
(assoc db :response (-> response :subsonic-response k))))
(re-frame/reg-event-db (re-frame/reg-event-db
::api-failure ::api-failure
@ -68,6 +88,7 @@
(fn [{:keys [db]} [_ route params query]] (fn [{:keys [db]} [_ route params query]]
;; all the naviagation logic is in routes.cljs; all we need to do here ;; all the naviagation logic is in routes.cljs; all we need to do here
;; is say what actually happens once we've navigated succesfully ;; is say what actually happens once we've navigated succesfully
;; FIXME: This is really bad and wonky actually
{:navigate [(:login db) route params query] {:navigate [(:login db) route params query]
:db (assoc db :current-route [route params query])})) :db (assoc db :current-route [route params query])}))
@ -75,8 +96,8 @@
::routes/forbidden-route ::routes/forbidden-route
(fn [fx _] (fn [fx _]
;; log out on 403 ;; log out on 403
{:db db/default-db {:navigate [nil routes/default-route]
:navigate [nil routes/default-route]})) :db db/default-db}))
;; database reset / init ;; database reset / init

View file

@ -4,15 +4,20 @@
(def default-route ::login) (def default-route ::login)
(def routes (def router
[["/" ::login] (r/router [["/" ::login]
["/hello" ::main]]) ["/hello" ::main]
["/album/:id" ::album-view]
["/artist/:id" ::artist-view]]))
(def protected-routes #{::main}) (def protected-routes #{::main ::album-view})
(defn is-authorized? [login route] (defn is-authorized? [login route]
(or (not (protected-routes route)) login)) (or (not (protected-routes route)) login))
(defn url-for [k params]
(str "#" (r/resolve router k params)))
;; shouldn't need to change this ;; shouldn't need to change this
;; TODO: This is kind of ugly because at the moment r/navigate! is called twice. ;; TODO: This is kind of ugly because at the moment r/navigate! is called twice.
@ -22,13 +27,12 @@
"Registers a :navigate effect that can be used for navigation; opts will be "Registers a :navigate effect that can be used for navigation; opts will be
passed to bide.core/start!" passed to bide.core/start!"
[opts] [opts]
(let [router (r/router routes)] (re-frame/reg-fx
(re-frame/reg-fx :navigate
:navigate (fn [[login route-id params query]]
(fn [[login route-id params query]] (if (is-authorized? login route-id)
(if (is-authorized? login route-id) (r/navigate! router route-id params query)
(r/navigate! router route-id params query) (do ;; 403 gets a special event
(do ;; 403 gets a special event (println "Not authorized to navigate to " route-id)
(println "Not authorized to navigate to " route-id) (re-frame/dispatch [::forbidden-route])))))
(re-frame/dispatch [::forbidden-route]))))) (r/start! router opts))
(r/start! router opts)))

View file

@ -8,9 +8,17 @@
(fn [db] (fn [db]
(:login db))) (:login db)))
;; --- ;; current hashbang
(re-frame/reg-sub (re-frame/reg-sub
::current-route ::current-route
(fn [db] (fn [db]
(:current-route db))) (:current-route db)))
;; ---
;; TODO: Make this nice and clean
(re-frame/reg-sub
::current-album-list
(fn [db]
(-> db :response :album)))

View file

@ -5,6 +5,8 @@
[airsonic-ui.events :as events] [airsonic-ui.events :as events]
[airsonic-ui.subs :as subs])) [airsonic-ui.subs :as subs]))
;; login form
(defn login-form [] (defn login-form []
(let [user (r/atom "") (let [user (r/atom "")
pass (r/atom "")] pass (r/atom "")]
@ -21,10 +23,32 @@
[:div [:div
[:button {:on-click #(re-frame/dispatch [::events/authenticate @user @pass])} "Submit"]]]))) [:button {:on-click #(re-frame/dispatch [::events/authenticate @user @pass])} "Submit"]]])))
(defn app [route] ;; album list
(defn album-item [album]
(let [{:keys [artist artistId name coverArt year id]} album]
[:div
;; link to artist page
[:a {:href (routes/url-for ::routes/artist-view {:id artistId})} artist]
" - "
;; link to album
[:a {:href (routes/url-for ::routes/album-view {:id id})} name] (when year (str " (" year ")"))]))
(defn album-list []
(let [albums @(re-frame/subscribe [::subs/current-album-list])]
[:ul
(map-indexed (fn [idx album]
[:li {:key idx} [album-item album]])
albums)]))
;; putting everything together
(defn app [route params query]
(let [login @(re-frame/subscribe [::subs/login])] (let [login @(re-frame/subscribe [::subs/login])]
[:div [:div
[:h2 (str "Currently logged in as " (:u login))] [:h2 (str "Currently logged in as " (:u login))]
[:h3 (str "Recently played")]
[album-list]
[:a {:on-click #(re-frame/dispatch [::events/initialize-db]) :href "#"} "Logout"]])) [:a {:on-click #(re-frame/dispatch [::events/initialize-db]) :href "#"} "Logout"]]))
(defn main-panel [] (defn main-panel []
@ -33,4 +57,4 @@
[:h1 "Airsonic"] [:h1 "Airsonic"]
(case route (case route
::routes/login [login-form] ::routes/login [login-form]
[app route])])) [app route params query])]))