@@ -37,10 +37,8 @@ module App =
3737 | NoIdentity
3838 | Identified of identity: Identity * conn: ConnStatus
3939
40− type IncomingMessage = { SenderPubkey: string; Message: ChatMessage }
41−
4240 type DecryptedEvent =
43− | IncomingChat of IncomingMessage
41+ | IncomingChat of senderPubkey: string * message: ChatMessage
4442 | IncomingDeliveryAck of senderPubkey: string * ackIds: string list
4543 | IncomingProfile of senderPubkey: string * displayName: string * bio: string * photo: string option
4644
@@ -138,8 +136,8 @@ module App =
138136 |> List.tryFind (fun c -> c.Pubkey = pk)
139137 |> Option.map (fun c ->
140138 if c.Nickname <> fallback then c.Nickname
141− elif not (String.IsNullOrEmpty c.DisplayName) then c.DisplayName
142− else c.Nickname)
139+ elif c.DisplayName <> "" then c.DisplayName
140+ else fallback)
143141 |> Option.defaultValue fallback
144142
145143 let private messagesFor (pk: string) (messages: Map<string, ChatMessage list>) =
@@ -261,13 +259,12 @@ module App =
261259 Page = Chat(pk, "")
262260 Messages = model.Messages |> appendMessage pk outMsg }
263261
264− let cmds = [ CmdSend(session, pk, compose, id); saveCmdMsg model' ]
265− let cmds =
266− match model.Profile, isFirstInteraction with
267− | Some profile, true -> cmds @ [ CmdSendProfileToAll(session, profile, [ pk ]) ]
268− | _ -> cmds
269−
270− model', cmds
262+ model',
263+ [ CmdSend(session, pk, compose, id)
264+ saveCmdMsg model'
265+ match model.Profile, isFirstInteraction with
266+ | Some profile, true -> CmdSendProfileToAll(session, profile, [ pk ])
267+ | _ -> () ]
271268 | _ -> model, []
272269
273270 | SendFailed err -> { model with Error = Some err }, []
@@ -279,10 +276,10 @@ module App =
279276 |> List.fold
280277 (fun (m: Model) evt ->
281278 match evt with
282− | IncomingChat msg ->
279+ | IncomingChat(senderPubkey, message) ->
283280 { m with
284− Contacts = m.Contacts |> addContactIfNew { Pubkey = msg.SenderPubkey; Nickname = truncKey msg.SenderPubkey; DisplayName = null; Bio = null; PhotoBase64 = null }
285− Messages = m.Messages |> appendMessage msg.SenderPubkey msg.Message }
281+ Contacts = m.Contacts |> addContactIfNew { Pubkey = senderPubkey; Nickname = truncKey senderPubkey; DisplayName = ""; Bio = ""; PhotoBase64 = "" }
282+ Messages = m.Messages |> appendMessage senderPubkey message }
286283 | IncomingDeliveryAck(senderPubkey, ackIds) ->
287284 { m with Messages = m.Messages |> markDelivered senderPubkey ackIds }
288285 | IncomingProfile(senderPubkey, displayName, bio, photo) ->
@@ -291,7 +288,7 @@ module App =
291288 m.Contacts
292289 |> List.map (fun c ->
293290 if c.Pubkey = senderPubkey then
294− { c with DisplayName = displayName; Bio = bio; PhotoBase64 = photo |> Option.defaultValue null }
291+ { c with DisplayName = displayName; Bio = bio; PhotoBase64 = photo |> Option.defaultValue "" }
295292 else c) })
296293 { model with PollStatus = status; PollCursor = newCursor }
297294
@@ -311,7 +308,7 @@ module App =
311308 let contact: Contact =
312309 match existing with
313310 | Some e -> { e with Nickname = cnn }
314− | None -> { Pubkey = hex; Nickname = cnn; DisplayName = null; Bio = null; PhotoBase64 = null }
311+ | None -> { Pubkey = hex; Nickname = cnn; DisplayName = ""; Bio = ""; PhotoBase64 = "" }
315312
316313 let model' =
317314 { model with
@@ -350,13 +347,11 @@ module App =
350347 match model.Page with
351348 | EditProfile(name, bio, photo) ->
352349 let profile: Profile = { DisplayName = name; Bio = bio; PhotoBase64 = photo }
353− let cmds = [ CmdSaveProfile profile ]
354− let cmds =
355− match trySession model with
356− | Some session ->
357− cmds @ [ CmdSendProfileToAll(session, profile, model.Contacts |> List.map _.Pubkey) ]
358− | None -> cmds
359− { model with Profile = Some profile; Page = Settings }, cmds
350+ { model with Profile = Some profile; Page = Settings },
351+ [ CmdSaveProfile profile
352+ match trySession model with
353+ | Some session -> CmdSendProfileToAll(session, profile, model.Contacts |> List.map _.Pubkey)
354+ | None -> () ]
360355 | _ -> model, []
361356
362357 | StateLoaded(IdentityAndData(identity, data, profile)) ->
@@ -397,6 +392,14 @@ module App =
397392 | ProfileMessage(id, displayName, bio, photo) ->
398393 JsonSerializer.Serialize({| ``type`` = "profile"; id = id; timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); display_name = displayName; bio = bio; photo = (photo |> Option.defaultValue null) |})
399394
395+ let private sendEnvelope (session: Session) (recipientHex: string) (envelope: Envelope) =
396+ async {
397+ let payload = serializeEnvelope envelope
398+ let blob = Crypto.encrypt session.Identity.PrivKey (Crypto.fromHex recipientHex) payload
399+ let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
400+ return! sendMessage session.Url session.Token recipientHex (Crypto.toHex blob) ts
401+ }
402+
400403 let private parseEnvelope (json: string) =
401404 use doc = JsonDocument.Parse(json)
402405 let root = doc.RootElement
@@ -427,13 +430,11 @@ module App =
427430 | Some(plaintext, senderHex) ->
428431 match parseEnvelope plaintext with
429432 | Some(TextMessage(id, body)) ->
430− Ok(IncomingChat
431− { SenderPubkey = senderHex
432− Message =
433− { Id = id; Body = body
434− Timestamp = DateTimeOffset.FromUnixTimeSeconds(payload.Timestamp)
435− IsOutgoing = false
436− Status = DeliveryStatus.Delivered } })
433+ Ok(IncomingChat(senderHex,
434+ { Id = id; Body = body
435+ Timestamp = DateTimeOffset.FromUnixTimeSeconds(payload.Timestamp)
436+ IsOutgoing = false
437+ Status = DeliveryStatus.Delivered }))
437438 | Some(DeliveryAck(_, ackIds)) ->
438439 Ok(IncomingDeliveryAck(senderHex, ackIds))
439440 | Some(ProfileMessage(_, displayName, bio, photo)) ->
@@ -447,15 +448,12 @@ module App =
447448 async {
448449 let chatsBySender =
449450 events
450− |> List.choose (function IncomingChat msg -> Some(msg.SenderPubkey, msg.Message.Id) | _ -> None)
451+ |> List.choose (function IncomingChat(senderPubkey, msg) -> Some(senderPubkey, msg.Id) | _ -> None)
451452 |> List.groupBy fst
452453 |> List.map (fun (sender, pairs) -> sender, List.map snd pairs)
453454
454455 for sender, msgIds in chatsBySender do
455− try
456− let ackPayload = serializeEnvelope (DeliveryAck(Guid.NewGuid().ToString(), msgIds))
457− let blob = Crypto.encrypt session.Identity.PrivKey (Crypto.fromHex sender) ackPayload
458− do! sendMessage session.Url session.Token sender (Crypto.toHex blob) (DateTimeOffset.UtcNow.ToUnixTimeSeconds()) |> Async.Ignore
456+ try do! sendEnvelope session sender (DeliveryAck(Guid.NewGuid().ToString(), msgIds)) |> Async.Ignore
459457 with _ -> ()
460458 }
461459
@@ -473,12 +471,7 @@ module App =
473471 | CmdSend(session, recipientHex, text, messageId) ->
474472 asyncCmd (async {
475473 try
476− let recipPub = Crypto.fromHex recipientHex
477− let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
478− let payload = serializeEnvelope (TextMessage(messageId, text))
479− let blob = Crypto.encrypt session.Identity.PrivKey recipPub payload
480− let! status = sendMessage session.Url session.Token recipientHex (Crypto.toHex blob) ts
481−
474+ let! status = sendEnvelope session recipientHex (TextMessage(messageId, text))
482475 match messageStatusToError status with
483476 | Some err -> return SendFailed err
484477 | None -> return SendOk
@@ -567,10 +560,7 @@ module App =
567560 Cmd.ofEffect (fun _ ->
568561 async {
569562 for recipientHex in recipientHexes do
570− try
571− let payload = serializeEnvelope (ProfileMessage(Guid.NewGuid().ToString(), profile.DisplayName, profile.Bio, profile.PhotoBase64))
572− let blob = Crypto.encrypt session.Identity.PrivKey (Crypto.fromHex recipientHex) payload
573− do! sendMessage session.Url session.Token recipientHex (Crypto.toHex blob) (DateTimeOffset.UtcNow.ToUnixTimeSeconds()) |> Async.Ignore
563+ try do! sendEnvelope session recipientHex (ProfileMessage(Guid.NewGuid().ToString(), profile.DisplayName, profile.Bio, profile.PhotoBase64)) |> Async.Ignore
574564 with _ -> ()
575565 } |> Async.Start)
576566
@@ -630,11 +620,11 @@ module App =
630620
631621 Label("Profile:").font(size = 18.)
632622 match model.Profile with
633− | Some profile when not (String.IsNullOrEmpty profile.DisplayName) ->
623+ | Some profile when profile.DisplayName <> "" ->
634624 Label($"Name: {profile.DisplayName}").font(size = 14.)
635625 | _ -> ()
636626 match model.Profile with
637− | Some profile when not (String.IsNullOrEmpty profile.Bio) ->
627+ | Some profile when profile.Bio <> "" ->
638628 Label($"Bio: {profile.Bio}").font(size = 14.).textColor(Colors.DimGray)
639629 | _ -> ()
640630 let profilePage =
@@ -773,7 +763,7 @@ module App =
773763 viewErrorBanner model.Error
774764
775765 match photo with
776− | Some p when not (String.IsNullOrEmpty p) ->
766+ | Some p when p <> "" ->
777767 Image(new MemoryStream(Convert.FromBase64String(p)) :> Stream)
778768 .width(120.)
779769 .height(120.)