From 0387d64f6cf4db98734a89e016ce400d5fdbc1f6 Mon Sep 17 00:00:00 2001 From: Jisu Kim Date: Sun, 8 Dec 2024 00:29:52 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[WEAV-154]=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/OpenapiGenerated/Client.swift | 107 ++++++++ .../Sources/OpenapiGenerated/Types.swift | 244 +++++++++++++++++- .../Sources/openapi-generator-cli/3days-oas | 2 +- .../ProfileService/ProfileService.swift | 2 +- .../ProfilePannel/ProfilePanelView.swift | 135 ++++++---- .../Model/Sources/Auth/Domain/UserInfo.swift | 5 +- .../Dependency+extensions.swift | 2 +- 7 files changed, 434 insertions(+), 63 deletions(-) diff --git a/OpenApiGenerator/Sources/OpenapiGenerated/Client.swift b/OpenApiGenerator/Sources/OpenapiGenerated/Client.swift index 65ff9df..3689ee3 100644 --- a/OpenApiGenerator/Sources/OpenapiGenerated/Client.swift +++ b/OpenApiGenerator/Sources/OpenapiGenerated/Client.swift @@ -1035,6 +1035,113 @@ public struct Client: APIProtocol { } ) } + /// 프로필 이미지 삭제 + /// + /// 특정 프로필 이미지를 삭제합니다. + /// + /// - Remark: HTTP `DELETE /users/my/profile-images/{imageId}`. + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)`. + public func deleteProfileImage(_ input: Operations.deleteProfileImage.Input) async throws -> Operations.deleteProfileImage.Output { + try await client.send( + input: input, + forOperation: Operations.deleteProfileImage.id, + serializer: { input in + let path = try converter.renderedPath( + template: "/users/my/profile-images/{}", + parameters: [ + input.path.imageId + ] + ) + var request: HTTPTypes.HTTPRequest = .init( + soar_path: path, + method: .delete + ) + suppressMutabilityWarning(&request) + converter.setAcceptHeader( + in: &request.headerFields, + contentTypes: input.headers.accept + ) + return (request, nil) + }, + deserializer: { response, responseBody in + switch response.status.code { + case 204: + return .noContent(.init()) + case 401: + let contentType = converter.extractContentTypeIfPresent(in: response.headerFields) + let body: Components.Responses.Unauthorized.Body + let chosenContentType = try converter.bestContentType( + received: contentType, + options: [ + "application/json" + ] + ) + switch chosenContentType { + case "application/json": + body = try await converter.getResponseBodyAsJSON( + Components.Schemas.ErrorResponse.self, + from: responseBody, + transforming: { value in + .json(value) + } + ) + default: + preconditionFailure("bestContentType chose an invalid content type.") + } + return .unauthorized(.init(body: body)) + case 404: + let contentType = converter.extractContentTypeIfPresent(in: response.headerFields) + let body: Components.Responses.NotFound.Body + let chosenContentType = try converter.bestContentType( + received: contentType, + options: [ + "application/json" + ] + ) + switch chosenContentType { + case "application/json": + body = try await converter.getResponseBodyAsJSON( + Components.Schemas.ErrorResponse.self, + from: responseBody, + transforming: { value in + .json(value) + } + ) + default: + preconditionFailure("bestContentType chose an invalid content type.") + } + return .notFound(.init(body: body)) + case 500: + let contentType = converter.extractContentTypeIfPresent(in: response.headerFields) + let body: Components.Responses.InternalServerError.Body + let chosenContentType = try converter.bestContentType( + received: contentType, + options: [ + "application/json" + ] + ) + switch chosenContentType { + case "application/json": + body = try await converter.getResponseBodyAsJSON( + Components.Schemas.ErrorResponse.self, + from: responseBody, + transforming: { value in + .json(value) + } + ) + default: + preconditionFailure("bestContentType chose an invalid content type.") + } + return .internalServerError(.init(body: body)) + default: + return .undocumented( + statusCode: response.status.code, + .init() + ) + } + } + ) + } /// 내 원하는 파트너 수정 /// /// 현재 로그인한 사용자의 원하는 파트너 정보를 수정합니다. diff --git a/OpenApiGenerator/Sources/OpenapiGenerated/Types.swift b/OpenApiGenerator/Sources/OpenapiGenerated/Types.swift index 350da4f..5d23793 100644 --- a/OpenApiGenerator/Sources/OpenapiGenerated/Types.swift +++ b/OpenApiGenerator/Sources/OpenapiGenerated/Types.swift @@ -67,6 +67,13 @@ public protocol APIProtocol: Sendable { /// - Remark: HTTP `POST /users/my/profile-images/upload-complete`. /// - Remark: Generated from `#/paths//users/my/profile-images/upload-complete/post(completeProfileImageUpload)`. func completeProfileImageUpload(_ input: Operations.completeProfileImageUpload.Input) async throws -> Operations.completeProfileImageUpload.Output + /// 프로필 이미지 삭제 + /// + /// 특정 프로필 이미지를 삭제합니다. + /// + /// - Remark: HTTP `DELETE /users/my/profile-images/{imageId}`. + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)`. + func deleteProfileImage(_ input: Operations.deleteProfileImage.Input) async throws -> Operations.deleteProfileImage.Output /// 내 원하는 파트너 수정 /// /// 현재 로그인한 사용자의 원하는 파트너 정보를 수정합니다. @@ -245,6 +252,21 @@ extension APIProtocol { body: body )) } + /// 프로필 이미지 삭제 + /// + /// 특정 프로필 이미지를 삭제합니다. + /// + /// - Remark: HTTP `DELETE /users/my/profile-images/{imageId}`. + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)`. + public func deleteProfileImage( + path: Operations.deleteProfileImage.Input.Path, + headers: Operations.deleteProfileImage.Input.Headers = .init() + ) async throws -> Operations.deleteProfileImage.Output { + try await deleteProfileImage(Operations.deleteProfileImage.Input( + path: path, + headers: headers + )) + } /// 내 원하는 파트너 수정 /// /// 현재 로그인한 사용자의 원하는 파트너 정보를 수정합니다. @@ -604,6 +626,10 @@ public enum Components { /// /// - Remark: Generated from `#/components/schemas/GetMyUserInfoResponse/name`. public var name: Swift.String + /// 프로필 이미지 목록 + /// + /// - Remark: Generated from `#/components/schemas/GetMyUserInfoResponse/profileImages`. + public var profileImages: [Components.Schemas.ProfileImage]? /// 사용자의 전화번호 (한국 휴대폰 번호 형식) /// /// - Remark: Generated from `#/components/schemas/GetMyUserInfoResponse/phoneNumber`. @@ -619,6 +645,7 @@ public enum Components { /// - Parameters: /// - id: 사용자 식별자 /// - name: 사용자 이름 + /// - profileImages: 프로필 이미지 목록 /// - phoneNumber: 사용자의 전화번호 (한국 휴대폰 번호 형식) /// - profile: /// - desiredPartner: @@ -626,6 +653,7 @@ public enum Components { public init( id: Swift.String? = nil, name: Swift.String, + profileImages: [Components.Schemas.ProfileImage]? = nil, phoneNumber: Swift.String, profile: Components.Schemas.UserProfileDisplayInfo, desiredPartner: Components.Schemas.UserDesiredPartner, @@ -633,6 +661,7 @@ public enum Components { ) { self.id = id self.name = name + self.profileImages = profileImages self.phoneNumber = phoneNumber self.profile = profile self.desiredPartner = desiredPartner @@ -641,6 +670,7 @@ public enum Components { public enum CodingKeys: String, CodingKey { case id case name + case profileImages case phoneNumber case profile case desiredPartner @@ -1293,7 +1323,7 @@ public enum Components { /// - Remark: Generated from `#/components/schemas/CompleteProfileImageUploadRequest/imageId`. public var imageId: Swift.String /// - Remark: Generated from `#/components/schemas/CompleteProfileImageUploadRequest/extension`. - public var _extension: Components.Schemas.ProfileImageExtension? + public var _extension: Components.Schemas.ProfileImageExtension /// Creates a new `CompleteProfileImageUploadRequest`. /// /// - Parameters: @@ -1301,7 +1331,7 @@ public enum Components { /// - _extension: public init( imageId: Swift.String, - _extension: Components.Schemas.ProfileImageExtension? = nil + _extension: Components.Schemas.ProfileImageExtension ) { self.imageId = imageId self._extension = _extension @@ -1361,6 +1391,39 @@ public enum Components { @frozen public enum ProfileImageExtension: String, Codable, Hashable, Sendable { case PNG = "PNG" } + /// - Remark: Generated from `#/components/schemas/ProfileImage`. + public struct ProfileImage: Codable, Hashable, Sendable { + /// 이미지 식별자 + /// + /// - Remark: Generated from `#/components/schemas/ProfileImage/id`. + public var id: Swift.String + /// 이미지 URL + /// + /// - Remark: Generated from `#/components/schemas/ProfileImage/url`. + public var url: Swift.String + /// - Remark: Generated from `#/components/schemas/ProfileImage/extension`. + public var _extension: Components.Schemas.ProfileImageExtension + /// Creates a new `ProfileImage`. + /// + /// - Parameters: + /// - id: 이미지 식별자 + /// - url: 이미지 URL + /// - _extension: + public init( + id: Swift.String, + url: Swift.String, + _extension: Components.Schemas.ProfileImageExtension + ) { + self.id = id + self.url = url + self._extension = _extension + } + public enum CodingKeys: String, CodingKey { + case id + case url + case _extension = "extension" + } + } } /// Types generated from the `#/components/parameters` section of the OpenAPI document. public enum Parameters { @@ -3116,6 +3179,183 @@ public enum Operations { } } } + /// 프로필 이미지 삭제 + /// + /// 특정 프로필 이미지를 삭제합니다. + /// + /// - Remark: HTTP `DELETE /users/my/profile-images/{imageId}`. + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)`. + public enum deleteProfileImage { + public static let id: Swift.String = "deleteProfileImage" + public struct Input: Sendable, Hashable { + /// - Remark: Generated from `#/paths/users/my/profile-images/{imageId}/DELETE/path`. + public struct Path: Sendable, Hashable { + /// 삭제할 프로필 이미지 ID + /// + /// - Remark: Generated from `#/paths/users/my/profile-images/{imageId}/DELETE/path/imageId`. + public var imageId: Swift.String + /// Creates a new `Path`. + /// + /// - Parameters: + /// - imageId: 삭제할 프로필 이미지 ID + public init(imageId: Swift.String) { + self.imageId = imageId + } + } + public var path: Operations.deleteProfileImage.Input.Path + /// - Remark: Generated from `#/paths/users/my/profile-images/{imageId}/DELETE/header`. + public struct Headers: Sendable, Hashable { + public var accept: [OpenAPIRuntime.AcceptHeaderContentType] + /// Creates a new `Headers`. + /// + /// - Parameters: + /// - accept: + public init(accept: [OpenAPIRuntime.AcceptHeaderContentType] = .defaultValues()) { + self.accept = accept + } + } + public var headers: Operations.deleteProfileImage.Input.Headers + /// Creates a new `Input`. + /// + /// - Parameters: + /// - path: + /// - headers: + public init( + path: Operations.deleteProfileImage.Input.Path, + headers: Operations.deleteProfileImage.Input.Headers = .init() + ) { + self.path = path + self.headers = headers + } + } + @frozen public enum Output: Sendable, Hashable { + public struct NoContent: Sendable, Hashable { + /// Creates a new `NoContent`. + public init() {} + } + /// 삭제 성공 + /// + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)/responses/204`. + /// + /// HTTP response code: `204 noContent`. + case noContent(Operations.deleteProfileImage.Output.NoContent) + /// The associated value of the enum case if `self` is `.noContent`. + /// + /// - Throws: An error if `self` is not `.noContent`. + /// - SeeAlso: `.noContent`. + public var noContent: Operations.deleteProfileImage.Output.NoContent { + get throws { + switch self { + case let .noContent(response): + return response + default: + try throwUnexpectedResponseStatus( + expectedStatus: "noContent", + response: self + ) + } + } + } + /// 인증 실패 (토큰 만료 또는 유효하지 않은 토큰) + /// + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)/responses/401`. + /// + /// HTTP response code: `401 unauthorized`. + case unauthorized(Components.Responses.Unauthorized) + /// The associated value of the enum case if `self` is `.unauthorized`. + /// + /// - Throws: An error if `self` is not `.unauthorized`. + /// - SeeAlso: `.unauthorized`. + public var unauthorized: Components.Responses.Unauthorized { + get throws { + switch self { + case let .unauthorized(response): + return response + default: + try throwUnexpectedResponseStatus( + expectedStatus: "unauthorized", + response: self + ) + } + } + } + /// 리소스를 찾을 수 없음 + /// + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)/responses/404`. + /// + /// HTTP response code: `404 notFound`. + case notFound(Components.Responses.NotFound) + /// The associated value of the enum case if `self` is `.notFound`. + /// + /// - Throws: An error if `self` is not `.notFound`. + /// - SeeAlso: `.notFound`. + public var notFound: Components.Responses.NotFound { + get throws { + switch self { + case let .notFound(response): + return response + default: + try throwUnexpectedResponseStatus( + expectedStatus: "notFound", + response: self + ) + } + } + } + /// 서버 오류 + /// + /// - Remark: Generated from `#/paths//users/my/profile-images/{imageId}/delete(deleteProfileImage)/responses/500`. + /// + /// HTTP response code: `500 internalServerError`. + case internalServerError(Components.Responses.InternalServerError) + /// The associated value of the enum case if `self` is `.internalServerError`. + /// + /// - Throws: An error if `self` is not `.internalServerError`. + /// - SeeAlso: `.internalServerError`. + public var internalServerError: Components.Responses.InternalServerError { + get throws { + switch self { + case let .internalServerError(response): + return response + default: + try throwUnexpectedResponseStatus( + expectedStatus: "internalServerError", + response: self + ) + } + } + } + /// Undocumented response. + /// + /// A response with a code that is not documented in the OpenAPI document. + case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload) + } + @frozen public enum AcceptableContentType: AcceptableProtocol { + case json + case other(Swift.String) + public init?(rawValue: Swift.String) { + switch rawValue.lowercased() { + case "application/json": + self = .json + default: + self = .other(rawValue) + } + } + public var rawValue: Swift.String { + switch self { + case let .other(string): + return string + case .json: + return "application/json" + } + } + public static var allCases: [Self] { + [ + .json + ] + } + } + } /// 내 원하는 파트너 수정 /// /// 현재 로그인한 사용자의 원하는 파트너 정보를 수정합니다. diff --git a/OpenApiGenerator/Sources/openapi-generator-cli/3days-oas b/OpenApiGenerator/Sources/openapi-generator-cli/3days-oas index 4768665..b4118d6 160000 --- a/OpenApiGenerator/Sources/openapi-generator-cli/3days-oas +++ b/OpenApiGenerator/Sources/openapi-generator-cli/3days-oas @@ -1 +1 @@ -Subproject commit 4768665d84af33868ec4481df16ff4d5b3dd3b52 +Subproject commit b4118d6a2e2cf6d57430fe47d6c4a45b9f9817e8 diff --git a/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift b/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift index f44941e..15c963f 100644 --- a/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift +++ b/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift @@ -143,7 +143,7 @@ extension ProfileService: ProfileServiceProtocol { } private func requestCompleteCallback(imageId: String) async throws { - let result = try await client.completeProfileImageUpload(body: .json(.init(imageId: imageId))) + let result = try await client.completeProfileImageUpload(body: .json(.init(imageId: imageId, _extension: .PNG))) _ = try result.ok } } diff --git a/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift b/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift index 0ead06f..99c85c6 100644 --- a/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift +++ b/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift @@ -10,7 +10,7 @@ import SwiftUI import Model import DesignCore import CommonKit -import Nuke +import NukeUI import NetworkKit struct ProfilePannelView: View { @@ -38,68 +38,89 @@ struct ProfilePannelView: View { VStack(spacing: 6) { HStack { Spacer() - ZStack(alignment: .topTrailing) { - DesignCore.Images.profileDefault.image - .cornerRadius(20, corners: .allCorners) - if profile.profileImageUrl == nil { - DesignCore.Images.profileBorder.image + ZStack(alignment: .center) { + if let profileImageUrl = profile.profileImageUrl { + LazyImage(url: profileImageUrl) { state in + if let image = state.image { + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 96, height: 96, alignment: .center) + .clipped() + .cornerRadius(20, corners: .allCorners) + .cornerRadius(48, corners: .bottomRight) + } else { + ProgressView() + } + } + } else { + DesignCore.Images.profileDefault.image + .frame(width: 102, height: 102) + .cornerRadius(20, corners: .allCorners) } - DesignCore.Images.cameraCircleFill.image + DesignCore.Images.profileBorder.image .resizable() - .frame(width: 36, height: 36) - .offset(x: 6, y: -6) - .onTapGesture { - isShowPhotoSheet = true - } - .confirmationDialog( - "프로필 사진 설정", - isPresented: $isShowPhotoSheet, - actions: { - Button("앨범에서 사진 선택") { - isShowPhotoPicker = true - } - Button("기본 이미지 적용") { - // default image - } - Button("취소", role: .cancel) {} - }, - message: { - Text("프로필 사진 설정") + .frame(width: 102, height: 102) + + ZStack(alignment: .topTrailing) { + Color.clear + DesignCore.Images.cameraCircleFill.image + .resizable() + .frame(width: 36, height: 36) + .offset(x: 6, y: -6) + .onTapGesture { + isShowPhotoSheet = true } - ) - .photoPicker( - isPresented: $isShowPhotoPicker - ) { images in - selectedImage = images.first - isShowPhotoPreview = true - } - .navigationDestination(isPresented: $isShowPhotoPreview) { - PhotoPreviewView( - image: selectedImage, - isPresented: .constant(true), - showButton: true, - navigationTitle: "내 프로필 설정", - buttonTitle: "프로필 사진으로 등록하기", - backHandler: { - isShowPhotoPreview = false - }, - buttonHandler: { imageData in - do { - if let imageData { - try await ProfileService.shared.requestUploadImage(image: imageData) - } - await MainActor.run { - isShowPhotoPreview = false - } - } catch { - print(error) - ToastHelper.showErrorMessage( - "프로필 사진 업로드에 실패하였습니다." - ) + .confirmationDialog( + "프로필 사진 설정", + isPresented: $isShowPhotoSheet, + actions: { + Button("앨범에서 사진 선택") { + isShowPhotoPicker = true + } + Button("기본 이미지 적용") { + // default image } + Button("취소", role: .cancel) {} + }, + message: { + Text("프로필 사진 설정") } ) - } + .photoPicker( + isPresented: $isShowPhotoPicker + ) { images in + selectedImage = images.first + isShowPhotoPreview = true + } + .navigationDestination(isPresented: $isShowPhotoPreview) { + PhotoPreviewView( + image: selectedImage, + isPresented: .constant(true), + showButton: true, + navigationTitle: "내 프로필 설정", + buttonTitle: "프로필 사진으로 등록하기", + backHandler: { + isShowPhotoPreview = false + }, + buttonHandler: { imageData in + do { + if let imageData { + try await ProfileService.shared.requestUploadImage(image: imageData) + } + await MainActor.run { + isShowPhotoPreview = false + } + } catch { + print(error) + ToastHelper.showErrorMessage( + "프로필 사진 업로드에 실패하였습니다." + ) + } + } + ) + } + } } .frame(width: 102, height: 102) Spacer() diff --git a/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift b/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift index 7850341..f4fd65a 100644 --- a/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift +++ b/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift @@ -50,6 +50,9 @@ public struct UserInfo: Equatable, Identifiable, Hashable { self.profile = .init(from: dto.profile) self.dreamPartner = .init(from: dto.desiredPartner) self.profileWidgets = dto.profileWidgets.map { .init(from: $0) } + if let profileUrl = dto.profileImages?.first?.url { + self.profile.profileImageUrl = URL(string: profileUrl) + } } public static var mock: UserInfo { @@ -87,7 +90,7 @@ public struct UserInfoProfile: Hashable, Identifiable, Equatable { public let jobOccupation: String public var jobOccupationRawValue: String public var locations: [LocationModel] - public let profileImageUrl: URL? + public var profileImageUrl: URL? public var companyName: String { set { diff --git a/Tuist/ProjectDescriptionHelpers/Dependency+extensions.swift b/Tuist/ProjectDescriptionHelpers/Dependency+extensions.swift index 3814797..9af189c 100644 --- a/Tuist/ProjectDescriptionHelpers/Dependency+extensions.swift +++ b/Tuist/ProjectDescriptionHelpers/Dependency+extensions.swift @@ -8,7 +8,7 @@ import ProjectDescription public enum ExternalDependency: String { - case nuke = "Nuke" + case nuke = "NukeUI" case openapiGenerated = "OpenapiGenerated" case navigationTransitions = "NavigationTransitions" case toast = "Toast" From 7275f514bb5f79f35aded39f5e72ebf206b0dbaf Mon Sep 17 00:00:00 2001 From: Jisu Kim Date: Sun, 8 Dec 2024 00:34:49 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[WEAV-154]=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A6=AC=EC=85=8B=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ProfileService/ProfileService.swift | 7 +++++++ .../Sources/ProfileService/ProfileServiceMock.swift | 5 +++++ .../Model/Model/Sources/Auth/Domain/UserInfo.swift | 10 +++++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift b/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift index 15c963f..749ca21 100644 --- a/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift +++ b/Projects/Core/NetworkKit/Sources/ProfileService/ProfileService.swift @@ -25,6 +25,8 @@ public protocol ProfileServiceProtocol { func requestPutPartnerInfo(userInfo: UserInfo) async throws + func requestResetProfileImage(imageId: String) async throws + func requestUploadImage(image: Data) async throws } @@ -103,6 +105,11 @@ extension ProfileService: ProfileServiceProtocol { return } + public func requestResetProfileImage(imageId: String) async throws { + let result = try await client.deleteProfileImage(path: .init(imageId: imageId)) + _ = try result.noContent + } + public func requestUploadImage(image: Data) async throws { // url 받기 let uploadUrlInfo = try await requestPresignedUrl() diff --git a/Projects/Core/NetworkKit/Sources/ProfileService/ProfileServiceMock.swift b/Projects/Core/NetworkKit/Sources/ProfileService/ProfileServiceMock.swift index a0e2a9f..40a136c 100644 --- a/Projects/Core/NetworkKit/Sources/ProfileService/ProfileServiceMock.swift +++ b/Projects/Core/NetworkKit/Sources/ProfileService/ProfileServiceMock.swift @@ -39,6 +39,11 @@ public final class ProfileServiceMock: ProfileServiceProtocol { return } + public func requestResetProfileImage(imageId: String) async throws { + print("✅ [ProfileServiceMock] requestResetProfileImage 성공!") + return + } + public func requestUploadImage(image: Data) async throws { print("✅ [ProfileServiceMock] requestUploadImage 성공!") return diff --git a/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift b/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift index f4fd65a..10ba0db 100644 --- a/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift +++ b/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift @@ -50,8 +50,9 @@ public struct UserInfo: Equatable, Identifiable, Hashable { self.profile = .init(from: dto.profile) self.dreamPartner = .init(from: dto.desiredPartner) self.profileWidgets = dto.profileWidgets.map { .init(from: $0) } - if let profileUrl = dto.profileImages?.first?.url { - self.profile.profileImageUrl = URL(string: profileUrl) + if let profileImage = dto.profileImages?.first { + self.profile.profileImageUrl = URL(string: profileImage.url) + self.profile.profileImageId = profileImage.id } } @@ -91,6 +92,7 @@ public struct UserInfoProfile: Hashable, Identifiable, Equatable { public var jobOccupationRawValue: String public var locations: [LocationModel] public var profileImageUrl: URL? + public var profileImageId: String? public var companyName: String { set { @@ -113,7 +115,8 @@ public struct UserInfoProfile: Hashable, Identifiable, Equatable { jobOccupation: String, jobOccupationRawValue: String, locations: [LocationModel], - profileImageUrl: URL? = nil + profileImageUrl: URL? = nil, + profileImageId: String? = nil ) { self.gender = gender self.birthYear = birthYear @@ -123,6 +126,7 @@ public struct UserInfoProfile: Hashable, Identifiable, Equatable { self.jobOccupationRawValue = jobOccupationRawValue self.locations = locations self.profileImageUrl = profileImageUrl + self.profileImageId = profileImageId } public init(from dto: Components.Schemas.UserProfileDisplayInfo) { From 1c3cbac11ba41110e30dcf68a1c686a919256266 Mon Sep 17 00:00:00 2001 From: Jisu Kim Date: Sun, 8 Dec 2024 01:02:34 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[WEAV-154]=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20reset=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/Home/Sources/Profile/ProfileView.swift | 6 +++++- .../Sources/ProfilePannel/ProfilePanelView.swift | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Projects/Features/Home/Sources/Profile/ProfileView.swift b/Projects/Features/Home/Sources/Profile/ProfileView.swift index b6262a2..42ccd50 100644 --- a/Projects/Features/Home/Sources/Profile/ProfileView.swift +++ b/Projects/Features/Home/Sources/Profile/ProfileView.swift @@ -63,7 +63,11 @@ public struct ProfileView: View { ProfilePannelView( name: userInfo.name, profile: userInfo.profile - ) + ) { + Task { + await intent.refreshUserInfo() + } + } LeftAlignText("Introductions") .typography(.en_medium_16) diff --git a/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift b/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift index 99c85c6..69301b0 100644 --- a/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift +++ b/Projects/Features/Home/Sources/ProfilePannel/ProfilePanelView.swift @@ -17,6 +17,7 @@ struct ProfilePannelView: View { let name: String let profile: UserInfoProfile + var refreshHandler: () -> Void @State var isShowPhotoSheet: Bool = false @State var isShowPhotoPicker: Bool = false @@ -79,7 +80,15 @@ struct ProfilePannelView: View { isShowPhotoPicker = true } Button("기본 이미지 적용") { - // default image + Task { + if let profileImageId = profile.profileImageId { + try await ProfileService.shared.requestResetProfileImage( + imageId: profileImageId + ) + _ = try await AppCoordinator.shared.refreshMyUserInfo() + refreshHandler() + } + } } Button("취소", role: .cancel) {} }, @@ -112,7 +121,6 @@ struct ProfilePannelView: View { isShowPhotoPreview = false } } catch { - print(error) ToastHelper.showErrorMessage( "프로필 사진 업로드에 실패하였습니다." )