Compare commits
2 Commits
2f04ea6d49
...
bd6bd11519
Author | SHA1 | Date | |
---|---|---|---|
bd6bd11519 | |||
941f668eff |
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>hello world d*</title>
|
<title>hello world d*</title>
|
||||||
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@main/bundles/datastar.js"></script>
|
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@main/bundles/datastar.js"></script>
|
||||||
|
<link rel="icon" type="image/x-icon" href="https://data-star.dev/cdn-cgi/image/format=auto,width=24/static/images/rocket-48x48-4c739bfaffe86a6ffcc3a6d77e3c5547730f03d74c11aa460209596d1811f7a3.png">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
%%content%%
|
%%content%%
|
||||||
|
@ -1,78 +1,19 @@
|
|||||||
(ns example.core
|
(ns example.core
|
||||||
(:require
|
(:require
|
||||||
[clojure.java.io :as io]
|
[example.utils :refer [html->str defpage defaction defaction-async] :as u]
|
||||||
[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]
|
|
||||||
[reitit.ring :as rr]
|
[reitit.ring :as rr]
|
||||||
[ring.util.response :as ruresp]
|
[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 "<div data-on-load=\"@get('connect')\"></div>")
|
|
||||||
(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
|
;; views
|
||||||
(def home-view
|
(defn home-view [_]
|
||||||
(fn [_]
|
|
||||||
(html->str
|
(html->str
|
||||||
[:main
|
[:main
|
||||||
[:div
|
[:div
|
||||||
[:input {:data-bind "msg"}]
|
[:input {:data-bind "msg"}]
|
||||||
[:div {:data-text "$msg"}]
|
[:div {:data-text "$msg"}]
|
||||||
[:button {:data-on-click "@get('/hello-world')"} "click for text animation"]]
|
[:button {:data-on-click "@get('/hello-world')"} "click for text animation"]]
|
||||||
[:button {:data-on-click "@get('/page2')"} "go to page 2"]])))
|
[:button {:data-on-click "@get('/page2')"} "go to page 2"]]))
|
||||||
|
|
||||||
|
|
||||||
(defn page2-view [_]
|
(defn page2-view [_]
|
||||||
@ -81,72 +22,6 @@
|
|||||||
[:p "this is page2"]
|
[:p "this is page2"]
|
||||||
[:button {:data-on-click "@get('/')"} "go back to home"]]))
|
[: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
|
;; hello world animation
|
||||||
(def message "Hello, world!")
|
(def message "Hello, world!")
|
||||||
|
|
||||||
@ -154,34 +29,15 @@
|
|||||||
|
|
||||||
(defn hello-world [sse]
|
(defn hello-world [sse]
|
||||||
(dotimes [i msg-count]
|
(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)))
|
(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
|
(def routes
|
||||||
(concat
|
(concat
|
||||||
(defpage "/" home-view)
|
(defpage "/" home-view)
|
||||||
(defpage "/page2" page2-view)
|
(defpage "/page2" page2-view)
|
||||||
(defaction "/hello-world" hello-world)
|
(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))
|
(def router (rr/router routes))
|
||||||
|
|
||||||
@ -190,11 +46,11 @@
|
|||||||
|
|
||||||
;; repl it up ;P
|
;; repl it up ;P
|
||||||
(comment
|
(comment
|
||||||
(broadcast-signals! {:msg "hi franz"})
|
(u/broadcast-signals! {:msg "hi franz"})
|
||||||
(broadcast-signals! {:msg "hi ty"})
|
(u/broadcast-signals! {:msg "hi ty"})
|
||||||
(broadcast! d*/console-log! "hi franz")
|
(u/broadcast! d*/console-log! "hi franz")
|
||||||
(broadcast-js! "console.log('test')")
|
(u/broadcast-js! "console.log('test')")
|
||||||
(broadcast-reload!)
|
(u/broadcast-reload!)
|
||||||
|
|
||||||
(kill-broadcast!)
|
(kill-broadcast!)
|
||||||
|
|
||||||
|
@ -1,13 +1,147 @@
|
|||||||
(ns example.utils
|
(ns example.utils
|
||||||
(:require
|
(:require
|
||||||
[charred.api :as charred]
|
[clojure.java.io :as io]
|
||||||
[starfederation.datastar.clojure.api :as d*]))
|
[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 "<div data-on-load=\"@get('connect')\"></div>")
|
||||||
|
(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)
|
;; d* api utils
|
||||||
(def read-json (charred/parse-json-fn {:async? false :bufsize bufSize}))
|
(defn patch-signals-edn! [sse edn]
|
||||||
|
(d*/patch-signals! sse (json/write-str edn)))
|
||||||
|
|
||||||
(defn get-signals [req]
|
(defn add-elements! [sse elems]
|
||||||
(-> req d*/get-signals read-json))
|
(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))}]])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user