better handling of user login/logout and passing user-id's to views

when logging in/out, there is a small window where the server's session
state and the client-side state and view subscriptions will not exactly
align and some view subscription updates (because
mtgcoll.client.auth/user-profile was updated) will get treated as
unauthorized.

this change is admittedly somewhat quick/hacky, but basically the idea
is to track the sente connection state (which is true when sente is both
connected and a handshake event has been received) in a reagent atom.
if false, we hide the entire ui and show a "connecting ..." message.

not super pretty, but it's only shown to the user briefly and it solves
the problem. i'll need to revisit this at some point and wrap it up
in some library code or something somewhere to make the whole method
prettier to use in the future
This commit is contained in:
Gered 2016-07-24 15:32:03 -04:00
parent 20ab3c0693
commit 962a3624b7
5 changed files with 59 additions and 35 deletions

View file

@ -19,6 +19,10 @@
[]
(not (nil? @user-profile)))
(defn get-username
[]
(:username @user-profile))
(defn set-user-profile!
[profile]
(reset! user-profile profile))

View file

@ -25,9 +25,15 @@
(ajax/POST (->url "/login")
:params {:username username :password password}
:on-error #(reset! error "Invalid username/password.")
:on-success (fn [_]
(on-close)
(views/reconnect!))))))
:on-success (fn [response]
; i'm sick and fucking tired of using cljs-ajax. it's a bloated piece
; of shit that has always been too easy to use in a wrong way and when
; it happens, it's almost always unclear what the fuck is wrong.
; this is the last fucking project where i will be using it.
(let [user-profile (clojure.walk/keywordize-keys response)]
(on-close)
(views/reconnect!)
(auth/set-user-profile! user-profile)))))))
on-key-up (fn [e]
(if (= 13 (.-keyCode e))
(on-submit)))]

View file

@ -5,7 +5,8 @@
[webtools.reagent.bootstrap :as bs]
[webtools.cljs.utils :refer [->url]]
[mtgcoll.client.auth :as auth]
[mtgcoll.client.components.auth :refer [login-form]]))
[mtgcoll.client.components.auth :refer [login-form]]
[mtgcoll.client.views :as views]))
(defonce error (r/atom nil))
@ -25,36 +26,40 @@
(defn app-body
[page-component]
(let [active-breadcrumb @active-breadcrumb]
[:div#app-body.container
[bs/Navbar {:inverse true}
[bs/Navbar.Header
[bs/Navbar.Brand
[:a#logo {:href "#/"}
[:span [:img {:src (->url "/img/mtg_icon.png")}]]
"Card Collection"]]]
[bs/Navbar.Collapse
[bs/Nav
[bs/NavItem {:href "#/owned" :active (= :owned active-breadcrumb)} "Owned"]
[bs/NavItem {:href "#/all" :active (= :all active-breadcrumb)} "All"]
[bs/NavItem {:href "#/sets" :active (= :sets active-breadcrumb)} "Sets"]
[bs/NavItem {:href "#/stats" :active (= :stats active-breadcrumb)} "Statistics"]]
(if (auth/auth-required?)
[bs/Nav {:pull-right true}
(if (auth/authenticated?)
[bs/NavDropdown {:title (:username @auth/user-profile)}
[bs/MenuItem {:on-click #(auth/logout!)} "Logout"]]
[bs/NavItem {:on-click auth/show-login-form!} "Login"])])]]
[bs/Modal
{:show (boolean @error)
:on-hide clear-error!}
[bs/Modal.Header [bs/Modal.Title "Error"]]
[bs/Modal.Body
[:p @error]]
[bs/Modal.Footer
[bs/Button {:on-click clear-error!} "Close"]]]
[login-form]
page-component]))
(if (views/connected?)
(let [active-breadcrumb @active-breadcrumb]
[:div#app-body.container
[bs/Navbar {:inverse true}
[bs/Navbar.Header
[bs/Navbar.Brand
[:a#logo {:href "#/"}
[:span [:img {:src (->url "/img/mtg_icon.png")}]]
"Card Collection"]]]
[bs/Navbar.Collapse
[bs/Nav
[bs/NavItem {:href "#/owned" :active (= :owned active-breadcrumb)} "Owned"]
[bs/NavItem {:href "#/all" :active (= :all active-breadcrumb)} "All"]
[bs/NavItem {:href "#/sets" :active (= :sets active-breadcrumb)} "Sets"]
[bs/NavItem {:href "#/stats" :active (= :stats active-breadcrumb)} "Statistics"]]
(if (auth/auth-required?)
[bs/Nav {:pull-right true}
(if (auth/authenticated?)
[bs/NavDropdown {:title (:username @auth/user-profile)}
[bs/MenuItem {:on-click (fn [_]
(auth/logout!)
(views/reconnect!))} "Logout"]]
[bs/NavItem {:on-click auth/show-login-form!} "Login"])])]]
[bs/Modal
{:show (boolean @error)
:on-hide clear-error!}
[bs/Modal.Header [bs/Modal.Title "Error"]]
[bs/Modal.Body
[:p @error]]
[bs/Modal.Footer
[bs/Button {:on-click clear-error!} "Close"]]]
[login-form]
page-component])
[:h1 "Connecting ..."]))
(defn page
[page-component]

View file

@ -1,11 +1,18 @@
(ns mtgcoll.client.views
(:require
[reagent.core :as r]
[taoensso.sente :as sente]
[views.reagent.sente.client :as vr]
[mtgcoll.client.auth :as auth]))
(defonce sente-socket (atom {}))
(defonce connected (r/atom false))
(defn connected?
[]
(boolean @connected))
(defn chsk-exists?
[]
(not (nil? (:chsk @sente-socket))))
@ -34,6 +41,7 @@
(= :chsk/handshake ev-id)
(let [[_ _ handshake-data] ev-data
{:keys [user]} handshake-data]
(reset! connected true)
(auth/set-user-profile! user))
(= :chsk/recv id)
@ -47,6 +55,7 @@
(defn reconnect!
[]
(clear-keepalive-interval!)
(reset! connected false)
(sente/chsk-reconnect! (:chsk @sente-socket)))
(defn init!

View file

@ -12,7 +12,7 @@
(if-let [user (auth/validate-credentials username password)]
(do
(log/info username " logged in.")
(-> (response/content "ok")
(-> (response/json user)
(session/set-from-request request)
(session/assoc :user user)))
(do