yattee/Shared/Settings/SponsorBlockSettings.swift
Toni Förster b5ac760af2
SponsorBlock set colors for each category
Default colors are defined in alignment to upstream. These can be changed by the user.
The colors are also used in the Timeline and the seek view.
2024-04-23 17:16:31 +02:00

171 lines
5.6 KiB
Swift

import Defaults
import SwiftUI
import UIKit
struct SponsorBlockSettings: View {
@ObservedObject private var settings = SettingsModel.shared
@Default(.sponsorBlockInstance) private var sponsorBlockInstance
@Default(.sponsorBlockCategories) private var sponsorBlockCategories
@Default(.sponsorBlockColors) private var sponsorBlockColors
var body: some View {
Group {
#if os(macOS)
sections
Spacer()
#else
List {
sections
}
#endif
}
#if os(tvOS)
.frame(maxWidth: 1000)
#else
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
#endif
.navigationTitle("SponsorBlock")
}
private var sections: some View {
Group {
Section(header: SettingsHeader(text: "SponsorBlock API")) {
TextField(
"SponsorBlock API Instance",
text: $sponsorBlockInstance
)
.labelsHidden()
#if !os(macOS)
.autocapitalization(.none)
.disableAutocorrection(true)
.keyboardType(.URL)
#endif
}
Section(header: SettingsHeader(text: "Categories to Skip".localized())) {
categoryRows
}
colorSection
Button {
settings.presentAlert(
Alert(
title: Text("Restore Default Colors?"),
message: Text("This action will reset all custom colors back to their original defaults. " +
"Any custom color changes you've made will be lost."),
primaryButton: .destructive(Text("Restore")) {
resetColors()
},
secondaryButton: .cancel()
)
)
} label: {
Text("Restore Default Colors …")
.foregroundColor(.red)
}
Section(footer: categoriesDetails) {
EmptyView()
}
}
}
private var colorSection: some View {
Section(header: SettingsHeader(text: "Colors for Categories")) {
ForEach(SponsorBlockAPI.categories, id: \.self) { category in
LazyVStack(alignment: .leading) {
ColorPicker(
SponsorBlockAPI.categoryDescription(category) ?? "Unknown",
selection: Binding(
get: { getColor(for: category) },
set: { setColor($0, for: category) }
)
)
}
}
}
}
private var categoryRows: some View {
ForEach(SponsorBlockAPI.categories, id: \.self) { category in
LazyVStack(alignment: .leading) {
MultiselectRow(
title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown",
selected: sponsorBlockCategories.contains(category)
) { value in
toggleCategory(category, value: value)
}
}
}
}
private var categoriesDetails: some View {
VStack(alignment: .leading) {
ForEach(SponsorBlockAPI.categories, id: \.self) { category in
Text(SponsorBlockAPI.categoryDescription(category) ?? "Category")
.fontWeight(.bold)
.padding(.bottom, 0.5)
#if os(tvOS)
.focusable()
#endif
Text(SponsorBlockAPI.categoryDetails(category) ?? "Details")
.padding(.bottom, 10)
.fixedSize(horizontal: false, vertical: true)
}
}
.foregroundColor(.secondary)
}
func toggleCategory(_ category: String, value: Bool) {
if let index = sponsorBlockCategories.firstIndex(where: { $0 == category }), !value {
sponsorBlockCategories.remove(at: index)
} else if value {
sponsorBlockCategories.insert(category)
}
}
private func getColor(for category: String) -> Color {
if let hexString = sponsorBlockColors[category], let rgbValue = Int(hexString.dropFirst(), radix: 16) {
let r = Double((rgbValue >> 16) & 0xFF) / 255.0
let g = Double((rgbValue >> 8) & 0xFF) / 255.0
let b = Double(rgbValue & 0xFF) / 255.0
return Color(red: r, green: g, blue: b)
}
return Color("AppRedColor") // Fallback color if no match found
}
private func setColor(_ color: Color, for category: String) {
let uiColor = UIColor(color)
// swiftlint:disable no_cgfloat
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
// swiftlint:enable no_cgfloat
uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
let r = Int(red * 255.0)
let g = Int(green * 255.0)
let b = Int(blue * 255.0)
let rgbValue = (r << 16) | (g << 8) | b
sponsorBlockColors[category] = String(format: "#%06x", rgbValue)
}
private func resetColors() {
sponsorBlockColors = SponsorBlockColors.dictionary
}
}
struct SponsorBlockSettings_Previews: PreviewProvider {
static var previews: some View {
VStack {
SponsorBlockSettings()
}
.frame(maxHeight: 600)
}
}