on master help sign in
Simplify code and improve type safety: dedup capPerKey, refactor authenticateSession with headOf, nest when conditions, use any instead of single, type SendResult.status as MessageStatus
f4311e3iko
client/ApiClient.fs+2−2
@@ -19,7 +19,7 @@ module ApiClient =
19 type SendResult = { Status: string; MessageId: string }
19 type SendResult = { Status: string; MessageId: string }
20
20
21 [<CLIMutable>]
21 [<CLIMutable>]
22− type PollResponse = { Cursor: uint64; Events: PollEvent array }
22+ type PollResponse = { Cursor: int64; Events: PollEvent array }
23
23
24 [<CLIMutable>]
24 [<CLIMutable>]
25 type private ChallengeResponse = { challenge: string }
25 type private ChallengeResponse = { challenge: string }
@@ -108,7 +108,7 @@ module ApiClient =
108 return! postJson<SendResult> client $"{serverUrl}/messages" body (Some token)
108 return! postJson<SendResult> client $"{serverUrl}/messages" body (Some token)
109 }
109 }
110
110
111− let poll (serverUrl: string) (token: string) (cursor: uint64) =
111+ let poll (serverUrl: string) (token: string) (cursor: int64) =
112 async {
112 async {
113 let body = JsonSerializer.Serialize({| cursor = cursor; timeout = 30000 |})
113 let body = JsonSerializer.Serialize({| cursor = cursor; timeout = 30000 |})
114 let! response = postJson<PollResponse> pollClient $"{serverUrl}/poll" body (Some token)
114 let! response = postJson<PollResponse> pollClient $"{serverUrl}/poll" body (Some token)
client/App.fs+49−55
@@ -26,15 +26,15 @@ module App =
26 type Page =
26 type Page =
27 | Setup
27 | Setup
28 | Conversations
28 | Conversations
29− | Chat of pubkey: string
29+ | Chat of pubkey: string * compose: string
30− | AddContact
30+ | AddContact of pubkey: string * nickname: string
31 | Settings
31 | Settings
32
32
33 type Session = { Url: string; Token: string; Identity: Identity }
33 type Session = { Url: string; Token: string; Identity: Identity }
34
34
35 type ConnState =
35 type ConnState =
36 | Offline
36 | Offline
37− | Connecting of Identity
37+ | Connecting
38 | Online of Session
38 | Online of Session
39
39
40 // ── Model ──
40 // ── Model ──
@@ -47,12 +47,8 @@ module App =
47 Contacts: Contact list
47 Contacts: Contact list
48 PollStatus: string
48 PollStatus: string
49 Messages: Map<string, ChatMessage list>
49 Messages: Map<string, ChatMessage list>
50− ComposeText: string
51− NewPubkey: string
52− NewNickname: string
53 Error: string option
50 Error: string option
54− PollCursor: uint64
51+ PollCursor: int64 }
55− LastSendStatus: string }
56
52
57 // ── Msg ──
53 // ── Msg ──
58
54
@@ -67,11 +63,11 @@ module App =
67 | DoDisconnect
63 | DoDisconnect
68 | SetCompose of string
64 | SetCompose of string
69 | DoSend
65 | DoSend
70− | Sent of ApiClient.SendResult
66+ | Sent of Result<ApiClient.SendResult, string>
71 | SetNewPubkey of string
67 | SetNewPubkey of string
72 | SetNewNickname of string
68 | SetNewNickname of string
73 | DoSaveContact
69 | DoSaveContact
74− | PollResult of messages: (string * ChatMessage) list * status: string * cursor: uint64
70+ | PollResult of messages: (string * ChatMessage) list * status: string * cursor: int64
75 | CopyPubKey
71 | CopyPubKey
76 | DismissError
72 | DismissError
77 | StateLoaded of Identity * Data option
73 | StateLoaded of Identity * Data option
@@ -82,7 +78,7 @@ module App =
82 | CmdGenIdentity
78 | CmdGenIdentity
83 | CmdConnect of url: string * identity: Identity
79 | CmdConnect of url: string * identity: Identity
84 | CmdSend of session: Session * recipientHex: string * text: string * messageId: string
80 | CmdSend of session: Session * recipientHex: string * text: string * messageId: string
85− | CmdPoll of session: Session * cursor: uint64
81+ | CmdPoll of session: Session * cursor: int64
86 | CmdCopyToClipboard of string
82 | CmdCopyToClipboard of string
87 | CmdLoadState
83 | CmdLoadState
88 | CmdSaveIdentity of Identity
84 | CmdSaveIdentity of Identity
@@ -136,12 +132,8 @@ module App =
136 PollStatus = ""
132 PollStatus = ""
137 Contacts = []
133 Contacts = []
138 Messages = Map.empty
134 Messages = Map.empty
139− ComposeText = ""
140− NewPubkey = ""
141− NewNickname = ""
142 Error = None
135 Error = None
143− PollCursor = 0UL
136+ PollCursor = 0L },
144− LastSendStatus = "" },
145 [ CmdLoadState ]
137 [ CmdLoadState ]
146
138
147 // ── Update ──
139 // ── Update ──
@@ -161,12 +153,12 @@ module App =
161 | DoConnect ->
153 | DoConnect ->
162 match model.Identity with
154 match model.Identity with
163 | Some id ->
155 | Some id ->
164− { model with Conn = Connecting id }, [ CmdConnect(model.ServerUrl, id) ]
156+ { model with Conn = Connecting }, [ CmdConnect(model.ServerUrl, id) ]
165 | None -> { model with Error = Some "No identity generated" }, []
157 | None -> { model with Error = Some "No identity generated" }, []
166
158
167 | AuthOk token ->
159 | AuthOk token ->
168− match model.Conn with
160+ match model.Identity, model.Conn with
169− | Connecting id ->
161+ | Some id, Connecting ->
170 let session = { Url = model.ServerUrl; Token = token; Identity = id }
162 let session = { Url = model.ServerUrl; Token = token; Identity = id }
171 { model with Conn = Online session; Page = Conversations; Error = None },
163 { model with Conn = Online session; Page = Conversations; Error = None },
172 [ CmdPoll(session, model.PollCursor) ]
164 [ CmdPoll(session, model.PollCursor) ]
@@ -176,28 +168,31 @@ module App =
176
168
177 | DoDisconnect -> { model with Conn = Offline }, []
169 | DoDisconnect -> { model with Conn = Offline }, []
178
170
179− | SetCompose text -> { model with ComposeText = text }, []
171+ | SetCompose text ->
172+ match model.Page with
173+ | Chat(pk, _) -> { model with Page = Chat(pk, text) }, []
174+ | _ -> model, []
180
175
181 | DoSend ->
176 | DoSend ->
182 match model.Page, trySession model with
177 match model.Page, trySession model with
183− | Chat pk, Some session when model.ComposeText <> "" ->
178+ | Chat(pk, compose), Some session when compose <> "" ->
184− let text = model.ComposeText
185 let id = Guid.NewGuid().ToString()
179 let id = Guid.NewGuid().ToString()
186 let outMsg =
180 let outMsg =
187 { Id = id
181 { Id = id
188− Body = text
182+ Body = compose
189 Timestamp = DateTimeOffset.UtcNow
183 Timestamp = DateTimeOffset.UtcNow
190 IsOutgoing = true }
184 IsOutgoing = true }
191
185
192 let model' =
186 let model' =
193 { model with
187 { model with
194− ComposeText = ""
188+ Page = Chat(pk, "")
195 Messages = model.Messages |> appendMessage pk outMsg }
189 Messages = model.Messages |> appendMessage pk outMsg }
196
190
197− model', CmdSend(session, pk, text, id) :: saveCmd model'
191+ model', CmdSend(session, pk, compose, id) :: saveCmd model'
198 | _ -> model, []
192 | _ -> model, []
199
193
200− | Sent result -> { model with LastSendStatus = $"{result.Status}:{result.MessageId}" }, []
194+ | Sent(Ok _) -> model, []
195+ | Sent(Error err) -> { model with Error = Some $"Send failed: {err}" }, []
201
196
202 | PollResult(incoming, status, newCursor) ->
197 | PollResult(incoming, status, newCursor) ->
203 let model' =
198 let model' =
@@ -219,25 +214,28 @@ module App =
219
214
220 model', cmds @ (if not incoming.IsEmpty then saveCmd model' else [])
215 model', cmds @ (if not incoming.IsEmpty then saveCmd model' else [])
221
216
222− | SetNewPubkey pk -> { model with NewPubkey = pk }, []
217+ | SetNewPubkey pk ->
218+ match model.Page with
219+ | AddContact(_, nn) -> { model with Page = AddContact(pk, nn) }, []
220+ | _ -> model, []
221+
223− | SetNewNickname nn -> { model with NewNickname = nn }, []
222+ | SetNewNickname nn ->
223+ match model.Page with
224+ | AddContact(pk, _) -> { model with Page = AddContact(pk, nn) }, []
225+ | _ -> model, []
224
226
225 | DoSaveContact ->
227 | DoSaveContact ->
226− if model.NewPubkey <> "" && model.NewNickname <> "" then
228+ match model.Page with
229+ | AddContact(pk, nn) when pk <> "" && nn <> "" ->
227− let contact: Contact =
230+ let contact: Contact = { Pubkey = pk; Nickname = nn }
228− { Pubkey = model.NewPubkey
229− Nickname = model.NewNickname }
230
231
231 let model' =
232 let model' =
232 { model with
233 { model with
233 Contacts = model.Contacts |> upsertContact contact
234 Contacts = model.Contacts |> upsertContact contact
234− NewPubkey = ""
235− NewNickname = ""
236 Page = Conversations }
235 Page = Conversations }
237
236
238 model', saveCmd model'
237 model', saveCmd model'
239− else
240− model, []
238+ | _ -> model, []
241
239
242 | CopyPubKey ->
240 | CopyPubKey ->
243 match model.Identity with
241 match model.Identity with
@@ -247,7 +245,7 @@ module App =
247 | DismissError -> { model with Error = None }, []
245 | DismissError -> { model with Error = None }, []
248
246
249 | StateLoaded(identity, dataOpt) ->
247 | StateLoaded(identity, dataOpt) ->
250− let model' = { model with Identity = Some identity; Conn = Connecting identity }
248+ let model' = { model with Identity = Some identity; Conn = Connecting }
251
249
252 let model' =
250 let model' =
253 match dataOpt with
251 match dataOpt with
@@ -319,9 +317,9 @@ module App =
319 let blob = Crypto.encrypt session.Identity.PrivKey recipPub payload
317 let blob = Crypto.encrypt session.Identity.PrivKey recipPub payload
320 let blobHex = Crypto.toHex blob
318 let blobHex = Crypto.toHex blob
321 let! result = ApiClient.sendMessage session.Url session.Token recipientHex blobHex ts
319 let! result = ApiClient.sendMessage session.Url session.Token recipientHex blobHex ts
322− return Sent result
320+ return Sent(Ok result)
323 with ex ->
321 with ex ->
324− return DismissError // TODO: show send errors properly
322+ return Sent(Error ex.Message)
325 })
323 })
326
324
327 | CmdPoll(session, cursor) ->
325 | CmdPoll(session, cursor) ->
@@ -373,7 +371,7 @@ module App =
373 |> Async.Start)
371 |> Async.Start)
374
372
375 | CmdSaveIdentity identity ->
373 | CmdSaveIdentity identity ->
376− Cmd.ofEffect (fun _ -> Store.saveIdentity identity |> ignore)
374+ Cmd.ofEffect (fun _ -> Store.saveIdentity identity |> Async.Start)
377
375
378 | CmdSaveData data ->
376 | CmdSaveData data ->
379 Cmd.ofEffect (fun _ -> Store.saveData data)
377 Cmd.ofEffect (fun _ -> Store.saveData data)
@@ -428,7 +426,7 @@ module App =
428
426
429 match model.Conn with
427 match model.Conn with
430 | Offline -> Button("Connect", DoConnect)
428 | Offline -> Button("Connect", DoConnect)
431− | Connecting _ -> Label("Connecting...").centerTextHorizontal()
429+ | Connecting -> Label("Connecting...").centerTextHorizontal()
432 | Online _ ->
430 | Online _ ->
433 Button("Disconnect", DoDisconnect)
431 Button("Disconnect", DoDisconnect)
434 Button("Go to Conversations", Nav Conversations)
432 Button("Go to Conversations", Nav Conversations)
@@ -441,7 +439,7 @@ module App =
441 let connStr =
439 let connStr =
442 match model.Conn with
440 match model.Conn with
443 | Offline -> "OFFLINE"
441 | Offline -> "OFFLINE"
444− | Connecting _ -> "CONNECTING"
442+ | Connecting -> "CONNECTING"
445 | Online _ -> "ONLINE"
443 | Online _ -> "ONLINE"
446
444
447 let contactNames =
445 let contactNames =
@@ -459,7 +457,7 @@ module App =
459 Label("Conversations")
457 Label("Conversations")
460 .font(size = 24.)
458 .font(size = 24.)
461
459
462− Button("+", Nav AddContact)
460+ Button("+", Nav(AddContact("", "")))
463 Button("Settings", Nav Settings)
461 Button("Settings", Nav Settings)
464 }
462 }
465
463
@@ -492,13 +490,13 @@ module App =
492 let preview =
490 let preview =
493 if lastMsg.Length > 40 then lastMsg.[..39] + "..." else lastMsg
491 if lastMsg.Length > 40 then lastMsg.[..39] + "..." else lastMsg
494
492
495− Button($"{c.Nickname}\n{preview}", Nav(Chat c.Pubkey))
493+ Button($"{c.Nickname}\n{preview}", Nav(Chat(c.Pubkey, "")))
496 })
494 })
497 .padding(20.)
495 .padding(20.)
498 )
496 )
499 )
497 )
500
498
501− let private viewChat model pk =
499+ let private viewChat model pk compose =
502 let name = contactName model.Contacts pk
500 let name = contactName model.Contacts pk
503 let msgs = messagesFor pk model.Messages
501 let msgs = messagesFor pk model.Messages
504
502
@@ -519,10 +517,6 @@ module App =
519 .font(size = 11.)
517 .font(size = 11.)
520 | None -> ()
518 | None -> ()
521
519
522− Label($"[{msgs.Length} message(s), compose: \"{model.ComposeText}\", send: \"{model.LastSendStatus}\"]")
523− .textColor(Colors.Gray)
524− .font(size = 10.)
525−
526 let allMessages =
520 let allMessages =
527 msgs
521 msgs
528 |> List.map (fun m ->
522 |> List.map (fun m ->
@@ -534,7 +528,7 @@ module App =
534 .font(size = 14.)
528 .font(size = 14.)
535
529
536 HStack(spacing = 8.) {
530 HStack(spacing = 8.) {
537− Entry(model.ComposeText, SetCompose)
531+ Entry(compose, SetCompose)
538 .placeholder("Message...")
532 .placeholder("Message...")
539
533
540 Button("Send", DoSend)
534 Button("Send", DoSend)
@@ -543,7 +537,7 @@ module App =
543 .padding(12.)
537 .padding(12.)
544 )
538 )
545
539
546− let private viewAddContact model =
540+ let private viewAddContact pk nn =
547 ContentPage(
541 ContentPage(
548 (VStack(spacing = 16.) {
542 (VStack(spacing = 16.) {
549 HStack(spacing = 8.) {
543 HStack(spacing = 8.) {
@@ -554,10 +548,10 @@ module App =
554 }
548 }
555
549
556 Label("Public Key (hex):")
550 Label("Public Key (hex):")
557− Entry(model.NewPubkey, SetNewPubkey)
551+ Entry(pk, SetNewPubkey)
558
552
559 Label("Nickname:")
553 Label("Nickname:")
560− Entry(model.NewNickname, SetNewNickname)
554+ Entry(nn, SetNewNickname)
561
555
562 Button("Save Contact", DoSaveContact)
556 Button("Save Contact", DoSaveContact)
563 .centerHorizontal()
557 .centerHorizontal()
@@ -571,8 +565,8 @@ module App =
571 | Setup -> viewSetup ()
565 | Setup -> viewSetup ()
572 | Settings -> viewSettings model
566 | Settings -> viewSettings model
573 | Conversations -> viewConversations model
567 | Conversations -> viewConversations model
574− | Chat pk -> viewChat model pk
568+ | Chat(pk, compose) -> viewChat model pk compose
575− | AddContact -> viewAddContact model
569+ | AddContact(pk, nn) -> viewAddContact pk nn
576
570
577 Application() { Window(page) }
571 Application() { Window(page) }
578
572
client/Store.fs+6−6
@@ -22,7 +22,7 @@ module Store =
22 { Contacts: Contact list
22 { Contacts: Contact list
23 Messages: Map<string, ChatMessage list>
23 Messages: Map<string, ChatMessage list>
24 ServerUrl: string
24 ServerUrl: string
25− PollCursor: uint64 }
25+ PollCursor: int64 }
26
26
27 [<CLIMutable>]
27 [<CLIMutable>]
28 type private MessageDoc =
28 type private MessageDoc =
@@ -61,9 +61,9 @@ module Store =
61 }
61 }
62
62
63 let saveIdentity (identity: Crypto.Identity) =
63 let saveIdentity (identity: Crypto.Identity) =
64− task {
64+ async {
65− do! SecureStorage.Default.SetAsync("identity_privkey", Convert.ToBase64String identity.PrivKey)
65+ do! SecureStorage.Default.SetAsync("identity_privkey", Convert.ToBase64String identity.PrivKey) |> Async.AwaitTask
66− do! SecureStorage.Default.SetAsync("identity_pubkey", identity.PubKeyHex)
66+ do! SecureStorage.Default.SetAsync("identity_pubkey", identity.PubKeyHex) |> Async.AwaitTask
67 }
67 }
68
68
69 let loadData () : Data option =
69 let loadData () : Data option =
@@ -95,7 +95,7 @@ module Store =
95 { Contacts = contacts
95 { Contacts = contacts
96 Messages = messages
96 Messages = messages
97 ServerUrl = settings.ServerUrl
97 ServerUrl = settings.ServerUrl
98− PollCursor = uint64 settings.PollCursor }
98+ PollCursor = settings.PollCursor }
99 with _ ->
99 with _ ->
100 None
100 None
101
101
@@ -123,7 +123,7 @@ module Store =
123 settings.Upsert
123 settings.Upsert
124 { Id = "settings"
124 { Id = "settings"
125 ServerUrl = data.ServerUrl
125 ServerUrl = data.ServerUrl
126− PollCursor = int64 data.PollCursor }
126+ PollCursor = data.PollCursor }
127 |> ignore
127 |> ignore
128 with _ ->
128 with _ ->
129 ()
129 ()
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-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+64e0fdcd67fd42117a3538c089bb99c276931127")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+e692eb5d42ef866622de97d2bc08d09fdb62a11b")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−be435fd044969cb8957c158b5e1a656e1e9260e869cd5e4059efeccf555209e5
1+4dcdec3fa3f1ebbb1a84fb886e9dd06477af7c654b1d2094630d80c574da8beb
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.fsproj.FileListAbsolute.txt−23
@@ -322,27 +322,4 @@
322/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb
322/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb
323/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.genruntimeconfig.cache
323/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.genruntimeconfig.cache
324/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll
324/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll
325−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Fabulous.stamp
326−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Fabulous.MauiControls.stamp
327−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/FSharp.Core.stamp
328−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Configuration.Abstractions.stamp
329−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Configuration.stamp
330−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.Abstractions.stamp
331−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.stamp
332−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Diagnostics.Abstractions.stamp
333−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.FileProviders.Abstractions.stamp
334−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Hosting.Abstractions.stamp
335−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Logging.Abstractions.stamp
336−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Logging.stamp
337−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Options.stamp
338−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Primitives.stamp
339−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Controls.Compatibility.stamp
340−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Controls.stamp
341−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Controls.Xaml.stamp
342−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.stamp
343−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Essentials.stamp
344−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Graphics.stamp
345−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Sodium.Core.stamp
346−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/unpack/Microsoft.Maui.Essentials/BundleResource/xml/microsoft_maui_essentials_fileprovider_file_paths.xml
347/Users/iko/Developer/skrepka/client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/LiteDB.dll
325/Users/iko/Developer/skrepka/client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/LiteDB.dll
348−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/LiteDB.stamp
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/64e0fdcd67fd42117a3538c089bb99c276931127/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/e692eb5d42ef866622de97d2bc08d09fdb62a11b/*"}}
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.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/FSharp.Core.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Fabulous.MauiControls.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Fabulous.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Configuration.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Configuration.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Diagnostics.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.FileProviders.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Hosting.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Logging.Abstractions.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Logging.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Options.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Extensions.Primitives.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Controls.Compatibility.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Controls.Xaml.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Controls.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Essentials.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.Graphics.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Microsoft.Maui.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/resourcestamps/Sodium.Core.stampdeleted

No changes to the content.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/unpack/Microsoft.Maui.Essentials/BundleResource/xml/microsoft_maui_essentials_fileprovider_file_paths.xmldeleted−6
@@ -1,6 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8" ?>
2−<paths>
3− <external-path name="external_files" path="." />
4− <cache-path name="internal_cache" path="." />
5− <external-cache-path name="external_cache" path="." />
6−</paths>
server.knot+16−30
@@ -72,7 +72,7 @@ type AuthResult = Authorized {pubkey: PubkeyHex} | Unauthorized {}
72type PruneResult = {expiredLocal: [PresenceEntry], peerServers: [Text]}
72type PruneResult = {expiredLocal: [PresenceEntry], peerServers: [Text]}
73type PollResult = PollUnauthorized {} | PollAuthorized {events: [PollEvent], cursor: Timestamp}
73type PollResult = PollUnauthorized {} | PollAuthorized {events: [PollEvent], cursor: Timestamp}
74type VerifyResult = VerifyFailed {} | VerifyOk {peerServers: [Text]}
74type VerifyResult = VerifyFailed {} | VerifyOk {peerServers: [Text]}
75−type SendResult = SendUnauthorized {} | SendRejected {} | SendDone {status: Text, remoteServers: [Text], fromKey: PubkeyHex}
75+type SendResult = SendUnauthorized {} | SendRejected {} | SendDone {status: MessageStatus, remoteServers: [Text], fromKey: PubkeyHex}
76type AckResult = AckUnauthorized {} | AckDone {pendingAcks: [AckedEntry], evictedEntries: [AckedEntry]}
76type AckResult = AckUnauthorized {} | AckDone {pendingAcks: [AckedEntry], evictedEntries: [AckedEntry]}
77type LookupResult = LookupUnauthorized {} | LookupDone {foundServer: Text}
77type LookupResult = LookupUnauthorized {} | LookupDone {foundServer: Text}
78
78
@@ -368,13 +368,9 @@ isBadServerName = \s ->
368
368
369authenticateSession : [Session] -> Text -> Timestamp -> AuthResult
369authenticateSession : [Session] -> Text -> Timestamp -> AuthResult
370authenticateSession = \sessions token t ->
370authenticateSession = \sessions token t ->
371− fold (\acc s ->
372− case acc of
373− Authorized _ -> acc
374− Unauthorized {} ->
375− if safeCompare s.token token && s.expiresAt > t
371+ case headOf (do s <- sessions; where safeCompare s.token token && s.expiresAt > t; yield s) of
376− then Authorized {pubkey: s.pubkey}
377− else acc) Unauthorized {} sessions
372+ Nothing {} -> Unauthorized {}
373+ Just {value: s} -> Authorized {pubkey: s.pubkey}
378
374
379extractToken = \authHeader ->
375extractToken = \authHeader ->
380 if toLower (take 7 authHeader) == "bearer "
376 if toLower (take 7 authHeader) == "bearer "
@@ -463,10 +459,9 @@ evictOneOldest = \items getKey ->
463
459
464capPerKey = \items matchKey maxPerKey getExpiry t ->
460capPerKey = \items matchKey maxPerKey getExpiry t ->
465 let active = do x <- items; where getExpiry x > t; yield x in
461 let active = do x <- items; where getExpiry x > t; yield x in
466− let activeForKeyCount = count (do x <- active; where matchKey x; yield x) in
462+ let forKey = do x <- active; where matchKey x; yield x in
467− if activeForKeyCount >= maxPerKey
463+ if count forKey >= maxPerKey
468− then let forKey = do x <- active; where matchKey x; yield x in
469− let others = do x <- active; where not (matchKey x); yield x in
464+ then let others = do x <- active; where not (matchKey x); yield x in
470 union others (evictOneOldest forKey getExpiry)
465 union others (evictOneOldest forKey getExpiry)
471 else active
466 else active
472
467
@@ -630,9 +625,7 @@ retryForwardsForServer = \entries remaining ->
630 timestamp: f.timestamp, originServer: serverHostN, messageId: f.messageId,
625 timestamp: f.timestamp, originServer: serverHostN, messageId: f.messageId,
631 authorization: "Bearer " ++ federationSecret})
626 authorization: "Bearer " ++ federationSecret})
632 case result of
627 case result of
633− Ok _ -> do
634− removeForward f.messageId f.toServer
628+ Ok _ -> removeForward f.messageId f.toServer
635− yield {}
636 Err _ -> yield {}
629 Err _ -> yield {}
637 retryForwardsForServer rest (remaining - 1)
630 retryForwardsForServer rest (remaining - 1)
638
631
@@ -755,15 +748,8 @@ serve = \req -> case req of
755 challenges <- *challenges
748 challenges <- *challenges
756 sessions <- *sessions
749 sessions <- *sessions
757 presence <- *presence
750 presence <- *presence
758− let matched = single (do
759− c <- challenges
760− where c.pubkey == pubkey
761− where c.challenge == challenge
751+ let hasMatch = any challenges (\c -> c.pubkey == pubkey && c.challenge == challenge && c.expiresAt > t)
762− where c.expiresAt > t
763− yield c)
764− let sigValid = case matched of
765− Nothing {} -> False {}
766− _ -> verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature)
752+ let sigValid = if hasMatch then verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature) else False {}
767 if not sigValid
753 if not sigValid
768 then yield {pr: pr, verify: VerifyFailed {}}
754 then yield {pr: pr, verify: VerifyFailed {}}
769 else do
755 else do
@@ -854,18 +840,18 @@ serve = \req -> case req of
854 yield {pr: pr, send: SendDone {status: status, remoteServers: remoteServers, fromKey: fromKey}}
840 yield {pr: pr, send: SendDone {status: status, remoteServers: remoteServers, fromKey: fromKey}}
855 fork (do
841 fork (do
856 case result.send of
842 case result.send of
857− SendDone {status, remoteServers, fromKey} -> do
843+ SendDone {status, remoteServers, fromKey} ->
858− when (count remoteServers > 0 && federationEnabled)
844+ when (count remoteServers > 0 && federationEnabled) (do
859− (sendToEach remoteServers (\server -> do
845+ sendToEach remoteServers (\server -> do
860 fwdResult <- fetch (federationScheme ++ server) (ForwardMessage {
846 fwdResult <- fetch (federationScheme ++ server) (ForwardMessage {
861 toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob,
847 toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob,
862 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
848 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
863 authorization: "Bearer " ++ federationSecret})
849 authorization: "Bearer " ++ federationSecret})
864 case fwdResult of
850 case fwdResult of
865 Ok _ -> removeForward msgId server
851 Ok _ -> removeForward msgId server
866− Err _ -> yield {}))
852+ Err _ -> yield {})
867− when (status == "queued" && count remoteServers > 0 && federationEnabled)
853+ when (status == "queued")
868− (println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking"))
854+ (println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking")))
869 SendUnauthorized {} -> yield {}
855 SendUnauthorized {} -> yield {}
870 SendRejected {} -> yield {}
856 SendRejected {} -> yield {}
871 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
857 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)