get latest items and tvos cinematic home view
This commit is contained in:
parent
d77f59d048
commit
5bfb8e240d
|
@ -68,9 +68,9 @@ internal enum L10n {
|
|||
internal static let genres = L10n.tr("Localizable", "genres")
|
||||
/// Home
|
||||
internal static let home = L10n.tr("Localizable", "home")
|
||||
/// Latest %@
|
||||
internal static func latestWithString(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "latestWithString", String(describing: p1))
|
||||
/// Latest in %@
|
||||
internal static func latestInWithString(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "latestInWithString", String(describing: p1))
|
||||
}
|
||||
/// Library
|
||||
internal static let library = L10n.tr("Localizable", "library")
|
||||
|
|
|
@ -14,10 +14,11 @@ import JellyfinAPI
|
|||
|
||||
final class HomeViewModel: ViewModel {
|
||||
|
||||
@Published var librariesShowRecentlyAddedIDs: [String] = []
|
||||
@Published var libraries: [BaseItemDto] = []
|
||||
@Published var latestAddedItems: [BaseItemDto] = []
|
||||
@Published var resumeItems: [BaseItemDto] = []
|
||||
@Published var nextUpItems: [BaseItemDto] = []
|
||||
@Published var librariesShowRecentlyAddedIDs: [String] = []
|
||||
@Published var libraries: [BaseItemDto] = []
|
||||
|
||||
// temp
|
||||
var recentFilterSet: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.descending], sortBy: [.dateAdded])
|
||||
|
@ -59,6 +60,7 @@ final class HomeViewModel: ViewModel {
|
|||
LogManager.shared.log.debug("Refresh called.")
|
||||
|
||||
refreshLibrariesLatest()
|
||||
refreshLatestAddedItems()
|
||||
refreshResumeItems()
|
||||
refreshNextUpItems()
|
||||
}
|
||||
|
@ -111,13 +113,34 @@ final class HomeViewModel: ViewModel {
|
|||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: Latest Added Items
|
||||
private func refreshLatestAddedItems() {
|
||||
UserLibraryAPI.getLatestMedia(userId: SessionManager.main.currentLogin.user.id,
|
||||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people, .chapters],
|
||||
enableImageTypes: [.primary, .backdrop, .thumb],
|
||||
enableUserData: true,
|
||||
limit: 8)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .finished: ()
|
||||
case .failure:
|
||||
self.nextUpItems = []
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
}
|
||||
} receiveValue: { items in
|
||||
LogManager.shared.log.debug("Retrieved \(String(items.count)) resume items")
|
||||
|
||||
self.latestAddedItems = items
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: Resume Items
|
||||
private func refreshResumeItems() {
|
||||
ItemsAPI.getResumeItems(userId: SessionManager.main.currentLogin.user.id, limit: 12,
|
||||
ItemsAPI.getResumeItems(userId: SessionManager.main.currentLogin.user.id,
|
||||
limit: 6,
|
||||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people, .chapters],
|
||||
mediaTypes: ["Video"],
|
||||
imageTypeLimit: 1,
|
||||
enableImageTypes: [.primary, .backdrop, .thumb])
|
||||
enableUserData: true)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
switch completion {
|
||||
|
@ -136,8 +159,10 @@ final class HomeViewModel: ViewModel {
|
|||
|
||||
// MARK: Next Up Items
|
||||
private func refreshNextUpItems() {
|
||||
TvShowsAPI.getNextUp(userId: SessionManager.main.currentLogin.user.id, limit: 12,
|
||||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people, .chapters])
|
||||
TvShowsAPI.getNextUp(userId: SessionManager.main.currentLogin.user.id,
|
||||
limit: 6,
|
||||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people, .chapters],
|
||||
enableUserData: true)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
switch completion {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import JellyfinAPI
|
||||
import Nuke
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
class DynamicCinematicBackgroundViewModel: ObservableObject {
|
||||
|
||||
@Published var currentItem: BaseItemDto?
|
||||
@Published var currentImageView: UIImageView?
|
||||
|
||||
func select(item: BaseItemDto) {
|
||||
|
||||
guard item.id != currentItem?.id else { return }
|
||||
|
||||
currentItem = item
|
||||
|
||||
let itemImageView = UIImageView()
|
||||
|
||||
let backdropImage: URL
|
||||
|
||||
if item.itemType == .episode {
|
||||
backdropImage = item.getSeriesBackdropImage(maxWidth: 1920)
|
||||
} else {
|
||||
backdropImage = item.getBackdropImage(maxWidth: 1920)
|
||||
}
|
||||
|
||||
let options = ImageLoadingOptions(transition: .fadeIn(duration: 0.2))
|
||||
|
||||
Nuke.loadImage(with: backdropImage, options: options, into: itemImageView, completion: { _ in })
|
||||
|
||||
currentImageView = itemImageView
|
||||
}
|
||||
}
|
||||
|
||||
struct CinematicBackgroundView: UIViewRepresentable {
|
||||
|
||||
@ObservedObject var viewModel: DynamicCinematicBackgroundViewModel
|
||||
|
||||
func updateUIView(_ uiView: UICinematicBackgroundView, context: Context) {
|
||||
uiView.update(imageView: viewModel.currentImageView ?? UIImageView())
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> UICinematicBackgroundView {
|
||||
return UICinematicBackgroundView(initialImageView: viewModel.currentImageView ?? UIImageView())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import JellyfinAPI
|
||||
import SwiftUI
|
||||
|
||||
struct CinematicNextUpCardView: View {
|
||||
|
||||
@EnvironmentObject var homeRouter: HomeCoordinator.Router
|
||||
let item: BaseItemDto
|
||||
let showOverlay: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Button {
|
||||
homeRouter.route(to: \.modalItem, item)
|
||||
} label: {
|
||||
ZStack(alignment: .bottomLeading) {
|
||||
|
||||
if item.itemType == .episode {
|
||||
ImageView(src: item.getSeriesBackdropImage(maxWidth: 350))
|
||||
.frame(width: 350, height: 210)
|
||||
} else {
|
||||
ImageView(src: item.getBackdropImage(maxWidth: 350))
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
|
||||
LinearGradient(colors: [.clear, .black],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom)
|
||||
.frame(height: 105)
|
||||
.ignoresSafeArea()
|
||||
|
||||
if showOverlay {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("Next")
|
||||
.font(.subheadline)
|
||||
.padding(.vertical, 5)
|
||||
.padding(.leading, 10)
|
||||
.foregroundColor(.white)
|
||||
|
||||
HStack {
|
||||
Color.clear
|
||||
.frame(width: 1, height: 7)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
.buttonStyle(CardButtonStyle())
|
||||
.padding(.top)
|
||||
}
|
||||
.padding(.vertical)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import JellyfinAPI
|
||||
import SwiftUI
|
||||
|
||||
struct CinematicResumeCardView: View {
|
||||
|
||||
@EnvironmentObject var homeRouter: HomeCoordinator.Router
|
||||
let item: BaseItemDto
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Button {
|
||||
homeRouter.route(to: \.modalItem, item)
|
||||
} label: {
|
||||
ZStack(alignment: .bottom) {
|
||||
|
||||
if item.itemType == .episode {
|
||||
ImageView(src: item.getSeriesBackdropImage(maxWidth: 350))
|
||||
.frame(width: 350, height: 210)
|
||||
} else {
|
||||
ImageView(src: item.getBackdropImage(maxWidth: 350))
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
|
||||
LinearGradient(colors: [.clear, .black],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom)
|
||||
.frame(height: 105)
|
||||
.ignoresSafeArea()
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(item.getItemProgressString() ?? "")
|
||||
.font(.subheadline)
|
||||
.padding(.vertical, 5)
|
||||
.padding(.leading, 10)
|
||||
.foregroundColor(.white)
|
||||
|
||||
HStack {
|
||||
Color(UIColor.systemPurple)
|
||||
.frame(width: 350 * (item.userData?.playedPercentage ?? 0) / 100, height: 7)
|
||||
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
.buttonStyle(CardButtonStyle())
|
||||
.padding(.top)
|
||||
}
|
||||
.padding(.vertical)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import JellyfinAPI
|
||||
|
||||
struct HomeCinematicViewItem: Hashable {
|
||||
|
||||
enum TopRowType {
|
||||
case resume
|
||||
case nextUp
|
||||
case plain
|
||||
}
|
||||
|
||||
let item: BaseItemDto
|
||||
let type: TopRowType
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(item)
|
||||
hasher.combine(type)
|
||||
}
|
||||
}
|
||||
|
||||
struct HomeCinematicView: View {
|
||||
|
||||
@FocusState var selectedItem: BaseItemDto?
|
||||
@State private var updatedSelectedItem: BaseItemDto?
|
||||
@State private var initiallyAppeared = false
|
||||
private let forcedItemSubtitle: String?
|
||||
private let items: [HomeCinematicViewItem]
|
||||
private let backgroundViewModel = DynamicCinematicBackgroundViewModel()
|
||||
|
||||
init(items: [HomeCinematicViewItem], forcedItemSubtitle: String? = nil) {
|
||||
self.items = items
|
||||
self.forcedItemSubtitle = forcedItemSubtitle
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
ZStack(alignment: .bottom) {
|
||||
|
||||
CinematicBackgroundView(viewModel: backgroundViewModel)
|
||||
.frame(height: UIScreen.main.bounds.height - 10)
|
||||
|
||||
LinearGradient(stops: [.init(color: .clear, location: 0.5),
|
||||
.init(color: .black.opacity(0.6), location: 0.7),
|
||||
.init(color: .black, location: 1)],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom)
|
||||
.ignoresSafeArea()
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
|
||||
if let forcedItemSubtitle = forcedItemSubtitle {
|
||||
Text(forcedItemSubtitle)
|
||||
.font(.callout)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(Color.secondary)
|
||||
} else {
|
||||
if updatedSelectedItem?.itemType == .episode {
|
||||
Text(updatedSelectedItem?.getEpisodeLocator() ?? "")
|
||||
.font(.callout)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(Color.secondary)
|
||||
} else {
|
||||
Text("")
|
||||
}
|
||||
}
|
||||
|
||||
Text("\(updatedSelectedItem?.seriesName ?? updatedSelectedItem?.name ?? "")")
|
||||
.font(.title)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.primary)
|
||||
.lineLimit(1)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.padding(.horizontal, 50)
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack {
|
||||
ForEach(items, id: \.self) { item in
|
||||
switch item.type {
|
||||
case .nextUp:
|
||||
CinematicNextUpCardView(item: item.item, showOverlay: true)
|
||||
.focused($selectedItem, equals: item.item)
|
||||
case .resume:
|
||||
CinematicResumeCardView(item: item.item)
|
||||
.focused($selectedItem, equals: item.item)
|
||||
case .plain:
|
||||
CinematicNextUpCardView(item: item.item, showOverlay: false)
|
||||
.focused($selectedItem, equals: item.item)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 50)
|
||||
.padding(.bottom)
|
||||
}
|
||||
.focusSection()
|
||||
}
|
||||
}
|
||||
.onChange(of: selectedItem) { newValue in
|
||||
if let newItem = newValue {
|
||||
backgroundViewModel.select(item: newItem)
|
||||
updatedSelectedItem = newItem
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
guard !initiallyAppeared else { return }
|
||||
selectedItem = items.first?.item
|
||||
updatedSelectedItem = items.first?.item
|
||||
initiallyAppeared = true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
class UICinematicBackgroundView: UIView {
|
||||
|
||||
private var currentImageView: UIView?
|
||||
|
||||
private var selectDelayTimer: Timer?
|
||||
|
||||
init(initialImageView: UIImageView) {
|
||||
super.init(frame: .zero)
|
||||
|
||||
initialImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
initialImageView.alpha = 0
|
||||
|
||||
addSubview(initialImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
initialImageView.topAnchor.constraint(equalTo: topAnchor),
|
||||
initialImageView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
initialImageView.leftAnchor.constraint(equalTo: leftAnchor),
|
||||
initialImageView.rightAnchor.constraint(equalTo: rightAnchor)
|
||||
])
|
||||
|
||||
self.currentImageView = initialImageView
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(imageView: UIImageView) {
|
||||
|
||||
selectDelayTimer?.invalidate()
|
||||
|
||||
selectDelayTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(delayTimerTimed), userInfo: imageView, repeats: false)
|
||||
|
||||
}
|
||||
|
||||
@objc private func delayTimerTimed(timer: Timer) {
|
||||
let newImageView = timer.userInfo as! UIImageView
|
||||
|
||||
newImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
newImageView.alpha = 0
|
||||
|
||||
addSubview(newImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
newImageView.topAnchor.constraint(equalTo: topAnchor),
|
||||
newImageView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
newImageView.leftAnchor.constraint(equalTo: leftAnchor),
|
||||
newImageView.rightAnchor.constraint(equalTo: rightAnchor)
|
||||
])
|
||||
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
newImageView.alpha = 1
|
||||
self.currentImageView?.alpha = 0
|
||||
} completion: { _ in
|
||||
self.currentImageView?.removeFromSuperview()
|
||||
self.currentImageView = newImageView
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -7,13 +7,16 @@
|
|||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Defaults
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import JellyfinAPI
|
||||
|
||||
struct HomeView: View {
|
||||
|
||||
@EnvironmentObject var homeRouter: HomeCoordinator.Router
|
||||
@ObservedObject var viewModel = HomeViewModel()
|
||||
@Default(.showPosterLabels) var showPosterLabels
|
||||
|
||||
@State var showingSettings = false
|
||||
|
||||
|
@ -24,16 +27,33 @@ struct HomeView: View {
|
|||
} else {
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading) {
|
||||
if !viewModel.resumeItems.isEmpty {
|
||||
ContinueWatchingView(items: viewModel.resumeItems)
|
||||
}
|
||||
|
||||
if !viewModel.nextUpItems.isEmpty {
|
||||
NextUpView(items: viewModel.nextUpItems)
|
||||
if viewModel.resumeItems.isEmpty {
|
||||
HomeCinematicView(items: viewModel.latestAddedItems.map({ .init(item: $0, type: .plain) }),
|
||||
forcedItemSubtitle: "Recently Added")
|
||||
|
||||
if !viewModel.nextUpItems.isEmpty {
|
||||
NextUpView(items: viewModel.nextUpItems)
|
||||
.focusSection()
|
||||
}
|
||||
} else {
|
||||
HomeCinematicView(items: viewModel.resumeItems.map({ .init(item: $0, type: .resume) }))
|
||||
|
||||
if !viewModel.nextUpItems.isEmpty {
|
||||
NextUpView(items: viewModel.nextUpItems)
|
||||
.focusSection()
|
||||
}
|
||||
|
||||
PortraitItemsRowView(rowTitle: "Recently Added",
|
||||
items: viewModel.latestAddedItems,
|
||||
showItemTitles: showPosterLabels) { item in
|
||||
homeRouter.route(to: \.item, item)
|
||||
}
|
||||
}
|
||||
|
||||
ForEach(viewModel.libraries, id: \.self) { library in
|
||||
LatestMediaView(viewModel: LatestMediaViewModel(library: library))
|
||||
.focusSection()
|
||||
}
|
||||
|
||||
Spacer(minLength: 100)
|
||||
|
@ -52,6 +72,7 @@ struct HomeView: View {
|
|||
.focusSection()
|
||||
}
|
||||
}
|
||||
.edgesIgnoringSafeArea(.top)
|
||||
.edgesIgnoringSafeArea(.horizontal)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ struct LatestMediaView: View {
|
|||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
L10n.latestWithString(viewModel.library.name ?? "").text
|
||||
L10n.latestInWithString(viewModel.library.name ?? "").text
|
||||
.font(.title3)
|
||||
.padding(.horizontal, 50)
|
||||
|
||||
|
|
|
@ -246,6 +246,11 @@
|
|||
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */; };
|
||||
C4E52305272CE68800654268 /* LiveTVChannelItemElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */; };
|
||||
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; };
|
||||
E103A6A0278A7E4500820EC7 /* UICinematicBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A69F278A7E4500820EC7 /* UICinematicBackgroundView.swift */; };
|
||||
E103A6A3278A7EC400820EC7 /* CinematicBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A6A2278A7EC400820EC7 /* CinematicBackgroundView.swift */; };
|
||||
E103A6A5278A82E500820EC7 /* HomeCinematicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A6A4278A82E500820EC7 /* HomeCinematicView.swift */; };
|
||||
E103A6A7278AB6D700820EC7 /* CinematicResumeCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A6A6278AB6D700820EC7 /* CinematicResumeCardView.swift */; };
|
||||
E103A6A9278AB6FF00820EC7 /* CinematicNextUpCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A6A8278AB6FF00820EC7 /* CinematicNextUpCardView.swift */; };
|
||||
E107BB9327880A8F00354E07 /* CollectionItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */; };
|
||||
E107BB9427880A8F00354E07 /* CollectionItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */; };
|
||||
E107BB972788104100354E07 /* CinematicCollectionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB952788104100354E07 /* CinematicCollectionItemView.swift */; };
|
||||
|
@ -638,6 +643,11 @@
|
|||
C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; };
|
||||
C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelItemElement.swift; sourceTree = "<group>"; };
|
||||
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayButtonRowView.swift; sourceTree = "<group>"; };
|
||||
E103A69F278A7E4500820EC7 /* UICinematicBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICinematicBackgroundView.swift; sourceTree = "<group>"; };
|
||||
E103A6A2278A7EC400820EC7 /* CinematicBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicBackgroundView.swift; sourceTree = "<group>"; };
|
||||
E103A6A4278A82E500820EC7 /* HomeCinematicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCinematicView.swift; sourceTree = "<group>"; };
|
||||
E103A6A6278AB6D700820EC7 /* CinematicResumeCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicResumeCardView.swift; sourceTree = "<group>"; };
|
||||
E103A6A8278AB6FF00820EC7 /* CinematicNextUpCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicNextUpCardView.swift; sourceTree = "<group>"; };
|
||||
E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionItemViewModel.swift; sourceTree = "<group>"; };
|
||||
E107BB952788104100354E07 /* CinematicCollectionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicCollectionItemView.swift; sourceTree = "<group>"; };
|
||||
E10D87D92784E4F100BD264C /* ItemViewDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewDetailsView.swift; sourceTree = "<group>"; };
|
||||
|
@ -977,6 +987,7 @@
|
|||
E1E5D5412783B33900692DFE /* PortraitItemsRowView.swift */,
|
||||
536D3D87267C17350004248C /* PublicUserButton.swift */,
|
||||
E17885A3278105170094FBCF /* SFSymbolButton.swift */,
|
||||
E103A6A1278A7EB500820EC7 /* HomeCinematicView */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1312,6 +1323,18 @@
|
|||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E103A6A1278A7EB500820EC7 /* HomeCinematicView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E103A6A2278A7EC400820EC7 /* CinematicBackgroundView.swift */,
|
||||
E103A6A8278AB6FF00820EC7 /* CinematicNextUpCardView.swift */,
|
||||
E103A6A6278AB6D700820EC7 /* CinematicResumeCardView.swift */,
|
||||
E103A6A4278A82E500820EC7 /* HomeCinematicView.swift */,
|
||||
E103A69F278A7E4500820EC7 /* UICinematicBackgroundView.swift */,
|
||||
);
|
||||
path = HomeCinematicView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E107BB9127880A4000354E07 /* ItemViewModel */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2039,6 +2062,7 @@
|
|||
E193D4DC27193CCA00900D82 /* PillStackable.swift in Sources */,
|
||||
E193D53327193F7D00900D82 /* FilterCoordinator.swift in Sources */,
|
||||
6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */,
|
||||
E103A6A9278AB6FF00820EC7 /* CinematicNextUpCardView.swift in Sources */,
|
||||
E107BB9427880A8F00354E07 /* CollectionItemViewModel.swift in Sources */,
|
||||
C40CD929271F8DAB000FB198 /* MovieLibrariesView.swift in Sources */,
|
||||
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */,
|
||||
|
@ -2064,10 +2088,12 @@
|
|||
53CD2A40268A49C2002ABD4E /* ItemView.swift in Sources */,
|
||||
53CD2A42268A4B38002ABD4E /* MovieItemView.swift in Sources */,
|
||||
E122A9142788EAAD0060FA63 /* MediaStreamExtension.swift in Sources */,
|
||||
E103A6A3278A7EC400820EC7 /* CinematicBackgroundView.swift in Sources */,
|
||||
E178859E2780F53B0094FBCF /* SliderView.swift in Sources */,
|
||||
536D3D7F267BDF100004248C /* LatestMediaView.swift in Sources */,
|
||||
E1384944278036C70024FB48 /* VLCPlayerViewController.swift in Sources */,
|
||||
091B5A8E268315D400D78B61 /* UDPBroadCastConnection.swift in Sources */,
|
||||
E103A6A5278A82E500820EC7 /* HomeCinematicView.swift in Sources */,
|
||||
E10EAA50277BBCC4000269ED /* CGSizeExtensions.swift in Sources */,
|
||||
E126F742278A656C00A522BF /* ServerStreamType.swift in Sources */,
|
||||
E1E5D5422783B33900692DFE /* PortraitItemsRowView.swift in Sources */,
|
||||
|
@ -2098,6 +2124,7 @@
|
|||
C4BE0767271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */,
|
||||
E193D53727193F8700900D82 /* LibraryListCoordinator.swift in Sources */,
|
||||
E10D87DF278510E400BD264C /* PosterSize.swift in Sources */,
|
||||
E103A6A0278A7E4500820EC7 /* UICinematicBackgroundView.swift in Sources */,
|
||||
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */,
|
||||
E17885A02780F55C0094FBCF /* tvOSVLCOverlay.swift in Sources */,
|
||||
E193D54D2719426600900D82 /* LibraryFilterView.swift in Sources */,
|
||||
|
@ -2122,6 +2149,7 @@
|
|||
62E632DD267D2E130063E547 /* LibrarySearchViewModel.swift in Sources */,
|
||||
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
|
||||
E1AA33232782648000F6439C /* OverlaySliderColor.swift in Sources */,
|
||||
E103A6A7278AB6D700820EC7 /* CinematicResumeCardView.swift in Sources */,
|
||||
62E1DCC4273CE19800C9AE76 /* URLExtensions.swift in Sources */,
|
||||
5398514726B64E4100101B49 /* SearchBarView.swift in Sources */,
|
||||
E10EAA54277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */,
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue