Implement ItemSeriesSelectableEpisodesView

This commit is contained in:
Shav Kinderlehrer 2024-02-27 14:22:42 -05:00
parent be11eed89d
commit aaf0207164
7 changed files with 124 additions and 21 deletions

View File

@ -40,6 +40,7 @@
3D91FDC92B28C62800919017 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDC82B28C62800919017 /* SignInView.swift */; }; 3D91FDC92B28C62800919017 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDC82B28C62800919017 /* SignInView.swift */; };
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */; }; 3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */; };
3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */; }; 3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */; };
3DADBBC42B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DADBBC32B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift */; };
3DAFA8E82B38AFED00D71AD1 /* ItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8E72B38AFED00D71AD1 /* ItemInfoView.swift */; }; 3DAFA8E82B38AFED00D71AD1 /* ItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8E72B38AFED00D71AD1 /* ItemInfoView.swift */; };
3DAFA8EA2B39039900D71AD1 /* BaseItemDtoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8E92B39039900D71AD1 /* BaseItemDtoExtensions.swift */; }; 3DAFA8EA2B39039900D71AD1 /* BaseItemDtoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8E92B39039900D71AD1 /* BaseItemDtoExtensions.swift */; };
3DAFA8EC2B394F9F00D71AD1 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */; }; 3DAFA8EC2B394F9F00D71AD1 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */; };
@ -102,6 +103,7 @@
3D91FDC82B28C62800919017 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; }; 3D91FDC82B28C62800919017 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; };
3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInToServerView.swift; sourceTree = "<group>"; }; 3D91FDCA2B28CA2500919017 /* SignInToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInToServerView.swift; sourceTree = "<group>"; };
3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinDateFormatter.swift; sourceTree = "<group>"; }; 3D91FDCC2B2907E800919017 /* JellyfinDateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinDateFormatter.swift; sourceTree = "<group>"; };
3DADBBC32B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesSelectableEpisodesView.swift; sourceTree = "<group>"; };
3DAFA8E72B38AFED00D71AD1 /* ItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemInfoView.swift; sourceTree = "<group>"; }; 3DAFA8E72B38AFED00D71AD1 /* ItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemInfoView.swift; sourceTree = "<group>"; };
3DAFA8E92B39039900D71AD1 /* BaseItemDtoExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseItemDtoExtensions.swift; sourceTree = "<group>"; }; 3DAFA8E92B39039900D71AD1 /* BaseItemDtoExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseItemDtoExtensions.swift; sourceTree = "<group>"; };
3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = "<group>"; }; 3DAFA8EB2B394F9F00D71AD1 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = "<group>"; };
@ -194,6 +196,7 @@
3D2552482B7A8B3100192879 /* ItemSeriesSeasonsView.swift */, 3D2552482B7A8B3100192879 /* ItemSeriesSeasonsView.swift */,
3DDF35D42B7D384000423923 /* ItemSeriesEpisodesView.swift */, 3DDF35D42B7D384000423923 /* ItemSeriesEpisodesView.swift */,
3DDF35D62B7D4D0100423923 /* ItemSeriesEpisodeIconView.swift */, 3DDF35D62B7D4D0100423923 /* ItemSeriesEpisodeIconView.swift */,
3DADBBC32B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift */,
); );
path = Series; path = Series;
sourceTree = "<group>"; sourceTree = "<group>";
@ -397,6 +400,7 @@
files = ( files = (
3D6A0DC12B867B4C001FDA40 /* ScreenSize.swift in Sources */, 3D6A0DC12B867B4C001FDA40 /* ScreenSize.swift in Sources */,
3D9063CD2B279A310063DD2A /* ContentView.swift in Sources */, 3D9063CD2B279A310063DD2A /* ContentView.swift in Sources */,
3DADBBC42B8A30DD006C242C /* ItemSeriesSelectableEpisodesView.swift in Sources */,
3D13F96F2B38A32500E91913 /* StickyHeaderView.swift in Sources */, 3D13F96F2B38A32500E91913 /* StickyHeaderView.swift in Sources */,
3DF1ED3E2B282836000AD8EA /* JellyfinClientController.swift in Sources */, 3DF1ED3E2B282836000AD8EA /* JellyfinClientController.swift in Sources */,
3D1015D92B27F57400F5C29A /* AddServerView.swift in Sources */, 3D1015D92B27F57400F5C29A /* AddServerView.swift in Sources */,

View File

@ -27,7 +27,7 @@ struct ItemView: View {
} }
} }
} }
.scrollIndicators(.hidden) // .scrollIndicators(.hidden)
} }
} }

View File

@ -37,7 +37,6 @@ struct ItemSeriesEpisodeIconView: View {
ExpandableText((item.overviewNL ?? "").replacingOccurrences(of: "<br>", with: "\n")) ExpandableText((item.overviewNL ?? "").replacingOccurrences(of: "<br>", with: "\n"))
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.padding(.vertical)
} }
} }

View File

@ -10,36 +10,46 @@ import JellyfinKit
struct ItemSeriesEpisodesView: View { struct ItemSeriesEpisodesView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController @EnvironmentObject var jellyfinClient: JellyfinClientController
@EnvironmentObject var size: ScreenSize
@ObservedObject var authState: AuthStateController = AuthStateController.shared @ObservedObject var authState: AuthStateController = AuthStateController.shared
var item: BaseItemDto var item: BaseItemDto
@State var episodeItems: [BaseItemDto] = [] @State var episodeItems: [BaseItemDto] = []
@State var loading: Bool = true
var body: some View { var body: some View {
VStack(alignment: .leading) { LazyVStack(alignment: .leading) {
ForEach(episodeItems) {episode in ForEach(episodeItems) {episode in
ItemSeriesEpisodeIconView(item: episode) ItemSeriesEpisodeIconView(item: episode)
} }
} }
.if(loading) {view in
view
.redacted(reason: .placeholder)
}
.onChange(of: item) {
self.loadEpisodes()
}
.onAppear { .onAppear {
Task { self.loadEpisodes()
let parameters = Paths.GetItemsParameters(
userID: authState.userId,
parentID: item.id ?? ""
)
let req = Paths.getItems(parameters: parameters)
do {
let res = try await jellyfinClient.send(req)
episodeItems = res.value.items ?? []
} catch {}
}
} }
} }
}
func loadEpisodes() {
Task {
let parameters = Paths.GetItemsParameters(
userID: authState.userId,
parentID: item.id ?? ""
)
let req = Paths.getItems(parameters: parameters)
do {
let res = try await jellyfinClient.send(req)
episodeItems = res.value.items ?? []
loading = false
} catch {}
}
}}
//#Preview { //#Preview {
// ItemSeriesEpisodesView() // ItemSeriesEpisodesView()

View File

@ -15,6 +15,7 @@ struct ItemSeriesSeasonsView: View {
@ObservedObject var authState: AuthStateController = AuthStateController.shared @ObservedObject var authState: AuthStateController = AuthStateController.shared
@State var seriesItems: [BaseItemDto] = [] @State var seriesItems: [BaseItemDto] = []
@State var loading: Bool = true
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
@ -34,9 +35,12 @@ struct ItemSeriesSeasonsView: View {
} }
} }
}.padding(.horizontal) }.padding(.horizontal)
} }.scrollIndicators(.hidden)
} }
.onAppear{ .if(loading) {view in
view.redacted(reason: .placeholder)
}
.onAppear {
Task { Task {
let parameters = Paths.GetItemsParameters( let parameters = Paths.GetItemsParameters(
userID: authState.userId ?? "", userID: authState.userId ?? "",
@ -48,6 +52,8 @@ struct ItemSeriesSeasonsView: View {
let res = try await jellyfinClient.send(req) let res = try await jellyfinClient.send(req)
seriesItems = res.value.items ?? [] seriesItems = res.value.items ?? []
} catch {} } catch {}
loading = false
} }
} }
} }

View File

@ -0,0 +1,84 @@
//
// ItemSeriesSelectableEpisodesView.swift
// Jel
//
// Created by zerocool on 2/24/24.
//
import SwiftUI
import JellyfinKit
struct Season: Identifiable, Hashable {
var name: String
var id: String
var season: BaseItemDto
}
extension Season {
static let emptySelection = Season(name: "", id: "", season: BaseItemDto())
}
struct ItemSeriesSelectableEpisodesView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@StateObject var authState: AuthStateController = AuthStateController.shared
var item: BaseItemDto // series
@State var seasons: [Season] = []
@State var currentSeason: Season = .emptySelection
@State var currentSeasonCopy: Season = .emptySelection
@State var loading: Bool = true
var body: some View {
VStack(alignment: .leading) {
HStack {
Text("Episodes")
.font(.title2)
.padding(.horizontal)
Picker("Season", selection: $currentSeason) {
ForEach(seasons, id: \.self) {season in
Text(season.name).tag(season)
}
}
}
ItemSeriesEpisodesView(item: currentSeasonCopy.season)
.padding(.horizontal)
}
.if(loading) {view in
view.redacted(reason: .placeholder)
}
.onChange(of: currentSeason) {
currentSeasonCopy = currentSeason
}
.onAppear {
Task {
let parameters = Paths.GetItemsParameters(
userID: authState.userId ?? "",
parentID: item.id ?? ""
)
let req = Paths.getItems(parameters: parameters)
do {
let res = try await jellyfinClient.send(req)
seasons = []
for season in res.value.items ?? [] {
let newSeason = Season(
name: season.name ?? "---",
id: season.id ?? "",
season: season
)
seasons.append(newSeason)
currentSeason = seasons.first ?? .emptySelection
loading = false
}
} catch {}
}
}
}
}
//#Preview {
// ItemSeriesSelectableEpisodesView()
//}

View File

@ -34,7 +34,7 @@ struct ItemSeriesView: View {
ItemGenresView(item: item) ItemGenresView(item: item)
.foregroundStyle(Color.primary) .foregroundStyle(Color.primary)
// TODO: Settable series with ItemSeriesEpisodesView ItemSeriesSelectableEpisodesView(item: item)
ItemSeriesSeasonsView(item: item) ItemSeriesSeasonsView(item: item)
.foregroundStyle(Color.primary) .foregroundStyle(Color.primary)