mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
M9 #2+#4: ServerListView root + ServerID-aware onboarding
ScarfGo now boots into a list of configured servers instead of the single-server Dashboard. Each row renders nickname + user@host:port, taps to connect, swipes to forget. A "+" toolbar button re-enters onboarding for a new server. Fresh install → straight to onboarding. RootModel state machine redesigned around the multi-server world: - `.loading` → `.serverList` when listAll() returns 1+ servers. - `.loading` → `.onboarding(forNewServer:)` on fresh install. - `.serverList` → `.onboarding(newID)` via "+" button. - `.serverList` → `.connected(id, config, key)` via row tap. - `.connected(id)` → `.serverList` via soft Disconnect (keeps creds). - `.connected(id)` → `.serverList|.onboarding` via Forget (wipes id). - `.onboarding` → `.connected(newID, …)` on completion. Published `servers: [ServerID: IOSServerConfig]` on the RootModel so ServerListView renders reactively without re-querying stores on every re-render. `refreshServers()` is the `.task` hook; `forget()` wipes a single id + refreshes. OnboardingViewModel gains an optional `targetServerID` so its final save lands in `keyStore.save(_:for:)` / `configStore.save(_🆔)` instead of the singleton shims. Nil falls back to the old singleton path for any remaining callers (tests, previews). OnboardingRootView accepts `targetServerID` + a new `onCancel` closure. The toolbar now shows Cancel so users can back out without leaving half-written credentials; Cancel hides on the final .connected step so you can't race-cancel a just-saved server. ScarfGoTabRoot takes the server's ServerID as the context id so the CitadelServerTransport pool caches per-server (two active servers → two connection holders, no SSH channel contention). Splits the v1 onDisconnect into two callbacks: - onSoftDisconnect: close transport, return to server list, keep creds. - onForget: wipe this server's creds + return to server list (or onboarding if empty). MoreTab renders both Disconnect and Forget rows in distinct sections with explicit footers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,17 +53,25 @@ public final class OnboardingViewModel {
|
||||
private let configStore: any IOSServerConfigStore
|
||||
private let tester: any SSHConnectionTester
|
||||
private let keyGenerator: KeyGenerator
|
||||
/// ServerID under which to save the key + config on completion.
|
||||
/// Single-server v1 left this nil and the stores fell back to the
|
||||
/// singleton APIs. M9 multi-server passes in a fresh ID from the
|
||||
/// caller (or an existing ID when re-onboarding an existing row),
|
||||
/// so the save lands in the right slot.
|
||||
public let targetServerID: ServerID?
|
||||
|
||||
public init(
|
||||
keyStore: any SSHKeyStore,
|
||||
configStore: any IOSServerConfigStore,
|
||||
tester: any SSHConnectionTester,
|
||||
keyGenerator: @escaping KeyGenerator
|
||||
keyGenerator: @escaping KeyGenerator,
|
||||
targetServerID: ServerID? = nil
|
||||
) {
|
||||
self.keyStore = keyStore
|
||||
self.configStore = configStore
|
||||
self.tester = tester
|
||||
self.keyGenerator = keyGenerator
|
||||
self.targetServerID = targetServerID
|
||||
}
|
||||
|
||||
// MARK: - Derived
|
||||
@@ -142,7 +150,11 @@ public final class OnboardingViewModel {
|
||||
defer { isWorking = false }
|
||||
|
||||
do {
|
||||
try await keyStore.save(bundle)
|
||||
if let id = targetServerID {
|
||||
try await keyStore.save(bundle, for: id)
|
||||
} else {
|
||||
try await keyStore.save(bundle)
|
||||
}
|
||||
} catch {
|
||||
lastTestError = .other("Couldn't save key to Keychain: \(error.localizedDescription)")
|
||||
step = .testFailed(reason: lastTestError?.errorDescription ?? "Keychain save failed")
|
||||
@@ -190,7 +202,11 @@ public final class OnboardingViewModel {
|
||||
|
||||
do {
|
||||
try await tester.testConnection(config: config, key: bundle)
|
||||
try await configStore.save(config)
|
||||
if let id = targetServerID {
|
||||
try await configStore.save(config, id: id)
|
||||
} else {
|
||||
try await configStore.save(config)
|
||||
}
|
||||
step = .connected
|
||||
} catch let err as SSHConnectionTestError {
|
||||
lastTestError = err
|
||||
|
||||
Reference in New Issue
Block a user