mirror of
https://github.com/yattee/yattee.git
synced 2026-07-20 14:22:02 +00:00
Render the macOS control bar button row and top bar from the active preset's sections via a new MacOSControlsSectionRenderer, replacing the hardcoded QuickTime-style rows in MacOSControlBar. Add a keepOnTop button type and macOS-specific button availability lists, and adapt the player controls settings editors (preset editor, section editor, button configuration) for macOS. Also apply the preset font style to the control bar time labels, and update the built-in macOS Default preset order (queue before transport, fullscreen at the end), bumping builtInPresetsVersion to 7.
409 lines
16 KiB
Swift
409 lines
16 KiB
Swift
//
|
|
// SettingsView.swift
|
|
// Yattee
|
|
//
|
|
// Main settings view.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct SettingsView: View {
|
|
@Environment(\.dismiss) private var dismiss
|
|
@Environment(\.appEnvironment) private var appEnvironment
|
|
|
|
var showCloseButton: Bool = true
|
|
|
|
#if os(macOS)
|
|
@State private var selectedSection: SettingsSection? = .sources
|
|
#endif
|
|
|
|
var body: some View {
|
|
#if os(macOS)
|
|
macOSSettings
|
|
.frame(minWidth: 600, minHeight: 520)
|
|
#elseif os(tvOS)
|
|
tvOSSettings
|
|
#else
|
|
iOSSettings
|
|
#endif
|
|
}
|
|
|
|
// MARK: - macOS Settings
|
|
|
|
#if os(macOS)
|
|
private var macOSSettings: some View {
|
|
NavigationSplitView {
|
|
VStack(spacing: 0) {
|
|
List(SettingsSection.allCases, selection: $selectedSection) { section in
|
|
Label(section.title, systemImage: section.icon)
|
|
.tag(section)
|
|
}
|
|
.listStyle(.sidebar)
|
|
|
|
Divider()
|
|
macOSSidebarFooter
|
|
}
|
|
.navigationSplitViewColumnWidth(min: 180, ideal: 200)
|
|
} detail: {
|
|
if appEnvironment != nil {
|
|
NavigationStack {
|
|
Group {
|
|
switch selectedSection {
|
|
case .sources:
|
|
SourcesListView()
|
|
case .icloud:
|
|
iCloudSettingsView()
|
|
case .appearance:
|
|
AppearanceSettingsView()
|
|
case .layoutNavigation:
|
|
LayoutNavigationSettingsView()
|
|
case .playback:
|
|
PlaybackSettingsView()
|
|
case .playerControls:
|
|
PlayerControlsSettingsView()
|
|
case .notifications:
|
|
NotificationSettingsView()
|
|
case .downloads:
|
|
DownloadSettingsView()
|
|
case .privacy:
|
|
PrivacySettingsView()
|
|
case .youtubeEnhancements:
|
|
YouTubeEnhancementsSettingsView()
|
|
case .advanced:
|
|
AdvancedSettingsView()
|
|
case .about:
|
|
AboutView()
|
|
case .none:
|
|
Text(String(localized: "settings.placeholder.selectSection"))
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var macOSSidebarFooter: some View {
|
|
VStack(spacing: 6) {
|
|
Image("AppIconPreview")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 48, height: 48)
|
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
|
|
|
Text(verbatim: "Yattee")
|
|
.font(.callout)
|
|
.fontWeight(.semibold)
|
|
|
|
Text("\(appVersion) (\(buildNumber))")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.padding(.vertical, 12)
|
|
.allowsHitTesting(false)
|
|
}
|
|
#endif
|
|
|
|
// MARK: - tvOS Settings
|
|
|
|
#if os(tvOS)
|
|
private var tvOSSettings: some View {
|
|
List {
|
|
if let appEnvironment {
|
|
NavigationLink {
|
|
SourcesListView()
|
|
} label: {
|
|
HStack {
|
|
Label(String(localized: "sources.title"), systemImage: "server.rack")
|
|
Spacer()
|
|
if appEnvironment.mediaSourcesManager.hasSourcesNeedingPassword {
|
|
Image(systemName: "exclamationmark.circle.fill")
|
|
.foregroundStyle(.orange)
|
|
}
|
|
}
|
|
}
|
|
.accessibilityIdentifier("settings.row.sources")
|
|
|
|
NavigationLink {
|
|
TVSidebarDetailContainer(systemImage: "icloud", title: String(localized: "settings.icloud.title")) { iCloudSettingsView() }
|
|
} label: {
|
|
HStack {
|
|
Label(String(localized: "settings.icloud.title"), systemImage: "icloud")
|
|
#if DEBUG
|
|
Spacer()
|
|
Text(String(localized: "settings.icloud.dev.badge"))
|
|
.font(.caption2.bold())
|
|
.foregroundStyle(.white)
|
|
.padding(.horizontal, 6)
|
|
.padding(.vertical, 2)
|
|
.background(.orange, in: Capsule())
|
|
#endif
|
|
}
|
|
}
|
|
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "paintbrush", title: String(localized: "settings.appearance.sectionTitle")) { AppearanceSettingsView() } } label: {
|
|
Label(String(localized: "settings.appearance.sectionTitle"), systemImage: "paintbrush")
|
|
}
|
|
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "hand.tap", title: String(localized: "settings.layoutNavigation.title")) { LayoutNavigationSettingsView() } } label: {
|
|
Label(String(localized: "settings.layoutNavigation.title"), systemImage: "hand.tap")
|
|
}
|
|
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "rectangle.on.rectangle.angled", title: String(localized: "settings.topShelf.title", defaultValue: "Top Shelf")) { TopShelfSettingsView() } } label: {
|
|
Label(String(localized: "settings.topShelf.title", defaultValue: "Top Shelf"), systemImage: "rectangle.on.rectangle.angled")
|
|
}
|
|
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "play.circle", title: String(localized: "settings.playback.sectionTitle")) { PlaybackSettingsView() } } label: {
|
|
Label(String(localized: "settings.playback.sectionTitle"), systemImage: "play.circle")
|
|
}
|
|
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "hand.raised", title: String(localized: "settings.privacy.title")) { PrivacySettingsView() } } label: {
|
|
Label(String(localized: "settings.privacy.title"), systemImage: "hand.raised")
|
|
}
|
|
|
|
if appEnvironment.instancesManager.enabledInstances.contains(where: \.isYouTubeInstance) {
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "puzzlepiece.extension", title: String(localized: "settings.youtubeEnhancements.title")) { YouTubeEnhancementsSettingsView() } } label: {
|
|
Label(String(localized: "settings.youtubeEnhancements.title"), systemImage: "puzzlepiece.extension")
|
|
}
|
|
}
|
|
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "gearshape.2", title: String(localized: "settings.advanced.title")) { AdvancedSettingsView() } } label: {
|
|
Label(String(localized: "settings.advanced.title"), systemImage: "gearshape.2")
|
|
}
|
|
|
|
NavigationLink { TVSidebarDetailContainer(systemImage: "info.circle", title: String(localized: "settings.about.title")) { AboutView() } } label: {
|
|
Label(String(localized: "settings.about.title"), systemImage: "info.circle")
|
|
}
|
|
}
|
|
}
|
|
.listStyle(.grouped)
|
|
.safeAreaInset(edge: .leading) {
|
|
VStack(spacing: 20) {
|
|
Spacer()
|
|
|
|
Image("AppIconPreview")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 200, height: 200)
|
|
.clipShape(RoundedRectangle(cornerRadius: 46))
|
|
|
|
Text(verbatim: "Yattee")
|
|
.font(.title2)
|
|
.fontWeight(.semibold)
|
|
|
|
Text("\(appVersion) (\(buildNumber))")
|
|
.font(.callout)
|
|
.foregroundStyle(.secondary)
|
|
|
|
Spacer()
|
|
}
|
|
.frame(width: 400)
|
|
.allowsHitTesting(false)
|
|
}
|
|
.accessibilityIdentifier("settings.view")
|
|
}
|
|
#endif
|
|
|
|
// MARK: - iOS Settings
|
|
|
|
#if os(iOS)
|
|
private var iOSSettings: some View {
|
|
NavigationStack {
|
|
List {
|
|
if let appEnvironment {
|
|
Section {
|
|
NavigationLink {
|
|
SourcesListView()
|
|
} label: {
|
|
HStack {
|
|
Label(String(localized: "sources.title"), systemImage: "server.rack")
|
|
Spacer()
|
|
if appEnvironment.mediaSourcesManager.hasSourcesNeedingPassword {
|
|
Image(systemName: "exclamationmark.circle.fill")
|
|
.foregroundStyle(.orange)
|
|
}
|
|
}
|
|
}
|
|
.accessibilityIdentifier("settings.row.sources")
|
|
}
|
|
|
|
Section {
|
|
NavigationLink {
|
|
iCloudSettingsView()
|
|
} label: {
|
|
HStack {
|
|
Label(String(localized: "settings.icloud.title"), systemImage: "icloud")
|
|
#if DEBUG
|
|
Spacer()
|
|
Text(String(localized: "settings.icloud.dev.badge"))
|
|
.font(.caption2.bold())
|
|
.foregroundStyle(.white)
|
|
.padding(.horizontal, 6)
|
|
.padding(.vertical, 2)
|
|
.background(.orange, in: Capsule())
|
|
#endif
|
|
}
|
|
}
|
|
|
|
NavigationLink {
|
|
AppearanceSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.appearance.sectionTitle"), systemImage: "paintbrush")
|
|
}
|
|
|
|
NavigationLink {
|
|
LayoutNavigationSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.layoutNavigation.title"), systemImage: "hand.tap")
|
|
}
|
|
|
|
NavigationLink {
|
|
PlaybackSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.playback.sectionTitle"), systemImage: "play.circle")
|
|
}
|
|
|
|
#if os(iOS)
|
|
NavigationLink {
|
|
PlayerControlsSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.playerControls.title"), systemImage: "slider.horizontal.below.rectangle")
|
|
}
|
|
|
|
NavigationLink {
|
|
NotificationSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.notifications.title"), systemImage: "bell.badge")
|
|
}
|
|
|
|
NavigationLink {
|
|
DownloadSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.downloads.title"), systemImage: "arrow.down.circle")
|
|
}
|
|
#endif
|
|
|
|
NavigationLink {
|
|
PrivacySettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.privacy.title"), systemImage: "hand.raised")
|
|
}
|
|
|
|
if appEnvironment.instancesManager.enabledInstances.contains(where: \.isYouTubeInstance) {
|
|
NavigationLink {
|
|
YouTubeEnhancementsSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.youtubeEnhancements.title"), systemImage: "puzzlepiece.extension")
|
|
}
|
|
}
|
|
|
|
NavigationLink {
|
|
AdvancedSettingsView()
|
|
} label: {
|
|
Label(String(localized: "settings.advanced.title"), systemImage: "gearshape.2")
|
|
}
|
|
}
|
|
|
|
Section {
|
|
NavigationLink {
|
|
AboutView()
|
|
} label: {
|
|
Label(String(localized: "settings.about.title"), systemImage: "info.circle")
|
|
}
|
|
}
|
|
|
|
Section {
|
|
VStack(spacing: 4) {
|
|
Text(verbatim: "Yattee")
|
|
.font(.headline)
|
|
Text("\(appVersion) (\(buildNumber))")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.listRowBackground(Color.clear)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle(String(localized: "settings.title"))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
if showCloseButton {
|
|
sheetCloseToolbarItem(identifier: "settings.doneButton") { dismiss() }
|
|
}
|
|
}
|
|
.accessibilityIdentifier("settings.view")
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// MARK: - App Info
|
|
|
|
private var appVersion: String {
|
|
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "Unknown"
|
|
}
|
|
|
|
private var buildNumber: String {
|
|
Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "Unknown"
|
|
}
|
|
}
|
|
|
|
// MARK: - Settings Sections
|
|
|
|
enum SettingsSection: String, CaseIterable, Identifiable {
|
|
case sources
|
|
case icloud
|
|
case appearance
|
|
case layoutNavigation
|
|
case playback
|
|
case playerControls
|
|
case notifications
|
|
case downloads
|
|
case privacy
|
|
case youtubeEnhancements
|
|
case advanced
|
|
case about
|
|
|
|
var id: String { rawValue }
|
|
|
|
var title: String {
|
|
switch self {
|
|
case .sources: return String(localized: "sources.title")
|
|
case .icloud: return String(localized: "settings.icloud.title")
|
|
case .appearance: return String(localized: "settings.appearance.sectionTitle")
|
|
case .layoutNavigation: return String(localized: "settings.layoutNavigation.title")
|
|
case .playback: return String(localized: "settings.playback.sectionTitle")
|
|
case .playerControls: return String(localized: "settings.playerControls.title")
|
|
case .notifications: return String(localized: "settings.notifications.title")
|
|
case .downloads: return String(localized: "settings.downloads.title")
|
|
case .privacy: return String(localized: "settings.privacy.title")
|
|
case .youtubeEnhancements: return String(localized: "settings.youtubeEnhancements.title")
|
|
case .advanced: return String(localized: "settings.advanced.title")
|
|
case .about: return String(localized: "settings.about.title")
|
|
}
|
|
}
|
|
|
|
var icon: String {
|
|
switch self {
|
|
case .sources: return "server.rack"
|
|
case .icloud: return "icloud"
|
|
case .appearance: return "paintbrush"
|
|
case .layoutNavigation: return "hand.tap"
|
|
case .playback: return "play.circle"
|
|
case .playerControls: return "slider.horizontal.below.rectangle"
|
|
case .notifications: return "bell.badge"
|
|
case .downloads: return "arrow.down.circle"
|
|
case .privacy: return "hand.raised"
|
|
case .youtubeEnhancements: return "puzzlepiece.extension"
|
|
case .advanced: return "gearshape.2"
|
|
case .about: return "info.circle"
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
SettingsView()
|
|
.appEnvironment(.preview)
|
|
}
|