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))