nandi/frqpublic Fork 0
879ae18
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Tag the Flutter line that answers as a reply

The composer's banner was the only place the reply existed: send built its
PRIVMSG by hand and never read `replying-to`, so the `+draft/reply` tag every
other freeq client threads on went out missing, and the banner stayed up to
aim the next line at the same message.

The line is `frq.irc.mutate`'s now — say-line, beside edit-line, unsigned
because a new message is not a change to one already said — so both halves
tag a reply the same way. jolt's `privmsg!` writes it rather than its own
string, which also gets the tag value escaped the way IRCv3 asks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-12T11:45:20-07:00 Browse files
879ae18 parent: f218f2a
modified common/frq/irc/mutate.cljc +13 -0
@@ -65,3 +65,16 @@
6565 (tag-line (assoc (msgsig/edit-tags target msgid text nil peer-did)
6666 "+draft/edit" msgid)
6767 (str "PRIVMSG " target " :" text))))
68+
69+(defn say-line
70+ "Say something. With `reply-to`, say it as an answer to that message.
71+
72+ The `+draft/reply` tag is what every other freeq client reads to thread it,
73+ and what both of ours draw their chips from. Unsigned, unlike the mutations
74+ above: a new message is not a change to one already said, and the server
75+ asks for no proof of authorship beyond the account it came in on."
76+ ([target text] (say-line target text nil))
77+ ([target text reply-to]
78+ (if (seq reply-to)
79+ (tag-line {"+draft/reply" reply-to} (str "PRIVMSG " target " :" text))
80+ (str "PRIVMSG " target " :" text))))
@@ -65,3 +65,16 @@
65 (tag-line (assoc (msgsig/edit-tags target msgid text nil peer-did)65 (tag-line (assoc (msgsig/edit-tags target msgid text nil peer-did)
66 "+draft/edit" msgid)66 "+draft/edit" msgid)
67 (str "PRIVMSG " target " :" text))))67 (str "PRIVMSG " target " :" text))))
68+
69+(defn say-line
70+ "Say something. With `reply-to`, say it as an answer to that message.
71+
72+ The `+draft/reply` tag is what every other freeq client reads to thread it,
73+ and what both of ours draw their chips from. Unsigned, unlike the mutations
74+ above: a new message is not a change to one already said, and the server
75+ asks for no proof of authorship beyond the account it came in on."
76+ ([target text] (say-line target text nil))
77+ ([target text reply-to]
78+ (if (seq reply-to)
79+ (tag-line {"+draft/reply" reply-to} (str "PRIVMSG " target " :" text))
80+ (str "PRIVMSG " target " :" text))))
modified flutter/src/frq/main.cljd +12 -3
@@ -921,7 +921,8 @@
921921 (defn- send-draft! []
922922 (let [text (str @cells/draft)
923923 room (str @cells/current)
924- edit @cells/editing]
924+ edit @cells/editing
925+ reply-to @cells/replying-to]
925926 (cond
926927 (empty? room) nil
927928
@@ -980,18 +981,26 @@
980981 ;; show. A line that is only a picture is only the link.
981982 text (.trim (str text (when url (str " " url))))]
982983 (do
983- (net/send-line! @conn (str "PRIVMSG " room " :" text))
984+ ;; The `+draft/reply` tag is what makes this an answer rather than a
985+ ;; line that happens to follow: without it the banner over the box is
986+ ;; the only place the reply ever existed, and it goes out untagged.
987+ (net/send-line! @conn (mutate/say-line room text (:id reply-to)))
984988 ;; Echoed locally only when the server will not echo it back. With
985989 ;; `echo-message` negotiated it does — that is what the cap is for, and
986990 ;; it is how a client learns the msgid of its own line — so adding one
987991 ;; here as well put every sent message in the room twice.
988992 (when-not (handshake/acked? @caps "echo-message")
989993 (swap! cells/channels update room
990- #(update % :messages conj {:from @cells/form-nick :text text})))
994+ #(update % :messages conj {:from @cells/form-nick :text text
995+ :reply-to (:id reply-to)})))
991996 ;; The attachment has done its job the moment the link is on the wire.
992997 (when-let [a @cells/attachment]
993998 (reset! cells/attachment nil)
994999 (discard! (:path a)))
1000+ ;; The banner comes down with the send: it is the question this line
1001+ ;; just answered, and leaving it up would aim the next line at the same
1002+ ;; message.
1003+ (reset! cells/replying-to nil)
9951004 (reset! cells/draft ""))))))
9961005
9971006 (defn- message-by-id [room id]
@@ -921,7 +921,8 @@
921 (defn- send-draft! []921 (defn- send-draft! []
922 (let [text (str @cells/draft)922 (let [text (str @cells/draft)
923 room (str @cells/current)923 room (str @cells/current)
924- edit @cells/editing]924+ edit @cells/editing
925+ reply-to @cells/replying-to]
925 (cond926 (cond
926 (empty? room) nil927 (empty? room) nil
927 928
@@ -980,18 +981,26 @@
980 ;; show. A line that is only a picture is only the link.981 ;; show. A line that is only a picture is only the link.
981 text (.trim (str text (when url (str " " url))))]982 text (.trim (str text (when url (str " " url))))]
982 (do983 (do
983- (net/send-line! @conn (str "PRIVMSG " room " :" text))984+ ;; The `+draft/reply` tag is what makes this an answer rather than a
985+ ;; line that happens to follow: without it the banner over the box is
986+ ;; the only place the reply ever existed, and it goes out untagged.
987+ (net/send-line! @conn (mutate/say-line room text (:id reply-to)))
984 ;; Echoed locally only when the server will not echo it back. With988 ;; Echoed locally only when the server will not echo it back. With
985 ;; `echo-message` negotiated it does — that is what the cap is for, and989 ;; `echo-message` negotiated it does — that is what the cap is for, and
986 ;; it is how a client learns the msgid of its own line — so adding one990 ;; it is how a client learns the msgid of its own line — so adding one
987 ;; here as well put every sent message in the room twice.991 ;; here as well put every sent message in the room twice.
988 (when-not (handshake/acked? @caps "echo-message")992 (when-not (handshake/acked? @caps "echo-message")
989 (swap! cells/channels update room993 (swap! cells/channels update room
990- #(update % :messages conj {:from @cells/form-nick :text text})))994+ #(update % :messages conj {:from @cells/form-nick :text text
995+ :reply-to (:id reply-to)})))
991 ;; The attachment has done its job the moment the link is on the wire.996 ;; The attachment has done its job the moment the link is on the wire.
992 (when-let [a @cells/attachment]997 (when-let [a @cells/attachment]
993 (reset! cells/attachment nil)998 (reset! cells/attachment nil)
994 (discard! (:path a)))999 (discard! (:path a)))
1000+ ;; The banner comes down with the send: it is the question this line
1001+ ;; just answered, and leaving it up would aim the next line at the same
1002+ ;; message.
1003+ (reset! cells/replying-to nil)
995 (reset! cells/draft ""))))))1004 (reset! cells/draft ""))))))
996 1005
997 (defn- message-by-id [room id]1006 (defn- message-by-id [room id]
modified src/frq/irc.clj +3 -3
@@ -234,11 +234,11 @@
234234 (defn privmsg!
235235 "Say something. With `reply-to`, say it as an answer to that message: the
236236 `+draft/reply` tag is what every other freeq client reads to thread it, and
237- what this one draws its chips from."
237+ what this one draws its chips from. The line is `frq.irc.mutate`'s, so both
238+ halves tag a reply the same way; this writes it."
238239 ([conn target text] (privmsg! conn target text nil))
239240 ([conn target text reply-to]
240- (send-line! conn (str (when (seq reply-to) (str "@+draft/reply=" reply-to " "))
241- "PRIVMSG " target " :" text))))
241+ (send-line! conn (mutate/say-line target text reply-to))))
242242
243243 (defn edit!
244244 "Rewrite something already said. The line is `frq.irc.mutate`'s; this writes
@@ -234,11 +234,11 @@
234 (defn privmsg!234 (defn privmsg!
235 "Say something. With `reply-to`, say it as an answer to that message: the235 "Say something. With `reply-to`, say it as an answer to that message: the
236 `+draft/reply` tag is what every other freeq client reads to thread it, and236 `+draft/reply` tag is what every other freeq client reads to thread it, and
237- what this one draws its chips from."237+ what this one draws its chips from. The line is `frq.irc.mutate`'s, so both
238+ halves tag a reply the same way; this writes it."
238 ([conn target text] (privmsg! conn target text nil))239 ([conn target text] (privmsg! conn target text nil))
239 ([conn target text reply-to]240 ([conn target text reply-to]
240- (send-line! conn (str (when (seq reply-to) (str "@+draft/reply=" reply-to " "))241+ (send-line! conn (mutate/say-line target text reply-to))))
241- "PRIVMSG " target " :" text))))
242 242
243 (defn edit!243 (defn edit!
244 "Rewrite something already said. The line is `frq.irc.mutate`'s; this writes244 "Rewrite something already said. The line is `frq.irc.mutate`'s; this writes