@@ -17,12 +17,31 @@ module App =
18 // ── Domain Types ──
18 // ── Domain Types ──
20+ type private EnvelopeType = Text | UnknownEnvelope of string
22+ type private EnvelopeTypeConverter() =
23+ inherit JsonConverter<EnvelopeType>()
24+ override _.Read(reader, _, _) =
25+ match reader.GetString() with
27+ | s -> UnknownEnvelope s
28+ override _.Write(writer, value, _) =
29+ writer.WriteStringValue(
32+ | UnknownEnvelope s -> s)
20 [<CLIMutable>]
34 [<CLIMutable>]
21 type private TextEnvelope =
35 type private TextEnvelope =
22− { [<JsonPropertyName("type")>] Type: string
36+ { [<JsonPropertyName("type")>] Type: EnvelopeType
23 [<JsonPropertyName("id")>] Id: string
37 [<JsonPropertyName("id")>] Id: string
24 [<JsonPropertyName("body")>] Body: string }
38 [<JsonPropertyName("body")>] Body: string }
40+ let private envelopeJsonOpts =
41+ let opts = JsonSerializerOptions()
42+ opts.Converters.Add(EnvelopeTypeConverter())
26 type Page =
45 type Page =
28 | Conversations
47 | Conversations
@@ -230,22 +249,17 @@ module App =
230 | DoSaveContact ->
249 | DoSaveContact ->
231 match model.Page with
250 match model.Page with
232 | AddContact(pk, nn) when pk <> "" && nn <> "" ->
251 | AddContact(pk, nn) when pk <> "" && nn <> "" ->
234− if pk.Contains("-") then
235− match Phonemic.fromOb pk with
252+ match Phonemic.fromOb pk |> Option.orElseWith (fun () -> Crypto.tryFromHex pk) with
236− | Some bytes -> Crypto.toHex bytes
253+ | Some bytes when bytes.Length = 32 ->
241− let contact: Contact = { Pubkey = pubkeyHex; Nickname = nn }
254+ let contact: Contact = { Pubkey = Crypto.toHex bytes; Nickname = nn }
243− let model' =
256+ let model' =
244− { model with
257+ { model with
245− Contacts = model.Contacts |> upsertContact contact
258+ Contacts = model.Contacts |> upsertContact contact
246− Page = Conversations }
259+ Page = Conversations }
248− model', [ saveCmdMsg model' ]
261+ model', [ saveCmdMsg model' ]
262+ | _ -> { model with Error = Some "Invalid public key" }, []
249 | _ -> model, []
263 | _ -> model, []
251 | CopyPubKey ->
265 | CopyPubKey ->
@@ -287,16 +301,16 @@ module App =
288 match Crypto.decrypt privKey blob with
302 match Crypto.decrypt privKey blob with
289 | Some(plaintext, senderHex) ->
303 | Some(plaintext, senderHex) ->
290− let envelope = JsonSerializer.Deserialize<TextEnvelope>(plaintext)
304+ let envelope = JsonSerializer.Deserialize<TextEnvelope>(plaintext, envelopeJsonOpts)
292 match envelope.Type with
306 match envelope.Type with
293− | "text" ->
307+ | Text ->
294 Ok(senderHex,
308 Ok(senderHex,
295 { Id = envelope.Id
309 { Id = envelope.Id
296 Body = envelope.Body
310 Body = envelope.Body
297 Timestamp = DateTimeOffset.FromUnixTimeSeconds(payload.Timestamp)
311 Timestamp = DateTimeOffset.FromUnixTimeSeconds(payload.Timestamp)
298 IsOutgoing = false })
312 IsOutgoing = false })
299− | t -> Error $"unknown type: {t}"
313+ | UnknownEnvelope t -> Error $"unknown envelope type: {t}"
300 | None -> Error "decrypt failed"
314 | None -> Error "decrypt failed"
301 with ex ->
315 with ex ->
302 Error $"parse: {ex.Message}"
316 Error $"parse: {ex.Message}"
@@ -321,7 +335,7 @@ module App =
322 let recipPub = Crypto.fromHex recipientHex
336 let recipPub = Crypto.fromHex recipientHex
323 let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
337 let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
324− let payload = JsonSerializer.Serialize({ Type = "text"; Id = messageId; Body = text }: TextEnvelope)
338+ let payload = JsonSerializer.Serialize<TextEnvelope>({ Type = Text; Id = messageId; Body = text }, envelopeJsonOpts)
325 let blob = Crypto.encrypt session.Identity.PrivKey recipPub payload
339 let blob = Crypto.encrypt session.Identity.PrivKey recipPub payload
326 let! status = sendMessage session.Url session.Token recipientHex (Crypto.toHex blob) ts
340 let! status = sendMessage session.Url session.Token recipientHex (Crypto.toHex blob) ts