diff --git a/src/airsonic_ui/core.cljs b/src/airsonic_ui/core.cljs index 797b1d5..a05f01a 100644 --- a/src/airsonic_ui/core.cljs +++ b/src/airsonic_ui/core.cljs @@ -14,18 +14,17 @@ (println "dev mode"))) (defn on-navigate - [name params query] - (println "Route changed to " name params query) - (re-frame/dispatch [::events/navigate name params query])) + [id params query] + (println "Route changed to " id params query) + (re-frame/dispatch [::events/hash-change id params query])) (defn mount-root [] (re-frame/clear-subscription-cache!) - (reagent/render [views/main-panel] - (.getElementById js/document "app"))) + (reagent/render [views/main-panel] (.getElementById js/document "app"))) (defn ^:export init [] + (routes/start-routing! {:default routes/default-route + :on-navigate on-navigate}) (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 b976e35..16f6fd5 100644 --- a/src/airsonic_ui/db.cljs +++ b/src/airsonic_ui/db.cljs @@ -4,4 +4,4 @@ (def default-db {:active-requests 0 ;; because navigate! executes asynchronously we force to display the login screen first - :route routes/default}) + :current-route [routes/default-route]}) diff --git a/src/airsonic_ui/events.cljs b/src/airsonic_ui/events.cljs index 1fa7f18..e2c50db 100644 --- a/src/airsonic_ui/events.cljs +++ b/src/airsonic_ui/events.cljs @@ -46,15 +46,14 @@ :on-success [::auth-successful user pass] :on-failure [::api-failure]}})) -(re-frame/reg-event-db +(re-frame/reg-event-fx ::auth-successful - (fn [db [_ user pass response]] + (fn [{:keys [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})))) + (let [login {:u user :p pass}] + {:navigate [login ::routes/main] + :db (-> (update db :active-requests dec) + (assoc :login login))}))) (re-frame/reg-event-db ::api-failure @@ -62,25 +61,24 @@ (println "api call gone bad; CORS headers missing? check for :status 0" event) db)) -;; app interface +;; routing -(defn authed? - "Predicate to determine whether we can access a specific route." - [route credentials] - (or (not (routes/protected route)) credentials)) +(re-frame/reg-event-fx + ::hash-change + (fn [{:keys [db]} [_ route params query]] + ;; all the naviagation logic is in routes.cljs; all we need to do here + ;; is say what actually happens once we've navigated succesfully + {:navigate [(:login db) route params query] + :db (assoc db :current-route [route params query])})) -(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-fx + ::routes/forbidden-route + (fn [fx _] + ;; log out on 403 + {:db db/default-db + :navigate [nil routes/default-route]})) + +;; database reset / init (re-frame/reg-event-db ::initialize-db diff --git a/src/airsonic_ui/routes.cljs b/src/airsonic_ui/routes.cljs index 58289d6..12f914a 100644 --- a/src/airsonic_ui/routes.cljs +++ b/src/airsonic_ui/routes.cljs @@ -1,14 +1,34 @@ (ns airsonic-ui.routes - (:require [bide.core :as r])) + (:require [bide.core :as r] + [re-frame.core :as re-frame])) -;; routing is started in core.cljs +(def default-route ::login) -(def default ::login) +(def routes + [["/" ::login] + ["/hello" ::main]]) -(def router - (r/router [["/" ::login] - ["/hello" ::main]])) +(def protected-routes #{::main}) -;; routes that need valid credentials +(defn is-authorized? [login route] + (or (not (protected-routes route)) login)) -(def protected #{::main}) +;; shouldn't need to change this + +;; TODO: This is kind of ugly because at the moment r/navigate! is called twice. +;; the order is click -> hash-change -> {:navigate [bla] :db [bla]} -> (hash-change) -> ... + +(defn start-routing! + "Registers a :navigate effect that can be used for navigation; opts will be + passed to bide.core/start!" + [opts] + (let [router (r/router routes)] + (re-frame/reg-fx + :navigate + (fn [[login route-id params query]] + (if (is-authorized? login route-id) + (r/navigate! router route-id params query) + (do ;; 403 gets a special event + (println "Not authorized to navigate to " route-id) + (re-frame/dispatch [::forbidden-route]))))) + (r/start! router opts))) diff --git a/src/airsonic_ui/subs.cljs b/src/airsonic_ui/subs.cljs index c85a7dc..bffb9ad 100644 --- a/src/airsonic_ui/subs.cljs +++ b/src/airsonic_ui/subs.cljs @@ -11,6 +11,6 @@ ;; --- (re-frame/reg-sub - ::current-page + ::current-route (fn [db] - (:route db))) + (:current-route db))) diff --git a/src/airsonic_ui/views.cljs b/src/airsonic_ui/views.cljs index df0224e..fbfb988 100644 --- a/src/airsonic_ui/views.cljs +++ b/src/airsonic_ui/views.cljs @@ -21,16 +21,16 @@ [:div [:button {:on-click #(re-frame/dispatch [::events/authenticate @user @pass])} "Submit"]]]))) -(defn app [current-page] +(defn app [route] (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 [] - (let [current-page @(re-frame/subscribe [::subs/current-page])] + (let [[route params query] @(re-frame/subscribe [::subs/current-route])] [:div [:h1 "Airsonic"] - (case current-page + (case route ::routes/login [login-form] - [app current-page])])) + [app route])]))