making progress

This commit is contained in:
Luciano Laratelli 2025-03-12 15:54:45 -04:00
parent 162ff2f407
commit da1e391830
15 changed files with 207 additions and 65 deletions

View file

@ -27,6 +27,7 @@
com.taoensso/slf4j-telemere {:mvn/version "1.0.0-beta14"}
com.taoensso/telemere {:mvn/version "1.0.0-beta14"}
org.sqids/sqids-clojure {:mvn/version "1.0.15"}
org.xerial/sqlite-jdbc {:mvn/version "3.45.2.0"}
com.github.seancorfield/honeysql {:mvn/version "2.6.1126"}

View file

@ -1 +0,0 @@
DELETE TABLE IF EXISTS users;

View file

@ -1,7 +0,0 @@
CREATE TABLE IF NOT EXISTS users (
id uuid PRIMARY KEY,
email text NOT NULL UNIQUE,
joined_at timestamp DEFAULT CURRENT_TIMESTAMP,
foo text,
bar text
);

View file

@ -1 +0,0 @@
DELETE TABLE IF EXISTS auth_code;

View file

@ -1,7 +0,0 @@
CREATE TABLE IF NOT EXISTS auth_code (
id uuid PRIMARY KEY,
email text NOT NULL UNIQUE,
code text NOT NULL,
created_at timestamp NOT NULL,
failed_attempts integer DEFAULT 0
);

View file

@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS game (
id text PRIMARY KEY,
code text UNIQUE,
display_session text NOT NULL,
control_session text,
active boolean not null
);

View file

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS player (
id INTEGER PRIMARY KEY,
score INTEGER NOT NULL,
name text NOT NULL,
game_code text NOT NULL,
FOREIGN KEY (game_code)
REFERENCES game(code)
);

View file

@ -0,0 +1,3 @@
.hidden {
display: none;
}

View file

@ -1,48 +1,170 @@
(ns com.biffweb.my-project.app
(:require [com.biffweb :as biff]
[com.biffweb.my-project.middleware :as mid]
[com.biffweb.my-project.ui :as ui]
[honey.sql :as sql]
[com.biffweb.my-project.settings :as settings]
[next.jdbc :as jdbc]))
(:require
[clojure.pprint :as pprint]
[clojure.pprint :as pp]
[clojure.string :as str]
[com.biffweb :as biff]
[com.biffweb.my-project.middleware :refer [wrap-session]]
[com.biffweb.my-project.settings :as settings]
[com.biffweb.my-project.ui :as ui]
[honey.sql :as sql]
[next.jdbc :as jdbc]
[org.sqids.clojure :as sqids]))
(defn bar-form [{:keys [value]}]
(biff/form
{:hx-post "/app/set-bar"
:hx-swap "outerHTML"}
[:label {:for "bar"} "Bar: "
[:span (pr-str value)]]
[:div
[:input#bar {:type "text" :name "bar" :value value}]
[:button {:type "submit"} "Update"]]
"This demonstrates updating a value with HTMX."))
(defn reset [_]
{:status 200
:headers {"HX-Redirect" "/"}
:session {}})
;; thanks https://stackoverflow.com/a/58098360
(def sqids (sqids/sqids {:alphabet "cdefhjknprtvwxy2345689"}))
(defn game-code []
(apply str (take 6 (sqids/encode sqids [(rand-int Integer/MAX_VALUE)]))))
(defn error-style [s]
[:h4.pico-color-red-500 s])
(defn set-bar [{:keys [example/ds session params] :as _ctx}]
(jdbc/execute! ds (sql/format {:update :users
:set {:bar (:bar params)}
:where [:= :id (:uid session)]}))
(biff/render (bar-form {:value (:bar params)})))
(let [players (map str/trim (-> params :players str/split-lines))
id (-> session :id)
code (game-code)]
(if (> 2 (count players))
(error-style "Need at least two players")
(do
(jdbc/execute! ds (sql/format {:insert-into :game
:values [{:id id
:code code
:display_session id
:active false}]
:on-conflict :id
:do-update-set [:code :display_session :active]}))
(defn app [{:keys [session example/ds] :as _ctx}]
(let [query (sql/format {:select [:*]
:from [:users] :where [:= :id (:uid session)]})
{:users/keys [email bar]} (jdbc/execute-one! ds query)]
(jdbc/execute! ds (sql/format {:insert-into :player
:values (for [p players]
{:score 0
:name p
:game_code code})}))
{:status 200
:headers {"HX-redirect" "/game"}
:session {:id id}}))))
(defn game [{:keys [session params]
:example/keys [ds]
:as _ctx}]
(let [id (:id session)
_ (println id)
_ (println (str id))
game (jdbc/execute-one! ds (sql/format {:select :*
:from :game
:where [:= :id id]}))
players (jdbc/execute! ds (sql/format {:select :*
:from :player
:where [:= :game_code (:game/code game)]}))]
(println id)
(pprint/pprint game)
(ui/page
{}
[:header.container
[:hgroup
{:style
{:display "flex"
:align-items "center"
:justify-content "space-between"}}
"some text for " email
(biff/form
{:action "/auth/signout"
:class "inline"}
[:button {:type "submit"}
"Sign out"])]]
[:div
[:nav
[:ul [:li [:strong "Score the pigs"]]]
[:ul [:li (biff/form {:id "reset"
:hx-get "/reset"}
[:button.secondary "Reset"])]]]
[:h4 id]
(bar-form {:value bar}))))
[:div
[:h4 "Game code is " (:game/code game)]
[:table
[:thead
[:tr
[:th {:scope "col"} "Player"]
[:th {:scope "col"} "Score"]]]
(into [:tbody]
(for [p players]
[:tr
[:th {:scope "row"} (:player/name p)]
[:td (:player/score p)]]))]]])))
(defn control-view [{:keys [session params path-params]
:example/keys [ds]
:as _ctx}]
(let [code (:code path-params)
players (jdbc/execute! ds (sql/format {:select :* :from :player :where [:= :game_code code]}))]
(ui/page {}
[:div [:nav
[:ul [:li [:strong "Score the pigs"]]]
[:ul [:li (biff/form {:id "reset"
:hx-get "/reset"}
[:button.secondary "Reset"])]]]
[:h5 (str "Game " code)]
[:table
[:thead
[:tr
[:th {:scope "col"} "Player"]
[:th {:scope "col"} "Score"]]]
(into [:tbody]
(for [p players]
[:tr
[:th {:scope "row"} (:player/name p)]
[:td (:player/score p)]]))]])))
(defn connect
[{:keys [session params]
:example/keys [ds]
:as _ctx}]
(let [code (:game-code params)
game (delay (jdbc/execute-one! ds (sql/format {:select :* :from :game
:where [:= :code code]})))]
(println params)
(if (and code @game)
{:status 200
:session session
:headers {"HX-Redirect" (str "/game/" code)}}
(error-style "Code does not exist."))))
(defn app [{:keys [session params]
:example/keys [ds]
:as _ctx}]
(let [id (:id session)]
(ui/page
{}
[:div
[:nav
[:ul [:li [:strong "Score the pigs"]]]
[:ul [:li (biff/form {:id "reset"
:hx-get "/reset"}
[:button.secondary "Reset"])]]]
[:h4 id]
[:section
[:button {:_ "on click toggle the *display of #new-game-form"} "New game"]]
(biff/form
{:hx-post "/set-bar"
:style {:display :none}
:hx-swap "afterend"
:id ::new-game-form}
[:div
[:textarea#players {:type "textarea" :rows "8" :name "players"}]
[:button {:type "submit"} "Start"]])
(biff/form
{:hx-post "/connect"
:hx-swap "afterend"
:id ::connect-to-game}
[:fieldset
{:role "group"}
[:input
{:name "game-code",
:type "text",
:placeholder "Connect to an existing game"}]
[:input {:type "submit", :value "Connect"}]])])))
(def about-page
(ui/page
@ -57,8 +179,13 @@
(def module
{:static {"/about/" about-page}
:routes ["/app" {:middleware [mid/wrap-signed-in]}
:routes ["/" {:middleware [wrap-session]}
["" {:get app}]
["game/"
["" {:get game}]
[":code" {:get control-view}]]
["/set-bar" {:post set-bar}]]
["connect" {:post connect}]
["reset" {:get reset}]
["set-bar" {:post set-bar}]]
:api-routes [["/api/echo" {:post echo}]]})

View file

@ -118,9 +118,4 @@
:on-error "/signin"}})]))
(def module
{:routes [["" {:middleware [mid/wrap-redirect-signed-in]}
["/" {:get home-page}]]
["/link-sent" {:get link-sent}]
["/verify-link" {:get verify-email-page}]
["/signin" {:get signin-page}]
["/verify-code" {:get enter-code-page}]]})
{:routes [[]]})

View file

@ -1,8 +1,10 @@
(ns com.biffweb.my-project.middleware
(:require [com.biffweb :as biff]
[muuntaja.middleware :as muuntaja]
[org.sqids.clojure :as sqids]
[ring.middleware.anti-forgery :as csrf]
[ring.middleware.defaults :as rd]))
[ring.middleware.defaults :as rd]
[clojure.string :as str]))
(defn wrap-redirect-signed-in [handler]
(fn [{:keys [session] :as ctx}]
@ -18,6 +20,18 @@
{:status 303
:headers {"location" "/signin?error=not-signed-in"}})))
(defn wrap-session [handler]
(fn [{:keys [session] :as req}]
(let [req (update-in req [:path-params :code] str/lower-case)]
(if (some? (:id session))
(do
(println "found session id" (:id session))
(handler req))
(let [new-id (random-uuid)]
(println "no session id, adding new one:" new-id)
(handler (assoc-in req [:session :id] new-id)))))))
;; Stick this function somewhere in your middleware stack below if you want to
;; inspect what things look like before/after certain middleware fns run.
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
@ -60,4 +74,6 @@
biff/wrap-resource
biff/wrap-internal-error
biff/wrap-ssl
biff/wrap-log-requests))
;; biff/wrap-log-requests
))

View file

@ -56,6 +56,7 @@
(update :base/head (fn [head]
(concat [[:link {:rel "stylesheet" :href "/css/pico.min.css"}]
[:link {:rel "stylesheet" :href "/css/pico.colors.min.css"}]
[:link {:rel "stylesheet" :href "/css/my.css"}]
[:script {:src "/js/main.js"}]
[:script {:src "/js/htmx-1.9.11.min.js"}]
[:script {:src "/js/htmx-1.9.11-ext-ws.min.js"}]

View file

@ -21,7 +21,7 @@
(callback job)))
(def module
{:tasks [{:task #'print-usage
:schedule #(every-n-minutes 5)}]
{;; :tasks [{:task #'print-usage
;; :schedule #(every-n-minutes 5)}]
:queues [{:id :echo
:consumer #'echo-consumer}]})