mirror of
https://github.com/yattee/yattee.git
synced 2026-11-02 18:07:19 +00:00
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:
@@ -1527,16 +1527,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commentsPill.mode.title" : {
|
|
||||||
"localizations" : {
|
|
||||||
"en" : {
|
|
||||||
"stringUnit" : {
|
|
||||||
"state" : "translated",
|
|
||||||
"value" : "Comments"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"common.add" : {
|
"common.add" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"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" : {
|
"panel.settings.comments.showAvatars" : {
|
||||||
"comment" : "Comments page element toggle",
|
"comment" : "Comments page element toggle",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -671,14 +671,6 @@ struct PanelSettings: Codable, Hashable, Sendable {
|
|||||||
#endif
|
#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,
|
/// Enabled pages an inline-sections host renders below the video info,
|
||||||
/// in configured order, with their per-page element settings.
|
/// in configured order, with their per-page element settings.
|
||||||
func inlineSections(in region: PanelRegion? = nil) -> [PanelPageConfiguration] {
|
func inlineSections(in region: PanelRegion? = nil) -> [PanelPageConfiguration] {
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import SwiftUI
|
|||||||
struct PanelPageEditorView: View {
|
struct PanelPageEditorView: View {
|
||||||
let pageType: PanelPageType
|
let pageType: PanelPageType
|
||||||
@Bindable var viewModel: PlayerControlsSettingsViewModel
|
@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
|
// Local state for immediate UI updates
|
||||||
@State private var isEnabled: Bool = true
|
@State private var isEnabled: Bool = true
|
||||||
@@ -38,12 +41,15 @@ struct PanelPageEditorView: View {
|
|||||||
pageType == PanelSettings.pinnedPageType
|
pageType == PanelSettings.pinnedPageType
|
||||||
}
|
}
|
||||||
|
|
||||||
/// iPhone configures inline sections rather than pages.
|
/// Comments on an inline-sections host only show as the pill, whose
|
||||||
private var usesSections: Bool { PanelSettings.isSectionsOnlyDevice }
|
/// mode includes off: no separate toggle.
|
||||||
|
private var hasEnabledToggle: Bool {
|
||||||
|
!isPinnedPage && !(usesSections && pageType == .comments)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
if !isPinnedPage {
|
if hasEnabledToggle {
|
||||||
enabledSection
|
enabledSection
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +182,7 @@ struct PanelPageEditorView: View {
|
|||||||
|
|
||||||
// MARK: - Comments Elements Section
|
// MARK: - Comments Elements Section
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
private var commentsElementsSection: some View {
|
private var commentsElementsSection: some View {
|
||||||
Section {
|
Section {
|
||||||
Toggle(
|
Toggle(
|
||||||
@@ -186,9 +193,18 @@ struct PanelPageEditorView: View {
|
|||||||
guard newValue != commentsSettings.effectiveShowAvatars else { return }
|
guard newValue != commentsSettings.effectiveShowAvatars else { return }
|
||||||
viewModel.updatePanelCommentsSettingsSync { $0.showAvatars = newValue }
|
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(
|
Picker(
|
||||||
String(localized: "commentsPill.mode.title"),
|
String(localized: "panel.settings.comments.pillMode"),
|
||||||
selection: $commentsPillMode
|
selection: $commentsPillMode
|
||||||
) {
|
) {
|
||||||
ForEach(CommentsPillMode.allCases, id: \.self) { mode in
|
ForEach(CommentsPillMode.allCases, id: \.self) { mode in
|
||||||
@@ -199,8 +215,8 @@ struct PanelPageEditorView: View {
|
|||||||
guard newValue != commentsSettings.effectivePillMode else { return }
|
guard newValue != commentsSettings.effectivePillMode else { return }
|
||||||
viewModel.updatePanelCommentsSettingsSync { $0.pillMode = newValue }
|
viewModel.updatePanelCommentsSettingsSync { $0.pillMode = newValue }
|
||||||
}
|
}
|
||||||
} header: {
|
} footer: {
|
||||||
Text(String(localized: "panel.settings.elements.header"))
|
Text(String(localized: "panel.settings.comments.pillMode.footer"))
|
||||||
}
|
}
|
||||||
.disabled(!viewModel.canEditActivePreset)
|
.disabled(!viewModel.canEditActivePreset)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ struct PanelSettingsView: View {
|
|||||||
@State private var wideLayoutSide: FloatingPanelSide = .right
|
@State private var wideLayoutSide: FloatingPanelSide = .right
|
||||||
@State private var belowVideoContent: PanelBelowVideoContent = .info
|
@State private var belowVideoContent: PanelBelowVideoContent = .info
|
||||||
@State private var showPageBarLabels: Bool = true
|
@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
|
/// Corner layout exists on iPad and macOS, and on a foldable iPhone whose
|
||||||
/// interior display is large enough (see `hasHinge`).
|
/// interior display is large enough (see `hasHinge`).
|
||||||
@@ -37,8 +35,23 @@ struct PanelSettingsView: View {
|
|||||||
|
|
||||||
private var showsLayoutSection: Bool { Self.supportsCornerLayout || hasHinge }
|
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.
|
/// 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 {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
@@ -50,9 +63,6 @@ struct PanelSettingsView: View {
|
|||||||
pageBarSection
|
pageBarSection
|
||||||
}
|
}
|
||||||
pagesSection
|
pagesSection
|
||||||
if usesSections {
|
|
||||||
commentsSection
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationTitle(String(localized: "settings.playerControls.detailsPanel"))
|
.navigationTitle(String(localized: "settings.playerControls.detailsPanel"))
|
||||||
#if os(iOS)
|
#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
|
// 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)
|
/// Page types whose slot the user cannot change: the pinned page (info)
|
||||||
/// and, in the corner layout with the comments pill below the video,
|
/// and comments while they show as the pill, since the pill is not part
|
||||||
/// comments too, since it is no longer a page in the side column.
|
/// of the page/section order.
|
||||||
private var lockedPageTypes: Set<PanelPageType> {
|
private var lockedPageTypes: Set<PanelPageType> {
|
||||||
var locked: Set<PanelPageType> = [PanelSettings.pinnedPageType]
|
var locked: Set<PanelPageType> = [PanelSettings.pinnedPageType]
|
||||||
if layoutMode == .corner, Self.supportsCornerLayout,
|
if commentsIsPill {
|
||||||
viewModel.panelSettings.effectiveRegion(for: .comments) == .belowVideo {
|
|
||||||
locked.insert(.comments)
|
locked.insert(.comments)
|
||||||
}
|
}
|
||||||
return locked
|
return locked
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Locked pages are rendered outside the reorderable list so they get no
|
/// Locked pages are rendered outside the reorderable list so they get no
|
||||||
/// drag handle and nothing can be dropped between or above them.
|
/// drag handle and nothing can be dropped between them: the pinned page
|
||||||
private var lockedPages: [PanelPageConfiguration] {
|
/// above, the comments pill below.
|
||||||
let locked = lockedPageTypes
|
private func lockedPages(_ types: Set<PanelPageType>) -> [PanelPageConfiguration] {
|
||||||
let hidden = PanelSettings.hiddenSettingsPageTypes
|
let hidden = hiddenPageTypes
|
||||||
return viewModel.panelPages.filter {
|
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
|
/// 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] {
|
private var reorderablePages: [PanelPageConfiguration] {
|
||||||
let locked = lockedPageTypes
|
let locked = lockedPageTypes
|
||||||
let hidden = PanelSettings.hiddenSettingsPageTypes
|
let hidden = hiddenPageTypes
|
||||||
return viewModel.panelPages.filter {
|
return viewModel.panelPages.filter {
|
||||||
!locked.contains($0.pageType) && !hidden.contains($0.pageType)
|
!locked.contains($0.pageType) && !hidden.contains($0.pageType)
|
||||||
}
|
}
|
||||||
@@ -290,7 +264,7 @@ struct PanelSettingsView: View {
|
|||||||
|
|
||||||
private var pagesSection: some View {
|
private var pagesSection: some View {
|
||||||
Section {
|
Section {
|
||||||
ForEach(lockedPages) { page in
|
ForEach(lockedPages([PanelSettings.pinnedPageType])) { page in
|
||||||
pageRow(for: page)
|
pageRow(for: page)
|
||||||
.moveDisabled(true)
|
.moveDisabled(true)
|
||||||
.deleteDisabled(true)
|
.deleteDisabled(true)
|
||||||
@@ -308,6 +282,12 @@ struct PanelSettingsView: View {
|
|||||||
toOffset: destination
|
toOffset: destination
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForEach(lockedPages([.comments])) { page in
|
||||||
|
pageRow(for: page)
|
||||||
|
.moveDisabled(true)
|
||||||
|
.deleteDisabled(true)
|
||||||
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text(usesSections
|
Text(usesSections
|
||||||
? String(localized: "panel.settings.sections.header")
|
? String(localized: "panel.settings.sections.header")
|
||||||
@@ -327,7 +307,11 @@ struct PanelSettingsView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func pageRow(for page: PanelPageConfiguration) -> some View {
|
private func pageRow(for page: PanelPageConfiguration) -> some View {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
PanelPageEditorView(pageType: page.pageType, viewModel: viewModel)
|
PanelPageEditorView(
|
||||||
|
pageType: page.pageType,
|
||||||
|
viewModel: viewModel,
|
||||||
|
usesSections: usesSections
|
||||||
|
)
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Label(page.pageType.displayName, systemImage: page.pageType.systemImage)
|
Label(page.pageType.displayName, systemImage: page.pageType.systemImage)
|
||||||
@@ -340,7 +324,11 @@ struct PanelSettingsView: View {
|
|||||||
.background(Capsule().fill(.tint))
|
.background(Capsule().fill(.tint))
|
||||||
}
|
}
|
||||||
Spacer()
|
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)
|
Text(viewModel.panelSettings.effectiveRegion(for: page.pageType).displayName)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
} else {
|
} else {
|
||||||
@@ -355,6 +343,10 @@ struct PanelSettingsView: View {
|
|||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private var commentsPillMode: CommentsPillMode {
|
||||||
|
viewModel.panelSettings.commentsPillMode
|
||||||
|
}
|
||||||
|
|
||||||
private func syncLocalState() {
|
private func syncLocalState() {
|
||||||
portraitDefaultVisibility = viewModel.panelDefaultVisibility(for: .portrait)
|
portraitDefaultVisibility = viewModel.panelDefaultVisibility(for: .portrait)
|
||||||
landscapeDefaultVisibility = viewModel.panelDefaultVisibility(for: .landscape)
|
landscapeDefaultVisibility = viewModel.panelDefaultVisibility(for: .landscape)
|
||||||
@@ -363,8 +355,6 @@ struct PanelSettingsView: View {
|
|||||||
// Clamp to what this platform offers so the picker never shows blank.
|
// Clamp to what this platform offers so the picker never shows blank.
|
||||||
belowVideoContent = viewModel.panelSettings.effectiveBelowVideoContent
|
belowVideoContent = viewModel.panelSettings.effectiveBelowVideoContent
|
||||||
showPageBarLabels = viewModel.panelShowPageBarLabels
|
showPageBarLabels = viewModel.panelShowPageBarLabels
|
||||||
commentsShowAvatars = commentsSettings.effectiveShowAvatars
|
|
||||||
commentsPillMode = commentsSettings.effectivePillMode
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user