on master help sign in
Add type annotations to server.knot functions and migrate iOS launch screen to storyboard format
commitdc7cb72fc10a0fb5c91737c492cd8b78bb5a2eaa authoriko <mail@iko.soy> dateMon 13 Apr 2026, 09:47 (6 months ago) parente4f77cc
5 files changed+54−45
client/Platform/iOS/AppDelegate.fs+8−7 client/Platform/iOS/Resources/LaunchScreen.storyboard+25 client/Platform/iOS/Resources/LaunchScreen.xib−32 client/Skrepka.fsproj+5−1 server.knot+16−5
client/Platform/iOS/AppDelegate.fs+8−7
@@ -10,14 +10,15 @@ open Skrepka
1010[<Register(nameof AppDelegate)>]
1111type AppDelegate() =
1212 inherit AvaloniaAppDelegate<FabApplication>()
13− override this.CreateAppBuilder() = App.create().UseiOS()
1413
15− [<Export("application:didFinishLaunchingWithOptions:")>]
16− member this.FinishedLaunching(app: UIApplication, options: NSDictionary) : bool =
17− let result = base.FinishedLaunching(app, options)
18− if this.Window <> null then
19− this.Window.BackgroundColor <- UIColor.White
20− result
14+ override this.CreateAppBuilder() =
15+ NSTimer.CreateScheduledTimer(0.0, false, fun _ ->
16+ if this.Window <> null then
17+ this.Window.BackgroundColor <- UIColor.White
18+ if this.Window.RootViewController <> null then
19+ this.Window.RootViewController.View.BackgroundColor <- UIColor.White)
20+ |> ignore
21+ App.create().UseiOS()
2122
2223module Main =
2324 [<EntryPoint>]
client/Platform/iOS/Resources/LaunchScreen.storyboardnew file+25
@@ -0,0 +1,25 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3+ <dependencies>
4+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
5+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
6+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
7+ </dependencies>
8+ <scenes>
9+ <!--View Controller-->
10+ <scene sceneID="EHf-IW-A2E">
11+ <objects>
12+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
13+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
14+ <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
15+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
16+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
17+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
18+ </view>
19+ </viewController>
20+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
21+ </objects>
22+ <point key="canvasLocation" x="53" y="375"/>
23+ </scene>
24+ </scenes>
25+</document>
client/Platform/iOS/Resources/LaunchScreen.xibdeleted−32
@@ -1,32 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
3− <dependencies>
4− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207" />
5− <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1" />
6− </dependencies>
7− <objects>
8− <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" />
9− <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder" />
10− <view contentMode="scaleToFill" id="iN0-l3-epB">
11− <rect key="frame" x="0.0" y="0.0" width="480" height="480" />
12− <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" />
13− <subviews>
14− <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Skrepka" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
15− <rect key="frame" x="20" y="140" width="441" height="43" />
16− <fontDescription key="fontDescription" type="boldSystem" pointSize="36" />
17− <color key="textColor" cocoaTouchSystemColor="darkTextColor" />
18− <nil key="highlightedColor" />
19− </label>
20− </subviews>
21− <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite" />
22− <constraints>
23− <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC" />
24− <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk" />
25− <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g" />
26− </constraints>
27− <nil key="simulatedStatusBarMetrics" />
28− <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics" />
29− <point key="canvasLocation" x="548" y="455" />
30− </view>
31− </objects>
32−</document>
client/Skrepka.fsproj+5−1
@@ -48,11 +48,15 @@
4848 <ItemGroup Condition="$(AvaloniaPlatform) == 'iOS'">
4949 <None Include="$(iOSPlatformFolder)Entitlements.plist" />
5050 <None Include="$(iOSPlatformFolder)Info.plist" LogicalName="Info.plist" />
51− <InterfaceDefinition Include="$(iOSPlatformFolder)Resources\LaunchScreen.xib" />
51+ <InterfaceDefinition Include="$(iOSPlatformFolder)Resources\LaunchScreen.storyboard" />
5252 <Compile Include="$(iOSPlatformFolder)AppDelegate.fs" />
5353 <PackageReference Include="Avalonia.iOS" Version="11.3.0" />
5454 </ItemGroup>
5555
56+ <Target Name="AddLaunchScreenToPlist" AfterTargets="_GenerateInfoPList" Condition="$(AvaloniaPlatform) == 'iOS'">
57+ <Exec Command="/usr/libexec/PlistBuddy -c 'Add :UILaunchStoryboardName string LaunchScreen' '$(AppBundleDir)/Info.plist'" ContinueOnError="true" />
58+ </Target>
59+
5660 <ItemGroup Condition="$(AvaloniaPlatform) == 'iOS' And $(RuntimeIdentifier.StartsWith('iossimulator'))">
5761 <NativeReference Include="native/iossimulator-arm64/libsodium.a">
5862 <Kind>Static</Kind>
server.knot+16−5
@@ -286,9 +286,11 @@ stripTrailingDot = \s ->
286286 else if take 1 s == "." && isTrailingDots (drop 1 s) then stripTrailingDot (drop 1 s)
287287 else take 1 s ++ stripTrailingDot (drop 1 s)
288288
289+normalizeServerName : Text -> Text
289290normalizeServerName = \s -> toLower (stripTrailingDot s)
290291
291292serverHostN = normalizeServerName serverHost
293+isLocalServer : Text -> Bool
292294isLocalServer = \s -> s == serverHostN
293295
294296containsSuffixN = \suffix suffixLen s ->
@@ -323,6 +325,7 @@ matchesHostname = \sn name ->
323325matchesHostnameOrSubdomain = \sn name ->
324326 matchesHostname sn name || containsSuffix ("." ++ name) sn
325327
328+isBadServerName : Text -> Bool
326329isBadServerName = \sn ->
327330 sn == ""
328331 || take 1 sn == "."
@@ -363,32 +366,40 @@ extractToken = \authHeader ->
363366 then Just {value: token}
364367 else Nothing {}
365368
369+verifySecret : Text -> Text -> Bool
366370verifySecret = \token secret ->
367371 secret != "" && safeCompare token secret
368372
373+verifyFederationAuth : Text -> Bool
369374verifyFederationAuth = \authorization ->
370375 case extractToken authorization of
371376 Nothing {} -> False {}
372377 Just {value: token} -> verifySecret token federationSecret
373378
379+verifyMaintenanceAuth : Text -> Bool
374380verifyMaintenanceAuth = \authorization ->
375381 case extractToken authorization of
376382 Nothing {} -> False {}
377383 Just {value: token} -> verifySecret token federationSecret || verifySecret token maintenanceSecret
378384
385+clampTtl : Int<S> -> Int<S>
379386clampTtl = clamp 1 maxPresenceTtl
380387
388+clampTimestamp : Timestamp -> Timestamp -> Timestamp
381389clampTimestamp = \timestamp t -> clamp (t - messageMaxAge) t timestamp
382390
391+deliveryToMessageStatus : DeliveryStatus -> MessageStatus
383392deliveryToMessageStatus = \ds -> case ds of
384393 DsDelivered {} -> MsDelivered {}
385394 DsFederated {} -> MsFederated {}
386395 DsQueued {} -> MsQueued {}
387396
397+nextSeqTs : Timestamp -> Timestamp -> Timestamp
388398nextSeqTs = \t seqCounter -> if t > seqCounter then t else seqCounter + 1
389399
390400takeN = \items n -> (fold (\acc e -> if acc.n >= n then acc else {n: acc.n + 1, items: union acc.items [e]}) {n: 0, items: []} items).items
391401
402+dedupMessageIds : [MessageIdHex] -> [MessageIdHex]
392403dedupMessageIds = \messageIds ->
393404 fold (\acc mid ->
394405 if count acc >= maxMessageIds then acc
@@ -502,9 +513,11 @@ capGossipPresence = \args ->
502513 (do e <- reclaimedEntries; yield (pubkeyServer e)),
503514 withinCapacity: False {}}
504515
516+countMessagesFor : [Message] -> PubkeyHex -> Timestamp -> Int
505517countMessagesFor = \messages toKey t ->
506518 count (do m <- messages; where m.toKey == toKey && m.receivedAt >= t - messageMaxAge; yield m)
507519
520+hasMessage : MessageIdHex -> PubkeyHex -> [{messageId: MessageIdHex, toKey: PubkeyHex | a}] -> Bool
508521hasMessage = \mid toKey items -> any items (\x -> x.messageId == mid && x.toKey == toKey)
509522
510523
@@ -627,13 +640,11 @@ mergeGossipPresence = \presence deduped trustedOffline fromServer t ->
627640 gossipPeerServers: collectPeerServers capResult.finalPresence (\p -> p.server),
628641 withinCapacity: capResult.withinCapacity}
629642
630−rejectFederation = \t respond -> do
631− maybePruneAndBroadcast t
632− yield (respond {ok: False {}})
633−
634643withFederationAuth = \authorization t respond precondition action ->
635644 if not (verifyFederationAuth authorization) || not precondition
636− then rejectFederation t respond
645+ then do
646+ maybePruneAndBroadcast t
647+ yield (respond {ok: False {}})
637648 else action
638649
639650maybePruneAndBroadcast = \t -> do