Skip to content

Commit

Permalink
Merge pull request #245 from depromeet/fix/#239-account-image-upload-…
Browse files Browse the repository at this point in the history
…modify

fix: 회원 가입시 프로필 이미지 업로드 로직 수정
  • Loading branch information
Do-hyun-Kim authored Jan 12, 2024
2 parents 316e2b2 + 2b66de5 commit 6553ffc
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public final class AccountSignUpReactor: Reactor {

case profileImageTapped // Action Sheet출력 하는 이벤트
case didTapCompletehButton
case profilePresignedURL(String)
case profilePresignedURL(String, Data)
case didTapPHAssetsImage(Data)
}

public enum Mutation {
Expand All @@ -47,7 +48,9 @@ public final class AccountSignUpReactor: Reactor {

case profileImageTapped
case setprofilePresignedURL(String)
case setprofileImage(Data)
case didTapCompletehButton(AccessTokenResponse?)
case setPHAssetsImage(Data)
}

public struct State {
Expand All @@ -70,6 +73,7 @@ public final class AccountSignUpReactor: Reactor {

var profilePresignedURL: String = ""
var profileImageButtontapped: Bool = false
var profileImage: Data? = nil
var didTapCompletehButtonFinish: AccessTokenResponse? = nil
}

Expand Down Expand Up @@ -109,8 +113,11 @@ extension AccountSignUpReactor {
case .profileImageTapped:
return Observable.just(Mutation.profileImageTapped)

case let .profilePresignedURL(presignedURL):
return Observable.just(Mutation.setprofilePresignedURL(presignedURL))
case let .profilePresignedURL(presignedURL, originImage):
return .concat(
.just(.setprofilePresignedURL(presignedURL)),
.just(.setprofileImage(originImage))
)
case let .didTapNickNameButton(nickName):
let parameters: AccountNickNameEditParameter = AccountNickNameEditParameter(name: nickName)
return accountRepository.executeNicknameUpdate(memberId: currentState.memberId, parameter: parameters)
Expand All @@ -121,10 +128,41 @@ extension AccountSignUpReactor {

case .didTapCompletehButton:
let date = getDateToString(year: currentState.year!, month: currentState.month, day: currentState.day)
return accountRepository.signUp(name: currentState.nickname, date: date, photoURL: nil)
return accountRepository.signUp(name: currentState.nickname, date: date, photoURL: currentState.profilePresignedURL)
.flatMap { tokenEntity -> Observable<Mutation> in
return Observable.just(Mutation.didTapCompletehButton(tokenEntity))
}
case let .didTapPHAssetsImage(profileImage):
let originalImage: String = "\(profileImage.hashValue).jpg"
let profileImageEditParameter: CameraDisplayImageParameters = CameraDisplayImageParameters(imageName: originalImage)

return .concat(
accountRepository.executePresignedImageURLCreate(parameter: profileImageEditParameter)
.withUnretained(self)
.subscribe(on: ConcurrentDispatchQueueScheduler.init(qos: .background))
.asObservable()
.flatMap { owner, entity -> Observable<AccountSignUpReactor.Mutation> in
guard let accountPresignedURL = entity?.imageURL else { return .empty() }
return owner.accountRepository.executeProfileImageUpload(to: accountPresignedURL, data: profileImage)
.asObservable()
.flatMap { isSuccess -> Observable<AccountSignUpReactor.Mutation> in
let originalPath = owner.configureAccountOriginalS3URL(url: accountPresignedURL)
let accountParameter = ProfileImageEditParameter(profileImageUrl: originalPath)
if isSuccess {
return .concat(
.just(.setprofilePresignedURL(accountPresignedURL)),
.just(.setprofileImage(profileImage))
)
} else {
return .empty()
}

}

}

)

}
}

Expand Down Expand Up @@ -166,12 +204,16 @@ extension AccountSignUpReactor {
newState.profilePresignedURL = url
case .profileImageTapped:
newState.profileImageButtontapped = true
case let .setprofileImage(profileImage):
newState.profileImage = profileImage
case .didTapCompletehButton(let token):
if let token = token {
newState.didTapCompletehButtonFinish = token
}
case let .setEditNickName(entity):
newState.profileNickNameEditEntity = entity
case let .setPHAssetsImage(profileImage):
newState.profileImage = profileImage
}

newState.isValidDateButton = newState.isValidYear && newState.isValidMonth && newState.isValidDay
Expand All @@ -192,4 +234,11 @@ extension AccountSignUpReactor {
let date = Calendar.current.date(from: components) ?? Date()
return dateFormatter.string(from: date)
}

func configureAccountOriginalS3URL(url: String) -> String {
guard let range = url.range(of: #"[^&?]+"#, options: .regularExpression) else { return "" }
return String(url[range])
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ final class AccountProfileViewController: BaseViewController<AccountSignUpReacto

NotificationCenter.default.rx
.notification(.AccountViewPresignURLDismissNotification)
.compactMap { notification -> String? in
guard let userInfo = notification.userInfo else { return nil }
return userInfo["presignedURL"] as? String
.compactMap { notification -> (String, Data)? in
guard let userInfo = notification.userInfo,
let presignedURL = userInfo["presignedURL"] as? String,
let originImage = userInfo["originImage"] as? Data else { return nil
}

return (presignedURL, originImage)
}
.map { Reactor.Action.profilePresignedURL($0)}
.map { Reactor.Action.profilePresignedURL($0.0, $0.1)}
.bind(to: reactor.action)
.disposed(by: disposeBag)
}
Expand All @@ -67,11 +71,18 @@ final class AccountProfileViewController: BaseViewController<AccountSignUpReacto
.bind(onNext: { $0.0.setProfilewView(with: $0.1) })
.disposed(by: disposeBag)

reactor.state.compactMap { $0.profileImage }
.map { UIImage(data: $0) }
.debug("ProfileView Account Bind")
.bind(to: profileView.rx.image)
.disposed(by: disposeBag)

reactor.state.map { $0.didTapCompletehButtonFinish }
.withUnretained(self)
.observe(on: Schedulers.main)
.bind(onNext: { $0.0.showNextPage(accessToken: $0.1) })
.disposed(by: disposeBag)

}

override func setupUI() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ public final class AccountSignUpViewController: BasePageViewController<AccountSi
.withUnretained(self)
.bind(onNext: { $0.0.createAlertController(owner: $0.0) })
.disposed(by: disposeBag)

NotificationCenter.default.rx
.notification(.PHPickerAssetsDidFinishPickingProcessingPhotoNotification)
.compactMap { notification -> Data? in
guard let userInfo = notification.userInfo else { return nil }
return userInfo["selectImage"] as? Data
}
.map{ Reactor.Action.didTapPHAssetsImage($0)}
.bind(to: reactor.action)
.disposed(by: disposeBag)

}

public override func setupUI() {
Expand Down Expand Up @@ -106,7 +117,7 @@ extension AccountSignUpViewController {
private func createAlertController(owner: AccountSignUpViewController) {
let alertController: UIAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let presentCameraAction: UIAlertAction = UIAlertAction(title: "카메라", style: .default) { _ in
let cameraViewController = CameraDIContainer(cameraType: .account).makeViewController()
let cameraViewController = CameraDIContainer(cameraType: .profile).makeViewController()
owner.navigationController?.pushViewController(cameraViewController, animated: true)
}
let presentAlbumAction: UIAlertAction = UIAlertAction(title: "앨범", style: .default) { _ in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,17 @@ public final class CameraViewController: BaseViewController<CameraViewReactor> {
.bind(onNext: { $0.0.dismissCameraViewController() } )
.disposed(by: disposeBag)

Observable
.zip(
reactor.state.map { $0.profileImageURLEntity },
reactor.state.map { $0.cameraType }
).filter { $0.1 == .account }
.map { $0.0 }

reactor.state
.map { ($0.accountImage, $0.profileImageURLEntity)}
.filter { $0.1 != nil }
.withUnretained(self)
.subscribe(onNext: { owner, entity in
let userInfo: [AnyHashable: Any] = ["presignedURL": entity]
.subscribe(onNext: { (owner, originEntity) in
let userInfo: [AnyHashable: Any] = ["presignedURL": originEntity.1?.imageURL, "originImage": originEntity.0]
NotificationCenter.default.post(name: .AccountViewPresignURLDismissNotification, object: nil, userInfo: userInfo)
owner.dismissCameraViewController()
}).disposed(by: disposeBag)


shutterButton
.rx.tap
Expand Down Expand Up @@ -373,7 +371,7 @@ extension CameraViewController: AVCapturePhotoCaptureDelegate {
public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
guard let photoData = photo.fileDataRepresentation(),
let imageData = UIImage(data: photoData)?.jpegData(compressionQuality: 1.0) else { return }
if self.reactor?.cameraType == .profile || self.reactor?.cameraType == .account {
if self.reactor?.cameraType == .profile {
output.photoOutputDidFinshProcessing(photo: imageData, error: error)
} else {
let cameraDisplayViewController = CameraDisplayDIContainer(displayData: imageData).makeViewController()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public final class CameraViewReactor: Reactor {
case setPosition(Bool)
case setFlashMode(Bool)
case setProfileS3Edit(Bool)
case setAccountProfileData(Data)
case setProfileImageURLResponse(CameraDisplayImageResponse?)
case setProfileMemberResponse(ProfileMemberResponse?)
}
Expand All @@ -40,6 +41,7 @@ public final class CameraViewReactor: Reactor {
@Pulse var isSwitchPosition: Bool
@Pulse var profileImageURLEntity: CameraDisplayImageResponse?
var cameraType: UploadLocation = .feed
var accountImage: Data?
var memberId: String
var isProfileEdit: Bool
@Pulse var profileMemberEntity: ProfileMemberResponse?
Expand All @@ -58,6 +60,7 @@ public final class CameraViewReactor: Reactor {
isSwitchPosition: false,
profileImageURLEntity: nil,
cameraType: cameraType,
accountImage: nil,
memberId: memberId,
isProfileEdit: false,
profileMemberEntity: nil
Expand Down Expand Up @@ -88,19 +91,21 @@ public final class CameraViewReactor: Reactor {
.flatMap { owner, entity -> Observable<CameraViewReactor.Mutation> in
// presignedURL에 image Upload 이것도 역시 병렬 큐 사용
guard let presingedURL = entity?.imageURL else { return .empty() }

if self.currentState.cameraType == .account {
return .concat(
.just(.setProfileImageURLResponse(entity)),
.just(.setLoading(false))
)
}


return owner.cameraUseCase.executeProfileUploadToS3(toURL: presingedURL, imageData: fileData)
.subscribe(on: ConcurrentDispatchQueueScheduler.init(qos: .background))
.asObservable()
.flatMap { isSuccess -> Observable<CameraViewReactor.Mutation> in
guard let profilePresingedURL = entity?.imageURL else { return .empty() }

if owner.memberId.isEmpty {
return .concat(
.just(.setProfileImageURLResponse(entity)),
.just(.setAccountProfileData(fileData)),
.just(.setLoading(false))
)
}

// 최종 Member Entity API 호출 작업 병렬 큐 사용 -> 사용 안하니 로딩 속도 느림
let originalURL = owner.configureProfileOriginalS3URL(url: profilePresingedURL, with: .profile)
let profileImageEditParameter: ProfileImageEditParameter = ProfileImageEditParameter(profileImageUrl: originalURL)
Expand Down Expand Up @@ -137,13 +142,12 @@ public final class CameraViewReactor: Reactor {
newState.isFlashMode = isFlash
case let .setProfileImageURLResponse(entity):
newState.profileImageURLEntity = entity
print("newState profileimageURL: \(newState.profileImageURLEntity)")
case let .setProfileS3Edit(isProfileEdit):
newState.isProfileEdit = isProfileEdit
print("newState isProfileEdit: \(newState.isProfileEdit)")
case let .setProfileMemberResponse(entity):
newState.profileMemberEntity = entity
print("newState profileMemberEnity: \(newState.profileMemberEntity)")
case let .setAccountProfileData(accountImage):
newState.accountImage = accountImage
}

return newState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public protocol AccountImpl: AnyObject {
func appleLogin(with snsType: SNS, vc: UIViewController) -> Observable<APIResult>
func executeNicknameUpdate(memberId: String, parameter: AccountNickNameEditParameter) -> Observable<AccountNickNameEditResponse>
func signUp(name: String, date: String, photoURL: String?) -> Observable<AccessTokenResponse?>
func executePresignedImageURLCreate(parameter: CameraDisplayImageParameters) -> Observable<CameraDisplayImageResponse?>
func executeProfileImageUpload(to url: String, data: Data) -> Observable<Bool>
}

public final class AccountRepository: AccountImpl {
Expand All @@ -35,6 +37,7 @@ public final class AccountRepository: AccountImpl {

let signInHelper = AccountSignInHelper()
private let apiWorker = AccountAPIWorker()
private let profileWorker = ProfileAPIWorker()
private let meApiWorekr = MeAPIWorker()

private let signInResult = PublishRelay<APIResult>()
Expand Down Expand Up @@ -120,6 +123,18 @@ public final class AccountRepository: AccountImpl {
.asObservable()
}

public func executePresignedImageURLCreate(parameter: CameraDisplayImageParameters) -> Observable<CameraDisplayImageResponse?> {
return profileWorker.createProfileImagePresingedURL(accessToken: accessToken, parameters: parameter)
.compactMap { $0?.toDomain() }
.asObservable()
}

public func executeProfileImageUpload(to url: String, data: Data) -> Observable<Bool> {
return profileWorker.uploadToProfilePresingedURL(accessToken: accessToken, toURL: url, with: data)
.asObservable()
}


public init() {
signInHelper.bind()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ import RxSwift
public enum UploadLocation {
case feed
case profile
case account

public var location: String {
switch self {
case .feed:
return "images/feed/"
case .profile:
return "images/profile/"
case .account:
return ""
}
}
}
Expand Down

0 comments on commit 6553ffc

Please sign in to comment.