From d94ccf53429bf05df7856edd05a90b88a98e1272 Mon Sep 17 00:00:00 2001 From: Sjmarf <78750526+Sjmarf@users.noreply.github.com> Date: Tue, 26 Sep 2023 21:38:15 +0100 Subject: [PATCH] Update --- Mlem.xcodeproj/project.pbxproj | 4 + Mlem/API/APIClient/APIClient+Pictrs.swift | 12 +-- .../Shared/Components/ImageUploadView.swift | 84 +++++++++++++++++++ .../Composer/PostDetailEditorView.swift | 81 ++---------------- .../Composer/PostEditorDetailView+Logic.swift | 15 ++-- 5 files changed, 109 insertions(+), 87 deletions(-) create mode 100644 Mlem/Views/Shared/Components/ImageUploadView.swift diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj index 54431b8ef..2326a0e21 100644 --- a/Mlem.xcodeproj/project.pbxproj +++ b/Mlem.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 031BF9552AB25AFB00F4517F /* SiteVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031BF9542AB25AFB00F4517F /* SiteVersionTests.swift */; }; 032109472AA7C3FC00912DFC /* CommunityLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032109462AA7C3FC00912DFC /* CommunityLabelView.swift */; }; 032109492AA7C41800912DFC /* AvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032109482AA7C41800912DFC /* AvatarView.swift */; }; + 032DD2FD2AC3594B00F1B33D /* ImageUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032DD2FC2AC3594B00F1B33D /* ImageUploadView.swift */; }; 034C724F2A82B61200B8A4B8 /* LayoutWidgetTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034C724E2A82B61200B8A4B8 /* LayoutWidgetTracker.swift */; }; 035EB0CA2A8687C200227859 /* JumpButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035EB0C92A8687C200227859 /* JumpButtonView.swift */; }; 038A16DF2A75172C0087987E /* LayoutWidgetEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 038A16DE2A75172C0087987E /* LayoutWidgetEditView.swift */; }; @@ -465,6 +466,7 @@ 031BF9542AB25AFB00F4517F /* SiteVersionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteVersionTests.swift; sourceTree = ""; }; 032109462AA7C3FC00912DFC /* CommunityLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunityLabelView.swift; sourceTree = ""; }; 032109482AA7C41800912DFC /* AvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarView.swift; sourceTree = ""; }; + 032DD2FC2AC3594B00F1B33D /* ImageUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageUploadView.swift; sourceTree = ""; }; 034C724E2A82B61200B8A4B8 /* LayoutWidgetTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutWidgetTracker.swift; sourceTree = ""; }; 035EB0C92A8687C200227859 /* JumpButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpButtonView.swift; sourceTree = ""; }; 038A16DE2A75172C0087987E /* LayoutWidgetEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutWidgetEditView.swift; sourceTree = ""; }; @@ -1975,6 +1977,7 @@ CD69F5702A422EDD0028D4F7 /* InteractionBarView.swift */, 632E8EE427EE63BD007E8D75 /* Components */, CDF1EF152A6C3BC2003594B6 /* End Of Feed View.swift */, + 032DD2FC2AC3594B00F1B33D /* ImageUploadView.swift */, CD45BCED2A75CA7200A2899C /* Thumbnail Image View.swift */, CDC1C9422A7AC24600072E3D /* ReadCheck.swift */, CD309C452A93FBD300988F95 /* Logo View.swift */, @@ -2816,6 +2819,7 @@ 03A1B3F72A84000400AB0DE0 /* APIContentAggregatesProtocol.swift in Sources */, CD4DBC032A6F803C001A1E61 /* ReplyToPost.swift in Sources */, CD6483302A38D31C00EE6CA3 /* UpvoteCounterView.swift in Sources */, + 032DD2FD2AC3594B00F1B33D /* ImageUploadView.swift in Sources */, 88B165B82A8643F4007C9115 /* View - NavigationBar Color.swift in Sources */, 030AC0522A64666C00037155 /* UserSettingsView.swift in Sources */, CDA2C5262A705D6000649D5A /* PostEditor.swift in Sources */, diff --git a/Mlem/API/APIClient/APIClient+Pictrs.swift b/Mlem/API/APIClient/APIClient+Pictrs.swift index d81c886fb..746a0bada 100644 --- a/Mlem/API/APIClient/APIClient+Pictrs.swift +++ b/Mlem/API/APIClient/APIClient+Pictrs.swift @@ -54,12 +54,12 @@ extension APIClient { struct ImageUploadResponse: Codable { public let msg: String - public let files: [File] - - struct File: Codable, Equatable { - public let file: String - public let deleteToken: String - } + public let files: [PictrsFile] +} + +struct PictrsFile: Codable, Equatable { + public let file: String + public let deleteToken: String } private struct MultiPartForm: Codable { diff --git a/Mlem/Views/Shared/Components/ImageUploadView.swift b/Mlem/Views/Shared/Components/ImageUploadView.swift new file mode 100644 index 000000000..128cbb461 --- /dev/null +++ b/Mlem/Views/Shared/Components/ImageUploadView.swift @@ -0,0 +1,84 @@ +// +// ImageUploadView.swift +// Mlem +// +// Created by Sjmarf on 26/09/2023. +// + +import SwiftUI + +struct PictrsImageModel { + enum UploadState { + case uploading(progress: Double) + case uploaded(file: PictrsFile?) + case failed(Error) + } + var image: Image? + var file: PictrsFile? + var state: UploadState = .uploading(progress: 0) +} + +struct ImageUploadView: View { + var imageModel: PictrsImageModel + let onCancel: () -> Void + + var body: some View { + VStack { + HStack(spacing: AppConstants.postAndCommentSpacing) { + if let image = imageModel.image { + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: AppConstants.thumbnailSize, height: AppConstants.thumbnailSize, alignment: .center) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.smallItemCornerRadius)) + } else { + placeHolderImage + } + VStack(alignment: .leading) { + Text("Attached Image") + Spacer() + HStack { + switch imageModel.state { + case .uploading(let progress): + Text("Uploading...") + ProgressView(value: progress) + .progressViewStyle(LinearProgressViewStyle()) + .frame(width: 100, height: 10) + case .uploaded: + Text("Uploaded") + case .failed: + Text("Failed") + .foregroundStyle(.red) + } + } + .foregroundStyle(.secondary) + } + .frame(height: AppConstants.thumbnailSize - 20) + Spacer() + } + .padding(10) + } + .frame(maxWidth: .infinity) + .background { + RoundedRectangle(cornerRadius: AppConstants.largeItemCornerRadius) + .fill(Color(UIColor.secondarySystemBackground)) + } + .overlay(alignment: .topTrailing) { + Button(action: onCancel, label: { + Image(systemName: "multiply") + .fontWeight(.semibold) + .tint(.secondary) + .padding(5) + .background(Circle().fill(.background)) + }) + .padding(5) + } + } + + @ViewBuilder + var placeHolderImage: some View { + RoundedRectangle(cornerRadius: AppConstants.smallItemCornerRadius) + .fill(.secondary) + .frame(width: AppConstants.thumbnailSize, height: AppConstants.thumbnailSize) + } +} diff --git a/Mlem/Views/Shared/Composer/PostDetailEditorView.swift b/Mlem/Views/Shared/Composer/PostDetailEditorView.swift index d47f33fd0..491109e4b 100644 --- a/Mlem/Views/Shared/Composer/PostDetailEditorView.swift +++ b/Mlem/Views/Shared/Composer/PostDetailEditorView.swift @@ -24,13 +24,6 @@ struct PostDetailEditorView: View { case title, url, body } - enum ImageUploadProgress { - case noImage - case uploading(Double) - case uploaded - case failed(Error) - } - @Dependency(\.apiClient) var apiClient @Dependency(\.errorHandler) var errorHandler @@ -50,8 +43,7 @@ struct PostDetailEditorView: View { @State var showingPhotosPicker: Bool = false @State var imageSelection: PhotosPickerItem? - @State var uploadedImage: Image? - @State var uploadProgress: ImageUploadProgress = .noImage + @State var imageModel: PictrsImageModel? @FocusState private var focusedField: Field? @@ -114,8 +106,12 @@ struct PostDetailEditorView: View { } // URL Row - if imageSelection != nil { - imageWidget + if let imageModel = imageModel { + ImageUploadView(imageModel: imageModel, onCancel: { + imageSelection = nil + self.imageModel = nil + postURL = "" + }) } else { VStack(alignment: .labelStart) { HStack { @@ -206,67 +202,4 @@ struct PostDetailEditorView: View { } } } - - @ViewBuilder - var imageWidget: some View { - VStack { - HStack(spacing: 10) { - if let uploadedImage = uploadedImage { - uploadedImage - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 60, height: 60, alignment: .center) - .clipShape(RoundedRectangle(cornerRadius: 5)) - .clipped() - } else { - RoundedRectangle(cornerRadius: 5) - .fill(.secondary) - .frame(width: 60, height: 60) - } - VStack(alignment: .leading) { - Text("Attached image") - Spacer() - HStack { - switch uploadProgress { - case .uploading(let progress): - Text("Uploading...") - ProgressView(value: progress) - .progressViewStyle(LinearProgressViewStyle()) - .frame(width: 100, height: 10) - case .uploaded: - Text("Uploaded") - case .failed: - Text("Failed") - .foregroundStyle(.red) - default: - EmptyView() - } - } - .foregroundStyle(.secondary) - } - .frame(height: 40) - Spacer() - } - .padding(10) - } - .frame(maxWidth: .infinity) - .background { - RoundedRectangle(cornerRadius: 10) - .fill(Color(UIColor.secondarySystemBackground)) - } - .overlay(alignment: .topTrailing) { - Button { - imageSelection = nil - uploadedImage = nil - postURL = "" - } label: { - Image(systemName: "multiply") - .fontWeight(.semibold) - .tint(.secondary) - .padding(5) - .background(Circle().fill(.background)) - } - .padding(5) - } - } } diff --git a/Mlem/Views/Shared/Composer/PostEditorDetailView+Logic.swift b/Mlem/Views/Shared/Composer/PostEditorDetailView+Logic.swift index fa98783c9..91e199c13 100644 --- a/Mlem/Views/Shared/Composer/PostEditorDetailView+Logic.swift +++ b/Mlem/Views/Shared/Composer/PostEditorDetailView+Logic.swift @@ -11,8 +11,8 @@ import PhotosUI extension PostDetailEditorView { var isReadyToPost: Bool { - switch uploadProgress { - case .noImage, .uploaded: + switch imageModel?.state { + case nil, .uploaded: return postTitle.trimmed.isNotEmpty default: return false @@ -57,27 +57,28 @@ extension PostDetailEditorView { } func uploadImage() { - uploadProgress = .uploading(0) + imageModel = .init() Task { do { let data = try await imageSelection?.loadTransferable(type: Data.self) if let data = data { + print("DONE") if let uiImage = UIImage(data: data) { - self.uploadedImage = Image(uiImage: uiImage) + imageModel?.image = Image(uiImage: uiImage) } - let res = try await apiClient.uploadImage(data, callback: { uploadProgress = .uploading($0) }) + let res = try await apiClient.uploadImage(data, callback: { imageModel?.state = .uploading(progress: $0) }) if let firstFile = res.files.first { var components = URLComponents() components.scheme = try apiClient.session.instanceUrl.scheme components.host = try apiClient.session.instanceUrl.host components.path = "/pictrs/image/\(firstFile.file)" postURL = components.url?.absoluteString ?? "" - uploadProgress = .uploaded + imageModel?.state = .uploaded(file: firstFile) } } } catch { - uploadProgress = .failed(error) + imageModel?.state = .failed(error) return } }