mirror of
				https://github.com/yattee/yattee.git
				synced 2025-11-03 22:22:02 +00:00 
			
		
		
		
	New chapters layout
This commit is contained in:
		@@ -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)
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user