Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

로그인 - 온보딩 쪽 디자인 & 기능 완성 #91

Merged
merged 32 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3f522ff
refactor #87: 로그인 뷰 디자인 변경
enebin Aug 31, 2023
54d9dae
refactor #88: 개인정보 등록 뷰 디자인 변경
enebin Aug 31, 2023
2ce9cb2
refactor #87: 루트뷰 로직 개선
enebin Sep 1, 2023
873edaf
refactor #87: 루트뷰 로직 및 푸시토큰 등록 개선
enebin Sep 2, 2023
2a62396
refactor #87: 마이페이지 APi 호출 로직 개선
enebin Sep 2, 2023
8f2488f
refactor #87: 루트뷰 애니메이션 개선
enebin Sep 2, 2023
63c0c27
refactor #87: 햅틱 개선
enebin Sep 2, 2023
05dad48
fix #88: 닉네임 로직 버그 수정
enebin Sep 2, 2023
7f4977e
refactor #87: 약관 수정
enebin Sep 2, 2023
f499971
refactor #87: 카카오 로그인 세팅
enebin Sep 2, 2023
b3b93be
refactor #87: xcconfig 세팅
enebin Sep 2, 2023
e983ace
feature #89: 마이페이지에 포토/설정 버튼 추가
enebin Sep 2, 2023
a5a6e54
feature #89: 마이페이지에 설정 네비게이션 로직 추가
enebin Sep 2, 2023
1ad62f2
feature #89: 네비게이션 바 커스텀
enebin Sep 2, 2023
3ab0e46
fix #89: 홈 뷰 애니메이션 버그 수정
enebin Sep 3, 2023
3771f8a
fix #89: 웹뷰 헤더 토큰 추가
enebin Sep 3, 2023
0706617
fix #89: 스택 오버플로우 버그 수정
enebin Sep 3, 2023
07be2dd
feature #89: 루트뷰에서 로그아웃 액션 수신
enebin Sep 4, 2023
fa42493
refactor #89: 루트뷰를 스위치스토어로 변경
enebin Sep 4, 2023
c80e875
refactor #89: 백그라운드 블러 로직 변경
enebin Sep 4, 2023
e875ec1
Merge branch 'refactor/루트뷰-통한의-리팩토링' into feature/setting-view(#89)
enebin Sep 4, 2023
9197ccf
refactor #89: 파일 이름 변경
enebin Sep 4, 2023
9563edd
refactor #89: 액션 이름 변경
enebin Sep 4, 2023
88ddca0
feature #89: 푸시알림 관리를 위한 매니저 클래스 추가
enebin Sep 4, 2023
d0ae5d6
feature #89: 푸시알림 등록 로직 수정
enebin Sep 4, 2023
c5beda8
refactor #89: 설정 진입 네비게이션 로직 개선
enebin Sep 4, 2023
f133d2b
refactor #89: 코드 개선
enebin Sep 5, 2023
ea19516
refactor #89: 웹 뷰 로딩속도 개선
enebin Sep 5, 2023
6f15114
refactor #89: 홈에서 테스트 진입 UX 개선
enebin Sep 6, 2023
ae04e23
feature #89: 권한 관련 에러처리 및 `alert` 추가
enebin Sep 6, 2023
f750031
chore #89: 코드린트
enebin Sep 6, 2023
6662b61
Merge pull request #92 from Nexters/feature/setting-view(#89)
enebin Sep 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor #89: 루트뷰를 스위치스토어로 변경
- 메모리 관리
- 로직 및 코드 단순화
  • Loading branch information
enebin committed Sep 4, 2023
commit fa4249333b7dd187a2390ef5af4b55419b984b09
44 changes: 44 additions & 0 deletions Projects/Features/Sources/Root/RootFeature+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,47 @@ extension RootFeature.UserNotificationCenterDelegateManager: MessagingDelegate {
tokenSemaphore.signal()
}
}

extension SwitchingRootFeature {
final class UserNotificationCenterDelegateManager: NSObject {
private var fcmToken: String?
private let tokenSemaphore = DispatchSemaphore(value: 0)

func waitForToken() async -> String? {
startRegister()

return await withCheckedContinuation { continuation in
// If the token has already been received before this method was called
if let token = self.fcmToken {
continuation.resume(returning: token)
return
}

// Wait for the token to be received
DispatchQueue.global().async {
_ = self.tokenSemaphore.wait(timeout: .now() + 10)
if let token = self.fcmToken {
continuation.resume(returning: token)
} else {
continuation.resume(returning: nil)
}
}
}
}

private func startRegister() {
Messaging.messaging().delegate = self
}
}
}

extension SwitchingRootFeature.UserNotificationCenterDelegateManager: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
guard let token = fcmToken else {
return
}

self.fcmToken = token
tokenSemaphore.signal()
}
}
10 changes: 0 additions & 10 deletions Projects/Features/Sources/Root/RootFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,3 @@ public struct RootFeature: Reducer {
}
}
}

private extension RootFeature {
func needsRegistration(forNickname nickname: String?) -> Bool {
if nickname == nil {
return true
} else {
return false
}
}
}
165 changes: 165 additions & 0 deletions Projects/Features/Sources/Root/SwitchingRootFeature.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//
// SwitchingRootFeature.swift
// Features
//
// Created by 이영빈 on 2023/09/04.
// Copyright © 2023 team.humanwave. All rights reserved.
//

import Foundation
import Domain
import Network
import ComposableArchitecture

public struct SwitchingRootFeature: Reducer {
@Dependency(\.userStorage) private var userStorage
@Dependency(\.keymeAPIManager) private var network

public init() {}

public enum State: Equatable {
case notDetermined
case needSignIn(SignInFeature.State)
case needRegistration(RegistrationFeature.State)
case needOnboarding(OnboardingFeature.State)
case canUseApp(MainPageFeature.State)
}

public enum Action {
public enum View {
case checkUserStatus
}
case view(View)

case login(SignInFeature.Action)
case registration(RegistrationFeature.Action)
case onboarding(OnboardingFeature.Action)
case mainPage(MainPageFeature.Action)

case updateState(State)
case updateMemberInformation(withMemberData: MemberUpdateDTO.MemberData?)
}

public var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .view(.checkUserStatus):
let accessToken = userStorage.accessToken
if accessToken == nil { // 로그 아웃 상태
return .send(.updateState(.needSignIn(.loggedOut)))
} else { // 로그인 상태
network.registerAuthorizationToken(accessToken)
return .send(.updateMemberInformation(withMemberData: nil))
}

case .updateMemberInformation(let receviedMemberData):
return .run(priority: .userInitiated) { send in
let memberInformation: MemberUpdateDTO.MemberData
if let receviedMemberData {
memberInformation = receviedMemberData
} else {
memberInformation = try await network.request(
.member(.fetch),
object: MemberUpdateDTO.self
).data
}

userStorage.userId = memberInformation.id
userStorage.nickname = memberInformation.nickname
userStorage.friendCode = memberInformation.friendCode

if let profileImageURL = URL(string: memberInformation.profileImage) {
userStorage.profileImageURL = profileImageURL
}

if let profileThumbnailURL = URL(string: memberInformation.profileImage) {
userStorage.profileThumbnailURL = profileThumbnailURL
}

if let userId = memberInformation.id, let nickname = memberInformation.nickname {
if memberInformation.isOnboardingClear != true {
await send(.updateState(.needOnboarding(OnboardingFeature.State())))
} else {
await send(.updateState(
.canUseApp(MainPageFeature.State(userId: userId, nickname: nickname))))
}
} else {
await send(.updateState(.needRegistration(RegistrationFeature.State())))
}

Task.detached(priority: .low) {
let notificationDelegate = UserNotificationCenterDelegateManager()

guard let token = await notificationDelegate.waitForToken() else {
print("푸시토큰 등록 중 에러 발생")
return
}

_ = try await network.request(.registerPushToken(.register(token)))
}
}

case .updateState(let receivedState):
state = receivedState
return .none

// MARK: - Presentation actions
case .login(.signInWithAppleResponse(let response)):
switch response {
case .success(let body):
let token = body.data.token.accessToken
userStorage.accessToken = token
network.registerAuthorizationToken(token)

return .send(.updateMemberInformation(withMemberData: nil))

case .failure:
return .none
}

case .login(.signInWithKakaoResponse(let response)):
switch response {
case .success(let body):
let token = body.data.token.accessToken
userStorage.accessToken = token
network.registerAuthorizationToken(token)

return .send(.updateMemberInformation(withMemberData: nil))

case .failure:
return .none
}

case .registration(.finishRegisterResponse(let response)):
return .send(.updateMemberInformation(withMemberData: response.data))

case .onboarding(.testResult(.closeButtonDidTap)):
guard let userId = userStorage.userId, let nickname = userStorage.nickname else {
// 멤버 정보 수신 재시도
// TODO: and show alert. 사실 있을 수 없는 케이스긴 함
return .send(.updateMemberInformation(withMemberData: nil))
}

return .send(.updateState(.canUseApp(MainPageFeature.State(userId: userId, nickname: nickname))))

case .mainPage(.myPage(.settingViewAction(.logout))):
print("logout from rootview")
return .none
default:
return .none
}
}
.ifCaseLet(/State.needSignIn, action: /Action.login) {
SignInFeature()
}
.ifCaseLet(/State.needRegistration, action: /Action.registration) {
RegistrationFeature()
}
.ifCaseLet(/State.needOnboarding, action: /Action.onboarding) {
OnboardingFeature()
}
.ifCaseLet(/State.canUseApp, action: /Action.mainPage) {
MainPageFeature()
}
}
}
98 changes: 98 additions & 0 deletions Projects/Features/Sources/Root/SwitchingRootView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// SwitchingRootView.swift
// Features
//
// Created by 이영빈 on 2023/09/04.
// Copyright © 2023 team.humanwave. All rights reserved.
//

import SwiftUI
import Core
import ComposableArchitecture
import DSKit

public struct SwitchingRootView: View {
@State private var showBlurringBackground = false
private let store: StoreOf<SwitchingRootFeature>

public init() {
self.store = Store(initialState: SwitchingRootFeature.State.notDetermined) {
SwitchingRootFeature()
}

store.send(.view(.checkUserStatus))
}

public var body: some View {
ZStack {
// 애니메이션 부웅.. 부웅..
KeymeLottieView(asset: .background, loopMode: .autoReverse)
.ignoresSafeArea()

if showBlurringBackground {
BackgroundBlurringView(style: .dark)
.ignoresSafeArea()
.transition(.opacity.animation(Animation.customInteractiveSpring()))
}

SwitchStore(store) { state in
switch state {
case .needSignIn:
CaseLet(
/SwitchingRootFeature.State.needSignIn,
action: SwitchingRootFeature.Action.login
) { store in
SignInView(store: store)
}
.zIndex(ViewZIndex.siginIn.rawValue)
.transition(.opacity.animation(Animation.customInteractiveSpring()))
.onDisappear {
showBlurringBackground = true
}

case .needRegistration:
CaseLet(
/SwitchingRootFeature.State.needRegistration,
action: SwitchingRootFeature.Action.registration
) { store in
RegistrationView(store: store)
}
.zIndex(ViewZIndex.registration.rawValue)
.transition(.opacity.animation(Animation.customInteractiveSpring()))

case .needOnboarding:
CaseLet(
/SwitchingRootFeature.State.needOnboarding,
action: SwitchingRootFeature.Action.onboarding
) { store in
OnboardingView(store: store)
}
.zIndex(ViewZIndex.onboarding.rawValue)
.transition(.opacity.animation(Animation.customInteractiveSpring()))

case .canUseApp:
CaseLet(
/SwitchingRootFeature.State.canUseApp,
action: SwitchingRootFeature.Action.mainPage
) { store in
KeymeMainView(store: store)
}
.zIndex(ViewZIndex.main.rawValue)
.transition(.opacity.animation(Animation.customInteractiveSpring()))

default:
Text("")
}
}
}
}
}

private extension SwitchingRootView {
enum ViewZIndex: CGFloat {
case siginIn = 4
case registration = 3
case onboarding = 2
case main = 1
}
}
2 changes: 1 addition & 1 deletion Projects/Keyme/Sources/KeymeApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct KeymeApp: App {

var body: some Scene {
WindowGroup {
RootView()
SwitchingRootView()
.onOpenURL { url in
print(url)
if (AuthApi.isKakaoTalkLoginUrl(url)) {
Expand Down