|
1 | 1 | (ns example.client |
2 | 2 | "Official Sente reference example: client" |
3 | | - {:author "Peter Taoussanis (@ptaoussanis)"} |
4 | | - |
5 | 3 | (:require |
6 | 4 | [clojure.string :as str] |
7 | | - [cljs.core.async :as async :refer [<! >! put! chan]] |
8 | | - [taoensso.encore :as encore :refer-macros [have have?]] |
9 | | - [taoensso.timbre :as timbre :refer-macros []] |
10 | | - [taoensso.sente :as sente :refer [cb-success?]] |
11 | | - |
12 | | - ;; Optional, for Transit encoding: |
13 | | - [taoensso.sente.packers.transit :as sente-transit]) |
| 5 | + [cljs.core.async :as async] |
| 6 | + [taoensso.encore :as encore] |
| 7 | + [taoensso.timbre :as timbre] |
| 8 | + [taoensso.sente :as sente] |
| 9 | + [example.dynamic-packer])) |
14 | 10 |
|
15 | | - (:require-macros |
16 | | - [cljs.core.async.macros :as asyncm :refer [go go-loop]])) |
| 11 | +;;;; Logging |
17 | 12 |
|
18 | | -;;;; Logging config |
19 | | - |
20 | | -(defonce min-log-level_ (atom nil)) |
21 | | -(defn- set-min-log-level! [level] |
| 13 | +(defonce min-log-level_ (atom nil)) |
| 14 | +(defn- set-min-log-level! [level] |
22 | 15 | (sente/set-min-log-level! level) ; Min log level for internal Sente namespaces |
23 | 16 | (timbre/set-ns-min-level! level) ; Min log level for this namespace |
24 | 17 | (reset! min-log-level_ level)) |
25 | 18 |
|
26 | | -(when-let [el (.getElementById js/document "sente-min-log-level")] |
27 | | - (let [level (if-let [attr (.getAttribute el "data-level")] |
28 | | - (keyword attr) |
29 | | - :warn)] |
30 | | - (set-min-log-level! level))) |
| 19 | +;;;; Init config |
| 20 | + |
| 21 | +(def init-config |
| 22 | + "{:keys [csrf-token min-log-level packer-mode]}" |
| 23 | + (when-let [el (.getElementById js/document "init-config")] |
| 24 | + (when-let [edn (.getAttribute el "data-edn")] |
| 25 | + (encore/read-edn edn)))) |
31 | 26 |
|
32 | | -;;;; Util for logging output to on-screen console |
| 27 | +(set-min-log-level! (get init-config :min-log-level)) |
| 28 | +(reset! example.dynamic-packer/mode_ (get init-config :packer-mode)) |
| 29 | + |
| 30 | +;;;; On-screen console |
33 | 31 |
|
34 | 32 | (let [output-el (.getElementById js/document "output")] |
35 | 33 | (defn- ->output!! [x] |
|
44 | 42 |
|
45 | 43 | (->output! "ClojureScript has successfully loaded") |
46 | 44 | (->output! "Sente version: %s" sente/sente-version) |
47 | | -(->output! "Min log level: %s (use toggle button to change)" @min-log-level_) |
| 45 | +(->output! "Init config: %s" init-config) |
48 | 46 | (->output!) |
49 | 47 |
|
50 | 48 | ;;;; Define our Sente channel socket (chsk) client |
51 | 49 |
|
52 | | -(def ?csrf-token |
53 | | - (when-let [el (.getElementById js/document "sente-csrf-token")] |
54 | | - (.getAttribute el "data-token"))) |
55 | | - |
56 | | -(if ?csrf-token |
57 | | - (->output! "CSRF token detected in HTML, great!") |
58 | | - (->output! "**IMPORTANT** CSRF token NOT detected in HTML, default Sente config will reject requests!")) |
| 50 | +(def ?csrf-token (get init-config :csrf-token)) |
| 51 | +(if ?csrf-token |
| 52 | + (->output! "CSRF token in init config, great!") |
| 53 | + (->output! "**IMPORTANT** no CSRF token in init config, default Sente config will reject requests!")) |
59 | 54 |
|
60 | 55 | (def chsk-type |
61 | 56 | "We'll select a random protocol for this example" |
|
64 | 59 | (->output! "Randomly selected chsk type: %s" chsk-type) |
65 | 60 | (->output!) |
66 | 61 |
|
67 | | -(let [;; Serializtion format, must use same val for client + server: |
68 | | - packer :edn ; Default packer, a good choice in most cases |
69 | | - ;; (sente-transit/get-transit-packer) ; Needs Transit dep |
| 62 | +(def packer |
| 63 | + "Sente uses \"packers\" to control how values are encoded during |
| 64 | + client<->server transit. |
| 65 | +
|
| 66 | + Default is to use edn, but this reference example uses a dynamic |
| 67 | + packer that can swap between edn/transit/binary for testing. |
70 | 68 |
|
71 | | - {:keys [chsk ch-recv send-fn state]} |
72 | | - (sente/make-channel-socket-client! |
73 | | - "/chsk" ; Must match server Ring routing URL |
74 | | - ?csrf-token |
75 | | - {:type chsk-type |
76 | | - :packer packer})] |
| 69 | + Client and server should use the same packer." |
77 | 70 |
|
| 71 | + #_:edn ; Default |
| 72 | + (example.dynamic-packer/get-packer)) |
| 73 | + |
| 74 | +(def chsk-client |
| 75 | + (sente/make-channel-socket-client! |
| 76 | + "/chsk" ; Must match server Ring routing URL |
| 77 | + ?csrf-token |
| 78 | + {:type chsk-type |
| 79 | + :packer packer})) |
| 80 | + |
| 81 | +(let [{:keys [chsk ch-recv send-fn state]} chsk-client] |
78 | 82 | (def chsk chsk) |
79 | 83 | (def ch-chsk ch-recv) ; ChannelSocket's receive channel |
80 | 84 | (def chsk-send! send-fn) ; ChannelSocket's send API fn |
|
100 | 104 |
|
101 | 105 | (defmethod -event-msg-handler :chsk/state |
102 | 106 | [{:as ev-msg :keys [?data]}] |
103 | | - (let [[old-state-map new-state-map] (have vector? ?data)] |
| 107 | + (let [[old-state-map new-state-map] (encore/have vector? ?data)] |
104 | 108 | (cond |
105 | 109 | ;; Tip: look for {:keys [opened? closed? first-open?]} in `new-state-map` to |
106 | 110 | ;; easily identify these commonly useful state transitions |
|
155 | 159 | (fn [ev] |
156 | 160 | (chsk-send! [:example/toggle-broadcast-loop] 5000 |
157 | 161 | (fn [cb-reply] |
158 | | - (when (cb-success? cb-reply) |
159 | | - (let [enabled? cb-reply] |
| 162 | + (when (sente/cb-success? cb-reply) |
| 163 | + (let [enabled? cb-reply] |
160 | 164 | (if enabled? |
161 | 165 | (->output! "Server broadcast loop now ENABLED") |
162 | 166 | (->output! "Server broadcast loop now DISABLED"))))))))) |
|
188 | 192 | (when-let [target-el (.getElementById js/document "btn-toggle-logging")] |
189 | 193 | (.addEventListener target-el "click" |
190 | 194 | (fn [ev] |
191 | | - (chsk-send! [:example/toggle-min-log-level] 5000 |
| 195 | + (chsk-send! [:example/toggle-log-level] 5000 |
| 196 | + (fn [cb-reply] |
| 197 | + (when (sente/cb-success? cb-reply) |
| 198 | + (let [new-level cb-reply] |
| 199 | + (set-min-log-level! new-level) |
| 200 | + (->output! "New log level: %s" new-level)))))))) |
| 201 | + |
| 202 | +(when-let [target-el (.getElementById js/document "btn-toggle-packer")] |
| 203 | + (.addEventListener target-el "click" |
| 204 | + (fn [ev] |
| 205 | + (chsk-send! [:example/toggle-packer] 5000 |
192 | 206 | (fn [cb-reply] |
193 | | - (if (cb-success? cb-reply) |
194 | | - (let [level cb-reply] |
195 | | - (set-min-log-level! level) |
196 | | - (->output! "New minimum log level (client+server): %s" level)) |
197 | | - (->output! "Request failed: %s" cb-reply))))))) |
| 207 | + (when (sente/cb-success? cb-reply) |
| 208 | + (let [new-mode cb-reply] |
| 209 | + (reset! example.dynamic-packer/mode_ new-mode) |
| 210 | + (->output! "New packer mode: %s" new-mode)))))))) |
198 | 211 |
|
199 | 212 | (when-let [target-el (.getElementById js/document "btn-toggle-bad-conn-rate")] |
200 | 213 | (.addEventListener target-el "click" |
201 | 214 | (fn [ev] |
202 | 215 | (chsk-send! [:example/toggle-bad-conn-rate] 5000 |
203 | 216 | (fn [cb-reply] |
204 | | - (if (cb-success? cb-reply) |
205 | | - (->output! "New rate: %s" cb-reply) |
206 | | - (->output! "Request failed: %s" cb-reply))))))) |
| 217 | + (when (sente/cb-success? cb-reply) |
| 218 | + (->output! "New rate: %s" cb-reply))))))) |
207 | 219 |
|
208 | 220 | (when-let [target-el (.getElementById js/document "btn-connected-uids")] |
209 | 221 | (.addEventListener target-el "click" |
210 | 222 | (fn [ev] |
211 | 223 | (chsk-send! [:example/connected-uids] 5000 |
212 | 224 | (fn [cb-reply] |
213 | | - (when (cb-success? cb-reply) |
| 225 | + (when (sente/cb-success? cb-reply) |
214 | 226 | (->output! "Connected uids: %s" cb-reply))))))) |
215 | 227 |
|
216 | 228 | (when-let [target-el (.getElementById js/document "btn-login")] |
|
249 | 261 | (->output!) |
250 | 262 | (->output! "Will rapidly change user-id from \"1\" to \"10\"...") |
251 | 263 | (let [c (async/chan)] |
252 | | - (go-loop [uids (range 11)] |
| 264 | + (async/go-loop [uids (range 11)] |
253 | 265 | (when-let [[next-uid] uids] |
254 | 266 | (sente/ajax-call "/login" |
255 | 267 | {:method :post |
256 | 268 | :headers {:X-CSRF-Token (:csrf-token @chsk-state)} |
257 | 269 | :params {:user-id (str next-uid)}} |
258 | 270 | (fn [ajax-resp] |
259 | 271 | (when (:success? ajax-resp) (sente/chsk-reconnect! chsk)) |
260 | | - (put! c :continue))) |
261 | | - (<! c) |
262 | | - (<! (async/timeout 100)) |
| 272 | + (async/put! c :continue))) |
| 273 | + (async/<! c) |
| 274 | + (async/<! (async/timeout 100)) |
263 | 275 | (recur (next uids)))))))) |
264 | 276 |
|
265 | 277 | ;;;; Init stuff |
|
0 commit comments