From b480676cef86c34d5d6401891bab9bf47e58ba67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20Schl=C3=BCter?= Date: Wed, 30 May 2018 18:38:40 +0200 Subject: [PATCH] Remember login credentials --- shadow-cljs.edn | 1 + src/cljs/airsonic_ui/core.cljs | 8 ++++- src/cljs/airsonic_ui/events.cljs | 50 +++++++++++++++++++------- src/cljs/airsonic_ui/subs.cljs | 4 +-- test/cljs/airsonic_ui/events_test.cljs | 5 +-- 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 8d41c32..ace816b 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -6,6 +6,7 @@ [[reagent "0.7.0"] [re-frame "0.10.5"] [day8.re-frame/http-fx "0.1.6"] + [akiroz.re-frame/storage "0.1.2"] [funcool/bide "1.6.0"] ;; debugging [day8.re-frame/re-frame-10x "0.3.2-react16"] diff --git a/src/cljs/airsonic_ui/core.cljs b/src/cljs/airsonic_ui/core.cljs index 17bd202..692a633 100644 --- a/src/cljs/airsonic_ui/core.cljs +++ b/src/cljs/airsonic_ui/core.cljs @@ -1,8 +1,11 @@ (ns airsonic-ui.core (:require [reagent.core :as reagent] [re-frame.core :as re-frame] + ;; 3rd party effects / coeffects [day8.re-frame.http-fx] - [airsonic-ui.audio] ; <- just registers effects + [akiroz.re-frame.storage :as storage] + ;; our app + [airsonic-ui.audio] ; <- just registers effects here [airsonic-ui.routes :as routes] [airsonic-ui.events :as events] [airsonic-ui.views :as views] @@ -19,6 +22,9 @@ (defn ^:export init [] (routes/start-routing!) + (storage/reg-co-fx! :airsonic-ui {:fx :store + :cofx :store}) (re-frame/dispatch-sync [::events/initialize-db]) + (re-frame/dispatch [::events/try-remember-user]) (dev-setup) (mount-root)) diff --git a/src/cljs/airsonic_ui/events.cljs b/src/cljs/airsonic_ui/events.cljs index 4e91a91..6b82793 100644 --- a/src/cljs/airsonic_ui/events.cljs +++ b/src/cljs/airsonic_ui/events.cljs @@ -22,9 +22,10 @@ (defn authenticate "Tries to authenticate a user by pinging the server with credentials, saving - them when the request was succesful." + them when the request was succesful. Bypasses the request when a user saved + their credentials." [{:keys [db]} [_ user pass server]] - {:db (assoc db :server server) + {:db (assoc-in db [:credentials :server] server) :http-xhrio {:method :get :uri (api/url server "ping" {:u user :p pass}) :response-format (ajax/json-response-format {:keywords? true}) @@ -34,17 +35,34 @@ (re-frame/reg-event-fx ::authenticate authenticate) +(defn try-remember-user + "Enables skipping the auth request when credentials are saved in the + local storage; otherwise has no effect" + [{:keys [db store]} [_]] + (when-let [credentials (:credentials store)] + {:db (assoc-in db [:credentials :server] (:server credentials)) + :dispatch [::credentials-verified (:u credentials) (:p credentials) nil]})) + +(re-frame/reg-event-fx + ::try-remember-user + [(re-frame/inject-cofx :store)] + try-remember-user) + (defn credentials-verified "Gets called after the server indicates that the credentials entered by a user - are correct (see `authenticate`)." - [{:keys [db]} [_ user pass response]] - (let [login {:u user :p pass}] - {:routes/set-credentials login - :db (assoc db :login login) + are correct (see `authenticate`)" + [{:keys [db store]} [_event user pass _response]] + (let [auth {:u user :p pass} + credentials (merge (:credentials db) auth)] + {:routes/set-credentials auth + :store {:credentials credentials} + :db (assoc db :credentials credentials) :dispatch [::logged-in]})) (re-frame/reg-event-fx - ::credentials-verified credentials-verified) + ::credentials-verified + [(re-frame/inject-cofx :store)] + credentials-verified) ;; TODO: We have to find another solution for this once we have routes that ;; don't require a login but have the bottom controls @@ -65,11 +83,15 @@ ;; TODO: Move these in the future? events.cljs should just do wiring. We could ;; implement api.cljs as a completely independent module. +(defn- api-url [db endpoint params] + (let [creds (:credentials db)] + (api/url (:server creds) endpoint (merge params (select-keys creds [:u :p]))))) + (re-frame/reg-event-fx :api-request (fn [{:keys [db]} [_ endpoint k params]] {:http-xhrio {:method :get - :uri (api/url (:server db) endpoint (merge params (:login db))) + :uri (api-url db endpoint params) :response-format (ajax/json-response-format {:keywords? true}) :on-success [::api-success k] :on-failure [::api-failure]}})) @@ -90,11 +112,15 @@ ; TODO: Make play, next and previous a bit prettier and more DRY +(defn- song-url [db song] + (let [creds (:credentials db)] + (api/song-url (:server creds) (select-keys creds [:u :p]) song))) + (re-frame/reg-event-fx ; sets up the db, starts to play a song and adds the rest to a playlist ::play-songs (fn [{:keys [db]} [_ songs song]] - {:play-song (api/song-url (:server db) (:login db) song) + {:play-song (song-url db song) :db (-> db (assoc-in [:currently-playing :item] song) (assoc-in [:currently-playing :playlist] songs))})) @@ -106,7 +132,7 @@ current (-> db :currently-playing :item) next (first (rest (drop-while #(not= % current) playlist)))] (when next - {:play-song (api/song-url (:server db) (:login db) next) + {:play-song (song-url db next) :db (assoc-in db [:currently-playing :item] next)})))) (re-frame/reg-event-fx @@ -116,7 +142,7 @@ current (-> db :currently-playing :item) previous (last (take-while #(not= % current) playlist))] (when previous - {:play-song (api/song-url (:server db) (:login db) previous) + {:play-song (song-url db previous) :db (assoc-in db [:currently-playing :item] previous)})))) (re-frame/reg-event-fx diff --git a/src/cljs/airsonic_ui/subs.cljs b/src/cljs/airsonic_ui/subs.cljs index 9d31228..a83f1f0 100644 --- a/src/cljs/airsonic_ui/subs.cljs +++ b/src/cljs/airsonic_ui/subs.cljs @@ -7,12 +7,12 @@ (re-frame/reg-sub ::login (fn [db] - (:login db))) + (select-keys (:credentials db) [:u :p]))) (re-frame/reg-sub ::server (fn [db] - (:server db))) + (get-in db [:credentials :server]))) ;; current hashbang diff --git a/test/cljs/airsonic_ui/events_test.cljs b/test/cljs/airsonic_ui/events_test.cljs index 1debe49..85a97ec 100644 --- a/test/cljs/airsonic_ui/events_test.cljs +++ b/test/cljs/airsonic_ui/events_test.cljs @@ -14,7 +14,7 @@ (is (str/starts-with? (:uri request) server)) (is (str/includes? (:uri request) "/ping"))) (testing "saves the given server location" - (is (= server (get-in fx [:db :server])))) + (is (= server (get-in fx [:db :credentials :server])))) (testing "invokes correct success callback" (is (= ::events/credentials-verified (first (:on-success request))))))) (testing "On succesfull response" @@ -23,6 +23,7 @@ (testing "credentials are sent to the router for access rights" (is (= credentials (:routes/set-credentials fx)))) (testing "credentials are saved in the global state" - (is (= credentials (get-in fx [:db :login])))) + (is (= credentials (-> (get-in fx [:db :credentials]) + (select-keys [:u :p]))))) (testing "the login process is finalized" (is (= [::events/logged-in] (:dispatch fx)))))))