diff --git a/resources/public/index.html b/resources/public/index.html index 3689315..0145f65 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -1,14 +1,10 @@ - - hello world d* - - - + %%content%% diff --git a/src/main/example/core.clj b/src/main/example/core.clj index 83bbaa8..a889d0d 100644 --- a/src/main/example/core.clj +++ b/src/main/example/core.clj @@ -12,7 +12,12 @@ [starfederation.datastar.clojure.adapter.ring :refer [->sse-response on-open on-close] :as dr] [clojure.data.json :as json])) -(declare conns) ;; for broadcasts +(declare conns add-elements! sse-navigate!) + +;; global utils +(defn tap! [x] + (println "tap:" x) + x) ;; html utils (defn html->str [hiccup-forms] @@ -20,32 +25,61 @@ (hc/compile hiccup-forms))) -(defn page-template [content] +(defn html-template [content] (-> (io/resource "public/index.html") slurp (string/split-lines) (->> (drop 3) (apply str)) + (str "
") (string/replace "%%content%%" content))) +;; fix. use replaceState api? or push some real state? back button doesn't work yet +(defn update-url-frag [url] + (html->str [:script (format "history.pushState({page:1}, 'Title', '%s')" + url)])) -;; home page -(def home-frag - (html->str - [:main {:id "main" - :data-on-load "@get('/connect')"} - [:input {:data-bind "msg"}] - [:div {:data-text "$msg"}] - [:button {:data-on-click "@get('/hello-world')"} "click for text animation"]])) - -(def home-page - (page-template home-frag)) - -(defn home [request respond _] +(defn sse-page [request respond view url] (respond - (-> home-page + (->sse-response request + {on-open + (fn [sse] + (d*/with-open-sse sse + (sse-navigate! sse (view {})) + (add-elements! sse (update-url-frag url))))}))) + +(defn text-html-page [request respond view] + (-> (html-template (view {})) ruresp/response - (ruresp/content-type "text/html")))) + (ruresp/content-type "text/html") + respond)) + +(defn page [request respond url view] + (if (-> request + :headers + (get "datastar-request") + (= "true")) + (sse-page request respond view url) + (text-html-page request respond view) + )) + +;; views +(def home-view + (fn [_] + (html->str + [:main + [:div + [:input {:data-bind "msg"}] + [:div {:data-text "$msg"}] + [:button {:data-on-click "@get('/hello-world')"} "click for text animation"]] + [:button {:data-on-click "@get('/page2')"} "go to page 2"]]))) + + +(defn page2-view [_] + (html->str + [:main + [:p "this is page2"] + [:button {:data-on-click "@get('/')"} "go back to home"]])) ;; d* api utils @@ -55,11 +89,15 @@ (defn add-elements! [sse elems] (d*/patch-elements! sse elems #:d*.elements{:patch-mode "append" :selector "body"})) +(defn sse-navigate! [sse elems] + (d*/patch-elements! sse elems #:d*.elements{:patch-mode "replace" + :selector "main"})) + (defn try! [d*-f! sse & args] (try (apply d*-f! sse args) - (catch Exception _ - (println "exception occured. dropping connection") + (catch Exception e + (println "exception occured. dropping connection. error:" e) (d*/close-sse! sse) (swap! conns disj sse)))) @@ -75,6 +113,14 @@ (doseq [conn @conns] (apply try! f conn arg args)))) +(defn connect-sse! [sse] + (swap! conns conj sse) + (println "adding connection")) + +(defn disconnect-sse! [sse] + (swap! conns disj sse) + (println "dropping connection")) + (defn broadcast-signals! [data] (broadcast! patch-signals-edn! data)) @@ -89,16 +135,16 @@ (try! d*/close-sse! conn) (swap! conns disj conn))) -(defn connect [request respond _raise] - (respond - (->sse-response request - {on-open - (fn [sse] (swap! conns conj sse) - (println "adding connection")) - on-close - (fn [sse] - (swap! conns disj sse) - (println "dropping connection"))}))) +(defn defaction-async [url on-open-f on-close-f] + (let [sse-handler + (fn [request respond raise_] + (respond + (->sse-response + request + {on-open on-open-f + on-close on-close-f})))] + [[url sse-handler] + [(str "/sse" url) sse-handler]])) ;; hello world animation @@ -106,22 +152,36 @@ (def msg-count (count message)) -(defn hello-world [request respond _raise] - (respond - (->sse-response request - {on-open - (fn [sse] - (d*/with-open-sse sse - (dotimes [i msg-count] - (patch-signals-edn! sse {:msg (subs message 0 (inc i))}) - (Thread/sleep 500))))}))) - +(defn hello-world [sse] + (dotimes [i msg-count] + (patch-signals-edn! sse {:msg (subs message 0 (inc i))}) + (Thread/sleep 500))) ;; http stuffs +(defn defaction [url f] + (let [sse-handler + (fn [request respond raise_] + (respond + (->sse-response + request + {on-open #(d*/with-open-sse % (f %))})))] + [[url sse-handler] + [(str "/sse" url) sse-handler]])) + + +(defn defpage [endpoint view] + [[endpoint + {:handler (fn [request respond _] + (page request respond endpoint view))}] + [(str "/sse" endpoint) + {:handler (fn [request respond _] + (page request respond endpoint view))}]]) (def routes - [["/" {:handler home}] - ["/connect" {:handler connect}] - ["/hello-world" {:handler hello-world}]]) + (concat + (defpage "/" home-view) + (defpage "/page2" page2-view) + (defaction "/hello-world" hello-world) + (defaction-async "/connect" connect-sse! disconnect-sse!))) (def router (rr/router routes))