diff --git a/src/main/example/core.clj b/src/main/example/core.clj index a889d0d..0ddf867 100644 --- a/src/main/example/core.clj +++ b/src/main/example/core.clj @@ -1,78 +1,19 @@ (ns example.core (:require - [clojure.java.io :as io] - [clojure.string :as string] - [dev.onionpancakes.chassis.compiler :as hc] - [dev.onionpancakes.chassis.core :as h] - [example.utils :as u] - [reitit.ring.middleware.parameters :as rmparams] + [example.utils :refer [html->str defpage defaction defaction-async] :as u] [reitit.ring :as rr] - [ring.util.response :as ruresp] - [starfederation.datastar.clojure.api :as d*] - [starfederation.datastar.clojure.adapter.ring :refer [->sse-response on-open on-close] :as dr] - [clojure.data.json :as json])) + [ring.util.response :as ruresp])) -(declare conns add-elements! sse-navigate!) - -;; global utils -(defn tap! [x] - (println "tap:" x) - x) - -;; html utils -(defn html->str [hiccup-forms] - (h/html - (hc/compile - hiccup-forms))) - -(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)])) - -(defn sse-page [request respond view url] - (respond - (->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") - 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 home-view [_] + (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 [_] @@ -81,72 +22,6 @@ [:p "this is page2"] [:button {:data-on-click "@get('/')"} "go back to home"]])) - -;; d* api utils -(defn patch-signals-edn! [sse edn] - (d*/patch-signals! sse (json/write-str edn))) - -(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 e - (println "exception occured. dropping connection. error:" e) - (d*/close-sse! sse) - (swap! conns disj sse)))) - - -;; broadcast utils -(def conns (atom #{})) - -(defn broadcast! - ([f] - (doseq [conn @conns] - (try! f conn))) - ([f arg & args] - (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)) - -(defn broadcast-js! [js-src] - (broadcast! add-elements! (html->str [:script js-src]))) - -(defn broadcast-reload! [] - (broadcast-js! "location.reload()")) - -(defn kill-broadcast! [] - (doseq [conn @conns] - (try! d*/close-sse! conn) - (swap! conns disj conn))) - -(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 (def message "Hello, world!") @@ -154,34 +29,15 @@ (defn hello-world [sse] (dotimes [i msg-count] - (patch-signals-edn! sse {:msg (subs message 0 (inc i))}) + (u/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 (concat (defpage "/" home-view) (defpage "/page2" page2-view) (defaction "/hello-world" hello-world) - (defaction-async "/connect" connect-sse! disconnect-sse!))) + (defaction-async "/connect" u/connect-sse! u/disconnect-sse!))) (def router (rr/router routes)) @@ -190,11 +46,11 @@ ;; repl it up ;P (comment - (broadcast-signals! {:msg "hi franz"}) - (broadcast-signals! {:msg "hi ty"}) - (broadcast! d*/console-log! "hi franz") - (broadcast-js! "console.log('test')") - (broadcast-reload!) + (u/broadcast-signals! {:msg "hi franz"}) + (u/broadcast-signals! {:msg "hi ty"}) + (u/broadcast! d*/console-log! "hi franz") + (u/broadcast-js! "console.log('test')") + (u/broadcast-reload!) (kill-broadcast!) diff --git a/src/main/example/utils.clj b/src/main/example/utils.clj index 09bd802..b010201 100644 --- a/src/main/example/utils.clj +++ b/src/main/example/utils.clj @@ -1,13 +1,147 @@ (ns example.utils (:require - [charred.api :as charred] - [starfederation.datastar.clojure.api :as d*])) + [clojure.java.io :as io] + [clojure.string :as string] + [dev.onionpancakes.chassis.compiler :as hc] + [dev.onionpancakes.chassis.core :as h] + [reitit.ring.middleware.parameters :as rmparams] + [reitit.ring :as rr] + [ring.util.response :as ruresp] + [starfederation.datastar.clojure.api :as d*] + [starfederation.datastar.clojure.adapter.ring :refer [->sse-response on-open on-close] :as dr] + [clojure.data.json :as json])) + +(declare conns add-elements! sse-navigate!) + +;; global utils +(defn tap! [x] + (println "tap:" x) + x) + +;; html utils +(defn html->str [hiccup-forms] + (h/html + (hc/compile + hiccup-forms))) + +(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)])) + +(defn sse-page [request respond view url] + (respond + (->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") + 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))) -(def ^:private bufSize 1024) -(def read-json (charred/parse-json-fn {:async? false :bufsize bufSize})) +;; d* api utils +(defn patch-signals-edn! [sse edn] + (d*/patch-signals! sse (json/write-str edn))) -(defn get-signals [req] - (-> req d*/get-signals read-json)) +(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 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]])) +;; broadcast utils +(defn try! + [d*-f! sse & args] + (try (apply d*-f! sse args) + (catch Exception e + (println "exception occured. dropping connection. error:" e) + (d*/close-sse! sse) + (swap! conns disj sse)))) + +(defonce conns (atom #{})) + +(defn broadcast! + ([f] + (doseq [conn @conns] + (try! f conn))) + ([f arg & args] + (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)) + +(defn broadcast-js! [js-src] + (broadcast! add-elements! (html->str [:script js-src]))) + +(defn broadcast-reload! [] + (broadcast-js! "location.reload()")) + +(defn kill-broadcast! [] + (doseq [conn @conns] + (try! d*/close-sse! conn) + (swap! conns disj conn))) + +;; 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))}]])