mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Merge pull request #665 from stonerl/chapter-pictures
allow user to disable thumbnails and jump to current chapter in horizontal view
This commit is contained in:
commit
ae12eefafc
@ -77,6 +77,8 @@ extension Defaults.Keys {
|
|||||||
static let collapsedLinesDescription = Key<Int>("collapsedLinesDescription", default: 5)
|
static let collapsedLinesDescription = Key<Int>("collapsedLinesDescription", default: 5)
|
||||||
|
|
||||||
static let showChapters = Key<Bool>("showChapters", default: true)
|
static let showChapters = Key<Bool>("showChapters", default: true)
|
||||||
|
static let showChapterThumbnails = Key<Bool>("showChapterThumbnails", default: true)
|
||||||
|
static let showChapterThumbnailsOnlyWhenDifferent = Key<Bool>("showChapterThumbnailsOnlyWhenDifferent", default: true)
|
||||||
static let expandChapters = Key<Bool>("expandChapters", default: true)
|
static let expandChapters = Key<Bool>("expandChapters", default: true)
|
||||||
static let showRelated = Key<Bool>("showRelated", default: true)
|
static let showRelated = Key<Bool>("showRelated", default: true)
|
||||||
static let showInspector = Key<ShowInspectorSetting>("showInspector", default: .onlyLocal)
|
static let showInspector = Key<ShowInspectorSetting>("showInspector", default: .onlyLocal)
|
||||||
|
@ -9,8 +9,13 @@ import SwiftUI
|
|||||||
var chapterIndex: Int
|
var chapterIndex: Int
|
||||||
@ObservedObject private var player = PlayerModel.shared
|
@ObservedObject private var player = PlayerModel.shared
|
||||||
|
|
||||||
|
var showThumbnail: Bool
|
||||||
|
|
||||||
var isCurrentChapter: Bool {
|
var isCurrentChapter: Bool {
|
||||||
player.currentChapterIndex == chapterIndex
|
if let currentChapterIndex = player.currentChapterIndex {
|
||||||
|
return currentChapterIndex == chapterIndex
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -27,7 +32,7 @@ import SwiftUI
|
|||||||
|
|
||||||
var verticalChapter: some View {
|
var verticalChapter: some View {
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
if !chapter.image.isNil {
|
if !chapter.image.isNil, showThumbnail {
|
||||||
smallImage(chapter)
|
smallImage(chapter)
|
||||||
}
|
}
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
@ -40,7 +45,7 @@ import SwiftUI
|
|||||||
.font(.system(.subheadline).monospacedDigit())
|
.font(.system(.subheadline).monospacedDigit())
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: !chapter.image.isNil ? Self.thumbnailWidth : nil, alignment: .leading)
|
.frame(maxWidth: !chapter.image.isNil && showThumbnail ? Self.thumbnailWidth : nil, alignment: .leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +131,7 @@ struct ChapterView_Preview: PreviewProvider {
|
|||||||
ChapterViewTVOS(chapter: .init(title: "Chapter", start: 30))
|
ChapterViewTVOS(chapter: .init(title: "Chapter", start: 30))
|
||||||
.injectFixtureEnvironmentObjects()
|
.injectFixtureEnvironmentObjects()
|
||||||
#else
|
#else
|
||||||
ChapterView(chapter: .init(title: "Chapter", start: 30), chapterIndex: 0)
|
ChapterView(chapter: .init(title: "Chapter", start: 30), chapterIndex: 0, showThumbnail: true)
|
||||||
.injectFixtureEnvironmentObjects()
|
.injectFixtureEnvironmentObjects()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,16 @@ import SwiftUI
|
|||||||
struct ChaptersView: View {
|
struct ChaptersView: View {
|
||||||
@ObservedObject private var player = PlayerModel.shared
|
@ObservedObject private var player = PlayerModel.shared
|
||||||
@Binding var expand: Bool
|
@Binding var expand: Bool
|
||||||
|
let chaptersHaveImages: Bool
|
||||||
|
let showThumbnails: Bool
|
||||||
|
|
||||||
var chapters: [Chapter] {
|
var chapters: [Chapter] {
|
||||||
player.videoForDisplay?.chapters ?? []
|
player.videoForDisplay?.chapters ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
var chaptersHaveImages: Bool {
|
|
||||||
chapters.allSatisfy { $0.image != nil }
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if !chapters.isEmpty {
|
if !chapters.isEmpty {
|
||||||
if chaptersHaveImages {
|
if chaptersHaveImages, showThumbnails {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
@ -29,7 +27,22 @@ struct ChaptersView: View {
|
|||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
#else
|
#else
|
||||||
ScrollView(.horizontal) {
|
ScrollView(.horizontal) {
|
||||||
LazyHStack(spacing: 20) { chapterViews(for: chapters[...]) }.padding(.horizontal, 15)
|
ScrollViewReader { scrollViewProxy in
|
||||||
|
LazyHStack(spacing: 20) {
|
||||||
|
chapterViews(for: chapters[...], scrollViewProxy: scrollViewProxy)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
.onAppear {
|
||||||
|
if let currentChapterIndex = player.currentChapterIndex {
|
||||||
|
scrollViewProxy.scrollTo(currentChapterIndex, anchor: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: player.currentChapterIndex) { currentChapterIndex in
|
||||||
|
if let index = currentChapterIndex {
|
||||||
|
scrollViewProxy.scrollTo(index, anchor: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else if expand {
|
} else if expand {
|
||||||
@ -67,10 +80,11 @@ struct ChaptersView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
private func chapterViews(for chaptersToShow: ArraySlice<Chapter>, opacity: Double = 1.0, clickable: Bool = true) -> some View {
|
private func chapterViews(for chaptersToShow: ArraySlice<Chapter>, opacity: Double = 1.0, clickable: Bool = true, scrollViewProxy: ScrollViewProxy? = nil) -> some View {
|
||||||
ForEach(Array(chaptersToShow.indices), id: \.self) { index in
|
ForEach(Array(chaptersToShow.indices), id: \.self) { index in
|
||||||
let chapter = chaptersToShow[index]
|
let chapter = chaptersToShow[index]
|
||||||
ChapterView(chapter: chapter, chapterIndex: index)
|
ChapterView(chapter: chapter, chapterIndex: index, showThumbnail: showThumbnails)
|
||||||
|
.id(index)
|
||||||
.opacity(index == 0 ? 1.0 : opacity)
|
.opacity(index == 0 ? 1.0 : opacity)
|
||||||
.allowsHitTesting(clickable)
|
.allowsHitTesting(clickable)
|
||||||
}
|
}
|
||||||
@ -80,7 +94,7 @@ struct ChaptersView: View {
|
|||||||
|
|
||||||
struct ChaptersView_Previews: PreviewProvider {
|
struct ChaptersView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ChaptersView(expand: .constant(false))
|
ChaptersView(expand: .constant(false), chaptersHaveImages: false, showThumbnails: true)
|
||||||
.injectFixtureEnvironmentObjects()
|
.injectFixtureEnvironmentObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,6 +186,8 @@ struct VideoDetails: View {
|
|||||||
@Default(.playerSidebar) private var playerSidebar
|
@Default(.playerSidebar) private var playerSidebar
|
||||||
@Default(.showInspector) private var showInspector
|
@Default(.showInspector) private var showInspector
|
||||||
@Default(.showChapters) private var showChapters
|
@Default(.showChapters) private var showChapters
|
||||||
|
@Default(.showChapterThumbnails) private var showChapterThumbnails
|
||||||
|
@Default(.showChapterThumbnailsOnlyWhenDifferent) private var showChapterThumbnailsOnlyWhenDifferent
|
||||||
@Default(.showRelated) private var showRelated
|
@Default(.showRelated) private var showRelated
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
@Default(.showScrollToTopInComments) private var showScrollToTopInComments
|
@Default(.showScrollToTopInComments) private var showScrollToTopInComments
|
||||||
@ -287,17 +289,7 @@ struct VideoDetails: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageView: some View {
|
func infoView(video: Video) -> some View {
|
||||||
ScrollView(.vertical) {
|
|
||||||
LazyVStack {
|
|
||||||
pageMenu
|
|
||||||
.id(Self.pageMenuID)
|
|
||||||
.padding(5)
|
|
||||||
|
|
||||||
switch page {
|
|
||||||
case .info:
|
|
||||||
Group {
|
|
||||||
if let video {
|
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
if !player.videoBeingOpened.isNil && (video.description.isNil || video.description!.isEmpty) {
|
if !player.videoBeingOpened.isNil && (video.description.isNil || video.description!.isEmpty) {
|
||||||
VStack {
|
VStack {
|
||||||
@ -323,7 +315,7 @@ struct VideoDetails: View {
|
|||||||
!video.chapters.isEmpty
|
!video.chapters.isEmpty
|
||||||
{
|
{
|
||||||
Section(header: chaptersHeader) {
|
Section(header: chaptersHeader) {
|
||||||
ChaptersView(expand: $chaptersExpanded)
|
ChaptersView(expand: $chaptersExpanded, chaptersHaveImages: chaptersHaveImages, showThumbnails: showThumbnails)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,10 +334,8 @@ struct VideoDetails: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if video != nil, !pageAvailable(page) {
|
if !pageAvailable(page) {
|
||||||
page = .info
|
page = .info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,11 +344,23 @@ struct VideoDetails: View {
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.frame(maxWidth: YatteeApp.isForPreviews ? .infinity : maxWidth)
|
.frame(maxWidth: YatteeApp.isForPreviews ? .infinity : maxWidth)
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
var pageView: some View {
|
||||||
|
ScrollView(.vertical) {
|
||||||
|
LazyVStack {
|
||||||
|
pageMenu
|
||||||
|
.id(Self.pageMenuID)
|
||||||
|
.padding(5)
|
||||||
|
|
||||||
|
switch page {
|
||||||
|
case .info:
|
||||||
|
if let video = self.video {
|
||||||
|
infoView(video: video)
|
||||||
|
}
|
||||||
case .queue:
|
case .queue:
|
||||||
PlayerQueueView(sidebarQueue: false)
|
PlayerQueueView(sidebarQueue: false)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
case .comments:
|
case .comments:
|
||||||
CommentsView()
|
CommentsView()
|
||||||
.onAppear {
|
.onAppear {
|
||||||
@ -447,9 +449,27 @@ struct VideoDetails: View {
|
|||||||
player.videoForDisplay?.chapters.allSatisfy { $0.image != nil } ?? false
|
player.videoForDisplay?.chapters.allSatisfy { $0.image != nil } ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var chapterImagesTheSame: Bool {
|
||||||
|
guard let firstChapterURL = player.videoForDisplay?.chapters.first?.image else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return player.videoForDisplay?.chapters.allSatisfy { $0.image == firstChapterURL } ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
var showThumbnails: Bool {
|
||||||
|
if !chaptersHaveImages || !showChapterThumbnails {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if showChapterThumbnailsOnlyWhenDifferent {
|
||||||
|
return !chapterImagesTheSame
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
var chaptersHeader: some View {
|
var chaptersHeader: some View {
|
||||||
Group {
|
Group {
|
||||||
if !chaptersHaveImages {
|
if !chaptersHaveImages || !showThumbnails {
|
||||||
#if canImport(UIKit)
|
#if canImport(UIKit)
|
||||||
Button(action: {
|
Button(action: {
|
||||||
chaptersExpanded.toggle()
|
chaptersExpanded.toggle()
|
||||||
|
@ -32,6 +32,8 @@ struct PlayerSettings: View {
|
|||||||
|
|
||||||
@Default(.showInspector) private var showInspector
|
@Default(.showInspector) private var showInspector
|
||||||
@Default(.showChapters) private var showChapters
|
@Default(.showChapters) private var showChapters
|
||||||
|
@Default(.showChapterThumbnails) private var showThumbnails
|
||||||
|
@Default(.showChapterThumbnailsOnlyWhenDifferent) private var showThumbnailsOnlyWhenDifferent
|
||||||
@Default(.expandChapters) private var expandChapters
|
@Default(.expandChapters) private var expandChapters
|
||||||
@Default(.showRelated) private var showRelated
|
@Default(.showRelated) private var showRelated
|
||||||
|
|
||||||
@ -80,8 +82,6 @@ struct PlayerSettings: View {
|
|||||||
Section(header: SettingsHeader(text: "Info".localized())) {
|
Section(header: SettingsHeader(text: "Info".localized())) {
|
||||||
expandVideoDescriptionToggle
|
expandVideoDescriptionToggle
|
||||||
collapsedLineDescriptionStepper
|
collapsedLineDescriptionStepper
|
||||||
showChaptersToggle
|
|
||||||
expandChaptersToggle
|
|
||||||
showRelatedToggle
|
showRelatedToggle
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
HStack {
|
HStack {
|
||||||
@ -93,6 +93,13 @@ struct PlayerSettings: View {
|
|||||||
inspectorVisibilityPicker
|
inspectorVisibilityPicker
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section(header: SettingsHeader(text: "Chapters".localized())) {
|
||||||
|
showChaptersToggle
|
||||||
|
showThumbnailsToggle
|
||||||
|
showThumbnailsWhenDifferentToggle
|
||||||
|
expandChaptersToggle
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let interface = Section(header: SettingsHeader(text: "Interface".localized())) {
|
let interface = Section(header: SettingsHeader(text: "Interface".localized())) {
|
||||||
@ -284,7 +291,19 @@ struct PlayerSettings: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var showChaptersToggle: some View {
|
private var showChaptersToggle: some View {
|
||||||
Toggle("Chapters (if available)", isOn: $showChapters)
|
Toggle("Show chapters", isOn: $showChapters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var showThumbnailsToggle: some View {
|
||||||
|
Toggle("Show thumbnails", isOn: $showThumbnails)
|
||||||
|
.disabled(!showChapters)
|
||||||
|
.foregroundColor(showChapters ? .primary : .secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var showThumbnailsWhenDifferentToggle: some View {
|
||||||
|
Toggle("Show thumbnails only when unique", isOn: $showThumbnailsOnlyWhenDifferent)
|
||||||
|
.disabled(!showChapters || !showThumbnails)
|
||||||
|
.foregroundColor(showChapters && showThumbnails ? .primary : .secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var expandChaptersToggle: some View {
|
private var expandChaptersToggle: some View {
|
||||||
|
Loading…
Reference in New Issue
Block a user