mirror of
https://github.com/yattee/yattee.git
synced 2026-05-13 10:55:03 +00:00
Convert YouTube Enhancements settings to macOS-native helpers
Includes SponsorBlock, Return YouTube Dislike, and DeArrow sub-screens. Extend SettingsNavigationRow with an optional trailing content slot so rows can show enabled/disabled status next to the chevron.
This commit is contained in:
@@ -11,7 +11,7 @@ struct DeArrowSettingsView: View {
|
|||||||
@Environment(\.appEnvironment) private var appEnvironment
|
@Environment(\.appEnvironment) private var appEnvironment
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
SettingsFormContainer {
|
||||||
if let settings = appEnvironment?.settingsManager {
|
if let settings = appEnvironment?.settingsManager {
|
||||||
// Enable/Disable toggle
|
// Enable/Disable toggle
|
||||||
EnableSection(settings: settings)
|
EnableSection(settings: settings)
|
||||||
@@ -44,13 +44,11 @@ private struct EnableSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection(footer: "settings.deArrow.footer") {
|
||||||
Toggle(
|
Toggle(
|
||||||
String(localized: "settings.deArrow.enabled"),
|
String(localized: "settings.deArrow.enabled"),
|
||||||
isOn: $settings.deArrowEnabled
|
isOn: $settings.deArrowEnabled
|
||||||
)
|
)
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.deArrow.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +59,7 @@ private struct OptionsSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(String(localized: "settings.deArrow.options.header")) {
|
SettingsFormSection("settings.deArrow.options.header") {
|
||||||
Toggle(
|
Toggle(
|
||||||
String(localized: "settings.deArrow.replaceTitles"),
|
String(localized: "settings.deArrow.replaceTitles"),
|
||||||
isOn: $settings.deArrowReplaceTitles
|
isOn: $settings.deArrowReplaceTitles
|
||||||
@@ -84,7 +82,7 @@ private struct AdvancedSection: View {
|
|||||||
@State private var thumbnailAPIURLText: String = ""
|
@State private var thumbnailAPIURLText: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.deArrow.advanced.header", footer: "settings.deArrow.apiURL.footer") {
|
||||||
TextField(
|
TextField(
|
||||||
String(localized: "settings.deArrow.apiURL"),
|
String(localized: "settings.deArrow.apiURL"),
|
||||||
text: $apiURLText,
|
text: $apiURLText,
|
||||||
@@ -140,10 +138,6 @@ private struct AdvancedSection: View {
|
|||||||
syncAPIURLs()
|
syncAPIURLs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.deArrow.advanced.header"))
|
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.deArrow.apiURL.footer"))
|
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
let currentAPIURL = settings.deArrowAPIURL
|
let currentAPIURL = settings.deArrowAPIURL
|
||||||
@@ -169,7 +163,7 @@ private struct AdvancedSection: View {
|
|||||||
|
|
||||||
private struct AboutSection: View {
|
private struct AboutSection: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(String(localized: "settings.deArrow.about.header")) {
|
SettingsFormSection("settings.deArrow.about.header") {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text(String(localized: "settings.deArrow.about.description"))
|
Text(String(localized: "settings.deArrow.about.description"))
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
|
|||||||
@@ -139,18 +139,21 @@ struct SettingsFormSection<Content: View>: View {
|
|||||||
/// On macOS it renders as a plain full-width list row with a trailing
|
/// On macOS it renders as a plain full-width list row with a trailing
|
||||||
/// chevron, matching the native macOS System Settings look. On iOS/tvOS
|
/// chevron, matching the native macOS System Settings look. On iOS/tvOS
|
||||||
/// it renders as a standard `NavigationLink` with a `Label`.
|
/// it renders as a standard `NavigationLink` with a `Label`.
|
||||||
struct SettingsNavigationRow<Destination: View>: View {
|
struct SettingsNavigationRow<Destination: View, Trailing: View>: View {
|
||||||
let titleKey: LocalizedStringKey
|
let titleKey: LocalizedStringKey
|
||||||
let systemImage: String
|
let systemImage: String
|
||||||
|
@ViewBuilder var trailing: () -> Trailing
|
||||||
@ViewBuilder var destination: () -> Destination
|
@ViewBuilder var destination: () -> Destination
|
||||||
|
|
||||||
init(
|
init(
|
||||||
_ titleKey: LocalizedStringKey,
|
_ titleKey: LocalizedStringKey,
|
||||||
systemImage: String,
|
systemImage: String,
|
||||||
|
@ViewBuilder trailing: @escaping () -> Trailing = { EmptyView() },
|
||||||
@ViewBuilder destination: @escaping () -> Destination
|
@ViewBuilder destination: @escaping () -> Destination
|
||||||
) {
|
) {
|
||||||
self.titleKey = titleKey
|
self.titleKey = titleKey
|
||||||
self.systemImage = systemImage
|
self.systemImage = systemImage
|
||||||
|
self.trailing = trailing
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +165,8 @@ struct SettingsNavigationRow<Destination: View>: View {
|
|||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Label(titleKey, systemImage: systemImage)
|
Label(titleKey, systemImage: systemImage)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
trailing()
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.tertiary)
|
.foregroundStyle(.tertiary)
|
||||||
@@ -169,7 +174,12 @@ struct SettingsNavigationRow<Destination: View>: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
#else
|
#else
|
||||||
|
HStack {
|
||||||
Label(titleKey, systemImage: systemImage)
|
Label(titleKey, systemImage: systemImage)
|
||||||
|
Spacer()
|
||||||
|
trailing()
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
|
|||||||
@@ -11,20 +11,16 @@ struct ReturnYouTubeDislikeSettingsView: View {
|
|||||||
@Environment(\.appEnvironment) private var appEnvironment
|
@Environment(\.appEnvironment) private var appEnvironment
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
SettingsFormContainer {
|
||||||
if let settings = appEnvironment?.settingsManager {
|
if let settings = appEnvironment?.settingsManager {
|
||||||
// Enable/Disable toggle
|
SettingsFormSection(footer: "settings.returnYouTubeDislike.footer") {
|
||||||
Section {
|
|
||||||
Toggle(
|
Toggle(
|
||||||
String(localized: "settings.returnYouTubeDislike.enabled"),
|
String(localized: "settings.returnYouTubeDislike.enabled"),
|
||||||
isOn: Bindable(settings).returnYouTubeDislikeEnabled
|
isOn: Bindable(settings).returnYouTubeDislikeEnabled
|
||||||
)
|
)
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.returnYouTubeDislike.footer"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// About section
|
SettingsFormSection("settings.returnYouTubeDislike.about.header") {
|
||||||
Section(String(localized: "settings.returnYouTubeDislike.about.header")) {
|
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text(String(localized: "settings.returnYouTubeDislike.about.description"))
|
Text(String(localized: "settings.returnYouTubeDislike.about.description"))
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ struct SponsorBlockSettingsView: View {
|
|||||||
@Environment(\.appEnvironment) private var appEnvironment
|
@Environment(\.appEnvironment) private var appEnvironment
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
SettingsFormContainer {
|
||||||
if let settings = appEnvironment?.settingsManager {
|
if let settings = appEnvironment?.settingsManager {
|
||||||
// Enable/Disable toggle
|
// Enable/Disable toggle
|
||||||
EnableSection(settings: settings)
|
EnableSection(settings: settings)
|
||||||
@@ -44,13 +44,11 @@ private struct EnableSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection(footer: "settings.sponsorBlock.footer") {
|
||||||
Toggle(
|
Toggle(
|
||||||
String(localized: "settings.sponsorBlock.enabled"),
|
String(localized: "settings.sponsorBlock.enabled"),
|
||||||
isOn: $settings.sponsorBlockEnabled
|
isOn: $settings.sponsorBlockEnabled
|
||||||
)
|
)
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.sponsorBlock.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +59,7 @@ private struct CategoriesSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.sponsorBlock.categories.header", footer: "settings.sponsorBlock.categories.footer") {
|
||||||
ForEach(SponsorBlockCategory.allCases, id: \.self) { category in
|
ForEach(SponsorBlockCategory.allCases, id: \.self) { category in
|
||||||
CategoryToggleRow(
|
CategoryToggleRow(
|
||||||
category: category,
|
category: category,
|
||||||
@@ -77,10 +75,6 @@ private struct CategoriesSection: View {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.sponsorBlock.categories.header"))
|
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.sponsorBlock.categories.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,7 +86,7 @@ private struct AdvancedSection: View {
|
|||||||
@State private var apiURLText: String = ""
|
@State private var apiURLText: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.sponsorBlock.advanced.header", footer: "settings.sponsorBlock.apiURL.footer") {
|
||||||
TextField(
|
TextField(
|
||||||
String(localized: "settings.sponsorBlock.apiURL"),
|
String(localized: "settings.sponsorBlock.apiURL"),
|
||||||
text: $apiURLText,
|
text: $apiURLText,
|
||||||
@@ -118,10 +112,6 @@ private struct AdvancedSection: View {
|
|||||||
settings.sponsorBlockAPIURL = SettingsManager.defaultSponsorBlockAPIURL
|
settings.sponsorBlockAPIURL = SettingsManager.defaultSponsorBlockAPIURL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.sponsorBlock.advanced.header"))
|
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.sponsorBlock.apiURL.footer"))
|
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
let currentURL = settings.sponsorBlockAPIURL
|
let currentURL = settings.sponsorBlockAPIURL
|
||||||
@@ -136,7 +126,7 @@ private struct AdvancedSection: View {
|
|||||||
|
|
||||||
private struct AboutSection: View {
|
private struct AboutSection: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(String(localized: "settings.sponsorBlock.about.header")) {
|
SettingsFormSection("settings.sponsorBlock.about.header") {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text(String(localized: "settings.sponsorBlock.about.description"))
|
Text(String(localized: "settings.sponsorBlock.about.description"))
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ struct YouTubeEnhancementsSettingsView: View {
|
|||||||
@Environment(\.appEnvironment) private var appEnvironment
|
@Environment(\.appEnvironment) private var appEnvironment
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
SettingsFormContainer {
|
||||||
if let settings = appEnvironment?.settingsManager {
|
if let settings = appEnvironment?.settingsManager {
|
||||||
SponsorBlockSection(settings: settings)
|
SponsorBlockSection(settings: settings)
|
||||||
ReturnYouTubeDislikeSection(settings: settings)
|
ReturnYouTubeDislikeSection(settings: settings)
|
||||||
@@ -33,21 +33,18 @@ private struct SponsorBlockSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection(footer: "settings.youtubeEnhancements.sponsorBlock.footer") {
|
||||||
NavigationLink {
|
SettingsNavigationRow(
|
||||||
SponsorBlockSettingsView()
|
"settings.sponsorBlock.sectionTitle",
|
||||||
} label: {
|
systemImage: "forward",
|
||||||
HStack {
|
trailing: {
|
||||||
Label(String(localized: "settings.sponsorBlock.sectionTitle"), systemImage: "forward")
|
|
||||||
Spacer()
|
|
||||||
Text(settings.sponsorBlockEnabled
|
Text(settings.sponsorBlockEnabled
|
||||||
? String(localized: "common.enabled")
|
? String(localized: "common.enabled")
|
||||||
: String(localized: "common.disabled"))
|
: String(localized: "common.disabled"))
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
SponsorBlockSettingsView()
|
||||||
}
|
}
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.youtubeEnhancements.sponsorBlock.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,21 +55,18 @@ private struct ReturnYouTubeDislikeSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection(footer: "settings.youtubeEnhancements.returnYouTubeDislike.footer") {
|
||||||
NavigationLink {
|
SettingsNavigationRow(
|
||||||
ReturnYouTubeDislikeSettingsView()
|
"settings.returnYouTubeDislike.sectionTitle",
|
||||||
} label: {
|
systemImage: "hand.thumbsdown",
|
||||||
HStack {
|
trailing: {
|
||||||
Label(String(localized: "settings.returnYouTubeDislike.sectionTitle"), systemImage: "hand.thumbsdown")
|
|
||||||
Spacer()
|
|
||||||
Text(settings.returnYouTubeDislikeEnabled
|
Text(settings.returnYouTubeDislikeEnabled
|
||||||
? String(localized: "common.enabled")
|
? String(localized: "common.enabled")
|
||||||
: String(localized: "common.disabled"))
|
: String(localized: "common.disabled"))
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
ReturnYouTubeDislikeSettingsView()
|
||||||
}
|
}
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.youtubeEnhancements.returnYouTubeDislike.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,21 +77,18 @@ private struct DeArrowSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection(footer: "settings.youtubeEnhancements.deArrow.footer") {
|
||||||
NavigationLink {
|
SettingsNavigationRow(
|
||||||
DeArrowSettingsView()
|
"settings.deArrow.sectionTitle",
|
||||||
} label: {
|
systemImage: "textformat",
|
||||||
HStack {
|
trailing: {
|
||||||
Label(String(localized: "settings.deArrow.sectionTitle"), systemImage: "textformat")
|
|
||||||
Spacer()
|
|
||||||
Text(settings.deArrowEnabled
|
Text(settings.deArrowEnabled
|
||||||
? String(localized: "common.enabled")
|
? String(localized: "common.enabled")
|
||||||
: String(localized: "common.disabled"))
|
: String(localized: "common.disabled"))
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
DeArrowSettingsView()
|
||||||
}
|
}
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.youtubeEnhancements.deArrow.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user