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