nandi/frqpublic Fork 0
232a455
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.

Say "frq" once, and let the guest sign-in finish

Two bugs on the phone's side of the shared screen, both from the scaffolding
that was there before it.

The title twice: connect-screen opens with [:title {:label "frq"}] and the
Scaffold had an AppBar saying the same. The desktop has no bar above that
title — a window's title belongs to the window — so the bar is gone and
SafeArea keeps the screen out from under the status bar.

And the sign-in that spun: `connect!` was writing its progress to a
frq.main-local `status` atom left over from when this file drew its own
panels, while the screen reads `cells/status`. So the label could never leave
"Not connected" however well the connection went. It writes to the cell now.

Its catch was `Exception`, which in Dart does not include Error — the two are
separate hierarchies — so anything that threw a TypeError or a
NoSuchMethodError left `connecting?` true for ever: a spinner with nothing
behind it and no message saying why. `catch Object` now, the same lesson the
blank screen taught.

Both were fixed together, so which of them the spin came from is not settled;
the status cell is certainly why nothing said it had worked.

Verified on the device: one "frq", and Connect reaches irc.freeq.at:6697 over
TLS and reports "Connected as frq-guest".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-11T20:44:43-07:00 Browse files
232a455 parent: 0bc64b5
modified flutter/src/frq/main.cljd +15 -59
@@ -32,7 +32,6 @@
3232
3333 (defonce ^:private lines (atom []))
3434 (defonce ^:private conn (atom nil))
35-(defonce ^:private status (atom "Not connected"))
3635
3736 (defn- note! [m]
3837 (swap! lines (fn [v] (vec (take-last 8 (conj v m))))))
@@ -52,7 +51,7 @@
5251 []
5352 (reset! cells/connecting? true)
5453 (reset! cells/error nil)
55- (reset! status (str "Connecting to " @cells/form-host ""))
54+ (reset! cells/status (str "Connecting to " @cells/form-host ""))
5655 (reset! lines [])
5756 (try
5857 (let [sock (await (net/connect!
@@ -64,25 +63,29 @@
6463 (note! m)
6564 (when (= "001" (:command m))
6665 (reset! cells/connecting? false)
67- (reset! status
66+ (reset! cells/status
6867 (str "Connected as " @cells/form-nick))))
6968 :on-close (fn [why]
7069 (reset! cells/connecting? false)
71- (reset! status "Not connected")
70+ (reset! cells/status "Not connected")
7271 (when why (reset! cells/error why)))}))]
7372 (reset! conn sock)
7473 (net/send-line! sock (str "NICK " @cells/form-nick))
7574 (net/send-line! sock (str "USER " @cells/form-nick " 0 * :frq")))
76- (catch Exception e
75+ ;; Object, not Exception. Dart keeps Error and Exception in separate
76+ ;; hierarchies, so a TypeError or a NoSuchMethodError is not an Exception
77+ ;; and would leave `connecting?` true for ever a spinner that never
78+ ;; stops and no message saying why, which is exactly what this did.
79+ (catch Object e
7780 (reset! cells/connecting? false)
78- (reset! status "Not connected")
81+ (reset! cells/status "Not connected")
7982 (reset! cells/error (str e)))))
8083
8184 (defn- disconnect! []
8285 (when-let [c @conn] (net/close! c))
8386 (reset! conn nil)
8487 (reset! cells/connecting? false)
85- (reset! status "Not connected"))
88+ (reset! cells/status "Not connected"))
8689
8790 (defonce ^:private handle (atom "nandi-test.bsky.social"))
8891 (defonce ^:private identity-out (atom nil))
@@ -97,57 +100,6 @@
97100 (catch Exception e
98101 (reset! identity-out [(str "failed: " e)]))))
99102
100-(defn- screen
101- "The phone's panels, in glimmer's tags.
102-
103- NOT `frq.screens.connect` yet, and the reason is worth writing down rather
104- than leaving as a TODO: the extraction is done and the desktop renders the
105- shared file, but rendering it here paints nothing at all — no exception,
106- nothing in logcat, and the sibling widgets in the same `children` vector
107- disappear with it, which is what a Dart `Error` thrown while that vector is
108- being built looks like. `(catch Object ...)` around it did not surface one
109- either, so the failure is not where it appears to be.
110-
111- What is already proven: `frq.cells` compiles under both compilers and holds
112- its values here — a probe read `cells/status` as \"Not connected\" and
113- `cells/form-nick` as \"frq-guest\" on the device. So the seam is sound and
114- what is left is one cljd-shaped bug between it and the screen."
115- []
116- [:page {:max-width 520}
117- [:title {:label "frq"}]
118- [:dim-label {:label "Painted by frq.hiccup in COSMIC's own theme."}]
119- [:card {}
120- [:title-2 {:label "Connection"}]
121- [:label {:label @status}]
122- [:hbox {:spacing 8}
123- [:button {:label "Connect" :kind :primary :on-click #(connect!)}]
124- [:button {:label "Disconnect" :destructive true :on-click #(disconnect!)}]]]
125- [:card {}
126- [:title-2 {:label "What the server said"}]
127- (if (empty? @lines)
128- [:dim-label {:label "nothing yet"}]
129- (for [[i m] (map-indexed vector @lines)]
130- [:label {:key i
131- :label (str (:command m)
132- (when-let [p (seq (:params m))]
133- (str " " (last p))))}]))]
134- [:card {}
135- [:title-2 {:label "Identity"}]
136- [:entry {:key :handle
137- :text @handle
138- :placeholder "alice.bsky.social"
139- :on-change #(reset! handle %)}]
140- [:button {:label "Resolve" :kind :primary :on-click #(resolve-identity!)}]
141- (when-let [out @identity-out]
142- (for [[i line] (map-indexed vector out)]
143- [:label {:key i :label line}]))]
144- [:card {}
145- [:title-2 {:label "Shared with the desktop"}]
146- [:label {:label (str "clock " (clock/clock-time (clock/now-ms)))}]
147- [:label {:label (str "cells " @cells/form-nick " @ " @cells/form-host
148- ":" @cells/form-port)}]
149- [:dim-label {:label "frq.cells, compiled by both."}]]])
150-
151103 (defn ^:async main []
152104 (m/WidgetsFlutterBinding.ensureInitialized)
153105 (let [dir (.-path (await (pp/getApplicationSupportDirectory)))]
@@ -158,8 +110,12 @@
158110 (f/run
159111 (m/MaterialApp .title "frq" .theme (t/app-theme))
160112 .home
161- (m/Scaffold .appBar (m/AppBar .title (m/Text "frq")))
113+ ;; No AppBar. `connect-screen` opens with [:title {:label "frq"}] the
114+ ;; desktop has no bar above it either, the window's title is the
115+ ;; window's so a bar here says "frq" twice.
116+ (m/Scaffold)
162117 .body
118+ m/SafeArea
163119 (f/widget
164120 :let [c-status cells/status
165121 c-error cells/error
@@ -32,7 +32,6 @@
32 32
33 (defonce ^:private lines (atom []))33 (defonce ^:private lines (atom []))
34 (defonce ^:private conn (atom nil))34 (defonce ^:private conn (atom nil))
35-(defonce ^:private status (atom "Not connected"))
36 35
37 (defn- note! [m]36 (defn- note! [m]
38 (swap! lines (fn [v] (vec (take-last 8 (conj v m))))))37 (swap! lines (fn [v] (vec (take-last 8 (conj v m))))))
@@ -52,7 +51,7 @@
52 []51 []
53 (reset! cells/connecting? true)52 (reset! cells/connecting? true)
54 (reset! cells/error nil)53 (reset! cells/error nil)
55- (reset! status (str "Connecting to " @cells/form-host ""))54+ (reset! cells/status (str "Connecting to " @cells/form-host ""))
56 (reset! lines [])55 (reset! lines [])
57 (try56 (try
58 (let [sock (await (net/connect!57 (let [sock (await (net/connect!
@@ -64,25 +63,29 @@
64 (note! m)63 (note! m)
65 (when (= "001" (:command m))64 (when (= "001" (:command m))
66 (reset! cells/connecting? false)65 (reset! cells/connecting? false)
67- (reset! status66+ (reset! cells/status
68 (str "Connected as " @cells/form-nick))))67 (str "Connected as " @cells/form-nick))))
69 :on-close (fn [why]68 :on-close (fn [why]
70 (reset! cells/connecting? false)69 (reset! cells/connecting? false)
71- (reset! status "Not connected")70+ (reset! cells/status "Not connected")
72 (when why (reset! cells/error why)))}))]71 (when why (reset! cells/error why)))}))]
73 (reset! conn sock)72 (reset! conn sock)
74 (net/send-line! sock (str "NICK " @cells/form-nick))73 (net/send-line! sock (str "NICK " @cells/form-nick))
75 (net/send-line! sock (str "USER " @cells/form-nick " 0 * :frq")))74 (net/send-line! sock (str "USER " @cells/form-nick " 0 * :frq")))
76- (catch Exception e75+ ;; Object, not Exception. Dart keeps Error and Exception in separate
76+ ;; hierarchies, so a TypeError or a NoSuchMethodError is not an Exception
77+ ;; and would leave `connecting?` true for ever a spinner that never
78+ ;; stops and no message saying why, which is exactly what this did.
79+ (catch Object e
77 (reset! cells/connecting? false)80 (reset! cells/connecting? false)
78- (reset! status "Not connected")81+ (reset! cells/status "Not connected")
79 (reset! cells/error (str e)))))82 (reset! cells/error (str e)))))
80 83
81 (defn- disconnect! []84 (defn- disconnect! []
82 (when-let [c @conn] (net/close! c))85 (when-let [c @conn] (net/close! c))
83 (reset! conn nil)86 (reset! conn nil)
84 (reset! cells/connecting? false)87 (reset! cells/connecting? false)
85- (reset! status "Not connected"))88+ (reset! cells/status "Not connected"))
86 89
87 (defonce ^:private handle (atom "nandi-test.bsky.social"))90 (defonce ^:private handle (atom "nandi-test.bsky.social"))
88 (defonce ^:private identity-out (atom nil))91 (defonce ^:private identity-out (atom nil))
@@ -97,57 +100,6 @@
97 (catch Exception e100 (catch Exception e
98 (reset! identity-out [(str "failed: " e)]))))101 (reset! identity-out [(str "failed: " e)]))))
99 102
100-(defn- screen
101- "The phone's panels, in glimmer's tags.
102-
103- NOT `frq.screens.connect` yet, and the reason is worth writing down rather
104- than leaving as a TODO: the extraction is done and the desktop renders the
105- shared file, but rendering it here paints nothing at all — no exception,
106- nothing in logcat, and the sibling widgets in the same `children` vector
107- disappear with it, which is what a Dart `Error` thrown while that vector is
108- being built looks like. `(catch Object ...)` around it did not surface one
109- either, so the failure is not where it appears to be.
110-
111- What is already proven: `frq.cells` compiles under both compilers and holds
112- its values here — a probe read `cells/status` as \"Not connected\" and
113- `cells/form-nick` as \"frq-guest\" on the device. So the seam is sound and
114- what is left is one cljd-shaped bug between it and the screen."
115- []
116- [:page {:max-width 520}
117- [:title {:label "frq"}]
118- [:dim-label {:label "Painted by frq.hiccup in COSMIC's own theme."}]
119- [:card {}
120- [:title-2 {:label "Connection"}]
121- [:label {:label @status}]
122- [:hbox {:spacing 8}
123- [:button {:label "Connect" :kind :primary :on-click #(connect!)}]
124- [:button {:label "Disconnect" :destructive true :on-click #(disconnect!)}]]]
125- [:card {}
126- [:title-2 {:label "What the server said"}]
127- (if (empty? @lines)
128- [:dim-label {:label "nothing yet"}]
129- (for [[i m] (map-indexed vector @lines)]
130- [:label {:key i
131- :label (str (:command m)
132- (when-let [p (seq (:params m))]
133- (str " " (last p))))}]))]
134- [:card {}
135- [:title-2 {:label "Identity"}]
136- [:entry {:key :handle
137- :text @handle
138- :placeholder "alice.bsky.social"
139- :on-change #(reset! handle %)}]
140- [:button {:label "Resolve" :kind :primary :on-click #(resolve-identity!)}]
141- (when-let [out @identity-out]
142- (for [[i line] (map-indexed vector out)]
143- [:label {:key i :label line}]))]
144- [:card {}
145- [:title-2 {:label "Shared with the desktop"}]
146- [:label {:label (str "clock " (clock/clock-time (clock/now-ms)))}]
147- [:label {:label (str "cells " @cells/form-nick " @ " @cells/form-host
148- ":" @cells/form-port)}]
149- [:dim-label {:label "frq.cells, compiled by both."}]]])
150-
151 (defn ^:async main []103 (defn ^:async main []
152 (m/WidgetsFlutterBinding.ensureInitialized)104 (m/WidgetsFlutterBinding.ensureInitialized)
153 (let [dir (.-path (await (pp/getApplicationSupportDirectory)))]105 (let [dir (.-path (await (pp/getApplicationSupportDirectory)))]
@@ -158,8 +110,12 @@
158 (f/run110 (f/run
159 (m/MaterialApp .title "frq" .theme (t/app-theme))111 (m/MaterialApp .title "frq" .theme (t/app-theme))
160 .home112 .home
161- (m/Scaffold .appBar (m/AppBar .title (m/Text "frq")))113+ ;; No AppBar. `connect-screen` opens with [:title {:label "frq"}] the
114+ ;; desktop has no bar above it either, the window's title is the
115+ ;; window's so a bar here says "frq" twice.
116+ (m/Scaffold)
162 .body117 .body
118+ m/SafeArea
163 (f/widget119 (f/widget
164 :let [c-status cells/status120 :let [c-status cells/status
165 c-error cells/error121 c-error cells/error