New chapters layout

This commit is contained in:
Arkadiusz Fal 2023-04-22 20:06:30 +02:00
parent d52ccf2ce6
commit 6596a440a5
7 changed files with 142 additions and 39 deletions

View File

@ -11,25 +11,53 @@ struct ChapterView: View {
Button { Button {
player.backend.seek(to: chapter.start, seekType: .userInteracted) player.backend.seek(to: chapter.start, seekType: .userInteracted)
} label: { } label: {
HStack(spacing: 12) { Group {
if !chapter.image.isNil { #if os(tvOS)
smallImage(chapter) horizontalChapter
} #else
verticalChapter
VStack(alignment: .leading, spacing: 4) { #endif
Text(chapter.title)
.font(.headline)
Text(chapter.start.formattedAsPlaybackTime(allowZero: true) ?? "")
.font(.system(.subheadline).monospacedDigit())
.foregroundColor(.secondary)
}
} }
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle()) .contentShape(Rectangle())
} }
.buttonStyle(.plain) .buttonStyle(.plain)
} }
var horizontalChapter: some View {
HStack(spacing: 12) {
if !chapter.image.isNil {
smallImage(chapter)
}
VStack(alignment: .leading, spacing: 4) {
Text(chapter.title)
.font(.headline)
Text(chapter.start.formattedAsPlaybackTime(allowZero: true) ?? "")
.font(.system(.subheadline).monospacedDigit())
.foregroundColor(.secondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
var verticalChapter: some View {
VStack(spacing: 12) {
if !chapter.image.isNil {
smallImage(chapter)
}
VStack(alignment: .leading, spacing: 4) {
Text(chapter.title)
.lineLimit(2)
.multilineTextAlignment(.leading)
.font(.headline)
Text(chapter.start.formattedAsPlaybackTime(allowZero: true) ?? "")
.font(.system(.subheadline).monospacedDigit())
.foregroundColor(.secondary)
}
.frame(maxWidth: Self.thumbnailWidth, alignment: .leading)
}
}
@ViewBuilder func smallImage(_ chapter: Chapter) -> some View { @ViewBuilder func smallImage(_ chapter: Chapter) -> some View {
WebImage(url: chapter.image, options: [.lowPriority]) WebImage(url: chapter.image, options: [.lowPriority])
.resizable() .resizable()
@ -37,21 +65,20 @@ struct ChapterView: View {
ProgressView() ProgressView()
} }
.indicator(.activity) .indicator(.activity)
.frame(width: Self.thumbnailWidth, height: Self.thumbnailHeight)
#if os(tvOS) #if os(tvOS)
.frame(width: thumbnailWidth, height: 140)
.mask(RoundedRectangle(cornerRadius: 12)) .mask(RoundedRectangle(cornerRadius: 12))
#else #else
.frame(width: thumbnailWidth, height: 60)
.mask(RoundedRectangle(cornerRadius: 6)) .mask(RoundedRectangle(cornerRadius: 6))
#endif #endif
} }
private var thumbnailWidth: Double { static var thumbnailWidth: Double {
#if os(tvOS) 250
250 }
#else
100 static var thumbnailHeight: Double {
#endif thumbnailWidth / 1.7777
} }
} }

View File

@ -5,24 +5,32 @@ import SwiftUI
struct ChaptersView: View { struct ChaptersView: View {
@ObservedObject private var player = PlayerModel.shared @ObservedObject private var player = PlayerModel.shared
var chapters: [Chapter] {
player.videoForDisplay?.chapters ?? []
}
var body: some View { var body: some View {
if let chapters = player.currentVideo?.chapters, !chapters.isEmpty { if !chapters.isEmpty {
List { #if os(tvOS)
Section { List {
ForEach(chapters) { chapter in Section {
ChapterView(chapter: chapter) ForEach(chapters) { chapter in
ChapterView(chapter: chapter)
}
} }
.listRowBackground(Color.clear)
} }
.listRowBackground(Color.clear) .listStyle(.plain)
}
#if os(macOS)
.listStyle(.inset)
#elseif os(iOS)
.listStyle(.grouped)
.backport
.scrollContentBackground(false)
#else #else
.listStyle(.plain) ScrollView(.horizontal) {
LazyHStack(spacing: 20) {
ForEach(chapters) { chapter in
ChapterView(chapter: chapter)
}
}
.padding(.horizontal, 15)
}
.frame(minHeight: ChapterView.thumbnailHeight + 100)
#endif #endif
} else { } else {
NoCommentsView(text: "No chapters information available".localized(), systemImage: "xmark.circle.fill") NoCommentsView(text: "No chapters information available".localized(), systemImage: "xmark.circle.fill")

View File

@ -50,6 +50,7 @@ struct VideoDescription: View {
keywords keywords
} }
.contentShape(Rectangle())
} }
var shouldExpand: Bool { var shouldExpand: Bool {

View File

@ -285,21 +285,37 @@ struct VideoDetails: View {
} else if let description = video.description, !description.isEmpty { } else if let description = video.description, !description.isEmpty {
Section(header: descriptionHeader) { Section(header: descriptionHeader) {
VideoDescription(video: video, detailsSize: detailsSize, expand: $descriptionExpanded) VideoDescription(video: video, detailsSize: detailsSize, expand: $descriptionExpanded)
.padding(.horizontal)
} }
} else if !video.isLocal { } else if !video.isLocal {
Text("No description") Text("No description")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
.padding(.horizontal)
} }
if video.isLocal || showInspector == .always { if player.videoBeingOpened.isNil,
!video.isLocal,
!video.chapters.isEmpty
{
Section(header: chaptersHeader) {
ChaptersView()
}
}
if player.videoBeingOpened.isNil,
video.isLocal || showInspector == .always
{
InspectorView(video: player.videoForDisplay) InspectorView(video: player.videoForDisplay)
.padding(.horizontal)
} }
if !sidebarQueue, if player.videoBeingOpened.isNil,
!sidebarQueue,
!(player.videoForDisplay?.related.isEmpty ?? true) !(player.videoForDisplay?.related.isEmpty ?? true)
{ {
RelatedView() RelatedView()
.padding(.horizontal)
.padding(.top, 20) .padding(.top, 20)
} }
} }
@ -313,7 +329,6 @@ struct VideoDetails: View {
} }
.transition(.opacity) .transition(.opacity)
.animation(nil, value: player.currentItem) .animation(nil, value: player.currentItem)
.padding(.horizontal)
#if os(iOS) #if os(iOS)
.frame(maxWidth: YatteeApp.isForPreviews ? .infinity : maxWidth) .frame(maxWidth: YatteeApp.isForPreviews ? .infinity : maxWidth)
#endif #endif
@ -360,9 +375,17 @@ struct VideoDetails: View {
.imageScale(.small) .imageScale(.small)
} }
} }
.padding(.horizontal)
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
var chaptersHeader: some View {
Text("Chapters".localized())
.padding(.horizontal)
.font(.caption)
.foregroundColor(.secondary)
}
} }
struct VideoDetails_Previews: PreviewProvider { struct VideoDetails_Previews: PreviewProvider {

View File

@ -412,7 +412,7 @@ struct VideoPlayerView: View {
List { List {
PlayerQueueView(sidebarQueue: true) PlayerQueueView(sidebarQueue: true)
} }
.frame(maxWidth: 350) .frame(maxWidth: 450)
.background(colorScheme == .dark ? Color.black : Color.white) .background(colorScheme == .dark ? Color.black : Color.white)
} }
#endif #endif
@ -431,6 +431,7 @@ struct VideoPlayerView: View {
} }
) )
#endif #endif
.ignoresSafeArea(edges: .horizontal)
} }
var fullScreenPlayer: Bool { var fullScreenPlayer: Bool {

View File

@ -561,6 +561,48 @@ struct SearchView: View {
searchSortOrder.rawValue searchSortOrder.rawValue
)) ))
} }
var shouldDisplayHeader: Bool {
#if os(tvOS)
!state.query.isEmpty
#else
false
#endif
}
var header: some View {
HStack {
clearButton
#if os(tvOS)
if accounts.app.supportsSearchFilters {
filtersHorizontalStack
}
#endif
FavoriteButton(item: favoriteItem)
.id(favoriteItem?.id)
.labelStyle(.iconOnly)
.font(.system(size: 25))
Spacer()
ListingStyleButtons(listingStyle: $searchListingStyle)
HideShortsButtons(hide: $hideShorts)
}
.labelStyle(.iconOnly)
.padding(.leading, 30)
.padding(.bottom, 15)
.padding(.trailing, 30)
}
var clearButton: some View {
Button {
state.queryText = ""
} label: {
Label("Clear", systemImage: "xmark")
.labelStyle(.iconOnly)
}
.font(.caption)
}
} }
struct SearchView_Previews: PreviewProvider { struct SearchView_Previews: PreviewProvider {

View File

@ -131,6 +131,7 @@ struct NowPlayingView: View {
Section(header: Text("Chapters")) { Section(header: Text("Chapters")) {
ForEach(video.chapters) { chapter in ForEach(video.chapters) { chapter in
ChapterView(chapter: chapter) ChapterView(chapter: chapter)
.padding(.horizontal, 40)
} }
} }
} }