mirror of
https://github.com/yattee/yattee.git
synced 2026-05-12 10:25:02 +00:00
Add List/Grid layout option for Home sections
Introduces a "Display sections as" picker in Home settings with List and Grid modes. Grid renders each section as a horizontal shelf of video cards, defaulting to Grid on tvOS and List on iOS/macOS. Per-platform defaults are preserved via a platform-specific settings key. On tvOS the shelf is a focus section so swiping up/down between rows of different lengths works without getting stuck at the end of a row.
This commit is contained in:
@@ -63,6 +63,7 @@ enum SettingsKey: String, CaseIterable {
|
|||||||
case homeSectionOrder
|
case homeSectionOrder
|
||||||
case homeSectionVisibility
|
case homeSectionVisibility
|
||||||
case homeSectionItemsLimit
|
case homeSectionItemsLimit
|
||||||
|
case homeSectionLayout
|
||||||
|
|
||||||
// Tab Bar (compact size class)
|
// Tab Bar (compact size class)
|
||||||
case tabBarItemOrder
|
case tabBarItemOrder
|
||||||
@@ -124,7 +125,7 @@ enum SettingsKey: String, CaseIterable {
|
|||||||
case .preferredQuality, .cellularQuality, .macPlayerMode, .listStyle,
|
case .preferredQuality, .cellularQuality, .macPlayerMode, .listStyle,
|
||||||
// Home layout — different UI paradigms per platform
|
// Home layout — different UI paradigms per platform
|
||||||
.homeShortcutOrder, .homeShortcutVisibility, .homeShortcutLayout,
|
.homeShortcutOrder, .homeShortcutVisibility, .homeShortcutLayout,
|
||||||
.homeSectionOrder, .homeSectionVisibility, .homeSectionItemsLimit,
|
.homeSectionOrder, .homeSectionVisibility, .homeSectionItemsLimit, .homeSectionLayout,
|
||||||
// Tab bar (compact size class) layout
|
// Tab bar (compact size class) layout
|
||||||
.tabBarItemOrder, .tabBarItemVisibility, .tabBarStartupTab,
|
.tabBarItemOrder, .tabBarItemVisibility, .tabBarStartupTab,
|
||||||
// Sidebar layout/selection
|
// Sidebar layout/selection
|
||||||
|
|||||||
@@ -95,6 +95,21 @@ extension SettingsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Layout mode for home sections (list or grid). Default is list on iOS/macOS, grid on tvOS.
|
||||||
|
var homeSectionLayout: HomeSectionLayout {
|
||||||
|
get {
|
||||||
|
if let cached = _homeSectionLayout { return cached }
|
||||||
|
guard let rawValue = string(for: .homeSectionLayout) else {
|
||||||
|
return HomeSectionLayout.platformDefault
|
||||||
|
}
|
||||||
|
return HomeSectionLayout(rawValue: rawValue) ?? HomeSectionLayout.platformDefault
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_homeSectionLayout = newValue
|
||||||
|
set(newValue.rawValue, for: .homeSectionLayout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Home Section Settings
|
// MARK: - Home Section Settings
|
||||||
|
|
||||||
/// Ordered list of home sections. Default order is bookmarks, history, downloads.
|
/// Ordered list of home sections. Default order is bookmarks, history, downloads.
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ final class SettingsManager {
|
|||||||
var _homeSectionOrder: [HomeSectionItem]?
|
var _homeSectionOrder: [HomeSectionItem]?
|
||||||
var _homeSectionVisibility: [HomeSectionItem: Bool]?
|
var _homeSectionVisibility: [HomeSectionItem: Bool]?
|
||||||
var _homeSectionItemsLimit: Int?
|
var _homeSectionItemsLimit: Int?
|
||||||
|
var _homeSectionLayout: HomeSectionLayout?
|
||||||
|
|
||||||
// Tab bar settings (compact size class only - iOS)
|
// Tab bar settings (compact size class only - iOS)
|
||||||
var _tabBarItemOrder: [TabBarItem]?
|
var _tabBarItemOrder: [TabBarItem]?
|
||||||
@@ -446,6 +447,7 @@ final class SettingsManager {
|
|||||||
_homeSectionOrder = nil
|
_homeSectionOrder = nil
|
||||||
_homeSectionVisibility = nil
|
_homeSectionVisibility = nil
|
||||||
_homeSectionItemsLimit = nil
|
_homeSectionItemsLimit = nil
|
||||||
|
_homeSectionLayout = nil
|
||||||
_tabBarItemOrder = nil
|
_tabBarItemOrder = nil
|
||||||
_tabBarItemVisibility = nil
|
_tabBarItemVisibility = nil
|
||||||
_sidebarMainItemOrder = nil
|
_sidebarMainItemOrder = nil
|
||||||
|
|||||||
@@ -4025,6 +4025,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"home.settings.sections.layout" : {
|
||||||
|
"comment" : "Label for sections layout picker in library settings",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Display sections as"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"home.settings.shortcuts.layout" : {
|
"home.settings.shortcuts.layout" : {
|
||||||
"comment" : "Label for shortcuts layout picker in library settings",
|
"comment" : "Label for shortcuts layout picker in library settings",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -4036,6 +4047,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"home.sections.layout.grid" : {
|
||||||
|
"comment" : "Grid layout option for library sections",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Grid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"home.sections.layout.list" : {
|
||||||
|
"comment" : "List layout option for library sections",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "List"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"home.settings.sourceDisabled" : {
|
"home.settings.sourceDisabled" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
53
Yattee/Views/Home/HomeHorizontalCards.swift
Normal file
53
Yattee/Views/Home/HomeHorizontalCards.swift
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// HomeHorizontalCards.swift
|
||||||
|
// Yattee
|
||||||
|
//
|
||||||
|
// Horizontal shelf of video cards for Home sections in grid layout mode.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// A horizontally scrolling row of `VideoCardView` cards used by Home sections
|
||||||
|
/// when `HomeSectionLayout.grid` is selected.
|
||||||
|
struct HomeHorizontalCards: View {
|
||||||
|
let videos: [Video]
|
||||||
|
let queueSource: QueueSource
|
||||||
|
let sourceLabel: String
|
||||||
|
var loadMoreVideos: LoadMoreVideosCallback? = nil
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
private let cardWidth: CGFloat = 320
|
||||||
|
private let cardHeight: CGFloat = 340
|
||||||
|
private let spacing: CGFloat = 60
|
||||||
|
private let verticalPadding: CGFloat = 28
|
||||||
|
#else
|
||||||
|
private let cardWidth: CGFloat = 180
|
||||||
|
private let cardHeight: CGFloat = 210
|
||||||
|
private let spacing: CGFloat = 28
|
||||||
|
private let verticalPadding: CGFloat = 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHStack(alignment: .top, spacing: spacing) {
|
||||||
|
ForEach(Array(videos.enumerated()), id: \.element.id) { index, video in
|
||||||
|
VideoCardView(video: video, isCompact: true)
|
||||||
|
.frame(width: cardWidth, height: cardHeight, alignment: .top)
|
||||||
|
.tappableVideo(
|
||||||
|
video,
|
||||||
|
queueSource: queueSource,
|
||||||
|
sourceLabel: sourceLabel,
|
||||||
|
videoList: videos,
|
||||||
|
videoIndex: index,
|
||||||
|
loadMoreVideos: loadMoreVideos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
.padding(.vertical, verticalPadding)
|
||||||
|
#if os(tvOS)
|
||||||
|
.focusSection()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,36 @@ enum HomeShortcutLayout: String, CaseIterable, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Home Section Layout
|
||||||
|
|
||||||
|
/// Layout mode for the configurable home sections (Continue Watching, Feed, etc.).
|
||||||
|
enum HomeSectionLayout: String, CaseIterable, Sendable {
|
||||||
|
case list
|
||||||
|
case grid
|
||||||
|
|
||||||
|
var displayName: LocalizedStringKey {
|
||||||
|
switch self {
|
||||||
|
case .list: return "home.sections.layout.list"
|
||||||
|
case .grid: return "home.sections.layout.grid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemImage: String {
|
||||||
|
switch self {
|
||||||
|
case .list: return "list.bullet"
|
||||||
|
case .grid: return "square.grid.2x2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var platformDefault: HomeSectionLayout {
|
||||||
|
#if os(tvOS)
|
||||||
|
return .grid
|
||||||
|
#else
|
||||||
|
return .list
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Instance Content Type
|
// MARK: - Instance Content Type
|
||||||
|
|
||||||
/// Content type for instance home items.
|
/// Content type for instance home items.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ struct HomeSettingsView: View {
|
|||||||
@State private var sectionOrder: [HomeSectionItem] = []
|
@State private var sectionOrder: [HomeSectionItem] = []
|
||||||
@State private var sectionVisibility: [HomeSectionItem: Bool] = [:]
|
@State private var sectionVisibility: [HomeSectionItem: Bool] = [:]
|
||||||
@State private var sectionItemsLimit: Int = 5
|
@State private var sectionItemsLimit: Int = 5
|
||||||
|
@State private var sectionLayout: HomeSectionLayout = HomeSectionLayout.platformDefault
|
||||||
|
|
||||||
// Available items (not yet added to Home)
|
// Available items (not yet added to Home)
|
||||||
@State private var availableShortcutsByInstance: [(instance: Instance, cards: [HomeShortcutItem])] = []
|
@State private var availableShortcutsByInstance: [(instance: Instance, cards: [HomeShortcutItem])] = []
|
||||||
@@ -125,6 +126,16 @@ struct HomeSettingsView: View {
|
|||||||
|
|
||||||
private var sectionsSection: some View {
|
private var sectionsSection: some View {
|
||||||
Section {
|
Section {
|
||||||
|
Picker(String(localized: "home.settings.sections.layout"), selection: $sectionLayout) {
|
||||||
|
ForEach(HomeSectionLayout.allCases, id: \.self) { layout in
|
||||||
|
Label(layout.displayName, systemImage: layout.systemImage)
|
||||||
|
.tag(layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
|
.pickerStyle(.segmented)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
ForEach(Array(sectionOrder.enumerated()), id: \.element.id) { index, section in
|
ForEach(Array(sectionOrder.enumerated()), id: \.element.id) { index, section in
|
||||||
if section != .downloads {
|
if section != .downloads {
|
||||||
@@ -257,6 +268,7 @@ struct HomeSettingsView: View {
|
|||||||
sectionOrder = settings.homeSectionOrder
|
sectionOrder = settings.homeSectionOrder
|
||||||
sectionVisibility = settings.homeSectionVisibility
|
sectionVisibility = settings.homeSectionVisibility
|
||||||
sectionItemsLimit = settings.homeSectionItemsLimit
|
sectionItemsLimit = settings.homeSectionItemsLimit
|
||||||
|
sectionLayout = settings.homeSectionLayout
|
||||||
|
|
||||||
// Load available items
|
// Load available items
|
||||||
let instances = env.instancesManager.instances
|
let instances = env.instancesManager.instances
|
||||||
@@ -276,6 +288,7 @@ struct HomeSettingsView: View {
|
|||||||
settings.homeSectionOrder = sectionOrder
|
settings.homeSectionOrder = sectionOrder
|
||||||
settings.homeSectionVisibility = sectionVisibility
|
settings.homeSectionVisibility = sectionVisibility
|
||||||
settings.homeSectionItemsLimit = sectionItemsLimit
|
settings.homeSectionItemsLimit = sectionItemsLimit
|
||||||
|
settings.homeSectionLayout = sectionLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Available Item Management
|
// MARK: - Available Item Management
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ struct HomeView: View {
|
|||||||
settingsManager?.homeShortcutLayout ?? .cards
|
settingsManager?.homeShortcutLayout ?? .cards
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The current layout for home sections (list or grid)
|
||||||
|
private var sectionLayout: HomeSectionLayout {
|
||||||
|
settingsManager?.homeSectionLayout ?? HomeSectionLayout.platformDefault
|
||||||
|
}
|
||||||
|
|
||||||
/// List style from centralized settings.
|
/// List style from centralized settings.
|
||||||
private var listStyle: VideoListStyle {
|
private var listStyle: VideoListStyle {
|
||||||
appEnvironment?.settingsManager.listStyle ?? .inset
|
appEnvironment?.settingsManager.listStyle ?? .inset
|
||||||
@@ -914,6 +919,14 @@ struct HomeView: View {
|
|||||||
appEnvironment?.navigationCoordinator.navigate(to: .continueWatching)
|
appEnvironment?.navigationCoordinator.navigate(to: .continueWatching)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sectionLayout == .grid {
|
||||||
|
HomeHorizontalCards(
|
||||||
|
videos: videoList,
|
||||||
|
queueSource: continueWatchingQueueSource,
|
||||||
|
sourceLabel: String(localized: "queue.source.continueWatching"),
|
||||||
|
loadMoreVideos: loadMoreContinueWatchingCallback
|
||||||
|
)
|
||||||
|
} else {
|
||||||
VideoListContent(listStyle: listStyle) {
|
VideoListContent(listStyle: listStyle) {
|
||||||
ForEach(Array(limitedEntries.enumerated()), id: \.element.videoIdentifier) { index, entry in
|
ForEach(Array(limitedEntries.enumerated()), id: \.element.videoIdentifier) { index, entry in
|
||||||
VideoListRow(
|
VideoListRow(
|
||||||
@@ -952,6 +965,7 @@ struct HomeView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -963,26 +977,35 @@ struct HomeView: View {
|
|||||||
appEnvironment?.navigationCoordinator.navigate(to: .subscriptionsFeed)
|
appEnvironment?.navigationCoordinator.navigate(to: .subscriptionsFeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoListContent(listStyle: listStyle) {
|
if sectionLayout == .grid {
|
||||||
ForEach(Array(limitedVideos.enumerated()), id: \.element.id) { index, video in
|
HomeHorizontalCards(
|
||||||
VideoListRow(
|
videos: limitedVideos,
|
||||||
isLast: index == limitedVideos.count - 1,
|
queueSource: feedQueueSource,
|
||||||
rowStyle: .regular,
|
sourceLabel: String(localized: "queue.source.subscriptions"),
|
||||||
listStyle: listStyle
|
loadMoreVideos: loadMoreFeedCallback
|
||||||
) {
|
)
|
||||||
VideoRowView(video: video, style: .regular)
|
} else {
|
||||||
.tappableVideo(
|
VideoListContent(listStyle: listStyle) {
|
||||||
video,
|
ForEach(Array(limitedVideos.enumerated()), id: \.element.id) { index, video in
|
||||||
queueSource: feedQueueSource,
|
VideoListRow(
|
||||||
sourceLabel: String(localized: "queue.source.subscriptions"),
|
isLast: index == limitedVideos.count - 1,
|
||||||
videoList: limitedVideos,
|
rowStyle: .regular,
|
||||||
videoIndex: index,
|
listStyle: listStyle
|
||||||
loadMoreVideos: loadMoreFeedCallback
|
) {
|
||||||
)
|
VideoRowView(video: video, style: .regular)
|
||||||
|
.tappableVideo(
|
||||||
|
video,
|
||||||
|
queueSource: feedQueueSource,
|
||||||
|
sourceLabel: String(localized: "queue.source.subscriptions"),
|
||||||
|
videoList: limitedVideos,
|
||||||
|
videoIndex: index,
|
||||||
|
loadMoreVideos: loadMoreFeedCallback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
|
.videoSwipeActions(video: video)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#if !os(tvOS)
|
|
||||||
.videoSwipeActions(video: video)
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -997,6 +1020,14 @@ struct HomeView: View {
|
|||||||
appEnvironment?.navigationCoordinator.navigate(to: .bookmarks)
|
appEnvironment?.navigationCoordinator.navigate(to: .bookmarks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sectionLayout == .grid {
|
||||||
|
HomeHorizontalCards(
|
||||||
|
videos: videoList,
|
||||||
|
queueSource: recentBookmarksQueueSource,
|
||||||
|
sourceLabel: String(localized: "queue.source.bookmarks"),
|
||||||
|
loadMoreVideos: loadMoreRecentBookmarksCallback
|
||||||
|
)
|
||||||
|
} else {
|
||||||
VideoListContent(listStyle: listStyle) {
|
VideoListContent(listStyle: listStyle) {
|
||||||
ForEach(Array(limitedBookmarks.enumerated()), id: \.element.videoID) { index, bookmark in
|
ForEach(Array(limitedBookmarks.enumerated()), id: \.element.videoID) { index, bookmark in
|
||||||
VideoListRow(
|
VideoListRow(
|
||||||
@@ -1035,6 +1066,7 @@ struct HomeView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1047,6 +1079,14 @@ struct HomeView: View {
|
|||||||
appEnvironment?.navigationCoordinator.navigate(to: .history)
|
appEnvironment?.navigationCoordinator.navigate(to: .history)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sectionLayout == .grid {
|
||||||
|
HomeHorizontalCards(
|
||||||
|
videos: videoList,
|
||||||
|
queueSource: recentHistoryQueueSource,
|
||||||
|
sourceLabel: String(localized: "queue.source.history"),
|
||||||
|
loadMoreVideos: loadMoreRecentHistoryCallback
|
||||||
|
)
|
||||||
|
} else {
|
||||||
VideoListContent(listStyle: listStyle) {
|
VideoListContent(listStyle: listStyle) {
|
||||||
ForEach(Array(limitedHistory.enumerated()), id: \.element.videoID) { index, entry in
|
ForEach(Array(limitedHistory.enumerated()), id: \.element.videoID) { index, entry in
|
||||||
VideoListRow(
|
VideoListRow(
|
||||||
@@ -1085,6 +1125,7 @@ struct HomeView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1100,6 +1141,14 @@ struct HomeView: View {
|
|||||||
appEnvironment?.navigationCoordinator.navigate(to: .downloads)
|
appEnvironment?.navigationCoordinator.navigate(to: .downloads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sectionLayout == .grid {
|
||||||
|
HomeHorizontalCards(
|
||||||
|
videos: videoList,
|
||||||
|
queueSource: .manual,
|
||||||
|
sourceLabel: String(localized: "queue.source.downloads"),
|
||||||
|
loadMoreVideos: loadMoreRecentDownloadsCallback
|
||||||
|
)
|
||||||
|
} else {
|
||||||
VideoListContent(listStyle: listStyle) {
|
VideoListContent(listStyle: listStyle) {
|
||||||
ForEach(Array(limitedDownloads.enumerated()), id: \.element.id) { index, download in
|
ForEach(Array(limitedDownloads.enumerated()), id: \.element.id) { index, download in
|
||||||
VideoListRow(
|
VideoListRow(
|
||||||
@@ -1139,6 +1188,7 @@ struct HomeView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1172,26 +1222,35 @@ struct HomeView: View {
|
|||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
VideoListContent(listStyle: listStyle) {
|
if sectionLayout == .grid {
|
||||||
ForEach(Array(limitedVideos.enumerated()), id: \.element.id) { index, video in
|
HomeHorizontalCards(
|
||||||
VideoListRow(
|
videos: limitedVideos,
|
||||||
isLast: index == limitedVideos.count - 1,
|
queueSource: instanceQueueSource(instanceID: instanceID, contentType: contentType),
|
||||||
rowStyle: .regular,
|
sourceLabel: contentType.localizedTitle,
|
||||||
listStyle: listStyle
|
loadMoreVideos: loadMoreInstanceContentCallback
|
||||||
) {
|
)
|
||||||
VideoRowView(video: video, style: .regular)
|
} else {
|
||||||
.tappableVideo(
|
VideoListContent(listStyle: listStyle) {
|
||||||
video,
|
ForEach(Array(limitedVideos.enumerated()), id: \.element.id) { index, video in
|
||||||
queueSource: instanceQueueSource(instanceID: instanceID, contentType: contentType),
|
VideoListRow(
|
||||||
sourceLabel: contentType.localizedTitle,
|
isLast: index == limitedVideos.count - 1,
|
||||||
videoList: limitedVideos,
|
rowStyle: .regular,
|
||||||
videoIndex: index,
|
listStyle: listStyle
|
||||||
loadMoreVideos: loadMoreInstanceContentCallback
|
) {
|
||||||
)
|
VideoRowView(video: video, style: .regular)
|
||||||
|
.tappableVideo(
|
||||||
|
video,
|
||||||
|
queueSource: instanceQueueSource(instanceID: instanceID, contentType: contentType),
|
||||||
|
sourceLabel: contentType.localizedTitle,
|
||||||
|
videoList: limitedVideos,
|
||||||
|
videoIndex: index,
|
||||||
|
loadMoreVideos: loadMoreInstanceContentCallback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
|
.videoSwipeActions(video: video)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#if !os(tvOS)
|
|
||||||
.videoSwipeActions(video: video)
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user