diff --git a/Yattee/Extensions/ToolbarContent+Close.swift b/Yattee/Extensions/ToolbarContent+Close.swift new file mode 100644 index 00000000..5bcc61bd --- /dev/null +++ b/Yattee/Extensions/ToolbarContent+Close.swift @@ -0,0 +1,50 @@ +// +// ToolbarContent+Close.swift +// Yattee +// +// Shared sheet-dismiss toolbar item. +// + +import SwiftUI + +/// Standard sheet-dismiss toolbar item. +/// +/// On macOS this renders a native text button (e.g. "Close"), which fits the desktop +/// convention. On iOS/tvOS it renders the compact icon-only xmark used by the rest of +/// the app's sheets. +/// +/// Placement defaults to `.confirmationAction`; only the *label* changes per platform, +/// so it can be dropped into existing `.toolbar { }` blocks without changing layout. +@ToolbarContentBuilder +func sheetCloseToolbarItem( + placement: ToolbarItemPlacement = .confirmationAction, + titleKey: LocalizedStringResource = "common.close", + identifier: String? = nil, + action: @escaping () -> Void +) -> some ToolbarContent { + ToolbarItem(placement: placement) { + sheetCloseButton(titleKey: titleKey, identifier: identifier, action: action) + } +} + +@ViewBuilder +private func sheetCloseButton( + titleKey: LocalizedStringResource, + identifier: String?, + action: @escaping () -> Void +) -> some View { + let button = Button(role: .cancel, action: action) { + #if os(macOS) + Text(String(localized: titleKey)) + #else + Label(String(localized: titleKey), systemImage: "xmark") + .labelStyle(.iconOnly) + #endif + } + + if let identifier { + button.accessibilityIdentifier(identifier) + } else { + button + } +} diff --git a/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift b/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift index efcb346c..f2ff2ed9 100644 --- a/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift +++ b/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift @@ -88,14 +88,7 @@ struct MediaBrowserViewOptionsSheet: View { .navigationBarTitleDisplayMode(.inline) #endif .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } } } } diff --git a/Yattee/Views/Player/ChaptersView.swift b/Yattee/Views/Player/ChaptersView.swift index f49c8335..6133a69c 100644 --- a/Yattee/Views/Player/ChaptersView.swift +++ b/Yattee/Views/Player/ChaptersView.swift @@ -31,14 +31,7 @@ struct ChaptersView: View { .navigationBarTitleDisplayMode(.inline) #endif .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } } .task { await preloadChapterSheets() diff --git a/Yattee/Views/Player/PlayerHelperViews.swift b/Yattee/Views/Player/PlayerHelperViews.swift index 80fa15ff..2e055d1f 100644 --- a/Yattee/Views/Player/PlayerHelperViews.swift +++ b/Yattee/Views/Player/PlayerHelperViews.swift @@ -114,14 +114,7 @@ struct ErrorDetailsSheet: View { #endif .toolbar { #if !os(tvOS) - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } ToolbarItemGroup(placement: .primaryAction) { // Copy button diff --git a/Yattee/Views/Player/QualitySelectorView.swift b/Yattee/Views/Player/QualitySelectorView.swift index 6298c03f..94c5f2bd 100644 --- a/Yattee/Views/Player/QualitySelectorView.swift +++ b/Yattee/Views/Player/QualitySelectorView.swift @@ -276,14 +276,7 @@ struct QualitySelectorView: View { #endif #if !os(tvOS) .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - performDismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { performDismiss() } } #endif .navigationDestination(for: QualitySelectorDestination.self) { destination in diff --git a/Yattee/Views/Player/QueueManagementSheet.swift b/Yattee/Views/Player/QueueManagementSheet.swift index 40f8f465..f29ba11c 100644 --- a/Yattee/Views/Player/QueueManagementSheet.swift +++ b/Yattee/Views/Player/QueueManagementSheet.swift @@ -125,14 +125,7 @@ struct QueueManagementSheet: View { ToolbarItem(placement: .cancellationAction) { queueModeMenu } - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - performDismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { performDismiss() } } } .presentationDragIndicator(.visible) diff --git a/Yattee/Views/RemoteControl/RemoteDevicesSheet.swift b/Yattee/Views/RemoteControl/RemoteDevicesSheet.swift index bd770329..0fcee390 100644 --- a/Yattee/Views/RemoteControl/RemoteDevicesSheet.swift +++ b/Yattee/Views/RemoteControl/RemoteDevicesSheet.swift @@ -24,14 +24,7 @@ struct RemoteDevicesSheet: View { .frame(minWidth: 400, minHeight: 300) #endif .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } } .navigationDestination(item: $selectedDevice) { device in RemoteControlView(device: device) diff --git a/Yattee/Views/Settings/AddSourceView.swift b/Yattee/Views/Settings/AddSourceView.swift index d3eff377..a99fe343 100644 --- a/Yattee/Views/Settings/AddSourceView.swift +++ b/Yattee/Views/Settings/AddSourceView.swift @@ -64,14 +64,7 @@ struct AddSourceView: View { .navigationBarTitleDisplayMode(.inline) #endif .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } } .navigationDestination(isPresented: $navigateToWebDAV) { AddWebDAVView( diff --git a/Yattee/Views/Settings/InstancePickerSheet.swift b/Yattee/Views/Settings/InstancePickerSheet.swift index 81d85cbd..bc9fa3cb 100644 --- a/Yattee/Views/Settings/InstancePickerSheet.swift +++ b/Yattee/Views/Settings/InstancePickerSheet.swift @@ -54,14 +54,7 @@ struct InstancePickerSheet: View { .navigationBarTitleDisplayMode(.inline) #endif .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } } } .presentationDetents([.medium, .large]) diff --git a/Yattee/Views/Settings/LogViewerView.swift b/Yattee/Views/Settings/LogViewerView.swift index 2e72ed74..ce56c71e 100644 --- a/Yattee/Views/Settings/LogViewerView.swift +++ b/Yattee/Views/Settings/LogViewerView.swift @@ -278,14 +278,7 @@ private struct LogEntryDetailView: View { .navigationBarTitleDisplayMode(.inline) #endif .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } } } .presentationDetents([.medium, .large]) diff --git a/Yattee/Views/Settings/PeerTubeInstancesExploreView.swift b/Yattee/Views/Settings/PeerTubeInstancesExploreView.swift index 7f5c9c6f..b4352052 100644 --- a/Yattee/Views/Settings/PeerTubeInstancesExploreView.swift +++ b/Yattee/Views/Settings/PeerTubeInstancesExploreView.swift @@ -215,14 +215,7 @@ struct PeerTubeInstancesExploreView: View { @ToolbarContentBuilder private var toolbarContent: some ToolbarContent { #if !os(tvOS) - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { dismiss() } #endif ToolbarItem(placement: .primaryAction) { diff --git a/Yattee/Views/Settings/PlayerControls/Gestures/TapGesturesSettingsView.swift b/Yattee/Views/Settings/PlayerControls/Gestures/TapGesturesSettingsView.swift index c56f48bf..7568bc2a 100644 --- a/Yattee/Views/Settings/PlayerControls/Gestures/TapGesturesSettingsView.swift +++ b/Yattee/Views/Settings/PlayerControls/Gestures/TapGesturesSettingsView.swift @@ -187,14 +187,7 @@ struct TapGesturesSettingsView: View { TapZoneActionPicker(position: position, action: binding) .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - selectedZonePosition = nil - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { selectedZonePosition = nil } } } diff --git a/Yattee/Views/Settings/SettingsView.swift b/Yattee/Views/Settings/SettingsView.swift index e4def9ee..7e3a81f7 100644 --- a/Yattee/Views/Settings/SettingsView.swift +++ b/Yattee/Views/Settings/SettingsView.swift @@ -328,15 +328,7 @@ struct SettingsView: View { .navigationBarTitleDisplayMode(.inline) .toolbar { if showCloseButton { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - .accessibilityIdentifier("settings.doneButton") - } + sheetCloseToolbarItem(identifier: "settings.doneButton") { dismiss() } } } .accessibilityIdentifier("settings.view") diff --git a/Yattee/Views/Video/PlaylistSelectorSheet.swift b/Yattee/Views/Video/PlaylistSelectorSheet.swift index 6b45984e..fc5eca44 100644 --- a/Yattee/Views/Video/PlaylistSelectorSheet.swift +++ b/Yattee/Views/Video/PlaylistSelectorSheet.swift @@ -89,22 +89,7 @@ struct PlaylistSelectorSheet: View { .navigationBarTitleDisplayMode(.inline) #endif .toolbar { - #if os(macOS) - ToolbarItem(placement: .cancellationAction) { - Button(String(localized: "common.close"), role: .cancel) { - dismiss() - } - } - #else - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - dismiss() - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } - #endif + sheetCloseToolbarItem { dismiss() } } #endif .sheet(isPresented: $showingNewPlaylist) { diff --git a/Yattee/Views/Video/VideoInfoView.swift b/Yattee/Views/Video/VideoInfoView.swift index c01ed6a9..b9d52a4f 100644 --- a/Yattee/Views/Video/VideoInfoView.swift +++ b/Yattee/Views/Video/VideoInfoView.swift @@ -1973,14 +1973,7 @@ struct VideoInfoView: View { #endif #if !os(tvOS) .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button(role: .cancel) { - showingCommentsSheet = false - } label: { - Label(String(localized: "common.close"), systemImage: "xmark") - .labelStyle(.iconOnly) - } - } + sheetCloseToolbarItem { showingCommentsSheet = false } } #endif }