Generic Button to Input from an Alert (#1273)
* Creation of an Alert Input Button based on the Chevron Button. * Only one button required for both iOS and tvOS * wip --------- Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
This commit is contained in:
parent
43811aa50d
commit
2bda693143
|
@ -0,0 +1,96 @@
|
||||||
|
//
|
||||||
|
// Swiftfin is subject to the terms of the Mozilla Public
|
||||||
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// TODO: find better name
|
||||||
|
|
||||||
|
struct ChevronAlertButton<Content>: View where Content: View {
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var isSelected = false
|
||||||
|
|
||||||
|
private let content: () -> Content
|
||||||
|
private let description: String?
|
||||||
|
private let onCancel: (() -> Void)?
|
||||||
|
private let onSave: (() -> Void)?
|
||||||
|
private let subtitle: Text?
|
||||||
|
private let title: String
|
||||||
|
|
||||||
|
// MARK: - Body
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ChevronButton(title, subtitle: subtitle)
|
||||||
|
.onSelect {
|
||||||
|
isSelected = true
|
||||||
|
}
|
||||||
|
.alert(title, isPresented: $isSelected) {
|
||||||
|
|
||||||
|
content()
|
||||||
|
|
||||||
|
if let onSave {
|
||||||
|
Button(L10n.save) {
|
||||||
|
onSave()
|
||||||
|
isSelected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let onCancel {
|
||||||
|
Button(L10n.cancel, role: .cancel) {
|
||||||
|
onCancel()
|
||||||
|
isSelected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} message: {
|
||||||
|
if let description = description {
|
||||||
|
Text(description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ChevronAlertButton {
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: String?,
|
||||||
|
description: String? = nil,
|
||||||
|
@ViewBuilder content: @escaping () -> Content,
|
||||||
|
onSave: (() -> Void)? = nil,
|
||||||
|
onCancel: (() -> Void)? = nil
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
content: content,
|
||||||
|
description: description,
|
||||||
|
onCancel: onCancel,
|
||||||
|
onSave: onSave,
|
||||||
|
subtitle: subtitle != nil ? Text(subtitle!) : nil,
|
||||||
|
title: title
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initializer: Text Inputs with Save/Cancel Actions
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: Text?,
|
||||||
|
description: String? = nil,
|
||||||
|
@ViewBuilder content: @escaping () -> Content,
|
||||||
|
onSave: (() -> Void)? = nil,
|
||||||
|
onCancel: (() -> Void)? = nil
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
content: content,
|
||||||
|
description: description,
|
||||||
|
onCancel: onCancel,
|
||||||
|
onSave: onSave,
|
||||||
|
subtitle: subtitle,
|
||||||
|
title: title
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
//
|
||||||
|
// Swiftfin is subject to the terms of the Mozilla Public
|
||||||
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ChevronButton<Icon: View>: View {
|
||||||
|
|
||||||
|
private let icon: Icon
|
||||||
|
private let isExternal: Bool
|
||||||
|
private let title: Text
|
||||||
|
private let subtitle: Text?
|
||||||
|
private var onSelect: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button(action: onSelect) {
|
||||||
|
HStack {
|
||||||
|
|
||||||
|
icon
|
||||||
|
.font(.body.weight(.bold))
|
||||||
|
|
||||||
|
title
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if let subtitle {
|
||||||
|
subtitle
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
Image(systemName: isExternal ? "arrow.up.forward" : "chevron.right")
|
||||||
|
.font(.body.weight(.regular))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundStyle(.primary, .secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ChevronButton where Icon == EmptyView {
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: String? = nil,
|
||||||
|
external: Bool = false
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
icon: EmptyView(),
|
||||||
|
isExternal: external,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: {
|
||||||
|
if let subtitle {
|
||||||
|
Text(subtitle)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
onSelect: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: Text?,
|
||||||
|
external: Bool = false
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
icon: EmptyView(),
|
||||||
|
isExternal: external,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: subtitle,
|
||||||
|
onSelect: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ChevronButton where Icon == Image {
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: String? = nil,
|
||||||
|
systemName: String,
|
||||||
|
external: Bool = false
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
icon: Image(systemName: systemName),
|
||||||
|
isExternal: external,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: {
|
||||||
|
if let subtitle {
|
||||||
|
Text(subtitle)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
onSelect: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: Text?,
|
||||||
|
systemName: String,
|
||||||
|
external: Bool = false
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
icon: Image(systemName: systemName),
|
||||||
|
isExternal: external,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: subtitle,
|
||||||
|
onSelect: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: String? = nil,
|
||||||
|
image: Image,
|
||||||
|
external: Bool = false
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
icon: image,
|
||||||
|
isExternal: external,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: {
|
||||||
|
if let subtitle {
|
||||||
|
Text(subtitle)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
onSelect: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ title: String,
|
||||||
|
subtitle: Text?,
|
||||||
|
image: Image,
|
||||||
|
external: Bool = false
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
icon: image,
|
||||||
|
isExternal: external,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: subtitle,
|
||||||
|
onSelect: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ChevronButton {
|
||||||
|
|
||||||
|
func onSelect(perform action: @escaping () -> Void) -> Self {
|
||||||
|
copy(modifying: \.onSelect, with: action)
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,3 +77,31 @@ extension ParseableFormatStyle where Self == DayIntervalParseableFormatStyle {
|
||||||
.init(range: range)
|
.init(range: range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension FormatStyle where Self == TimeIntervalFormatStyle {
|
||||||
|
|
||||||
|
static func interval(
|
||||||
|
style: Date.ComponentsFormatStyle.Style,
|
||||||
|
fields: Set<Date.ComponentsFormatStyle.Field>
|
||||||
|
) -> TimeIntervalFormatStyle {
|
||||||
|
TimeIntervalFormatStyle(style: style, fields: fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TimeIntervalFormatStyle: FormatStyle {
|
||||||
|
|
||||||
|
let style: Date.ComponentsFormatStyle.Style
|
||||||
|
let fields: Set<Date.ComponentsFormatStyle.Field>
|
||||||
|
|
||||||
|
func format(_ value: TimeInterval) -> String {
|
||||||
|
let value = abs(value)
|
||||||
|
let t = Date.now
|
||||||
|
|
||||||
|
return Date.ComponentsFormatStyle(
|
||||||
|
style: style,
|
||||||
|
locale: .current,
|
||||||
|
calendar: .current,
|
||||||
|
fields: fields
|
||||||
|
).format(t ..< t.addingTimeInterval(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -230,6 +230,8 @@ internal enum L10n {
|
||||||
internal static let dashboard = L10n.tr("Localizable", "dashboard", fallback: "Dashboard")
|
internal static let dashboard = L10n.tr("Localizable", "dashboard", fallback: "Dashboard")
|
||||||
/// Perform administrative tasks for your Jellyfin server.
|
/// Perform administrative tasks for your Jellyfin server.
|
||||||
internal static let dashboardDescription = L10n.tr("Localizable", "dashboardDescription", fallback: "Perform administrative tasks for your Jellyfin server.")
|
internal static let dashboardDescription = L10n.tr("Localizable", "dashboardDescription", fallback: "Perform administrative tasks for your Jellyfin server.")
|
||||||
|
/// Days
|
||||||
|
internal static let days = L10n.tr("Localizable", "days", fallback: "Days")
|
||||||
/// Default Scheme
|
/// Default Scheme
|
||||||
internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme")
|
internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme")
|
||||||
/// Delete
|
/// Delete
|
||||||
|
@ -434,8 +436,8 @@ internal enum L10n {
|
||||||
internal static let nextUp = L10n.tr("Localizable", "nextUp", fallback: "Next Up")
|
internal static let nextUp = L10n.tr("Localizable", "nextUp", fallback: "Next Up")
|
||||||
/// Days in Next Up
|
/// Days in Next Up
|
||||||
internal static let nextUpDays = L10n.tr("Localizable", "nextUpDays", fallback: "Days in Next Up")
|
internal static let nextUpDays = L10n.tr("Localizable", "nextUpDays", fallback: "Days in Next Up")
|
||||||
/// Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.
|
/// Set the maximum amount of days a show should stay in the 'Next Up' list without watching it. Set the value to 0 to disable.
|
||||||
internal static let nextUpDaysDescription = L10n.tr("Localizable", "nextUpDaysDescription", fallback: "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.")
|
internal static let nextUpDaysDescription = L10n.tr("Localizable", "nextUpDaysDescription", fallback: "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it. Set the value to 0 to disable.")
|
||||||
/// Rewatching in Next Up
|
/// Rewatching in Next Up
|
||||||
internal static let nextUpRewatch = L10n.tr("Localizable", "nextUpRewatch", fallback: "Rewatching in Next Up")
|
internal static let nextUpRewatch = L10n.tr("Localizable", "nextUpRewatch", fallback: "Rewatching in Next Up")
|
||||||
/// No Cast devices found..
|
/// No Cast devices found..
|
||||||
|
@ -638,6 +640,8 @@ internal enum L10n {
|
||||||
internal static let running = L10n.tr("Localizable", "running", fallback: "Running...")
|
internal static let running = L10n.tr("Localizable", "running", fallback: "Running...")
|
||||||
/// Runtime
|
/// Runtime
|
||||||
internal static let runtime = L10n.tr("Localizable", "runtime", fallback: "Runtime")
|
internal static let runtime = L10n.tr("Localizable", "runtime", fallback: "Runtime")
|
||||||
|
/// Save
|
||||||
|
internal static let save = L10n.tr("Localizable", "save", fallback: "Save")
|
||||||
/// Scan All Libraries
|
/// Scan All Libraries
|
||||||
internal static let scanAllLibraries = L10n.tr("Localizable", "scanAllLibraries", fallback: "Scan All Libraries")
|
internal static let scanAllLibraries = L10n.tr("Localizable", "scanAllLibraries", fallback: "Scan All Libraries")
|
||||||
/// Scheduled Tasks
|
/// Scheduled Tasks
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
//
|
|
||||||
// Swiftfin is subject to the terms of the Mozilla Public
|
|
||||||
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ChevronButton: View {
|
|
||||||
|
|
||||||
private let isExternal: Bool
|
|
||||||
private let title: Text
|
|
||||||
private let subtitle: Text?
|
|
||||||
private var leadingView: () -> any View
|
|
||||||
private var onSelect: () -> Void
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Button {
|
|
||||||
onSelect()
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
|
|
||||||
leadingView()
|
|
||||||
.eraseToAnyView()
|
|
||||||
|
|
||||||
title
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if let subtitle {
|
|
||||||
subtitle
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
|
|
||||||
Image(systemName: isExternal ? "arrow.up.forward" : "chevron.right")
|
|
||||||
.font(.body.weight(.regular))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ChevronButton {
|
|
||||||
|
|
||||||
init(
|
|
||||||
_ title: String,
|
|
||||||
subtitle: String? = nil,
|
|
||||||
external: Bool = false
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
isExternal: external,
|
|
||||||
title: Text(title),
|
|
||||||
subtitle: {
|
|
||||||
if let subtitle {
|
|
||||||
Text(subtitle)
|
|
||||||
} else {
|
|
||||||
nil
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
leadingView: { EmptyView() },
|
|
||||||
onSelect: {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(_ title: String, external: Bool = false, subtitle: @autoclosure () -> Text) {
|
|
||||||
self.init(
|
|
||||||
isExternal: external,
|
|
||||||
title: Text(title),
|
|
||||||
subtitle: subtitle(),
|
|
||||||
leadingView: { EmptyView() },
|
|
||||||
onSelect: {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func leadingView(@ViewBuilder _ content: @escaping () -> any View) -> Self {
|
|
||||||
copy(modifying: \.leadingView, with: content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func onSelect(_ action: @escaping () -> Void) -> Self {
|
|
||||||
copy(modifying: \.onSelect, with: action)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,6 +10,7 @@ import Defaults
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
extension CustomizeViewsSettings {
|
extension CustomizeViewsSettings {
|
||||||
|
|
||||||
struct HomeSection: View {
|
struct HomeSection: View {
|
||||||
|
|
||||||
@Default(.Customization.Home.showRecentlyAdded)
|
@Default(.Customization.Home.showRecentlyAdded)
|
||||||
|
@ -29,34 +30,23 @@ extension CustomizeViewsSettings {
|
||||||
|
|
||||||
Toggle(L10n.nextUpRewatch, isOn: $resumeNextUp)
|
Toggle(L10n.nextUpRewatch, isOn: $resumeNextUp)
|
||||||
|
|
||||||
ChevronButton(
|
ChevronAlertButton(
|
||||||
L10n.nextUpDays,
|
L10n.nextUpDays,
|
||||||
subtitle: {
|
subtitle: {
|
||||||
if maxNextUp > 0 {
|
if maxNextUp > 0 {
|
||||||
return Text(
|
return Text(maxNextUp, format: .interval(style: .narrow, fields: [.day]))
|
||||||
Date.now.addingTimeInterval(-maxNextUp) ..< Date.now,
|
|
||||||
format: .components(style: .narrow, fields: [.year, .month, .week, .day])
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return Text(L10n.disabled)
|
return Text(L10n.disabled)
|
||||||
}
|
}
|
||||||
}()
|
}(),
|
||||||
)
|
description: L10n.nextUpDaysDescription
|
||||||
.onSelect {
|
) {
|
||||||
isPresentingNextUpDays = true
|
|
||||||
}
|
|
||||||
.alert(L10n.nextUpDays, isPresented: $isPresentingNextUpDays) {
|
|
||||||
|
|
||||||
// TODO: Validate whether this says Done or a Number
|
|
||||||
TextField(
|
TextField(
|
||||||
L10n.nextUpDays,
|
L10n.days,
|
||||||
value: $maxNextUp,
|
value: $maxNextUp,
|
||||||
format: .dayInterval(range: 0 ... 1000)
|
format: .dayInterval(range: 0 ... 1000)
|
||||||
)
|
)
|
||||||
.keyboardType(.numberPad)
|
.keyboardType(.numberPad)
|
||||||
|
|
||||||
} message: {
|
|
||||||
L10n.nextUpDaysDescription.text
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
4E2AC4D42C6C4C1200DD600D /* OrderedSectionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AC4D32C6C4C1200DD600D /* OrderedSectionSelectorView.swift */; };
|
4E2AC4D42C6C4C1200DD600D /* OrderedSectionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AC4D32C6C4C1200DD600D /* OrderedSectionSelectorView.swift */; };
|
||||||
4E2AC4D62C6C4CDC00DD600D /* PlaybackQualitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AC4D52C6C4CDC00DD600D /* PlaybackQualitySettingsView.swift */; };
|
4E2AC4D62C6C4CDC00DD600D /* PlaybackQualitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AC4D52C6C4CDC00DD600D /* PlaybackQualitySettingsView.swift */; };
|
||||||
4E2AC4D92C6C4D9400DD600D /* PlaybackQualitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AC4D72C6C4D8D00DD600D /* PlaybackQualitySettingsView.swift */; };
|
4E2AC4D92C6C4D9400DD600D /* PlaybackQualitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AC4D72C6C4D8D00DD600D /* PlaybackQualitySettingsView.swift */; };
|
||||||
|
4E4A53222CBE0A1C003BD24D /* ChevronAlertButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */; };
|
||||||
4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; };
|
4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; };
|
||||||
4E63B9FA2C8A5BEF00C25378 /* UserDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9F42C8A5BEF00C25378 /* UserDashboardView.swift */; };
|
4E63B9FA2C8A5BEF00C25378 /* UserDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9F42C8A5BEF00C25378 /* UserDashboardView.swift */; };
|
||||||
4E63B9FC2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9FB2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift */; };
|
4E63B9FC2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9FB2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift */; };
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
4EB1A8CA2C9A766200F43898 /* ActiveSessionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB1A8C92C9A765800F43898 /* ActiveSessionsView.swift */; };
|
4EB1A8CA2C9A766200F43898 /* ActiveSessionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB1A8C92C9A765800F43898 /* ActiveSessionsView.swift */; };
|
||||||
4EB1A8CC2C9B1BA200F43898 /* ServerTaskButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB1A8CB2C9B1B9700F43898 /* ServerTaskButton.swift */; };
|
4EB1A8CC2C9B1BA200F43898 /* ServerTaskButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB1A8CB2C9B1B9700F43898 /* ServerTaskButton.swift */; };
|
||||||
4EB1A8CE2C9B2D0800F43898 /* ActiveSessionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB1A8CD2C9B2D0100F43898 /* ActiveSessionRow.swift */; };
|
4EB1A8CE2C9B2D0800F43898 /* ActiveSessionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB1A8CD2C9B2D0100F43898 /* ActiveSessionRow.swift */; };
|
||||||
|
4EB7B33B2CBDE645004A342E /* ChevronAlertButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */; };
|
||||||
4EBE06462C7E9509004A6C03 /* PlaybackCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */; };
|
4EBE06462C7E9509004A6C03 /* PlaybackCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */; };
|
||||||
4EBE06472C7E9509004A6C03 /* PlaybackCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */; };
|
4EBE06472C7E9509004A6C03 /* PlaybackCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */; };
|
||||||
4EBE064D2C7EB6D3004A6C03 /* VideoPlayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE064C2C7EB6D3004A6C03 /* VideoPlayerType.swift */; };
|
4EBE064D2C7EB6D3004A6C03 /* VideoPlayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE064C2C7EB6D3004A6C03 /* VideoPlayerType.swift */; };
|
||||||
|
@ -389,7 +391,6 @@
|
||||||
E12CC1CB28D1333400678D5D /* CinematicResumeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12CC1CA28D1333400678D5D /* CinematicResumeItemView.swift */; };
|
E12CC1CB28D1333400678D5D /* CinematicResumeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12CC1CA28D1333400678D5D /* CinematicResumeItemView.swift */; };
|
||||||
E12CC1CD28D135C700678D5D /* NextUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12CC1CC28D135C700678D5D /* NextUpView.swift */; };
|
E12CC1CD28D135C700678D5D /* NextUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12CC1CC28D135C700678D5D /* NextUpView.swift */; };
|
||||||
E12E30F1296383810022FAC9 /* SplitFormWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12E30F0296383810022FAC9 /* SplitFormWindowView.swift */; };
|
E12E30F1296383810022FAC9 /* SplitFormWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12E30F0296383810022FAC9 /* SplitFormWindowView.swift */; };
|
||||||
E12E30F329638B140022FAC9 /* ChevronButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12E30F229638B140022FAC9 /* ChevronButton.swift */; };
|
|
||||||
E12E30F5296392EC0022FAC9 /* EnumPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12E30F4296392EC0022FAC9 /* EnumPickerView.swift */; };
|
E12E30F5296392EC0022FAC9 /* EnumPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12E30F4296392EC0022FAC9 /* EnumPickerView.swift */; };
|
||||||
E12F038C28F8B0B100976CC3 /* EdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12F038B28F8B0B100976CC3 /* EdgeInsets.swift */; };
|
E12F038C28F8B0B100976CC3 /* EdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12F038B28F8B0B100976CC3 /* EdgeInsets.swift */; };
|
||||||
E132D3C82BD200C10058A2DF /* CollectionVGrid in Frameworks */ = {isa = PBXBuildFile; productRef = E132D3C72BD200C10058A2DF /* CollectionVGrid */; };
|
E132D3C82BD200C10058A2DF /* CollectionVGrid in Frameworks */ = {isa = PBXBuildFile; productRef = E132D3C72BD200C10058A2DF /* CollectionVGrid */; };
|
||||||
|
@ -641,6 +642,7 @@
|
||||||
E17FB55B28C1266400311DFE /* GenresHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17FB55A28C1266400311DFE /* GenresHStack.swift */; };
|
E17FB55B28C1266400311DFE /* GenresHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17FB55A28C1266400311DFE /* GenresHStack.swift */; };
|
||||||
E1803EA12BFBD6CF0039F90E /* Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1803EA02BFBD6CF0039F90E /* Hashable.swift */; };
|
E1803EA12BFBD6CF0039F90E /* Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1803EA02BFBD6CF0039F90E /* Hashable.swift */; };
|
||||||
E1803EA22BFBD6CF0039F90E /* Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1803EA02BFBD6CF0039F90E /* Hashable.swift */; };
|
E1803EA22BFBD6CF0039F90E /* Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1803EA02BFBD6CF0039F90E /* Hashable.swift */; };
|
||||||
|
E18121062CBE428000682985 /* ChevronButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A1528728FD229500600579 /* ChevronButton.swift */; };
|
||||||
E18295E429CAC6F100F91ED0 /* BasicNavigationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E154967D296CCB6C00C4EF88 /* BasicNavigationCoordinator.swift */; };
|
E18295E429CAC6F100F91ED0 /* BasicNavigationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E154967D296CCB6C00C4EF88 /* BasicNavigationCoordinator.swift */; };
|
||||||
E18443CB2A037773002DDDC8 /* UDPBroadcast in Frameworks */ = {isa = PBXBuildFile; productRef = E18443CA2A037773002DDDC8 /* UDPBroadcast */; };
|
E18443CB2A037773002DDDC8 /* UDPBroadcast in Frameworks */ = {isa = PBXBuildFile; productRef = E18443CA2A037773002DDDC8 /* UDPBroadcast */; };
|
||||||
E185920628CDAA6400326F80 /* CastAndCrewHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E185920528CDAA6400326F80 /* CastAndCrewHStack.swift */; };
|
E185920628CDAA6400326F80 /* CastAndCrewHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E185920528CDAA6400326F80 /* CastAndCrewHStack.swift */; };
|
||||||
|
@ -1053,6 +1055,7 @@
|
||||||
4EB1A8C92C9A765800F43898 /* ActiveSessionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionsView.swift; sourceTree = "<group>"; };
|
4EB1A8C92C9A765800F43898 /* ActiveSessionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionsView.swift; sourceTree = "<group>"; };
|
||||||
4EB1A8CB2C9B1B9700F43898 /* ServerTaskButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTaskButton.swift; sourceTree = "<group>"; };
|
4EB1A8CB2C9B1B9700F43898 /* ServerTaskButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTaskButton.swift; sourceTree = "<group>"; };
|
||||||
4EB1A8CD2C9B2D0100F43898 /* ActiveSessionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionRow.swift; sourceTree = "<group>"; };
|
4EB1A8CD2C9B2D0100F43898 /* ActiveSessionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionRow.swift; sourceTree = "<group>"; };
|
||||||
|
4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChevronAlertButton.swift; sourceTree = "<group>"; };
|
||||||
4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackCompatibility.swift; sourceTree = "<group>"; };
|
4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackCompatibility.swift; sourceTree = "<group>"; };
|
||||||
4EBE064C2C7EB6D3004A6C03 /* VideoPlayerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerType.swift; sourceTree = "<group>"; };
|
4EBE064C2C7EB6D3004A6C03 /* VideoPlayerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerType.swift; sourceTree = "<group>"; };
|
||||||
4EBE06502C7ED0E1004A6C03 /* DeviceProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceProfile.swift; sourceTree = "<group>"; };
|
4EBE06502C7ED0E1004A6C03 /* DeviceProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceProfile.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1307,7 +1310,6 @@
|
||||||
E12CC1CA28D1333400678D5D /* CinematicResumeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicResumeItemView.swift; sourceTree = "<group>"; };
|
E12CC1CA28D1333400678D5D /* CinematicResumeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicResumeItemView.swift; sourceTree = "<group>"; };
|
||||||
E12CC1CC28D135C700678D5D /* NextUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextUpView.swift; sourceTree = "<group>"; };
|
E12CC1CC28D135C700678D5D /* NextUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextUpView.swift; sourceTree = "<group>"; };
|
||||||
E12E30F0296383810022FAC9 /* SplitFormWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitFormWindowView.swift; sourceTree = "<group>"; };
|
E12E30F0296383810022FAC9 /* SplitFormWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitFormWindowView.swift; sourceTree = "<group>"; };
|
||||||
E12E30F229638B140022FAC9 /* ChevronButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChevronButton.swift; sourceTree = "<group>"; };
|
|
||||||
E12E30F4296392EC0022FAC9 /* EnumPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumPickerView.swift; sourceTree = "<group>"; };
|
E12E30F4296392EC0022FAC9 /* EnumPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumPickerView.swift; sourceTree = "<group>"; };
|
||||||
E12F038B28F8B0B100976CC3 /* EdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EdgeInsets.swift; sourceTree = "<group>"; };
|
E12F038B28F8B0B100976CC3 /* EdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EdgeInsets.swift; sourceTree = "<group>"; };
|
||||||
E13316FD2ADE42B6009BF865 /* OnSizeChangedModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnSizeChangedModifier.swift; sourceTree = "<group>"; };
|
E13316FD2ADE42B6009BF865 /* OnSizeChangedModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnSizeChangedModifier.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2195,7 +2197,6 @@
|
||||||
536D3D77267BB9650004248C /* Components */ = {
|
536D3D77267BB9650004248C /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E12E30F229638B140022FAC9 /* ChevronButton.swift */,
|
|
||||||
E1DC9818296DD1CD00982F06 /* CinematicBackgroundView.swift */,
|
E1DC9818296DD1CD00982F06 /* CinematicBackgroundView.swift */,
|
||||||
E1A42E4928CA6CCD00A14DCB /* CinematicItemSelector.swift */,
|
E1A42E4928CA6CCD00A14DCB /* CinematicItemSelector.swift */,
|
||||||
E1C92618288756BD002A7A66 /* DotHStack.swift */,
|
E1C92618288756BD002A7A66 /* DotHStack.swift */,
|
||||||
|
@ -2430,7 +2431,6 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E1D8429429346C6400D1041A /* BasicStepper.swift */,
|
E1D8429429346C6400D1041A /* BasicStepper.swift */,
|
||||||
E1A1528728FD229500600579 /* ChevronButton.swift */,
|
|
||||||
E133328C2953AE4B00EE76AB /* CircularProgressView.swift */,
|
E133328C2953AE4B00EE76AB /* CircularProgressView.swift */,
|
||||||
E1A3E4CE2BB7E02B005C59F8 /* DelayedProgressView.swift */,
|
E1A3E4CE2BB7E02B005C59F8 /* DelayedProgressView.swift */,
|
||||||
E18E01A7288746AF0022598C /* DotHStack.swift */,
|
E18E01A7288746AF0022598C /* DotHStack.swift */,
|
||||||
|
@ -3594,6 +3594,8 @@
|
||||||
E104DC952B9E7E29008F506D /* AssertionFailureView.swift */,
|
E104DC952B9E7E29008F506D /* AssertionFailureView.swift */,
|
||||||
E18E0203288749200022598C /* BlurView.swift */,
|
E18E0203288749200022598C /* BlurView.swift */,
|
||||||
E145EB212BDCCA43003BF6F3 /* BulletedList.swift */,
|
E145EB212BDCCA43003BF6F3 /* BulletedList.swift */,
|
||||||
|
4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */,
|
||||||
|
E1A1528728FD229500600579 /* ChevronButton.swift */,
|
||||||
E1153DCB2BBB633B00424D36 /* FastSVGView.swift */,
|
E1153DCB2BBB633B00424D36 /* FastSVGView.swift */,
|
||||||
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
||||||
4E16FD562C01A32700110147 /* LetterPickerOrientation.swift */,
|
4E16FD562C01A32700110147 /* LetterPickerOrientation.swift */,
|
||||||
|
@ -4338,6 +4340,7 @@
|
||||||
E1575E74293E77B5001665B1 /* PanDirectionGestureRecognizer.swift in Sources */,
|
E1575E74293E77B5001665B1 /* PanDirectionGestureRecognizer.swift in Sources */,
|
||||||
E1575E85293E7A00001665B1 /* DarkAppIcon.swift in Sources */,
|
E1575E85293E7A00001665B1 /* DarkAppIcon.swift in Sources */,
|
||||||
E1763A762BF3FF01004DF6AB /* AppLoadingView.swift in Sources */,
|
E1763A762BF3FF01004DF6AB /* AppLoadingView.swift in Sources */,
|
||||||
|
E18121062CBE428000682985 /* ChevronButton.swift in Sources */,
|
||||||
E102315A2BCF8AF8009D71FC /* ProgramButtonContent.swift in Sources */,
|
E102315A2BCF8AF8009D71FC /* ProgramButtonContent.swift in Sources */,
|
||||||
E17639F82BF2E25B004DF6AB /* Keychain.swift in Sources */,
|
E17639F82BF2E25B004DF6AB /* Keychain.swift in Sources */,
|
||||||
C45C36552A8B1F2C003DAE46 /* LiveVideoPlayerManager.swift in Sources */,
|
C45C36552A8B1F2C003DAE46 /* LiveVideoPlayerManager.swift in Sources */,
|
||||||
|
@ -4381,7 +4384,6 @@
|
||||||
4E9A24E92C82B79D0023DA83 /* EditCustomDeviceProfileCoordinator.swift in Sources */,
|
4E9A24E92C82B79D0023DA83 /* EditCustomDeviceProfileCoordinator.swift in Sources */,
|
||||||
E1C9261C288756BD002A7A66 /* PosterHStack.swift in Sources */,
|
E1C9261C288756BD002A7A66 /* PosterHStack.swift in Sources */,
|
||||||
E1722DB229491C3900CC0239 /* ImageBlurHashes.swift in Sources */,
|
E1722DB229491C3900CC0239 /* ImageBlurHashes.swift in Sources */,
|
||||||
E12E30F329638B140022FAC9 /* ChevronButton.swift in Sources */,
|
|
||||||
4E9A24ED2C82BAFB0023DA83 /* EditCustomDeviceProfileView.swift in Sources */,
|
4E9A24ED2C82BAFB0023DA83 /* EditCustomDeviceProfileView.swift in Sources */,
|
||||||
E12CC1BC28D11E1000678D5D /* RecentlyAddedViewModel.swift in Sources */,
|
E12CC1BC28D11E1000678D5D /* RecentlyAddedViewModel.swift in Sources */,
|
||||||
E1575E9E293E7B1E001665B1 /* Equatable.swift in Sources */,
|
E1575E9E293E7B1E001665B1 /* Equatable.swift in Sources */,
|
||||||
|
@ -4468,6 +4470,7 @@
|
||||||
E1763A722BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift in Sources */,
|
E1763A722BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift in Sources */,
|
||||||
E1937A3C288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */,
|
E1937A3C288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */,
|
||||||
E18A17F0298C68B700C22F62 /* Overlay.swift in Sources */,
|
E18A17F0298C68B700C22F62 /* Overlay.swift in Sources */,
|
||||||
|
4E4A53222CBE0A1C003BD24D /* ChevronAlertButton.swift in Sources */,
|
||||||
E1A42E4A28CA6CCD00A14DCB /* CinematicItemSelector.swift in Sources */,
|
E1A42E4A28CA6CCD00A14DCB /* CinematicItemSelector.swift in Sources */,
|
||||||
4E2AC4CF2C6C4A0600DD600D /* PlaybackQualitySettingsCoordinator.swift in Sources */,
|
4E2AC4CF2C6C4A0600DD600D /* PlaybackQualitySettingsCoordinator.swift in Sources */,
|
||||||
E1D37F492B9C648E00343D2B /* MaxHeightText.swift in Sources */,
|
E1D37F492B9C648E00343D2B /* MaxHeightText.swift in Sources */,
|
||||||
|
@ -5044,6 +5047,7 @@
|
||||||
E1721FAE28FB801C00762992 /* SmallPlaybackButtons.swift in Sources */,
|
E1721FAE28FB801C00762992 /* SmallPlaybackButtons.swift in Sources */,
|
||||||
E1A7B1662B9ADAD300152546 /* ItemTypeLibraryViewModel.swift in Sources */,
|
E1A7B1662B9ADAD300152546 /* ItemTypeLibraryViewModel.swift in Sources */,
|
||||||
E1ED7FD62CA8A7FD00ACB6E3 /* EditScheduledTaskView.swift in Sources */,
|
E1ED7FD62CA8A7FD00ACB6E3 /* EditScheduledTaskView.swift in Sources */,
|
||||||
|
4EB7B33B2CBDE645004A342E /* ChevronAlertButton.swift in Sources */,
|
||||||
E1545BD82BDC55C300D9578F /* ResetUserPasswordView.swift in Sources */,
|
E1545BD82BDC55C300D9578F /* ResetUserPasswordView.swift in Sources */,
|
||||||
E1E750682A33E9B400B2C1EE /* OverviewCard.swift in Sources */,
|
E1E750682A33E9B400B2C1EE /* OverviewCard.swift in Sources */,
|
||||||
E1CCF13128AC07EC006CAC9E /* PosterHStack.swift in Sources */,
|
E1CCF13128AC07EC006CAC9E /* PosterHStack.swift in Sources */,
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
//
|
|
||||||
// Swiftfin is subject to the terms of the Mozilla Public
|
|
||||||
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ChevronButton: View {
|
|
||||||
|
|
||||||
private let isExternal: Bool
|
|
||||||
private let title: Text
|
|
||||||
private let subtitle: Text?
|
|
||||||
private var leadingView: () -> any View
|
|
||||||
private var onSelect: () -> Void
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Button {
|
|
||||||
onSelect()
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
|
|
||||||
leadingView()
|
|
||||||
.eraseToAnyView()
|
|
||||||
|
|
||||||
title
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if let subtitle {
|
|
||||||
subtitle
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
|
|
||||||
Image(systemName: isExternal ? "arrow.up.forward" : "chevron.right")
|
|
||||||
.font(.body.weight(.regular))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ChevronButton {
|
|
||||||
|
|
||||||
init(
|
|
||||||
_ title: String,
|
|
||||||
subtitle: String? = nil,
|
|
||||||
external: Bool = false
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
isExternal: external,
|
|
||||||
title: Text(title),
|
|
||||||
subtitle: {
|
|
||||||
if let subtitle {
|
|
||||||
Text(subtitle)
|
|
||||||
} else {
|
|
||||||
nil
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
leadingView: { EmptyView() },
|
|
||||||
onSelect: {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(_ title: String, external: Bool = false, subtitle: @autoclosure () -> Text) {
|
|
||||||
self.init(
|
|
||||||
isExternal: external,
|
|
||||||
title: Text(title),
|
|
||||||
subtitle: subtitle(),
|
|
||||||
leadingView: { EmptyView() },
|
|
||||||
onSelect: {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func leadingView(@ViewBuilder _ content: @escaping () -> any View) -> Self {
|
|
||||||
copy(modifying: \.leadingView, with: content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func onSelect(_ action: @escaping () -> Void) -> Self {
|
|
||||||
copy(modifying: \.onSelect, with: action)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,44 +38,34 @@ struct AboutAppView: View {
|
||||||
trailing: "\(UIApplication.appVersion ?? .emptyDash) (\(UIApplication.bundleVersion ?? .emptyDash))"
|
trailing: "\(UIApplication.appVersion ?? .emptyDash) (\(UIApplication.bundleVersion ?? .emptyDash))"
|
||||||
)
|
)
|
||||||
|
|
||||||
ChevronButton(L10n.sourceCode, external: true)
|
ChevronButton(
|
||||||
.leadingView {
|
L10n.sourceCode,
|
||||||
Image(.logoGithub)
|
image: Image(.logoGithub),
|
||||||
.resizable()
|
external: true
|
||||||
.aspectRatio(1, contentMode: .fit)
|
)
|
||||||
.frame(width: 24, height: 24)
|
.onSelect {
|
||||||
.foregroundColor(.primary)
|
UIApplication.shared.open(.swiftfinGithub)
|
||||||
}
|
}
|
||||||
.onSelect {
|
|
||||||
UIApplication.shared.open(.swiftfinGithub)
|
|
||||||
}
|
|
||||||
|
|
||||||
ChevronButton(L10n.bugsAndFeatures, external: true)
|
ChevronButton(
|
||||||
.leadingView {
|
L10n.bugsAndFeatures,
|
||||||
Image(systemName: "plus.circle.fill")
|
systemName: "plus.circle.fill",
|
||||||
.resizable()
|
external: true
|
||||||
.backport
|
)
|
||||||
.fontWeight(.bold)
|
.onSelect {
|
||||||
.aspectRatio(1, contentMode: .fit)
|
UIApplication.shared.open(.swiftfinGithubIssues)
|
||||||
.frame(width: 24, height: 24)
|
}
|
||||||
.foregroundColor(.primary)
|
.symbolRenderingMode(.monochrome)
|
||||||
}
|
|
||||||
.onSelect {
|
|
||||||
UIApplication.shared.open(.swiftfinGithubIssues)
|
|
||||||
}
|
|
||||||
|
|
||||||
ChevronButton(L10n.settings, external: true)
|
ChevronButton(
|
||||||
.leadingView {
|
L10n.settings,
|
||||||
Image(systemName: "gearshape.fill")
|
systemName: "gearshape.fill",
|
||||||
.resizable()
|
external: true
|
||||||
.aspectRatio(1, contentMode: .fit)
|
)
|
||||||
.frame(width: 24, height: 24)
|
.onSelect {
|
||||||
.foregroundColor(.primary)
|
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
|
||||||
}
|
UIApplication.shared.open(url)
|
||||||
.onSelect {
|
}
|
||||||
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Defaults
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
extension CustomizeViewsSettings {
|
extension CustomizeViewsSettings {
|
||||||
|
|
||||||
struct HomeSection: View {
|
struct HomeSection: View {
|
||||||
|
|
||||||
@Default(.Customization.Home.showRecentlyAdded)
|
@Default(.Customization.Home.showRecentlyAdded)
|
||||||
|
@ -19,9 +20,6 @@ extension CustomizeViewsSettings {
|
||||||
@Default(.Customization.Home.resumeNextUp)
|
@Default(.Customization.Home.resumeNextUp)
|
||||||
private var resumeNextUp
|
private var resumeNextUp
|
||||||
|
|
||||||
@State
|
|
||||||
private var isPresentingNextUpDays = false
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(L10n.home) {
|
Section(L10n.home) {
|
||||||
|
|
||||||
|
@ -29,33 +27,23 @@ extension CustomizeViewsSettings {
|
||||||
|
|
||||||
Toggle(L10n.nextUpRewatch, isOn: $resumeNextUp)
|
Toggle(L10n.nextUpRewatch, isOn: $resumeNextUp)
|
||||||
|
|
||||||
ChevronButton(
|
ChevronAlertButton(
|
||||||
L10n.nextUpDays,
|
L10n.nextUpDays,
|
||||||
subtitle: {
|
subtitle: {
|
||||||
if maxNextUp > 0 {
|
if maxNextUp > 0 {
|
||||||
return Text(
|
return Text(maxNextUp, format: .interval(style: .narrow, fields: [.day]))
|
||||||
Date.now.addingTimeInterval(-maxNextUp) ..< Date.now,
|
|
||||||
format: .components(style: .narrow, fields: [.year, .month, .week, .day])
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return Text(L10n.disabled)
|
return Text(L10n.disabled)
|
||||||
}
|
}
|
||||||
}()
|
}(),
|
||||||
)
|
description: L10n.nextUpDaysDescription
|
||||||
.onSelect {
|
) {
|
||||||
isPresentingNextUpDays = true
|
|
||||||
}
|
|
||||||
.alert(L10n.nextUpDays, isPresented: $isPresentingNextUpDays) {
|
|
||||||
|
|
||||||
TextField(
|
TextField(
|
||||||
L10n.nextUpDays,
|
L10n.days,
|
||||||
value: $maxNextUp,
|
value: $maxNextUp,
|
||||||
format: .dayInterval(range: 0 ... 1000)
|
format: .dayInterval(range: 0 ... 1000)
|
||||||
)
|
)
|
||||||
.keyboardType(.numberPad)
|
.keyboardType(.numberPad)
|
||||||
|
|
||||||
} message: {
|
|
||||||
L10n.nextUpDaysDescription.text
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue