further decoupling of state from the view

don't pass state objects to the view.
don't construct urls in the view.

the controller is to prepare a view based on request parameters,
session state, data model
This commit is contained in:
rdiedrich 2020-06-29 12:25:54 +02:00
parent d51647c671
commit 567de507f5
2 changed files with 100 additions and 91 deletions

View File

@ -25,18 +25,25 @@
(defroutes app-routes (defroutes app-routes
(GET "/" request (views/index-page request)) (GET "/" request (views/index-page request))
(GET "/login" [] (views/login-page)) (GET "/login" []
(views/main-template
{:title "Login"}
(-> (views/login-form ["/bookmarks"] false)
(views/login-page))))
(POST "/login" [username password next :as {session :session}] (POST "/login" [username password next :as {session :session}]
(let [resp-cookies (client/login-response username password session)] (let [resp-cookies (client/login-response username password session)]
{:status 302 {:status 302
:headers {"Location" next} :headers {"Location" next}
:session (assoc session :cookies resp-cookies)})) :session (assoc session :cookies resp-cookies :loggedin true)}))
(GET "/bookmarks" [page :<< as-int (GET "/bookmarks" [page :<< as-int
:as {session :session}] :as {session :session}]
(let [beems (get-bookmarks session page)] (if (get session :loggedin)
(views/bookmarks-page beems))) (let [beems (get-bookmarks session page)]
(views/bookmarks-page beems))
{:status 302
:headers {"Location" "/login"}}))
(GET "/bookmarks" [] (GET "/bookmarks" []
{:status 302 :headers {"Location" "/bookmarks?page=1"}}) {:status 302 :headers {"Location" "/bookmarks?page=1"}})
@ -44,8 +51,17 @@
(GET "/thread/:id" [id :<< as-int (GET "/thread/:id" [id :<< as-int
page :<< as-int page :<< as-int
:as {session :session}] :as {session :session}]
(let [thread (get-thread session id page)] (let [thread (get-thread session id page)
(views/thread-page thread))) {:keys [id page page-count title]} thread
login-part (views/login-form
["/thread/%d?page=%d" id page]
(get session :loggedin false))
header-part (views/header-fragment login-part)
thread-part (views/thread-page thread)
paginate-part (views/paginate
(str "/thread/" id) page page-count)]
(views/main-template {:title title}
header-part thread-part paginate-part)))
(GET "/thread/:id" [id] (GET "/thread/:id" [id]
{:status 302 :headers {"Location" (str "/thread/" id "?page=1")}}) {:status 302 :headers {"Location" (str "/thread/" id "?page=1")}})

View File

@ -5,44 +5,43 @@
[ring.util.anti-forgery :refer [anti-forgery-field]])) [ring.util.anti-forgery :refer [anti-forgery-field]]))
(def ^:dynamic *thread* {}) (defn login-form [next loggedin]
(if loggedin
[:a.navbar-item "Logout"]
[:div.navbar-item.has-dropdown.is-hoverable
[:a.navbar-link "Login"]
[:div.navbar-dropdown.is-right
[:div.navbar-item
[:form#login-form {:action "/login" :method "post"}
(anti-forgery-field)
[:input {:type "hidden" :name "next"
:value (apply format next)}]
[:div.field
[:label.label {:for "username"} "username"]
[:div.control
[:input#username.input {:name "username" :type "text"
:placeholder "enter your username"}]]]
[:div.field
[:label.label {:for "password"} "password"]
[:div.control
[:input#password.input {:name "password" :type "password"
:placeholder "enter your password"}]]]
[:div.field
[:div.control
[:button.button.is-primary "Submit"]]]]]]]))
(defn login-form [] (defn header-fragment [login]
[:form#login-form {:action "/login" :method "post"} [:nav.navbar.is-transparent
(anti-forgery-field) [:div.navbar-brand
[:input {:type "hidden" :name "next" [:a.burger.navbar-burger {:role "button" :data-target "mainNav"}
:value (format "/thread/%d?page=%d" (:id *thread*) (:page *thread*))}] (repeat 3 [:span {:aria-hidden "true"}])]]
[:div.field [:div#mainNav.navbar-menu
[:label.label {:for "username"} "username"] [:div.navbar-start
[:div.control [:a.navbar-item {:href "/bookmarks"} "Bookmarks"]]
[:input#username.input {:name "username" :type "text" [:div.navbar-end
:placeholder "enter your username"}]]] login]]])
[:div.field
[:label.label {:for "password"} "password"]
[:div.control
[:input#password.input {:name "password" :type "password"
:placeholder "enter your password"}]]]
[:div.field
[:div.control
[:button.button.is-primary "Submit"]]]])
(defn header-fragment [] (defn main-template [opts header & insert-body-here]
(html
[:nav.navbar
[:div.navbar-brand
[:a.burger.navbar-burger {:role "button" :data-target "mainNav"}
(repeat 3 [:span {:aria-hidden "true"}])]]
[:div#mainNav.navbar-menu
[:div.navbar-start
[:a.navbar-item {:href "/bookmarks"} "Bookmarks"]]
[:div.navbar-end
[:div.navbar-item.has-dropdown.is-hoverable
[:a.navbar-link "Login"]
[:div.navbar-dropdown.is-right
[:div.navbar-item
(login-form)]]]]]]))
(defn main-template [opts & insert-body-here]
(html5 (html5
{:lang "de"} {:lang "de"}
[:head [:head
@ -56,7 +55,7 @@
[:body [:body
{:hx-boost "false"} {:hx-boost "false"}
[:header [:header
(header-fragment)] header]
[:main [:main
insert-body-here] insert-body-here]
(include-js "/js/main.js")])) (include-js "/js/main.js")]))
@ -68,59 +67,53 @@
[:pre.output [:pre.output
[:code (with-out-str (clojure.pprint/pprint req))]]])) [:code (with-out-str (clojure.pprint/pprint req))]]]))
(defn login-page [] (defn login-page [login]
(main-template [:div.container.box
{:title "login"} login])
[:div.container.box
(login-form "")]))
(defn paginate [base cur last] (defn paginate [base cur last]
(let [page-fstring "%s?page=%d" (let [page-fstring "%s?page=%d"
href (partial format page-fstring base)] href (partial format page-fstring base)]
[:nav.container.box.pagination {:hx-boot "false"} [:section
[:a.pagination-previous [:nav.container.box.pagination {:hx-boot "false"}
{:href (href (dec cur))} "<"] [:a.pagination-previous
[:a.pagination-next {:href (href (dec cur))} "<"]
{:href (href (inc cur))} ">"] [:a.pagination-next
[:ul.pagination-list {:href (href (inc cur))} ">"]
[:li [:ul.pagination-list
[:a.pagination-link [:li
{:href (href 1)} (str 1)]] [:a.pagination-link
[:li {:href (href 1)} (str 1)]]
[:span.pagination-ellipsis "&hellip;"]] [:li
(for [i (range (- cur 2) (+ cur 3))] [:span.pagination-ellipsis "&hellip;"]]
[:li (for [i (range (- cur 2) (+ cur 3))]
[:a.pagination-link [:li
{:href (href i) [:a.pagination-link
:class (when (= i cur) "is-current")} (str i)]]) {:href (href i)
[:li :class (when (= i cur) "is-current")} (str i)]])
[:span.pagination-ellipsis "&hellip;"]] [:li
[:li [:span.pagination-ellipsis "&hellip;"]]
[:a.pagination-link [:li
{:href (href last)} (str last)]]]])) [:a.pagination-link
{:href (href last)} (str last)]]]]]))
(defn thread-page [thread] (defn thread-page [{:keys [title content]}]
(binding [*thread* thread] (list
(let [{:keys [id title page page-count content]} thread] [:section.title [:div.container
(main-template [:h1.is-size-3.mb-4 title]]]
{:title title} [:section.thread
[:div.container (for [post content]
[:h1.is-size-3.mb-4 title]] [:article.container.box
[:section.thread [:div.tile.is-ancestor
(for [post content] [:aside.userinfo.tile.is-3.is-parent
[:article.container.box (:ui post)]
[:div.tile.is-ancestor [:main.postbody.content.tile.is-9.is-parent.is-vertical
[:aside.userinfo.tile.is-3.is-parent [:div.tile.is-child
(:ui post)] (:pb post)]
[:main.postbody.content.tile.is-9.is-parent.is-vertical [:div.level.tile.is-child.is-12
[:div.tile.is-child [:div.level-right
(:pb post)] [:span.postdate.level-item
[:div.level.tile.is-child.is-12 (:pd post)]]]]]])]))
[:div.level-right
[:span.postdate.level-item
(:pd post)]]]]]])]
[:section
(paginate (str "/thread/" id) page page-count)]))))
(defn bookmarks-page [beems] (defn bookmarks-page [beems]
(let [{:keys [title page page-count content]} beems] (let [{:keys [title page page-count content]} beems]