Swing begins.

This commit is contained in:
Robert C. Martin 2022-04-27 12:07:24 -05:00
parent 6760ba48a8
commit df415f8181
5 changed files with 186 additions and 95 deletions

View File

@ -14,15 +14,15 @@
["e" "0002" "anotherurl"]]
"content" "the content"
"sig" "dddddd"})
(with state {:application
{:text-event-map {}
:chronological-text-events (make-chronological-text-events)
}
})
(with state
{:text-event-map {}
:chronological-text-events (make-chronological-text-events)
}
)
(it "creates the map of text events by id"
(let [state (process-text-event @state @event)
event-map (get-in state [:application :text-event-map])
text-events (get-in state [:application :chronological-text-events])
event-map (get-in state [:text-event-map])
text-events (get-in state [:chronological-text-events])
event (get event-map 0xdeadbeef :not-in-map)]
(should= 1 (count event-map))
(should= 1 (count text-events))
@ -38,10 +38,10 @@
(it "adds references to tagged articles."
(let [state (assoc-in @state
[:application :text-event-map 2]
[:text-event-map 2]
{:id 2})
state (process-references state (translate-text-event @event))
text-event-map (get-in state [:application :text-event-map])
text-event-map (get-in state [:text-event-map])
article (get text-event-map 2)]
(should= [0xdeadbeef] (:references article)))
)
@ -49,31 +49,31 @@
(context "sorted set for handling events"
(it "adds one element"
(let [state (add-event @state {:id 10 :created-at 0})]
(should= #{[10 0]} (get-in state [:application :chronological-text-events]))
(should= {10 {:id 10 :created-at 0}} (get-in state [:application :text-event-map]))))
(should= #{[10 0]} (get-in state [:chronological-text-events]))
(should= {10 {:id 10 :created-at 0}} (get-in state [:text-event-map]))))
(it "adds two elements in chronological order, should be reversed"
(let [state (add-event @state {:id 10 :created-at 0})
state (add-event state {:id 20 :created-at 1})
]
(should= [[20 1] [10 0]] (seq (get-in state [:application :chronological-text-events])))
(should= [[20 1] [10 0]] (seq (get-in state [:chronological-text-events])))
(should= {10 {:id 10 :created-at 0}
20 {:id 20 :created-at 1}} (get-in state [:application :text-event-map])))
20 {:id 20 :created-at 1}} (get-in state [:text-event-map])))
)
(it "adds two elements in reverse chronological order, should remain."
(let [state (add-event @state {:id 10 :created-at 1})
state (add-event state {:id 20 :created-at 0})
]
(should= [[10 1] [20 0]] (seq (get-in state [:application :chronological-text-events])))
(should= [[10 1] [20 0]] (seq (get-in state [:chronological-text-events])))
(should= {10 {:id 10 :created-at 1}
20 {:id 20 :created-at 0}} (get-in state [:application :text-event-map])))
20 {:id 20 :created-at 0}} (get-in state [:text-event-map])))
)
(it "adds two elements with equal ids"
(let [state (add-event @state {:id 10 :created-at 1})
state (add-event state {:id 10 :created-at 0})
event-map (get-in state [:application :text-event-map])]
(should= [[10 1]] (seq (get-in state [:application :chronological-text-events])))
event-map (get-in state [:text-event-map])]
(should= [[10 1]] (seq (get-in state [:chronological-text-events])))
(should= 1 (count event-map))
)
)
@ -90,13 +90,13 @@
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
(should= (ecc/bytes->hex-string public-key) pubkey)
(should (<= 0 (- now created_at) 1 )) ;within one second.
(should (<= 0 (- now created_at) 1)) ;within one second.
(should= 1 kind)
(should= [] tags)
(should= text content)
(should (ecc/do-verify (ecc/hex-string->bytes id)
public-key
(ecc/hex-string->bytes sig)))))
public-key
(ecc/hex-string->bytes sig)))))
(it "composes a reply."
(let [private-key (ecc/num->bytes 64 42)
@ -108,31 +108,31 @@
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
(should= (ecc/bytes->hex-string public-key) pubkey)
(should (<= 0 (- now created_at) 1)) ;within one second.
(should (<= 0 (- now created_at) 1)) ;within one second.
(should= 1 kind)
(should= [[:e (ecc/bytes->hex-string reply-to)]] tags)
(should= text content)
(should (ecc/do-verify (ecc/hex-string->bytes id)
public-key
(ecc/hex-string->bytes sig)))))
(should= text content)
(should (ecc/do-verify (ecc/hex-string->bytes id)
public-key
(ecc/hex-string->bytes sig)))))
(it "composes a message with a slash."
(let [private-key (ecc/num->bytes 64 42)
public-key (ecc/get-pub-key private-key)
reply-to nil
text "message/text"
event (compose-text-event private-key text reply-to)
{:keys [pubkey created_at kind tags content id sig]} (second event)
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
(should= (ecc/bytes->hex-string public-key) pubkey)
(should (<= 0 (- now created_at) 1)) ;within one second.
(should= 1 kind)
(should= [] tags)
(should= text content)
(should (ecc/do-verify (ecc/hex-string->bytes id)
public-key
(ecc/hex-string->bytes sig)))))
(let [private-key (ecc/num->bytes 64 42)
public-key (ecc/get-pub-key private-key)
reply-to nil
text "message/text"
event (compose-text-event private-key text reply-to)
{:keys [pubkey created_at kind tags content id sig]} (second event)
now (quot (System/currentTimeMillis) 1000)]
(should= "EVENT" (first event))
(should= (ecc/bytes->hex-string public-key) pubkey)
(should (<= 0 (- now created_at) 1)) ;within one second.
(should= 1 kind)
(should= [] tags)
(should= text content)
(should (ecc/do-verify (ecc/hex-string->bytes id)
public-key
(ecc/hex-string->bytes sig)))))
)
(describe "json"

View File

@ -15,7 +15,6 @@
(ns more-speech.core
(:require [quil.core :as q]
[quil.middleware :as m]
[clojure.core.async :as async]
[more-speech.nostr.events :as nostr]
[more-speech.ui.widget :refer [draw-widget
@ -26,8 +25,10 @@
[more-speech.ui.application :refer [map->application]]
[more-speech.ui.graphics :as g]
[more-speech.ui.widget :as w]
[more-speech.ui.config :as config]
[more-speech.nostr.protocol :as protocol]))
[more-speech.nostr.protocol :as protocol]
[more-speech.ui.swing.main-window :as swing]
[more-speech.nostr.events :as events])
)
(def events (atom []))
(def send-chan (async/chan))
@ -80,25 +81,29 @@
(draw-widget application state)
)
(declare more-speech)
(declare more-speech setup-jframe)
(defn ^:export -main [& args]
(q/defsketch more-speech
:title "More Speech"
:size [(q/screen-width) (- (q/screen-height) config/window-margin)]
:setup setup
:update update-state
:draw draw-state
:mouse-wheel w/mouse-wheel
:mouse-pressed w/mouse-pressed
:mouse-released w/mouse-released
:mouse-moved w/mouse-moved
:mouse-dragged w/mouse-dragged
:key-pressed w/key-pressed
:middleware [m/fun-mode]
:on-close protocol/close-connection)
;(q/defsketch more-speech
; :title "More Speech"
; :size [(q/screen-width) (- (q/screen-height) config/window-margin)]
; :setup setup
; :update update-state
; :draw draw-state
; :mouse-wheel w/mouse-wheel
; :mouse-pressed w/mouse-pressed
; :mouse-released w/mouse-released
; :mouse-moved w/mouse-moved
; :mouse-dragged w/mouse-dragged
; :key-pressed w/key-pressed
; :middleware [m/fun-mode]
; :on-close protocol/close-connection)
;(reset! events (read-string (slurp "nostr-messages")))
(protocol/get-events events send-chan)
(let [event-agent (events/make-event-agent)]
(swing/setup-jframe event-agent send-chan)
(protocol/get-events event-agent send-chan))
args
)

View File

@ -2,7 +2,6 @@
(:require [clojure.spec.alpha :as s]
[clojure.data.json :as json]
[more-speech.nostr.util :refer [hex-string->num]]
[more-speech.ui.widget :as w]
[more-speech.nostr.elliptic-signature :as ecc])
(:import (java.nio.charset StandardCharsets)))
(s/def ::id number?)
@ -23,34 +22,42 @@
(declare process-text-event
process-name-event)
(defn make-event-agent []
(agent {:chronological-text-events []
:nicknames {}
:text-event-map {}
:update false}))
(defn updated [event-state]
(assoc event-state :update false))
(defn to-json [o]
(json/write-str o :escape-slash false :escape-unicode false))
(defn process-event [{:keys [application] :as state} event]
(let [{:keys [_articles nicknames]} application
_name-of (fn [pubkey] (get nicknames pubkey pubkey))
(defn process-event [{:keys [nicknames] :as event-state} event]
(let [_name-of (fn [pubkey] (get nicknames pubkey pubkey))
[_name _subscription-id inner-event :as _decoded-msg] event
{:strs [_id _pubkey _created_at kind _tags _content _sig]} inner-event]
(condp = kind
0 (process-name-event state inner-event)
0 (process-name-event event-state inner-event)
3 (do
;(printf "%s: %s %s %s\n" kind (f/format-time created_at) (name-of pubkey) content)
state)
event-state)
1 (do
;(printf "%s: %s %s %s\n" kind (f/format-time created_at) (name-of pubkey) (subs content 0 (min 50 (count content))))
(process-text-event state inner-event))
(process-text-event event-state inner-event))
4 (do
;(printf "%s: %s %s %s\n" kind (f/format-time created_at) (name-of pubkey) content)
state)
event-state)
(do (prn "unknown event: " event)
state))))
event-state))))
(defn process-name-event [state {:strs [_id pubkey _created_at _kind _tags content _sig]}]
(defn process-name-event [event-state {:strs [_id pubkey _created_at _kind _tags content _sig]}]
(let [pubkey (hex-string->num pubkey)
name (get (json/read-str content) "name" "tilt")
state (w/redraw-widget state [:application :author-window])]
(update-in
state [:application :nicknames] assoc pubkey name)))
name (get (json/read-str content) "name" "tilt")]
(-> event-state
(update-in [:nicknames] assoc pubkey name)
(assoc :update true))))
(defn process-tag [[type arg1 arg2]]
[(keyword type) arg1 arg2])
@ -67,7 +74,7 @@
state state]
(if (empty? refs)
state
(let [referent-path [:application :text-event-map (first refs)]]
(let [referent-path [:text-event-map (first refs)]]
(if (nil? (get-in state referent-path))
(recur (rest refs) state)
(recur (rest refs)
@ -87,18 +94,20 @@
:sig sig
:tags (process-tags (get event "tags"))}))
(defn add-event [state event]
(defn add-event [event-state event]
(let [id (:id event)
time (:created-at event)
state (assoc-in state [:application :text-event-map id] event)
state (update-in state [:application :chronological-text-events] conj [id time])]
state))
time (:created-at event)]
(-> event-state
(assoc-in [:text-event-map id] event)
(update-in [:chronological-text-events] conj [id time]))))
(defn process-text-event [state event]
(let [event (translate-text-event event)
state (add-event state event)
state (w/redraw-widget state [:application :header-window])]
(process-references state event)))
(defn process-text-event [event-state event]
(let [event (translate-text-event event)]
(-> event-state
(add-event event)
(process-references event)
(assoc :update true)
)))
(defn chronological-event-comparator [[i1 t1] [i2 t2]]
(if (= i1 i2)

View File

@ -21,7 +21,6 @@
"ws://nostr-pub.wellorder.net:7000"
])
(defn send-to [^WebSocket conn msg]
(let [msg (events/to-json msg)]
(println "sending:" msg)
@ -57,14 +56,14 @@
(defn unsubscribe [^WebSocket conn id]
(send-to conn ["CLOSE" id]))
(defrecord listener [buffer events]
(defrecord listener [buffer event-agent]
WebSocket$Listener
(onOpen [_this _webSocket]
(prn 'open))
(onText [_this webSocket data last]
(.append buffer (.toString data))
(when last
(swap! events conj (json/read-str (.toString buffer)))
(send event-agent events/process-event (json/read-str (.toString buffer)))
(.delete buffer 0 (.length buffer)))
(.request webSocket 1)
)
@ -85,10 +84,10 @@
)
)
(defn connect-to-relay ^WebSocket [url events]
(defn connect-to-relay ^WebSocket [url event-agent]
(let [client (HttpClient/newHttpClient)
cl (.newWebSocketBuilder client)
cws (.buildAsync cl (URI/create url) (->listener (StringBuffer.) events))
cws (.buildAsync cl (URI/create url) (->listener (StringBuffer.) event-agent))
ws (.get cws)
]
ws)
@ -96,15 +95,14 @@
(def private-key (ecc/sha-256 (.getBytes "I am Bob.")))
(defn get-events [events send-chan]
(let [conn (connect-to-relay (get relays 0) events)
(defn get-events [event-agent send-chan]
(let [conn (connect-to-relay (get relays 0) event-agent)
id "more-speech"
date (make-date "04/01/2022")
date (make-date "04/25/2022")
]
(prn date (format-time date))
(unsubscribe conn id)
(subscribe conn id date)
;(send-to conn ["EVENT" {:pubkey "2ef93f01cd2493e04235a6b87b10d3c4a74e2a7eb7c3caf168268f6af73314b5", :created_at 1649969311, :kind 1, :tags [], :content "Hello from more-speech.", :id "b05141aecb7d975ae7df861a13082d98ad76e9f46accc046ba221533877b69c6", :sig "e80411eee168aa620b035ce16622eda24d059859562276305a1c8900559b3bc5ae0505754e5d0f352891717eb34f4495771bcf11c80d01cdd19025a51e99979d"}])
(loop [msg (async/<!! send-chan)]
(condp = (first msg)
:closed nil
@ -114,7 +112,7 @@
(recur (async/<!! send-chan)))))
(unsubscribe conn id)
(.get (.sendClose conn WebSocket/NORMAL_CLOSURE "done"))
(Thread/sleep 1000))
(Thread/sleep 100))
(prn 'done)
)

View File

@ -0,0 +1,79 @@
(ns more-speech.ui.swing.main-window
(:require [clojure.core.async :as async]
[more-speech.nostr.util :as util]
[more-speech.ui.formatters :as formatters]
[more-speech.nostr.events :as events])
(:import (javax.swing JFrame SwingUtilities Timer JScrollPane JTextPane)
(java.awt BorderLayout Toolkit Font)
(java.awt.event ActionListener WindowListener)))
(declare display-jframe action-event draw-events)
(defn setup-jframe [event-agent output-channel]
(SwingUtilities/invokeLater #(display-jframe event-agent output-channel)))
(defmacro with-action [component event & body]
`(.addActionListener ~component
(proxy [ActionListener] []
(actionPerformed [~event] ~@body))))
(defn display-jframe [event-agent output-channel]
(let [frame (JFrame. "More Speech")
text-area (doto
(JTextPane.)
(.setContentType "text/html")
(.setText "<br><br><br><br><br><br><br><br><br><br>"))
scroll-pane (JScrollPane. text-area)
content (.getContentPane frame)
timer (Timer. 100 nil)
screen-size (.getScreenSize (Toolkit/getDefaultToolkit))
font (Font. "Courier New" Font/PLAIN, 14)]
(.addWindowListener frame (proxy [WindowListener] []
(windowActivated [_e])
(windowOpened [_e])
(windowClosed [_e])
(windowDeactivated [_e])
(windowClosing [_e]
(.stop timer)
(async/>!! output-channel [:closed])
(.dispose frame))))
(with-action timer action-event
(draw-events text-area event-agent)
)
(.setSize frame (.getWidth screen-size) (.getHeight screen-size))
(.setSize text-area (.getWidth screen-size) (.getHeight screen-size))
(.setFont text-area font)
(.add content scroll-pane BorderLayout/NORTH)
(.setVisible frame true)
(.start timer)))
(declare format-events append-event format-event)
(defn draw-events [text-area event-agent]
(prn "tick")
(when (:update @event-agent)
(let [event-state @event-agent
formatted-events (format-events event-state)]
(.setText text-area formatted-events)
(send event-agent events/updated))))
(defn format-events [{:keys [chronological-text-events nicknames text-event-map]}]
(let [header-ids (map first chronological-text-events)
headers (map #(get text-event-map %) header-ids)]
(reduce #(append-event nicknames %1 %2) "" headers)))
(defn append-event [nicknames formatted-events event]
(str formatted-events (format-event nicknames event)))
(defn format-event [nicknames {:keys [pubkey created-at content]}]
(str "<p>"
(formatters/abbreviate
(get nicknames pubkey (util/num->hex-string pubkey))
20)
" "
(formatters/format-time created-at)
" "
(formatters/abbreviate content 50)
"</b><<hr>"))