cleanup (#1484)
This commit is contained in:
parent
84fd2e82a5
commit
cfc0105dc7
|
@ -1,96 +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) 2025 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 {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -8,154 +8,374 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
struct ChevronButton<Icon: View>: View {
|
||||
struct ChevronButton<Icon: View, Subtitle: View>: View {
|
||||
|
||||
private let icon: Icon
|
||||
private let isExternal: Bool
|
||||
private let title: Text
|
||||
private let subtitle: Text?
|
||||
private var onSelect: () -> Void
|
||||
private let subtitle: Subtitle
|
||||
|
||||
private let innerContent: (AnyView) -> any View
|
||||
|
||||
@ViewBuilder
|
||||
private var label: some View {
|
||||
HStack {
|
||||
|
||||
icon
|
||||
.font(.body.weight(.bold))
|
||||
|
||||
title
|
||||
|
||||
Spacer()
|
||||
|
||||
subtitle
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Image(systemName: isExternal ? "arrow.up.forward" : "chevron.right")
|
||||
.font(.body.weight(.regular))
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
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: {}
|
||||
)
|
||||
innerContent(label.eraseToAnyView())
|
||||
.eraseToAnyView()
|
||||
}
|
||||
}
|
||||
|
||||
extension ChevronButton {
|
||||
|
||||
func onSelect(perform action: @escaping () -> Void) -> Self {
|
||||
copy(modifying: \.onSelect, with: action)
|
||||
private struct AlertContentView<Content: View, Label: View>: View {
|
||||
|
||||
@State
|
||||
private var isPresented: Bool = false
|
||||
|
||||
let alertTitle: String
|
||||
let content: () -> Content
|
||||
let description: String?
|
||||
let label: Label
|
||||
let onCancel: (() -> Void)?
|
||||
let onSave: (() -> Void)?
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
isPresented = true
|
||||
} label: {
|
||||
label
|
||||
}
|
||||
.foregroundStyle(.primary, .secondary)
|
||||
.alert(alertTitle, isPresented: $isPresented) {
|
||||
|
||||
content()
|
||||
|
||||
if let onSave {
|
||||
Button(L10n.save) {
|
||||
onSave()
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
|
||||
if let onCancel {
|
||||
Button(L10n.cancel, role: .cancel) {
|
||||
onCancel()
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
} message: {
|
||||
if let description {
|
||||
Text(description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ButtonContentView<Label: View>: View {
|
||||
|
||||
let label: Label
|
||||
let action: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
label
|
||||
}
|
||||
.foregroundStyle(.primary, .secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChevronButton {
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void,
|
||||
@ViewBuilder icon: @escaping () -> Icon,
|
||||
@ViewBuilder subtitle: @escaping () -> Subtitle
|
||||
) {
|
||||
self.icon = icon()
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = subtitle()
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init(
|
||||
_ title: Text,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void,
|
||||
@ViewBuilder icon: @escaping () -> Icon,
|
||||
@ViewBuilder subtitle: @escaping () -> Subtitle
|
||||
) {
|
||||
self.icon = icon()
|
||||
self.isExternal = external
|
||||
self.title = title
|
||||
self.subtitle = subtitle()
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChevronButton where Icon == EmptyView, Subtitle == Text {
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
subtitle: String,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = EmptyView()
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = Text(subtitle)
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
subtitle: Text,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = EmptyView()
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = subtitle
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChevronButton where Icon == EmptyView, Subtitle == EmptyView {
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = EmptyView()
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = EmptyView()
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChevronButton where Icon == Image, Subtitle == Text {
|
||||
|
||||
// systemName
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
subtitle: String,
|
||||
systemName: String,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = Image(systemName: systemName)
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = Text(subtitle)
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
subtitle: Text,
|
||||
systemName: String,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = Image(systemName: systemName)
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = subtitle
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ImageResource
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
subtitle: String,
|
||||
image: ImageResource,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = Image(image)
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = Text(subtitle)
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
subtitle: Text,
|
||||
image: ImageResource,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = Image(image)
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = subtitle
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChevronButton where Icon == Image, Subtitle == EmptyView {
|
||||
|
||||
// systemName
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
systemName: String,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = Image(systemName: systemName)
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = EmptyView()
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ImageResource
|
||||
|
||||
init(
|
||||
_ title: String,
|
||||
image: ImageResource,
|
||||
external: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.icon = Image(image)
|
||||
self.isExternal = external
|
||||
self.title = Text(title)
|
||||
self.subtitle = EmptyView()
|
||||
self.innerContent = { label in
|
||||
ButtonContentView(
|
||||
label: label,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChevronButton where Icon == EmptyView, Subtitle == Text {
|
||||
|
||||
init<Content: View>(
|
||||
_ title: String,
|
||||
subtitle: String? = nil,
|
||||
description: String? = nil,
|
||||
@ViewBuilder content: @escaping () -> Content,
|
||||
onSave: (() -> Void)? = nil,
|
||||
onCancel: (() -> Void)? = nil
|
||||
) {
|
||||
self.icon = EmptyView()
|
||||
self.isExternal = false
|
||||
self.title = Text(title)
|
||||
self.subtitle = Text(subtitle ?? "")
|
||||
self.innerContent = { label in
|
||||
AlertContentView(
|
||||
alertTitle: title,
|
||||
content: content,
|
||||
description: description,
|
||||
label: label,
|
||||
onCancel: onCancel,
|
||||
onSave: onSave
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init<Content: View>(
|
||||
_ title: String,
|
||||
subtitle: Text? = nil,
|
||||
description: String? = nil,
|
||||
@ViewBuilder content: @escaping () -> Content,
|
||||
onSave: (() -> Void)? = nil,
|
||||
onCancel: (() -> Void)? = nil
|
||||
) {
|
||||
self.icon = EmptyView()
|
||||
self.isExternal = false
|
||||
self.title = Text(title)
|
||||
self.subtitle = subtitle ?? Text("")
|
||||
self.innerContent = { label in
|
||||
AlertContentView(
|
||||
alertTitle: title,
|
||||
content: content,
|
||||
description: description,
|
||||
label: label,
|
||||
onCancel: onCancel,
|
||||
onSave: onSave
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
extension URL: Identifiable {
|
||||
extension URL: @retroactive Identifiable {
|
||||
|
||||
public var id: String {
|
||||
absoluteString
|
||||
|
|
|
@ -87,10 +87,9 @@ struct AppSettingsView: View {
|
|||
|
||||
SignOutIntervalSection()
|
||||
|
||||
ChevronButton(L10n.logs)
|
||||
.onSelect {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
ChevronButton(L10n.logs) {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
}
|
||||
.navigationTitle(L10n.advanced)
|
||||
}
|
||||
|
|
|
@ -40,8 +40,7 @@ extension AppSettingsView {
|
|||
ChevronButton(
|
||||
L10n.duration,
|
||||
subtitle: Text(backgroundSignOutInterval, format: .hourMinute)
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
router.route(to: \.hourPicker)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ extension CustomizeViewsSettings {
|
|||
|
||||
Toggle(L10n.nextUpRewatch, isOn: $resumeNextUp)
|
||||
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.nextUpDays,
|
||||
subtitle: {
|
||||
if maxNextUp > 0 {
|
||||
|
|
|
@ -35,10 +35,9 @@ extension CustomizeViewsSettings {
|
|||
var body: some View {
|
||||
Section(L10n.items) {
|
||||
|
||||
ChevronButton(L10n.mediaAttributes)
|
||||
.onSelect {
|
||||
router.route(to: \.itemViewAttributes, $itemViewAttributes)
|
||||
}
|
||||
ChevronButton(L10n.mediaAttributes) {
|
||||
router.route(to: \.itemViewAttributes, $itemViewAttributes)
|
||||
}
|
||||
|
||||
ListRowMenu(L10n.enabledTrailers, selection: $enabledTrailers)
|
||||
|
||||
|
|
|
@ -57,8 +57,7 @@ extension CustomizeViewsSettings {
|
|||
ChevronButton(
|
||||
L10n.columns,
|
||||
subtitle: listColumnCount.description
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
router.route(to: \.listColumnSettings, $listColumnCount)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,10 +53,9 @@ struct CustomizeViewsSettings: View {
|
|||
|
||||
Section(L10n.posters) {
|
||||
|
||||
ChevronButton(L10n.indicators)
|
||||
.onSelect {
|
||||
router.route(to: \.indicatorSettings)
|
||||
}
|
||||
ChevronButton(L10n.indicators) {
|
||||
router.route(to: \.indicatorSettings)
|
||||
}
|
||||
|
||||
Toggle(L10n.showPosterLabels, isOn: $showPosterLabels)
|
||||
|
||||
|
|
|
@ -61,10 +61,9 @@ struct PlaybackQualitySettingsView: View {
|
|||
.focused($focusedItem, equals: .compatibility)
|
||||
|
||||
if compatibilityMode == .custom {
|
||||
ChevronButton(L10n.profiles)
|
||||
.onSelect {
|
||||
router.route(to: \.customDeviceProfileSettings)
|
||||
}
|
||||
ChevronButton(L10n.profiles) {
|
||||
router.route(to: \.customDeviceProfileSettings)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
L10n.deviceProfile.text
|
||||
|
|
|
@ -40,8 +40,7 @@ struct SettingsView: View {
|
|||
ChevronButton(
|
||||
L10n.server,
|
||||
subtitle: viewModel.userSession.server.name
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
router.route(to: \.serverDetail, viewModel.userSession.server)
|
||||
}
|
||||
}
|
||||
|
@ -58,23 +57,20 @@ struct SettingsView: View {
|
|||
|
||||
ListRowMenu(L10n.videoPlayerType, selection: $videoPlayerType)
|
||||
|
||||
ChevronButton(L10n.videoPlayer)
|
||||
.onSelect {
|
||||
router.route(to: \.videoPlayerSettings)
|
||||
}
|
||||
ChevronButton(L10n.videoPlayer) {
|
||||
router.route(to: \.videoPlayerSettings)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.playbackQuality)
|
||||
.onSelect {
|
||||
router.route(to: \.playbackQualitySettings)
|
||||
}
|
||||
ChevronButton(L10n.playbackQuality) {
|
||||
router.route(to: \.playbackQualitySettings)
|
||||
}
|
||||
}
|
||||
|
||||
Section(L10n.accessibility) {
|
||||
|
||||
ChevronButton(L10n.customize)
|
||||
.onSelect {
|
||||
router.route(to: \.customizeViewsSettings)
|
||||
}
|
||||
ChevronButton(L10n.customize) {
|
||||
router.route(to: \.customizeViewsSettings)
|
||||
}
|
||||
//
|
||||
// ChevronButton(L10n.experimental)
|
||||
// .onSelect {
|
||||
|
@ -84,10 +80,9 @@ struct SettingsView: View {
|
|||
|
||||
Section {
|
||||
|
||||
ChevronButton(L10n.logs)
|
||||
.onSelect {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
ChevronButton(L10n.logs) {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ struct UserLocalSecurityView: View {
|
|||
|
||||
if signInPolicy == .requirePin {
|
||||
Section {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.hint,
|
||||
subtitle: pinHint,
|
||||
description: L10n.setPinHintDescription
|
||||
|
|
|
@ -53,10 +53,9 @@ struct UserProfileSettingsView: View {
|
|||
// }
|
||||
|
||||
Section {
|
||||
ChevronButton(L10n.security)
|
||||
.onSelect {
|
||||
router.route(to: \.localSecurity)
|
||||
}
|
||||
ChevronButton(L10n.security) {
|
||||
router.route(to: \.localSecurity)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do we want this option on tvOS?
|
||||
|
|
|
@ -47,8 +47,7 @@ struct VideoPlayerSettingsView: View {
|
|||
ChevronButton(
|
||||
L10n.offset,
|
||||
subtitle: resumeOffset.secondLabel
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
isPresentingResumeOffsetStepper = true
|
||||
}
|
||||
} header: {
|
||||
|
@ -59,10 +58,9 @@ struct VideoPlayerSettingsView: View {
|
|||
|
||||
Section {
|
||||
|
||||
ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName)
|
||||
.onSelect {
|
||||
router.route(to: \.fontPicker, $subtitleFontName)
|
||||
}
|
||||
ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName) {
|
||||
router.route(to: \.fontPicker, $subtitleFontName)
|
||||
}
|
||||
} header: {
|
||||
L10n.subtitles.text
|
||||
} footer: {
|
||||
|
|
|
@ -93,7 +93,6 @@
|
|||
4E49DEE32CE55FB900352DCD /* SyncPlayUserAccessType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E49DEE22CE55FB500352DCD /* SyncPlayUserAccessType.swift */; };
|
||||
4E49DEE42CE55FB900352DCD /* SyncPlayUserAccessType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E49DEE22CE55FB500352DCD /* SyncPlayUserAccessType.swift */; };
|
||||
4E49DEE62CE5616800352DCD /* UserProfileImagePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E49DEE52CE5616800352DCD /* UserProfileImagePickerView.swift */; };
|
||||
4E4A53222CBE0A1C003BD24D /* ChevronAlertButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */; };
|
||||
4E4DAC372D11EE5E00E13FF9 /* SplitLoginWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E4DAC362D11EE4F00E13FF9 /* SplitLoginWindowView.swift */; };
|
||||
4E4DAC3D2D11F94400E13FF9 /* LocalServerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E4DAC3C2D11F94000E13FF9 /* LocalServerButton.swift */; };
|
||||
4E4E9C672CFEBF2A00A6946F /* StudioEditorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E4E9C662CFEBF2500A6946F /* StudioEditorViewModel.swift */; };
|
||||
|
@ -203,15 +202,12 @@
|
|||
4EB3F0372D8CD33300EBEDAA /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB3F0362D8CD33100EBEDAA /* ActionButton.swift */; };
|
||||
4EB3F0392D8CD5CF00EBEDAA /* TrailerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB3F0382D8CD5CC00EBEDAA /* TrailerMenu.swift */; };
|
||||
4EB3F03B2D8CD6A900EBEDAA /* VersionMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB3F03A2D8CD6A700EBEDAA /* VersionMenu.swift */; };
|
||||
4EB4ECE32CBEFC4D002FF2FC /* SessionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB4ECE22CBEFC49002FF2FC /* SessionInfo.swift */; };
|
||||
4EB4ECE42CBEFC4D002FF2FC /* SessionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB4ECE22CBEFC49002FF2FC /* SessionInfo.swift */; };
|
||||
4EB538B52CE3C77200EB72D5 /* ServerUserPermissionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB538B42CE3C76D00EB72D5 /* ServerUserPermissionsView.swift */; };
|
||||
4EB538BD2CE3CCD100EB72D5 /* MediaPlaybackSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB538BC2CE3CCCF00EB72D5 /* MediaPlaybackSection.swift */; };
|
||||
4EB538C12CE3CF0F00EB72D5 /* ManagementSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB538C02CE3CF0E00EB72D5 /* ManagementSection.swift */; };
|
||||
4EB538C32CE3E21800EB72D5 /* SyncPlaySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB538C22CE3E21500EB72D5 /* SyncPlaySection.swift */; };
|
||||
4EB538C52CE3E25700EB72D5 /* ExternalAccessSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB538C42CE3E25500EB72D5 /* ExternalAccessSection.swift */; };
|
||||
4EB538C82CE3E8A600EB72D5 /* RemoteControlSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB538C72CE3E8A100EB72D5 /* RemoteControlSection.swift */; };
|
||||
4EB7B33B2CBDE645004A342E /* ChevronAlertButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */; };
|
||||
4EB7C8D52CCED6E7000CC011 /* AddServerUserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB7C8D42CCED6E1000CC011 /* AddServerUserView.swift */; };
|
||||
4EBE06462C7E9509004A6C03 /* PlaybackCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */; };
|
||||
4EBE06472C7E9509004A6C03 /* PlaybackCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EBE06452C7E9509004A6C03 /* PlaybackCompatibility.swift */; };
|
||||
|
@ -1440,14 +1436,12 @@
|
|||
4EB3F0362D8CD33100EBEDAA /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = "<group>"; };
|
||||
4EB3F0382D8CD5CC00EBEDAA /* TrailerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrailerMenu.swift; sourceTree = "<group>"; };
|
||||
4EB3F03A2D8CD6A700EBEDAA /* VersionMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionMenu.swift; sourceTree = "<group>"; };
|
||||
4EB4ECE22CBEFC49002FF2FC /* SessionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionInfo.swift; sourceTree = "<group>"; };
|
||||
4EB538B42CE3C76D00EB72D5 /* ServerUserPermissionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerUserPermissionsView.swift; sourceTree = "<group>"; };
|
||||
4EB538BC2CE3CCCF00EB72D5 /* MediaPlaybackSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlaybackSection.swift; sourceTree = "<group>"; };
|
||||
4EB538C02CE3CF0E00EB72D5 /* ManagementSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagementSection.swift; sourceTree = "<group>"; };
|
||||
4EB538C22CE3E21500EB72D5 /* SyncPlaySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncPlaySection.swift; sourceTree = "<group>"; };
|
||||
4EB538C42CE3E25500EB72D5 /* ExternalAccessSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalAccessSection.swift; sourceTree = "<group>"; };
|
||||
4EB538C72CE3E8A100EB72D5 /* RemoteControlSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteControlSection.swift; sourceTree = "<group>"; };
|
||||
4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChevronAlertButton.swift; sourceTree = "<group>"; };
|
||||
4EB7C8D42CCED6E1000CC011 /* AddServerUserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddServerUserView.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>"; };
|
||||
|
@ -5136,7 +5130,6 @@
|
|||
E18E0203288749200022598C /* BlurView.swift */,
|
||||
E145EB212BDCCA43003BF6F3 /* BulletedList.swift */,
|
||||
E11982B92DA04F9B0008FC3F /* CenteredLazyVGrid.swift */,
|
||||
4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */,
|
||||
E1A1528728FD229500600579 /* ChevronButton.swift */,
|
||||
E11982D62DA0E8240008FC3F /* ConditionalMenu.swift */,
|
||||
E1153DCB2BBB633B00424D36 /* FastSVGView.swift */,
|
||||
|
@ -6195,7 +6188,6 @@
|
|||
E1763A722BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift in Sources */,
|
||||
E1937A3C288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */,
|
||||
E18A17F0298C68B700C22F62 /* Overlay.swift in Sources */,
|
||||
4E4A53222CBE0A1C003BD24D /* ChevronAlertButton.swift in Sources */,
|
||||
4E7315752D1485C900EA2A95 /* UserProfileImage.swift in Sources */,
|
||||
E1A42E4A28CA6CCD00A14DCB /* CinematicItemSelector.swift in Sources */,
|
||||
4E2AC4CF2C6C4A0600DD600D /* PlaybackQualitySettingsCoordinator.swift in Sources */,
|
||||
|
@ -6918,7 +6910,6 @@
|
|||
BD3957792C113EC40078CEF8 /* SubtitleSection.swift in Sources */,
|
||||
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */,
|
||||
E1721FAE28FB801C00762992 /* SmallPlaybackButtons.swift in Sources */,
|
||||
4EB7B33B2CBDE645004A342E /* ChevronAlertButton.swift in Sources */,
|
||||
E1E750682A33E9B400B2C1EE /* OverviewCard.swift in Sources */,
|
||||
E1CCF13128AC07EC006CAC9E /* PosterHStack.swift in Sources */,
|
||||
4E2AC4C22C6C491200DD600D /* AudoCodec.swift in Sources */,
|
||||
|
|
|
@ -40,10 +40,9 @@ struct AboutAppView: View {
|
|||
|
||||
ChevronButton(
|
||||
L10n.sourceCode,
|
||||
image: Image(.logoGithub),
|
||||
image: .logoGithub,
|
||||
external: true
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
UIApplication.shared.open(.swiftfinGithub)
|
||||
}
|
||||
|
||||
|
@ -51,8 +50,7 @@ struct AboutAppView: View {
|
|||
L10n.bugsAndFeatures,
|
||||
systemName: "plus.circle.fill",
|
||||
external: true
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
UIApplication.shared.open(.swiftfinGithubIssues)
|
||||
}
|
||||
.symbolRenderingMode(.monochrome)
|
||||
|
@ -61,8 +59,7 @@ struct AboutAppView: View {
|
|||
L10n.settings,
|
||||
systemName: "gearshape.fill",
|
||||
external: true
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
|
|
|
@ -23,38 +23,32 @@ struct AdminDashboardView: View {
|
|||
description: L10n.dashboardDescription
|
||||
)
|
||||
|
||||
ChevronButton(L10n.sessions)
|
||||
.onSelect {
|
||||
router.route(to: \.activeSessions)
|
||||
}
|
||||
ChevronButton(L10n.sessions) {
|
||||
router.route(to: \.activeSessions)
|
||||
}
|
||||
|
||||
Section(L10n.activity) {
|
||||
ChevronButton(L10n.devices)
|
||||
.onSelect {
|
||||
router.route(to: \.devices)
|
||||
}
|
||||
ChevronButton(L10n.users)
|
||||
.onSelect {
|
||||
router.route(to: \.users)
|
||||
}
|
||||
ChevronButton(L10n.devices) {
|
||||
router.route(to: \.devices)
|
||||
}
|
||||
ChevronButton(L10n.users) {
|
||||
router.route(to: \.users)
|
||||
}
|
||||
}
|
||||
|
||||
Section(L10n.advanced) {
|
||||
|
||||
ChevronButton(L10n.apiKeys)
|
||||
.onSelect {
|
||||
router.route(to: \.apiKeys)
|
||||
}
|
||||
ChevronButton(L10n.apiKeys) {
|
||||
router.route(to: \.apiKeys)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.logs)
|
||||
.onSelect {
|
||||
router.route(to: \.serverLogs)
|
||||
}
|
||||
ChevronButton(L10n.logs) {
|
||||
router.route(to: \.serverLogs)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.tasks)
|
||||
.onSelect {
|
||||
router.route(to: \.tasks)
|
||||
}
|
||||
ChevronButton(L10n.tasks) {
|
||||
router.route(to: \.tasks)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(L10n.dashboard)
|
||||
|
|
|
@ -29,7 +29,7 @@ extension AddTaskTriggerView {
|
|||
// MARK: - Body
|
||||
|
||||
var body: some View {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.every,
|
||||
subtitle: ServerTicks(
|
||||
taskTriggerInfo.intervalTicks
|
||||
|
|
|
@ -30,7 +30,7 @@ extension AddTaskTriggerView {
|
|||
|
||||
var body: some View {
|
||||
Section {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.timeLimit,
|
||||
subtitle: subtitleString,
|
||||
description: L10n.taskTriggerTimeLimit
|
||||
|
|
|
@ -65,7 +65,7 @@ struct ServerUserDetailsView: View {
|
|||
}
|
||||
|
||||
Section {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.username,
|
||||
subtitle: viewModel.user.name
|
||||
) {
|
||||
|
@ -82,45 +82,37 @@ struct ServerUserDetailsView: View {
|
|||
}
|
||||
}
|
||||
if let userId = viewModel.user.id {
|
||||
ChevronButton(L10n.password)
|
||||
.onSelect {
|
||||
router.route(to: \.resetUserPassword, userId)
|
||||
}
|
||||
}
|
||||
ChevronButton(L10n.permissions)
|
||||
.onSelect {
|
||||
router.route(to: \.userPermissions, viewModel)
|
||||
ChevronButton(L10n.password) {
|
||||
router.route(to: \.resetUserPassword, userId)
|
||||
}
|
||||
}
|
||||
ChevronButton(L10n.permissions) {
|
||||
router.route(to: \.userPermissions, viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
Section(L10n.access) {
|
||||
ChevronButton(L10n.devices)
|
||||
.onSelect {
|
||||
router.route(to: \.userDeviceAccess, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.liveTV)
|
||||
.onSelect {
|
||||
router.route(to: \.userLiveTVAccess, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.media)
|
||||
.onSelect {
|
||||
router.route(to: \.userMediaAccess, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.devices) {
|
||||
router.route(to: \.userDeviceAccess, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.liveTV) {
|
||||
router.route(to: \.userLiveTVAccess, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.media) {
|
||||
router.route(to: \.userMediaAccess, viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
Section(L10n.parentalControls) {
|
||||
ChevronButton(L10n.ratings)
|
||||
.onSelect {
|
||||
router.route(to: \.userParentalRatings, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.accessSchedules)
|
||||
.onSelect {
|
||||
router.route(to: \.userEditAccessSchedules, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.accessTags)
|
||||
.onSelect {
|
||||
router.route(to: \.userEditAccessTags, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.ratings) {
|
||||
router.route(to: \.userParentalRatings, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.accessSchedules) {
|
||||
router.route(to: \.userEditAccessSchedules, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.accessTags) {
|
||||
router.route(to: \.userEditAccessTags, viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(L10n.user)
|
||||
|
|
|
@ -35,7 +35,7 @@ extension ServerUserPermissionsView {
|
|||
)
|
||||
|
||||
if policy.remoteClientBitrateLimit != MaxBitratePolicy.unlimited.rawValue {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.customBitrate,
|
||||
subtitle: Text(policy.remoteClientBitrateLimit ?? 0, format: .bitRate),
|
||||
description: L10n.enterCustomBitrate
|
||||
|
|
|
@ -74,7 +74,7 @@ extension ServerUserPermissionsView {
|
|||
|
||||
@ViewBuilder
|
||||
private func MaxFailedLoginsButton() -> some View {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.customFailedLogins,
|
||||
subtitle: Text(policy.loginAttemptsBeforeLockout ?? 1, format: .number),
|
||||
description: L10n.enterCustomFailedLogins
|
||||
|
@ -127,7 +127,7 @@ extension ServerUserPermissionsView {
|
|||
|
||||
@ViewBuilder
|
||||
private func MaxSessionsButton() -> some View {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.customSessions,
|
||||
subtitle: Text(policy.maxActiveSessions ?? 1, format: .number),
|
||||
description: L10n.enterCustomMaxSessions
|
||||
|
|
|
@ -37,17 +37,15 @@ struct AppSettingsView: View {
|
|||
var body: some View {
|
||||
Form {
|
||||
|
||||
ChevronButton(L10n.about)
|
||||
.onSelect {
|
||||
router.route(to: \.about, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.about) {
|
||||
router.route(to: \.about, viewModel)
|
||||
}
|
||||
|
||||
Section(L10n.accessibility) {
|
||||
|
||||
ChevronButton(L10n.appIcon)
|
||||
.onSelect {
|
||||
router.route(to: \.appIconSelector, viewModel)
|
||||
}
|
||||
ChevronButton(L10n.appIcon) {
|
||||
router.route(to: \.appIconSelector, viewModel)
|
||||
}
|
||||
|
||||
if !selectUserUseSplashscreen {
|
||||
CaseIterablePicker(
|
||||
|
@ -85,10 +83,9 @@ struct AppSettingsView: View {
|
|||
|
||||
SignOutIntervalSection()
|
||||
|
||||
ChevronButton(L10n.logs)
|
||||
.onSelect {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
ChevronButton(L10n.logs) {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
}
|
||||
.animation(.linear, value: selectUserUseSplashscreen)
|
||||
.navigationTitle(L10n.advanced)
|
||||
|
|
|
@ -99,38 +99,31 @@ struct ItemEditorView: View {
|
|||
private var editView: some View {
|
||||
Section(L10n.edit) {
|
||||
if [.boxSet, .movie, .person, .series].contains(viewModel.item.type) {
|
||||
ChevronButton(L10n.identify)
|
||||
.onSelect {
|
||||
router.route(to: \.identifyItem, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.identify) {
|
||||
router.route(to: \.identifyItem, viewModel.item)
|
||||
}
|
||||
}
|
||||
ChevronButton(L10n.images) {
|
||||
router.route(to: \.editImages, ItemImagesViewModel(item: viewModel.item))
|
||||
}
|
||||
ChevronButton(L10n.metadata) {
|
||||
router.route(to: \.editMetadata, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.images)
|
||||
.onSelect {
|
||||
router.route(to: \.editImages, ItemImagesViewModel(item: viewModel.item))
|
||||
}
|
||||
ChevronButton(L10n.metadata)
|
||||
.onSelect {
|
||||
router.route(to: \.editMetadata, viewModel.item)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
ChevronButton(L10n.genres)
|
||||
.onSelect {
|
||||
router.route(to: \.editGenres, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.people)
|
||||
.onSelect {
|
||||
router.route(to: \.editPeople, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.tags)
|
||||
.onSelect {
|
||||
router.route(to: \.editTags, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.studios)
|
||||
.onSelect {
|
||||
router.route(to: \.editStudios, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.genres) {
|
||||
router.route(to: \.editGenres, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.people) {
|
||||
router.route(to: \.editPeople, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.tags) {
|
||||
router.route(to: \.editTags, viewModel.item)
|
||||
}
|
||||
ChevronButton(L10n.studios) {
|
||||
router.route(to: \.editStudios, viewModel.item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,8 +92,7 @@ extension ItemImageDetailsView {
|
|||
ChevronButton(
|
||||
L10n.imageSource,
|
||||
external: true
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ extension EditMetadataView {
|
|||
|
||||
// MARK: - Season Number
|
||||
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.season,
|
||||
subtitle: item.parentIndexNumber?.description,
|
||||
description: L10n.enterSeasonNumber
|
||||
|
@ -39,7 +39,7 @@ extension EditMetadataView {
|
|||
|
||||
// MARK: - Episode Number
|
||||
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.episode,
|
||||
subtitle: item.indexNumber?.description,
|
||||
description: L10n.enterEpisodeNumber
|
||||
|
|
|
@ -24,7 +24,7 @@ extension EditMetadataView {
|
|||
|
||||
// MARK: - Critics Rating
|
||||
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.critics,
|
||||
subtitle: item.criticRating.map { "\($0)" } ?? .emptyDash,
|
||||
description: L10n.ratingDescription(L10n.critics)
|
||||
|
@ -44,7 +44,7 @@ extension EditMetadataView {
|
|||
|
||||
// MARK: - Community Rating
|
||||
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.community,
|
||||
subtitle: item.communityRating.map { "\($0)" } ?? .emptyDash,
|
||||
description: L10n.ratingDescription(L10n.community)
|
||||
|
|
|
@ -99,7 +99,7 @@ extension EditMetadataView {
|
|||
|
||||
@ViewBuilder
|
||||
private var runTimeView: some View {
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.runtime,
|
||||
subtitle: ServerTicks(item.runTimeTicks ?? 0)
|
||||
.seconds.formatted(.hourMinute),
|
||||
|
|
|
@ -24,10 +24,9 @@ struct MediaSourceInfoView: View {
|
|||
{
|
||||
Section(L10n.video) {
|
||||
ForEach(videoStreams, id: \.self) { stream in
|
||||
ChevronButton(stream.displayTitle ?? .emptyDash)
|
||||
.onSelect {
|
||||
router.route(to: \.mediaStreamInfo, stream)
|
||||
}
|
||||
ChevronButton(stream.displayTitle ?? .emptyDash) {
|
||||
router.route(to: \.mediaStreamInfo, stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +36,9 @@ struct MediaSourceInfoView: View {
|
|||
{
|
||||
Section(L10n.audio) {
|
||||
ForEach(audioStreams, id: \.self) { stream in
|
||||
ChevronButton(stream.displayTitle ?? .emptyDash)
|
||||
.onSelect {
|
||||
router.route(to: \.mediaStreamInfo, stream)
|
||||
}
|
||||
ChevronButton(stream.displayTitle ?? .emptyDash) {
|
||||
router.route(to: \.mediaStreamInfo, stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,10 +48,9 @@ struct MediaSourceInfoView: View {
|
|||
{
|
||||
Section(L10n.subtitle) {
|
||||
ForEach(subtitleStreams, id: \.self) { stream in
|
||||
ChevronButton(stream.displayTitle ?? .emptyDash)
|
||||
.onSelect {
|
||||
router.route(to: \.mediaStreamInfo, stream)
|
||||
}
|
||||
ChevronButton(stream.displayTitle ?? .emptyDash) {
|
||||
router.route(to: \.mediaStreamInfo, stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ extension CustomizeViewsSettings {
|
|||
|
||||
Toggle(L10n.nextUpRewatch, isOn: $resumeNextUp)
|
||||
|
||||
ChevronAlertButton(
|
||||
ChevronButton(
|
||||
L10n.nextUpDays,
|
||||
subtitle: {
|
||||
if maxNextUp > 0 {
|
||||
|
|
|
@ -35,10 +35,9 @@ extension CustomizeViewsSettings {
|
|||
var body: some View {
|
||||
Section(L10n.items) {
|
||||
|
||||
ChevronButton(L10n.mediaAttributes)
|
||||
.onSelect {
|
||||
router.route(to: \.itemViewAttributes, $itemViewAttributes)
|
||||
}
|
||||
ChevronButton(L10n.mediaAttributes) {
|
||||
router.route(to: \.itemViewAttributes, $itemViewAttributes)
|
||||
}
|
||||
|
||||
CaseIterablePicker(
|
||||
L10n.enabledTrailers,
|
||||
|
|
|
@ -104,15 +104,13 @@ struct CustomizeViewsSettings: View {
|
|||
)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.library)
|
||||
.onSelect {
|
||||
router.route(to: \.itemFilterDrawerSelector, $libraryEnabledDrawerFilters)
|
||||
}
|
||||
ChevronButton(L10n.library) {
|
||||
router.route(to: \.itemFilterDrawerSelector, $libraryEnabledDrawerFilters)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.search)
|
||||
.onSelect {
|
||||
router.route(to: \.itemFilterDrawerSelector, $searchEnabledDrawerFilters)
|
||||
}
|
||||
ChevronButton(L10n.search) {
|
||||
router.route(to: \.itemFilterDrawerSelector, $searchEnabledDrawerFilters)
|
||||
}
|
||||
|
||||
} header: {
|
||||
L10n.filters.text
|
||||
|
@ -127,10 +125,9 @@ struct CustomizeViewsSettings: View {
|
|||
|
||||
Section(L10n.posters) {
|
||||
|
||||
ChevronButton(L10n.indicators)
|
||||
.onSelect {
|
||||
router.route(to: \.indicatorSettings)
|
||||
}
|
||||
ChevronButton(L10n.indicators) {
|
||||
router.route(to: \.indicatorSettings)
|
||||
}
|
||||
|
||||
Toggle(L10n.showPosterLabels, isOn: $showPosterLabels)
|
||||
|
||||
|
|
|
@ -70,10 +70,9 @@ struct PlaybackQualitySettingsView: View {
|
|||
.animation(.none, value: compatibilityMode)
|
||||
|
||||
if compatibilityMode == .custom {
|
||||
ChevronButton(L10n.profiles)
|
||||
.onSelect {
|
||||
router.route(to: \.customDeviceProfileSettings)
|
||||
}
|
||||
ChevronButton(L10n.profiles) {
|
||||
router.route(to: \.customDeviceProfileSettings)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.deviceProfile)
|
||||
|
|
|
@ -38,16 +38,14 @@ struct SettingsView: View {
|
|||
ChevronButton(
|
||||
L10n.server,
|
||||
subtitle: viewModel.userSession.server.name
|
||||
)
|
||||
.onSelect {
|
||||
) {
|
||||
router.route(to: \.serverConnection, viewModel.userSession.server)
|
||||
}
|
||||
|
||||
if viewModel.userSession.user.permissions.isAdministrator {
|
||||
ChevronButton(L10n.dashboard)
|
||||
.onSelect {
|
||||
router.route(to: \.adminDashboard)
|
||||
}
|
||||
ChevronButton(L10n.dashboard) {
|
||||
router.route(to: \.adminDashboard)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,29 +64,25 @@ struct SettingsView: View {
|
|||
selection: $videoPlayerType
|
||||
)
|
||||
|
||||
ChevronButton(L10n.nativePlayer)
|
||||
.onSelect {
|
||||
router.route(to: \.nativePlayerSettings)
|
||||
}
|
||||
ChevronButton(L10n.nativePlayer) {
|
||||
router.route(to: \.nativePlayerSettings)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.videoPlayer)
|
||||
.onSelect {
|
||||
router.route(to: \.videoPlayerSettings)
|
||||
}
|
||||
ChevronButton(L10n.videoPlayer) {
|
||||
router.route(to: \.videoPlayerSettings)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.playbackQuality)
|
||||
.onSelect {
|
||||
router.route(to: \.playbackQualitySettings)
|
||||
}
|
||||
ChevronButton(L10n.playbackQuality) {
|
||||
router.route(to: \.playbackQualitySettings)
|
||||
}
|
||||
}
|
||||
|
||||
Section(L10n.accessibility) {
|
||||
CaseIterablePicker(L10n.appearance, selection: $appearance)
|
||||
|
||||
ChevronButton(L10n.customize)
|
||||
.onSelect {
|
||||
router.route(to: \.customizeViewsSettings)
|
||||
}
|
||||
ChevronButton(L10n.customize) {
|
||||
router.route(to: \.customizeViewsSettings)
|
||||
}
|
||||
|
||||
// Note: uncomment if there are current
|
||||
// experimental settings
|
||||
|
@ -105,17 +99,15 @@ struct SettingsView: View {
|
|||
Text(L10n.viewsMayRequireRestart)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.logs)
|
||||
.onSelect {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
ChevronButton(L10n.logs) {
|
||||
router.route(to: \.log)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
ChevronButton("Debug")
|
||||
.onSelect {
|
||||
router.route(to: \.debugSettings)
|
||||
}
|
||||
ChevronButton("Debug") {
|
||||
router.route(to: \.debugSettings)
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -44,22 +44,19 @@ struct UserProfileSettingsView: View {
|
|||
}
|
||||
|
||||
Section {
|
||||
ChevronButton(L10n.quickConnect)
|
||||
.onSelect {
|
||||
router.route(to: \.quickConnect)
|
||||
}
|
||||
ChevronButton(L10n.quickConnect) {
|
||||
router.route(to: \.quickConnect)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.password)
|
||||
.onSelect {
|
||||
router.route(to: \.resetUserPassword, viewModel.userSession.user.id)
|
||||
}
|
||||
ChevronButton(L10n.password) {
|
||||
router.route(to: \.resetUserPassword, viewModel.userSession.user.id)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
ChevronButton(L10n.security)
|
||||
.onSelect {
|
||||
router.route(to: \.localSecurity)
|
||||
}
|
||||
ChevronButton(L10n.security) {
|
||||
router.route(to: \.localSecurity)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
|
|
|
@ -42,15 +42,13 @@ extension VideoPlayerSettingsView {
|
|||
}
|
||||
}
|
||||
|
||||
ChevronButton(L10n.barButtons)
|
||||
.onSelect {
|
||||
router.route(to: \.actionButtonSelector, $barActionButtons)
|
||||
}
|
||||
ChevronButton(L10n.barButtons) {
|
||||
router.route(to: \.actionButtonSelector, $barActionButtons)
|
||||
}
|
||||
|
||||
ChevronButton(L10n.menuButtons)
|
||||
.onSelect {
|
||||
router.route(to: \.actionButtonSelector, $menuActionButtons)
|
||||
}
|
||||
ChevronButton(L10n.menuButtons) {
|
||||
router.route(to: \.actionButtonSelector, $menuActionButtons)
|
||||
}
|
||||
}
|
||||
.onChange(of: barActionButtons) { newValue in
|
||||
autoPlayEnabled = newValue.contains(.autoPlay) || menuActionButtons.contains(.autoPlay)
|
||||
|
|
|
@ -23,10 +23,9 @@ extension VideoPlayerSettingsView {
|
|||
|
||||
var body: some View {
|
||||
Section {
|
||||
ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName)
|
||||
.onSelect {
|
||||
router.route(to: \.fontPicker, $subtitleFontName)
|
||||
}
|
||||
ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName) {
|
||||
router.route(to: \.fontPicker, $subtitleFontName)
|
||||
}
|
||||
|
||||
BasicStepper(
|
||||
title: L10n.subtitleSize,
|
||||
|
|
|
@ -24,10 +24,9 @@ struct VideoPlayerSettingsView: View {
|
|||
var body: some View {
|
||||
Form {
|
||||
|
||||
ChevronButton(L10n.gestures)
|
||||
.onSelect {
|
||||
router.route(to: \.gestureSettings)
|
||||
}
|
||||
ChevronButton(L10n.gestures) {
|
||||
router.route(to: \.gestureSettings)
|
||||
}
|
||||
|
||||
CaseIterablePicker(L10n.jumpBackwardLength, selection: $jumpBackwardLength)
|
||||
|
||||
|
|
|
@ -34,10 +34,9 @@ struct PlaybackSettingsView: View {
|
|||
Form {
|
||||
Section {
|
||||
|
||||
ChevronButton(L10n.videoPlayer)
|
||||
.onSelect {
|
||||
router.route(to: \.videoPlayerSettings)
|
||||
}
|
||||
ChevronButton(L10n.videoPlayer) {
|
||||
router.route(to: \.videoPlayerSettings)
|
||||
}
|
||||
|
||||
// TODO: playback information
|
||||
} header: {
|
||||
|
@ -67,10 +66,9 @@ struct PlaybackSettingsView: View {
|
|||
if viewModel.videoStreams.isNotEmpty {
|
||||
Section(L10n.video) {
|
||||
ForEach(viewModel.videoStreams, id: \.displayTitle) { mediaStream in
|
||||
ChevronButton(mediaStream.displayTitle ?? .emptyDash)
|
||||
.onSelect {
|
||||
router.route(to: \.mediaStreamInfo, mediaStream)
|
||||
}
|
||||
ChevronButton(mediaStream.displayTitle ?? .emptyDash) {
|
||||
router.route(to: \.mediaStreamInfo, mediaStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,10 +76,9 @@ struct PlaybackSettingsView: View {
|
|||
if viewModel.audioStreams.isNotEmpty {
|
||||
Section(L10n.audio) {
|
||||
ForEach(viewModel.audioStreams, id: \.displayTitle) { mediaStream in
|
||||
ChevronButton(mediaStream.displayTitle ?? .emptyDash)
|
||||
.onSelect {
|
||||
router.route(to: \.mediaStreamInfo, mediaStream)
|
||||
}
|
||||
ChevronButton(mediaStream.displayTitle ?? .emptyDash) {
|
||||
router.route(to: \.mediaStreamInfo, mediaStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,10 +86,9 @@ struct PlaybackSettingsView: View {
|
|||
if viewModel.subtitleStreams.isNotEmpty {
|
||||
Section(L10n.subtitle) {
|
||||
ForEach(viewModel.subtitleStreams, id: \.displayTitle) { mediaStream in
|
||||
ChevronButton(mediaStream.displayTitle ?? .emptyDash)
|
||||
.onSelect {
|
||||
router.route(to: \.mediaStreamInfo, mediaStream)
|
||||
}
|
||||
ChevronButton(mediaStream.displayTitle ?? .emptyDash) {
|
||||
router.route(to: \.mediaStreamInfo, mediaStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue