@@ -17,12 +17,31 @@ module App =
1717
1818 // ── Domain Types ──
1919
20+ type private EnvelopeType = Text | UnknownEnvelope of string
21+
22+ type private EnvelopeTypeConverter() =
23+ inherit JsonConverter<EnvelopeType>()
24+ override _.Read(reader, _, _) =
25+ match reader.GetString() with
26+ | "text" -> Text
27+ | s -> UnknownEnvelope s
28+ override _.Write(writer, value, _) =
29+ writer.WriteStringValue(
30+ match value with
31+ | Text -> "text"
32+ | UnknownEnvelope s -> s)
33+
2034 [<CLIMutable>]
2135 type private TextEnvelope =
22− { [<JsonPropertyName("type")>] Type: string
36+ { [<JsonPropertyName("type")>] Type: EnvelopeType
2337 [<JsonPropertyName("id")>] Id: string
2438 [<JsonPropertyName("body")>] Body: string }
2539
40+ let private envelopeJsonOpts =
41+ let opts = JsonSerializerOptions()
42+ opts.Converters.Add(EnvelopeTypeConverter())
43+ opts
44+
2645 type Page =
2746 | Setup
2847 | Conversations
@@ -230,22 +249,17 @@ module App =
230249 | DoSaveContact ->
231250 match model.Page with
232251 | AddContact(pk, nn) when pk <> "" && nn <> "" ->
233− let pubkeyHex =
234− if pk.Contains("-") then
235− match Phonemic.fromOb pk with
236− | Some bytes -> Crypto.toHex bytes
237− | None -> pk
238− else
239− pk
240−
241− let contact: Contact = { Pubkey = pubkeyHex; Nickname = nn }
252+ match Phonemic.fromOb pk |> Option.orElseWith (fun () -> Crypto.tryFromHex pk) with
253+ | Some bytes when bytes.Length = 32 ->
254+ let contact: Contact = { Pubkey = Crypto.toHex bytes; Nickname = nn }
242255
243− let model' =
244− { model with
245− Contacts = model.Contacts |> upsertContact contact
246− Page = Conversations }
256+ let model' =
257+ { model with
258+ Contacts = model.Contacts |> upsertContact contact
259+ Page = Conversations }
247260
248− model', [ saveCmdMsg model' ]
261+ model', [ saveCmdMsg model' ]
262+ | _ -> { model with Error = Some "Invalid public key" }, []
249263 | _ -> model, []
250264
251265 | CopyPubKey ->
@@ -287,16 +301,16 @@ module App =
287301
288302 match Crypto.decrypt privKey blob with
289303 | Some(plaintext, senderHex) ->
290− let envelope = JsonSerializer.Deserialize<TextEnvelope>(plaintext)
304+ let envelope = JsonSerializer.Deserialize<TextEnvelope>(plaintext, envelopeJsonOpts)
291305
292306 match envelope.Type with
293− | "text" ->
307+ | Text ->
294308 Ok(senderHex,
295309 { Id = envelope.Id
296310 Body = envelope.Body
297311 Timestamp = DateTimeOffset.FromUnixTimeSeconds(payload.Timestamp)
298312 IsOutgoing = false })
299− | t -> Error $"unknown type: {t}"
313+ | UnknownEnvelope t -> Error $"unknown envelope type: {t}"
300314 | None -> Error "decrypt failed"
301315 with ex ->
302316 Error $"parse: {ex.Message}"
@@ -321,7 +335,7 @@ module App =
321335 try
322336 let recipPub = Crypto.fromHex recipientHex
323337 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)
325339 let blob = Crypto.encrypt session.Identity.PrivKey recipPub payload
326340 let! status = sendMessage session.Url session.Token recipientHex (Crypto.toHex blob) ts
327341