on master help sign in
Simplify code and improve type safety: eliminate IncomingMessage, type-safe Connecting state, reuse postJson in poll
commit7816ce823352603c0905f7108f261bca2b902f94 authoriko <mail@iko.soy> dateFri 10 Apr 2026, 10:02 (6 months ago) parent79a607d
27 files changed+34−70
client/ApiClient.fs+6−9 client/App.fs+16−27 client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.dll client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.pdb client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/_CodeSignature/CodeResources+4−4 client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.dll client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.pdb client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/_CodeSignature/CodeResources+2−2 client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfo.fs+1−1 client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfoInputs.cache+1−1 client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.fsproj.FileListAbsolute.txt−22 client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.sourcelink.json+1−1 client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll client/obj/Debug/net10.0-ios/iossimulator-arm64/refint/Skrepka.dll client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1 client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1 client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1 client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll
client/ApiClient.fs+6−9
@@ -30,7 +30,7 @@ module ApiClient =
3030 c.Timeout <- TimeSpan.FromSeconds(60.)
3131 c
3232
33− let private postJson (url: string) (body: string) (token: string option) =
33+ let private postJsonWith (httpClient: HttpClient) (url: string) (body: string) (token: string option) =
3434 async {
3535 let request = new HttpRequestMessage(HttpMethod.Post, url)
3636 request.Content <- new StringContent(body, Encoding.UTF8, "application/json")
@@ -38,11 +38,13 @@ module ApiClient =
3838 token
3939 |> Option.iter (fun t -> request.Headers.Add("Authorization", $"Bearer {t}"))
4040
41− let! response = client.SendAsync(request) |> awaitTask
41+ let! response = httpClient.SendAsync(request) |> awaitTask
4242 let! text = response.Content.ReadAsStringAsync() |> awaitTask
4343 return JsonDocument.Parse(text)
4444 }
4545
46+ let private postJson url body token = postJsonWith client url body token
47+
4648 let private getJson (url: string) (token: string) =
4749 async {
4850 let request = new HttpRequestMessage(HttpMethod.Get, url)
@@ -99,13 +101,8 @@ module ApiClient =
99101
100102 let poll (serverUrl: string) (token: string) (cursor: uint64) =
101103 async {
102− let request = new HttpRequestMessage(HttpMethod.Post, $"{serverUrl}/poll")
103− request.Content <- new StringContent(JsonSerializer.Serialize({| cursor = cursor; timeout = 30000 |}), Encoding.UTF8, "application/json")
104− request.Headers.Add("Authorization", $"Bearer {token}")
105−
106− let! response = pollClient.SendAsync(request) |> awaitTask
107− let! text = response.Content.ReadAsStringAsync() |> awaitTask
108− let doc = JsonDocument.Parse(text)
104+ let body = JsonSerializer.Serialize({| cursor = cursor; timeout = 30000 |})
105+ let! doc = postJsonWith pollClient $"{serverUrl}/poll" body (Some token)
109106 let root = expectObject doc "/poll"
110107 let cursor = root.GetProperty("cursor").GetUInt64()
111108
client/App.fs+16−27
@@ -21,12 +21,6 @@ module App =
2121 Timestamp: DateTimeOffset
2222 IsOutgoing: bool }
2323
24− type IncomingMessage =
25− { FromPubkey: string
26− Body: string
27− Timestamp: int64
28− MessageId: string }
29−
3024 [<CLIMutable>]
3125 type TextEnvelope =
3226 { ``type``: string
@@ -47,11 +41,11 @@ module App =
4741
4842 type ConnState =
4943 | Offline
50− | Connecting
44+ | Connecting of Identity
5145 | Online of Session
5246
5347 type PollResultData =
54− { Messages: IncomingMessage list
48+ { Messages: (string * ChatMessage) list
5549 Status: string
5650 Cursor: uint64 }
5751
@@ -183,12 +177,12 @@ module App =
183177 | DoConnect ->
184178 match model.Identity with
185179 | Some id ->
186− { model with Conn = Connecting }, [ CmdConnect(model.ServerUrl, id) ]
180+ { model with Conn = Connecting id }, [ CmdConnect(model.ServerUrl, id) ]
187181 | None -> { model with Error = Some "No identity generated" }, []
188182
189183 | AuthOk token ->
190− match model.Conn, model.Identity with
191− | Connecting, Some id ->
184+ match model.Conn with
185+ | Connecting id ->
192186 let session = { Url = model.ServerUrl; Token = token; Identity = id }
193187 { model with Conn = Online session; Page = Conversations; Error = None },
194188 [ CmdPoll(session, model.PollCursor) ]
@@ -225,19 +219,13 @@ module App =
225219 let model' =
226220 incoming
227221 |> List.fold
228− (fun m msg ->
229− if messagesFor msg.FromPubkey m.Messages |> List.exists (fun x -> x.Id = msg.MessageId) then
222+ (fun m (fromPubkey, chatMsg) ->
223+ if messagesFor fromPubkey m.Messages |> List.exists (fun x -> x.Id = chatMsg.Id) then
230224 m
231225 else
232− let chatMsg =
233− { Id = msg.MessageId
234− Body = msg.Body
235− Timestamp = DateTimeOffset.FromUnixTimeSeconds(msg.Timestamp)
236− IsOutgoing = false }
237−
238226 { m with
239− Contacts = m.Contacts |> addContactIfNew { Pubkey = msg.FromPubkey; Nickname = truncKey msg.FromPubkey }
240− Messages = m.Messages |> appendMessage msg.FromPubkey chatMsg })
227+ Contacts = m.Contacts |> addContactIfNew { Pubkey = fromPubkey; Nickname = truncKey fromPubkey }
228+ Messages = m.Messages |> appendMessage fromPubkey chatMsg })
241229 { model with PollStatus = status; PollCursor = newCursor }
242230
243231 let cmds =
@@ -276,7 +264,7 @@ module App =
276264
277265 | StateLoaded { Identity = None } -> model, []
278266 | StateLoaded { Identity = Some identity; Data = dataOpt } ->
279− let model' = { model with Identity = Some identity; Conn = Connecting }
267+ let model' = { model with Identity = Some identity; Conn = Connecting identity }
280268
281269 let model' =
282270 match dataOpt with
@@ -324,10 +312,11 @@ module App =
324312 let envelope = JsonSerializer.Deserialize<TextEnvelope>(plaintext)
325313
326314 if envelope.``type`` = "text" then
327− Ok { FromPubkey = senderHex
315+ Ok(senderHex,
316+ { Id = envelope.id
328317 Body = envelope.body
329− Timestamp = payload.Timestamp
330− MessageId = envelope.id }
318+ Timestamp = DateTimeOffset.FromUnixTimeSeconds(payload.Timestamp)
319+ IsOutgoing = false })
331320 else
332321 Error "unknown type"
333322 | None -> Error "decrypt failed"
@@ -488,7 +477,7 @@ module App =
488477
489478 match model.Conn with
490479 | Offline -> Button("Connect", DoConnect)
491− | Connecting -> Label("Connecting...").centerTextHorizontal()
480+ | Connecting _ -> Label("Connecting...").centerTextHorizontal()
492481 | Online _ ->
493482 Button("Disconnect", DoDisconnect)
494483 Button("Go to Conversations", Nav Conversations)
@@ -501,7 +490,7 @@ module App =
501490 let connStr =
502491 match model.Conn with
503492 | Offline -> "OFFLINE"
504− | Connecting -> "CONNECTING"
493+ | Connecting _ -> "CONNECTING"
505494 | Online _ -> "ONLINE"
506495
507496 let contactNames =
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/_CodeSignature/CodeResources+4−4
@@ -162,11 +162,11 @@
162162 </data>
163163 <key>Skrepka.dll</key>
164164 <data>
165− +r8/McvWcZyDH15Tlxw+A2YZSqA=
165+ HO+rGAQB+KP+O6GLdnrpg4gXNTo=
166166 </data>
167167 <key>Skrepka.pdb</key>
168168 <data>
169− 7zDN9fLf1e4bib8C9qjD5OXl3h8=
169+ gVY2miH2VNwTxjVewvuMbzDx/GU=
170170 </data>
171171 <key>Sodium.Core.dll</key>
172172 <data>
@@ -1382,14 +1382,14 @@
13821382 <dict>
13831383 <key>hash2</key>
13841384 <data>
1385− eu/QLEnyJRtXlclZnchcx4K7yYLiVH8cqgv9VmdInys=
1385+ D1zjN3wi4JGWYPuOGUv3OatZGfdLQhYyE2a3FQRO3Sk=
13861386 </data>
13871387 </dict>
13881388 <key>Skrepka.pdb</key>
13891389 <dict>
13901390 <key>hash2</key>
13911391 <data>
1392− pw4SQcQwk/RuJxp+aK9kDVeLoqstIlHDM6TY71IG6F0=
1392+ Qdm9JVWHZq1+ocF6a6hRUFeaSuFiylZp+ykwgIfTQ3A=
13931393 </data>
13941394 </dict>
13951395 <key>Sodium.Core.dll</key>
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/_CodeSignature/CodeResources+2−2
@@ -240,14 +240,14 @@
240240 <dict>
241241 <key>hash2</key>
242242 <data>
243− YxPtcfpGxa+IKDOrDbOsu1NtXsc5daoFGZDbf16at/Q=
243+ iGYj1YzZ1prtD0NbQ3DXzBIxx8GfZCXBBkeG+NhAlAg=
244244 </data>
245245 </dict>
246246 <key>MonoBundle/Skrepka.pdb</key>
247247 <dict>
248248 <key>hash2</key>
249249 <data>
250− DyrAjXWRy1gvOE/NF4Imut7V+kKP3kTubqBKbjn9eik=
250+ gc3cHrHWiW0qjPSnbfHqE/s8LkMQGikr2nzPGAaHEgE=
251251 </data>
252252 </dict>
253253 <key>MonoBundle/Sodium.Core.dll</key>
client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
1010[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
1111[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
1212[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+e6337c83d5e6108acdc6927de577a601ea6949b4")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+79a607dd3a4773a3bae1ef20240a704c153f91da")>]
1414[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
1515[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
1616[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−febc92d682ab55304ab763b41325b2810feafbce3abb3916e01645680f754010
1+2cd69f5b98b69e1a72e3f4bd87ef1f300a712fcc06d03dda524b6c00fdfe5200
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.fsproj.FileListAbsolute.txt−22
@@ -333,25 +333,3 @@
333333/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb
334334/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.genruntimeconfig.cache
335335/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll
336−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.stamp
337−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.MauiControls.stamp
338−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/FSharp.Core.stamp
339−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.Abstractions.stamp
340−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.stamp
341−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.Abstractions.stamp
342−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.stamp
343−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Diagnostics.Abstractions.stamp
344−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.FileProviders.Abstractions.stamp
345−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Hosting.Abstractions.stamp
346−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.Abstractions.stamp
347−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.stamp
348−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Options.stamp
349−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Primitives.stamp
350−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Compatibility.stamp
351−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.stamp
352−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Xaml.stamp
353−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.stamp
354−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Essentials.stamp
355−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Graphics.stamp
356−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Sodium.Core.stamp
357−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/unpack/Microsoft.Maui.Essentials/BundleResource/xml/microsoft_maui_essentials_fileprovider_file_paths.xml
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/e6337c83d5e6108acdc6927de577a601ea6949b4/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/79a607dd3a4773a3bae1ef20240a704c153f91da/*"}}
client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/refint/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
1010[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
1111[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
1212[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+e6337c83d5e6108acdc6927de577a601ea6949b4")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+79a607dd3a4773a3bae1ef20240a704c153f91da")>]
1414[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
1515[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
1616[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−786da36e5c8bb4572c2194d3d1b64b25fa7ad4d4e4fbac83233c30ec5fd7b804
1+20452108ea7920252e262f31e3d85af043c1d46fd7b19b9a54754e30a319f325
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/e6337c83d5e6108acdc6927de577a601ea6949b4/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/79a607dd3a4773a3bae1ef20240a704c153f91da/*"}}
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll

Binary file not shown.