Configure comments from their row in details panel settings

The separate comments group (pill mode, author avatars) is gone: comments
are a row in the pages/sections list whose editor holds both options.
Inline-sections hosts list comments as a locked pill row at the bottom;
a foldable iPhone in the corner layout lists real pages, including
comments and queue in the side column.
This commit is contained in:
Arkadiusz Fal
2026-10-20 22:26:21 +02:00
parent f9c448b0aa
commit 14912f0203
4 changed files with 97 additions and 87 deletions

View File

@@ -1527,16 +1527,6 @@
}
}
},
"commentsPill.mode.title" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Comments"
}
}
}
},
"common.add" : {
"localizations" : {
"en" : {
@@ -7967,6 +7957,28 @@
}
}
},
"panel.settings.comments.pillMode" : {
"comment" : "Comments page editor picker label",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Comments Pill"
}
}
}
},
"panel.settings.comments.pillMode.footer" : {
"comment" : "Comments page editor picker footer",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "How comments show below the video details: as a pill that collapses on scroll, as a button, or not at all."
}
}
}
},
"panel.settings.comments.showAvatars" : {
"comment" : "Comments page element toggle",
"localizations" : {

View File

@@ -671,14 +671,6 @@ struct PanelSettings: Codable, Hashable, Sendable {
#endif
}
/// Page types the settings list hides on this device because the panel
/// never renders them as pages: iPhone shows only the info page with
/// inline sections, so queue has no effect there and comments live on
/// the pill/overlay (configured in their own settings group).
static var hiddenSettingsPageTypes: Set<PanelPageType> {
isSectionsOnlyDevice ? [.queue, .comments] : []
}
/// Enabled pages an inline-sections host renders below the video info,
/// in configured order, with their per-page element settings.
func inlineSections(in region: PanelRegion? = nil) -> [PanelPageConfiguration] {

View File

@@ -12,6 +12,9 @@ import SwiftUI
struct PanelPageEditorView: View {
let pageType: PanelPageType
@Bindable var viewModel: PlayerControlsSettingsViewModel
/// Whether the panel list configures inline sections (iPhone, or a
/// foldable iPhone outside the corner layout) rather than pages.
let usesSections: Bool
// Local state for immediate UI updates
@State private var isEnabled: Bool = true
@@ -38,12 +41,15 @@ struct PanelPageEditorView: View {
pageType == PanelSettings.pinnedPageType
}
/// iPhone configures inline sections rather than pages.
private var usesSections: Bool { PanelSettings.isSectionsOnlyDevice }
/// Comments on an inline-sections host only show as the pill, whose
/// mode includes off: no separate toggle.
private var hasEnabledToggle: Bool {
!isPinnedPage && !(usesSections && pageType == .comments)
}
var body: some View {
Form {
if !isPinnedPage {
if hasEnabledToggle {
enabledSection
}
@@ -176,6 +182,7 @@ struct PanelPageEditorView: View {
// MARK: - Comments Elements Section
@ViewBuilder
private var commentsElementsSection: some View {
Section {
Toggle(
@@ -186,9 +193,18 @@ struct PanelPageEditorView: View {
guard newValue != commentsSettings.effectiveShowAvatars else { return }
viewModel.updatePanelCommentsSettingsSync { $0.showAvatars = newValue }
}
} header: {
Text(String(localized: "panel.settings.elements.header"))
} footer: {
if !hasEnabledToggle, !viewModel.canEditActivePreset {
Text(String(localized: "settings.playerControls.duplicateToEdit"))
}
}
.disabled(!viewModel.canEditActivePreset)
Section {
Picker(
String(localized: "commentsPill.mode.title"),
String(localized: "panel.settings.comments.pillMode"),
selection: $commentsPillMode
) {
ForEach(CommentsPillMode.allCases, id: \.self) { mode in
@@ -199,8 +215,8 @@ struct PanelPageEditorView: View {
guard newValue != commentsSettings.effectivePillMode else { return }
viewModel.updatePanelCommentsSettingsSync { $0.pillMode = newValue }
}
} header: {
Text(String(localized: "panel.settings.elements.header"))
} footer: {
Text(String(localized: "panel.settings.comments.pillMode.footer"))
}
.disabled(!viewModel.canEditActivePreset)
}

View File

@@ -19,8 +19,6 @@ struct PanelSettingsView: View {
@State private var wideLayoutSide: FloatingPanelSide = .right
@State private var belowVideoContent: PanelBelowVideoContent = .info
@State private var showPageBarLabels: Bool = true
@State private var commentsShowAvatars: Bool = true
@State private var commentsPillMode: CommentsPillMode = .pill
/// Corner layout exists on iPad and macOS, and on a foldable iPhone whose
/// interior display is large enough (see `hasHinge`).
@@ -37,8 +35,23 @@ struct PanelSettingsView: View {
private var showsLayoutSection: Bool { Self.supportsCornerLayout || hasHinge }
/// Whether the corner layout is selected and available on this device.
private var isCornerLayout: Bool { layoutMode == .corner && showsLayoutSection }
/// iPhone has no page bar: the list configures inline sections instead.
private var usesSections: Bool { PanelSettings.isSectionsOnlyDevice }
/// A foldable iPhone in the corner layout gets a side column with real
/// pages (comments and queue included), so it configures pages there.
private var usesSections: Bool {
PanelSettings.isSectionsOnlyDevice && !isCornerLayout
}
/// Comments shown as the pill on the info panel rather than as a page:
/// always on inline-sections hosts, and in the corner layout when they
/// sit below the video.
private var commentsIsPill: Bool {
usesSections
|| (isCornerLayout && viewModel.panelSettings.effectiveRegion(for: .comments) == .belowVideo)
}
var body: some View {
List {
@@ -50,9 +63,6 @@ struct PanelSettingsView: View {
pageBarSection
}
pagesSection
if usesSections {
commentsSection
}
}
.navigationTitle(String(localized: "settings.playerControls.detailsPanel"))
#if os(iOS)
@@ -213,76 +223,40 @@ struct PanelSettingsView: View {
}
}
// MARK: - Comments Section (iPhone)
/// iPhone never renders a comments page: comments live on the pill and
/// overlay, so their settings sit here instead of in the sections list.
private var commentsSettings: CommentsPageSettings {
viewModel.panelSettings.configuration(for: .comments)?.commentsSettings ?? CommentsPageSettings()
}
private var commentsSection: some View {
Section {
Picker(
String(localized: "commentsPill.mode.title"),
selection: $commentsPillMode
) {
ForEach(CommentsPillMode.allCases, id: \.self) { mode in
Text(mode.displayName).tag(mode)
}
}
.onChange(of: commentsPillMode) { _, newValue in
guard newValue != commentsSettings.effectivePillMode else { return }
viewModel.updatePanelCommentsSettingsSync { $0.pillMode = newValue }
}
Toggle(
String(localized: "panel.settings.comments.showAvatars"),
isOn: $commentsShowAvatars
)
.onChange(of: commentsShowAvatars) { _, newValue in
guard newValue != commentsSettings.effectiveShowAvatars else { return }
viewModel.updatePanelCommentsSettingsSync { $0.showAvatars = newValue }
}
} header: {
Text(String(localized: "player.comments"))
} footer: {
if !viewModel.canEditActivePreset {
Text(String(localized: "settings.playerControls.duplicateToEdit"))
}
}
.disabled(!viewModel.canEditActivePreset)
}
// MARK: - Pages Section
/// Page types this layout never renders: inline-sections hosts have no
/// queue (comments stay listed as the pill row).
private var hiddenPageTypes: Set<PanelPageType> {
usesSections ? [.queue] : []
}
/// Page types whose slot the user cannot change: the pinned page (info)
/// and, in the corner layout with the comments pill below the video,
/// comments too, since it is no longer a page in the side column.
/// and comments while they show as the pill, since the pill is not part
/// of the page/section order.
private var lockedPageTypes: Set<PanelPageType> {
var locked: Set<PanelPageType> = [PanelSettings.pinnedPageType]
if layoutMode == .corner, Self.supportsCornerLayout,
viewModel.panelSettings.effectiveRegion(for: .comments) == .belowVideo {
if commentsIsPill {
locked.insert(.comments)
}
return locked
}
/// Locked pages are rendered outside the reorderable list so they get no
/// drag handle and nothing can be dropped between or above them.
private var lockedPages: [PanelPageConfiguration] {
let locked = lockedPageTypes
let hidden = PanelSettings.hiddenSettingsPageTypes
/// drag handle and nothing can be dropped between them: the pinned page
/// above, the comments pill below.
private func lockedPages(_ types: Set<PanelPageType>) -> [PanelPageConfiguration] {
let hidden = hiddenPageTypes
return viewModel.panelPages.filter {
locked.contains($0.pageType) && !hidden.contains($0.pageType)
types.contains($0.pageType) && lockedPageTypes.contains($0.pageType) && !hidden.contains($0.pageType)
}
}
/// Pages the user can reorder: everything but the locked pages and the
/// types this device never renders (iPhone: queue, comments).
/// types this layout never renders.
private var reorderablePages: [PanelPageConfiguration] {
let locked = lockedPageTypes
let hidden = PanelSettings.hiddenSettingsPageTypes
let hidden = hiddenPageTypes
return viewModel.panelPages.filter {
!locked.contains($0.pageType) && !hidden.contains($0.pageType)
}
@@ -290,7 +264,7 @@ struct PanelSettingsView: View {
private var pagesSection: some View {
Section {
ForEach(lockedPages) { page in
ForEach(lockedPages([PanelSettings.pinnedPageType])) { page in
pageRow(for: page)
.moveDisabled(true)
.deleteDisabled(true)
@@ -308,6 +282,12 @@ struct PanelSettingsView: View {
toOffset: destination
)
}
ForEach(lockedPages([.comments])) { page in
pageRow(for: page)
.moveDisabled(true)
.deleteDisabled(true)
}
} header: {
Text(usesSections
? String(localized: "panel.settings.sections.header")
@@ -327,7 +307,11 @@ struct PanelSettingsView: View {
@ViewBuilder
private func pageRow(for page: PanelPageConfiguration) -> some View {
NavigationLink {
PanelPageEditorView(pageType: page.pageType, viewModel: viewModel)
PanelPageEditorView(
pageType: page.pageType,
viewModel: viewModel,
usesSections: usesSections
)
} label: {
HStack {
Label(page.pageType.displayName, systemImage: page.pageType.systemImage)
@@ -340,7 +324,11 @@ struct PanelSettingsView: View {
.background(Capsule().fill(.tint))
}
Spacer()
if page.isEnabled, layoutMode == .corner, Self.supportsCornerLayout {
if page.pageType == .comments, usesSections {
// The pill has no page toggle: its mode includes off.
Text(commentsPillMode.displayName)
.foregroundStyle(.secondary)
} else if page.isEnabled, isCornerLayout {
Text(viewModel.panelSettings.effectiveRegion(for: page.pageType).displayName)
.foregroundStyle(.secondary)
} else {
@@ -355,6 +343,10 @@ struct PanelSettingsView: View {
// MARK: - Helpers
private var commentsPillMode: CommentsPillMode {
viewModel.panelSettings.commentsPillMode
}
private func syncLocalState() {
portraitDefaultVisibility = viewModel.panelDefaultVisibility(for: .portrait)
landscapeDefaultVisibility = viewModel.panelDefaultVisibility(for: .landscape)
@@ -363,8 +355,6 @@ struct PanelSettingsView: View {
// Clamp to what this platform offers so the picker never shows blank.
belowVideoContent = viewModel.panelSettings.effectiveBelowVideoContent
showPageBarLabels = viewModel.panelShowPageBarLabels
commentsShowAvatars = commentsSettings.effectiveShowAvatars
commentsPillMode = commentsSettings.effectivePillMode
}
}