mirror of
https://github.com/yattee/yattee.git
synced 2026-02-20 01:39:46 +00:00
Yattee v2 rewrite
This commit is contained in:
259
Yattee/Views/Settings/AppearanceSettingsView.swift
Normal file
259
Yattee/Views/Settings/AppearanceSettingsView.swift
Normal file
@@ -0,0 +1,259 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user