diff --git a/spec/more_speech/ui/formatters_spec.clj b/spec/more_speech/ui/formatters_spec.clj index 196c51b..c8737ad 100644 --- a/spec/more_speech/ui/formatters_spec.clj +++ b/spec/more_speech/ui/formatters_spec.clj @@ -1,12 +1,12 @@ (ns more-speech.ui.formatters-spec - (:require [speclj.core :refer :all] + (:require [more-speech.config :as config] [more-speech.db.gateway :as gateway] [more-speech.db.in-memory :as in-memory] - [more-speech.nostr.util :refer [hexify]] - [more-speech.ui.formatters :refer :all] - [more-speech.ui.formatter-util :refer :all] [more-speech.mem :refer :all] - [more-speech.config :as config])) + [more-speech.nostr.util :refer [hexify]] + [more-speech.ui.formatter-util :refer :all] + [more-speech.ui.formatters :refer :all] + [speclj.core :refer :all])) (describe "Abbreviations." (it "abbreviates pubkeys" @@ -241,12 +241,30 @@ (segment-article "@name text"))) (it "returns an :idreference segment" - (should= [[:idreference "@0000000000000000000000000000000000000000000000000000000000000000"] [:text " text"]] - (segment-article "@0000000000000000000000000000000000000000000000000000000000000000 text"))) + (should= [[:idreference "@0000000000000000000000000000000000000000000000000000000000000000"] [:text " text"]] + (segment-article "@0000000000000000000000000000000000000000000000000000000000000000 text"))) (it "returns a list of :text and :url and :namereference segments" (should= [[:text "Hey "] [:namereference "@bob"] [:text " Check this "] [:url "http://nostr.com"] [:text " It's cool"]] - (segment-article "Hey @bob Check this http://nostr.com It's cool"))) + (segment-article "Hey @bob Check this http://nostr.com It's cool")) + (should= [[:namereference "npub1qq"] [:text " "] + [:namereference "@npub1qq"] [:text " "] + [:nostrnamereference "nostr:npub1qq"] [:text " "] + [:nostrnamereference "@nostr:npub1qq"]] + (segment-article "npub1qq @npub1qq nostr:npub1qq @nostr:npub1qq")) + (should= [[:notereference "note1qq"] [:text " "] + [:notereference "@note1qq"] [:text " "] + [:nostrnotereference "nostr:note1qq"] [:text " "] + [:nostrnotereference "@nostr:note1qq"]] + (segment-article "note1qq @note1qq nostr:note1qq @nostr:note1qq"))) + (it "extracts text from segments" + (should= "name" (extract-reference "@name")) + (should= "npub1qq" (extract-reference "npub1qq")) + (should= "x" (extract-reference "nostr:x")) + (should= "x" (extract-reference "@nostr:x")) + ) + + ) (describe "Format article" @@ -257,11 +275,11 @@ (should= "nostr.com" (reformat-article-into-html "https://nostr.com"))) (it "should ms-link a namereference" - (should= "@name" + (should= "@name" (reformat-article-into-html "@name"))) (it "should ms-link an idreference" - (should= "@0000000000000000000000000000000000000000000000000000000000000000" + (should= "@0000000000000000000000000000000000000000000000000000000000000000" (reformat-article-into-html "@0000000000000000000000000000000000000000000000000000000000000000"))) (it "should escape HTML entities and linkify url" @@ -274,7 +292,6 @@ (it "should replace multiple spaces with  " (should= "one two  three   ." (reformat-article-into-html "one two three ."))) - ) (declare db) @@ -337,9 +354,9 @@ (should= "(?pattern1)" (str pattern)))) (it "combines multiple patterns and names" - (let [pattern (combine-patterns [:name1 #"pattern1"] - [:name2 #"pattern2"])] - (should= java.util.regex.Pattern (type pattern)) - (should= "(?pattern1)|(?pattern2)" (str pattern)))) + (let [pattern (combine-patterns [:name1 #"pattern1"] + [:name2 #"pattern2"])] + (should= java.util.regex.Pattern (type pattern)) + (should= "(?pattern1)|(?pattern2)" (str pattern)))) ) diff --git a/src/more_speech/config.clj b/src/more_speech/config.clj index 304ed7b..19b8f50 100644 --- a/src/more_speech/config.clj +++ b/src/more_speech/config.clj @@ -56,13 +56,6 @@ (def user-configuration-filename (atom "private/user-configuration")) (def contact-lists-filename (atom "private/ub-contacts")) -(def npub-reference #"npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+") -(def user-reference-pattern #"(?:\@[\w\-]+)|(?:npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+)") -(def id-reference-pattern #"\@[0-9a-f]{64}") -(def hex-key-pattern #"[0-9a-f]{64}") -(def user-name-chars #"[\w\-]+") -(def reference-pattern #"\#\[\d+\]") - (def proof-of-work-default 12) (def max-nodes-per-tab 1000) (def prune-tabs-frequency-in-minutes 60) @@ -77,10 +70,21 @@ ;; https://daringfireball.net/2010/07/improved_regex_for_matching_urls (def url-pattern #"(?i)\b(?:(?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(?:(?:[^\s()<>]+|(?:\(?:[^\s()<>]+\)))*\))+(?:\(?:(?:[^\s()<>]+|(?:\(?:[^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))") +(def nostr-note-reference-pattern #"(?:\@)?nostr:note1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+") +(def note-reference-pattern #"(?:\@)?note1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+") +(def nostr-user-reference-pattern #"(?:\@)?nostr:npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+") +(def user-reference-pattern #"(?:\@[\w\-]+)|(?:\@)?(?:npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+)") +(def id-reference-pattern #"\@[0-9a-f]{64}") + (def email-pattern #"[\w.-]+@[\w.-]+") (def relay-pattern #"ws+://[\w.-]+/?") (def lud16-pattern #"([a-z0-9-_.]+)@([a-z0-9-_.]+\.[a-z0-9-_]+)") +(def npub-reference #"npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+") +(def hex-key-pattern #"[0-9a-f]{64}") +(def user-name-chars #"[\w\-]+") +(def reference-pattern #"\#\[\d+\]") + (def production-db :xtdb) (def db-type (atom nil)) diff --git a/src/more_speech/nostr/protocol.clj b/src/more_speech/nostr/protocol.clj index 7cf660d..7d66ee0 100644 --- a/src/more_speech/nostr/protocol.clj +++ b/src/more_speech/nostr/protocol.clj @@ -18,23 +18,38 @@ date (Date. (long time))] (.format (SimpleDateFormat. "MM/dd/yyyy kk:mm:ss z") date))) +(defn send-request [request close] + (doseq [url (keys @relays)] + (when (not= :read-none (get-in @relays [url :read])) + (let [relay (:connection (get @relays url))] + (when (some? relay) + (relay/send relay request) + (future (do (Thread/sleep 2000) + (relay/send relay close)))))))) + +(defn- make-request-id [] + (let [r (rand-int 1000000)] + (str "ms-request-" r))) + +(defn request-note [id] + (let [req-id (make-request-id) + request ["REQ" req-id {"kinds" [1] "ids" [(util/hexify id)]} ] + close ["CLOSE" req-id]] + (send-request request close)) + ) + (defn request-profiles-and-contacts-for [authors] (let [authors (if (coll? authors) authors [authors]) hexified-authors (map util/hexify authors) trimmed-authors (if (<= (count hexified-authors) 100) hexified-authors (map #(subs % 0 10) (take 1000 (shuffle hexified-authors)))) - r (rand-int 1000000)] - (doseq [url (keys @relays)] - (when (not= :read-none (get-in @relays [url :read])) - (let [relay (:connection (get @relays url))] - (when (some? relay) - (relay/send relay - ["REQ" (str "ms-authors-" r) {"kinds" [0 3] - "authors" trimmed-authors}]) - (future (do (Thread/sleep 2000) - (relay/send relay - ["CLOSE" (str "ms-authors-" r)]))))))))) + r (rand-int 1000000) + request ["REQ" (str "ms-request-" r) {"kinds" [0 3] + "authors" trimmed-authors}] + close ["CLOSE" (str "ms-request-" r)]] + (send-request request close) + )) (defn request-contact-lists [relay] (let [now (quot (System/currentTimeMillis) 1000) diff --git a/src/more_speech/ui/formatters.clj b/src/more_speech/ui/formatters.clj index 01a5802..3dc3be1 100644 --- a/src/more_speech/ui/formatters.clj +++ b/src/more_speech/ui/formatters.clj @@ -113,7 +113,7 @@ [reply-id _ _] (events/get-references event) reply-mark (if (some? reply-id) "^" " ") dm-mark (if (= 4 (:kind event)) (make-dm-mark event) "") - zap-mark (if (some? zaps) "❗ " "") + zap-mark (if (some? zaps) "❗⚡ " "") reaction-mark (make-reaction-mark event) header-text (-> content (string/replace \newline \~) (abbreviate 130)) content (if (empty? subject) @@ -162,7 +162,7 @@ (str "" uri ""))) (defn ms-linkify [type subject] - (str "" subject "")) + (str "@" subject "")) (defn img-ify [seg] (str "
" (linkify seg))) @@ -189,9 +189,13 @@ (segment-article content [])) ([content segments] - (let [patterns [[:url config/url-pattern] + (let [patterns [[:nostrnotereference config/nostr-note-reference-pattern] + [:nostrnamereference config/nostr-user-reference-pattern] + [:notereference config/note-reference-pattern] [:idreference config/id-reference-pattern] - [:namereference config/user-reference-pattern]] + [:namereference config/user-reference-pattern] + + [:url config/url-pattern]] pattern (apply combine-patterns patterns) group-names (map first patterns)] (loop [content content @@ -220,12 +224,19 @@ :else (concat segments (list [:text content])))))))) +(defn extract-reference [s] + (cond + (.startsWith s "nostr:") (subs s 6) + (.startsWith s "@nostr:") (subs s 7) + (.startsWith s "@") (subs s 1) + :else s)) + (defn reformat-article-into-html [article] (let [segments (segment-article article)] (reduce (fn [formatted-content [seg-type seg]] - (condp = seg-type - :text + (cond + (= seg-type :text) (str formatted-content ((comp non-breaking-spaces @@ -234,18 +245,23 @@ format-replies ) seg) ) - :url + + (= seg-type :url) (str formatted-content (linkify seg)) - :namereference - (str formatted-content (ms-linkify "ms-namereference" seg)) + (or (= seg-type :namereference) (= seg-type :nostrnamereference)) + (str formatted-content (ms-linkify "ms-namereference" (extract-reference seg))) - :idreference - (str formatted-content (ms-linkify "ms-idreference" seg)) + (= seg-type :idreference) + (str formatted-content (ms-linkify "ms-idreference" (subs seg 1))) - :img + (or (= seg-type :notereference) (= seg-type :nostrnotereference)) + (str formatted-content (ms-linkify "ms-notereference" (extract-reference seg))) + + (= seg-type :img) (str formatted-content (img-ify seg)) + :else formatted-content )) "" diff --git a/src/more_speech/ui/swing/article_panel.clj b/src/more_speech/ui/swing/article_panel.clj index 87a436c..3b5c778 100644 --- a/src/more_speech/ui/swing/article_panel.clj +++ b/src/more_speech/ui/swing/article_panel.clj @@ -273,7 +273,7 @@ (text! up-arrow "👍🏻") (text! dn-arrow "👎🏻"))) (if zapped? - (text! zap-icon "❗ ") + (text! zap-icon "❗⚡ ") (text! zap-icon "")) (swing-util/clear-popup relays-popup) (swing-util/clear-popup reactions-popup) @@ -328,7 +328,8 @@ (.startsWith subject "npub") (bech32/address->number subject) - :else nil)) + :else + (gateway/get-id-from-username (get-db) subject))) (defn get-user-info [subject _e] (when-let [id (get-user-id-from-subject subject)] @@ -345,7 +346,7 @@ (defn open-link [e] (when (= HyperlinkEvent$EventType/ACTIVATED (.getEventType e)) (when-let [url (str (.getURL e))] - (let [[type subject] (string/split (.getDescription e) #"://")] + (let [[type subject] (string/split (.getDescription e) #"\:\/\/")] (cond (or (= type "http") (= type "https")) (try @@ -355,7 +356,13 @@ (log-pr 1 ex))) (= type "ms-idreference") - (let [id (util/unhexify (subs subject 1))] + (let [id (util/unhexify subject)] + (protocol/request-note id) + (swing-util/select-event id)) + + (= type "ms-notereference") + (let [id (bech32/address->number subject)] + (protocol/request-note id) (swing-util/select-event id)) (= type "ms-namereference")