feat(auth): 인증 후 콘텐츠 설정과 개발용 모의 인증을 추가한다
This commit is contained in:
@@ -13,8 +13,18 @@ import Moya
|
||||
final class AuthRepository {
|
||||
private let api = MoyaProvider<AuthApi>()
|
||||
|
||||
func authCertificate(receiptId: String) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.auth(request: AuthVerifyRequest(receiptId: receiptId)))
|
||||
func authCertificate(request: AuthVerifyRequest) -> AnyPublisher<Response, MoyaError> {
|
||||
#if AUTH_VERIFICATION_MOCK
|
||||
let requestToken = UserDefaults.string(forKey: .token)
|
||||
guard let data = AuthVerificationMock.shared.confirm(request: request, requestToken: requestToken) else {
|
||||
return Fail(error: MoyaError.statusCode(Response(statusCode: 401, data: Data())))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
return Just(Response(statusCode: 200, data: data))
|
||||
.setFailureType(to: MoyaError.self)
|
||||
.eraseToAnyPublisher()
|
||||
#else
|
||||
return api.requestPublisher(.auth(request: request))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,12 @@ import Foundation
|
||||
|
||||
struct AuthVerifyRequest: Encodable {
|
||||
let receiptId: String
|
||||
let isAdultContentVisible: Bool
|
||||
let contentType: ContentType
|
||||
|
||||
enum CodingKeys : String, CodingKey {
|
||||
case receiptId = "receipt_id"
|
||||
case isAdultContentVisible
|
||||
case contentType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import SwiftUI
|
||||
|
||||
import Bootpay
|
||||
import BootpayUI
|
||||
import Kingfisher
|
||||
import RefreshableScrollView
|
||||
|
||||
@@ -33,28 +32,23 @@ struct MyPageView: View {
|
||||
BaseView(isLoading: $viewModel.isLoading) {
|
||||
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
|
||||
viewModel.isShowAuthView {
|
||||
BootpayUI(payload: payload, requestType: BootpayRequest.TYPE_AUTHENTICATION)
|
||||
.onConfirm {
|
||||
DEBUG_LOG("onConfirm: \($0)")
|
||||
return true
|
||||
}
|
||||
.onCancel {
|
||||
DEBUG_LOG("onCancel: \($0)")
|
||||
}
|
||||
.onError {
|
||||
DEBUG_LOG("onError: \($0)")
|
||||
AuthVerificationView(
|
||||
payload: payload,
|
||||
onCancel: {},
|
||||
onError: {
|
||||
viewModel.errorMessage = I18n.Main.Auth.authenticationError
|
||||
viewModel.isShowPopup = true
|
||||
viewModel.isShowAuthView = false
|
||||
}
|
||||
.onDone {
|
||||
DEBUG_LOG("onDone: \($0)")
|
||||
},
|
||||
onDone: {
|
||||
viewModel.authVerify($0)
|
||||
}
|
||||
.onClose {
|
||||
DEBUG_LOG("onClose")
|
||||
viewModel.isShowAuthView = false
|
||||
viewModel.presentAuthContentSettings()
|
||||
},
|
||||
onClose: {
|
||||
viewModel.isShowAuthView = false
|
||||
}
|
||||
)
|
||||
} else {
|
||||
VStack(spacing: 0) {
|
||||
// Header
|
||||
@@ -180,7 +174,7 @@ struct MyPageView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2)
|
||||
.authContentSettings(viewModel: viewModel)
|
||||
.onAppear {
|
||||
payload.applicationId = BOOTPAY_APP_ID
|
||||
payload.price = 0
|
||||
|
||||
@@ -14,6 +14,9 @@ final class MyPageViewModel: ObservableObject {
|
||||
private let authRepository = AuthRepository()
|
||||
private let noticeRepository = NoticeRepository()
|
||||
private var subscription = Set<AnyCancellable>()
|
||||
private var pendingAuthReceiptId: String?
|
||||
private var pendingAuthRequest: AuthVerifyRequest?
|
||||
private var pendingAuthSuccess: (() -> Void)?
|
||||
|
||||
@Published var latestNotice: NoticeItem? = nil
|
||||
|
||||
@@ -28,6 +31,16 @@ final class MyPageViewModel: ObservableObject {
|
||||
@Published var isShowPopup = false
|
||||
@Published var isLoading = false
|
||||
@Published var isShowAuthView = false
|
||||
@Published var isShowAuthContentSettings = false
|
||||
@Published var authIsAdultContentVisible = true {
|
||||
didSet {
|
||||
if !authIsAdultContentVisible {
|
||||
authContentType = .ALL
|
||||
}
|
||||
}
|
||||
}
|
||||
@Published var authContentType: ContentType = .ALL
|
||||
@Published private(set) var isSubmittingAuth = false
|
||||
|
||||
func getMypage() {
|
||||
isLoading = true
|
||||
@@ -73,61 +86,164 @@ final class MyPageViewModel: ObservableObject {
|
||||
}
|
||||
|
||||
func authVerify(_ data: [String: Any], onSuccess: (() -> Void)? = nil) {
|
||||
isLoading = true
|
||||
|
||||
let _data = data["data"] as? [String: Any]
|
||||
|
||||
if let data = _data {
|
||||
let receiptId = data["receipt_id"] as! String
|
||||
|
||||
authRepository.authCertificate(receiptId: receiptId)
|
||||
.sink { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
DEBUG_LOG("finish")
|
||||
case .failure(let error):
|
||||
ERROR_LOG(error.localizedDescription)
|
||||
}
|
||||
} receiveValue: { [unowned self] response in
|
||||
self.isLoading = false
|
||||
let responseData = response.data
|
||||
|
||||
do {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let decoded = try jsonDecoder.decode(ApiResponse<AuthResponse>.self, from: responseData)
|
||||
|
||||
if let data = decoded.data, decoded.success {
|
||||
if data.gender == 0 {
|
||||
UserDefaults.set(ContentType.FEMALE.rawValue, forKey: .contentPreference)
|
||||
} else {
|
||||
UserDefaults.set(ContentType.MALE.rawValue, forKey: .contentPreference)
|
||||
}
|
||||
|
||||
if let onSuccess = onSuccess {
|
||||
onSuccess()
|
||||
} else {
|
||||
AppState.shared.isRestartApp = true
|
||||
AppState.shared.setAppStep(step: .splash)
|
||||
}
|
||||
} else {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = I18n.MyPage.Auth.verificationErrorWithSupport
|
||||
}
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = I18n.MyPage.Auth.verificationErrorWithSupport
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
} else {
|
||||
isLoading = false
|
||||
guard Thread.isMainThread else {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.authVerify(data, onSuccess: onSuccess)
|
||||
}
|
||||
return
|
||||
}
|
||||
guard pendingAuthReceiptId == nil, !isSubmittingAuth else { return }
|
||||
|
||||
guard let payload = data["data"] as? [String: Any],
|
||||
let receiptId = payload["receipt_id"] as? String,
|
||||
!receiptId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||||
errorMessage = I18n.Main.Auth.authenticationError
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
pendingAuthReceiptId = receiptId
|
||||
pendingAuthSuccess = onSuccess
|
||||
authIsAdultContentVisible = true
|
||||
authContentType = .ALL
|
||||
}
|
||||
|
||||
func presentAuthContentSettings() {
|
||||
guard Thread.isMainThread else {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.presentAuthContentSettings()
|
||||
}
|
||||
return
|
||||
}
|
||||
guard pendingAuthReceiptId != nil, !isSubmittingAuth else { return }
|
||||
isShowAuthContentSettings = true
|
||||
}
|
||||
|
||||
func confirmAuthContentSettings() {
|
||||
guard Thread.isMainThread else {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.confirmAuthContentSettings()
|
||||
}
|
||||
return
|
||||
}
|
||||
guard let receiptId = pendingAuthReceiptId, !isSubmittingAuth else { return }
|
||||
|
||||
pendingAuthRequest = AuthVerifyRequest(
|
||||
receiptId: receiptId,
|
||||
isAdultContentVisible: authIsAdultContentVisible,
|
||||
contentType: authIsAdultContentVisible ? authContentType : .ALL
|
||||
)
|
||||
isSubmittingAuth = true
|
||||
isShowAuthContentSettings = false
|
||||
}
|
||||
|
||||
func submitPendingAuthVerification() {
|
||||
guard Thread.isMainThread else {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.submitPendingAuthVerification()
|
||||
}
|
||||
return
|
||||
}
|
||||
guard isSubmittingAuth, let request = pendingAuthRequest else { return }
|
||||
pendingAuthRequest = nil
|
||||
|
||||
authRepository.authCertificate(request: request)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] result in
|
||||
if case .failure = result {
|
||||
self?.handleAuthSubmissionFailure()
|
||||
}
|
||||
} receiveValue: { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
|
||||
guard (200..<300).contains(response.statusCode) else {
|
||||
self.handleAuthSubmissionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let decoded = try JSONDecoder().decode(ApiResponse<AuthResponse>.self, from: response.data)
|
||||
guard decoded.success, decoded.data != nil else {
|
||||
self.handleAuthSubmissionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
if self.pendingAuthSuccess != nil {
|
||||
self.synchronizeAuthenticatedMember()
|
||||
} else {
|
||||
UserDefaults.set(request.isAdultContentVisible, forKey: .isAdultContentVisible)
|
||||
UserDefaults.set(request.contentType.rawValue, forKey: .contentPreference)
|
||||
self.restartAfterAuthVerification()
|
||||
}
|
||||
} catch {
|
||||
self.handleAuthSubmissionFailure()
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
private func handleAuthSubmissionFailure() {
|
||||
pendingAuthRequest = nil
|
||||
isSubmittingAuth = false
|
||||
errorMessage = I18n.MyPage.Auth.verificationErrorWithSupport
|
||||
isShowPopup = true
|
||||
isShowAuthContentSettings = true
|
||||
}
|
||||
|
||||
private func synchronizeAuthenticatedMember() {
|
||||
repository.getMemberInfo()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] result in
|
||||
if case .failure = result {
|
||||
self?.restartAfterAuthVerification()
|
||||
}
|
||||
} receiveValue: { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
|
||||
guard (200..<300).contains(response.statusCode) else {
|
||||
self.restartAfterAuthVerification()
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let decoded = try JSONDecoder().decode(ApiResponse<GetMemberInfoResponse>.self, from: response.data)
|
||||
guard let data = decoded.data, decoded.success, data.isAuth else {
|
||||
self.restartAfterAuthVerification()
|
||||
return
|
||||
}
|
||||
|
||||
UserDefaults.set(data.can, forKey: .can)
|
||||
UserDefaults.set(data.point, forKey: .point)
|
||||
UserDefaults.set(data.isAuth, forKey: .auth)
|
||||
UserDefaults.set(data.role.rawValue, forKey: .role)
|
||||
UserDefaults.set(data.auditionNotice ?? false, forKey: .isAuditionNotification)
|
||||
UserDefaults.set(data.countryCode, forKey: .countryCode)
|
||||
UserDefaults.set(data.isAdultContentVisible, forKey: .isAdultContentVisible)
|
||||
UserDefaults.set(data.isAdultContentVisible ? data.contentType : ContentType.ALL.rawValue, forKey: .contentPreference)
|
||||
self.isAuth = data.isAuth
|
||||
|
||||
let onSuccess = self.pendingAuthSuccess
|
||||
self.clearPendingAuthVerification()
|
||||
onSuccess?()
|
||||
} catch {
|
||||
self.restartAfterAuthVerification()
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
private func clearPendingAuthVerification() {
|
||||
pendingAuthReceiptId = nil
|
||||
pendingAuthRequest = nil
|
||||
pendingAuthSuccess = nil
|
||||
isSubmittingAuth = false
|
||||
isShowAuthContentSettings = false
|
||||
}
|
||||
|
||||
private func restartAfterAuthVerification() {
|
||||
clearPendingAuthVerification()
|
||||
AppState.shared.isRestartApp = true
|
||||
AppState.shared.setAppStep(step: .splash)
|
||||
}
|
||||
|
||||
func getLatestNotice() {
|
||||
|
||||
Reference in New Issue
Block a user