@@ -49,7 +49,7 @@ module App =
49 type ConnStatus =
49 type ConnStatus =
51 | Connecting
51 | Connecting
52− | Online of url: string * token: string
52+ | Online of token: string
54 type AuthState =
54 type AuthState =
55 | NoIdentity
55 | NoIdentity
@@ -57,10 +57,12 @@ module App =
58 type IncomingMessage = { SenderPubkey: string; Message: ChatMessage }
58 type IncomingMessage = { SenderPubkey: string; Message: ChatMessage }
60+ type PollReceived = { Events: int; Messages: int; Errors: int; FirstError: string option }
60 type PollStatus =
62 type PollStatus =
63− | Received of events: int * messages: int * errors: int * firstError: string option
65+ | Received of PollReceived
64 | PollError of string
66 | PollError of string
66 // ── Model ──
68 // ── Model ──
@@ -160,7 +162,7 @@ module App =
161 let private trySession model =
163 let private trySession model =
162 match model.Auth with
164 match model.Auth with
163− | Identified(id, Online(url, token)) -> Some { Url = url; Token = token; Identity = id }
165+ | Identified(id, Online token) -> Some { Url = model.ServerUrl; Token = token; Identity = id }
164 | _ -> None
166 | _ -> None
166 let private setConn status model =
168 let private setConn status model =
@@ -225,7 +227,7 @@ module App =
225 match model.Auth with
227 match model.Auth with
226 | Identified(id, Connecting) ->
228 | Identified(id, Connecting) ->
227 let session = { Url = model.ServerUrl; Token = token; Identity = id }
229 let session = { Url = model.ServerUrl; Token = token; Identity = id }
228− { model with Auth = Identified(id, Online(model.ServerUrl, token)); Page = Conversations; Error = None },
230+ { model with Auth = Identified(id, Online token); Page = Conversations; Error = None },
229 [ CmdPoll(session, model.PollCursor) ]
231 [ CmdPoll(session, model.PollCursor) ]
230 | _ -> model, []
232 | _ -> model, []
@@ -392,7 +394,7 @@ module App =
392 if not ackIds.IsEmpty then
394 if not ackIds.IsEmpty then
393 do! ackMessages session.Url session.Token ackIds
395 do! ackMessages session.Url session.Token ackIds
395− let status = Received(response.Events.Length, messages.Length, errors.Length, List.tryHead errors)
397+ let status = Received { Events = response.Events.Length; Messages = messages.Length; Errors = errors.Length; FirstError = List.tryHead errors }
396 return PollResult(messages, status, response.Cursor)
398 return PollResult(messages, status, response.Cursor)
398 | :? TimeoutException ->
400 | :? TimeoutException ->
@@ -429,11 +431,20 @@ module App =
429 let private formatPollStatus = function
431 let private formatPollStatus = function
430 | Idle -> ""
432 | Idle -> ""
431 | Polling -> "polling..."
433 | Polling -> "polling..."
432− | Received(evts, msgs, errs, firstErr) ->
434+ | Received r ->
433− $"evts:{evts} msgs:{msgs} errs:{errs}"
435+ $"evts:{r.Events} msgs:{r.Messages} errs:{r.Errors}"
434− + (match firstErr with Some e -> $" [{e}]" | None -> "")
436+ + (match r.FirstError with Some e -> $" [{e}]" | None -> "")
435 | PollError msg -> $"poll error: {msg}"
437 | PollError msg -> $"poll error: {msg}"
439+ let private viewErrorBanner (error: string option) =
440+ VStack(spacing = 4.) {
443+ Label(err).textColor(Colors.Red).font(size = 12.)
444+ Button("Dismiss", DismissError)
437 let private viewSetup () =
448 let private viewSetup () =
438 ContentPage(
449 ContentPage(
439 (VStack(spacing = 24.) {
450 (VStack(spacing = 24.) {
@@ -471,16 +482,11 @@ module App =
472 Label("Server:")
483 Label("Server:")
474− Entry(model.ServerUrl, SetServerUrl)
476− match model.Error with
485+ match conn with
477− | Some err ->
486+ | Online _ -> Label(model.ServerUrl).font(size = 14.)
478− Label(err)
487+ | _ -> Entry(model.ServerUrl, SetServerUrl)
479− .textColor(Colors.Red)
482− Button("Dismiss", DismissError)
489+ viewErrorBanner model.Error
485 match conn with
491 match conn with
486 | Offline -> Button("Connect", DoConnect)
492 | Offline -> Button("Connect", DoConnect)
@@ -516,11 +522,7 @@ module App =
516 .font(size = 10.)
522 .font(size = 10.)
517 .textColor(Colors.DimGray)
523 .textColor(Colors.DimGray)
519− match model.Error with
525+ viewErrorBanner model.Error
521− Label(err).textColor(Colors.Red).font(size = 12.)
522− Button("Dismiss", DismissError)
525 if model.Contacts.IsEmpty then
527 if model.Contacts.IsEmpty then
526 Label("No contacts yet. Tap + to add one.")
528 Label("No contacts yet. Tap + to add one.")
@@ -554,14 +556,7 @@ module App =
554 .centerTextHorizontal()
556 .centerTextHorizontal()
557− match model.Error with
559+ viewErrorBanner model.Error
560− .textColor(Colors.Red)
563− Button("Dismiss", DismissError)
566 if msgs.IsEmpty then
561 if msgs.IsEmpty then
567 Label("No messages yet")
562 Label("No messages yet")
@@ -593,11 +588,7 @@ module App =
593 .font(size = 20.)
588 .font(size = 20.)
596− match model.Error with
591+ viewErrorBanner model.Error
598− Label(err).textColor(Colors.Red).font(size = 12.)
599− Button("Dismiss", DismissError)
602 Label("Public Key:")
593 Label("Public Key:")
603 Entry(pubkey, SetContactPubkey)
594 Entry(pubkey, SetContactPubkey)