From f01b07a9734b90df34c50b1a9762d4f243885ade Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 11 Jun 2026 14:03:10 +0200 Subject: [PATCH] =?UTF-8?q?fix(apple/tvOS):=20pushed=20routes=20instead=20?= =?UTF-8?q?of=20modal=20covers=20=E2=80=94=20the=20Settings-app=20navigati?= =?UTF-8?q?on=20feel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Host, Settings and PIN pairing were fullScreenCover overlays, which is why navigating felt unlike the system Settings app (no push animation, no Menu-pops-a-level semantics). They are now navigationDestination ROUTES pushed inside the home NavigationStack: - the system push/pop animation and Menu-button back navigation come for free; - the Settings pickers' navigationLink pushes reuse the same stack (its inner NavigationStack wrapper is gone, as is the tvOS Done row — Menu pops, like Settings); - Add Host is a real full-screen page (system navigation title, Settings-style rows on the standard backdrop) instead of a floating dialog, same for the pairing page; - the thickMaterial cover backdrops became unnecessary and are gone. The system keyboard entries stay as covers — that presentation is system-owned either way. iOS/macOS keep their sheets. Verified by screenshot: Add Host renders as a pushed full-screen route with the title top-center. Co-Authored-By: Claude Fable 5 --- .../PunktfunkClient/AddHostSheet.swift | 3 +- .../Sources/PunktfunkClient/ContentView.swift | 47 +++++++++---------- .../Sources/PunktfunkClient/PairSheet.swift | 4 +- .../PunktfunkClient/SettingsView.swift | 3 -- 4 files changed, 23 insertions(+), 34 deletions(-) diff --git a/clients/apple/Sources/PunktfunkClient/AddHostSheet.swift b/clients/apple/Sources/PunktfunkClient/AddHostSheet.swift index 2a675d8..c8619bb 100644 --- a/clients/apple/Sources/PunktfunkClient/AddHostSheet.swift +++ b/clients/apple/Sources/PunktfunkClient/AddHostSheet.swift @@ -23,8 +23,6 @@ struct AddHostSheet: View { // No inline text editing on tvOS — Settings-style value rows; pressing one // raises the SYSTEM fullscreen keyboard (TVTextEntry). VStack(spacing: 24) { - Text("Add Host") - .font(.title3.weight(.semibold)) TVFieldRow( label: "Name", value: name, placeholder: "Optional" ) { editing = .name } @@ -43,6 +41,7 @@ struct AddHostSheet: View { } .frame(maxWidth: 1000) .padding(60) + .navigationTitle("Add Host") .fullScreenCover(item: $editing) { field in switch field { case .name: diff --git a/clients/apple/Sources/PunktfunkClient/ContentView.swift b/clients/apple/Sources/PunktfunkClient/ContentView.swift index 29a5752..9d3846f 100644 --- a/clients/apple/Sources/PunktfunkClient/ContentView.swift +++ b/clients/apple/Sources/PunktfunkClient/ContentView.swift @@ -55,18 +55,7 @@ struct ContentView: View { // On the outer Group so the sheet survives the trust-prompt → home transition // (the "Pair with PIN instead" path disconnects first — the host's accept loop // is sequential, a pairing connection would queue behind the live session). - #if os(tvOS) - .fullScreenCover(item: $pairingTarget) { host in - PairSheet(host: host) { fingerprint in - guard pairingTarget?.id == host.id else { return } - store.pin(host.id, fingerprint: fingerprint) - var pinned = host - pinned.pinnedSHA256 = fingerprint - connect(pinned) - } - .background(.thickMaterial, ignoresSafeAreaEdges: .all) - } - #else + #if !os(tvOS) .sheet(item: $pairingTarget) { host in PairSheet(host: host) { fingerprint in // Backstop against a stale ceremony surfacing after dismissal (PairSheet @@ -149,6 +138,25 @@ struct ContentView: View { } } .navigationTitle("Punktfunkempfänger") + #if os(tvOS) + // Pushed routes — the Settings-app navigation feel (push animation, Menu + // pops) instead of modal overlays. + .navigationDestination(isPresented: $showAddHost) { + AddHostSheet { store.add($0) } + } + .navigationDestination(isPresented: $showSettings) { + SettingsView() + } + .navigationDestination(item: $pairingTarget) { host in + PairSheet(host: host) { fingerprint in + guard pairingTarget?.id == host.id else { return } + store.pin(host.id, fingerprint: fingerprint) + var pinned = host + pinned.pinnedSHA256 = fingerprint + connect(pinned) + } + } + #endif #if !os(tvOS) .toolbar { #if os(iOS) @@ -173,20 +181,7 @@ struct ContentView: View { #if os(macOS) .frame(minWidth: 480, minHeight: 360) #endif - #if os(tvOS) - // tvOS forms/lists have CLEAR backgrounds and a cover only shows what the - // presented view paints — back them with the standard tv blur-over-content. - .fullScreenCover(isPresented: $showAddHost) { - AddHostSheet { store.add($0) } - .background(.thickMaterial, ignoresSafeAreaEdges: .all) - } - .fullScreenCover(isPresented: $showSettings) { - NavigationStack { - SettingsView() - } - .background(.thickMaterial, ignoresSafeAreaEdges: .all) - } - #else + #if !os(tvOS) .sheet(isPresented: $showAddHost) { AddHostSheet { store.add($0) } } diff --git a/clients/apple/Sources/PunktfunkClient/PairSheet.swift b/clients/apple/Sources/PunktfunkClient/PairSheet.swift index 8e7aa67..272e58f 100644 --- a/clients/apple/Sources/PunktfunkClient/PairSheet.swift +++ b/clients/apple/Sources/PunktfunkClient/PairSheet.swift @@ -44,9 +44,6 @@ struct PairSheet: View { var body: some View { #if os(tvOS) VStack(spacing: 24) { - Label("Pair with \(host.displayName)", systemImage: "lock.shield") - .font(.title3.weight(.semibold)) - .foregroundStyle(.tint) Text("The host prints the PIN when pairing is armed " + "(--allow-pairing, \u{201C}PAIRING ARMED\u{201D} in its log). " + "Pairing verifies both sides at once — no fingerprint comparison " @@ -80,6 +77,7 @@ struct PairSheet: View { } .frame(maxWidth: 1000) .padding(60) + .navigationTitle("Pair with \(host.displayName)") .onDisappear { token.cancelled = true } .fullScreenCover(item: $editing) { field in switch field { diff --git a/clients/apple/Sources/PunktfunkClient/SettingsView.swift b/clients/apple/Sources/PunktfunkClient/SettingsView.swift index e358f43..81fd5a7 100644 --- a/clients/apple/Sources/PunktfunkClient/SettingsView.swift +++ b/clients/apple/Sources/PunktfunkClient/SettingsView.swift @@ -87,9 +87,6 @@ struct SettingsView: View { .font(.caption) .foregroundStyle(.secondary) } - Section { - Button("Done") { dismiss() } - } } .navigationTitle("Settings") }