From fa96ef5a63a3bf3552ab3c02b71a18d14e0208b2 Mon Sep 17 00:00:00 2001 From: Kaede Akino Date: Fri, 25 Sep 2026 19:59:22 +0800 Subject: [PATCH] fix(snippets): simplify add hosts picker --- TFM/Features/Snippets/SnippetsPageView.swift | 48 +++++-------------- TFM/Features/Vault/VaultView.swift | 2 +- .../SnippetTargetPickerRenderingTests.swift | 7 +++ 3 files changed, 20 insertions(+), 37 deletions(-) diff --git a/TFM/Features/Snippets/SnippetsPageView.swift b/TFM/Features/Snippets/SnippetsPageView.swift index cdcb70fe..5ccaeb85 100644 --- a/TFM/Features/Snippets/SnippetsPageView.swift +++ b/TFM/Features/Snippets/SnippetsPageView.swift @@ -539,22 +539,19 @@ private struct SnippetCardContent: View { } } -struct SnippetEditorSheet: View { - private enum Route: Hashable { case targets } - +struct SnippetEditorView: View { let existing: Snippet? let save: (Snippet) -> Void - var requestedClose: (() -> Void)? + let close: () -> Void @Environment(AppModel.self) private var app @Environment(WindowModel.self) private var window @Environment(VaultWorkEditingSession.self) private var editingSession - @Environment(\.dismiss) private var dismiss @State private var title: String @State private var bodyText: String @State private var packageID: SnippetPackage.ID? @State private var targets: Set - @State private var route: Route? + @State private var showsTargetPicker = false @State private var error: String? @State private var confirmsDeletion = false @State private var editingToken: UUID? @@ -562,11 +559,11 @@ struct SnippetEditorSheet: View { init( snippet: Snippet? = nil, packageID: SnippetPackage.ID? = nil, save: @escaping (Snippet) -> Void, - close: (() -> Void)? = nil + close: @escaping () -> Void ) { existing = snippet self.save = save - requestedClose = close + self.close = close _title = State(initialValue: snippet?.title ?? "") _bodyText = State(initialValue: snippet?.body ?? "") _packageID = State(initialValue: snippet?.packageID ?? packageID) @@ -575,9 +572,11 @@ struct SnippetEditorSheet: View { var body: some View { ZStack { - if let route { - destination(route) - .transition(.move(edge: .trailing)) + if showsTargetPicker { + SnippetTargetPickerView(selected: targets, back: { showsTargetPicker = false }) { + targets = $0 + } + .transition(.move(edge: .trailing)) } else { editor .transition(.move(edge: .leading)) @@ -588,7 +587,7 @@ struct SnippetEditorSheet: View { // detail, which makes its destination cover the Snippets collection too. .frame(maxWidth: .infinity, maxHeight: .infinity) .clipped() - .animation(.snappy(duration: 0.2), value: route) + .animation(.snappy(duration: 0.2), value: showsTargetPicker) .onAppear { editingToken = editingSession.register( route: .snippetEditor(snippetID: existing?.id, packageID: packageID), isDirty: { isDirty } @@ -663,7 +662,7 @@ struct SnippetEditorSheet: View { Text(AppStrings.Snippets.targets) Spacer() Button { - route = .targets + showsTargetPicker = true } label: { Label(AppStrings.Common.addHosts.swiftUIKey, systemImage: "plus") } @@ -710,10 +709,6 @@ struct SnippetEditorSheet: View { app.snippets.searchHosts("").filter { targets.contains($0.id) } } - private func close() { - if let requestedClose { requestedClose() } else { dismiss() } - } - private func cancelEditing() { editingSession.requestTransition( route: .snippetEditor(snippetID: existing?.id, packageID: packageID), action: close) @@ -748,20 +743,6 @@ struct SnippetEditorSheet: View { window.pruneVaultSelections(in: app.snippets) close() } - - @ViewBuilder - private func destination(_ route: Route) -> some View { - switch route { - case .targets: - SnippetTargetPickerView(selected: targets, back: closeNestedEditor) { - targets = $0 - } - } - } - - private func closeNestedEditor() { - route = nil - } } struct SnippetTargetPickerView: View { @@ -874,11 +855,6 @@ struct SnippetTargetPickerView: View { private var footer: some View { HStack { - Text(AppStrings.Common.selected(chooser.selected.count)) - .font(.caption) - .foregroundStyle(.secondary) - .monospacedDigit() - Button( allResultsChecked ? AppStrings.Common.deselectAll.swiftUIKey : AppStrings.Common.selectAll.swiftUIKey ) { diff --git a/TFM/Features/Vault/VaultView.swift b/TFM/Features/Vault/VaultView.swift index 23541160..17f201ca 100644 --- a/TFM/Features/Vault/VaultView.swift +++ b/TFM/Features/Vault/VaultView.swift @@ -73,7 +73,7 @@ struct VaultWorkDetailView: View { case .exportKeys(let ids): ExportKeyToHostsSheet(keys: ids.compactMap(app.hostLibrary.key)) case .snippetEditor(let snippetID, let packageID): - SnippetEditorSheet( + SnippetEditorView( snippet: snippetID.flatMap(app.hostLibrary.snippet), packageID: packageID, save: { snippet in app.hostLibrary.upsert(snippet) diff --git a/TFMTests/SnippetTargetPickerRenderingTests.swift b/TFMTests/SnippetTargetPickerRenderingTests.swift index 7563c7f1..d6959dc5 100644 --- a/TFMTests/SnippetTargetPickerRenderingTests.swift +++ b/TFMTests/SnippetTargetPickerRenderingTests.swift @@ -40,6 +40,7 @@ struct SnippetTargetPickerRenderingTests { Notification(name: NSControl.textDidChangeNotification, object: search) ) await fixture.settle() + try fixture.focusSearch() try fixture.sendKey(" ", code: 49) try fixture.performKeyEquivalent("\u{1B}", code: 53) #expect(capture.backCount == 1) @@ -54,6 +55,7 @@ struct SnippetTargetPickerRenderingTests { Notification(name: NSControl.textDidChangeNotification, object: reopenedSearch) ) await fixture.settle() + try fixture.focusSearch() try fixture.sendKey(" ", code: 49) try fixture.performKeyEquivalent("\r", code: 36) #expect(capture.backCount == cancelBackCount + 1) @@ -122,6 +124,11 @@ struct SnippetTargetPickerRenderingTests { NSApp.sendEvent(try keyEvent(characters, code: code)) } + func focusSearch() throws { + let search = try #require(visibleTextFields.first { $0.isEditable }) + #expect(window.makeFirstResponder(search)) + } + func performKeyEquivalent(_ characters: String, code: UInt16) throws { #expect(window.performKeyEquivalent(with: try keyEvent(characters, code: code))) }