Color price green/red based on direction, fix SSE middleware error
Show price in green when rising, red when falling. Use Tailwind classes with a safelist comment so JIT includes them. Remove middleware from SSE route to prevent "response already started" error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -12,8 +12,14 @@
|
|||||||
[io.undertow.server HttpServerExchange]
|
[io.undertow.server HttpServerExchange]
|
||||||
[io.undertow.util HttpString]
|
[io.undertow.util HttpString]
|
||||||
[java.io BufferedWriter OutputStreamWriter]
|
[java.io BufferedWriter OutputStreamWriter]
|
||||||
|
[java.time ZoneId]
|
||||||
|
[java.time.format DateTimeFormatter]
|
||||||
[java.util.concurrent LinkedBlockingQueue TimeUnit]))
|
[java.util.concurrent LinkedBlockingQueue TimeUnit]))
|
||||||
|
|
||||||
|
(def ^:private time-fmt
|
||||||
|
(-> (DateTimeFormatter/ofPattern "dd-MM HH:mm:ss")
|
||||||
|
(.withZone (ZoneId/systemDefault))))
|
||||||
|
|
||||||
(defn home-page [_req]
|
(defn home-page [_req]
|
||||||
(page {:title "BTC Price"}
|
(page {:title "BTC Price"}
|
||||||
[:header.text-center.mb-8
|
[:header.text-center.mb-8
|
||||||
@@ -25,15 +31,22 @@
|
|||||||
[:div.bg-white.rounded-2xl.shadow-sm.border.border-gray-200.p-6
|
[:div.bg-white.rounded-2xl.shadow-sm.border.border-gray-200.p-6
|
||||||
[:p.text-center.text-gray-400.text-sm "Connecting..."]]]]))
|
[:p.text-center.text-gray-400.text-sm "Connecting..."]]]]))
|
||||||
|
|
||||||
(defn- render-price-html [{:keys [price recorded-at]}]
|
;; Tailwind classes referenced for price color:
|
||||||
|
;; text-green-600 text-red-600 text-gray-900
|
||||||
|
(defn- render-price-html [{:keys [price prev-price recorded-at]}]
|
||||||
|
(let [color (cond
|
||||||
|
(nil? prev-price) "text-gray-900"
|
||||||
|
(> price prev-price) "text-green-600"
|
||||||
|
(< price prev-price) "text-red-600"
|
||||||
|
:else "text-gray-900")]
|
||||||
(str
|
(str
|
||||||
(h/html
|
(h/html
|
||||||
[:div.bg-white.rounded-2xl.shadow-sm.border.border-gray-200.p-6
|
[:div.bg-white.rounded-2xl.shadow-sm.border.border-gray-200.p-6
|
||||||
[:div.text-center
|
[:div.text-center
|
||||||
[:p.text-4xl.font-bold.text-gray-900
|
[:p {:class (str "text-4xl font-bold " color)}
|
||||||
(str "$" (format "%,.2f" (double price)))]
|
(str "$" (format "%,.2f" (double price)))]
|
||||||
[:p.text-xs.text-gray-400.mt-2
|
[:p.text-xs.text-gray-400.mt-2
|
||||||
(str "Updated " recorded-at)]]])))
|
(str "Updated " (.format time-fmt recorded-at))]]]))))
|
||||||
|
|
||||||
(defn- format-sse [event data]
|
(defn- format-sse [event data]
|
||||||
(str "event: " event "\n"
|
(str "event: " event "\n"
|
||||||
@@ -76,7 +89,8 @@
|
|||||||
[["/"
|
[["/"
|
||||||
{:get home-page}]
|
{:get home-page}]
|
||||||
["/price/stream"
|
["/price/stream"
|
||||||
{:get (fn [req] (price-stream-handler opts req))}]])
|
{:get (fn [req] (price-stream-handler opts req))
|
||||||
|
:middleware []}]])
|
||||||
|
|
||||||
(defn route-data [opts]
|
(defn route-data [opts]
|
||||||
(merge
|
(merge
|
||||||
|
|||||||
@@ -40,9 +40,11 @@
|
|||||||
(swap! state assoc :last-write now)
|
(swap! state assoc :last-write now)
|
||||||
(log/info "BTC price:" (str price))
|
(log/info "BTC price:" (str price))
|
||||||
(save-price! query-fn price)
|
(save-price! query-fn price)
|
||||||
|
(let [prev-price (:price @latest-price)]
|
||||||
(reset! latest-price
|
(reset! latest-price
|
||||||
{:price price
|
{:price price
|
||||||
:recorded-at (java.time.Instant/now)})))))
|
:prev-price prev-price
|
||||||
|
:recorded-at (java.time.Instant/now)}))))))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(log/error e "Failed to parse Binance message")))))
|
(log/error e "Failed to parse Binance message")))))
|
||||||
(let [^CompletionStage cf (CompletableFuture/completedFuture nil)]
|
(let [^CompletionStage cf (CompletableFuture/completedFuture nil)]
|
||||||
|
|||||||
Reference in New Issue
Block a user