Transition Media View (#541)
This commit is contained in:
parent
98a5507b52
commit
d078d71393
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class LibraryListCoordinator: NavigationCoordinatable {
|
||||
|
||||
let stack = NavigationStack(initial: \LibraryListCoordinator.start)
|
||||
|
||||
@Root
|
||||
var start = makeStart
|
||||
@Route(.push)
|
||||
var library = makeLibrary
|
||||
#if os(iOS)
|
||||
@Route(.push)
|
||||
var liveTV = makeLiveTV
|
||||
#endif
|
||||
|
||||
let viewModel: LibraryListViewModel
|
||||
|
||||
init(viewModel: LibraryListViewModel) {
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
func makeLibrary(params: LibraryCoordinatorParams) -> LibraryCoordinator {
|
||||
LibraryCoordinator(viewModel: params.viewModel, title: params.title)
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
func makeLiveTV() -> LiveTVCoordinator {
|
||||
LiveTVCoordinator()
|
||||
}
|
||||
#endif
|
||||
|
||||
@ViewBuilder
|
||||
func makeStart() -> some View {
|
||||
LibraryListView(viewModel: self.viewModel)
|
||||
}
|
||||
}
|
|
@ -14,15 +14,15 @@ final class MainTabCoordinator: TabCoordinatable {
|
|||
var child = TabChild(startingItems: [
|
||||
\MainTabCoordinator.home,
|
||||
\MainTabCoordinator.search,
|
||||
\MainTabCoordinator.allMedia,
|
||||
\MainTabCoordinator.media,
|
||||
])
|
||||
|
||||
@Route(tabItem: makeHomeTab, onTapped: onHomeTapped)
|
||||
var home = makeHome
|
||||
@Route(tabItem: makeSearchTab, onTapped: onSearchTapped)
|
||||
var search = makeSearch
|
||||
@Route(tabItem: makeAllMediaTab, onTapped: onMediaTapped)
|
||||
var allMedia = makeAllMedia
|
||||
@Route(tabItem: makeMediaTab, onTapped: onMediaTapped)
|
||||
var media = makeMedia
|
||||
|
||||
func makeHome() -> NavigationViewCoordinator<HomeCoordinator> {
|
||||
NavigationViewCoordinator(HomeCoordinator())
|
||||
|
@ -56,20 +56,20 @@ final class MainTabCoordinator: TabCoordinatable {
|
|||
L10n.search.text
|
||||
}
|
||||
|
||||
func makeAllMedia() -> NavigationViewCoordinator<LibraryListCoordinator> {
|
||||
NavigationViewCoordinator(LibraryListCoordinator(viewModel: LibraryListViewModel()))
|
||||
func makeMedia() -> NavigationViewCoordinator<MediaCoordinator> {
|
||||
NavigationViewCoordinator(MediaCoordinator())
|
||||
}
|
||||
|
||||
func onMediaTapped(isRepeat: Bool, coordinator: NavigationViewCoordinator<LibraryListCoordinator>) {
|
||||
func onMediaTapped(isRepeat: Bool, coordinator: NavigationViewCoordinator<MediaCoordinator>) {
|
||||
if isRepeat {
|
||||
coordinator.child.popToRoot()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func makeAllMediaTab(isActive: Bool) -> some View {
|
||||
Image(systemName: "folder")
|
||||
L10n.allMedia.text
|
||||
func makeMediaTab(isActive: Bool) -> some View {
|
||||
Image(systemName: "rectangle.stack.fill")
|
||||
L10n.media.text
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
|
|
@ -16,7 +16,7 @@ final class MainTabCoordinator: TabCoordinatable {
|
|||
\MainTabCoordinator.tv,
|
||||
\MainTabCoordinator.movies,
|
||||
\MainTabCoordinator.search,
|
||||
\MainTabCoordinator.other,
|
||||
\MainTabCoordinator.media,
|
||||
\MainTabCoordinator.settings,
|
||||
])
|
||||
|
||||
|
@ -28,8 +28,8 @@ final class MainTabCoordinator: TabCoordinatable {
|
|||
var movies = makeMovies
|
||||
@Route(tabItem: makeSearchTab)
|
||||
var search = makeSearch
|
||||
@Route(tabItem: makeOtherTab)
|
||||
var other = makeOther
|
||||
@Route(tabItem: makeMediaTab)
|
||||
var media = makeMedia
|
||||
@Route(tabItem: makeSettingsTab)
|
||||
var settings = makeSettings
|
||||
|
||||
|
@ -81,15 +81,15 @@ final class MainTabCoordinator: TabCoordinatable {
|
|||
}
|
||||
}
|
||||
|
||||
func makeOther() -> NavigationViewCoordinator<LibraryListCoordinator> {
|
||||
NavigationViewCoordinator(LibraryListCoordinator(viewModel: LibraryListViewModel()))
|
||||
func makeMedia() -> NavigationViewCoordinator<MediaCoordinator> {
|
||||
NavigationViewCoordinator(MediaCoordinator())
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func makeOtherTab(isActive: Bool) -> some View {
|
||||
func makeMediaTab(isActive: Bool) -> some View {
|
||||
HStack {
|
||||
Image(systemName: "folder")
|
||||
L10n.other.text
|
||||
Image(systemName: "rectangle.stack")
|
||||
L10n.media.text
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class MediaCoordinator: NavigationCoordinatable {
|
||||
|
||||
let stack = NavigationStack(initial: \MediaCoordinator.start)
|
||||
|
||||
@Root
|
||||
var start = makeStart
|
||||
#if os(tvOS)
|
||||
@Route(.modal)
|
||||
var library = makeLibrary
|
||||
#else
|
||||
@Route(.push)
|
||||
var library = makeLibrary
|
||||
@Route(.push)
|
||||
var liveTV = makeLiveTV
|
||||
#endif
|
||||
|
||||
#if os(tvOS)
|
||||
func makeLibrary(params: LibraryCoordinatorParams) -> NavigationViewCoordinator<LibraryCoordinator> {
|
||||
NavigationViewCoordinator(LibraryCoordinator(viewModel: params.viewModel, title: params.title))
|
||||
}
|
||||
|
||||
#else
|
||||
func makeLibrary(params: LibraryCoordinatorParams) -> LibraryCoordinator {
|
||||
LibraryCoordinator(viewModel: params.viewModel, title: params.title)
|
||||
}
|
||||
|
||||
func makeLiveTV() -> LiveTVCoordinator {
|
||||
LiveTVCoordinator()
|
||||
}
|
||||
#endif
|
||||
|
||||
@ViewBuilder
|
||||
func makeStart() -> some View {
|
||||
MediaView(viewModel: .init())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Array {
|
||||
func appending(_ element: Element) -> [Element] {
|
||||
self + [element]
|
||||
}
|
||||
|
||||
func appending(_ element: Element, if condition: Bool) -> [Element] {
|
||||
if condition {
|
||||
return self + [element]
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
func appending(_ contents: [Element]) -> [Element] {
|
||||
self + contents
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import JellyfinAPI
|
||||
import SwiftUI
|
||||
|
||||
struct LibraryItem: Equatable, Poster {
|
||||
|
||||
var library: BaseItemDto
|
||||
var viewModel: MediaViewModel
|
||||
var title: String = ""
|
||||
var subtitle: String?
|
||||
var showTitle: Bool = false
|
||||
|
||||
func portraitPosterImageSource(maxWidth: CGFloat) -> ImageSource {
|
||||
.init()
|
||||
}
|
||||
|
||||
func landscapePosterImageSources(maxWidth: CGFloat, single: Bool) -> [ImageSource] {
|
||||
viewModel.libraryImages[library.id ?? ""] ?? []
|
||||
}
|
||||
|
||||
static func == (lhs: LibraryItem, rhs: LibraryItem) -> Bool {
|
||||
lhs.library == rhs.library &&
|
||||
lhs.viewModel.libraryImages[lhs.library.id ?? ""] == rhs.viewModel.libraryImages[rhs.library.id ?? ""]
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ struct LibraryFilters: Codable, Hashable {
|
|||
var sortBy: [SortBy] = [.name]
|
||||
|
||||
static let `default` = LibraryFilters()
|
||||
static let favorites: LibraryFilters = .init(filters: [.isFavorite], sortOrder: [.ascending], sortBy: [.name])
|
||||
}
|
||||
|
||||
public enum SortBy: String, Codable, CaseIterable {
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
final class LibraryListViewModel: ViewModel {
|
||||
|
||||
@Published
|
||||
var libraries: [BaseItemDto] = []
|
||||
|
||||
var filteredLibraries: [BaseItemDto] {
|
||||
var supportedLibraries = ["movies", "tvshows", "unknown"]
|
||||
|
||||
if Defaults[.Experimental.liveTVAlphaEnabled] {
|
||||
supportedLibraries.append("livetv")
|
||||
}
|
||||
|
||||
return libraries.filter { supportedLibraries.contains($0.collectionType ?? "unknown") }
|
||||
}
|
||||
|
||||
// temp
|
||||
let withFavorites = LibraryFilters(filters: [.isFavorite], sortOrder: [], withGenres: [], sortBy: [])
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
requestLibraries()
|
||||
}
|
||||
|
||||
func requestLibraries() {
|
||||
UserViewsAPI.getUserViews(userId: SessionManager.main.currentLogin.user.id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
self.libraries = response.items ?? []
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
final class MediaViewModel: ViewModel {
|
||||
|
||||
@Published
|
||||
var libraries: [BaseItemDto] = []
|
||||
@Published
|
||||
var libraryImages: [String: [ImageSource]] = [:]
|
||||
|
||||
private var supportedLibraries: [String] {
|
||||
["movies", "tvshows", "unknown"]
|
||||
.appending("livetv", if: Defaults[.Experimental.liveTVAlphaEnabled])
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
requestLibraries()
|
||||
getRandomItemImageSource(with: [.isFavorite], id: nil, key: "favorites")
|
||||
}
|
||||
|
||||
func requestLibraries() {
|
||||
UserViewsAPI.getUserViews(userId: SessionManager.main.currentLogin.user.id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
guard let items = response.items else { return }
|
||||
self.libraries = items.filter { self.supportedLibraries.contains($0.collectionType ?? "unknown") }
|
||||
self.libraries.forEach {
|
||||
self.getRandomItemImageSource(with: nil, id: $0.id, key: $0.id ?? "")
|
||||
}
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func getRandomItemImageSource(with filters: [ItemFilter]?, id: String?, key: String) {
|
||||
ItemsAPI.getItemsByUserId(
|
||||
userId: SessionManager.main.currentLogin.user.id,
|
||||
limit: 3,
|
||||
recursive: true,
|
||||
parentId: id,
|
||||
includeItemTypes: [.movie, .series],
|
||||
filters: filters,
|
||||
sortBy: ["Random"]
|
||||
)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
guard let items = response.items else { return }
|
||||
let imageSources = items.map { $0.imageSource(.backdrop, maxWidth: 500) }
|
||||
self?.libraryImages[key] = imageSources
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
|
@ -53,7 +53,6 @@ final class SearchViewModel: ViewModel {
|
|||
|
||||
private func cancelPreviousSearch() {
|
||||
searchCancellables.forEach { $0.cancel() }
|
||||
print(searchCancellables.count)
|
||||
}
|
||||
|
||||
func search(with query: String) {
|
||||
|
|
|
@ -61,12 +61,18 @@ struct PosterButton<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
Button {
|
||||
onSelect(item)
|
||||
} label: {
|
||||
switch type {
|
||||
case .portrait:
|
||||
ImageView(item.portraitPosterImageSource(maxWidth: itemWidth))
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
case .landscape:
|
||||
ImageView(item.landscapePosterImageSources(maxWidth: itemWidth, single: singleImage))
|
||||
Group {
|
||||
switch type {
|
||||
case .portrait:
|
||||
ImageView(item.portraitPosterImageSource(maxWidth: itemWidth))
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
case .landscape:
|
||||
ImageView(item.landscapePosterImageSources(maxWidth: itemWidth, single: singleImage))
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
imageOverlay(item)
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
}
|
||||
}
|
||||
|
@ -74,10 +80,6 @@ struct PosterButton<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
.contextMenu(menuItems: {
|
||||
contextMenu(item)
|
||||
})
|
||||
.overlay {
|
||||
imageOverlay(item)
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
}
|
||||
.posterShadow()
|
||||
|
||||
content(item)
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
struct LibraryListView: View {
|
||||
@EnvironmentObject
|
||||
var mainCoordinator: MainCoordinator.Router
|
||||
@EnvironmentObject
|
||||
private var libraryListRouter: LibraryListCoordinator.Router
|
||||
@StateObject
|
||||
var viewModel = LibraryListViewModel()
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
if !viewModel.isLoading {
|
||||
|
||||
ForEach(viewModel.filteredLibraries, id: \.id) { library in
|
||||
Button {
|
||||
if library.collectionType == "livetv" {
|
||||
self.mainCoordinator.root(\.liveTV)
|
||||
} else {
|
||||
self.libraryListRouter.route(
|
||||
to: \.library,
|
||||
(viewModel: LibraryViewModel(parentID: library.id), title: library.name ?? "")
|
||||
)
|
||||
}
|
||||
}
|
||||
label: {
|
||||
ZStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
Text(library.name ?? "")
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
Spacer()
|
||||
}.padding(32)
|
||||
}
|
||||
.frame(minWidth: 100, maxWidth: .infinity)
|
||||
.frame(height: 100)
|
||||
}
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 5)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
}.padding(.leading, 16)
|
||||
.padding(.trailing, 16)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import CollectionView
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
struct MediaView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var router: MediaCoordinator.Router
|
||||
@ObservedObject
|
||||
var viewModel: MediaViewModel
|
||||
|
||||
private var libraryItems: [LibraryItem] {
|
||||
[LibraryItem(library: .init(name: L10n.favorites, id: "favorites"), viewModel: viewModel)] +
|
||||
viewModel.libraries.map { LibraryItem(library: $0, viewModel: viewModel) }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
CollectionView(items: libraryItems) { _, item, _ in
|
||||
PosterButton(item: item, type: .landscape)
|
||||
.scaleItem(0.8)
|
||||
.onSelect { _ in
|
||||
if item.library.id == "favorites" {
|
||||
router.route(to: \.library, (viewModel: .init(filters: .favorites), title: ""))
|
||||
} else {
|
||||
router.route(to: \.library, (viewModel: .init(parentID: item.library.id), title: ""))
|
||||
}
|
||||
}
|
||||
.imageOverlay { _ in
|
||||
ZStack {
|
||||
Color.black
|
||||
.opacity(0.5)
|
||||
|
||||
Text(item.library.displayName)
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
.lineLimit(2)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(alignment: .center)
|
||||
}
|
||||
}
|
||||
}
|
||||
.layout { _, layoutEnvironment in
|
||||
.grid(
|
||||
layoutEnvironment: layoutEnvironment,
|
||||
layoutMode: .adaptive(withMinItemSize: 400),
|
||||
lineSpacing: 50,
|
||||
itemSize: .estimated(400),
|
||||
sectionInsets: .zero
|
||||
)
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
|
@ -75,7 +75,7 @@
|
|||
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */; };
|
||||
53A83C33268A309300DF3D92 /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A83C32268A309300DF3D92 /* LibraryView.swift */; };
|
||||
53ABFDDC267972BF00886593 /* TVServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53ABFDDB267972BF00886593 /* TVServices.framework */; };
|
||||
53ABFDE4267974EF00886593 /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; };
|
||||
53ABFDE4267974EF00886593 /* MediaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* MediaViewModel.swift */; };
|
||||
53ABFDE5267974EF00886593 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB57B2678CE1000530A6E /* ViewModel.swift */; };
|
||||
53ABFDE6267974EF00886593 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; };
|
||||
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */; };
|
||||
|
@ -90,7 +90,7 @@
|
|||
5D160403278A41FD00D22B99 /* VLCPlayer+subtitles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D160402278A41FD00D22B99 /* VLCPlayer+subtitles.swift */; };
|
||||
5D32EA12278C95E30020E292 /* VLCPlayer+subtitles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D160402278A41FD00D22B99 /* VLCPlayer+subtitles.swift */; };
|
||||
5D64683D277B1649009E09AE /* PreferenceUIHostingSwizzling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D64683C277B1649009E09AE /* PreferenceUIHostingSwizzling.swift */; };
|
||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* LibraryListView.swift */; };
|
||||
62133890265F83A900A81A2A /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* MediaView.swift */; };
|
||||
621338932660107500A81A2A /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
|
||||
6220D0AD26D5EABB00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; };
|
||||
6220D0AE26D5EABB00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; };
|
||||
|
@ -105,7 +105,7 @@
|
|||
62400C4B287ED19600F6AD3D /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; };
|
||||
62400C4C287ED19600F6AD3D /* UDPBroadcast.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5722678C32A00530A6E /* HomeViewModel.swift */; };
|
||||
625CB5752678C33500530A6E /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; };
|
||||
625CB5752678C33500530A6E /* MediaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* MediaViewModel.swift */; };
|
||||
625CB5772678C34300530A6E /* ConnectToServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */; };
|
||||
625CB57A2678C4A400530A6E /* ActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 625CB5792678C4A400530A6E /* ActivityIndicator */; };
|
||||
6264E88C273850380081A12A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6264E88B273850380081A12A /* Strings.swift */; };
|
||||
|
@ -152,7 +152,7 @@
|
|||
62C29EA126D102A500C1D2E7 /* iOSMainTabCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA026D102A500C1D2E7 /* iOSMainTabCoordinator.swift */; };
|
||||
62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA226D1030F00C1D2E7 /* ConnectToServerCoodinator.swift */; };
|
||||
62C29EA626D1036A00C1D2E7 /* HomeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */; };
|
||||
62C29EA826D103D500C1D2E7 /* LibraryListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */; };
|
||||
62C29EA826D103D500C1D2E7 /* MediaCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA726D103D500C1D2E7 /* MediaCoordinator.swift */; };
|
||||
62C83B08288C6A630004ED0C /* FontPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C83B07288C6A630004ED0C /* FontPicker.swift */; };
|
||||
62E1DCC3273CE19800C9AE76 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E1DCC2273CE19800C9AE76 /* URLExtensions.swift */; };
|
||||
62E1DCC4273CE19800C9AE76 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E1DCC2273CE19800C9AE76 /* URLExtensions.swift */; };
|
||||
|
@ -220,7 +220,7 @@
|
|||
C4BE07892728448B003F4AD1 /* LiveTVChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE07872728448B003F4AD1 /* LiveTVChannelsCoordinator.swift */; };
|
||||
C4BE078C272844AF003F4AD1 /* LiveTVChannelsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE078A272844AF003F4AD1 /* LiveTVChannelsView.swift */; };
|
||||
C4BE078E27298818003F4AD1 /* LiveTVHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE078D27298817003F4AD1 /* LiveTVHomeView.swift */; };
|
||||
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E508172703E8190045C9AB /* LibraryListView.swift */; };
|
||||
C4E5081B2703F82A0045C9AB /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E508172703E8190045C9AB /* MediaView.swift */; };
|
||||
C4E5598928124C10003DECA5 /* LiveTVChannelItemElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5598828124C10003DECA5 /* LiveTVChannelItemElement.swift */; };
|
||||
E1002B5F2793C3BE00E47059 /* VLCPlayerChapterOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1002B5E2793C3BE00E47059 /* VLCPlayerChapterOverlayView.swift */; };
|
||||
E1002B642793CEE800E47059 /* ChapterInfoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1002B632793CEE700E47059 /* ChapterInfoExtensions.swift */; };
|
||||
|
@ -391,7 +391,7 @@
|
|||
E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */; };
|
||||
E193D53527193F8100900D82 /* ItemCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */; };
|
||||
E193D53627193F8500900D82 /* LibraryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0B326D5ED8000B8E046 /* LibraryCoordinator.swift */; };
|
||||
E193D53727193F8700900D82 /* LibraryListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */; };
|
||||
E193D53727193F8700900D82 /* MediaCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA726D103D500C1D2E7 /* MediaCoordinator.swift */; };
|
||||
E193D53927193F8E00900D82 /* SearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0B626D5EE1100B8E046 /* SearchCoordinator.swift */; };
|
||||
E193D53A27193F9000900D82 /* ServerListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3E827177ED6009D4DAF /* ServerListCoordinator.swift */; };
|
||||
E193D53B27193F9200900D82 /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0B026D5EC9900B8E046 /* SettingsCoordinator.swift */; };
|
||||
|
@ -478,6 +478,10 @@
|
|||
E1E1643A28BAC2EF00323B0A /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1643928BAC2EF00323B0A /* SearchView.swift */; };
|
||||
E1E1643E28BB074000323B0A /* MultiSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1643D28BB074000323B0A /* MultiSelectorView.swift */; };
|
||||
E1E1643F28BB075C00323B0A /* MultiSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1643D28BB074000323B0A /* MultiSelectorView.swift */; };
|
||||
E1E1644128BB301900323B0A /* ArrayExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1644028BB301900323B0A /* ArrayExtensions.swift */; };
|
||||
E1E1644228BB301900323B0A /* ArrayExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1644028BB301900323B0A /* ArrayExtensions.swift */; };
|
||||
E1E1644428BC60C600323B0A /* LibraryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1644328BC60C600323B0A /* LibraryItem.swift */; };
|
||||
E1E1644528BC60C600323B0A /* LibraryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1644328BC60C600323B0A /* LibraryItem.swift */; };
|
||||
E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E48CC8271E6D410021A2F9 /* RefreshHelper.swift */; };
|
||||
E1E5D5442783BB5100692DFE /* ItemDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5432783BB5100692DFE /* ItemDetailsView.swift */; };
|
||||
E1E5D5492783CDD700692DFE /* OverlaySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5472783CCF900692DFE /* OverlaySettingsView.swift */; };
|
||||
|
@ -618,7 +622,7 @@
|
|||
5D1603FB278A3D5700D22B99 /* SubtitleSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubtitleSize.swift; sourceTree = "<group>"; };
|
||||
5D160402278A41FD00D22B99 /* VLCPlayer+subtitles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VLCPlayer+subtitles.swift"; sourceTree = "<group>"; };
|
||||
5D64683C277B1649009E09AE /* PreferenceUIHostingSwizzling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceUIHostingSwizzling.swift; sourceTree = "<group>"; };
|
||||
6213388F265F83A900A81A2A /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
|
||||
6213388F265F83A900A81A2A /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
|
||||
621338922660107500A81A2A /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = "<group>"; };
|
||||
6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = "<group>"; };
|
||||
6220D0B026D5EC9900B8E046 /* SettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = "<group>"; };
|
||||
|
@ -629,7 +633,7 @@
|
|||
6220D0C526D62D8700B8E046 /* iOSVideoPlayerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSVideoPlayerCoordinator.swift; sourceTree = "<group>"; };
|
||||
6220D0CB26D640C400B8E046 /* AppURLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppURLHandler.swift; sourceTree = "<group>"; };
|
||||
625CB5722678C32A00530A6E /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB5742678C33500530A6E /* LibraryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB5742678C33500530A6E /* MediaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB57B2678CE1000530A6E /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB57D2678E81E00530A6E /* TVVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TVVLCKit.xcframework; path = Carthage/Build/TVVLCKit.xcframework; sourceTree = "<group>"; };
|
||||
|
@ -668,7 +672,7 @@
|
|||
62C29EA026D102A500C1D2E7 /* iOSMainTabCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSMainTabCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA226D1030F00C1D2E7 /* ConnectToServerCoodinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerCoodinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA726D103D500C1D2E7 /* MediaCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C83B07288C6A630004ED0C /* FontPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontPicker.swift; sourceTree = "<group>"; };
|
||||
62E1DCC2273CE19800C9AE76 /* URLExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLExtensions.swift; sourceTree = "<group>"; };
|
||||
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -715,7 +719,7 @@
|
|||
C4BE07872728448B003F4AD1 /* LiveTVChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelsCoordinator.swift; sourceTree = "<group>"; };
|
||||
C4BE078A272844AF003F4AD1 /* LiveTVChannelsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelsView.swift; sourceTree = "<group>"; };
|
||||
C4BE078D27298817003F4AD1 /* LiveTVHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVHomeView.swift; sourceTree = "<group>"; };
|
||||
C4E508172703E8190045C9AB /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
|
||||
C4E508172703E8190045C9AB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
|
||||
C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelItemElement.swift; sourceTree = "<group>"; };
|
||||
C4E5598828124C10003DECA5 /* LiveTVChannelItemElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LiveTVChannelItemElement.swift; sourceTree = "<group>"; };
|
||||
E1002B5E2793C3BE00E47059 /* VLCPlayerChapterOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCPlayerChapterOverlayView.swift; sourceTree = "<group>"; };
|
||||
|
@ -891,6 +895,8 @@
|
|||
E1E00A34278628A40022235B /* DoubleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleExtensions.swift; sourceTree = "<group>"; };
|
||||
E1E1643928BAC2EF00323B0A /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
||||
E1E1643D28BB074000323B0A /* MultiSelectorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiSelectorView.swift; sourceTree = "<group>"; };
|
||||
E1E1644028BB301900323B0A /* ArrayExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtensions.swift; sourceTree = "<group>"; };
|
||||
E1E1644328BC60C600323B0A /* LibraryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryItem.swift; sourceTree = "<group>"; };
|
||||
E1E48CC8271E6D410021A2F9 /* RefreshHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshHelper.swift; sourceTree = "<group>"; };
|
||||
E1E5D5432783BB5100692DFE /* ItemDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsView.swift; sourceTree = "<group>"; };
|
||||
E1E5D5472783CCF900692DFE /* OverlaySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlaySettingsView.swift; sourceTree = "<group>"; };
|
||||
|
@ -1028,10 +1034,10 @@
|
|||
E107BB9127880A4000354E07 /* ItemViewModel */,
|
||||
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */,
|
||||
62E632EE267D43320063E547 /* LibraryFilterViewModel.swift */,
|
||||
625CB5742678C33500530A6E /* LibraryListViewModel.swift */,
|
||||
62E632DF267D30CA0063E547 /* LibraryViewModel.swift */,
|
||||
C4BE07842728446F003F4AD1 /* LiveTVChannelsViewModel.swift */,
|
||||
C4BE07752725EBEA003F4AD1 /* LiveTVProgramsViewModel.swift */,
|
||||
625CB5742678C33500530A6E /* MediaViewModel.swift */,
|
||||
C40CD924271F8D1E000FB198 /* MovieLibrariesViewModel.swift */,
|
||||
6334175C287DE0D0000603CE /* QuickConnectSettingsViewModel.swift */,
|
||||
62E632DB267D2E130063E547 /* SearchViewModel.swift */,
|
||||
|
@ -1144,6 +1150,7 @@
|
|||
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */,
|
||||
E19169CD272514760085832A /* HTTPScheme.swift */,
|
||||
E1C925F328875037002A7A66 /* ItemViewType.swift */,
|
||||
E1E1644328BC60C600323B0A /* LibraryItem.swift */,
|
||||
E1AA331E2782639D00F6439C /* OverlayType.swift */,
|
||||
E1C925F62887504B002A7A66 /* PanDirectionGestureRecognizer.swift */,
|
||||
E193D4DA27193CCA00900D82 /* PillStackable.swift */,
|
||||
|
@ -1421,6 +1428,7 @@
|
|||
621338912660106C00A81A2A /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E1E1644028BB301900323B0A /* ArrayExtensions.swift */,
|
||||
E1A2C157279A7D76005EC829 /* BundleExtensions.swift */,
|
||||
E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */,
|
||||
6267B3D526710B8900A7371D /* CollectionExtensions.swift */,
|
||||
|
@ -1460,12 +1468,12 @@
|
|||
6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */,
|
||||
E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */,
|
||||
6220D0B326D5ED8000B8E046 /* LibraryCoordinator.swift */,
|
||||
62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */,
|
||||
C4BE07872728448B003F4AD1 /* LiveTVChannelsCoordinator.swift */,
|
||||
C45942C427F67DA400C54FE7 /* LiveTVCoordinator.swift */,
|
||||
C4BE07702725EB06003F4AD1 /* LiveTVProgramsCoordinator.swift */,
|
||||
C4BE07782726EE82003F4AD1 /* LiveTVTabCoordinator.swift */,
|
||||
E193D5412719404B00900D82 /* MainCoordinator */,
|
||||
62C29EA726D103D500C1D2E7 /* MediaCoordinator.swift */,
|
||||
C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */,
|
||||
E18CE0B828A2322D0092E7F1 /* QuickConnectCoordinator.swift */,
|
||||
6220D0B626D5EE1100B8E046 /* SearchCoordinator.swift */,
|
||||
|
@ -1615,12 +1623,12 @@
|
|||
E193D54E271942C000900D82 /* ItemView */,
|
||||
E1C925F828875647002A7A66 /* LatestInLibraryView.swift */,
|
||||
E193D54C2719426600900D82 /* LibraryFilterView.swift */,
|
||||
C4E508172703E8190045C9AB /* LibraryListView.swift */,
|
||||
53A83C32268A309300DF3D92 /* LibraryView.swift */,
|
||||
C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */,
|
||||
C4BE078A272844AF003F4AD1 /* LiveTVChannelsView.swift */,
|
||||
C4BE078D27298817003F4AD1 /* LiveTVHomeView.swift */,
|
||||
C4BE07732725EB66003F4AD1 /* LiveTVProgramsView.swift */,
|
||||
C4E508172703E8190045C9AB /* MediaView.swift */,
|
||||
C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */,
|
||||
E1E1643928BAC2EF00323B0A /* SearchView.swift */,
|
||||
E193D54F2719430400900D82 /* ServerDetailView.swift */,
|
||||
|
@ -1673,13 +1681,13 @@
|
|||
E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */,
|
||||
E14F7D0A26DB3714007C3AE6 /* ItemView */,
|
||||
53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */,
|
||||
6213388F265F83A900A81A2A /* LibraryListView.swift */,
|
||||
53DF641D263D9C0600A7CD1A /* LibraryView.swift */,
|
||||
C4E5598828124C10003DECA5 /* LiveTVChannelItemElement.swift */,
|
||||
C400DB6C27FE8E65007B65FE /* LiveTVChannelItemWideElement.swift */,
|
||||
C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */,
|
||||
C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */,
|
||||
C4AE2C3127498D6A00AE13CF /* LiveTVProgramsView.swift */,
|
||||
6213388F265F83A900A81A2A /* MediaView.swift */,
|
||||
E1171A1828A2212600FA1AF5 /* QuickConnectView.swift */,
|
||||
53EE24E5265060780068F029 /* SearchView.swift */,
|
||||
E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */,
|
||||
|
@ -2433,7 +2441,7 @@
|
|||
62E632E7267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
|
||||
E1C926122887565C002A7A66 /* SeriesItemContentView.swift in Sources */,
|
||||
C4BE0767271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */,
|
||||
E193D53727193F8700900D82 /* LibraryListCoordinator.swift in Sources */,
|
||||
E193D53727193F8700900D82 /* MediaCoordinator.swift in Sources */,
|
||||
E18E023C288749540022598C /* UIScrollViewExtensions.swift in Sources */,
|
||||
C4534983279A40990045F1E2 /* tvOSLiveTVVideoPlayerCoordinator.swift in Sources */,
|
||||
E1C9260F2887565C002A7A66 /* AttributeHStack.swift in Sources */,
|
||||
|
@ -2483,6 +2491,7 @@
|
|||
53ABFDE5267974EF00886593 /* ViewModel.swift in Sources */,
|
||||
E184C161288C5C08000B25BA /* RequestBuilderExtensions.swift in Sources */,
|
||||
E19169CF272514760085832A /* HTTPScheme.swift in Sources */,
|
||||
E1E1644528BC60C600323B0A /* LibraryItem.swift in Sources */,
|
||||
E13849452780370B0024FB48 /* PlaybackSpeed.swift in Sources */,
|
||||
E1C812CD277AE40A00918266 /* VideoPlayerViewModel.swift in Sources */,
|
||||
C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */,
|
||||
|
@ -2498,6 +2507,7 @@
|
|||
E1E00A36278628A40022235B /* DoubleExtensions.swift in Sources */,
|
||||
E1E1643A28BAC2EF00323B0A /* SearchView.swift in Sources */,
|
||||
E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */,
|
||||
E1E1644228BB301900323B0A /* ArrayExtensions.swift in Sources */,
|
||||
E193D53C27193F9500900D82 /* UserListCoordinator.swift in Sources */,
|
||||
E1CEFBF727914E6400F60429 /* CustomizeViewsSettings.swift in Sources */,
|
||||
E18CE0B528A22EDD0092E7F1 /* RepeatingTimer.swift in Sources */,
|
||||
|
@ -2515,7 +2525,7 @@
|
|||
6264E88D273850380081A12A /* Strings.swift in Sources */,
|
||||
E1C926102887565C002A7A66 /* PlayButton.swift in Sources */,
|
||||
E193D5512719432400900D82 /* ServerDetailViewModel.swift in Sources */,
|
||||
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */,
|
||||
C4E5081B2703F82A0045C9AB /* MediaView.swift in Sources */,
|
||||
E193D53B27193F9200900D82 /* SettingsCoordinator.swift in Sources */,
|
||||
E18E021C2887492B0022598C /* BlurView.swift in Sources */,
|
||||
E1E5D5442783BB5100692DFE /* ItemDetailsView.swift in Sources */,
|
||||
|
@ -2527,7 +2537,7 @@
|
|||
E1D4BF7F2719D1DD00A11E64 /* BasicAppSettingsViewModel.swift in Sources */,
|
||||
E18E021D2887492B0022598C /* AttributeOutlineView.swift in Sources */,
|
||||
E193D53227193F7B00900D82 /* ConnectToServerCoodinator.swift in Sources */,
|
||||
53ABFDE4267974EF00886593 /* LibraryListViewModel.swift in Sources */,
|
||||
53ABFDE4267974EF00886593 /* MediaViewModel.swift in Sources */,
|
||||
5364F456266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */,
|
||||
E1937A62288F32DB00CB80AA /* Poster.swift in Sources */,
|
||||
C4BE0764271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */,
|
||||
|
@ -2563,7 +2573,7 @@
|
|||
E1C925F428875037002A7A66 /* ItemViewType.swift in Sources */,
|
||||
62E632EC267D410B0063E547 /* SeriesItemViewModel.swift in Sources */,
|
||||
625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */,
|
||||
62C29EA826D103D500C1D2E7 /* LibraryListCoordinator.swift in Sources */,
|
||||
62C29EA826D103D500C1D2E7 /* MediaCoordinator.swift in Sources */,
|
||||
62E632DC267D2E130063E547 /* SearchViewModel.swift in Sources */,
|
||||
C400DB6D27FE8E65007B65FE /* LiveTVChannelItemWideElement.swift in Sources */,
|
||||
62C29E9F26D1016600C1D2E7 /* iOSMainCoordinator.swift in Sources */,
|
||||
|
@ -2579,6 +2589,7 @@
|
|||
62C29EA126D102A500C1D2E7 /* iOSMainTabCoordinator.swift in Sources */,
|
||||
E18E01E8288747230022598C /* SeriesItemContentView.swift in Sources */,
|
||||
E16AA60828A364A6009A983C /* PosterButton.swift in Sources */,
|
||||
E1E1644128BB301900323B0A /* ArrayExtensions.swift in Sources */,
|
||||
E18CE0AF28A222240092E7F1 /* PublicUserSignInView.swift in Sources */,
|
||||
E18E01E5288747230022598C /* CinematicScrollView.swift in Sources */,
|
||||
E1C812C0277A8E5D00918266 /* VLCPlayerView.swift in Sources */,
|
||||
|
@ -2608,7 +2619,7 @@
|
|||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */,
|
||||
C4AE2C3027498D2300AE13CF /* LiveTVHomeView.swift in Sources */,
|
||||
E1EBCB42278BD174009FE6E9 /* TruncatedTextView.swift in Sources */,
|
||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
||||
62133890265F83A900A81A2A /* MediaView.swift in Sources */,
|
||||
62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */,
|
||||
E18E01E1288747230022598C /* EpisodeItemContentView.swift in Sources */,
|
||||
62E632DA267D2BC40063E547 /* LatestMediaViewModel.swift in Sources */,
|
||||
|
@ -2694,6 +2705,7 @@
|
|||
62E632E0267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
||||
E11CEB8B28998552003E74C7 /* iOSViewExtensions.swift in Sources */,
|
||||
E193D4DB27193CCA00900D82 /* PillStackable.swift in Sources */,
|
||||
E1E1644428BC60C600323B0A /* LibraryItem.swift in Sources */,
|
||||
E18E0206288749200022598C /* AttributeFillView.swift in Sources */,
|
||||
E1AA331F2782639D00F6439C /* OverlayType.swift in Sources */,
|
||||
E1A2C154279A7D5A005EC829 /* UIApplicationExtensions.swift in Sources */,
|
||||
|
@ -2746,7 +2758,7 @@
|
|||
E13DD4022717EE79009D4DAF /* UserListCoordinator.swift in Sources */,
|
||||
E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */,
|
||||
53EE24E6265060780068F029 /* SearchView.swift in Sources */,
|
||||
625CB5752678C33500530A6E /* LibraryListViewModel.swift in Sources */,
|
||||
625CB5752678C33500530A6E /* MediaViewModel.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -51,21 +51,23 @@ struct PosterButton<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
Button {
|
||||
onSelect(item)
|
||||
} label: {
|
||||
switch type {
|
||||
case .portrait:
|
||||
ImageView(item.portraitPosterImageSource(maxWidth: itemWidth))
|
||||
case .landscape:
|
||||
ImageView(item.landscapePosterImageSources(maxWidth: itemWidth, single: singleImage))
|
||||
Group {
|
||||
switch type {
|
||||
case .portrait:
|
||||
ImageView(item.portraitPosterImageSource(maxWidth: itemWidth))
|
||||
case .landscape:
|
||||
ImageView(item.landscapePosterImageSources(maxWidth: itemWidth, single: singleImage))
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
imageOverlay(item)
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
}
|
||||
}
|
||||
.contextMenu(menuItems: {
|
||||
contextMenu(item)
|
||||
})
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
.overlay {
|
||||
imageOverlay(item)
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
}
|
||||
.posterShadow()
|
||||
|
||||
content(item)
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
struct LibraryListView: View {
|
||||
@EnvironmentObject
|
||||
private var libraryListRouter: LibraryListCoordinator.Router
|
||||
@StateObject
|
||||
var viewModel = LibraryListViewModel()
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
Button {
|
||||
libraryListRouter.route(
|
||||
to: \.library,
|
||||
(viewModel: LibraryViewModel(filters: viewModel.withFavorites), title: L10n.favorites)
|
||||
)
|
||||
} label: {
|
||||
ZStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
L10n.yourFavorites.text
|
||||
.foregroundColor(.black)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.semibold)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
.background(Color.white)
|
||||
.frame(minWidth: 100, maxWidth: .infinity)
|
||||
}
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 5)
|
||||
.padding(.bottom, 5)
|
||||
|
||||
if !viewModel.isLoading {
|
||||
ForEach(viewModel.filteredLibraries, id: \.id) { library in
|
||||
Button {
|
||||
if library.collectionType == "livetv" {
|
||||
libraryListRouter.route(to: \.liveTV)
|
||||
} else {
|
||||
libraryListRouter.route(
|
||||
to: \.library,
|
||||
(
|
||||
viewModel: LibraryViewModel(parentID: library.id),
|
||||
title: library.name ?? ""
|
||||
)
|
||||
)
|
||||
}
|
||||
} label: {
|
||||
ZStack {
|
||||
ImageView(library.imageSource(.primary, maxWidth: 500))
|
||||
.opacity(0.4)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
Text(library.name ?? "")
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
Spacer()
|
||||
}.padding(32)
|
||||
}.background(Color.black)
|
||||
.frame(minWidth: 100, maxWidth: .infinity)
|
||||
.frame(height: 100)
|
||||
}
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 5)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
}.padding(.leading, 16)
|
||||
.padding(.trailing, 16)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
.navigationTitle(L10n.allMedia)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import CollectionView
|
||||
import Defaults
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
struct MediaView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var router: MediaCoordinator.Router
|
||||
@ObservedObject
|
||||
var viewModel: MediaViewModel
|
||||
@Default(.Experimental.liveTVAlphaEnabled)
|
||||
var liveTVEnabled
|
||||
|
||||
private var libraryItems: [LibraryItem] {
|
||||
[LibraryItem(library: .init(name: L10n.favorites, id: "favorites"), viewModel: viewModel)]
|
||||
.appending(.init(library: .init(name: "LiveTV", id: "liveTV"), viewModel: viewModel), if: liveTVEnabled)
|
||||
.appending(viewModel.libraries.map { LibraryItem(library: $0, viewModel: viewModel) })
|
||||
}
|
||||
|
||||
private var gridLayout: NSCollectionLayoutSection.GridLayoutMode {
|
||||
if UIDevice.isPhone {
|
||||
return .fixedNumberOfColumns(2)
|
||||
} else {
|
||||
return .adaptive(withMinItemSize: PosterType.landscape.width)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
CollectionView(items: libraryItems) { _, item, _ in
|
||||
PosterButton(item: item, type: .landscape)
|
||||
.scaleItem(UIDevice.isPhone ? 0.9 : 1)
|
||||
.onSelect { _ in
|
||||
if item.library.id == "favorites" {
|
||||
router.route(to: \.library, (viewModel: .init(filters: .favorites), title: ""))
|
||||
} else if item.library.id == "liveTV" {
|
||||
router.route(to: \.liveTV)
|
||||
} else {
|
||||
router.route(to: \.library, (viewModel: .init(parentID: item.library.id), title: ""))
|
||||
}
|
||||
}
|
||||
.imageOverlay { _ in
|
||||
ZStack {
|
||||
Color.black
|
||||
.opacity(0.5)
|
||||
|
||||
Text(item.library.displayName)
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
.lineLimit(2)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(alignment: .center)
|
||||
}
|
||||
}
|
||||
}
|
||||
.layout { _, layoutEnvironment in
|
||||
.grid(
|
||||
layoutEnvironment: layoutEnvironment,
|
||||
layoutMode: gridLayout,
|
||||
sectionInsets: .init(top: 0, leading: 10, bottom: 0, trailing: 10)
|
||||
)
|
||||
}
|
||||
.configure { configuration in
|
||||
configuration.showsVerticalScrollIndicator = false
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
.navigationTitle(L10n.allMedia)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue