diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 336714a..85d832b 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"] + [funcool/bide "1.6.0"] ;; debugging [day8.re-frame/re-frame-10x "0.3.2-react16"] ;; for CIDER diff --git a/src/airsonic_ui/core.cljs b/src/airsonic_ui/core.cljs index 64c4cc1..797b1d5 100644 --- a/src/airsonic_ui/core.cljs +++ b/src/airsonic_ui/core.cljs @@ -2,6 +2,8 @@ (:require [reagent.core :as reagent] [re-frame.core :as re-frame] [day8.re-frame.http-fx] + [bide.core :as r] + [airsonic-ui.routes :as routes] [airsonic-ui.events :as events] [airsonic-ui.views :as views] [airsonic-ui.config :as config])) @@ -11,6 +13,11 @@ (enable-console-print!) (println "dev mode"))) +(defn on-navigate + [name params query] + (println "Route changed to " name params query) + (re-frame/dispatch [::events/navigate name params query])) + (defn mount-root [] (re-frame/clear-subscription-cache!) (reagent/render [views/main-panel] @@ -18,5 +25,7 @@ (defn ^:export init [] (re-frame/dispatch-sync [::events/initialize-db]) + (r/start! routes/router {:default ::routes/login + :on-navigate on-navigate}) (dev-setup) (mount-root)) diff --git a/src/airsonic_ui/db.cljs b/src/airsonic_ui/db.cljs index ae2ab14..b976e35 100644 --- a/src/airsonic_ui/db.cljs +++ b/src/airsonic_ui/db.cljs @@ -1,4 +1,7 @@ -(ns airsonic-ui.db) +(ns airsonic-ui.db + (:require [airsonic-ui.routes :as routes])) (def default-db - {:active-requests 0}) + {:active-requests 0 + ;; because navigate! executes asynchronously we force to display the login screen first + :route routes/default}) diff --git a/src/airsonic_ui/events.cljs b/src/airsonic_ui/events.cljs index cad841f..1fa7f18 100644 --- a/src/airsonic_ui/events.cljs +++ b/src/airsonic_ui/events.cljs @@ -1,10 +1,17 @@ (ns airsonic-ui.events (:require [re-frame.core :as re-frame] [ajax.core :as ajax] + [bide.core :as r] + [airsonic-ui.routes :as routes] [airsonic-ui.config :as config] [airsonic-ui.db :as db] [clojure.string :as string])) + +;; TODO: Remove impurities + +;; api related functions + (defn ^:private uri-escape [s] (js/encodeURIComponent s)) @@ -43,6 +50,8 @@ ::auth-successful (fn [db [_ user pass response]] ;; TODO: Handle failures differently + ;; TODO: Refactor navigation into effect + (r/navigate! routes/router ::routes/main) (-> (update db :active-requests dec) (assoc :login {:u user :p pass})))) @@ -53,6 +62,26 @@ (println "api call gone bad; CORS headers missing? check for :status 0" event) db)) +;; app interface + +(defn authed? + "Predicate to determine whether we can access a specific route." + [route credentials] + (or (not (routes/protected route)) credentials)) + +(re-frame/reg-event-db + ::navigate + (fn [db [_ route]] + (println "authed?" route (authed? route (:login db))) + (if (authed? route (:login db)) + ;; continue to correct page + ;; TODO: Fetch data based on route + (assoc db :route route) + ;; logout and redirect to login + (do (re-frame/dispatch [::initialize-db]) + (r/navigate! routes/router routes/default) + db)))) + (re-frame/reg-event-db ::initialize-db (fn [_] diff --git a/src/airsonic_ui/routes.cljs b/src/airsonic_ui/routes.cljs new file mode 100644 index 0000000..58289d6 --- /dev/null +++ b/src/airsonic_ui/routes.cljs @@ -0,0 +1,14 @@ +(ns airsonic-ui.routes + (:require [bide.core :as r])) + +;; routing is started in core.cljs + +(def default ::login) + +(def router + (r/router [["/" ::login] + ["/hello" ::main]])) + +;; routes that need valid credentials + +(def protected #{::main}) diff --git a/src/airsonic_ui/subs.cljs b/src/airsonic_ui/subs.cljs index e08164c..c85a7dc 100644 --- a/src/airsonic_ui/subs.cljs +++ b/src/airsonic_ui/subs.cljs @@ -1,7 +1,16 @@ (ns airsonic-ui.subs (:require [re-frame.core :as re-frame])) +;; can be used to query the user's credentials + (re-frame/reg-sub ::login (fn [db] (:login db))) + +;; --- + +(re-frame/reg-sub + ::current-page + (fn [db] + (:route db))) diff --git a/src/airsonic_ui/views.cljs b/src/airsonic_ui/views.cljs index f5b199e..df0224e 100644 --- a/src/airsonic_ui/views.cljs +++ b/src/airsonic_ui/views.cljs @@ -1,6 +1,7 @@ (ns airsonic-ui.views (:require [re-frame.core :as re-frame] [reagent.core :as r] + [airsonic-ui.routes :as routes] [airsonic-ui.events :as events] [airsonic-ui.subs :as subs])) @@ -20,14 +21,16 @@ [:div [:button {:on-click #(re-frame/dispatch [::events/authenticate @user @pass])} "Submit"]]]))) -(defn app [user] - [:div - [:h2 (str "Currently logged in as " user)] - [:a {:on-click #(re-frame/dispatch [::events/initialize-db]) :href "#"} "Logout"]]) +(defn app [current-page] + (let [login @(re-frame/subscribe [::subs/login])] + [:div + [:h2 (str "Currently logged in as " (:u login))] + [:a {:on-click #(re-frame/dispatch [::events/initialize-db]) :href "#"} "Logout"]])) (defn main-panel [] - [:div - [:h1 "Airsonic"] - (if-let [login @(re-frame/subscribe [::subs/login])] - [app (:u login)] - [login-form])]) + (let [current-page @(re-frame/subscribe [::subs/current-page])] + [:div + [:h1 "Airsonic"] + (case current-page + ::routes/login [login-form] + [app current-page])]))