mirror of
https://github.com/yattee/yattee.git
synced 2026-06-04 13:54:19 +00:00
Drop the standalone iOS section for Integrations and inline its row into the main list right above Advanced Settings. Swap the tvOS sidebar order so Integrations appears before Advanced as well. macOS was already correctly ordered via SettingsSection enum declaration.
414 lines
17 KiB
Swift
414 lines
17 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 .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 {
|
|
NavigationStack {
|
|
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 {
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button(role: .cancel) {
|
|
dismiss()
|
|
} label: {
|
|
Label(String(localized: "common.close"), systemImage: "xmark")
|
|
.labelStyle(.iconOnly)
|
|
}
|
|
.accessibilityIdentifier("settings.doneButton")
|
|
}
|
|
}
|
|
}
|
|
.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 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 .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 .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)
|
|
}
|