;; Demo app logic: a roster of open peers + broadcast text notes.
;;
;; This is the namespace you gut. It shows the working pattern — own your
;; state, consume the small net surface main hands you, validate every frame
;; (in main, via frames/is-app-frame), let ui touch the DOM — and nothing
;; else. Shared mutable state rides plain JS objects with string keys (never
;; renamed by :advanced), accessed through the s/s! helpers — the game1 shape.
(ns __NAME__.game.app
(:require [__NAME__.config :as config]
[__NAME__.game.frames :as frames]
[__NAME__.game.ui :as ui]))
(def S
(js-obj "joined" false
"nick" ""
"nicks" (js/Map.) ; peerId → announced nick (learned from "hi" frames)
"notes" #js []))
(defn s [k] (unchecked-get S k))
(defn s! [k v] (unchecked-set S k v))
;; the slice of Net the app consumes (see net/index for the full surface)
(def ^:private net* (volatile! nil))
(defn init-app [n] (vreset! net* n))
(defn- net ^js [] @net*)
(defn- my-id [] (unchecked-get (net) "myId"))
(defn nick-of [id]
(if (identical? id (my-id))
(s "nick")
(let [v (.get ^js (s "nicks") id)]
(if (some? v) v (str "peer-" (.slice ^string id -4))))))
(defn rerender
"Re-render everything the app owns (also called on language change)."
[]
(when (s "joined")
(let [rows #js [(js-obj "id" (my-id) "nick" (s "nick") "me" true)]]
(doseq [id (array-seq ((unchecked-get (net) "openPeers")))]
(.push rows (js-obj "id" id "nick" (nick-of id) "me" false)))
(ui/render-roster rows)
(ui/render-notes (s "notes") (my-id)))))
(defn- hi [] (frames/mk-hi (s "nick")))
(defn join [nick]
(s! "joined" true)
(s! "nick" nick)
(doseq [id (array-seq ((unchecked-get (net) "openPeers")))]
((unchecked-get (net) "send") id (hi)))
(ui/show-room)
(rerender))
;; ---------------- net callbacks (wired in main) ----------------
(defn on-room-roster [_peers]
(rerender))
(defn on-peer-open [id]
(when (s "joined")
((unchecked-get (net) "send") id (hi))) ; introduce myself to the newcomer
(rerender))
(defn on-peer-gone [id]
(.delete ^js (s "nicks") id)
(rerender))
(defn- add-note [^js n]
(let [^js notes (s "notes")]
(.push notes n)
(when (> (.-length notes) 200) (.shift notes))
(ui/render-notes notes (my-id))))
(defn handle-msg [from ^js frame]
(let [t (unchecked-get frame "t")]
(cond
(identical? t "hi")
(do (.set ^js (s "nicks") from (.slice ^string (unchecked-get frame "nick") 0 24))
(rerender))
(identical? t "note")
(add-note (js-obj "from" from
"nick" (nick-of from)
"text" (.slice ^string (unchecked-get frame "text") 0 config/NOTE-MAX)
"ts" (unchecked-get frame "ts"))))))
;; ---------------- actions ----------------
(defn send-note [text]
(let [clean (.slice (.trim ^string text) 0 config/NOTE-MAX)]
(when (and (seq clean) (s "joined"))
(let [f (frames/mk-note clean (js/Date.now))]
((unchecked-get (net) "broadcast") f)
(add-note (js-obj "from" (my-id)
"nick" (s "nick")
"text" clean
"ts" (unchecked-get f "ts")))))))