diff --git a/Atcha-iOS.xcodeproj/project.pbxproj b/Atcha-iOS.xcodeproj/project.pbxproj index e257cc61..862e7052 100644 --- a/Atcha-iOS.xcodeproj/project.pbxproj +++ b/Atcha-iOS.xcodeproj/project.pbxproj @@ -2516,7 +2516,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482; FRAMEWORK_SEARCH_PATHS = ( @@ -2563,7 +2563,7 @@ CODE_SIGN_ENTITLEMENTS = "Atcha-iOS/Atcha-iOS.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 23SCTLK482; EXCLUDED_ARCHS = ""; FRAMEWORK_SEARCH_PATHS = ( @@ -2611,7 +2611,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482; FRAMEWORK_SEARCH_PATHS = ( diff --git a/Atcha-iOS/Core/Network/Token/TokenInterceptor.swift b/Atcha-iOS/Core/Network/Token/TokenInterceptor.swift index 97361f57..4d8c69f0 100644 --- a/Atcha-iOS/Core/Network/Token/TokenInterceptor.swift +++ b/Atcha-iOS/Core/Network/Token/TokenInterceptor.swift @@ -77,8 +77,6 @@ final class TokenInterceptor: RequestInterceptor, @unchecked Sendable { return } - let actualHeaders = request.request?.allHTTPHeaderFields ?? [:] - guard let refreshToken = tokenStorage.refreshToken else { SessionController.shared.expireAndRouteToLogin() completion(.doNotRetry) @@ -109,24 +107,7 @@ final class TokenInterceptor: RequestInterceptor, @unchecked Sendable { waiters.forEach { $0(.doNotRetry) } return } - - let successBody = [ - "newAccessToken": p.accessToken, - "newRefreshToken": p.refreshToken - ] - - DiscordWebhookManager.shared.sendErrorLog( - baseURL: NetworkConstant.baseURL, - statusCode: 200, - method: "GET", - path: "/auth/reissue", - responseCode: "REISSUE_SUCCESS", - message: "토큰 재발급에 성공하여 새로운 토큰을 수신했습니다.", - requestHeaders: actualHeaders, - requestBody: successBody, // 여기서 받은 토큰 정보를 보냅니다. - requestParameters: nil - ) - + self.tokenStorage.accessToken = p.accessToken self.tokenStorage.refreshToken = p.refreshToken diff --git a/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/Contents.json b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/Contents.json new file mode 100644 index 00000000..e334e205 --- /dev/null +++ b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "setting_home_mark.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "setting_home_mark@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "setting_home_mark@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark.png b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark.png new file mode 100644 index 00000000..91b6e675 Binary files /dev/null and b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark.png differ diff --git a/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark@2x.png b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark@2x.png new file mode 100644 index 00000000..0d03202e Binary files /dev/null and b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark@2x.png differ diff --git a/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark@3x.png b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark@3x.png new file mode 100644 index 00000000..24f4a8f8 Binary files /dev/null and b/Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/setting_home_mark.imageset/setting_home_mark@3x.png differ diff --git a/Atcha-iOS/Presentation/Location/MainViewController.swift b/Atcha-iOS/Presentation/Location/MainViewController.swift index 87ceab62..c5a7d096 100644 --- a/Atcha-iOS/Presentation/Location/MainViewController.swift +++ b/Atcha-iOS/Presentation/Location/MainViewController.swift @@ -78,6 +78,7 @@ final class MainViewController: BaseViewController, if viewModel.isGuest { return false } return viewModel.isGuideActiveInSession } + private var guestTapCount = 0 // MARK: - Life Cycle @@ -152,6 +153,12 @@ final class MainViewController: BaseViewController, } } } + + self.guestTapCount = 0 + if viewModel.isGuest { + ballonView.isHidden = true + ballonView.alpha = 0 + } } override func viewDidAppear(_ animated: Bool) { @@ -229,6 +236,8 @@ final class MainViewController: BaseViewController, flagImageView.image = UIImage.settingLocationMark atchaImageView.isUserInteractionEnabled = true atchaImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleBallonTap))) + ballonView.isUserInteractionEnabled = true + ballonView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleBallonTap))) ballonView.isHidden = true ballonView.alpha = 0 @@ -309,6 +318,7 @@ extension MainViewController { observeArrival() observeScheduledArrivalTimeout() observeAlarmTimeout() + observeLoginDismissal() } private func bindPermissionAlert() { @@ -956,6 +966,13 @@ extension MainViewController { } @objc private func handleBallonTap() { + safeStartJump() + + if viewModel.isGuest { + handleGuestBallonTap() + return + } + // 알람 등록 후(departure 상태)일 때만 반응 guard viewModel.bottomType == .departure else { return } @@ -1014,6 +1031,31 @@ extension MainViewController { postAlarmTapIndex += 1 } } + + private func handleGuestBallonTap() { + if guestTapCount == 0 { + // 첫 번째 터치: "궁금하면 로그인 해봐요!" + guestTapCount = 1 + + ballonView.layer.removeAllAnimations() + ballonView.isHidden = false + ballonView.alpha = 1 + + ballonView.setupTitle(topMessage: nil, bottomMessage: "궁금하면 로그인 해봐요!") + ballonView.animateStaggered(secondaryDelay: 0, fade: 0.25) + + } else { + // 두 번째 터치: 로그인 시트 노출 + guestTapCount = 0 // 카운트 리셋 + + // [중요] 말풍선을 즉시 숨김 상태로 만들어야 시트가 내려간 뒤 다시 Persistent(???원) 메시지가 나타납니다. + ballonView.layer.removeAllAnimations() + ballonView.isHidden = true + ballonView.alpha = 0 + + presentLoginAlert() + } + } } // MARK: - Map Delegate & Gesture @@ -1233,6 +1275,10 @@ extension MainViewController { private func showOrUpdatePersistentBalloon(isFirstVisit: Bool, isServiceRegion: Bool?, fareStr: String?) { guard !isShowingToast else { return } + if viewModel.isGuest && guestTapCount == 1 { + return + } + // [수정] 우리가 정의한 로그인 기반 가이드 로직 적용 let showGuideLine = shouldShowMapGuide let topText: String? = showGuideLine ? "지도를 움직여 출발지를 설정해요" : nil @@ -1242,7 +1288,7 @@ extension MainViewController { } else { if viewModel.isGuest { // 비로그인: 가이드 없이 ???원만 노출 - ballonView.separationTitle(grayMessage: "여기서 막차 놓치면 택시비 ", whiteMessage: "약 ???원", showTopLine: false) + ballonView.separationTitle(grayMessage: "여기서 막차 놓치면 택시비 ", whiteMessage: "???원", showTopLine: false) } else { // 로그인 상태 if let fare = fareStr { @@ -1315,4 +1361,25 @@ extension MainViewController { balloonHideWorkItem = workItem DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: workItem) } + + private func observeLoginDismissal() { + NotificationCenter.default.publisher(for: NSNotification.Name("LoginSheetDismissed")) + .receive(on: RunLoop.main) + .sink { [weak self] _ in + guard let self = self else { return } + + // 로그인 시트가 내려갔으니 guestTapCount도 초기화해주는 게 자연스러워요 + self.guestTapCount = 0 + + // 현재 검색 모드라면 다시 고정 말풍선 노출 + if self.viewModel.bottomType == .search || self.viewModel.bottomType == nil { + self.showOrUpdatePersistentBalloon( + isFirstVisit: self.isFirstVisit, + isServiceRegion: self.latestIsServiceRegion, + fareStr: self.latestFareString + ) + } + } + .store(in: &cancellables) + } } diff --git a/Atcha-iOS/Presentation/Login/LoginViewController.swift b/Atcha-iOS/Presentation/Login/LoginViewController.swift index a4c63712..a1b4c52f 100644 --- a/Atcha-iOS/Presentation/Login/LoginViewController.swift +++ b/Atcha-iOS/Presentation/Login/LoginViewController.swift @@ -245,6 +245,7 @@ extension LoginViewController { self.containerView.transform = CGAffineTransform(translationX: 0, y: self.sheetHeight) }) { _ in self.dismiss(animated: false) { + NotificationCenter.default.post(name: NSNotification.Name("LoginSheetDismissed"), object: nil) self.viewModel.loginCancelled?() } } diff --git a/Atcha-iOS/Presentation/Setting/PushAlarmSheet/PushAlarmSheetViewController.swift b/Atcha-iOS/Presentation/Setting/PushAlarmSheet/PushAlarmSheetViewController.swift index 92d09f0e..77a50c59 100644 --- a/Atcha-iOS/Presentation/Setting/PushAlarmSheet/PushAlarmSheetViewController.swift +++ b/Atcha-iOS/Presentation/Setting/PushAlarmSheet/PushAlarmSheetViewController.swift @@ -203,13 +203,10 @@ final class PushAlarmSheetViewController: BaseViewController { // 서버 버전이 앱 버전보다 높다면 업데이트가 필요한 상황 if isVersion(appVersion, lessThan: serverVersion) { - showUpdatePopup(isEssential: true) + showUpdatePopup(isEssential: false) } else { viewModel.makeInitialFlow() } diff --git a/Atcha-iOS/Presentation/User/Home/HomeFindViewController.swift b/Atcha-iOS/Presentation/User/Home/HomeFindViewController.swift index 542f9f9c..ccc41a43 100644 --- a/Atcha-iOS/Presentation/User/Home/HomeFindViewController.swift +++ b/Atcha-iOS/Presentation/User/Home/HomeFindViewController.swift @@ -51,7 +51,7 @@ final class HomeFindViewController: BaseViewController, backButton) mapContainerView.delegate = self - flagImageView.image = UIImage.settingLocationMark + flagImageView.image = UIImage.settingHomeMark backButton.setImage(UIImage.chevronLeft, for: .normal) backButton.tintColor = .white backButton.backgroundColor = .black