Compare commits
No commits in common. "eba0dbe7c2b2857535211160773aa950880ff412" and "0c61609774d1418944979a24dfb54324170c39ec" have entirely different histories.
eba0dbe7c2
...
0c61609774
6
deps.edn
6
deps.edn
@ -9,11 +9,7 @@
|
|||||||
ring-logger/ring-logger {:mvn/version "1.1.1"}
|
ring-logger/ring-logger {:mvn/version "1.1.1"}
|
||||||
ring/ring-core {:mvn/version "1.14.2"}
|
ring/ring-core {:mvn/version "1.14.2"}
|
||||||
ring/ring-json {:mvn/version "0.5.1"}
|
ring/ring-json {:mvn/version "0.5.1"}
|
||||||
ring/ring-jetty-adapter {:mvn/version "1.14.2"}
|
ring/ring-jetty-adapter {:mvn/version "1.14.2"}}
|
||||||
dev.data-star.clojure/sdk {:mvn/version "1.0.0-RC3"}
|
|
||||||
dev.data-star.clojure/ring {:mvn/version "1.0.0-RC3"}
|
|
||||||
datastar/expressions {:git/url "https://github.com/outskirtslabs/datastar-expressions/"
|
|
||||||
:git/sha "53efc7093be1ba33b331b4a27884be8925d8bdce"}}
|
|
||||||
:paths [:clj-paths :resource-paths]
|
:paths [:clj-paths :resource-paths]
|
||||||
:aliases
|
:aliases
|
||||||
{:repl {:extra-paths ["src/dev"]
|
{:repl {:extra-paths ["src/dev"]
|
||||||
|
|||||||
@ -6,11 +6,12 @@
|
|||||||
[compojure.core :refer [context defroutes DELETE GET let-routes PATCH POST]]
|
[compojure.core :refer [context defroutes DELETE GET let-routes PATCH POST]]
|
||||||
[compojure.route :as route]
|
[compojure.route :as route]
|
||||||
[hiccup2.core :as h]
|
[hiccup2.core :as h]
|
||||||
[ring.middleware.json :refer [wrap-json-body]]
|
[ring.middleware.content-type :refer [wrap-content-type]]
|
||||||
[starfederation.datastar.clojure.adapter.common :refer [on-open]]
|
[ring.middleware.json :as rmjson]
|
||||||
[starfederation.datastar.clojure.adapter.ring :refer [->sse-response]]
|
[ring.middleware.not-modified :refer [wrap-not-modified]]
|
||||||
[starfederation.datastar.clojure.api :as d*]
|
[ring.middleware.params :refer [wrap-params]]
|
||||||
[starfederation.datastar.clojure.expressions :refer [->expr]]))
|
[ring.middleware.resource :refer [wrap-resource]]
|
||||||
|
[ring.util.response :refer [response]]))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(do
|
(do
|
||||||
@ -19,137 +20,120 @@
|
|||||||
|
|
||||||
(remove-tap printer))
|
(remove-tap printer))
|
||||||
|
|
||||||
(defn page-template [view]
|
(defn get-todos [_]
|
||||||
(str
|
(let [res (db/get-all-todos)]
|
||||||
(h/html
|
(response res)))
|
||||||
(h/raw "<!DOCTYPE html>")
|
|
||||||
[:html
|
|
||||||
[:head
|
|
||||||
[:script {:src "https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.5/bundles/datastar.js"
|
|
||||||
:type "module"
|
|
||||||
:defer true}]
|
|
||||||
[:link {:rel "icon"
|
|
||||||
:type "imaage/x-icon"
|
|
||||||
:href "https://data-star.dev/cdn-cgi/image/format=auto,width=24/static/images/rocket-48x48-4c739bfaffe86a6ffcc3a6d77e3c5547730f03d74c11aa460209596d1811f7a3.png"}]]
|
|
||||||
[:body view]])))
|
|
||||||
|
|
||||||
(defn todos-fragment []
|
(defn get-todo [id _]
|
||||||
(let [todos (db/get-all-todos)]
|
(let [res (db/get-todo id)]
|
||||||
(h/html
|
(response res)))
|
||||||
[:div {:id "todos"}
|
|
||||||
[:div {:data-computed-show-toggle-display "($showComplete ? 'hide' : 'show') + ' done'"}]
|
(defn add-todo [req]
|
||||||
|
(db/add-todo! (-> req :body :title))
|
||||||
|
{:status 201})
|
||||||
|
|
||||||
|
(defn update-todo [id {{:keys [done]} :body}]
|
||||||
|
(db/set-todo-done! id done))
|
||||||
|
|
||||||
|
(defn delete-todo [id]
|
||||||
|
(db/delete-todo! id)
|
||||||
|
{:status 204})
|
||||||
|
|
||||||
|
(defn page-template [view]
|
||||||
|
(-> [:html
|
||||||
|
[:head
|
||||||
|
[:script {:src "https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js"}]]
|
||||||
|
[:body view]]
|
||||||
|
h/html
|
||||||
|
str))
|
||||||
|
|
||||||
|
(def hx-app {:hx-swap "outerHTML"
|
||||||
|
:hx-target "#app"})
|
||||||
|
|
||||||
|
(defn render-app [{:keys [show-complete]
|
||||||
|
:or {show-complete true}
|
||||||
|
:as opts}]
|
||||||
|
(-> (let [todos (db/get-all-todos)
|
||||||
|
complete-toggle (if show-complete "hide" "show")]
|
||||||
|
[:div {:id "app"}
|
||||||
[:ul
|
[:ul
|
||||||
(for [{id :id
|
(for [{id :id
|
||||||
done :todo/done?
|
done :todo/done?
|
||||||
title :todo/title} todos]
|
title :todo/title} todos
|
||||||
[:li {:data-show (->expr (or (not ~done)
|
:when (or show-complete (not done))]
|
||||||
$showComplete))}
|
[:li
|
||||||
[:span {:style (str "text-decoration: " (if done "line-through" "none"))}
|
[:span {:style (str "text-decoration: " (if done "line-through" "none"))}
|
||||||
title]
|
title]
|
||||||
[:input {:type "checkbox"
|
[:input (merge hx-app
|
||||||
|
{:type "checkbox"
|
||||||
:checked done
|
:checked done
|
||||||
:data-on-click (str "@patch('/sse/todo/" id "/" (when done "un") "set')")
|
:hx-patch (format "/htmx/todo/%s/%s?opts=%s"
|
||||||
:style "margin-right: 0.25rem;"}]
|
id
|
||||||
(when done
|
(if done "unset" "set")
|
||||||
[:button {:data-on-click (->expr (@delete ~(str "/sse/todo/" id)))}
|
(json/write-str opts))})]])]
|
||||||
"X"])])]
|
|
||||||
[:div
|
[:div
|
||||||
[:p "settings"]
|
[:p "settings"]
|
||||||
[:button {:data-on-click "$showComplete = !$showComplete"
|
[:button (merge hx-app
|
||||||
:data-text "$showToggleDisplay"
|
{:hx-get (format "/htmx/todo/?opts=%s"
|
||||||
:style "margin-right: 0.25rem;"}]
|
(-> opts
|
||||||
[:button {:data-on-click "$debugMode = !$debugMode"}
|
(update :show-complete not)
|
||||||
"toggle debug"]]])))
|
json/write-str))})
|
||||||
|
complete-toggle " done"]]])
|
||||||
|
h/html))
|
||||||
|
|
||||||
(defn create-todo-fragment []
|
;; im gonna regert this
|
||||||
(h/html
|
(defn DRY [req]
|
||||||
[:div {:id "create-box"
|
(-> req
|
||||||
:style "margin-top: 2rem;"}
|
:params
|
||||||
[:label "Make a todo: "]
|
(get "opts" "{}")
|
||||||
[:input {:name "title"
|
json/read-json
|
||||||
:data-bind "title"
|
render-app
|
||||||
:style "margin-right: 0.25rem;"}]
|
str))
|
||||||
[:button {:data-on-click (->expr (@post "/sse/todo/create-todo"))}
|
|
||||||
"submit"]]))
|
|
||||||
|
|
||||||
(defn app-fragment []
|
(defroutes api-routes
|
||||||
(h/html
|
|
||||||
[:div {:data-signals #json{:showComplete true
|
|
||||||
:debugMode false
|
|
||||||
:title ""}}]
|
|
||||||
[:pre {:data-json-signals "true"
|
|
||||||
:data-show "$debugMode"}]
|
|
||||||
[:div {:id "app"}
|
|
||||||
(todos-fragment)
|
|
||||||
(create-todo-fragment)]))
|
|
||||||
|
|
||||||
(defn refresh-todos! [sse]
|
|
||||||
(d*/patch-elements! sse
|
|
||||||
(str (todos-fragment))
|
|
||||||
#:d*.elements{:selector "#todos"
|
|
||||||
:patch-mode "replace"}))
|
|
||||||
|
|
||||||
(defroutes approutes
|
|
||||||
(GET "/" []
|
(GET "/" []
|
||||||
(fn [_ respond _]
|
(wrap-params
|
||||||
(-> (app-fragment)
|
#(-> % :params
|
||||||
page-template
|
(get "opts" "{}")
|
||||||
respond)))
|
json/read-json
|
||||||
|
render-app
|
||||||
(wrap-json-body
|
page-template)))
|
||||||
(context "/sse/todo" []
|
(-> (context "/api/v1" []
|
||||||
(GET "/" []
|
(context "/todo" []
|
||||||
(fn [req respond _]
|
(GET "/" [] get-todos)
|
||||||
(respond
|
(POST "/" [] add-todo)
|
||||||
(->sse-response req
|
|
||||||
{on-open (fn [sse]
|
|
||||||
(d*/with-open-sse sse
|
|
||||||
(refresh-todos! sse)))}))))
|
|
||||||
(POST "/create-todo" []
|
|
||||||
(fn [req respond _]
|
|
||||||
(let [title (-> req :body :title)]
|
|
||||||
(tap> title)
|
|
||||||
(when title
|
|
||||||
(db/add-todo! title))
|
|
||||||
(respond
|
|
||||||
(->sse-response req
|
|
||||||
{on-open (fn [sse]
|
|
||||||
(d*/with-open-sse sse
|
|
||||||
(refresh-todos! sse)
|
|
||||||
(d*/patch-signals! sse
|
|
||||||
#json{:title ""})))})))))
|
|
||||||
|
|
||||||
(context ["/:id", :id #"[0-9]+"] [id]
|
(context ["/:id", :id #"[0-9]+"] [id]
|
||||||
(let-routes [id (read-string id)]
|
(let-routes [id (read-string id)]
|
||||||
(PATCH "/set" []
|
(GET "/" [] (partial get-todo id))
|
||||||
(fn [req respond _]
|
(PATCH "/" [] (partial update-todo id))
|
||||||
(db/set-todo-done! id true)
|
(DELETE "/" [] (delete-todo id)))))
|
||||||
(respond
|
|
||||||
(->sse-response req
|
(GET "/example/math/:x/:y" [x y]
|
||||||
{on-open (fn [sse]
|
(->> (+ (parse-long x)
|
||||||
(d*/with-open-sse sse
|
(parse-long y))
|
||||||
(refresh-todos! sse)))}))))
|
(hash-map :result)
|
||||||
(PATCH "/unset" []
|
response)))
|
||||||
(fn [req respond _]
|
(rmjson/wrap-json-body {:keywords? true})
|
||||||
(db/set-todo-done! id false)
|
(rmjson/wrap-json-response))
|
||||||
(respond
|
|
||||||
(->sse-response
|
(wrap-params
|
||||||
req
|
(context "/htmx/todo" []
|
||||||
{on-open (fn [sse]
|
(GET "/" [] DRY)
|
||||||
(d*/with-open-sse sse
|
(POST "/" [] add-todo)
|
||||||
(refresh-todos! sse)))}))))
|
(context ["/:id", :id #"[0-9]+"] [id]
|
||||||
(DELETE "/" []
|
(let-routes [id (read-string id)]
|
||||||
(db/delete-todo! id)
|
(PATCH "/set" [] (do (db/set-todo-done! id true)
|
||||||
(fn [req respond _]
|
DRY))
|
||||||
(respond
|
(PATCH "/unset" [] (do (db/set-todo-done! id false)
|
||||||
(->sse-response
|
DRY))
|
||||||
req
|
(DELETE "/" [] (do (delete-todo id)
|
||||||
{on-open
|
DRY))))))
|
||||||
(fn [sse]
|
|
||||||
(d*/with-open-sse sse
|
|
||||||
(refresh-todos! sse)))})))))))
|
|
||||||
{:keywords? true})
|
|
||||||
(route/not-found (str (h/html [:h2 "Not Found"]))))
|
(route/not-found (str (h/html [:h2 "Not Found"]))))
|
||||||
|
|
||||||
(def app #'approutes)
|
(def app
|
||||||
|
(-> #'api-routes
|
||||||
|
(wrap-resource "public")
|
||||||
|
wrap-content-type
|
||||||
|
wrap-not-modified)) ;; files from resources/public are served
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,7 @@
|
|||||||
(def port 1738)
|
(def port 1738)
|
||||||
|
|
||||||
(defn make-server [opts]
|
(defn make-server [opts]
|
||||||
(run-jetty #'core/app (merge {:join? false,
|
(run-jetty #'core/app (merge {:join? false, :port port} opts)))
|
||||||
:port port,
|
|
||||||
:async? true}
|
|
||||||
opts)))
|
|
||||||
|
|
||||||
(defn -main [& _args]
|
(defn -main [& _args]
|
||||||
(make-server {}))
|
(make-server {}))
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
{json clojure.data.json/write-str}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user