Consolidate item views.
Add watched badges Add remaining episode badges Add favorite badges. Fix genres to only show genres from current library. Show watched episodes in series view. Add progress bar to currently watching items in library. Fix showing favorites.
This commit is contained in:
parent
3a2328fbee
commit
19c5e3e4c8
|
@ -101,6 +101,7 @@
|
||||||
53EC6E25267EB10F006DD26A /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 53EC6E24267EB10F006DD26A /* SwiftyJSON */; };
|
53EC6E25267EB10F006DD26A /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 53EC6E24267EB10F006DD26A /* SwiftyJSON */; };
|
||||||
53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EE24E5265060780068F029 /* LibrarySearchView.swift */; };
|
53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EE24E5265060780068F029 /* LibrarySearchView.swift */; };
|
||||||
53F8377D265FF67C00F456B3 /* VideoPlayerSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */; };
|
53F8377D265FF67C00F456B3 /* VideoPlayerSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */; };
|
||||||
|
53F866442687A45F00DCD1D7 /* PortraitItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F866432687A45F00DCD1D7 /* PortraitItemView.swift */; };
|
||||||
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */; };
|
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */; };
|
||||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* LibraryListView.swift */; };
|
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* LibraryListView.swift */; };
|
||||||
621338932660107500A81A2A /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
|
621338932660107500A81A2A /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
|
||||||
|
@ -285,6 +286,7 @@
|
||||||
53E4E648263F725B00F67C6B /* MultiSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSelectorView.swift; sourceTree = "<group>"; };
|
53E4E648263F725B00F67C6B /* MultiSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSelectorView.swift; sourceTree = "<group>"; };
|
||||||
53EE24E5265060780068F029 /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; };
|
53EE24E5265060780068F029 /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; };
|
||||||
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerSettingsView.swift; sourceTree = "<group>"; };
|
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerSettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
53F866432687A45F00DCD1D7 /* PortraitItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitItemView.swift; sourceTree = "<group>"; };
|
||||||
53FF7F29263CF3F500585C35 /* LatestMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaView.swift; sourceTree = "<group>"; };
|
53FF7F29263CF3F500585C35 /* LatestMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaView.swift; sourceTree = "<group>"; };
|
||||||
6213388F265F83A900A81A2A /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
|
6213388F265F83A900A81A2A /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
|
||||||
621338922660107500A81A2A /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = "<group>"; };
|
621338922660107500A81A2A /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = "<group>"; };
|
||||||
|
@ -404,23 +406,23 @@
|
||||||
532175392671BCED005491E6 /* ViewModels */ = {
|
532175392671BCED005491E6 /* ViewModels */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5321753A2671BCFC005491E6 /* SettingsViewModel.swift */,
|
|
||||||
625CB5692678B71200530A6E /* SplashViewModel.swift */,
|
|
||||||
625CB5722678C32A00530A6E /* HomeViewModel.swift */,
|
|
||||||
625CB5742678C33500530A6E /* LibraryListViewModel.swift */,
|
|
||||||
625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */,
|
625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */,
|
||||||
625CB57B2678CE1000530A6E /* ViewModel.swift */,
|
62E632F2267D54030063E547 /* DetailItemViewModel.swift */,
|
||||||
536D3D75267BA9BB0004248C /* MainTabViewModel.swift */,
|
62E632E5267D3F5B0063E547 /* EpisodeItemViewModel.swift */,
|
||||||
|
625CB5722678C32A00530A6E /* HomeViewModel.swift */,
|
||||||
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */,
|
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */,
|
||||||
|
62E632EE267D43320063E547 /* LibraryFilterViewModel.swift */,
|
||||||
|
625CB5742678C33500530A6E /* LibraryListViewModel.swift */,
|
||||||
62E632DB267D2E130063E547 /* LibrarySearchViewModel.swift */,
|
62E632DB267D2E130063E547 /* LibrarySearchViewModel.swift */,
|
||||||
62E632DF267D30CA0063E547 /* LibraryViewModel.swift */,
|
62E632DF267D30CA0063E547 /* LibraryViewModel.swift */,
|
||||||
|
536D3D75267BA9BB0004248C /* MainTabViewModel.swift */,
|
||||||
62E632E2267D3BA60063E547 /* MovieItemViewModel.swift */,
|
62E632E2267D3BA60063E547 /* MovieItemViewModel.swift */,
|
||||||
62E632E5267D3F5B0063E547 /* EpisodeItemViewModel.swift */,
|
|
||||||
62E632E8267D3FF50063E547 /* SeasonItemViewModel.swift */,
|
62E632E8267D3FF50063E547 /* SeasonItemViewModel.swift */,
|
||||||
62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */,
|
62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */,
|
||||||
62E632EE267D43320063E547 /* LibraryFilterViewModel.swift */,
|
5321753A2671BCFC005491E6 /* SettingsViewModel.swift */,
|
||||||
62E632F2267D54030063E547 /* DetailItemViewModel.swift */,
|
625CB5692678B71200530A6E /* SplashViewModel.swift */,
|
||||||
09389CC626819B4500AE350E /* VideoPlayerModel.swift */,
|
09389CC626819B4500AE350E /* VideoPlayerModel.swift */,
|
||||||
|
625CB57B2678CE1000530A6E /* ViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = ViewModels;
|
path = ViewModels;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -494,10 +496,10 @@
|
||||||
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
|
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
|
||||||
5377CBF3263B596A003A4E83 /* JellyfinPlayer */,
|
5377CBF3263B596A003A4E83 /* JellyfinPlayer */,
|
||||||
535870612669D21600D05A09 /* JellyfinPlayer tvOS */,
|
535870612669D21600D05A09 /* JellyfinPlayer tvOS */,
|
||||||
|
C78797A232E2B8774099D1E9 /* Pods */,
|
||||||
5377CBF2263B596A003A4E83 /* Products */,
|
5377CBF2263B596A003A4E83 /* Products */,
|
||||||
535870752669D60C00D05A09 /* Shared */,
|
535870752669D60C00D05A09 /* Shared */,
|
||||||
628B95252670CABD0091AF3B /* WidgetExtension */,
|
628B95252670CABD0091AF3B /* WidgetExtension */,
|
||||||
C78797A232E2B8774099D1E9 /* Pods */,
|
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -514,6 +516,7 @@
|
||||||
5377CBF3263B596A003A4E83 /* JellyfinPlayer */ = {
|
5377CBF3263B596A003A4E83 /* JellyfinPlayer */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
53F866422687A45400DCD1D7 /* Components */,
|
||||||
53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */,
|
53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */,
|
||||||
5377CBF8263B596B003A4E83 /* Assets.xcassets */,
|
5377CBF8263B596B003A4E83 /* Assets.xcassets */,
|
||||||
5338F74D263B61370014BF09 /* ConnectToServerView.swift */,
|
5338F74D263B61370014BF09 /* ConnectToServerView.swift */,
|
||||||
|
@ -586,6 +589,14 @@
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
53F866422687A45400DCD1D7 /* Components */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
53F866432687A45F00DCD1D7 /* PortraitItemView.swift */,
|
||||||
|
);
|
||||||
|
path = Components;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
621338912660106C00A81A2A /* Extensions */ = {
|
621338912660106C00A81A2A /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -992,6 +1003,7 @@
|
||||||
62E632DC267D2E130063E547 /* LibrarySearchViewModel.swift in Sources */,
|
62E632DC267D2E130063E547 /* LibrarySearchViewModel.swift in Sources */,
|
||||||
5377CBFE263B596B003A4E83 /* PersistenceController.swift in Sources */,
|
5377CBFE263B596B003A4E83 /* PersistenceController.swift in Sources */,
|
||||||
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
||||||
|
53F866442687A45F00DCD1D7 /* PortraitItemView.swift in Sources */,
|
||||||
53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */,
|
53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */,
|
||||||
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
||||||
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */,
|
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */,
|
||||||
|
|
|
@ -11,12 +11,14 @@ import SwiftUI
|
||||||
struct LibraryFilterView: View {
|
struct LibraryFilterView: View {
|
||||||
@Environment(\.presentationMode) var presentationMode
|
@Environment(\.presentationMode) var presentationMode
|
||||||
@Binding var filters: LibraryFilters
|
@Binding var filters: LibraryFilters
|
||||||
|
var parentId: String = ""
|
||||||
|
|
||||||
@StateObject var viewModel: LibraryFilterViewModel
|
@StateObject var viewModel: LibraryFilterViewModel
|
||||||
|
|
||||||
init(filters: Binding<LibraryFilters>, enabledFilterType: [FilterType]) {
|
init(filters: Binding<LibraryFilters>, enabledFilterType: [FilterType], parentId: String) {
|
||||||
_filters = filters
|
_filters = filters
|
||||||
_viewModel = StateObject(wrappedValue: .init(filters: filters.wrappedValue, enabledFilterType: enabledFilterType))
|
self.parentId = parentId
|
||||||
|
_viewModel = StateObject(wrappedValue: .init(filters: filters.wrappedValue, enabledFilterType: enabledFilterType, parentId: parentId))
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
|
@ -56,6 +56,7 @@ struct LibraryListView: View {
|
||||||
.shadow(radius: 5)
|
.shadow(radius: 5)
|
||||||
.padding(.bottom, 15)
|
.padding(.bottom, 15)
|
||||||
|
|
||||||
|
if(!viewModel.isLoading) {
|
||||||
ForEach(viewModel.libraries, id: \.id) { library in
|
ForEach(viewModel.libraries, id: \.id) { library in
|
||||||
if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" {
|
if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" {
|
||||||
NavigationLink(destination: LazyView {
|
NavigationLink(destination: LazyView {
|
||||||
|
@ -66,15 +67,17 @@ struct LibraryListView: View {
|
||||||
.opacity(0.4)
|
.opacity(0.4)
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
VStack() {
|
||||||
Text(library.name ?? "")
|
Text(library.name ?? "")
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}.padding(32)
|
}.padding(32)
|
||||||
}.background(Color.black)
|
}.background(Color.black)
|
||||||
.frame(minWidth: 100, maxWidth: .infinity)
|
.frame(minWidth: 100, maxWidth: .infinity)
|
||||||
.frame(height: 72)
|
.frame(height: 100)
|
||||||
}
|
}
|
||||||
.cornerRadius(10)
|
.cornerRadius(10)
|
||||||
.shadow(radius: 5)
|
.shadow(radius: 5)
|
||||||
|
@ -83,6 +86,9 @@ struct LibraryListView: View {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ProgressView()
|
||||||
|
}
|
||||||
}.padding(.leading, 16)
|
}.padding(.leading, 16)
|
||||||
.padding(.trailing, 16)
|
.padding(.trailing, 16)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,36 +30,7 @@ struct LibrarySearchView: View {
|
||||||
Spacer().frame(height: 16)
|
Spacer().frame(height: 16)
|
||||||
LazyVGrid(columns: tracks) {
|
LazyVGrid(columns: tracks) {
|
||||||
ForEach(viewModel.items, id: \.id) { item in
|
ForEach(viewModel.items, id: \.id) { item in
|
||||||
NavigationLink(destination: ItemView(item: item)) {
|
PortraitItemView(item: item)
|
||||||
VStack(alignment: .leading) {
|
|
||||||
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
|
|
||||||
.frame(width: 100, height: 150)
|
|
||||||
.cornerRadius(10)
|
|
||||||
.overlay(
|
|
||||||
ZStack {
|
|
||||||
if item.userData!.played ?? false {
|
|
||||||
Image(systemName: "circle.fill")
|
|
||||||
.foregroundColor(.white)
|
|
||||||
Image(systemName: "checkmark.circle.fill")
|
|
||||||
.foregroundColor(Color(.systemBlue))
|
|
||||||
}
|
|
||||||
}.padding(2)
|
|
||||||
.opacity(1), alignment: .topTrailing).opacity(1)
|
|
||||||
Text(item.name ?? "")
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
.lineLimit(1)
|
|
||||||
if item.productionYear != nil {
|
|
||||||
Text(String(item.productionYear!))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
} else {
|
|
||||||
Text(item.type ?? "")
|
|
||||||
}
|
|
||||||
}.frame(width: 100)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Spacer().frame(height: 16)
|
Spacer().frame(height: 16)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,38 +34,8 @@ struct LibraryView: View {
|
||||||
Spacer().frame(height: 16)
|
Spacer().frame(height: 16)
|
||||||
LazyVGrid(columns: tracks) {
|
LazyVGrid(columns: tracks) {
|
||||||
ForEach(viewModel.items, id: \.id) { item in
|
ForEach(viewModel.items, id: \.id) { item in
|
||||||
NavigationLink(destination: ItemView(item: item)) {
|
if(item.type != "Folder") {
|
||||||
VStack(alignment: .leading) {
|
PortraitItemView(item: item)
|
||||||
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
|
|
||||||
.frame(width: 100, height: 150)
|
|
||||||
.cornerRadius(10)
|
|
||||||
.overlay(
|
|
||||||
ZStack {
|
|
||||||
if item.userData!.played ?? false {
|
|
||||||
Image(systemName: "circle.fill")
|
|
||||||
.foregroundColor(.white)
|
|
||||||
Image(systemName: "checkmark.circle.fill")
|
|
||||||
.foregroundColor(Color(.systemBlue))
|
|
||||||
}
|
|
||||||
}.padding(2)
|
|
||||||
.opacity(1), alignment: .topTrailing).opacity(1)
|
|
||||||
Text(item.name ?? "")
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
.lineLimit(1)
|
|
||||||
if item.productionYear != nil {
|
|
||||||
Text(String(item.productionYear!))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
} else {
|
|
||||||
Text(item.type ?? "")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
}
|
|
||||||
}.frame(width: 100)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onRotate { _ in
|
}.onRotate { _ in
|
||||||
|
@ -82,8 +52,8 @@ struct LibraryView: View {
|
||||||
.font(.system(size: 25))
|
.font(.system(size: 25))
|
||||||
}.disabled(!viewModel.hasPreviousPage)
|
}.disabled(!viewModel.hasPreviousPage)
|
||||||
Text("Page \(String(viewModel.currentPage + 1)) of \(String(viewModel.totalPages))")
|
Text("Page \(String(viewModel.currentPage + 1)) of \(String(viewModel.totalPages))")
|
||||||
.font(.headline)
|
.font(.subheadline)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.medium)
|
||||||
Button {
|
Button {
|
||||||
viewModel.requestNextPage()
|
viewModel.requestNextPage()
|
||||||
} label: {
|
} label: {
|
||||||
|
@ -109,14 +79,14 @@ struct LibraryView: View {
|
||||||
viewModel.requestPreviousPage()
|
viewModel.requestPreviousPage()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "chevron.left")
|
Image(systemName: "chevron.left")
|
||||||
}
|
}.disabled(viewModel.isLoading)
|
||||||
}
|
}
|
||||||
if viewModel.hasNextPage {
|
if viewModel.hasNextPage {
|
||||||
Button {
|
Button {
|
||||||
viewModel.requestNextPage()
|
viewModel.requestNextPage()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
}
|
}.disabled(viewModel.isLoading)
|
||||||
}
|
}
|
||||||
Label("Icon One", systemImage: "line.horizontal.3.decrease.circle")
|
Label("Icon One", systemImage: "line.horizontal.3.decrease.circle")
|
||||||
.foregroundColor(viewModel.filters == defaultFilters ? .accentColor : Color(UIColor.systemOrange))
|
.foregroundColor(viewModel.filters == defaultFilters ? .accentColor : Color(UIColor.systemOrange))
|
||||||
|
@ -131,7 +101,7 @@ struct LibraryView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $isShowingFilterView) {
|
.sheet(isPresented: $isShowingFilterView) {
|
||||||
LibraryFilterView(filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType)
|
LibraryFilterView(filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType, parentId: viewModel.parentID ?? "")
|
||||||
}
|
}
|
||||||
.background(
|
.background(
|
||||||
NavigationLink(destination: LibrarySearchView(viewModel: .init(parentID: viewModel.parentID)),
|
NavigationLink(destination: LibrarySearchView(viewModel: .init(parentID: viewModel.parentID)),
|
||||||
|
|
|
@ -22,24 +22,7 @@ struct NextUpView: View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
LazyHStack {
|
LazyHStack {
|
||||||
ForEach(items, id: \.id) { item in
|
ForEach(items, id: \.id) { item in
|
||||||
NavigationLink(destination: LazyView { ItemView(item: item) }) {
|
PortraitItemView(item: item)
|
||||||
VStack(alignment: .leading) {
|
|
||||||
ImageView(src: item.getSeriesPrimaryImage(maxWidth: 100), bh: item.getSeriesPrimaryImageBlurHash())
|
|
||||||
.frame(width: 100, height: 150)
|
|
||||||
.cornerRadius(10)
|
|
||||||
.shadow(radius: 4)
|
|
||||||
Text(item.seriesName!)
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
.lineLimit(1)
|
|
||||||
Text("S\(item.parentIndexNumber ?? 0):E\(item.indexNumber ?? 0)")
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.lineLimit(1)
|
|
||||||
}.frame(width: 100)
|
|
||||||
}
|
|
||||||
}.padding(.trailing, 16)
|
}.padding(.trailing, 16)
|
||||||
}
|
}
|
||||||
.padding(.leading, 20)
|
.padding(.leading, 20)
|
||||||
|
|
|
@ -73,12 +73,27 @@ struct SeasonItemView: View {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
|
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
|
||||||
.mask(ProgressBar())
|
.mask(ProgressBar())
|
||||||
.frame(width: CGFloat(episode.userData!.playedPercentage ?? 0 * 1.5), height: 7)
|
.frame(width: CGFloat(episode.userData?.playedPercentage ?? 0 * 1.5), height: 7)
|
||||||
.padding(0), alignment: .bottomLeading
|
.padding(0), alignment: .bottomLeading
|
||||||
)
|
)
|
||||||
.overlay(
|
.overlay(
|
||||||
ZStack {
|
ZStack {
|
||||||
if episode.userData!.played ?? false {
|
if episode.userData?.isFavorite ?? false {
|
||||||
|
Image(systemName: "circle.fill")
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.opacity(0.6)
|
||||||
|
Image(systemName: "heart.fill")
|
||||||
|
.foregroundColor(Color(.systemRed))
|
||||||
|
.font(.system(size: 10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.leading, 2)
|
||||||
|
.padding(.bottom, episode.userData?.playedPercentage == nil ? 2 : 9)
|
||||||
|
.opacity(1)
|
||||||
|
, alignment: .bottomLeading)
|
||||||
|
.overlay(
|
||||||
|
ZStack {
|
||||||
|
if episode.userData?.played ?? false {
|
||||||
Image(systemName: "circle.fill")
|
Image(systemName: "circle.fill")
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
Image(systemName: "checkmark.circle.fill")
|
Image(systemName: "checkmark.circle.fill")
|
||||||
|
|
|
@ -24,25 +24,7 @@ struct SeriesItemView: View {
|
||||||
Spacer().frame(height: 16)
|
Spacer().frame(height: 16)
|
||||||
LazyVGrid(columns: tracks) {
|
LazyVGrid(columns: tracks) {
|
||||||
ForEach(viewModel.seasons, id: \.id) { season in
|
ForEach(viewModel.seasons, id: \.id) { season in
|
||||||
NavigationLink(destination: ItemView(item: season)) {
|
PortraitItemView(item: season)
|
||||||
VStack(alignment: .leading) {
|
|
||||||
ImageView(src: season.getPrimaryImage(maxWidth: 100), bh: season.getPrimaryImageBlurHash())
|
|
||||||
.frame(width: 100, height: 150)
|
|
||||||
.cornerRadius(10)
|
|
||||||
.shadow(radius: 5)
|
|
||||||
Text(season.name ?? "")
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
.lineLimit(1)
|
|
||||||
if season.productionYear != nil {
|
|
||||||
Text(String(season.productionYear!))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
}
|
|
||||||
}.frame(width: 100)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Spacer().frame(height: 2)
|
Spacer().frame(height: 2)
|
||||||
}.onRotate { _ in
|
}.onRotate { _ in
|
||||||
|
|
|
@ -111,7 +111,7 @@ extension BaseItemDto {
|
||||||
func getItemRuntime() -> String {
|
func getItemRuntime() -> String {
|
||||||
let timeHMSFormatter: DateComponentsFormatter = {
|
let timeHMSFormatter: DateComponentsFormatter = {
|
||||||
let formatter = DateComponentsFormatter()
|
let formatter = DateComponentsFormatter()
|
||||||
formatter.unitsStyle = .brief
|
formatter.unitsStyle = .abbreviated
|
||||||
formatter.allowedUnits = [.hour, .minute]
|
formatter.allowedUnits = [.hour, .minute]
|
||||||
return formatter
|
return formatter
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -40,6 +40,8 @@ final class LibraryFilterViewModel: ViewModel {
|
||||||
@Published
|
@Published
|
||||||
var selectedSortBy: SortBy = .name
|
var selectedSortBy: SortBy = .name
|
||||||
|
|
||||||
|
var parentId: String = ""
|
||||||
|
|
||||||
func updateModifiedFilter() {
|
func updateModifiedFilter() {
|
||||||
modifiedFilters.sortOrder = [selectedSortOrder]
|
modifiedFilters.sortOrder = [selectedSortOrder]
|
||||||
modifiedFilters.sortBy = [selectedSortBy]
|
modifiedFilters.sortBy = [selectedSortBy]
|
||||||
|
@ -50,10 +52,11 @@ final class LibraryFilterViewModel: ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
init(filters: LibraryFilters? = nil,
|
init(filters: LibraryFilters? = nil,
|
||||||
enabledFilterType: [FilterType] = [.tag, .genre, .sortBy, .sortOrder, .filter]) {
|
enabledFilterType: [FilterType] = [.tag, .genre, .sortBy, .sortOrder, .filter], parentId: String) {
|
||||||
self.enabledFilterType = enabledFilterType
|
self.enabledFilterType = enabledFilterType
|
||||||
self.selectedSortBy = filters!.sortBy.first!
|
self.selectedSortBy = filters!.sortBy.first!
|
||||||
self.selectedSortOrder = filters!.sortOrder.first!
|
self.selectedSortOrder = filters!.sortOrder.first!
|
||||||
|
self.parentId = parentId
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
if let filters = filters {
|
if let filters = filters {
|
||||||
|
@ -63,7 +66,7 @@ final class LibraryFilterViewModel: ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestQueryFilters() {
|
func requestQueryFilters() {
|
||||||
FilterAPI.getQueryFilters(userId: SessionManager.current.user.user_id!)
|
FilterAPI.getQueryFilters(userId: SessionManager.current.user.user_id!, parentId: self.parentId)
|
||||||
.trackActivity(loading)
|
.trackActivity(loading)
|
||||||
.sink(receiveCompletion: { [weak self] completion in
|
.sink(receiveCompletion: { [weak self] completion in
|
||||||
self?.handleAPIRequestCompletion(completion: completion)
|
self?.handleAPIRequestCompletion(completion: completion)
|
||||||
|
|
|
@ -69,10 +69,10 @@ final class LibraryViewModel: ViewModel {
|
||||||
}
|
}
|
||||||
let sortBy = filters.sortBy.map(\.rawValue)
|
let sortBy = filters.sortBy.map(\.rawValue)
|
||||||
|
|
||||||
ItemsAPI.getItemsByUserId(userId: SessionManager.current.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true,
|
ItemsAPI.getItemsByUserId(userId: SessionManager.current.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: filters.filters.contains(.isFavorite) ? true : false,
|
||||||
searchTerm: nil, sortOrder: filters.sortOrder, parentId: parentID,
|
searchTerm: nil, sortOrder: filters.sortOrder, parentId: parentID,
|
||||||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
||||||
includeItemTypes: ["Movie", "Series"], filters: filters.filters, sortBy: sortBy, tags: filters.tags,
|
filters: filters.filters, sortBy: sortBy, tags: filters.tags,
|
||||||
enableUserData: true, personIds: personIDs, studioIds: studioIDs, genreIds: genreIDs, enableImages: true)
|
enableUserData: true, personIds: personIDs, studioIds: studioIDs, genreIds: genreIDs, enableImages: true)
|
||||||
.trackActivity(loading)
|
.trackActivity(loading)
|
||||||
.sink(receiveCompletion: { [weak self] completion in
|
.sink(receiveCompletion: { [weak self] completion in
|
||||||
|
|
|
@ -26,7 +26,7 @@ final class SeriesItemViewModel: ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestSeasons() {
|
func requestSeasons() {
|
||||||
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people])
|
TvShowsAPI.getSeasons(seriesId: item.id ?? "", userId: SessionManager.current.user.user_id!, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], enableUserData: true)
|
||||||
.trackActivity(loading)
|
.trackActivity(loading)
|
||||||
.sink(receiveCompletion: { [weak self] completion in
|
.sink(receiveCompletion: { [weak self] completion in
|
||||||
self?.handleAPIRequestCompletion(completion: completion)
|
self?.handleAPIRequestCompletion(completion: completion)
|
||||||
|
|
Loading…
Reference in New Issue