[tvOS] Settings Cleanup (#1163)

* Settings Cleanup. Replace strings with labels. Enforce the same font. Ensure Forms don't get clipped by their boundries. Create consistent, reusable button sizing/coloring. Apply to all Settings Pages.

* Remove custom Button/Form styling in exchange for just using .scrollClipDisabled()

* Swap back to Jellyfin Purple from Purple.

* Remove Check Button. Check all Section Inits where possible. Make Server Details Server non-focusable.

Create a new menu for Server Details selection. This is a WIP awaiting feedback from https://github.com/jellyfin/Swiftfin/pull/1163#discussion_r1705957885

---------

Co-authored-by: Joseph Kribs <joseph@kribs.net>
This commit is contained in:
Joe 2024-08-06 20:56:24 -06:00 committed by GitHub
parent 0ae1324534
commit e4fd98c244
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 147 additions and 90 deletions

View File

@ -19,6 +19,7 @@ struct TextPairView: View {
var body: some View { var body: some View {
HStack { HStack {
Text(leading) Text(leading)
.foregroundColor(.primary)
Spacer() Spacer()

View File

@ -18,6 +18,8 @@ internal enum L10n {
internal static let accentColorDescription = L10n.tr("Localizable", "accentColorDescription", fallback: "Some views may need an app restart to update.") internal static let accentColorDescription = L10n.tr("Localizable", "accentColorDescription", fallback: "Some views may need an app restart to update.")
/// Accessibility /// Accessibility
internal static let accessibility = L10n.tr("Localizable", "accessibility", fallback: "Accessibility") internal static let accessibility = L10n.tr("Localizable", "accessibility", fallback: "Accessibility")
/// Add Server
internal static let addServer = L10n.tr("Localizable", "addServer", fallback: "Add Server")
/// Add URL /// Add URL
internal static let addURL = L10n.tr("Localizable", "addURL", fallback: "Add URL") internal static let addURL = L10n.tr("Localizable", "addURL", fallback: "Add URL")
/// Advanced /// Advanced
@ -30,6 +32,8 @@ internal enum L10n {
internal static let allGenres = L10n.tr("Localizable", "allGenres", fallback: "All Genres") internal static let allGenres = L10n.tr("Localizable", "allGenres", fallback: "All Genres")
/// All Media /// All Media
internal static let allMedia = L10n.tr("Localizable", "allMedia", fallback: "All Media") internal static let allMedia = L10n.tr("Localizable", "allMedia", fallback: "All Media")
/// All Servers
internal static let allServers = L10n.tr("Localizable", "allServers", fallback: "All Servers")
/// Appearance /// Appearance
internal static let appearance = L10n.tr("Localizable", "appearance", fallback: "Appearance") internal static let appearance = L10n.tr("Localizable", "appearance", fallback: "Appearance")
/// App Icon /// App Icon
@ -112,6 +116,8 @@ internal enum L10n {
internal static let chapterSlider = L10n.tr("Localizable", "chapterSlider", fallback: "Chapter Slider") internal static let chapterSlider = L10n.tr("Localizable", "chapterSlider", fallback: "Chapter Slider")
/// Cinematic /// Cinematic
internal static let cinematic = L10n.tr("Localizable", "cinematic", fallback: "Cinematic") internal static let cinematic = L10n.tr("Localizable", "cinematic", fallback: "Cinematic")
/// Cinematic Background
internal static let cinematicBackground = L10n.tr("Localizable", "cinematicBackground", fallback: "Cinematic Background")
/// Cinematic Views /// Cinematic Views
internal static let cinematicViews = L10n.tr("Localizable", "cinematicViews", fallback: "Cinematic Views") internal static let cinematicViews = L10n.tr("Localizable", "cinematicViews", fallback: "Cinematic Views")
/// Close /// Close
@ -160,6 +166,10 @@ internal enum L10n {
internal static let dark = L10n.tr("Localizable", "dark", fallback: "Dark") internal static let dark = L10n.tr("Localizable", "dark", fallback: "Dark")
/// Default Scheme /// Default Scheme
internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme") internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme")
/// Delete
internal static let delete = L10n.tr("Localizable", "delete", fallback: "Delete")
/// Delete Server
internal static let deleteServer = L10n.tr("Localizable", "deleteServer", fallback: "Delete Server")
/// Delivery /// Delivery
internal static let delivery = L10n.tr("Localizable", "delivery", fallback: "Delivery") internal static let delivery = L10n.tr("Localizable", "delivery", fallback: "Delivery")
/// DIRECTOR /// DIRECTOR
@ -176,6 +186,8 @@ internal enum L10n {
internal static let downloads = L10n.tr("Localizable", "downloads", fallback: "Downloads") internal static let downloads = L10n.tr("Localizable", "downloads", fallback: "Downloads")
/// Edit Jump Lengths /// Edit Jump Lengths
internal static let editJumpLengths = L10n.tr("Localizable", "editJumpLengths", fallback: "Edit Jump Lengths") internal static let editJumpLengths = L10n.tr("Localizable", "editJumpLengths", fallback: "Edit Jump Lengths")
/// Edit Server
internal static let editServer = L10n.tr("Localizable", "editServer", fallback: "Edit Server")
/// Empty Next Up /// Empty Next Up
internal static let emptyNextUp = L10n.tr("Localizable", "emptyNextUp", fallback: "Empty Next Up") internal static let emptyNextUp = L10n.tr("Localizable", "emptyNextUp", fallback: "Empty Next Up")
/// Enabled /// Enabled
@ -228,6 +240,8 @@ internal enum L10n {
internal static let invertedLight = L10n.tr("Localizable", "invertedLight", fallback: "Inverted Light") internal static let invertedLight = L10n.tr("Localizable", "invertedLight", fallback: "Inverted Light")
/// Items /// Items
internal static let items = L10n.tr("Localizable", "items", fallback: "Items") internal static let items = L10n.tr("Localizable", "items", fallback: "Items")
/// Jellyfin
internal static let jellyfin = L10n.tr("Localizable", "jellyfin", fallback: "Jellyfin")
/// Jump /// Jump
internal static let jump = L10n.tr("Localizable", "jump", fallback: "Jump") internal static let jump = L10n.tr("Localizable", "jump", fallback: "Jump")
/// Jump Backward /// Jump Backward
@ -338,6 +352,8 @@ internal enum L10n {
} }
/// No title /// No title
internal static let noTitle = L10n.tr("Localizable", "noTitle", fallback: "No title") internal static let noTitle = L10n.tr("Localizable", "noTitle", fallback: "No title")
/// Offset
internal static let offset = L10n.tr("Localizable", "offset", fallback: "Offset")
/// Ok /// Ok
internal static let ok = L10n.tr("Localizable", "ok", fallback: "Ok") internal static let ok = L10n.tr("Localizable", "ok", fallback: "Ok")
/// 1 user /// 1 user
@ -380,6 +396,8 @@ internal enum L10n {
internal static let playback = L10n.tr("Localizable", "playback", fallback: "Playback") internal static let playback = L10n.tr("Localizable", "playback", fallback: "Playback")
/// Playback Buttons /// Playback Buttons
internal static let playbackButtons = L10n.tr("Localizable", "playbackButtons", fallback: "Playback Buttons") internal static let playbackButtons = L10n.tr("Localizable", "playbackButtons", fallback: "Playback Buttons")
/// Playback Quality
internal static let playbackQuality = L10n.tr("Localizable", "playbackQuality", fallback: "Playback Quality")
/// Playback settings /// Playback settings
internal static let playbackSettings = L10n.tr("Localizable", "playbackSettings", fallback: "Playback settings") internal static let playbackSettings = L10n.tr("Localizable", "playbackSettings", fallback: "Playback settings")
/// Playback Speed /// Playback Speed
@ -474,12 +492,16 @@ internal enum L10n {
internal static let resetAppSettings = L10n.tr("Localizable", "resetAppSettings", fallback: "Reset App Settings") internal static let resetAppSettings = L10n.tr("Localizable", "resetAppSettings", fallback: "Reset App Settings")
/// Reset User Settings /// Reset User Settings
internal static let resetUserSettings = L10n.tr("Localizable", "resetUserSettings", fallback: "Reset User Settings") internal static let resetUserSettings = L10n.tr("Localizable", "resetUserSettings", fallback: "Reset User Settings")
/// Resume
internal static let resume = L10n.tr("Localizable", "resume", fallback: "Resume")
/// Resume 5 Second Offset /// Resume 5 Second Offset
internal static let resume5SecondOffset = L10n.tr("Localizable", "resume5SecondOffset", fallback: "Resume 5 Second Offset") internal static let resume5SecondOffset = L10n.tr("Localizable", "resume5SecondOffset", fallback: "Resume 5 Second Offset")
/// Resume Offset /// Resume Offset
internal static let resumeOffset = L10n.tr("Localizable", "resumeOffset", fallback: "Resume Offset") internal static let resumeOffset = L10n.tr("Localizable", "resumeOffset", fallback: "Resume Offset")
/// Resume content seconds before the recorded resume time /// 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") internal static let resumeOffsetDescription = L10n.tr("Localizable", "resumeOffsetDescription", fallback: "Resume content seconds before the recorded resume time")
/// Resume Offset
internal static let resumeOffsetTitle = L10n.tr("Localizable", "resumeOffsetTitle", fallback: "Resume Offset")
/// Retrieving media information /// Retrieving media information
internal static let retrievingMediaInformation = L10n.tr("Localizable", "retrievingMediaInformation", fallback: "Retrieving media information") internal static let retrievingMediaInformation = L10n.tr("Localizable", "retrievingMediaInformation", fallback: "Retrieving media information")
/// Retry /// Retry
@ -540,6 +562,10 @@ internal enum L10n {
internal static let showCastAndCrew = L10n.tr("Localizable", "showCastAndCrew", fallback: "Show Cast & Crew") internal static let showCastAndCrew = L10n.tr("Localizable", "showCastAndCrew", fallback: "Show Cast & Crew")
/// Show Chapters Info In Bottom Overlay /// Show Chapters Info In Bottom Overlay
internal static let showChaptersInfoInBottomOverlay = L10n.tr("Localizable", "showChaptersInfoInBottomOverlay", fallback: "Show Chapters Info In Bottom Overlay") internal static let showChaptersInfoInBottomOverlay = L10n.tr("Localizable", "showChaptersInfoInBottomOverlay", fallback: "Show Chapters Info In Bottom Overlay")
/// Show Favorited
internal static let showFavorited = L10n.tr("Localizable", "showFavorited", fallback: "Show Favorited")
/// Show Favorites
internal static let showFavorites = L10n.tr("Localizable", "showFavorites", fallback: "Show Favorites")
/// Flatten Library Items /// Flatten Library Items
internal static let showFlattenView = L10n.tr("Localizable", "showFlattenView", fallback: "Flatten Library Items") internal static let showFlattenView = L10n.tr("Localizable", "showFlattenView", fallback: "Flatten Library Items")
/// Show Missing Episodes /// Show Missing Episodes
@ -548,6 +574,14 @@ internal enum L10n {
internal static let showMissingSeasons = L10n.tr("Localizable", "showMissingSeasons", fallback: "Show Missing Seasons") internal static let showMissingSeasons = L10n.tr("Localizable", "showMissingSeasons", fallback: "Show Missing Seasons")
/// Show Poster Labels /// Show Poster Labels
internal static let showPosterLabels = L10n.tr("Localizable", "showPosterLabels", fallback: "Show Poster Labels") internal static let showPosterLabels = L10n.tr("Localizable", "showPosterLabels", fallback: "Show Poster Labels")
/// Show Progress
internal static let showProgress = L10n.tr("Localizable", "showProgress", fallback: "Show Progress")
/// Show Recently Added
internal static let showRecentlyAdded = L10n.tr("Localizable", "showRecentlyAdded", fallback: "Show Recently Added")
/// Show Unwatched
internal static let showUnwatched = L10n.tr("Localizable", "showUnwatched", fallback: "Show Unwatched")
/// Show Watched
internal static let showWatched = L10n.tr("Localizable", "showWatched", fallback: "Show Watched")
/// Signed in as %@ /// Signed in as %@
internal static func signedInAsWithString(_ p1: Any) -> String { internal static func signedInAsWithString(_ p1: Any) -> String {
return L10n.tr("Localizable", "signedInAsWithString", String(describing: p1), fallback: "Signed in as %@") return L10n.tr("Localizable", "signedInAsWithString", String(describing: p1), fallback: "Signed in as %@")
@ -594,6 +628,8 @@ internal enum L10n {
internal static let subtitleOffset = L10n.tr("Localizable", "subtitleOffset", fallback: "Subtitle Offset") internal static let subtitleOffset = L10n.tr("Localizable", "subtitleOffset", fallback: "Subtitle Offset")
/// Subtitles /// Subtitles
internal static let subtitles = L10n.tr("Localizable", "subtitles", fallback: "Subtitles") internal static let subtitles = L10n.tr("Localizable", "subtitles", fallback: "Subtitles")
/// Settings only affect some subtitle types
internal static let subtitlesDisclaimer = L10n.tr("Localizable", "subtitlesDisclaimer", fallback: "Settings only affect some subtitle types")
/// Subtitle Size /// Subtitle Size
internal static let subtitleSize = L10n.tr("Localizable", "subtitleSize", fallback: "Subtitle Size") internal static let subtitleSize = L10n.tr("Localizable", "subtitleSize", fallback: "Subtitle Size")
/// Suggestions /// Suggestions

View File

@ -31,6 +31,7 @@ struct SplitFormWindowView: View {
.if(descriptionTopPadding) { view in .if(descriptionTopPadding) { view in
view.padding(.top) view.padding(.top)
} }
.scrollClipDisabled()
} }
} }
} }

View File

@ -39,7 +39,7 @@ struct SelectServerView: View {
} }
var body: some View { var body: some View {
FullScreenMenu("Servers") { FullScreenMenu(L10n.servers) {
Section { Section {
Button { Button {
router.popLast { router.popLast {
@ -47,7 +47,7 @@ struct SelectServerView: View {
} }
} label: { } label: {
HStack { HStack {
Text("Add Server") L10n.addServer.text
Spacer() Spacer()
@ -62,7 +62,7 @@ struct SelectServerView: View {
} }
} label: { } label: {
HStack { HStack {
Text("Edit Server") L10n.editServer.text
Spacer() Spacer()
@ -80,7 +80,7 @@ struct SelectServerView: View {
router.popLast() router.popLast()
} label: { } label: {
HStack { HStack {
Text("All Servers") L10n.allServers.text
Spacer() Spacer()
@ -116,9 +116,10 @@ struct SelectServerView: View {
.padding() .padding()
} }
.buttonStyle(.card) .buttonStyle(.card)
.padding(.horizontal)
} }
} header: { } header: {
Text("Servers") Text(L10n.servers)
} }
.headerProminence(.increased) .headerProminence(.increased)
} }

View File

@ -35,36 +35,61 @@ struct EditServerView: View {
.frame(maxWidth: 400) .frame(maxWidth: 400)
} }
.contentView { .contentView {
Section(L10n.server) { Section(L10n.server) {
TextPairView( TextPairView(
leading: L10n.name, leading: L10n.name,
trailing: viewModel.server.name trailing: viewModel.server.name
) )
.focusable(false)
} }
Section("URL") { Section(L10n.url) {
ForEach(viewModel.server.urls.sorted(using: \.absoluteString)) { url in Menu {
if url == viewModel.server.currentURL { ForEach(viewModel.server.urls.sorted(using: \.absoluteString), id: \.self) { url in
Button(url.absoluteString, systemImage: "checkmark") {} Button(action: {
} else { guard viewModel.server.currentURL != url else { return }
Button(url.absoluteString) {
viewModel.setCurrentURL(to: url) viewModel.setCurrentURL(to: url)
}) {
HStack {
Text(url.absoluteString)
.foregroundColor(.primary)
Spacer()
if viewModel.server.currentURL == url {
Image(systemName: "checkmark")
.font(.body.weight(.regular))
.foregroundColor(.secondary)
} }
} }
} }
} }
} label: {
HStack {
Text(viewModel.server.currentURL.absoluteString)
.foregroundColor(.primary)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
Image(systemName: "chevron.down")
.foregroundColor(.secondary)
}
}
}
if isEditing { if isEditing {
ListRowButton("Delete") { Section {
ListRowButton(L10n.delete) {
isPresentingConfirmDeletion = true isPresentingConfirmDeletion = true
} }
.foregroundStyle(.primary, .red.opacity(0.5)) .foregroundStyle(.primary, .red.opacity(0.5))
} }
} }
}
.withDescriptionTopPadding() .withDescriptionTopPadding()
.navigationTitle(L10n.server) .navigationTitle(L10n.server)
.alert("Delete Server", isPresented: $isPresentingConfirmDeletion) { .alert(L10n.deleteServer, isPresented: $isPresentingConfirmDeletion) {
Button("Delete", role: .destructive) { Button(L10n.delete, role: .destructive) {
viewModel.delete() viewModel.delete()
router.popLast() router.popLast()
} }

View File

@ -53,18 +53,16 @@ struct CustomizeViewsSettings: View {
} }
.contentView { .contentView {
Section { Section(L10n.missingItems) {
Toggle(L10n.showMissingSeasons, isOn: $shouldShowMissingSeasons) Toggle(L10n.showMissingSeasons, isOn: $shouldShowMissingSeasons)
Toggle(L10n.showMissingEpisodes, isOn: $shouldShowMissingEpisodes) Toggle(L10n.showMissingEpisodes, isOn: $shouldShowMissingEpisodes)
} header: {
L10n.missingItems.text
} }
Section { Section(L10n.posters) {
ChevronButton("Indicators") ChevronButton(L10n.indicators)
.onSelect { .onSelect {
router.route(to: \.indicatorSettings) router.route(to: \.indicatorSettings)
} }
@ -82,23 +80,17 @@ struct CustomizeViewsSettings: View {
InlineEnumToggle(title: L10n.search, selection: $searchPosterType) InlineEnumToggle(title: L10n.search, selection: $searchPosterType)
InlineEnumToggle(title: L10n.library, selection: $libraryViewType) InlineEnumToggle(title: L10n.library, selection: $libraryViewType)
} header: {
Text("Posters")
} }
Section { Section(L10n.library) {
Toggle("Cinematic Background", isOn: $cinematicBackground) Toggle(L10n.cinematicBackground, isOn: $cinematicBackground)
Toggle("Random Image", isOn: $libraryRandomImage) Toggle(L10n.randomImage, isOn: $libraryRandomImage)
Toggle("Show Favorites", isOn: $showFavorites) Toggle(L10n.showFavorites, isOn: $showFavorites)
Toggle("Show Recently Added", isOn: $showRecentlyAdded) Toggle(L10n.showRecentlyAdded, isOn: $showRecentlyAdded)
} header: {
L10n.library.text
} }
} }
.withDescriptionTopPadding() .withDescriptionTopPadding()

View File

@ -25,20 +25,15 @@ struct ExperimentalSettingsView: View {
.frame(maxWidth: 400) .frame(maxWidth: 400)
} }
.contentView { .contentView {
Section {
Section("Video Player") {
Toggle("Force Direct Play", isOn: $forceDirectPlay) Toggle("Force Direct Play", isOn: $forceDirectPlay)
} header: {
Text("Video Player")
} }
Section { Section("Live TV") {
Toggle("Live TV Force Direct Play", isOn: $liveTVForceDirectPlay) Toggle("Live TV Force Direct Play", isOn: $liveTVForceDirectPlay)
} header: {
Text("Live TV")
} }
} }
.navigationTitle(L10n.experimental) .navigationTitle(L10n.experimental)

View File

@ -32,18 +32,18 @@ struct IndicatorSettingsView: View {
} }
.contentView { .contentView {
Section { Section(L10n.posters) {
Toggle("Show Favorited", isOn: $showFavorited) Toggle(L10n.showFavorited, isOn: $showFavorited)
Toggle("Show Progress", isOn: $showProgress) Toggle(L10n.showProgress, isOn: $showProgress)
Toggle("Show Unwatched", isOn: $showUnwatched) Toggle(L10n.showUnwatched, isOn: $showUnwatched)
Toggle("Show Watched", isOn: $showWatched) Toggle(L10n.showWatched, isOn: $showWatched)
} }
} }
.withDescriptionTopPadding() .withDescriptionTopPadding()
.navigationTitle("Indicators") .navigationTitle(L10n.indicators)
} }
} }

View File

@ -24,21 +24,19 @@ struct MaximumBitrateSettingsView: View {
.frame(maxWidth: 400) .frame(maxWidth: 400)
} }
.contentView { .contentView {
Section { Section {
CaseIterablePicker(
L10n.maximumBitrate, InlineEnumToggle(title: L10n.maximumBitrate, selection: $appMaximumBitrate)
selection: $appMaximumBitrate
)
if appMaximumBitrate == PlaybackBitrate.auto { if appMaximumBitrate == PlaybackBitrate.auto {
CaseIterablePicker( InlineEnumToggle(title: L10n.testSize, selection: $appMaximumBitrateTest)
L10n.testSize,
selection: $appMaximumBitrateTest
)
} }
} header: {
L10n.playbackQuality.text
} footer: { } footer: {
if appMaximumBitrate == PlaybackBitrate.auto { if appMaximumBitrate == PlaybackBitrate.auto {
Text(L10n.bitrateTestDescription) L10n.bitrateTestDescription.text
} }
} }
} }

View File

@ -31,7 +31,7 @@ struct SettingsView: View {
.frame(maxWidth: 400) .frame(maxWidth: 400)
} }
.contentView { .contentView {
Section { Section(L10n.jellyfin) {
Button {} label: { Button {} label: {
TextPairView( TextPairView(
@ -51,14 +51,23 @@ struct SettingsView: View {
Button { Button {
viewModel.signOut() viewModel.signOut()
} label: { } label: {
L10n.switchUser.text HStack {
Text(L10n.switchUser)
.foregroundColor(.jellyfinPurple) .foregroundColor(.jellyfinPurple)
Spacer()
Image(systemName: "chevron.right")
.font(.body.weight(.regular))
.foregroundColor(.secondary)
}
} }
} }
Section { Section(L10n.videoPlayer) {
InlineEnumToggle(title: "Video Player Type", selection: $videoPlayerType) InlineEnumToggle(title: L10n.videoPlayerType, selection: $videoPlayerType)
ChevronButton(L10n.videoPlayer) ChevronButton(L10n.videoPlayer)
.onSelect { .onSelect {
@ -69,12 +78,9 @@ struct SettingsView: View {
.onSelect { .onSelect {
router.route(to: \.maximumBitrateSettings) router.route(to: \.maximumBitrateSettings)
} }
} header: {
L10n.videoPlayer.text
} }
Section { Section(L10n.accessibility) {
ChevronButton(L10n.customize) ChevronButton(L10n.customize)
.onSelect { .onSelect {
@ -85,14 +91,11 @@ struct SettingsView: View {
.onSelect { .onSelect {
router.route(to: \.experimentalSettings) router.route(to: \.experimentalSettings)
} }
} header: {
L10n.accessibility.text
} }
Section { Section {
ChevronButton("Logs") ChevronButton(L10n.logs)
.onSelect { .onSelect {
router.route(to: \.log) router.route(to: \.log)
} }

View File

@ -43,37 +43,41 @@ struct VideoPlayerSettingsView: View {
.contentView { .contentView {
Section { Section {
ChevronButton( ChevronButton(
"Resume Offset", L10n.offset,
subtitle: resumeOffset.secondLabel subtitle: resumeOffset.secondLabel
) )
.onSelect { .onSelect {
isPresentingResumeOffsetStepper = true isPresentingResumeOffsetStepper = true
} }
} header: {
L10n.resume.text
} footer: { } footer: {
Text("Resume content seconds before the recorded resume time") L10n.resumeOffsetDescription.text
} }
Section { Section {
ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName) ChevronButton(L10n.subtitleFont, subtitle: subtitleFontName)
.onSelect { .onSelect {
router.route(to: \.fontPicker, $subtitleFontName) router.route(to: \.fontPicker, $subtitleFontName)
} }
} header: {
L10n.subtitles.text
} footer: { } footer: {
Text("Settings only affect some subtitle types") L10n.subtitlesDisclaimer.text
} }
Section { Section(L10n.playback) {
Toggle(L10n.pauseOnBackground, isOn: $pauseOnBackground)
Toggle("Pause on background", isOn: $pauseOnBackground) Toggle(L10n.playOnActive, isOn: $playOnActive)
Toggle("Play on active", isOn: $playOnActive)
} }
} .navigationTitle(L10n.videoPlayer.text)
.navigationTitle("Video Player")
.blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) { .blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) {
StepperView( StepperView(
title: "Resume Offset", title: L10n.resumeOffsetTitle,
description: "Resume content seconds before the recorded resume time", description: L10n.resumeOffsetDescription,
value: $resumeOffset, value: $resumeOffset,
range: 0 ... 30, range: 0 ... 30,
step: 1 step: 1
@ -87,3 +91,4 @@ struct VideoPlayerSettingsView: View {
} }
} }
} }
}