Use asyncImage for libraryIcons

This commit is contained in:
Shav Kinderlehrer 2023-12-23 11:14:53 -05:00
parent 885615d1dd
commit a25acb1219
6 changed files with 58 additions and 86 deletions

View File

@ -27,7 +27,6 @@
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 */; };
3DAA71C62B31E19200D5FB33 /* AsyncImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DAA71C52B31E19200D5FB33 /* AsyncImageView.swift */; };
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC6BA2C2B2A422300416B9F /* SettingsController.swift */; }; 3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC6BA2C2B2A422300416B9F /* SettingsController.swift */; };
3DDD67932B293BC40026781E /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67922B293BC40026781E /* DashboardView.swift */; }; 3DDD67932B293BC40026781E /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67922B293BC40026781E /* DashboardView.swift */; };
3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67952B29E28B0026781E /* SettingsView.swift */; }; 3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDD67952B29E28B0026781E /* SettingsView.swift */; };
@ -87,7 +86,6 @@
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>"; };
3DAA71C52B31E19200D5FB33 /* AsyncImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncImageView.swift; sourceTree = "<group>"; };
3DC0E5802B2832B9001CCE96 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 3DC0E5802B2832B9001CCE96 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3DC6BA2C2B2A422300416B9F /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; }; 3DC6BA2C2B2A422300416B9F /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
3DDD67922B293BC40026781E /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; }; 3DDD67922B293BC40026781E /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; };
@ -126,7 +124,6 @@
3D1015D72B27F54A00F5C29A /* Views */ = { 3D1015D72B27F54A00F5C29A /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3DAA71C42B31E11D00D5FB33 /* Utility */,
3D9063CC2B279A310063DD2A /* ContentView.swift */, 3D9063CC2B279A310063DD2A /* ContentView.swift */,
3DDD67902B293B780026781E /* Dashboard */, 3DDD67902B293B780026781E /* Dashboard */,
3D8AB2A62B366309005BD7D0 /* Library */, 3D8AB2A62B366309005BD7D0 /* Library */,
@ -235,14 +232,6 @@
path = SignIn; path = SignIn;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3DAA71C42B31E11D00D5FB33 /* Utility */ = {
isa = PBXGroup;
children = (
3DAA71C52B31E19200D5FB33 /* AsyncImageView.swift */,
);
path = Utility;
sourceTree = "<group>";
};
3DDD67902B293B780026781E /* Dashboard */ = { 3DDD67902B293B780026781E /* Dashboard */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -413,7 +402,6 @@
3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */, 3DC6BA2D2B2A422300416B9F /* SettingsController.swift in Sources */,
3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */, 3D91FDCB2B28CA2500919017 /* SignInToServerView.swift in Sources */,
3D16FC3C2B2CDFB500E6D8B3 /* DashboardLibraryView.swift in Sources */, 3D16FC3C2B2CDFB500E6D8B3 /* DashboardLibraryView.swift in Sources */,
3DAA71C62B31E19200D5FB33 /* AsyncImageView.swift in Sources */,
3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */, 3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */,
3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */, 3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */,
3D41D1F52B2C962500E58234 /* AppearancePicker.swift in Sources */, 3D41D1F52B2C962500E58234 /* AppearancePicker.swift in Sources */,

View File

@ -21,6 +21,7 @@ struct JelApp: App {
ContentView() ContentView()
.environmentObject(jellyfinClientController) .environmentObject(jellyfinClientController)
.task { .task {
URLCache.shared.diskCapacity = 1_000_000_000 // 1GB cache for images
AuthStateController.shared.load() AuthStateController.shared.load()
SettingsController.shared.load() SettingsController.shared.load()
jellyfinClientController.setUrl(url: AuthStateController.shared.serverUrl) jellyfinClientController.setUrl(url: AuthStateController.shared.serverUrl)

View File

@ -14,7 +14,12 @@ struct DashboardLibraryView: View {
@StateObject var authState: AuthStateController = AuthStateController.shared @StateObject var authState: AuthStateController = AuthStateController.shared
@State var libraries: [BaseItemDto] = [] @State var libraries: [BaseItemDto] = []
@State var loading: Bool = true
var body: some View { var body: some View {
if loading {
ProgressView()
.progressViewStyle(.circular)
}
ScrollView(.horizontal, showsIndicators: false) { ScrollView(.horizontal, showsIndicators: false) {
HStack { HStack {
ForEach(libraries) {library in ForEach(libraries) {library in
@ -22,9 +27,10 @@ struct DashboardLibraryView: View {
NavigationLink { NavigationLink {
LibraryDetailView(library: library) LibraryDetailView(library: library)
} label: { } label: {
LibraryIconView(library: library, height: 200) LibraryIconView(library: library, height: 150)
.padding() .padding()
} }
.buttonStyle(PlainButtonStyle())
} }
} }
} }
@ -36,11 +42,11 @@ struct DashboardLibraryView: View {
if let results = try await jellyfinClient.send(request).value.items { if let results = try await jellyfinClient.send(request).value.items {
libraries = results libraries = results
} }
loading = false
} catch { } catch {
} }
} }
} }}
}
} }
//#Preview { //#Preview {

View File

@ -15,11 +15,22 @@ struct LibraryDetailView: View {
@State var library: BaseItemDto @State var library: BaseItemDto
@State var items: [BaseItemDto]? = [] @State var items: [BaseItemDto]? = []
@State var loading: Bool = true
let columns = [
GridItem(.adaptive(minimum: 150))
]
var body: some View { var body: some View {
if loading {
ProgressView()
.progressViewStyle(.circular)
}
ScrollView { ScrollView {
ForEach(items ?? []) {item in LazyVGrid(columns: columns) {
LibraryIconView(library: item, imageType: "Primary", width: 120) ForEach(items ?? []) {item in
.padding() LibraryIconView(library: item, imageType: "Primary", height: 150)
.padding()
}
} }
} }
.navigationTitle(library.name ?? "Unknown") .navigationTitle(library.name ?? "Unknown")
@ -31,6 +42,7 @@ struct LibraryDetailView: View {
do { do {
let res = try await jellyfinClient.send(request) let res = try await jellyfinClient.send(request)
items = res.value.items items = res.value.items
loading = false
} catch { } catch {
} }
} }
@ -38,6 +50,6 @@ struct LibraryDetailView: View {
} }
} }
#Preview { //#Preview {
LibraryDetailView(library: BaseItemDto()) // LibraryDetailView(library: BaseItemDto())
} //}

View File

@ -18,20 +18,38 @@ struct LibraryIconView: View {
var width: CGFloat? var width: CGFloat?
var height: CGFloat? var height: CGFloat?
@State var loadedImageBinaryData: Data? @State var blurHashImage: UIImage = UIImage()
@State var imageUrl: URL?
var body: some View { var body: some View {
VStack { AsyncImage(url: imageUrl) {image in
AsyncImageView(imageId: library.id ?? "", VStack {
blurhash: library.imageBlurHashes?.primary?[library.imageTags?[imageType] ?? ""] ?? "", image
imageType: imageType) .resizable()
.aspectRatio(contentMode: .fill) .aspectRatio(contentMode: .fit)
.frame(width: width, height: height) .frame(width: width, height: height)
.clipShape(RoundedRectangle(cornerRadius: 5)) .clipShape(RoundedRectangle(cornerRadius: 3))
Text(library.name ?? "Unknown")
.font(.subheadline)
}
} placeholder: {
VStack {
Image(uiImage: blurHashImage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: width, height: height)
.clipShape(RoundedRectangle(cornerRadius: 3))
Text(library.name ?? "Unknown")
.font(.subheadline)
}
}
.onAppear {
let blurhash = library.imageBlurHashes?.primary?[library.imageTags?[imageType] ?? ""] ?? ""
blurHashImage = UIImage(blurHash: blurhash, size: CGSize(width: 32, height: 32)) ?? UIImage()
Text(library.name ?? "Unknown") let imageId = library.id ?? ""
.font(.subheadline) let request = Paths.getItemImage(itemID: imageId, imageType: imageType)
imageUrl = jellyfinClient.getUrl()?.appending(path: request.url?.absoluteString ?? "")
} }
} }
} }

View File

@ -1,53 +0,0 @@
//
// AsyncImageView.swift
// Jel
//
// Created by zerocool on 12/19/23.
//
import SwiftUI
import JellyfinKit
struct AsyncImageView: View {
@EnvironmentObject var jellyfinClient: JellyfinClientController
@State var imageId: String
@State var blurhash: String
@State var imageType: String
@State var loading = true
@State var uiImage: UIImage = UIImage()
var body: some View {
VStack {
if loading {
Image(uiImage: uiImage)
.resizable()
} else {
Image(uiImage: uiImage)
.resizable()
}
}
.onAppear {
uiImage = UIImage(blurHash: blurhash, size: CGSize(width: 16, height: 16)) ?? UIImage()
Task {
let request = Paths.getItemImage(itemID: imageId, imageType: imageType)
do {
let res = try await jellyfinClient.send(request)
if let image = UIImage(data: res.value) {
uiImage = image
loading = false
} else {
}
}
}
}
}
}
//#Preview {
// AsyncImageView(imageId: "", blurhash: "", imageType: "")
//}