diff --git a/Shared/Coordinators/ItemImagesCoordinator.swift b/Shared/Coordinators/ItemImagesCoordinator.swift index c378c102f..e49d150ca 100644 --- a/Shared/Coordinators/ItemImagesCoordinator.swift +++ b/Shared/Coordinators/ItemImagesCoordinator.swift @@ -44,7 +44,7 @@ final class ItemImagesCoordinator: ObservableObject, NavigationCoordinatable { AddItemImageView(viewModel: viewModel, imageType: imageType) } - func makeDeleteImage(imageInfo: (key: ImageInfo, value: UIImage)) -> NavigationViewCoordinator { + func makeDeleteImage(imageInfo: (key: ImageInfo, value: URL)) -> NavigationViewCoordinator { NavigationViewCoordinator { ItemImageDetailsView(viewModel: self.viewModel, localImageInfo: imageInfo) } diff --git a/Shared/ViewModels/ItemAdministration/ItemImagesViewModel.swift b/Shared/ViewModels/ItemAdministration/ItemImagesViewModel.swift index c2847239c..4a812ab1b 100644 --- a/Shared/ViewModels/ItemAdministration/ItemImagesViewModel.swift +++ b/Shared/ViewModels/ItemAdministration/ItemImagesViewModel.swift @@ -52,7 +52,7 @@ class ItemImagesViewModel: ViewModel, Stateful, Eventful { @Published var item: BaseItemDto @Published - var images: [ImageInfo: UIImage] = [:] + var images: [ImageInfo: URL] = [:] // MARK: - State Management @@ -281,7 +281,7 @@ class ItemImagesViewModel: ViewModel, Stateful, Eventful { let missingImageInfos = imageInfos.filter { !self.images.keys.contains($0) } - try await withThrowingTaskGroup(of: (ImageInfo, UIImage).self) { group in + try await withThrowingTaskGroup(of: (ImageInfo, URL).self) { group in for imageInfo in missingImageInfos { group.addTask { do { @@ -294,23 +294,18 @@ class ItemImagesViewModel: ViewModel, Stateful, Eventful { imageType: imageInfo.imageType?.rawValue ?? "", parameters: parameters ) - let response = try await self.userSession.client.send(request) - // TODO: Is there a way for me to just get the Image URL so I can use - // ImageView instead of passing/storing the full images? - if let image = UIImage(data: response.value) { - return (imageInfo, image) + if let imageURL = self.userSession.client.fullURL(with: request) { + return (imageInfo, imageURL) } - } catch { - throw JellyfinAPIError("Failed to fetch image for \(imageInfo): \(error)") } throw JellyfinAPIError("Failed to fetch image for \(imageInfo)") } } - for try await (imageInfo, image) in group { + for try await (imageInfo, URL) in group { await MainActor.run { - self.images[imageInfo] = image + self.images[imageInfo] = URL } } } @@ -452,7 +447,7 @@ class ItemImagesViewModel: ViewModel, Stateful, Eventful { } return (lhs.key.imageIndex ?? 0) < (rhs.key.imageIndex ?? 0) } - .reduce(into: [ImageInfo: UIImage]()) { result, pair in + .reduce(into: [ImageInfo: URL]()) { result, pair in var updatedInfo = pair.key if updatedInfo.imageType == imageInfo.imageType, let index = updatedInfo.imageIndex, diff --git a/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/Components/LocalImageInfoView.swift b/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/Components/LocalImageInfoView.swift index c6a399c5b..47dc06bef 100644 --- a/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/Components/LocalImageInfoView.swift +++ b/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/Components/LocalImageInfoView.swift @@ -21,7 +21,7 @@ extension ItemImageDetailsView { // MARK: - Image Info let imageInfo: ImageInfo - let image: UIImage + let imageURL: URL // MARK: - Image Functions @@ -32,17 +32,17 @@ extension ItemImageDetailsView { @ViewBuilder private var header: some View { Section { - ZStack { - Color.secondarySystemFill - - Image(uiImage: image) - .resizable() - .scaledToFill() - } - .scaledToFit() - .posterStyle(imageInfo.height ?? 0 > imageInfo.width ?? 0 ? .portrait : .landscape) - .frame(maxWidth: .infinity) - .accessibilityIgnoresInvertColors() + ImageView(imageURL) + .placeholder { _ in + Image(systemName: "circle") + } + .failure { + Image(systemName: "circle") + } + .scaledToFit() + .frame(maxWidth: .infinity) + .posterStyle(imageInfo.height ?? 0 > imageInfo.width ?? 0 ? .portrait : .landscape) + .accessibilityIgnoresInvertColors() } .listRowBackground(Color.clear) .listRowCornerRadius(0) diff --git a/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/ItemImageDetailsView.swift b/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/ItemImageDetailsView.swift index cce11f626..3e1a86257 100644 --- a/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/ItemImageDetailsView.swift +++ b/Swiftfin/Views/ItemEditorView/ItemImages/ItemImageDetailsView/ItemImageDetailsView.swift @@ -31,7 +31,7 @@ struct ItemImageDetailsView: View { // MARK: - Image Variable - private let localImageInfo: (key: ImageInfo, value: UIImage)? + private let localImageInfo: (key: ImageInfo, value: URL)? private let remoteImageInfo: RemoteImageInfo? @@ -47,7 +47,7 @@ struct ItemImageDetailsView: View { // MARK: - Initializer - init(viewModel: ItemImagesViewModel, localImageInfo: (key: ImageInfo, value: UIImage)? = nil, remoteImageInfo: RemoteImageInfo? = nil) { + init(viewModel: ItemImagesViewModel, localImageInfo: (key: ImageInfo, value: URL)? = nil, remoteImageInfo: RemoteImageInfo? = nil) { self._viewModel = ObservedObject(wrappedValue: viewModel) self.localImageInfo = localImageInfo self.remoteImageInfo = remoteImageInfo @@ -89,7 +89,7 @@ struct ItemImageDetailsView: View { @ViewBuilder var contentView: some View { if let imageInfo = localImageInfo { - LocalImageInfoView(imageInfo: imageInfo.key, image: imageInfo.value) { + LocalImageInfoView(imageInfo: imageInfo.key, imageURL: imageInfo.value) { viewModel.send(.deleteImage(imageInfo.key)) } } else if let imageInfo = remoteImageInfo { diff --git a/Swiftfin/Views/ItemEditorView/ItemImages/ItemImagesView/ItemImagesView.swift b/Swiftfin/Views/ItemEditorView/ItemImages/ItemImagesView/ItemImagesView.swift index 8d2e88efb..814d48026 100644 --- a/Swiftfin/Views/ItemEditorView/ItemImages/ItemImagesView/ItemImagesView.swift +++ b/Swiftfin/Views/ItemEditorView/ItemImages/ItemImagesView/ItemImagesView.swift @@ -117,7 +117,7 @@ struct ItemImagesView: View { ScrollView(.horizontal, showsIndicators: false) { HStack { ForEach(sortedImageArray, id: \.key) { imageData in - imageButton(imageData.value) { + imageButton(imageInfo: imageData.key, imageURL: imageData.value) { router.route(to: \.deleteImage, imageData) } } @@ -165,16 +165,22 @@ struct ItemImagesView: View { // MARK: - Image Button - private func imageButton(_ image: UIImage, onSelect: @escaping () -> Void) -> some View { + private func imageButton(imageInfo: ImageInfo, imageURL: URL?, onSelect: @escaping () -> Void) -> some View { Button(action: onSelect) { ZStack { Color.secondarySystemFill - Image(uiImage: image) - .resizable() - .scaledToFill() + ImageView(imageURL) + .placeholder { _ in + Image(systemName: "circle") + } + .failure { + Image(systemName: "circle") + } + .scaledToFit() + .frame(maxWidth: .infinity) } .scaledToFit() - .posterStyle(image.size.height > image.size.width ? .portrait : .landscape) + .posterStyle(imageInfo.height ?? 0 > imageInfo.width ?? 0 ? .portrait : .landscape) .frame(maxHeight: 150) .shadow(radius: 4) .padding(16) diff --git a/Swiftfin/Views/ItemEditorView/ItemImages/ItemPhotoPickerView/Components/ItemPhotoCropView.swift b/Swiftfin/Views/ItemEditorView/ItemImages/ItemPhotoPickerView/Components/ItemPhotoCropView.swift index 165ea242c..019109591 100644 --- a/Swiftfin/Views/ItemEditorView/ItemImages/ItemPhotoPickerView/Components/ItemPhotoCropView.swift +++ b/Swiftfin/Views/ItemEditorView/ItemImages/ItemPhotoPickerView/Components/ItemPhotoCropView.swift @@ -32,6 +32,11 @@ struct ItemPhotoCropView: View { let type: ImageType + // MARK: - Error State + + @State + private var error: Error? + // MARK: - Body var body: some View { diff --git a/Swiftfin/Views/UserProfileImagePicker/Components/UserProfileImageCropView.swift b/Swiftfin/Views/UserProfileImagePicker/Components/UserProfileImageCropView.swift index cecf78f6a..e3357fcf8 100644 --- a/Swiftfin/Views/UserProfileImagePicker/Components/UserProfileImageCropView.swift +++ b/Swiftfin/Views/UserProfileImagePicker/Components/UserProfileImageCropView.swift @@ -33,7 +33,7 @@ struct UserProfileImageCropView: View { // MARK: - Error State @State - private var error: Error? = nil + private var error: Error? // MARK: - Body