From 3154274dc4672fa15874e5c03f586bf2938f35df Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Sun, 18 Aug 2024 19:01:00 -0600 Subject: [PATCH 1/8] Settings Descriptions for iOS Video Player Settings. Get all Video Player Sections into their own files. Make tvOS mirror iOS for VideoPlayerSettings with the exception of the ColorPickers and the Gestures. Remove the Native Player Settings in favor of a single settings screen with a switch case. --- Shared/Coordinators/SettingsCoordinator.swift | 6 - .../VideoPlayerSettingsCoordinator.swift | 31 ++- Shared/Strings/Strings.swift | 26 ++- .../OrderedSectionSelectorView.swift | 201 ++++++++++++++++++ Swiftfin tvOS/Components/StepperView.swift | 44 ++-- Swiftfin tvOS/Views/FontPickerView.swift | 1 + .../Views/ResumeOffsetPickerView.swift | 28 +++ .../VideoPlayerSettingsView.swift | 94 -------- .../Components/ActionButtonSelectorView.swift | 33 +++ .../Components/Sections/ButtonSection.swift | 60 ++++++ .../Sections/PlayerControlsSection.swift | 36 ++++ .../Sections/ResumeOffsetSection.swift | 36 ++++ .../Components/Sections/SliderSection.swift | 40 ++++ .../Components/Sections/SubtitleSection.swift | 51 +++++ .../Sections/TimestampSection.swift | 35 +++ .../Sections/TransitionSection.swift | 31 +++ .../VideoPlayerSettingsView.swift | 51 +++++ .../Views/SubtitleSizePickerView.swift | 25 +++ Swiftfin.xcodeproj/project.pbxproj | 86 +++++++- Swiftfin/Components/SettingsHelpText.swift | 24 +++ .../SettingsView/SettingsView.swift | 5 - .../Components/Sections/ButtonSection.swift | 8 +- .../Sections/PlayerControlsSection.swift | 39 ++++ .../Sections/ResumeOffsetSection.swift} | 23 +- .../Components/Sections/SliderSection.swift | 8 +- .../Components/Sections/SubtitleSection.swift | 5 +- .../Sections/TimestampSection.swift | 8 +- .../Sections/TransitionSection.swift | 8 +- .../VideoPlayerSettingsView.swift | 48 ++--- Translations/en.lproj/Localizable.strings | Bin 27976 -> 29868 bytes 30 files changed, 888 insertions(+), 203 deletions(-) create mode 100644 Swiftfin tvOS/Components/OrderedSectionSelectorView.swift create mode 100644 Swiftfin tvOS/Views/ResumeOffsetPickerView.swift delete mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift create mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift create mode 100644 Swiftfin tvOS/Views/SubtitleSizePickerView.swift create mode 100644 Swiftfin/Components/SettingsHelpText.swift create mode 100644 Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift rename Swiftfin/Views/SettingsView/{NativeVideoPlayerSettingsView.swift => VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift} (61%) diff --git a/Shared/Coordinators/SettingsCoordinator.swift b/Shared/Coordinators/SettingsCoordinator.swift index 431d88938..5641371b3 100644 --- a/Shared/Coordinators/SettingsCoordinator.swift +++ b/Shared/Coordinators/SettingsCoordinator.swift @@ -20,8 +20,6 @@ final class SettingsCoordinator: NavigationCoordinatable { @Route(.push) var log = makeLog @Route(.push) - var nativePlayerSettings = makeNativePlayerSettings - @Route(.push) var maximumBitrateSettings = makeMaximumBitrateSettings @Route(.push) var quickConnect = makeQuickConnectAuthorize @@ -69,10 +67,6 @@ final class SettingsCoordinator: NavigationCoordinatable { #endif #if os(iOS) - @ViewBuilder - func makeNativePlayerSettings() -> some View { - NativeVideoPlayerSettingsView() - } @ViewBuilder func makeMaximumBitrateSettings() -> some View { diff --git a/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift b/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift index 0133f055b..ea347a6f3 100644 --- a/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift +++ b/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift @@ -18,28 +18,43 @@ final class VideoPlayerSettingsCoordinator: NavigationCoordinatable { var start = makeStart @Route(.push) var fontPicker = makeFontPicker + @Route(.push) + var actionButtonSelector = makeActionButtonSelector - #if os(iOS) + #if os(tvOS) @Route(.push) - var gestureSettings = makeGestureSettings + var resumeOffset = makeResumeOffset @Route(.push) - var actionButtonSelector = makeActionButtonSelector + var subtitleSize = makeSubtitleSize + #elseif os(iOS) + @Route(.push) + var gestureSettings = makeGestureSettings #endif func makeFontPicker(selection: Binding) -> some View { FontPickerView(selection: selection) } - #if os(iOS) + func makeActionButtonSelector(selectedButtonsBinding: Binding<[VideoPlayerActionButton]>) -> some View { + ActionButtonSelectorView(selection: selectedButtonsBinding) + } + + #if os(tvOS) + + func makeResumeOffset(selection: Binding) -> some View { + ResumeOffsetPickerView(selection: selection) + } + + func makeSubtitleSize(selection: Binding) -> some View { + SubtitleSizePickerView(selection: selection) + } + + #elseif os(iOS) @ViewBuilder func makeGestureSettings() -> some View { GestureSettingsView() } - - func makeActionButtonSelector(selectedButtonsBinding: Binding<[VideoPlayerActionButton]>) -> some View { - ActionButtonSelectorView(selection: selectedButtonsBinding) - } #endif @ViewBuilder diff --git a/Shared/Strings/Strings.swift b/Shared/Strings/Strings.swift index 1fa91a3fc..9ed81187e 100644 --- a/Shared/Strings/Strings.swift +++ b/Shared/Strings/Strings.swift @@ -98,6 +98,8 @@ internal enum L10n { internal static let bugsAndFeatures = L10n.tr("Localizable", "bugsAndFeatures", fallback: "Bugs and Features") /// Buttons internal static let buttons = L10n.tr("Localizable", "buttons", fallback: "Buttons") + /// Customize which playback buttons are active and adjust their size + internal static let buttonsDescription = L10n.tr("Localizable", "buttonsDescription", fallback: "Customize which playback buttons are active and adjust their size") /// Cancel internal static let cancel = L10n.tr("Localizable", "cancel", fallback: "Cancel") /// Cannot connect to host @@ -384,8 +386,8 @@ internal enum L10n { } /// Password internal static let password = L10n.tr("Localizable", "password", fallback: "Password") - /// Pause on background - internal static let pauseOnBackground = L10n.tr("Localizable", "pauseOnBackground", fallback: "Pause on background") + /// Pause on Background + internal static let pauseOnBackground = L10n.tr("Localizable", "pauseOnBackground", fallback: "Pause on Background") /// People internal static let people = L10n.tr("Localizable", "people", fallback: "People") /// Play @@ -404,6 +406,10 @@ internal enum L10n { internal static let playbackSpeed = L10n.tr("Localizable", "playbackSpeed", fallback: "Playback Speed") /// Played internal static let played = L10n.tr("Localizable", "played", fallback: "Played") + /// Player Controls + internal static let playerControls = L10n.tr("Localizable", "playerControls", fallback: "Player Controls") + /// Customize playback controls and gestures + internal static let playerControlsDescription = L10n.tr("Localizable", "playerControlsDescription", fallback: "Customize playback controls and gestures") /// Player Gestures Lock Gesture Enabled internal static let playerGesturesLockGestureEnabled = L10n.tr("Localizable", "playerGesturesLockGestureEnabled", fallback: "Player Gestures Lock Gesture Enabled") /// Play From Beginning @@ -412,8 +418,8 @@ internal enum L10n { internal static let playNext = L10n.tr("Localizable", "playNext", fallback: "Play Next") /// Play Next Item internal static let playNextItem = L10n.tr("Localizable", "playNextItem", fallback: "Play Next Item") - /// Play on active - internal static let playOnActive = L10n.tr("Localizable", "playOnActive", fallback: "Play on active") + /// Play on Active + internal static let playOnActive = L10n.tr("Localizable", "playOnActive", fallback: "Play on Active") /// Play Previous Item internal static let playPreviousItem = L10n.tr("Localizable", "playPreviousItem", fallback: "Play Previous Item") /// Posters @@ -498,8 +504,8 @@ internal enum L10n { internal static let resume5SecondOffset = L10n.tr("Localizable", "resume5SecondOffset", fallback: "Resume 5 Second Offset") /// Resume Offset internal static let resumeOffset = L10n.tr("Localizable", "resumeOffset", fallback: "Resume Offset") - /// Resume content seconds before the recorded resume time - internal static let resumeOffsetDescription = L10n.tr("Localizable", "resumeOffsetDescription", fallback: "Resume content seconds before the recorded resume time") + /// Set the media playback to rewind by the offset amount in seconds when resuming + internal static let resumeOffsetDescription = L10n.tr("Localizable", "resumeOffsetDescription", fallback: "Set the media playback to rewind by the offset amount in seconds when resuming") /// Resume Offset internal static let resumeOffsetTitle = L10n.tr("Localizable", "resumeOffsetTitle", fallback: "Resume Offset") /// Retrieving media information @@ -598,6 +604,8 @@ internal enum L10n { internal static let slider = L10n.tr("Localizable", "slider", fallback: "Slider") /// Slider Color internal static let sliderColor = L10n.tr("Localizable", "sliderColor", fallback: "Slider Color") + /// Customize the timeline slider for media playback + internal static let sliderDescription = L10n.tr("Localizable", "sliderDescription", fallback: "Customize the timeline slider for media playback") /// Slider Type internal static let sliderType = L10n.tr("Localizable", "sliderType", fallback: "Slider Type") /// Smaller @@ -622,6 +630,8 @@ internal enum L10n { internal static let subtitle = L10n.tr("Localizable", "subtitle", fallback: "Subtitle") /// Subtitle Color internal static let subtitleColor = L10n.tr("Localizable", "subtitleColor", fallback: "Subtitle Color") + /// Customize text-based subtitles font and color + internal static let subtitleDescription = L10n.tr("Localizable", "subtitleDescription", fallback: "Customize text-based subtitles font and color") /// Subtitle Font internal static let subtitleFont = L10n.tr("Localizable", "subtitleFont", fallback: "Subtitle Font") /// Subtitle Offset @@ -646,6 +656,8 @@ internal enum L10n { internal static let testSize = L10n.tr("Localizable", "testSize", fallback: "Test Size") /// Timestamp internal static let timestamp = L10n.tr("Localizable", "timestamp", fallback: "Timestamp") + /// Adjust how the current timestamp is shown during playback + internal static let timestampDescription = L10n.tr("Localizable", "timestampDescription", fallback: "Adjust how the current timestamp is shown during playback") /// Timestamp Type internal static let timestampType = L10n.tr("Localizable", "timestampType", fallback: "Timestamp Type") /// Too Many Redirects @@ -654,6 +666,8 @@ internal enum L10n { internal static let trailingValue = L10n.tr("Localizable", "trailingValue", fallback: "Trailing Value") /// Transition internal static let transition = L10n.tr("Localizable", "transition", fallback: "Transition") + /// Configure how Swiftfin manages transitions between foreground and background while media is playing + internal static let transitionDescription = L10n.tr("Localizable", "transitionDescription", fallback: "Configure how Swiftfin manages transitions between foreground and background while media is playing") /// Try again internal static let tryAgain = L10n.tr("Localizable", "tryAgain", fallback: "Try again") /// TV Shows diff --git a/Swiftfin tvOS/Components/OrderedSectionSelectorView.swift b/Swiftfin tvOS/Components/OrderedSectionSelectorView.swift new file mode 100644 index 000000000..5fa42ed06 --- /dev/null +++ b/Swiftfin tvOS/Components/OrderedSectionSelectorView.swift @@ -0,0 +1,201 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Factory +import SwiftUI + +struct OrderedSectionSelectorView: View { + @Environment(\.editMode) + private var editMode + + @Binding + private var selection: [Element] + + @State + private var updateSelection: [Element] + + @State + private var focusedElement: Element? + + private var disabledSelection: [Element] { + sources.filter { !updateSelection.contains($0) } + } + + private var label: (Element) -> AnyView + private let sources: [Element] + private let image: Image + private let title: String + + private func move(from source: IndexSet, to destination: Int) { + updateSelection.move(fromOffsets: source, toOffset: destination) + editMode?.wrappedValue = .inactive + } + + private func select(element: Element) { + if updateSelection.contains(element) { + updateSelection.removeAll(where: { $0 == element }) + } else { + updateSelection.append(element) + } + } + + var body: some View { + NavigationView { + SplitFormWindowView() + .descriptionView { + image + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 400) + } + .contentView { + List { + EnabledSection( + elements: $updateSelection, + label: label, + isEditing: editMode?.wrappedValue.isEditing ?? false, + select: select, + move: move, + focusedElement: $focusedElement + ) + + DisabledSection( + elements: disabledSelection, + label: label, + isEditing: editMode?.wrappedValue.isEditing ?? false, + select: select, + focusedElement: $focusedElement + ) + } + .environment(\.editMode, editMode) + } + .withDescriptionTopPadding() + .navigationTitle(title) + .animation(.linear(duration: 0.2), value: updateSelection) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button(editMode?.wrappedValue.isEditing ?? false ? "Done" : "Edit") { + withAnimation { + if editMode?.wrappedValue.isEditing ?? false { + editMode?.wrappedValue = .inactive + } else { + editMode?.wrappedValue = .active + } + } + } + } + } + .onChange(of: updateSelection) { _, newValue in + selection = newValue + } + } + } +} + +private struct EnabledSection: View { + + @Binding + var elements: [Element] + + let label: (Element) -> AnyView + let isEditing: Bool + let select: (Element) -> Void + let move: (IndexSet, Int) -> Void + + @Binding + var focusedElement: Element? + + var body: some View { + Section(L10n.enabled) { + if elements.isEmpty { + Text(L10n.none) + .foregroundStyle(.secondary) + } + + ForEach(elements, id: \.self) { element in + Button { + if !isEditing { + select(element) + } + } label: { + HStack { + label(element) + + Spacer() + + if !isEditing { + Image(systemName: "minus.circle.fill") + .foregroundColor(.red) + } + } + .foregroundColor(.primary) + } + } + .onMove(perform: move) + } + } +} + +private struct DisabledSection: View { + + let elements: [Element] + + let label: (Element) -> AnyView + let isEditing: Bool + let select: (Element) -> Void + + @Binding + var focusedElement: Element? + + var body: some View { + Section(L10n.disabled) { + if elements.isEmpty { + Text(L10n.none) + .foregroundStyle(.secondary) + } + + ForEach(elements, id: \.self) { element in + Button { + if !isEditing { + select(element) + } + } label: { + HStack { + label(element) + + Spacer() + + if !isEditing { + Image(systemName: "plus.circle.fill") + .foregroundColor(.green) + } + } + .foregroundColor(.primary) + } + } + } + } +} + +extension OrderedSectionSelectorView { + + init(title: String, selection: Binding<[Element]>, sources: [Element], image: Image = Image(systemName: "filemenu.and.selection")) { + self.title = title + self._selection = selection + self._updateSelection = State(initialValue: selection.wrappedValue) + self.sources = sources + self.label = { Text($0.displayTitle).foregroundColor(.primary).eraseToAnyView() } + self.image = image + } + + func label(@ViewBuilder _ content: @escaping (Element) -> AnyView) -> Self { + var copy = self + copy.label = content + return copy + } +} diff --git a/Swiftfin tvOS/Components/StepperView.swift b/Swiftfin tvOS/Components/StepperView.swift index e193243bc..f72084f6a 100644 --- a/Swiftfin tvOS/Components/StepperView.swift +++ b/Swiftfin tvOS/Components/StepperView.swift @@ -13,6 +13,12 @@ struct StepperView: View { @Binding private var value: Value + @State + private var updatedValue: Value + + @Environment(\.presentationMode) + private var presentationMode + private var title: String private var description: String? private var range: ClosedRange @@ -36,16 +42,17 @@ struct StepperView: View { } .frame(maxHeight: .infinity) - formatter(value).text + formatter(updatedValue).text .font(.title) .frame(height: 250) VStack { - HStack { Button { - guard value >= range.lowerBound else { return } - value = value.advanced(by: -step) + if updatedValue > range.lowerBound { + updatedValue = max(updatedValue.advanced(by: -step), range.lowerBound) + value = updatedValue + } } label: { Image(systemName: "minus") .font(.title2.weight(.bold)) @@ -54,8 +61,10 @@ struct StepperView: View { .buttonStyle(.card) Button { - guard value <= range.upperBound else { return } - value = value.advanced(by: step) + if updatedValue < range.upperBound { + updatedValue = min(updatedValue.advanced(by: step), range.upperBound) + value = updatedValue + } } label: { Image(systemName: "plus") .font(.title2.weight(.bold)) @@ -64,10 +73,8 @@ struct StepperView: View { .buttonStyle(.card) } - Button { - onCloseSelected() - } label: { - Text("Close") + Button(L10n.close) { + presentationMode.wrappedValue.dismiss() } Spacer() @@ -86,15 +93,14 @@ extension StepperView { range: ClosedRange, step: Value.Stride ) { - self.init( - value: value, - title: title, - description: description, - range: range, - step: step, - formatter: { $0.description }, - onCloseSelected: {} - ) + self._value = value + self._updatedValue = State(initialValue: value.wrappedValue) + self.title = title + self.description = description + self.range = range + self.step = step + self.formatter = { $0.description } + self.onCloseSelected = {} } func valueFormatter(_ formatter: @escaping (Value) -> String) -> Self { diff --git a/Swiftfin tvOS/Views/FontPickerView.swift b/Swiftfin tvOS/Views/FontPickerView.swift index 8b76365cc..befb76210 100644 --- a/Swiftfin tvOS/Views/FontPickerView.swift +++ b/Swiftfin tvOS/Views/FontPickerView.swift @@ -50,5 +50,6 @@ struct FontPickerView: View { } } .withDescriptionTopPadding() + .navigationTitle(L10n.subtitleFont) } } diff --git a/Swiftfin tvOS/Views/ResumeOffsetPickerView.swift b/Swiftfin tvOS/Views/ResumeOffsetPickerView.swift new file mode 100644 index 000000000..59c27b1d3 --- /dev/null +++ b/Swiftfin tvOS/Views/ResumeOffsetPickerView.swift @@ -0,0 +1,28 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +struct ResumeOffsetPickerView: View { + + @Binding + var selection: Int + + var body: some View { + StepperView( + title: L10n.resumeOffset, + value: $selection, + range: 0 ... 30, + step: 1 + ) + .valueFormatter { + $0.secondLabel + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView.swift deleted file mode 100644 index 264409779..000000000 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// Swiftfin is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, you can obtain one at https://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2024 Jellyfin & Jellyfin Contributors -// - -import Defaults -import SwiftUI - -struct VideoPlayerSettingsView: View { - - @Default(.VideoPlayer.Subtitle.subtitleFontName) - private var subtitleFontName - - @Default(.VideoPlayer.jumpBackwardLength) - private var jumpBackwardLength - @Default(.VideoPlayer.jumpForwardLength) - private var jumpForwardLength - @Default(.VideoPlayer.resumeOffset) - private var resumeOffset - - @Default(.VideoPlayer.Transition.pauseOnBackground) - private var pauseOnBackground - @Default(.VideoPlayer.Transition.playOnActive) - private var playOnActive - - @EnvironmentObject - private var router: VideoPlayerSettingsCoordinator.Router - - @State - private var isPresentingResumeOffsetStepper: Bool = false - - var body: some View { - SplitFormWindowView() - .descriptionView { - Image(systemName: "tv") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 400) - } - .contentView { - - Section { - - ChevronButton( - L10n.offset, - subtitle: resumeOffset.secondLabel - ) - .onSelect { - isPresentingResumeOffsetStepper = true - } - } header: { - L10n.resume.text - } footer: { - L10n.resumeOffsetDescription.text - } - - Section { - - ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName) - .onSelect { - router.route(to: \.fontPicker, $subtitleFontName) - } - } header: { - L10n.subtitles.text - } footer: { - L10n.subtitlesDisclaimer.text - } - - Section(L10n.playback) { - Toggle(L10n.pauseOnBackground, isOn: $pauseOnBackground) - Toggle(L10n.playOnActive, isOn: $playOnActive) - } - .navigationTitle(L10n.videoPlayer.text) - .blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) { - StepperView( - title: L10n.resumeOffsetTitle, - description: L10n.resumeOffsetDescription, - value: $resumeOffset, - range: 0 ... 30, - step: 1 - ) - .valueFormatter { - $0.secondLabel - } - .onCloseSelected { - isPresentingResumeOffsetStepper = false - } - } - } - } -} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift new file mode 100644 index 000000000..c3623b867 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift @@ -0,0 +1,33 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +struct ActionButtonSelectorView: View { + + @Binding + var selection: [VideoPlayerActionButton] + + var body: some View { + OrderedSectionSelectorView( + title: L10n.playbackButtons, + selection: $selection, + sources: VideoPlayerActionButton.allCases, + image: Image(systemName: "ellipsis.rectangle") + ) + .label { button in + AnyView( + HStack { + Image(systemName: button.settingsSystemImage) + Text(button.displayTitle) + } + ) + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift new file mode 100644 index 000000000..ed54e4407 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift @@ -0,0 +1,60 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct ButtonSection: View { + @Default(.VideoPlayer.Overlay.playbackButtonType) + private var playbackButtonType + + @Default(.VideoPlayer.showJumpButtons) + private var showJumpButtons + + @Default(.VideoPlayer.barActionButtons) + private var barActionButtons + + @Default(.VideoPlayer.menuActionButtons) + private var menuActionButtons + + @Default(.VideoPlayer.autoPlayEnabled) + private var autoPlayEnabled + + @EnvironmentObject + private var router: VideoPlayerSettingsCoordinator.Router + + var body: some View { + Section { + InlineEnumToggle(title: L10n.playbackButtons, selection: $playbackButtonType) + + Toggle(L10n.jump, isOn: $showJumpButtons) + + ChevronButton(L10n.barButtons) + .onSelect { + router.route(to: \.actionButtonSelector, $barActionButtons) + } + + ChevronButton(L10n.menuButtons) + .onSelect { + router.route(to: \.actionButtonSelector, $menuActionButtons) + } + } header: { + L10n.buttons.text + } footer: { + L10n.buttonsDescription.text + } + .onChange(of: barActionButtons) { _, newValue in + autoPlayEnabled = newValue.contains(.autoPlay) || menuActionButtons.contains(.autoPlay) + } + .onChange(of: menuActionButtons) { _, newValue in + autoPlayEnabled = newValue.contains(.autoPlay) || barActionButtons.contains(.autoPlay) + } + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift new file mode 100644 index 000000000..9ace45ced --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift @@ -0,0 +1,36 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct PlayerControlsSection: View { + @Default(.VideoPlayer.jumpBackwardLength) + private var jumpBackwardLength + @Default(.VideoPlayer.jumpForwardLength) + private var jumpForwardLength + + @EnvironmentObject + private var router: VideoPlayerSettingsCoordinator.Router + + var body: some View { + Section { + // Gestures have been removes as tvOS does not have touch gestures + + InlineEnumToggle(title: L10n.jumpBackwardLength, selection: $jumpBackwardLength) + + InlineEnumToggle(title: L10n.jumpForwardLength, selection: $jumpForwardLength) + } header: { + L10n.playerControls.text + } footer: { + L10n.playerControlsDescription.text + } + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift new file mode 100644 index 000000000..ddb163864 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift @@ -0,0 +1,36 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct ResumeOffsetSection: View { + @Default(.VideoPlayer.resumeOffset) + private var resumeOffset + + @EnvironmentObject + private var router: VideoPlayerSettingsCoordinator.Router + + var body: some View { + Section { + ChevronButton( + L10n.offset, + subtitle: resumeOffset.secondLabel + ) + .onSelect { + router.route(to: \.resumeOffset, $resumeOffset) + } + } header: { + L10n.resume.text + } footer: { + L10n.resumeOffsetDescription.text + } + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift new file mode 100644 index 000000000..c6ca0e1a7 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift @@ -0,0 +1,40 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct SliderSection: View { + @Default(.VideoPlayer.Overlay.chapterSlider) + private var chapterSlider + + @Default(.VideoPlayer.Overlay.sliderColor) + private var sliderColor + + @Default(.VideoPlayer.Overlay.sliderType) + private var sliderType + + var body: some View { + Section { + Toggle(L10n.chapterSlider, isOn: $chapterSlider) + + // Commenting since ColorPicker is not around on tvOS. May need to be recreated manually? + /* ColorPicker(selection: $sliderColor, supportsOpacity: false) { + Text(L10n.sliderColor) + } */ + + InlineEnumToggle(title: L10n.sliderType, selection: $sliderType) + } header: { + L10n.slider.text + } footer: { + L10n.sliderDescription.text + } + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift new file mode 100644 index 000000000..21adf0a09 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift @@ -0,0 +1,51 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct SubtitleSection: View { + @Default(.VideoPlayer.Subtitle.subtitleFontName) + private var subtitleFontName + @Default(.VideoPlayer.Subtitle.subtitleSize) + private var subtitleSize + @Default(.VideoPlayer.Subtitle.subtitleColor) + private var subtitleColor + + @EnvironmentObject + private var router: VideoPlayerSettingsCoordinator.Router + + var body: some View { + Section { + + ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName) + .onSelect { + router.route(to: \.fontPicker, $subtitleFontName) + } + + // Leaving this comment since this isn't tvOS compatible. Just in case we want this later. + /* ColorPicker(selection: $subtitleColor) { + Text(L10n.subtitleColor) + } */ + + ChevronButton( + L10n.subtitleSize, + subtitle: subtitleSize.description + ) + .onSelect { + router.route(to: \.subtitleSize, $subtitleSize) + } + } header: { + L10n.subtitle.text + } footer: { + L10n.subtitleDescription.text + } + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift new file mode 100644 index 000000000..74fc96c51 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift @@ -0,0 +1,35 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct TimestampSection: View { + @Default(.VideoPlayer.Overlay.trailingTimestampType) + private var trailingTimestampType + @Default(.VideoPlayer.Overlay.showCurrentTimeWhileScrubbing) + private var showCurrentTimeWhileScrubbing + @Default(.VideoPlayer.Overlay.timestampType) + private var timestampType + + var body: some View { + Section { + Toggle(L10n.scrubCurrentTime, isOn: $showCurrentTimeWhileScrubbing) + + InlineEnumToggle(title: L10n.timestampType, selection: $timestampType) + + InlineEnumToggle(title: L10n.trailingValue, selection: $trailingTimestampType) + } header: { + L10n.timestamp.text + } footer: { + L10n.timestampDescription.text + } + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift new file mode 100644 index 000000000..91db16086 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift @@ -0,0 +1,31 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct TransitionSection: View { + @Default(.VideoPlayer.Transition.pauseOnBackground) + private var pauseOnBackground + @Default(.VideoPlayer.Transition.playOnActive) + private var playOnActive + + var body: some View { + Section { + Toggle(L10n.pauseOnBackground, isOn: $pauseOnBackground) + + Toggle(L10n.playOnActive, isOn: $playOnActive) + } header: { + L10n.transition.text + } footer: { + L10n.transitionDescription.text + } + } + } +} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift new file mode 100644 index 000000000..8fecaeb75 --- /dev/null +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift @@ -0,0 +1,51 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +struct VideoPlayerSettingsView: View { + @Default(.VideoPlayer.videoPlayerType) + private var videoPlayerType + + @EnvironmentObject + private var router: VideoPlayerSettingsCoordinator.Router + + var body: some View { + SplitFormWindowView() + .descriptionView { + Image(systemName: "tv") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 400) + } + .contentView { + switch videoPlayerType { + case .native: + ResumeOffsetSection() + + case .swiftfin: + // Not currently available for tvOS + PlayerControlsSection() + + ResumeOffsetSection() + // Not currently available for tvOS + ButtonSection() + // Not currently available for tvOS + SliderSection() + + SubtitleSection() + // Not currently available for tvOS + TimestampSection() + + TransitionSection() + } + } + .navigationTitle(L10n.videoPlayer.text) + } +} diff --git a/Swiftfin tvOS/Views/SubtitleSizePickerView.swift b/Swiftfin tvOS/Views/SubtitleSizePickerView.swift new file mode 100644 index 000000000..b7c09839e --- /dev/null +++ b/Swiftfin tvOS/Views/SubtitleSizePickerView.swift @@ -0,0 +1,25 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +struct SubtitleSizePickerView: View { + + @Binding + var selection: Int + + var body: some View { + StepperView( + title: L10n.subtitleSize, + value: $selection, + range: 8 ... 24, + step: 1 + ) + } +} diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 584379c3e..afdb456b6 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -14,6 +14,20 @@ 4E16FD572C01A32700110147 /* LetterPickerOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16FD562C01A32700110147 /* LetterPickerOrientation.swift */; }; 4E16FD582C01A32700110147 /* LetterPickerOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16FD562C01A32700110147 /* LetterPickerOrientation.swift */; }; 4E204E592C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E204E582C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift */; }; + 4E23D9552C711C81004B6FE5 /* ResumeOffsetSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9542C711C81004B6FE5 /* ResumeOffsetSection.swift */; }; + 4E23D9572C711E16004B6FE5 /* PlayerControlsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9562C711E16004B6FE5 /* PlayerControlsSection.swift */; }; + 4E23D9762C71547F004B6FE5 /* ResumeOffsetSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D96A2C71547F004B6FE5 /* ResumeOffsetSection.swift */; }; + 4E23D9842C71B81F004B6FE5 /* ResumeOffsetPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9832C71B81F004B6FE5 /* ResumeOffsetPickerView.swift */; }; + 4E23D9862C71C006004B6FE5 /* SubtitleSizePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9852C71C006004B6FE5 /* SubtitleSizePickerView.swift */; }; + 4E23D98F2C71C8CC004B6FE5 /* ButtonSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9872C71C8CC004B6FE5 /* ButtonSection.swift */; }; + 4E23D9902C71C8CC004B6FE5 /* PlayerControlsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9882C71C8CC004B6FE5 /* PlayerControlsSection.swift */; }; + 4E23D9912C71C8CC004B6FE5 /* ResumeOffsetSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9892C71C8CC004B6FE5 /* ResumeOffsetSection.swift */; }; + 4E23D9922C71C8CC004B6FE5 /* SliderSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */; }; + 4E23D9932C71C8CC004B6FE5 /* SubtitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */; }; + 4E23D9942C71C8CC004B6FE5 /* TimestampSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98C2C71C8CC004B6FE5 /* TimestampSection.swift */; }; + 4E23D9952C71C8CC004B6FE5 /* TransitionSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */; }; + 4E23D9972C72C08C004B6FE5 /* ActionButtonSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */; }; + 4E23D9992C72C108004B6FE5 /* OrderedSectionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */; }; 4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; }; 4E73E2A62C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */; }; 4E73E2A72C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */; }; @@ -453,7 +467,6 @@ E1559A76294D960C00C1FFBC /* MainOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1559A75294D960C00C1FFBC /* MainOverlay.swift */; }; E157563029355B7900976E1F /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E157562F29355B7900976E1F /* UpdateView.swift */; }; E15756322935642A00976E1F /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15756312935642A00976E1F /* Double.swift */; }; - E15756342936851D00976E1F /* NativeVideoPlayerSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15756332936851D00976E1F /* NativeVideoPlayerSettingsView.swift */; }; E15756362936856700976E1F /* VideoPlayerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15756352936856700976E1F /* VideoPlayerType.swift */; }; E1575E3C293C6B15001665B1 /* Files in Frameworks */ = {isa = PBXBuildFile; productRef = E1575E3B293C6B15001665B1 /* Files */; }; E1575E56293E7650001665B1 /* VLCUI in Frameworks */ = {isa = PBXBuildFile; productRef = E1575E55293E7650001665B1 /* VLCUI */; }; @@ -938,6 +951,20 @@ 4E16FD522C01840C00110147 /* LetterPickerBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LetterPickerBar.swift; sourceTree = ""; }; 4E16FD562C01A32700110147 /* LetterPickerOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LetterPickerOrientation.swift; sourceTree = ""; }; 4E204E582C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeSettingsCoordinator.swift; sourceTree = ""; }; + 4E23D9542C711C81004B6FE5 /* ResumeOffsetSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResumeOffsetSection.swift; sourceTree = ""; }; + 4E23D9562C711E16004B6FE5 /* PlayerControlsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsSection.swift; sourceTree = ""; }; + 4E23D96A2C71547F004B6FE5 /* ResumeOffsetSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResumeOffsetSection.swift; sourceTree = ""; }; + 4E23D9832C71B81F004B6FE5 /* ResumeOffsetPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResumeOffsetPickerView.swift; sourceTree = ""; }; + 4E23D9852C71C006004B6FE5 /* SubtitleSizePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubtitleSizePickerView.swift; sourceTree = ""; }; + 4E23D9872C71C8CC004B6FE5 /* ButtonSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonSection.swift; sourceTree = ""; }; + 4E23D9882C71C8CC004B6FE5 /* PlayerControlsSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerControlsSection.swift; sourceTree = ""; }; + 4E23D9892C71C8CC004B6FE5 /* ResumeOffsetSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResumeOffsetSection.swift; sourceTree = ""; }; + 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderSection.swift; sourceTree = ""; }; + 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubtitleSection.swift; sourceTree = ""; }; + 4E23D98C2C71C8CC004B6FE5 /* TimestampSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimestampSection.swift; sourceTree = ""; }; + 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionSection.swift; sourceTree = ""; }; + 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtonSelectorView.swift; sourceTree = ""; }; + 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSectionSelectorView.swift; sourceTree = ""; }; 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = ""; }; 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackBitrateTestSize.swift; sourceTree = ""; }; 4E73E2AD2C420207002D2A78 /* MaximumBitrateSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaximumBitrateSettingsView.swift; sourceTree = ""; }; @@ -1256,7 +1283,6 @@ E1559A75294D960C00C1FFBC /* MainOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainOverlay.swift; sourceTree = ""; }; E157562F29355B7900976E1F /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = ""; }; E15756312935642A00976E1F /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = ""; }; - E15756332936851D00976E1F /* NativeVideoPlayerSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeVideoPlayerSettingsView.swift; sourceTree = ""; }; E15756352936856700976E1F /* VideoPlayerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerType.swift; sourceTree = ""; }; E1575EA5293E7D40001665B1 /* VideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = ""; }; E1579EA62B97DC1500A31CA1 /* Eventful.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Eventful.swift; sourceTree = ""; }; @@ -1683,6 +1709,39 @@ path = Components; sourceTree = ""; }; + 4E23D96F2C71547F004B6FE5 /* Sections */ = { + isa = PBXGroup; + children = ( + 4E23D9872C71C8CC004B6FE5 /* ButtonSection.swift */, + 4E23D9882C71C8CC004B6FE5 /* PlayerControlsSection.swift */, + 4E23D9892C71C8CC004B6FE5 /* ResumeOffsetSection.swift */, + 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */, + 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */, + 4E23D98C2C71C8CC004B6FE5 /* TimestampSection.swift */, + 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */, + 4E23D96A2C71547F004B6FE5 /* ResumeOffsetSection.swift */, + ); + path = Sections; + sourceTree = ""; + }; + 4E23D9712C71547F004B6FE5 /* Components */ = { + isa = PBXGroup; + children = ( + 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */, + 4E23D96F2C71547F004B6FE5 /* Sections */, + ); + path = Components; + sourceTree = ""; + }; + 4E23D9732C71547F004B6FE5 /* VideoPlayerSettingsView */ = { + isa = PBXGroup; + children = ( + E1549679296CB4B000C4EF88 /* VideoPlayerSettingsView.swift */, + 4E23D9712C71547F004B6FE5 /* Components */, + ); + path = VideoPlayerSettingsView; + sourceTree = ""; + }; 4E3A785D2C3B87A400D33C11 /* PlaybackBitrate */ = { isa = PBXGroup; children = ( @@ -1870,6 +1929,7 @@ E1A42E5028CBE44500A14DCB /* LandscapePosterProgressBar.swift */, E1763A632BF3C9AA004DF6AB /* ListRowButton.swift */, E10E842B29A589860064EA49 /* NonePosterButton.swift */, + 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */, E111D8F928D0400900400001 /* PagingLibraryView.swift */, E1C92617288756BD002A7A66 /* PosterButton.swift */, E1C92619288756BD002A7A66 /* PosterHStack.swift */, @@ -2221,6 +2281,8 @@ isa = PBXGroup; children = ( BD3957742C112A330078CEF8 /* ButtonSection.swift */, + 4E23D9562C711E16004B6FE5 /* PlayerControlsSection.swift */, + 4E23D9542C711C81004B6FE5 /* ResumeOffsetSection.swift */, BD3957762C112AD30078CEF8 /* SliderSection.swift */, BD3957782C113EC40078CEF8 /* SubtitleSection.swift */, BD39577B2C113FAA0078CEF8 /* TimestampSection.swift */, @@ -2604,11 +2666,13 @@ E103DF932BCF31C5000229B2 /* MediaView */, E10231572BCF8AF8009D71FC /* ProgramsView */, E10B1E8C2BD7708900A92EAF /* QuickConnectView.swift */, + 4E23D9832C71B81F004B6FE5 /* ResumeOffsetPickerView.swift */, E1E1643928BAC2EF00323B0A /* SearchView.swift */, E193D54A271941D300900D82 /* SelectServerView.swift */, E164A8122BE4995200A54B18 /* SelectUserView */, E193D54F2719430400900D82 /* ServerDetailView.swift */, E1E5D54D2783E66600692DFE /* SettingsView */, + 4E23D9852C71C006004B6FE5 /* SubtitleSizePickerView.swift */, E1763A672BF3D168004DF6AB /* UserSignInView */, 5310694F2684E7EE00CFFDBA /* VideoPlayer */, ); @@ -3483,7 +3547,6 @@ E104C86F296E087200C1C3F9 /* IndicatorSettingsView.swift */, E16AF11B292C98A7001422A8 /* GestureSettingsView.swift */, 4E73E2AD2C420207002D2A78 /* MaximumBitrateSettingsView.swift */, - E15756332936851D00976E1F /* NativeVideoPlayerSettingsView.swift */, E1545BD62BDC559500D9578F /* UserProfileSettingsView */, E1BE1CEB2BDB68BC008176A9 /* SettingsView */, E1BDF2E7295148F400CC0294 /* VideoPlayerSettingsView */, @@ -3499,7 +3562,7 @@ E104C872296E0D0A00C1C3F9 /* IndicatorSettingsView.swift */, 4E73E2AF2C4211CA002D2A78 /* MaximumBitrateSettingsView.swift */, 5398514426B64DA100101B49 /* SettingsView.swift */, - E1549679296CB4B000C4EF88 /* VideoPlayerSettingsView.swift */, + 4E23D9732C71547F004B6FE5 /* VideoPlayerSettingsView */, ); path = SettingsView; sourceTree = ""; @@ -3901,6 +3964,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4E23D9842C71B81F004B6FE5 /* ResumeOffsetPickerView.swift in Sources */, E1AEFA392BE36C4C00CFAFD8 /* SwiftfinStore+ServerState.swift in Sources */, E15D4F0B2B1BD88900442DB8 /* Edge.swift in Sources */, E193D53627193F8500900D82 /* LibraryCoordinator.swift in Sources */, @@ -3917,6 +3981,7 @@ E1575E92293E7B1E001665B1 /* CGSize.swift in Sources */, E1575E7E293E77B5001665B1 /* ItemFilterCollection.swift in Sources */, C46DD8EF2A8FB56E0046A504 /* LiveBottomBarView.swift in Sources */, + 4E23D9952C71C8CC004B6FE5 /* TransitionSection.swift in Sources */, C46DD8EA2A8FB45C0046A504 /* LiveOverlay.swift in Sources */, E11E376D293E9CC1009EF240 /* VideoPlayerCoordinator.swift in Sources */, E1575E6F293E77B5001665B1 /* GestureAction.swift in Sources */, @@ -4054,6 +4119,7 @@ E1579EA82B97DC1500A31CA1 /* Eventful.swift in Sources */, E185920828CDAAA200326F80 /* SimilarItemsHStack.swift in Sources */, E10E842C29A589860064EA49 /* NonePosterButton.swift in Sources */, + 4E23D9902C71C8CC004B6FE5 /* PlayerControlsSection.swift in Sources */, E1575E5C293E77B5001665B1 /* PlaybackSpeed.swift in Sources */, E1DC9842296DEBD800982F06 /* WatchedIndicator.swift in Sources */, E1575E6C293E77B5001665B1 /* SliderType.swift in Sources */, @@ -4073,6 +4139,7 @@ E1356E0429A731EB00382563 /* SeparatorHStack.swift in Sources */, E1575E69293E77B5001665B1 /* ItemSortBy.swift in Sources */, E1B490482967E2E500D3EDCE /* CoreStore.swift in Sources */, + 4E23D9972C72C08C004B6FE5 /* ActionButtonSelectorView.swift in Sources */, E1DC9845296DECB600982F06 /* ProgressIndicator.swift in Sources */, E1C925F928875647002A7A66 /* LatestInLibraryView.swift in Sources */, E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */, @@ -4115,6 +4182,7 @@ E19D41B42BF2C0020082B8B2 /* StoredValues+Temp.swift in Sources */, E11BDF7B2B85529D0045C54A /* SupportedCaseIterable.swift in Sources */, 4E204E592C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift in Sources */, + 4E23D9942C71C8CC004B6FE5 /* TimestampSection.swift in Sources */, E1575E8C293E7B1E001665B1 /* UIScreen.swift in Sources */, C46DD8EC2A8FB49A0046A504 /* LiveMainOverlay.swift in Sources */, E1BCDB502BE1F491009F6744 /* ResetUserPasswordViewModel.swift in Sources */, @@ -4125,12 +4193,14 @@ E1DABAFA2A270E62008AC34A /* OverviewCard.swift in Sources */, E11CEB8928998549003E74C7 /* BottomEdgeGradientModifier.swift in Sources */, E129428628F080B500796AC6 /* OnReceiveNotificationModifier.swift in Sources */, + 4E23D9762C71547F004B6FE5 /* ResumeOffsetSection.swift in Sources */, 53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */, 62E632E4267D3BA60063E547 /* MovieItemViewModel.swift in Sources */, E149CCAE2BE6ECC8008B9331 /* Storable.swift in Sources */, C46DD8D92A8DC2990046A504 /* LiveNativeVideoPlayer.swift in Sources */, E1575E9F293E7B1E001665B1 /* Int.swift in Sources */, E1D9F475296E86D400129AF3 /* NativeVideoPlayer.swift in Sources */, + 4E23D9912C71C8CC004B6FE5 /* ResumeOffsetSection.swift in Sources */, E145EB462BE0AD4E003BF6F3 /* Set.swift in Sources */, E1575E7D293E77B5001665B1 /* PosterDisplayType.swift in Sources */, E1E5D553278419D900692DFE /* ConfirmCloseOverlay.swift in Sources */, @@ -4150,10 +4220,12 @@ E12CC1CD28D135C700678D5D /* NextUpView.swift in Sources */, E18E02232887492B0022598C /* ImageView.swift in Sources */, E1575E7F293E77B5001665B1 /* AppAppearance.swift in Sources */, + 4E23D9862C71C006004B6FE5 /* SubtitleSizePickerView.swift in Sources */, E1575E5D293E77B5001665B1 /* ItemViewType.swift in Sources */, E12CC1AF28D0FAEA00678D5D /* NextUpLibraryViewModel.swift in Sources */, E1575E7A293E77B5001665B1 /* TimeStampType.swift in Sources */, E1763A252BF2F77B004DF6AB /* ScrollIfLargerThanContainerModifier.swift in Sources */, + 4E23D9992C72C108004B6FE5 /* OrderedSectionSelectorView.swift in Sources */, E11E374E293E7F08009EF240 /* MediaSourceInfo.swift in Sources */, E1E1643A28BAC2EF00323B0A /* SearchView.swift in Sources */, E1763A642BF3C9AA004DF6AB /* ListRowButton.swift in Sources */, @@ -4214,6 +4286,7 @@ E1D37F4F2B9CEDC400343D2B /* DeviceProfile.swift in Sources */, E1575E94293E7B1E001665B1 /* VerticalAlignment.swift in Sources */, E1575EA3293E7B1E001665B1 /* UIDevice.swift in Sources */, + 4E23D9922C71C8CC004B6FE5 /* SliderSection.swift in Sources */, E193D547271941C500900D82 /* SelectUserView.swift in Sources */, E1BDF2E62951475300CC0294 /* VideoPlayerActionButton.swift in Sources */, E10231592BCF8AF8009D71FC /* ChannelLibraryView.swift in Sources */, @@ -4236,9 +4309,11 @@ E14EDECD2B8FB709000F00A4 /* ItemYear.swift in Sources */, E154965F296CA2EF00C4EF88 /* DownloadTask.swift in Sources */, E154967E296CCB6C00C4EF88 /* BasicNavigationCoordinator.swift in Sources */, + 4E23D98F2C71C8CC004B6FE5 /* ButtonSection.swift in Sources */, E1B90C8A2BC475E7007027C8 /* ScalingButtonStyle.swift in Sources */, E1DABAFE2A27B982008AC34A /* RatingsCard.swift in Sources */, E1C9261B288756BD002A7A66 /* DotHStack.swift in Sources */, + 4E23D9932C71C8CC004B6FE5 /* SubtitleSection.swift in Sources */, E104C873296E0D0A00C1C3F9 /* IndicatorSettingsView.swift in Sources */, E18ACA8D2A14773500BB4F35 /* (null) in Sources */, E10B1E8E2BD7708900A92EAF /* QuickConnectView.swift in Sources */, @@ -4307,6 +4382,7 @@ E18CE0AF28A222240092E7F1 /* PublicUserRow.swift in Sources */, E129429828F4785200796AC6 /* CaseIterablePicker.swift in Sources */, E18E01E5288747230022598C /* CinematicScrollView.swift in Sources */, + 4E23D9552C711C81004B6FE5 /* ResumeOffsetSection.swift in Sources */, E154965E296CA2EF00C4EF88 /* DownloadTask.swift in Sources */, 535BAE9F2649E569005FA86D /* ItemView.swift in Sources */, E1E2F8422B757E0900B75998 /* OnFirstAppearModifier.swift in Sources */, @@ -4567,12 +4643,12 @@ E1FE69AA28C29CC20021BC93 /* LandscapePosterProgressBar.swift in Sources */, E1C925F72887504B002A7A66 /* PanDirectionGestureRecognizer.swift in Sources */, E18E01E9288747230022598C /* SeriesItemView.swift in Sources */, - E15756342936851D00976E1F /* NativeVideoPlayerSettingsView.swift in Sources */, E1D4BF7C2719D05000A11E64 /* AppSettingsView.swift in Sources */, 4E16FD512C0183DB00110147 /* LetterPickerButton.swift in Sources */, E19D41AE2BF288320082B8B2 /* ServerCheckViewModel.swift in Sources */, E1BDF2F329524C3B00CC0294 /* ChaptersActionButton.swift in Sources */, E173DA5026D048D600CC4EB7 /* ServerDetailView.swift in Sources */, + 4E23D9572C711E16004B6FE5 /* PlayerControlsSection.swift in Sources */, E1BE1CF02BDB6C97008176A9 /* UserProfileSettingsView.swift in Sources */, E1DC7ACA2C63337C00AEE368 /* iOS15View.swift in Sources */, E1CFE28028FA606800B7D34C /* ChapterTrack.swift in Sources */, diff --git a/Swiftfin/Components/SettingsHelpText.swift b/Swiftfin/Components/SettingsHelpText.swift new file mode 100644 index 000000000..2f209eea5 --- /dev/null +++ b/Swiftfin/Components/SettingsHelpText.swift @@ -0,0 +1,24 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import SwiftUI + +struct SettingsHelpText: View { + var header: String + var bodyText: String + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + Text(header) + .font(.headline) + Text(bodyText) + .font(.body) + } + .padding() + } +} diff --git a/Swiftfin/Views/SettingsView/SettingsView/SettingsView.swift b/Swiftfin/Views/SettingsView/SettingsView/SettingsView.swift index b70eec14c..14c4afcb7 100644 --- a/Swiftfin/Views/SettingsView/SettingsView/SettingsView.swift +++ b/Swiftfin/Views/SettingsView/SettingsView/SettingsView.swift @@ -62,11 +62,6 @@ struct SettingsView: View { selection: $videoPlayerType ) - ChevronButton(L10n.nativePlayer) - .onSelect { - router.route(to: \.nativePlayerSettings) - } - ChevronButton(L10n.videoPlayer) .onSelect { router.route(to: \.videoPlayerSettings) diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift index 05e2f6ee1..a6d079112 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift @@ -11,7 +11,6 @@ import SwiftUI extension VideoPlayerSettingsView { struct ButtonSection: View { - @Default(.VideoPlayer.Overlay.playbackButtonType) private var playbackButtonType @@ -31,8 +30,7 @@ extension VideoPlayerSettingsView { private var router: VideoPlayerSettingsCoordinator.Router var body: some View { - Section(L10n.buttons) { - + Section { CaseIterablePicker(L10n.playbackButtons, selection: $playbackButtonType) Toggle(isOn: $showJumpButtons) { @@ -51,6 +49,10 @@ extension VideoPlayerSettingsView { .onSelect { router.route(to: \.actionButtonSelector, $menuActionButtons) } + } header: { + L10n.buttons.text + } footer: { + L10n.buttonsDescription.text } .onChange(of: barActionButtons) { newValue in autoPlayEnabled = newValue.contains(.autoPlay) || menuActionButtons.contains(.autoPlay) diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift new file mode 100644 index 000000000..f86dfabd6 --- /dev/null +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift @@ -0,0 +1,39 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension VideoPlayerSettingsView { + struct PlayerControlsSection: View { + @Default(.VideoPlayer.jumpBackwardLength) + private var jumpBackwardLength + @Default(.VideoPlayer.jumpForwardLength) + private var jumpForwardLength + + @EnvironmentObject + private var router: VideoPlayerSettingsCoordinator.Router + + var body: some View { + Section { + ChevronButton(L10n.gestures) + .onSelect { + router.route(to: \.gestureSettings) + } + + CaseIterablePicker(L10n.jumpBackwardLength, selection: $jumpBackwardLength) + + CaseIterablePicker(L10n.jumpForwardLength, selection: $jumpForwardLength) + } header: { + L10n.playerControls.text + } footer: { + L10n.playerControlsDescription.text + } + } + } +} diff --git a/Swiftfin/Views/SettingsView/NativeVideoPlayerSettingsView.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift similarity index 61% rename from Swiftfin/Views/SettingsView/NativeVideoPlayerSettingsView.swift rename to Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift index 0f64516c2..b87c57fe6 100644 --- a/Swiftfin/Views/SettingsView/NativeVideoPlayerSettingsView.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ResumeOffsetSection.swift @@ -9,18 +9,15 @@ import Defaults import SwiftUI -struct NativeVideoPlayerSettingsView: View { - - @Default(.VideoPlayer.resumeOffset) - private var resumeOffset - - var body: some View { - Form { +extension VideoPlayerSettingsView { + struct ResumeOffsetSection: View { + @Default(.VideoPlayer.resumeOffset) + private var resumeOffset + var body: some View { Section { - BasicStepper( - title: "Resume Offset", + title: L10n.resumeOffset, value: $resumeOffset, range: 0 ... 30, step: 1 @@ -28,10 +25,12 @@ struct NativeVideoPlayerSettingsView: View { .valueFormatter { $0.secondLabel } - } footer: { - Text("Resume content seconds before the recorded resume time") + } header: { + L10n.resume.text + } + footer: { + L10n.resumeOffsetDescription.text } } - .navigationTitle("Native Player") } } diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift index e70365ad8..d6ff8b404 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift @@ -11,7 +11,6 @@ import SwiftUI extension VideoPlayerSettingsView { struct SliderSection: View { - @Default(.VideoPlayer.Overlay.chapterSlider) private var chapterSlider @@ -22,8 +21,7 @@ extension VideoPlayerSettingsView { private var sliderType var body: some View { - Section(L10n.slider) { - + Section { Toggle(L10n.chapterSlider, isOn: $chapterSlider) ColorPicker(selection: $sliderColor, supportsOpacity: false) { @@ -31,6 +29,10 @@ extension VideoPlayerSettingsView { } CaseIterablePicker(L10n.sliderType, selection: $sliderType) + } header: { + L10n.slider.text + } footer: { + L10n.sliderDescription.text } } } diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift index 10b9a63fd..31fc92b65 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift @@ -39,10 +39,9 @@ extension VideoPlayerSettingsView { Text(L10n.subtitleColor) } } header: { - Text(L10n.subtitle) + L10n.subtitle.text } footer: { - // TODO: better wording - Text("Settings only affect some subtitle types") + L10n.subtitleDescription.text } } } diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift index 3ad3a5479..c2761fad6 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift @@ -11,7 +11,6 @@ import SwiftUI extension VideoPlayerSettingsView { struct TimestampSection: View { - @Default(.VideoPlayer.Overlay.trailingTimestampType) private var trailingTimestampType @Default(.VideoPlayer.Overlay.showCurrentTimeWhileScrubbing) @@ -20,13 +19,16 @@ extension VideoPlayerSettingsView { private var timestampType var body: some View { - Section(L10n.timestamp) { - + Section { Toggle(L10n.scrubCurrentTime, isOn: $showCurrentTimeWhileScrubbing) CaseIterablePicker(L10n.timestampType, selection: $timestampType) CaseIterablePicker(L10n.trailingValue, selection: $trailingTimestampType) + } header: { + L10n.timestamp.text + } footer: { + L10n.timestampDescription.text } } } diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift index 3c307a527..91db16086 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TransitionSection.swift @@ -17,10 +17,14 @@ extension VideoPlayerSettingsView { private var playOnActive var body: some View { - Section(L10n.transition) { - + Section { Toggle(L10n.pauseOnBackground, isOn: $pauseOnBackground) + Toggle(L10n.playOnActive, isOn: $playOnActive) + } header: { + L10n.transition.text + } footer: { + L10n.transitionDescription.text } } } diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift index 885f046a8..8cb18958d 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift @@ -10,53 +10,33 @@ import Defaults import SwiftUI struct VideoPlayerSettingsView: View { - - @Default(.VideoPlayer.jumpBackwardLength) - private var jumpBackwardLength - @Default(.VideoPlayer.jumpForwardLength) - private var jumpForwardLength - @Default(.VideoPlayer.resumeOffset) - private var resumeOffset + @Default(.VideoPlayer.videoPlayerType) + private var videoPlayerType @EnvironmentObject private var router: VideoPlayerSettingsCoordinator.Router var body: some View { Form { + switch videoPlayerType { + case .native: + ResumeOffsetSection() - ChevronButton(L10n.gestures) - .onSelect { - router.route(to: \.gestureSettings) - } - - CaseIterablePicker(L10n.jumpBackwardLength, selection: $jumpBackwardLength) - - CaseIterablePicker(L10n.jumpForwardLength, selection: $jumpForwardLength) + case .swiftfin: + PlayerControlsSection() - Section { + ResumeOffsetSection() - BasicStepper( - title: L10n.resumeOffset, - value: $resumeOffset, - range: 0 ... 30, - step: 1 - ) - .valueFormatter { - $0.secondLabel - } - } footer: { - Text(L10n.resumeOffsetDescription) - } - - ButtonSection() + ButtonSection() - SliderSection() + SliderSection() - SubtitleSection() + SubtitleSection() - TimestampSection() + TimestampSection() - TransitionSection() + TransitionSection() + } } .navigationTitle(L10n.videoPlayer) } diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index 077a43620c6fca2f02238662d28ca507797812df..1639d90b15d4c15c4f3b1fccafbaf4832ce68d29 100644 GIT binary patch delta 1084 zcma)5F>ljg5WRwmpcEnEh7dqt=mx4PB({JA&4B7kAe610oWzc8ISGmr0!Z*5&~4q2 z5CdW;bVewMfsvVonUR$R-uvtiIfVgPiT&Mo_uk#RyU$-2-h5x!`|0iPZbcXLRlK5i zw~pxfWLF2t^8UwU)KlB%R{sa}XT)6cV3g-FH;Hjt%0rk4Ff{@ A?*IS* delta 65 zcmZ4UlJUeX#tpx`CM#t+Oin2gVOCleb06QTUE&u=k From bccc7be6e9c6ae3800f5be43e16ddfba13669fc8 Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Mon, 26 Aug 2024 10:47:14 -0600 Subject: [PATCH 2/8] Remove extra spacing in the VideoPlayerSections. Only include the Router in the Sections where Routing is required. --- .../Components/Sections/ButtonSection.swift | 4 ---- .../Components/Sections/SliderSection.swift | 2 -- Swiftfin.xcodeproj/project.pbxproj | 2 +- .../Components/ActionButtonSelectorView.swift | 1 - .../Components/Sections/ButtonSection.swift | 4 ---- .../Components/Sections/SliderSection.swift | 2 -- .../VideoPlayerSettingsView/VideoPlayerSettingsView.swift | 3 --- 7 files changed, 1 insertion(+), 17 deletions(-) diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift index ed54e4407..e09144cc4 100644 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift @@ -13,16 +13,12 @@ extension VideoPlayerSettingsView { struct ButtonSection: View { @Default(.VideoPlayer.Overlay.playbackButtonType) private var playbackButtonType - @Default(.VideoPlayer.showJumpButtons) private var showJumpButtons - @Default(.VideoPlayer.barActionButtons) private var barActionButtons - @Default(.VideoPlayer.menuActionButtons) private var menuActionButtons - @Default(.VideoPlayer.autoPlayEnabled) private var autoPlayEnabled diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift index c6ca0e1a7..b2fc00af5 100644 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift @@ -13,10 +13,8 @@ extension VideoPlayerSettingsView { struct SliderSection: View { @Default(.VideoPlayer.Overlay.chapterSlider) private var chapterSlider - @Default(.VideoPlayer.Overlay.sliderColor) private var sliderColor - @Default(.VideoPlayer.Overlay.sliderType) private var sliderType diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index afdb456b6..cbb311311 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -1736,8 +1736,8 @@ 4E23D9732C71547F004B6FE5 /* VideoPlayerSettingsView */ = { isa = PBXGroup; children = ( - E1549679296CB4B000C4EF88 /* VideoPlayerSettingsView.swift */, 4E23D9712C71547F004B6FE5 /* Components */, + E1549679296CB4B000C4EF88 /* VideoPlayerSettingsView.swift */, ); path = VideoPlayerSettingsView; sourceTree = ""; diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift index fc92ba9ef..05d598ea2 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift @@ -10,7 +10,6 @@ import Defaults import SwiftUI struct ActionButtonSelectorView: View { - @Binding var selection: [VideoPlayerActionButton] diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift index a6d079112..e5227853b 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift @@ -13,16 +13,12 @@ extension VideoPlayerSettingsView { struct ButtonSection: View { @Default(.VideoPlayer.Overlay.playbackButtonType) private var playbackButtonType - @Default(.VideoPlayer.showJumpButtons) private var showJumpButtons - @Default(.VideoPlayer.barActionButtons) private var barActionButtons - @Default(.VideoPlayer.menuActionButtons) private var menuActionButtons - @Default(.VideoPlayer.autoPlayEnabled) private var autoPlayEnabled diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift index d6ff8b404..fad2f4089 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift @@ -13,10 +13,8 @@ extension VideoPlayerSettingsView { struct SliderSection: View { @Default(.VideoPlayer.Overlay.chapterSlider) private var chapterSlider - @Default(.VideoPlayer.Overlay.sliderColor) private var sliderColor - @Default(.VideoPlayer.Overlay.sliderType) private var sliderType diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift index 8cb18958d..6562461da 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift @@ -13,9 +13,6 @@ struct VideoPlayerSettingsView: View { @Default(.VideoPlayer.videoPlayerType) private var videoPlayerType - @EnvironmentObject - private var router: VideoPlayerSettingsCoordinator.Router - var body: some View { Form { switch videoPlayerType { From bbd841a5c97d5aadcedca50f73c5cf8b321bb6f5 Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Tue, 27 Aug 2024 23:39:23 -0600 Subject: [PATCH 3/8] OriginHash??? I guess just merge back to Main --- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../Sections/CustomizeFilterSection.swift | 54 +++++++++++++ .../Sections/CustomizeItemSection.swift | 64 +++++++++++++++ .../Sections/CustomizeLibrarySection.swift | 80 +++++++++++++++++++ .../Sections/CustomizePosterSection.swift | 59 ++++++++++++++ .../CustomizeViewsSettings.swift | 34 ++++++++ 6 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeFilterSection.swift create mode 100644 Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeItemSection.swift create mode 100644 Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeLibrarySection.swift create mode 100644 Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizePosterSection.swift create mode 100644 Swiftfin/Views/SettingsView/CustomizeViewsSettings/CustomizeViewsSettings.swift diff --git a/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 38f911da8..a201177ee 100644 --- a/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "651194fc1966b57201a0de2cba27dc40798bbdf515febdc83f00d634d916fea4", + "originHash" : "c3c005943de15b4f098b3401fea9ea92a16063e9fb79137ea4cfd816903c993a", "pins" : [ { "identity" : "blurhashkit", diff --git a/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeFilterSection.swift b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeFilterSection.swift new file mode 100644 index 000000000..9fabcf051 --- /dev/null +++ b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeFilterSection.swift @@ -0,0 +1,54 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension CustomizeViewsSettings { + struct CustomizeFilterSection: View { + @Default(.Customization.Library.letterPickerEnabled) + private var letterPickerEnabled + @Default(.Customization.Library.letterPickerOrientation) + private var letterPickerOrientation + @Default(.Customization.Library.enabledDrawerFilters) + private var libraryEnabledDrawerFilters + @Default(.Customization.Search.enabledDrawerFilters) + private var searchEnabledDrawerFilters + + @EnvironmentObject + private var router: SettingsCoordinator.Router + + var body: some View { + Section { + Toggle(L10n.letterPicker, isOn: $letterPickerEnabled) + + if letterPickerEnabled { + CaseIterablePicker( + L10n.orientation, + selection: $letterPickerOrientation + ) + } + + ChevronButton(L10n.library) + .onSelect { + router.route(to: \.itemFilterDrawerSelector, $libraryEnabledDrawerFilters) + } + + ChevronButton(L10n.search) + .onSelect { + router.route(to: \.itemFilterDrawerSelector, $searchEnabledDrawerFilters) + } + + } header: { + L10n.filters.text + } footer: { + L10n.filters.text + } + } + } +} diff --git a/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeItemSection.swift b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeItemSection.swift new file mode 100644 index 000000000..44cd5b533 --- /dev/null +++ b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeItemSection.swift @@ -0,0 +1,64 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension CustomizeViewsSettings { + struct CustomizeItemSection: View { + @Default(.Customization.itemViewType) + private var itemViewType + @Default(.Customization.CinematicItemViewType.usePrimaryImage) + private var cinematicItemViewTypeUsePrimaryImage + + @Default(.Customization.Episodes.useSeriesLandscapeBackdrop) + private var useSeriesLandscapeBackdrop + + @Default(.Customization.shouldShowMissingSeasons) + private var shouldShowMissingSeasons + @Default(.Customization.shouldShowMissingEpisodes) + private var shouldShowMissingEpisodes + + var body: some View { + if UIDevice.isPhone { + Section { + CaseIterablePicker(L10n.items, selection: $itemViewType) + } header: { + Text("Media Items") + } footer: { + // L10n.itemsDescription.text + } + + if itemViewType == .cinematic { + Section { + Toggle(L10n.usePrimaryImage, isOn: $cinematicItemViewTypeUsePrimaryImage) + } footer: { + L10n.usePrimaryImageDescription.text + } + } + } + + Section { + Toggle(L10n.seriesBackdrop, isOn: $useSeriesLandscapeBackdrop) + } footer: { + // TODO: think of a better name + L10n.episodeLandscapePoster.text + } + + Section { + Toggle(L10n.showMissingSeasons, isOn: $shouldShowMissingSeasons) + + Toggle(L10n.showMissingEpisodes, isOn: $shouldShowMissingEpisodes) + } header: { + L10n.missingItems.text + } footer: { + // L10n.missingItemsDescription.text + } + } + } +} diff --git a/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeLibrarySection.swift b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeLibrarySection.swift new file mode 100644 index 000000000..e1b65e870 --- /dev/null +++ b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizeLibrarySection.swift @@ -0,0 +1,80 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension CustomizeViewsSettings { + struct CustomizeLibrarySection: View { + @Default(.Customization.Library.cinematicBackground) + private var cinematicBackground + @Default(.Customization.Library.randomImage) + private var libraryRandomImage + @Default(.Customization.Library.showFavorites) + private var showFavorites + @Default(.Customization.showRecentlyAdded) + private var showRecentlyAdded + + @Default(.Customization.Library.displayType) + private var libraryDisplayType + @Default(.Customization.Library.posterType) + private var libraryPosterType + @Default(.Customization.Library.listColumnCount) + private var listColumnCount + + @Default(.Customization.Library.rememberLayout) + private var rememberLibraryLayout + @Default(.Customization.Library.rememberSort) + private var rememberLibrarySort + + var body: some View { + Section { + Toggle(L10n.cinematicBackground, isOn: $cinematicBackground) + + Toggle(L10n.randomImage, isOn: $libraryRandomImage) + + Toggle(L10n.showFavorites, isOn: $showFavorites) + + Toggle(L10n.showRecentlyAdded, isOn: $showRecentlyAdded) + } header: { + L10n.library.text + } footer: { + // L10n.libraryDescription.text + } + + Section { + CaseIterablePicker(L10n.library, selection: $libraryDisplayType) + + CaseIterablePicker(L10n.posters, selection: $libraryPosterType) + + if libraryDisplayType == .list, UIDevice.isPad { + BasicStepper( + title: "Columns", + value: $listColumnCount, + range: 1 ... 4, + step: 1 + ) + } + } footer: { + Text("Customize The library layouts") // L10n.libraryDescription.text + } + + Section { + Toggle("Remember layout", isOn: $rememberLibraryLayout) + } footer: { + Text("Remember layout for individual libraries") + } + + Section { + Toggle("Remember sorting", isOn: $rememberLibrarySort) + } footer: { + Text("Remember sorting for individual libraries") + } + } + } +} diff --git a/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizePosterSection.swift b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizePosterSection.swift new file mode 100644 index 000000000..5f2523cb1 --- /dev/null +++ b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/CustomizePosterSection.swift @@ -0,0 +1,59 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +extension CustomizeViewsSettings { + struct CustomizePosterSection: View { + @Default(.Customization.showPosterLabels) + private var showPosterLabels + @Default(.Customization.nextUpPosterType) + private var nextUpPosterType + @Default(.Customization.recentlyAddedPosterType) + private var recentlyAddedPosterType + @Default(.Customization.latestInLibraryPosterType) + private var latestInLibraryPosterType + @Default(.Customization.similarPosterType) + private var similarPosterType + @Default(.Customization.searchPosterType) + private var searchPosterType + @Default(.Customization.Library.displayType) + private var libraryViewType + + @EnvironmentObject + private var router: SettingsCoordinator.Router + + var body: some View { + Section { + ChevronButton(L10n.indicators) + .onSelect { + router.route(to: \.indicatorSettings) + } + + Toggle(L10n.showPosterLabels, isOn: $showPosterLabels) + + CaseIterablePicker(L10n.next, selection: $nextUpPosterType) + + CaseIterablePicker(L10n.recentlyAdded, selection: $recentlyAddedPosterType) + + CaseIterablePicker(L10n.latestWithString(L10n.library), selection: $latestInLibraryPosterType) + + CaseIterablePicker(L10n.recommended, selection: $similarPosterType) + + CaseIterablePicker(L10n.search, selection: $searchPosterType) + + CaseIterablePicker(L10n.library, selection: $libraryViewType) + } header: { + L10n.posters.text + } footer: { + // L10n.postersDescription.text + } + } + } +} diff --git a/Swiftfin/Views/SettingsView/CustomizeViewsSettings/CustomizeViewsSettings.swift b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/CustomizeViewsSettings.swift new file mode 100644 index 000000000..a1c01b0e1 --- /dev/null +++ b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/CustomizeViewsSettings.swift @@ -0,0 +1,34 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Defaults +import SwiftUI + +// TODO: will be entirely re-organized + +struct CustomizeViewsSettings: View { + @Default(.Customization.showRecentlyAdded) + private var showRecentlyAdded + + var body: some View { + List { + CustomizeItemSection() + + CustomizeFilterSection() + + CustomizePosterSection() + + CustomizeLibrarySection() + + Section("Home") { + Toggle("Show recently added", isOn: $showRecentlyAdded) + } + } + .navigationTitle(L10n.customize) + } +} From f0396541f4519564f55171f4c7434ad1f2807a4e Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Tue, 27 Aug 2024 23:46:08 -0600 Subject: [PATCH 4/8] Remove Unwanted Sections from tvOS. Should now mirrow iOS on all SHARED elements. --- .../Components/Sections/ButtonSection.swift | 56 ------------------- .../Sections/PlayerControlsSection.swift | 2 - .../Components/Sections/SliderSection.swift | 11 ---- .../Components/Sections/SubtitleSection.swift | 8 --- .../Sections/TimestampSection.swift | 35 ------------ .../VideoPlayerSettingsView.swift | 7 +-- Swiftfin.xcodeproj/project.pbxproj | 8 --- 7 files changed, 1 insertion(+), 126 deletions(-) delete mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift delete mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift deleted file mode 100644 index e09144cc4..000000000 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/ButtonSection.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// Swiftfin is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, you can obtain one at https://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2024 Jellyfin & Jellyfin Contributors -// - -import Defaults -import SwiftUI - -extension VideoPlayerSettingsView { - struct ButtonSection: View { - @Default(.VideoPlayer.Overlay.playbackButtonType) - private var playbackButtonType - @Default(.VideoPlayer.showJumpButtons) - private var showJumpButtons - @Default(.VideoPlayer.barActionButtons) - private var barActionButtons - @Default(.VideoPlayer.menuActionButtons) - private var menuActionButtons - @Default(.VideoPlayer.autoPlayEnabled) - private var autoPlayEnabled - - @EnvironmentObject - private var router: VideoPlayerSettingsCoordinator.Router - - var body: some View { - Section { - InlineEnumToggle(title: L10n.playbackButtons, selection: $playbackButtonType) - - Toggle(L10n.jump, isOn: $showJumpButtons) - - ChevronButton(L10n.barButtons) - .onSelect { - router.route(to: \.actionButtonSelector, $barActionButtons) - } - - ChevronButton(L10n.menuButtons) - .onSelect { - router.route(to: \.actionButtonSelector, $menuActionButtons) - } - } header: { - L10n.buttons.text - } footer: { - L10n.buttonsDescription.text - } - .onChange(of: barActionButtons) { _, newValue in - autoPlayEnabled = newValue.contains(.autoPlay) || menuActionButtons.contains(.autoPlay) - } - .onChange(of: menuActionButtons) { _, newValue in - autoPlayEnabled = newValue.contains(.autoPlay) || barActionButtons.contains(.autoPlay) - } - } - } -} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift index 9ace45ced..55ec44b95 100644 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/PlayerControlsSection.swift @@ -21,8 +21,6 @@ extension VideoPlayerSettingsView { var body: some View { Section { - // Gestures have been removes as tvOS does not have touch gestures - InlineEnumToggle(title: L10n.jumpBackwardLength, selection: $jumpBackwardLength) InlineEnumToggle(title: L10n.jumpForwardLength, selection: $jumpForwardLength) diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift index b2fc00af5..39a334dec 100644 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SliderSection.swift @@ -13,21 +13,10 @@ extension VideoPlayerSettingsView { struct SliderSection: View { @Default(.VideoPlayer.Overlay.chapterSlider) private var chapterSlider - @Default(.VideoPlayer.Overlay.sliderColor) - private var sliderColor - @Default(.VideoPlayer.Overlay.sliderType) - private var sliderType var body: some View { Section { Toggle(L10n.chapterSlider, isOn: $chapterSlider) - - // Commenting since ColorPicker is not around on tvOS. May need to be recreated manually? - /* ColorPicker(selection: $sliderColor, supportsOpacity: false) { - Text(L10n.sliderColor) - } */ - - InlineEnumToggle(title: L10n.sliderType, selection: $sliderType) } header: { L10n.slider.text } footer: { diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift index 21adf0a09..e5519038f 100644 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift @@ -15,25 +15,17 @@ extension VideoPlayerSettingsView { private var subtitleFontName @Default(.VideoPlayer.Subtitle.subtitleSize) private var subtitleSize - @Default(.VideoPlayer.Subtitle.subtitleColor) - private var subtitleColor @EnvironmentObject private var router: VideoPlayerSettingsCoordinator.Router var body: some View { Section { - ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName) .onSelect { router.route(to: \.fontPicker, $subtitleFontName) } - // Leaving this comment since this isn't tvOS compatible. Just in case we want this later. - /* ColorPicker(selection: $subtitleColor) { - Text(L10n.subtitleColor) - } */ - ChevronButton( L10n.subtitleSize, subtitle: subtitleSize.description diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift deleted file mode 100644 index 74fc96c51..000000000 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/TimestampSection.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Swiftfin is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, you can obtain one at https://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2024 Jellyfin & Jellyfin Contributors -// - -import Defaults -import SwiftUI - -extension VideoPlayerSettingsView { - struct TimestampSection: View { - @Default(.VideoPlayer.Overlay.trailingTimestampType) - private var trailingTimestampType - @Default(.VideoPlayer.Overlay.showCurrentTimeWhileScrubbing) - private var showCurrentTimeWhileScrubbing - @Default(.VideoPlayer.Overlay.timestampType) - private var timestampType - - var body: some View { - Section { - Toggle(L10n.scrubCurrentTime, isOn: $showCurrentTimeWhileScrubbing) - - InlineEnumToggle(title: L10n.timestampType, selection: $timestampType) - - InlineEnumToggle(title: L10n.trailingValue, selection: $trailingTimestampType) - } header: { - L10n.timestamp.text - } footer: { - L10n.timestampDescription.text - } - } - } -} diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift index 8fecaeb75..40b5bf5e2 100644 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/VideoPlayerSettingsView.swift @@ -30,18 +30,13 @@ struct VideoPlayerSettingsView: View { ResumeOffsetSection() case .swiftfin: - // Not currently available for tvOS PlayerControlsSection() ResumeOffsetSection() - // Not currently available for tvOS - ButtonSection() - // Not currently available for tvOS + SliderSection() SubtitleSection() - // Not currently available for tvOS - TimestampSection() TransitionSection() } diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index cbb311311..ba50abbe9 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -19,12 +19,10 @@ 4E23D9762C71547F004B6FE5 /* ResumeOffsetSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D96A2C71547F004B6FE5 /* ResumeOffsetSection.swift */; }; 4E23D9842C71B81F004B6FE5 /* ResumeOffsetPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9832C71B81F004B6FE5 /* ResumeOffsetPickerView.swift */; }; 4E23D9862C71C006004B6FE5 /* SubtitleSizePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9852C71C006004B6FE5 /* SubtitleSizePickerView.swift */; }; - 4E23D98F2C71C8CC004B6FE5 /* ButtonSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9872C71C8CC004B6FE5 /* ButtonSection.swift */; }; 4E23D9902C71C8CC004B6FE5 /* PlayerControlsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9882C71C8CC004B6FE5 /* PlayerControlsSection.swift */; }; 4E23D9912C71C8CC004B6FE5 /* ResumeOffsetSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9892C71C8CC004B6FE5 /* ResumeOffsetSection.swift */; }; 4E23D9922C71C8CC004B6FE5 /* SliderSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */; }; 4E23D9932C71C8CC004B6FE5 /* SubtitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */; }; - 4E23D9942C71C8CC004B6FE5 /* TimestampSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98C2C71C8CC004B6FE5 /* TimestampSection.swift */; }; 4E23D9952C71C8CC004B6FE5 /* TransitionSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */; }; 4E23D9972C72C08C004B6FE5 /* ActionButtonSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */; }; 4E23D9992C72C108004B6FE5 /* OrderedSectionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */; }; @@ -956,12 +954,10 @@ 4E23D96A2C71547F004B6FE5 /* ResumeOffsetSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResumeOffsetSection.swift; sourceTree = ""; }; 4E23D9832C71B81F004B6FE5 /* ResumeOffsetPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResumeOffsetPickerView.swift; sourceTree = ""; }; 4E23D9852C71C006004B6FE5 /* SubtitleSizePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubtitleSizePickerView.swift; sourceTree = ""; }; - 4E23D9872C71C8CC004B6FE5 /* ButtonSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonSection.swift; sourceTree = ""; }; 4E23D9882C71C8CC004B6FE5 /* PlayerControlsSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerControlsSection.swift; sourceTree = ""; }; 4E23D9892C71C8CC004B6FE5 /* ResumeOffsetSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResumeOffsetSection.swift; sourceTree = ""; }; 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderSection.swift; sourceTree = ""; }; 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubtitleSection.swift; sourceTree = ""; }; - 4E23D98C2C71C8CC004B6FE5 /* TimestampSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimestampSection.swift; sourceTree = ""; }; 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionSection.swift; sourceTree = ""; }; 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtonSelectorView.swift; sourceTree = ""; }; 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSectionSelectorView.swift; sourceTree = ""; }; @@ -1712,12 +1708,10 @@ 4E23D96F2C71547F004B6FE5 /* Sections */ = { isa = PBXGroup; children = ( - 4E23D9872C71C8CC004B6FE5 /* ButtonSection.swift */, 4E23D9882C71C8CC004B6FE5 /* PlayerControlsSection.swift */, 4E23D9892C71C8CC004B6FE5 /* ResumeOffsetSection.swift */, 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */, 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */, - 4E23D98C2C71C8CC004B6FE5 /* TimestampSection.swift */, 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */, 4E23D96A2C71547F004B6FE5 /* ResumeOffsetSection.swift */, ); @@ -4182,7 +4176,6 @@ E19D41B42BF2C0020082B8B2 /* StoredValues+Temp.swift in Sources */, E11BDF7B2B85529D0045C54A /* SupportedCaseIterable.swift in Sources */, 4E204E592C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift in Sources */, - 4E23D9942C71C8CC004B6FE5 /* TimestampSection.swift in Sources */, E1575E8C293E7B1E001665B1 /* UIScreen.swift in Sources */, C46DD8EC2A8FB49A0046A504 /* LiveMainOverlay.swift in Sources */, E1BCDB502BE1F491009F6744 /* ResetUserPasswordViewModel.swift in Sources */, @@ -4309,7 +4302,6 @@ E14EDECD2B8FB709000F00A4 /* ItemYear.swift in Sources */, E154965F296CA2EF00C4EF88 /* DownloadTask.swift in Sources */, E154967E296CCB6C00C4EF88 /* BasicNavigationCoordinator.swift in Sources */, - 4E23D98F2C71C8CC004B6FE5 /* ButtonSection.swift in Sources */, E1B90C8A2BC475E7007027C8 /* ScalingButtonStyle.swift in Sources */, E1DABAFE2A27B982008AC34A /* RatingsCard.swift in Sources */, E1C9261B288756BD002A7A66 /* DotHStack.swift in Sources */, From bf4b88aba762f8b6023f07c9d035f7907c8a8856 Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Tue, 27 Aug 2024 23:50:31 -0600 Subject: [PATCH 5/8] Remove ActionButtonSelectorView for tvOS since the Bar/Menu Buttons are no longer in tvOS --- .../VideoPlayerSettingsCoordinator.swift | 6 ---- .../Components/ActionButtonSelectorView.swift | 33 ------------------- Swiftfin.xcodeproj/project.pbxproj | 4 --- 3 files changed, 43 deletions(-) delete mode 100644 Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift diff --git a/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift b/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift index ea347a6f3..d214ce9b7 100644 --- a/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift +++ b/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift @@ -18,8 +18,6 @@ final class VideoPlayerSettingsCoordinator: NavigationCoordinatable { var start = makeStart @Route(.push) var fontPicker = makeFontPicker - @Route(.push) - var actionButtonSelector = makeActionButtonSelector #if os(tvOS) @Route(.push) @@ -35,10 +33,6 @@ final class VideoPlayerSettingsCoordinator: NavigationCoordinatable { FontPickerView(selection: selection) } - func makeActionButtonSelector(selectedButtonsBinding: Binding<[VideoPlayerActionButton]>) -> some View { - ActionButtonSelectorView(selection: selectedButtonsBinding) - } - #if os(tvOS) func makeResumeOffset(selection: Binding) -> some View { diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift deleted file mode 100644 index c3623b867..000000000 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/ActionButtonSelectorView.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Swiftfin is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, you can obtain one at https://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2024 Jellyfin & Jellyfin Contributors -// - -import Defaults -import SwiftUI - -struct ActionButtonSelectorView: View { - - @Binding - var selection: [VideoPlayerActionButton] - - var body: some View { - OrderedSectionSelectorView( - title: L10n.playbackButtons, - selection: $selection, - sources: VideoPlayerActionButton.allCases, - image: Image(systemName: "ellipsis.rectangle") - ) - .label { button in - AnyView( - HStack { - Image(systemName: button.settingsSystemImage) - Text(button.displayTitle) - } - ) - } - } -} diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index ba50abbe9..f34422f26 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ 4E23D9922C71C8CC004B6FE5 /* SliderSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */; }; 4E23D9932C71C8CC004B6FE5 /* SubtitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */; }; 4E23D9952C71C8CC004B6FE5 /* TransitionSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */; }; - 4E23D9972C72C08C004B6FE5 /* ActionButtonSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */; }; 4E23D9992C72C108004B6FE5 /* OrderedSectionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */; }; 4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; }; 4E73E2A62C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */; }; @@ -959,7 +958,6 @@ 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderSection.swift; sourceTree = ""; }; 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubtitleSection.swift; sourceTree = ""; }; 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionSection.swift; sourceTree = ""; }; - 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtonSelectorView.swift; sourceTree = ""; }; 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSectionSelectorView.swift; sourceTree = ""; }; 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = ""; }; 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackBitrateTestSize.swift; sourceTree = ""; }; @@ -1721,7 +1719,6 @@ 4E23D9712C71547F004B6FE5 /* Components */ = { isa = PBXGroup; children = ( - 4E23D9962C72C08C004B6FE5 /* ActionButtonSelectorView.swift */, 4E23D96F2C71547F004B6FE5 /* Sections */, ); path = Components; @@ -4133,7 +4130,6 @@ E1356E0429A731EB00382563 /* SeparatorHStack.swift in Sources */, E1575E69293E77B5001665B1 /* ItemSortBy.swift in Sources */, E1B490482967E2E500D3EDCE /* CoreStore.swift in Sources */, - 4E23D9972C72C08C004B6FE5 /* ActionButtonSelectorView.swift in Sources */, E1DC9845296DECB600982F06 /* ProgressIndicator.swift in Sources */, E1C925F928875647002A7A66 /* LatestInLibraryView.swift in Sources */, E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */, From 2184be267a0264e1d004556fb4d2765ffd42a5be Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Wed, 28 Aug 2024 00:15:46 -0600 Subject: [PATCH 6/8] Delete OrderedSectionSelectorView since this isn't required for this PR. Will likely re-introduce this at a later time based on need. --- .../OrderedSectionSelectorView.swift | 201 ------------------ Swiftfin.xcodeproj/project.pbxproj | 4 - 2 files changed, 205 deletions(-) delete mode 100644 Swiftfin tvOS/Components/OrderedSectionSelectorView.swift diff --git a/Swiftfin tvOS/Components/OrderedSectionSelectorView.swift b/Swiftfin tvOS/Components/OrderedSectionSelectorView.swift deleted file mode 100644 index 5fa42ed06..000000000 --- a/Swiftfin tvOS/Components/OrderedSectionSelectorView.swift +++ /dev/null @@ -1,201 +0,0 @@ -// -// Swiftfin is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, you can obtain one at https://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2024 Jellyfin & Jellyfin Contributors -// - -import Factory -import SwiftUI - -struct OrderedSectionSelectorView: View { - @Environment(\.editMode) - private var editMode - - @Binding - private var selection: [Element] - - @State - private var updateSelection: [Element] - - @State - private var focusedElement: Element? - - private var disabledSelection: [Element] { - sources.filter { !updateSelection.contains($0) } - } - - private var label: (Element) -> AnyView - private let sources: [Element] - private let image: Image - private let title: String - - private func move(from source: IndexSet, to destination: Int) { - updateSelection.move(fromOffsets: source, toOffset: destination) - editMode?.wrappedValue = .inactive - } - - private func select(element: Element) { - if updateSelection.contains(element) { - updateSelection.removeAll(where: { $0 == element }) - } else { - updateSelection.append(element) - } - } - - var body: some View { - NavigationView { - SplitFormWindowView() - .descriptionView { - image - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 400) - } - .contentView { - List { - EnabledSection( - elements: $updateSelection, - label: label, - isEditing: editMode?.wrappedValue.isEditing ?? false, - select: select, - move: move, - focusedElement: $focusedElement - ) - - DisabledSection( - elements: disabledSelection, - label: label, - isEditing: editMode?.wrappedValue.isEditing ?? false, - select: select, - focusedElement: $focusedElement - ) - } - .environment(\.editMode, editMode) - } - .withDescriptionTopPadding() - .navigationTitle(title) - .animation(.linear(duration: 0.2), value: updateSelection) - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button(editMode?.wrappedValue.isEditing ?? false ? "Done" : "Edit") { - withAnimation { - if editMode?.wrappedValue.isEditing ?? false { - editMode?.wrappedValue = .inactive - } else { - editMode?.wrappedValue = .active - } - } - } - } - } - .onChange(of: updateSelection) { _, newValue in - selection = newValue - } - } - } -} - -private struct EnabledSection: View { - - @Binding - var elements: [Element] - - let label: (Element) -> AnyView - let isEditing: Bool - let select: (Element) -> Void - let move: (IndexSet, Int) -> Void - - @Binding - var focusedElement: Element? - - var body: some View { - Section(L10n.enabled) { - if elements.isEmpty { - Text(L10n.none) - .foregroundStyle(.secondary) - } - - ForEach(elements, id: \.self) { element in - Button { - if !isEditing { - select(element) - } - } label: { - HStack { - label(element) - - Spacer() - - if !isEditing { - Image(systemName: "minus.circle.fill") - .foregroundColor(.red) - } - } - .foregroundColor(.primary) - } - } - .onMove(perform: move) - } - } -} - -private struct DisabledSection: View { - - let elements: [Element] - - let label: (Element) -> AnyView - let isEditing: Bool - let select: (Element) -> Void - - @Binding - var focusedElement: Element? - - var body: some View { - Section(L10n.disabled) { - if elements.isEmpty { - Text(L10n.none) - .foregroundStyle(.secondary) - } - - ForEach(elements, id: \.self) { element in - Button { - if !isEditing { - select(element) - } - } label: { - HStack { - label(element) - - Spacer() - - if !isEditing { - Image(systemName: "plus.circle.fill") - .foregroundColor(.green) - } - } - .foregroundColor(.primary) - } - } - } - } -} - -extension OrderedSectionSelectorView { - - init(title: String, selection: Binding<[Element]>, sources: [Element], image: Image = Image(systemName: "filemenu.and.selection")) { - self.title = title - self._selection = selection - self._updateSelection = State(initialValue: selection.wrappedValue) - self.sources = sources - self.label = { Text($0.displayTitle).foregroundColor(.primary).eraseToAnyView() } - self.image = image - } - - func label(@ViewBuilder _ content: @escaping (Element) -> AnyView) -> Self { - var copy = self - copy.label = content - return copy - } -} diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index f34422f26..3c0f4236a 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ 4E23D9922C71C8CC004B6FE5 /* SliderSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */; }; 4E23D9932C71C8CC004B6FE5 /* SubtitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */; }; 4E23D9952C71C8CC004B6FE5 /* TransitionSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */; }; - 4E23D9992C72C108004B6FE5 /* OrderedSectionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */; }; 4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; }; 4E73E2A62C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */; }; 4E73E2A72C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */; }; @@ -958,7 +957,6 @@ 4E23D98A2C71C8CC004B6FE5 /* SliderSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderSection.swift; sourceTree = ""; }; 4E23D98B2C71C8CC004B6FE5 /* SubtitleSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubtitleSection.swift; sourceTree = ""; }; 4E23D98D2C71C8CC004B6FE5 /* TransitionSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionSection.swift; sourceTree = ""; }; - 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSectionSelectorView.swift; sourceTree = ""; }; 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = ""; }; 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackBitrateTestSize.swift; sourceTree = ""; }; 4E73E2AD2C420207002D2A78 /* MaximumBitrateSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaximumBitrateSettingsView.swift; sourceTree = ""; }; @@ -1920,7 +1918,6 @@ E1A42E5028CBE44500A14DCB /* LandscapePosterProgressBar.swift */, E1763A632BF3C9AA004DF6AB /* ListRowButton.swift */, E10E842B29A589860064EA49 /* NonePosterButton.swift */, - 4E23D9982C72C108004B6FE5 /* OrderedSectionSelectorView.swift */, E111D8F928D0400900400001 /* PagingLibraryView.swift */, E1C92617288756BD002A7A66 /* PosterButton.swift */, E1C92619288756BD002A7A66 /* PosterHStack.swift */, @@ -4214,7 +4211,6 @@ E12CC1AF28D0FAEA00678D5D /* NextUpLibraryViewModel.swift in Sources */, E1575E7A293E77B5001665B1 /* TimeStampType.swift in Sources */, E1763A252BF2F77B004DF6AB /* ScrollIfLargerThanContainerModifier.swift in Sources */, - 4E23D9992C72C108004B6FE5 /* OrderedSectionSelectorView.swift in Sources */, E11E374E293E7F08009EF240 /* MediaSourceInfo.swift in Sources */, E1E1643A28BAC2EF00323B0A /* SearchView.swift in Sources */, E1763A642BF3C9AA004DF6AB /* ListRowButton.swift in Sources */, From b7f7e5161f96e1938613422a2c331d5ba40d5715 Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Wed, 28 Aug 2024 00:19:54 -0600 Subject: [PATCH 7/8] Remove WIP / Removed Items from iOS. --- Swiftfin/Components/SettingsHelpText.swift | 24 ---------------------- 1 file changed, 24 deletions(-) delete mode 100644 Swiftfin/Components/SettingsHelpText.swift diff --git a/Swiftfin/Components/SettingsHelpText.swift b/Swiftfin/Components/SettingsHelpText.swift deleted file mode 100644 index 2f209eea5..000000000 --- a/Swiftfin/Components/SettingsHelpText.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Swiftfin is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, you can obtain one at https://mozilla.org/MPL/2.0/. -// -// Copyright (c) 2024 Jellyfin & Jellyfin Contributors -// - -import SwiftUI - -struct SettingsHelpText: View { - var header: String - var bodyText: String - - var body: some View { - VStack(alignment: .leading, spacing: 10) { - Text(header) - .font(.headline) - Text(bodyText) - .font(.body) - } - .padding() - } -} From f97f4477e52039dbf2bf6b121af2d16bde02a352 Mon Sep 17 00:00:00 2001 From: Joseph Kribs Date: Wed, 28 Aug 2024 00:30:10 -0600 Subject: [PATCH 8/8] Cleanup/Revert Unwanted Changes --- .../VideoPlayerSettingsCoordinator.swift | 6 ++++++ Shared/Strings/Strings.swift | 4 +++- .../Components/Sections/SubtitleSection.swift | 2 +- .../Components/Sections/SubtitleSection.swift | 2 +- Translations/en.lproj/Localizable.strings | Bin 29868 -> 30170 bytes 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift b/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift index d214ce9b7..fbcba2096 100644 --- a/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift +++ b/Shared/Coordinators/VideoPlayerSettingsCoordinator.swift @@ -27,6 +27,8 @@ final class VideoPlayerSettingsCoordinator: NavigationCoordinatable { #elseif os(iOS) @Route(.push) var gestureSettings = makeGestureSettings + @Route(.push) + var actionButtonSelector = makeActionButtonSelector #endif func makeFontPicker(selection: Binding) -> some View { @@ -49,6 +51,10 @@ final class VideoPlayerSettingsCoordinator: NavigationCoordinatable { func makeGestureSettings() -> some View { GestureSettingsView() } + + func makeActionButtonSelector(selectedButtonsBinding: Binding<[VideoPlayerActionButton]>) -> some View { + ActionButtonSelectorView(selection: selectedButtonsBinding) + } #endif @ViewBuilder diff --git a/Shared/Strings/Strings.swift b/Shared/Strings/Strings.swift index 9ed81187e..407372891 100644 --- a/Shared/Strings/Strings.swift +++ b/Shared/Strings/Strings.swift @@ -631,7 +631,9 @@ internal enum L10n { /// Subtitle Color internal static let subtitleColor = L10n.tr("Localizable", "subtitleColor", fallback: "Subtitle Color") /// Customize text-based subtitles font and color - internal static let subtitleDescription = L10n.tr("Localizable", "subtitleDescription", fallback: "Customize text-based subtitles font and color") + internal static let subtitleDescriptionIOS = L10n.tr("Localizable", "subtitleDescriptionIOS", fallback: "Customize text-based subtitles font and color") + /// Customize text-based subtitles font + internal static let subtitleDescriptionTVOS = L10n.tr("Localizable", "subtitleDescriptionTVOS", fallback: "Customize text-based subtitles font") /// Subtitle Font internal static let subtitleFont = L10n.tr("Localizable", "subtitleFont", fallback: "Subtitle Font") /// Subtitle Offset diff --git a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift index e5519038f..9c9a375ee 100644 --- a/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift +++ b/Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift @@ -36,7 +36,7 @@ extension VideoPlayerSettingsView { } header: { L10n.subtitle.text } footer: { - L10n.subtitleDescription.text + L10n.subtitleDescriptionTVOS.text } } } diff --git a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift index 31fc92b65..8e09748dd 100644 --- a/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift +++ b/Swiftfin/Views/SettingsView/VideoPlayerSettingsView/Components/Sections/SubtitleSection.swift @@ -41,7 +41,7 @@ extension VideoPlayerSettingsView { } header: { L10n.subtitle.text } footer: { - L10n.subtitleDescription.text + L10n.subtitleDescriptionIOS.text } } } diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index 1639d90b15d4c15c4f3b1fccafbaf4832ce68d29..47dc52c6988014aeaacb51e201e86317755e812a 100644 GIT binary patch delta 84 zcmZ4UlJVAS#tjLDlWY7qSTh;?8GQk#+=