diff --git a/Model/CommentsModel.swift b/Model/CommentsModel.swift index 5f0c73e4..67bc1d0a 100644 --- a/Model/CommentsModel.swift +++ b/Model/CommentsModel.swift @@ -29,6 +29,12 @@ final class CommentsModel: ObservableObject { !Defaults[.commentsInstanceID].isNil && !Defaults[.commentsInstanceID]!.isEmpty } + #if !os(tvOS) + static var placement: CommentsPlacement { + Defaults[.commentsPlacement] + } + #endif + var nextPageAvailable: Bool { !(nextPage?.isEmpty ?? true) } diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index edf0a567..b2d70232 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -34,6 +34,9 @@ extension Defaults.Keys { static let playerInstanceID = Key("playerInstance") static let showKeywords = Key("showKeywords", default: false) static let commentsInstanceID = Key("commentsInstance", default: kavinPipedInstanceID) + #if !os(tvOS) + static let commentsPlacement = Key("commentsPlacement", default: .separate) + #endif static let recentlyOpened = Key<[RecentItem]>("recentlyOpened", default: []) @@ -129,3 +132,9 @@ enum VisibleSection: String, CaseIterable, Comparable, Defaults.Serializable { lhs.sortOrder < rhs.sortOrder } } + +#if !os(tvOS) + enum CommentsPlacement: String, CaseIterable, Defaults.Serializable { + case info, separate + } +#endif diff --git a/Shared/Player/VideoDetails.swift b/Shared/Player/VideoDetails.swift index 3f6b7c80..e0b36893 100644 --- a/Shared/Player/VideoDetails.swift +++ b/Shared/Player/VideoDetails.swift @@ -65,7 +65,7 @@ struct VideoDetails: View { } .padding(.horizontal) - if CommentsModel.enabled { + if CommentsModel.enabled, CommentsModel.placement == .separate { pagePicker .padding(.horizontal) } @@ -245,13 +245,13 @@ struct VideoDetails: View { Picker("Page", selection: $currentPage) { if !video.isNil { Text("Info").tag(Page.info) - if !sidebarQueue { - Text("Related").tag(Page.related) - } - if CommentsModel.enabled { + if CommentsModel.enabled, CommentsModel.placement == .separate { Text("Comments") .tag(Page.comments) } + if !sidebarQueue { + Text("Related").tag(Page.related) + } } if !sidebarQueue { Text("Queue").tag(Page.queue) @@ -366,65 +366,81 @@ struct VideoDetails: View { var detailsPage: some View { Group { - if let video = player.currentItem?.video { - Group { - HStack { - publishedDateSection - Spacer() + Group { + if let video = player.currentItem?.video { + Group { + HStack { + publishedDateSection + Spacer() + } + + Divider() + + countsSection } Divider() - countsSection - } - - Divider() - - VStack(alignment: .leading, spacing: 10) { - if let description = video.description { - Group { - if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { - Text(description) - .textSelection(.enabled) - } else { - Text(description) - } - } - .frame(maxWidth: .infinity, alignment: .leading) - .font(.system(size: 14)) - .lineSpacing(3) - .padding(.bottom, 4) - } else { - Text("No description") - .foregroundColor(.secondary) - } - - if showKeywords { - ScrollView(.horizontal, showsIndicators: showScrollIndicators) { - HStack { - ForEach(video.keywords, id: \.self) { keyword in - HStack(alignment: .center, spacing: 0) { - Text("#") - .font(.system(size: 11).bold()) - - Text(keyword) - .frame(maxWidth: 500) - } - .font(.caption) - .foregroundColor(.white) - .padding(.vertical, 4) - .padding(.horizontal, 8) - .background(Color("VideoDetailLikesSymbolColor")) - .mask(RoundedRectangle(cornerRadius: 3)) + VStack(alignment: .leading, spacing: 10) { + if let description = video.description { + Group { + if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { + Text(description) + .textSelection(.enabled) + } else { + Text(description) } } - .padding(.bottom, 10) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.system(size: 14)) + .lineSpacing(3) + } else { + Text("No description") + .foregroundColor(.secondary) + } + + if showKeywords { + ScrollView(.horizontal, showsIndicators: showScrollIndicators) { + HStack { + ForEach(video.keywords, id: \.self) { keyword in + HStack(alignment: .center, spacing: 0) { + Text("#") + .font(.system(size: 11).bold()) + + Text(keyword) + .frame(maxWidth: 500) + } + .font(.caption) + .foregroundColor(.white) + .padding(.vertical, 4) + .padding(.horizontal, 8) + .background(Color("VideoDetailLikesSymbolColor")) + .mask(RoundedRectangle(cornerRadius: 3)) + } + } + .padding(.bottom, 10) + } } } } + + if !video.isNil, CommentsModel.placement == .info { + Divider() + #if os(macOS) + .padding(.bottom, 20) + #else + .padding(.vertical, 10) + #endif + } + } + .padding(.horizontal) + + Group { + if !video.isNil, CommentsModel.placement == .info { + CommentsView() + } } } - .padding(.horizontal) } func videoDetail(label: String, value: String, symbol: String) -> some View { diff --git a/Shared/Settings/ServicesSettings.swift b/Shared/Settings/ServicesSettings.swift index 10ae9dcf..209f062a 100644 --- a/Shared/Settings/ServicesSettings.swift +++ b/Shared/Settings/ServicesSettings.swift @@ -6,9 +6,17 @@ struct ServicesSettings: View { @Default(.sponsorBlockCategories) private var sponsorBlockCategories @Default(.commentsInstanceID) private var commentsInstanceID + #if !os(tvOS) + @Default(.commentsPlacement) private var commentsPlacement + #endif + var body: some View { Section(header: SettingsHeader(text: "Comments")) { commentsInstancePicker + #if !os(tvOS) + commentsPlacementPicker + .disabled(!CommentsModel.enabled) + #endif } Section(header: SettingsHeader(text: "SponsorBlock API")) { @@ -58,7 +66,7 @@ struct ServicesSettings: View { } private var commentsInstancePicker: some View { - Picker("Comments", selection: $commentsInstanceID) { + Picker("Source", selection: $commentsInstanceID) { Text("Disabled").tag(Optional("")) ForEach(InstancesModel.all.filter { $0.app.supportsComments }) { instance in @@ -73,6 +81,19 @@ struct ServicesSettings: View { #endif } + #if !os(tvOS) + private var commentsPlacementPicker: some View { + Picker("Placement", selection: $commentsPlacement) { + Text("Below video description").tag(CommentsPlacement.info) + Text("Separate tab").tag(CommentsPlacement.separate) + } + .labelsHidden() + #if os(iOS) + .pickerStyle(.automatic) + #endif + } + #endif + func toggleCategory(_ category: String, value: Bool) { if let index = sponsorBlockCategories.firstIndex(where: { $0 == category }), !value { sponsorBlockCategories.remove(at: index)