From 23ca3b1ab0e93bd9ce630ea127f888bdcd93a39f Mon Sep 17 00:00:00 2001 From: Ethen Date: Tue, 30 Aug 2022 01:55:52 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NetworkProtocol/Sources/Request.swift | 1 + .../User/Delete/DeleteUserRequest.swift | 24 ++++++++++++ .../User/Delete/DeleteUserResponse.swift | 13 +++++++ .../Sources/User/UserDataSource.swift | 12 +++++- .../Targets/Router/GuestDetailRouter.swift | 4 ++ .../GuestDetailRoutingLogic.swift | 1 + .../Scene/Sources/GuestDetailInteractor.swift | 15 ++++++++ .../Scene/Sources/GuestDetailPresenter.swift | 5 +++ .../Sources/GuestDetailViewController.swift | 37 ++++++++++++++++++- .../Scene/Sources/GuestDetailWorker.swift | 14 +++++-- .../Sources/GuestListViewController.swift | 15 +------- Projects/Marryting/Sources/AppDelegate.swift | 14 +++++++ 12 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 Projects/DataSource/Sources/User/Delete/DeleteUserRequest.swift create mode 100644 Projects/DataSource/Sources/User/Delete/DeleteUserResponse.swift diff --git a/Projects/Core/NetworkProtocol/Sources/Request.swift b/Projects/Core/NetworkProtocol/Sources/Request.swift index e86eb45..4ae1e2b 100644 --- a/Projects/Core/NetworkProtocol/Sources/Request.swift +++ b/Projects/Core/NetworkProtocol/Sources/Request.swift @@ -13,6 +13,7 @@ public enum HTTPMethod: String, Encodable { case get = "GET" case post = "POST" case put = "PUT" + case delete = "DELETE" } /// Query Params Array diff --git a/Projects/DataSource/Sources/User/Delete/DeleteUserRequest.swift b/Projects/DataSource/Sources/User/Delete/DeleteUserRequest.swift new file mode 100644 index 0000000..11c6516 --- /dev/null +++ b/Projects/DataSource/Sources/User/Delete/DeleteUserRequest.swift @@ -0,0 +1,24 @@ +// +// DeleteUserRequest.swift +// DataSource +// +// Created by 박건우 on 2022/08/30. +// Copyright © 2022 kr.mash-up. All rights reserved. +// + +import NetworkProtocol + +public struct DeleteUserRequest: Request { + + public typealias Body = EmptyRequestBody + + public typealias Output = DeleteUserResponse + + public var endpoint: String = "/api/v1/users" + + public var method: HTTPMethod = .delete + + public init(userId: Int) { + endpoint = endpoint + "/\(userId)" + } +} diff --git a/Projects/DataSource/Sources/User/Delete/DeleteUserResponse.swift b/Projects/DataSource/Sources/User/Delete/DeleteUserResponse.swift new file mode 100644 index 0000000..8ec5f2a --- /dev/null +++ b/Projects/DataSource/Sources/User/Delete/DeleteUserResponse.swift @@ -0,0 +1,13 @@ +// +// DeleteUserResponse.swift +// DataSource +// +// Created by 박건우 on 2022/08/30. +// Copyright © 2022 kr.mash-up. All rights reserved. +// + +import NetworkProtocol + +public typealias DeleteUserResponse = BaseResponse + +public struct DeleteUserResponseBody: Response {} diff --git a/Projects/DataSource/Sources/User/UserDataSource.swift b/Projects/DataSource/Sources/User/UserDataSource.swift index 7326ef1..55d35bd 100644 --- a/Projects/DataSource/Sources/User/UserDataSource.swift +++ b/Projects/DataSource/Sources/User/UserDataSource.swift @@ -8,6 +8,7 @@ import Foundation import NetworkProtocol +import Network public enum LocalStorageKey: String { case token @@ -20,11 +21,16 @@ public protocol UserLocalDataSoureceProtocol { func read(key: LocalStorageKey) -> LocalUser? func save(_ data: T, key: LocalStorageKey) func removeAll(key: LocalStorageKey) + func deleteUser(request: DeleteUserRequest) async throws -> DeleteUserResponse } public final class UserLocalDataSourece: UserLocalDataSoureceProtocol { - public init() {} + private let network: NetworkProtocol + + public init(network: NetworkProtocol = Network(session: .shared)) { + self.network = network + } public func saveToken(_ token: String, key: LocalStorageKey) { UserDefaults.standard.set(token, forKey: key.rawValue) @@ -57,4 +63,8 @@ public final class UserLocalDataSourece: UserLocalDataSoureceProtocol { public func removeAll(key: LocalStorageKey) { UserDefaults.standard.removeObject(forKey: key.rawValue) } + + public func deleteUser(request: DeleteUserRequest) async throws -> DeleteUserResponse { + return try await network.send(request) + } } diff --git a/Projects/Features/GuestDetail/Targets/Router/GuestDetailRouter.swift b/Projects/Features/GuestDetail/Targets/Router/GuestDetailRouter.swift index f297482..c229b59 100644 --- a/Projects/Features/GuestDetail/Targets/Router/GuestDetailRouter.swift +++ b/Projects/Features/GuestDetail/Targets/Router/GuestDetailRouter.swift @@ -40,4 +40,8 @@ public final class GuestDetailRouter: GuestDetailRoutingLogic, GuestDetailDataPa public func routeToMeetingListScene() { viewController?.navigationController?.popToRootViewController(animated: true) } + + public func routeToLoginScene() { + NotificationCenter.default.post(name: Notification.Name("LogoutNotification"), object: nil) + } } diff --git a/Projects/Features/GuestDetail/Targets/RoutingProtocol/GuestDetailRoutingLogic.swift b/Projects/Features/GuestDetail/Targets/RoutingProtocol/GuestDetailRoutingLogic.swift index 105d798..05d6969 100644 --- a/Projects/Features/GuestDetail/Targets/RoutingProtocol/GuestDetailRoutingLogic.swift +++ b/Projects/Features/GuestDetail/Targets/RoutingProtocol/GuestDetailRoutingLogic.swift @@ -15,4 +15,5 @@ public protocol GuestDetailRoutingLogic { func routeToLikeRequestScene(targetId id: Int) func routeToMeetingListScene() + func routeToLoginScene() } diff --git a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailInteractor.swift b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailInteractor.swift index 2b3fe44..1f007e7 100644 --- a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailInteractor.swift +++ b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailInteractor.swift @@ -15,6 +15,7 @@ import Models protocol GuestDetailBusinessLogic { func fetchGuest() + func withdraw() } final class GuestDetailInteractor: GuestDetailBusinessLogic, GuestDetailDataStore { @@ -44,6 +45,20 @@ final class GuestDetailInteractor: GuestDetailBusinessLogic, GuestDetailDataStor presenter?.presentGuest(response: .init(guest: selectedGuest)) } + + func withdraw() { + guard let worker = worker else { + return + } + Task { + do { + try await worker.withdraw() + presenter?.presentWithdrawResult() + } catch { + + } + } + } private func fetchChangeMeetingButton() { guard let worker = worker else { diff --git a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailPresenter.swift b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailPresenter.swift index ab4d8cf..029414d 100644 --- a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailPresenter.swift +++ b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailPresenter.swift @@ -15,6 +15,7 @@ import UIKit protocol GuestDetailPresentationLogic { func presentGuest(response: GuestDetail.GetGuest.Response) func presentMeetingChangeButton(response: GuestDetail.GetMeetingCount.Response) + func presentWithdrawResult() } final class GuestDetailPresenter: GuestDetailPresentationLogic { @@ -50,4 +51,8 @@ final class GuestDetailPresenter: GuestDetailPresentationLogic { let meetings = response.meetings self.viewController?.displayChangeMeetingButton(viewModel: .init(isHidden: meetings.count <= 1)) } + + func presentWithdrawResult() { + self.viewController?.displayWithdrawResult() + } } diff --git a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift index 83e6912..6f97f38 100644 --- a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift +++ b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift @@ -21,6 +21,7 @@ import SnapKit protocol GuestDetailDisplayLogic: AnyObject { func displayGuest(viewModel: GuestDetail.GetGuest.ViewModel) func displayChangeMeetingButton(viewModel: GuestDetail.GetMeetingCount.ViewModel) + func displayWithdrawResult() } struct GuestDetailViewModel { @@ -234,6 +235,16 @@ public final class GuestDetailViewController: UIViewController, GuestDetailDispl v.title = "WHO I AM" return v }() + + private lazy var withdrawLabel: UILabel = { + let lb = UILabel() + lb.text = "탈퇴하기" + lb.font = .body2() + lb.textColor = Pallete.Light.grey300.color + lb.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapWithdraw))) + lb.isUserInteractionEnabled = true + return lb + }() var viewModel: GuestDetailViewModel? { didSet { @@ -298,7 +309,7 @@ public final class GuestDetailViewController: UIViewController, GuestDetailDispl self.contentView.addSubviews(self.topHeaderStackView) self.contentView.addSubviews(self.addressStackView, self.careerStackView) self.contentView.addSubviews(self.collectionView, self.pageControl) - self.contentView.addSubviews(self.keywordContainerView, self.whoIAmContainerView) + self.contentView.addSubviews(self.keywordContainerView, self.whoIAmContainerView, self.withdrawLabel) self.scrollView.addSubview(self.contentView) self.navigationView.addSubview(self.backButton) self.navigationView.addSubview(self.changeMeetingButton) @@ -371,7 +382,11 @@ public final class GuestDetailViewController: UIViewController, GuestDetailDispl self.whoIAmContainerView.snp.makeConstraints { make in make.top.equalTo(self.keywordContainerView.snp.bottom) make.leading.trailing.equalToSuperview() - make.bottom.equalToSuperview().inset(20) + } + self.withdrawLabel.snp.makeConstraints { make in + make.top.equalTo(self.whoIAmContainerView.snp.bottom) + make.leading.equalToSuperview().inset(40) + make.bottom.equalToSuperview().inset(90) } self.collectionView.snp.makeConstraints { make in make.top.equalTo(careerStackView.snp.bottom).offset(50) @@ -404,6 +419,10 @@ public final class GuestDetailViewController: UIViewController, GuestDetailDispl // } } + + func displayWithdrawResult() { + self.router?.routeToLoginScene() + } } // MARK: Button Tap @@ -462,6 +481,20 @@ extension GuestDetailViewController { func changeMeetingButtonDidTap() { router?.routeToMeetingListScene() } + + @objc func didTapWithdraw() { + let alertViewController = UIAlertController( + title: "탈퇴하기", + message: "현재 계정을 탈퇴합니다.", + preferredStyle: .alert + ) + alertViewController.addAction(UIAlertAction(title: "취소", style: .cancel, handler: nil)) + alertViewController.addAction(UIAlertAction(title: "탈퇴할래요", style: .default, handler: { [weak self] _ in + self?.interactor?.withdraw() + })) + + self.present(alertViewController, animated: true) + } } // MARK: UICollectionViewDataSource diff --git a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailWorker.swift b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailWorker.swift index 4e63cda..6827bf8 100644 --- a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailWorker.swift +++ b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailWorker.swift @@ -17,6 +17,7 @@ import DataSource protocol GuestDetailWorkerProtocol { func fetchUser() -> User func fetchMeetings() async throws -> [Meeting] + func withdraw() async throws } final class GuestDetailWorker: GuestDetailWorkerProtocol { @@ -33,9 +34,10 @@ final class GuestDetailWorker: GuestDetailWorkerProtocol { } func fetchUser() -> User { - userLocalDataSourece.read(key: .localUser).map { [weak self] localUser in - return self!.convertToUser(localUser) - }! + let defaultUser: User = .init(id: 0, name: "", gender: .male, career: "", birth: Date(), age: 0, address: "", pictures: [], answers: [], keyword: []) + return userLocalDataSourece.read(key: .localUser).map { [weak self] localUser in + return self?.convertToUser(localUser) ?? defaultUser + } ?? defaultUser } func convertToUser(_ user: LocalUser) -> User { @@ -53,6 +55,12 @@ final class GuestDetailWorker: GuestDetailWorkerProtocol { return [] } } + + func withdraw() async throws { + _ = try await userLocalDataSourece.deleteUser(request: .init(userId: fetchUser().id)) + userLocalDataSourece.removeAll(key: .token) + userLocalDataSourece.removeAll(key: .localUser) + } private func convertToMeeting(_ dto: GetMeetingListDTO) -> Meeting { let dateFormatter = DateFormatter() diff --git a/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift b/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift index 3420d84..95cdf9f 100644 --- a/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift +++ b/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift @@ -68,12 +68,6 @@ public class GuestListViewController: UIViewController, GuestListDisplayLogic { return v }() - lazy var logoImageView: UIImageView = { - let v = UIImageView() - v.image = .create(.logo) - return v - }() - lazy var likeListButton: UIImageView = { let v = UIImageView() v.image = .create(.ic_heart) @@ -108,7 +102,7 @@ public class GuestListViewController: UIViewController, GuestListDisplayLogic { lazy var guestSwipeableView: ZLSwipeableView = { let v = ZLSwipeableView() - v.shouldSwipeView = { _, _, _ in true } + v.onlySwipeTopCard = true return v }() @@ -196,7 +190,6 @@ public class GuestListViewController: UIViewController, GuestListDisplayLogic { self.view.addSubview(self.secondTitleLabel) self.view.addSubview(self.guestSwipeableView) self.view.addSubview(self.reportButton) - self.navigationView.addSubview(self.logoImageView) self.navigationView.addSubview(self.likeListButton) self.navigationView.addSubview(self.myInfoButton) @@ -218,12 +211,6 @@ public class GuestListViewController: UIViewController, GuestListDisplayLogic { make.top.equalTo(self.secondTitleLabel.snp.bottom).offset(32) make.bottom.equalToSuperview().inset(84) } - self.logoImageView.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) - make.centerY.equalToSuperview() - make.height.equalTo(25) - make.width.equalTo(81) - } self.likeListButton.snp.makeConstraints { make in make.height.width.equalTo(40) make.trailing.equalTo(self.myInfoButton.snp.leading) diff --git a/Projects/Marryting/Sources/AppDelegate.swift b/Projects/Marryting/Sources/AppDelegate.swift index c4fb841..6009801 100644 --- a/Projects/Marryting/Sources/AppDelegate.swift +++ b/Projects/Marryting/Sources/AppDelegate.swift @@ -24,6 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) -> Bool { KakaoSDK.initSDK(appKey: "bd614625345b00170f51b167c97e96e9") Font.registerFonts() + registerNotification() window = UIWindow(frame: UIScreen.main.bounds) let navigationController = UINavigationController() navigationController.isNavigationBarHidden = true @@ -41,4 +42,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return false } + + func registerNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(logout(_:)), name: NSNotification.Name("LogoutNotification"), object: nil) + } + + @objc func logout(_ notification: Notification) { + DispatchQueue.main.async { + let loginViewController = LoginViewController() + let navigationController = UINavigationController(rootViewController: loginViewController) + navigationController.isNavigationBarHidden = true + self.window?.rootViewController = navigationController + } + } } From 66f1ef2f82e2b6f739c5f8369a0a5fb2e64a25ac Mon Sep 17 00:00:00 2001 From: Ethen Date: Tue, 30 Aug 2022 02:10:22 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EB=82=B4=20=EC=A0=95=EB=B3=B4,=20=EA=B2=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=B6=84=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/GuestDetailViewController.swift | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift index 6f97f38..f42a278 100644 --- a/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift +++ b/Projects/Features/GuestDetail/Targets/Scene/Sources/GuestDetailViewController.swift @@ -41,15 +41,6 @@ public final class GuestDetailViewController: UIViewController, GuestDetailDispl // MARK: Object lifecycle - public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - setup() - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setup() - } // MARK: Setup @@ -243,6 +234,7 @@ public final class GuestDetailViewController: UIViewController, GuestDetailDispl lb.textColor = Pallete.Light.grey300.color lb.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapWithdraw))) lb.isUserInteractionEnabled = true + lb.isHidden = profileDetailType == .guestProfile return lb }() @@ -282,15 +274,20 @@ public final class GuestDetailViewController: UIViewController, GuestDetailDispl case guestProfile } - private var profileDetailType: ProfileDetailType? + private var profileDetailType: ProfileDetailType // MARK: View lifecycle public init(profileDetailType: ProfileDetailType = .guestProfile) { - self.init() self.profileDetailType = profileDetailType + super.init(nibName: nil, bundle: nil) + setup() } - + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + public override func viewDidLoad() { super.viewDidLoad() setUI() From 2f1fa5c88558811d61d6c2e48cb1b452efb9b5ce Mon Sep 17 00:00:00 2001 From: Ethen Date: Tue, 30 Aug 2022 02:17:52 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Targets/Scene/Sources/GuestListViewController.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift b/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift index 5508d27..0ca7796 100644 --- a/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift +++ b/Projects/Features/GuestList/Targets/Scene/Sources/GuestListViewController.swift @@ -67,11 +67,7 @@ public class GuestListViewController: UIViewController, GuestListDisplayLogic { let v = UIView() return v }() -<<<<<<< HEAD -======= - ->>>>>>> develop lazy var likeListButton: UIImageView = { let v = UIImageView() v.image = .create(.ic_heart)