mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
New chapters layout
This commit is contained in:
parent
d52ccf2ce6
commit
6596a440a5
@ -11,25 +11,53 @@ struct ChapterView: View {
|
||||
Button {
|
||||
player.backend.seek(to: chapter.start, seekType: .userInteracted)
|
||||
} label: {
|
||||
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)
|
||||
}
|
||||
Group {
|
||||
#if os(tvOS)
|
||||
horizontalChapter
|
||||
#else
|
||||
verticalChapter
|
||||
#endif
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.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 {
|
||||
WebImage(url: chapter.image, options: [.lowPriority])
|
||||
.resizable()
|
||||
@ -37,21 +65,20 @@ struct ChapterView: View {
|
||||
ProgressView()
|
||||
}
|
||||
.indicator(.activity)
|
||||
.frame(width: Self.thumbnailWidth, height: Self.thumbnailHeight)
|
||||
#if os(tvOS)
|
||||
.frame(width: thumbnailWidth, height: 140)
|
||||
.mask(RoundedRectangle(cornerRadius: 12))
|
||||
#else
|
||||
.frame(width: thumbnailWidth, height: 60)
|
||||
.mask(RoundedRectangle(cornerRadius: 6))
|
||||
#endif
|
||||
}
|
||||
|
||||
private var thumbnailWidth: Double {
|
||||
#if os(tvOS)
|
||||
250
|
||||
#else
|
||||
100
|
||||
#endif
|
||||
static var thumbnailWidth: Double {
|
||||
250
|
||||
}
|
||||
|
||||
static var thumbnailHeight: Double {
|
||||
thumbnailWidth / 1.7777
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,24 +5,32 @@ import SwiftUI
|
||||
struct ChaptersView: View {
|
||||
@ObservedObject private var player = PlayerModel.shared
|
||||
|
||||
var chapters: [Chapter] {
|
||||
player.videoForDisplay?.chapters ?? []
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if let chapters = player.currentVideo?.chapters, !chapters.isEmpty {
|
||||
List {
|
||||
Section {
|
||||
ForEach(chapters) { chapter in
|
||||
ChapterView(chapter: chapter)
|
||||
if !chapters.isEmpty {
|
||||
#if os(tvOS)
|
||||
List {
|
||||
Section {
|
||||
ForEach(chapters) { chapter in
|
||||
ChapterView(chapter: chapter)
|
||||
}
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
#if os(macOS)
|
||||
.listStyle(.inset)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.grouped)
|
||||
.backport
|
||||
.scrollContentBackground(false)
|
||||
.listStyle(.plain)
|
||||
#else
|
||||
.listStyle(.plain)
|
||||
ScrollView(.horizontal) {
|
||||
LazyHStack(spacing: 20) {
|
||||
ForEach(chapters) { chapter in
|
||||
ChapterView(chapter: chapter)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 15)
|
||||
}
|
||||
.frame(minHeight: ChapterView.thumbnailHeight + 100)
|
||||
#endif
|
||||
} else {
|
||||
NoCommentsView(text: "No chapters information available".localized(), systemImage: "xmark.circle.fill")
|
||||
|
@ -50,6 +50,7 @@ struct VideoDescription: View {
|
||||
|
||||
keywords
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
|
||||
var shouldExpand: Bool {
|
||||
|
@ -285,21 +285,37 @@ struct VideoDetails: View {
|
||||
} else if let description = video.description, !description.isEmpty {
|
||||
Section(header: descriptionHeader) {
|
||||
VideoDescription(video: video, detailsSize: detailsSize, expand: $descriptionExpanded)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
} else if !video.isLocal {
|
||||
Text("No description")
|
||||
.font(.caption)
|
||||
.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)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
if !sidebarQueue,
|
||||
if player.videoBeingOpened.isNil,
|
||||
!sidebarQueue,
|
||||
!(player.videoForDisplay?.related.isEmpty ?? true)
|
||||
{
|
||||
RelatedView()
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 20)
|
||||
}
|
||||
}
|
||||
@ -313,7 +329,6 @@ struct VideoDetails: View {
|
||||
}
|
||||
.transition(.opacity)
|
||||
.animation(nil, value: player.currentItem)
|
||||
.padding(.horizontal)
|
||||
#if os(iOS)
|
||||
.frame(maxWidth: YatteeApp.isForPreviews ? .infinity : maxWidth)
|
||||
#endif
|
||||
@ -360,9 +375,17 @@ struct VideoDetails: View {
|
||||
.imageScale(.small)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
var chaptersHeader: some View {
|
||||
Text("Chapters".localized())
|
||||
.padding(.horizontal)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
struct VideoDetails_Previews: PreviewProvider {
|
||||
|
@ -412,7 +412,7 @@ struct VideoPlayerView: View {
|
||||
List {
|
||||
PlayerQueueView(sidebarQueue: true)
|
||||
}
|
||||
.frame(maxWidth: 350)
|
||||
.frame(maxWidth: 450)
|
||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||
}
|
||||
#endif
|
||||
@ -431,6 +431,7 @@ struct VideoPlayerView: View {
|
||||
}
|
||||
)
|
||||
#endif
|
||||
.ignoresSafeArea(edges: .horizontal)
|
||||
}
|
||||
|
||||
var fullScreenPlayer: Bool {
|
||||
|
@ -561,6 +561,48 @@ struct SearchView: View {
|
||||
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 {
|
||||
|
@ -131,6 +131,7 @@ struct NowPlayingView: View {
|
||||
Section(header: Text("Chapters")) {
|
||||
ForEach(video.chapters) { chapter in
|
||||
ChapterView(chapter: chapter)
|
||||
.padding(.horizontal, 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user