mirror of
https://github.com/yattee/yattee.git
synced 2026-02-20 17:59:45 +00:00
260 lines
7.9 KiB
Swift
260 lines
7.9 KiB
Swift
//
|
|
// AppearanceSettingsView.swift
|
|
// Yattee
|
|
//
|
|
// Appearance settings with theme and accent color selection.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct AppearanceSettingsView: View {
|
|
@Environment(\.appEnvironment) private var appEnvironment
|
|
|
|
var body: some View {
|
|
Form {
|
|
if let settings = appEnvironment?.settingsManager {
|
|
// Theme section
|
|
ThemeSection(settings: settings)
|
|
|
|
// App icon section (iOS only)
|
|
#if os(iOS)
|
|
AppIconSection(settings: settings)
|
|
#endif
|
|
|
|
// Accent color section
|
|
AccentColorSection(settings: settings)
|
|
|
|
// List style section
|
|
ListStyleSection(settings: settings)
|
|
|
|
// Thumbnail section
|
|
ThumbnailSection(settings: settings)
|
|
}
|
|
}
|
|
.navigationTitle(String(localized: "settings.appearance.title"))
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// MARK: - Theme Section
|
|
|
|
private struct ThemeSection: View {
|
|
@Bindable var settings: SettingsManager
|
|
|
|
var body: some View {
|
|
Section(String(localized: "settings.appearance.theme.header")) {
|
|
Picker(
|
|
String(localized: "settings.appearance.theme"),
|
|
selection: $settings.theme
|
|
) {
|
|
ForEach(AppTheme.allCases, id: \.self) { theme in
|
|
Text(theme.displayName).tag(theme)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - App Icon Section (iOS only)
|
|
|
|
#if os(iOS)
|
|
private struct AppIconSection: View {
|
|
@Bindable var settings: SettingsManager
|
|
|
|
var body: some View {
|
|
Section(String(localized: "settings.appearance.appIcon.header")) {
|
|
NavigationLink {
|
|
AppIconPickerView(settings: settings)
|
|
} label: {
|
|
HStack {
|
|
Image(settings.appIcon.previewImageName)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 44, height: 44)
|
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
|
|
|
Text(settings.appIcon.displayName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct AppIconPickerView: View {
|
|
@Bindable var settings: SettingsManager
|
|
|
|
var body: some View {
|
|
List {
|
|
ForEach(AppIcon.allCases, id: \.self) { appIcon in
|
|
Button {
|
|
settings.appIcon = appIcon
|
|
} label: {
|
|
HStack {
|
|
Image(appIcon.previewImageName)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 60, height: 60)
|
|
.clipShape(RoundedRectangle(cornerRadius: 13.5))
|
|
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(appIcon.displayName)
|
|
.foregroundStyle(.primary)
|
|
|
|
if let author = appIcon.author {
|
|
Text(author)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
if settings.appIcon == appIcon {
|
|
Image(systemName: "checkmark")
|
|
.foregroundStyle(.blue)
|
|
}
|
|
}
|
|
.contentShape(Rectangle())
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.navigationTitle(String(localized: "settings.appearance.appIcon.header"))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// MARK: - Accent Color Section
|
|
|
|
private struct AccentColorSection: View {
|
|
@Bindable var settings: SettingsManager
|
|
|
|
var body: some View {
|
|
Section(String(localized: "settings.appearance.accentColor.header")) {
|
|
LazyVGrid(columns: [GridItem(.adaptive(minimum: 50))], spacing: 16) {
|
|
ForEach(AccentColor.allCases, id: \.self) { accentColor in
|
|
AccentColorButton(
|
|
accentColor: accentColor,
|
|
isSelected: settings.accentColor == accentColor,
|
|
onSelect: { settings.accentColor = accentColor }
|
|
)
|
|
}
|
|
}
|
|
.padding(.vertical, 8)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - List Style Section
|
|
|
|
private struct ListStyleSection: View {
|
|
@Bindable var settings: SettingsManager
|
|
|
|
var body: some View {
|
|
Section {
|
|
Picker(selection: $settings.listStyle) {
|
|
ForEach(VideoListStyle.allCases, id: \.self) { style in
|
|
Text(style.displayName).tag(style)
|
|
}
|
|
} label: {
|
|
Label(String(localized: "settings.appearance.listStyle"), systemImage: "list.bullet")
|
|
}
|
|
} header: {
|
|
Text(String(localized: "settings.appearance.listStyle.header"))
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Thumbnail Section
|
|
|
|
private struct ThumbnailSection: View {
|
|
@Bindable var settings: SettingsManager
|
|
|
|
var body: some View {
|
|
Section {
|
|
Toggle(isOn: $settings.showWatchedCheckmark) {
|
|
Label(String(localized: "settings.appearance.showWatchedCheckmark"),
|
|
systemImage: "checkmark.circle.fill")
|
|
}
|
|
} header: {
|
|
Text(String(localized: "settings.appearance.thumbnails.header"))
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Accent Color Button
|
|
|
|
private struct AccentColorButton: View {
|
|
let accentColor: AccentColor
|
|
let isSelected: Bool
|
|
let onSelect: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: onSelect) {
|
|
ZStack {
|
|
Circle()
|
|
.fill(accentColor.color)
|
|
.frame(width: 40, height: 40)
|
|
|
|
if isSelected {
|
|
Circle()
|
|
.strokeBorder(.white, lineWidth: 3)
|
|
.frame(width: 40, height: 40)
|
|
|
|
Image(systemName: "checkmark")
|
|
.font(.caption.bold())
|
|
.foregroundStyle(.white)
|
|
}
|
|
}
|
|
}
|
|
.buttonStyle(.plain)
|
|
.accessibilityLabel(accentColor.displayName)
|
|
}
|
|
}
|
|
|
|
// MARK: - Theme Display Names
|
|
|
|
extension AppTheme {
|
|
var displayName: String {
|
|
switch self {
|
|
case .system: return String(localized: "settings.appearance.theme.system")
|
|
case .light: return String(localized: "settings.appearance.theme.light")
|
|
case .dark: return String(localized: "settings.appearance.theme.dark")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Ambient Glow Section
|
|
|
|
// MARK: - Accent Color Display Names
|
|
|
|
extension AccentColor {
|
|
var displayName: String {
|
|
switch self {
|
|
case .default: return String(localized: "settings.appearance.accentColor.default")
|
|
case .red: return String(localized: "settings.appearance.accentColor.red")
|
|
case .pink: return String(localized: "settings.appearance.accentColor.pink")
|
|
case .orange: return String(localized: "settings.appearance.accentColor.orange")
|
|
case .yellow: return String(localized: "settings.appearance.accentColor.yellow")
|
|
case .green: return String(localized: "settings.appearance.accentColor.green")
|
|
case .teal: return String(localized: "settings.appearance.accentColor.teal")
|
|
case .blue: return String(localized: "settings.appearance.accentColor.blue")
|
|
case .purple: return String(localized: "settings.appearance.accentColor.purple")
|
|
case .indigo: return String(localized: "settings.appearance.accentColor.indigo")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Preview
|
|
|
|
#Preview {
|
|
NavigationStack {
|
|
AppearanceSettingsView()
|
|
}
|
|
.appEnvironment(.preview)
|
|
}
|