Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by 이동현 on 8/23/25.
//

import Shared
import SnapKit
import UIKit

Expand All @@ -30,6 +31,7 @@ final class EmotionRegisterCompletionViewController: UIViewController {
static let marbleImageTopSpacing: CGFloat = 96.92
}

private let emotion: Emotion
private let backgroundImageView = UIImageView()
private let groundImageView = UIImageView()
private let speechImageView = UIImageView()
Expand All @@ -40,6 +42,7 @@ final class EmotionRegisterCompletionViewController: UIViewController {
private let pomoRightHandImageView = UIImageView()

init(emotion: Emotion) {
self.emotion = emotion
super.init(nibName: nil, bundle: nil)

marbleImageView.image = emotion.image
Expand All @@ -57,11 +60,14 @@ final class EmotionRegisterCompletionViewController: UIViewController {
super.viewDidLoad()
configureAttribute()
configureLayout()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// TODO: - 임시 네비 바 설정
configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: "임시뒤로가기"))
let filteredNavigations = navigationController?.viewControllers.filter { !($0 is EmotionRegistrationViewController) } ?? []
navigationController?.setViewControllers(filteredNavigations, animated: false)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
self?.goToRecommendedRoutineView()
}
}

func configureAttribute() {
Expand Down Expand Up @@ -136,4 +142,14 @@ final class EmotionRegisterCompletionViewController: UIViewController {
make.horizontalEdges.equalTo(speechImageView).inset(Layout.speechImageHorizontalSpacing)
}
}

private func goToRecommendedRoutineView() {
guard let resultRecommendedRoutineViewModel = DIContainer.shared.resolve(type: ResultRecommendedRoutineViewModel.self)
else{ fatalError("resultRecommendedRoutineViewModel 의존성이 등록되지 않았습니다.") }

resultRecommendedRoutineViewModel.configure(viewModelType: .emotion(emotion: emotion))
let resultRecommendedRoutineView = ResultRecommendedRoutineViewController(entryPoint: .emotion, viewModel: resultRecommendedRoutineViewModel)
resultRecommendedRoutineView.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(resultRecommendedRoutineView, animated: true)
}
}
3 changes: 3 additions & 0 deletions Projects/Presentation/Sources/MyPage/View/MypageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ final class MypageView: BaseViewController<MypageViewModel> {

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

navigationController?.setNavigationBarHidden(false, animated: animated)

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .white
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
routineLabel.textColor = BitnagilColor.gray60

headerStackView.axis = .horizontal
registerEmotionButton.delegate = self

levelButton.addAction(UIAction { _ in
self.showBottomSheet()
Expand Down Expand Up @@ -302,6 +303,18 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
}
}

// MARK: RegisterEmotionButtonViewDelegate
extension RecommendedRoutineViewController: RegisterEmotionButtonViewDelegate {
func registerEmotionButtonViewDidTapRegisterButton(_ sender: RegisterEmotionButtonView) {
guard let emotionRegisterViewModel = DIContainer.shared.resolve(type: EmotionRegisterViewModel.self)
else { fatalError("emotionRegisterViewModel 의존성이 등록되지 않았습니다.") }

let emotionRegistrationViewController = EmotionRegistrationViewController(viewModel: emotionRegisterViewModel)
emotionRegistrationViewController.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(emotionRegistrationViewController, animated: true)
}
}

// MARK: RoutineCategoryViewDelegate
extension RecommendedRoutineViewController: RoutineCategoryViewDelegate {
func routineCategoryView(_ sender: RoutineCategoryView, didSelectCategory category: RoutineCategoryType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ final class ResultRecommendedRoutineViewController: BaseViewController<ResultRec

var confirmButtonLabel: String {
switch self {
case .onboarding: "등록하기"
case .mypage: "확인"
case .onboarding, .mypage: "등록하기"
case .emotion: "맞춤 추천 루틴 보러 가기"
}
}
Expand All @@ -54,9 +53,9 @@ final class ResultRecommendedRoutineViewController: BaseViewController<ResultRec

var isRoutineButtonEnabled: Bool {
switch self {
case .onboarding, .emotion:
case .onboarding, .mypage:
true
case .mypage:
case .emotion:
false
}
}
Expand Down Expand Up @@ -132,7 +131,7 @@ final class ResultRecommendedRoutineViewController: BaseViewController<ResultRec
case .onboarding, .mypage:
configureCustomNavigationBar(navigationBarStyle: .withProgressBar(step: OnboardingType.allCases.count + 1))
case .emotion:
configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: ""))
configureCustomNavigationBar(navigationBarStyle: .withTitle(title: ""))
}

let mainLabelText = entryPoint.mainLabelText
Expand Down Expand Up @@ -216,9 +215,9 @@ final class ResultRecommendedRoutineViewController: BaseViewController<ResultRec
case .onboarding:
confirmButton = PrimaryButton(buttonState: .disabled, buttonTitle: entryPoint.confirmButtonLabel)
case .mypage:
confirmButton = PrimaryButton(buttonState: .default, buttonTitle: entryPoint.confirmButtonLabel)
case .emotion:
confirmButton = PrimaryButton(buttonState: .disabled, buttonTitle: entryPoint.confirmButtonLabel)
case .emotion:
confirmButton = PrimaryButton(buttonState: .default, buttonTitle: entryPoint.confirmButtonLabel)
}
}

Expand Down Expand Up @@ -312,20 +311,27 @@ final class ResultRecommendedRoutineViewController: BaseViewController<ResultRec
}

private func configureSkipButton() {
skipButton.addAction(UIAction { [weak self] _ in
guard let self else { return }
switch entryPoint {
case .emotion:
if let navigationController = self.navigationController {
let viewControllers = navigationController.viewControllers
if viewControllers.count >= 3 {
navigationController.popToViewController(viewControllers[viewControllers.count - 3], animated: false)
skipButton.addAction(
UIAction { [weak self] _ in
guard let self else { return }
switch entryPoint {
case .emotion:
if
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first(where: { $0.isKeyWindow }),
let tabBarView = window.rootViewController as? TabBarView {
self.navigationController?.popToRootViewController(animated: false)
tabBarView.selectedIndex = 0
}

case .onboarding:
goToNextView()

case .mypage:
break
}
case .onboarding, .mypage:
goToNextView()
}
}, for: .touchUpInside)
},
for: .touchUpInside)
}

private func goToNextView() {
Expand All @@ -337,6 +343,9 @@ final class ResultRecommendedRoutineViewController: BaseViewController<ResultRec
}

case .mypage:
viewModel.action(input: .fetchSelectedRoutineId)

case .emotion:
if
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first(where: { $0.isKeyWindow }),
Expand All @@ -345,16 +354,16 @@ final class ResultRecommendedRoutineViewController: BaseViewController<ResultRec
tabBarView.selectedIndex = 1
}
viewModel.action(input: .showRecommendedRoutineToastMessageView)

case .emotion:
viewModel.action(input: .fetchSelectedRoutineId)
}
}

private func goToRoutineCreationView(routineId: Int) {
guard let routineCreationViewModel = DIContainer.shared.resolve(type: RoutineCreationViewModel.self)
else { fatalError("routineCreationViewModel 의존성이 등록되지 않았습니다.") }
let routineCreationView = RoutineCreationViewController(viewModel: routineCreationViewModel, recommendRoutineId: routineId)
let routineCreationView = RoutineCreationViewController(
viewModel: routineCreationViewModel,
recommendRoutineId: routineId,
isFromMypage: true)
routineCreationView.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(routineCreationView, animated: true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ final class ResultRecommendedRoutineViewModel: ViewModel {
if selectedRoutines.contains(routine) {
selectedRoutines.remove(routine)
} else {
if case .emotion = viewModelType {
if case .mypage = viewModelType {
selectedRoutines.removeAll()
}
selectedRoutines.insert(routine)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ final class RoutineCreationViewController: BaseViewController<RoutineCreationVie
private let registerButton = UIButton()
private let navigationTitle: String
private let registerButtonTitle: String
private let isFromMypage: Bool
private var cancellables = Set<AnyCancellable>()

init(
Expand All @@ -75,6 +76,7 @@ final class RoutineCreationViewController: BaseViewController<RoutineCreationVie
) {
navigationTitle = updateInfo?.routineId == nil ? "루틴 등록" : "루틴 수정"
registerButtonTitle = updateInfo?.routineId == nil ? "등록하기" : "수정하기"
self.isFromMypage = false

super.init(viewModel: viewModel)

Expand All @@ -85,9 +87,14 @@ final class RoutineCreationViewController: BaseViewController<RoutineCreationVie
}
}

init(viewModel: RoutineCreationViewModel, recommendRoutineId: Int) {
init(
viewModel: RoutineCreationViewModel,
recommendRoutineId: Int,
isFromMypage: Bool = false
) {
navigationTitle = "루틴 등록"
registerButtonTitle = "등록하기"
self.isFromMypage = isFromMypage

super.init(viewModel: viewModel)

Expand Down Expand Up @@ -136,8 +143,20 @@ final class RoutineCreationViewController: BaseViewController<RoutineCreationVie
registerButton.titleLabel?.font = BitnagilFont.init(style: .body1, weight: .semiBold).font
registerButton.addAction(
UIAction { [weak self] _ in
self?.viewModel.action(input: .registerRoutine)
self?.navigationController?.popViewController(animated: true)
guard let self else { return }
self.viewModel.action(input: .registerRoutine)
if self.isFromMypage {
if
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first(where: { $0.isKeyWindow }),
let tabBarView = window.rootViewController as? TabBarView {
self.navigationController?.popToRootViewController(animated: false)
tabBarView.selectedIndex = 1
viewModel.action(input: .showRecommendedRoutineToastMessageView)
}
Comment on lines 145 to +156

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

self 누락으로 인한 컴파일 오류

UIAction 클로저 내부에서는 self를 명시해야 합니다. 현재 viewModel.action(input: .showRecommendedRoutineToastMessageView) 호출은 컴파일되지 않습니다. 아래와 같이 self.viewModel로 수정해 주세요.

-                        viewModel.action(input: .showRecommendedRoutineToastMessageView)
+                        self.viewModel.action(input: .showRecommendedRoutineToastMessageView)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
UIAction { [weak self] _ in
self?.viewModel.action(input: .registerRoutine)
self?.navigationController?.popViewController(animated: true)
guard let self else { return }
self.viewModel.action(input: .registerRoutine)
if self.isFromMypage {
if
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first(where: { $0.isKeyWindow }),
let tabBarView = window.rootViewController as? TabBarView {
self.navigationController?.popToRootViewController(animated: false)
tabBarView.selectedIndex = 1
viewModel.action(input: .showRecommendedRoutineToastMessageView)
}
UIAction { [weak self] _ in
guard let self else { return }
self.viewModel.action(input: .registerRoutine)
if self.isFromMypage {
if
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first(where: { $0.isKeyWindow }),
let tabBarView = window.rootViewController as? TabBarView {
self.navigationController?.popToRootViewController(animated: false)
tabBarView.selectedIndex = 1
self.viewModel.action(input: .showRecommendedRoutineToastMessageView)
}
}
}
🤖 Prompt for AI Agents
Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift
lines 145-156: inside the UIAction closure the call viewModel.action(input:
.showRecommendedRoutineToastMessageView) is missing the explicit self reference
causing a compile error; change that call to self.viewModel.action(input:
.showRecommendedRoutineToastMessageView) (keeping the existing [weak self]
capture and the guard let self else { return } intact).

} else {
self.navigationController?.popViewController(animated: true)
}
},
for: .touchUpInside)
bindCreationCardViews()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ final class RoutineCreationViewModel: ViewModel {
case configureEndDate(date: Date)
case configureExecution(type: ExecutionTime)
case registerRoutine
case showRecommendedRoutineToastMessageView
}

struct Output {
Expand Down Expand Up @@ -108,6 +109,8 @@ final class RoutineCreationViewModel: ViewModel {
periodStartSubject.send(date)
case .configureEndDate(let date):
periodEndSubject.send(date)
case .showRecommendedRoutineToastMessageView:
showRecommendedRoutineToastMessageView()
}

updateIsRoutineValid()
Expand Down Expand Up @@ -279,4 +282,13 @@ final class RoutineCreationViewModel: ViewModel {
}
}
}

private func showRecommendedRoutineToastMessageView() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
NotificationCenter.default.post(
name: .showRecommendedRoutineToast,
object: nil,
userInfo: nil)
}
}
}