diff --git a/BeforeGoing/Presentation/Common/Toast/ToastMessageType.swift b/BeforeGoing/Presentation/Common/Toast/ToastMessageType.swift index 77c691e..e03c520 100644 --- a/BeforeGoing/Presentation/Common/Toast/ToastMessageType.swift +++ b/BeforeGoing/Presentation/Common/Toast/ToastMessageType.swift @@ -12,6 +12,7 @@ enum ToastMessageType { case todayMissionLimit case missionLimit case duplicateMission + case duplicateScenario var message: String { switch self { @@ -21,6 +22,8 @@ enum ToastMessageType { return "* 미션은 20개까지만 설정할 수 있어요" case .duplicateMission: return "* 중복된 이름의 미션은 설정할 수 없어요" + case .duplicateScenario: + return "* 중복된 이름의 시나리오는 설정할 수 없어요" } } } diff --git a/BeforeGoing/Presentation/Feature/Scenario/ViewController/ManageScenarioViewController.swift b/BeforeGoing/Presentation/Feature/Scenario/ViewController/ManageScenarioViewController.swift index 7e902ec..405e0e7 100644 --- a/BeforeGoing/Presentation/Feature/Scenario/ViewController/ManageScenarioViewController.swift +++ b/BeforeGoing/Presentation/Feature/Scenario/ViewController/ManageScenarioViewController.swift @@ -14,6 +14,7 @@ final class ManageScenarioViewController: BaseViewController { private var didCellTap: Bool = false private var selectedIndex: Int? + private var scenarioNames: [String]? init(viewModel: ManageScenarioViewModel) { self.viewModel = viewModel @@ -68,12 +69,23 @@ extension ManageScenarioViewController { let viewController = ViewControllerFactory.shared.makeSettingScenarioViewController() viewController.navigationItem.hidesBackButton = true - viewController.configure(scenarioType: scenarioType, enterType: .addScenario) + viewController.configure( + scenarioType: scenarioType, + enterType: .addScenario, + scenarioNames: scenarioNames + ) self.navigationController?.pushViewController(viewController, animated: false) } } +extension ManageScenarioViewController { + + func configure(scenarioNames: [String]) { + self.scenarioNames = scenarioNames + } +} + extension ManageScenarioViewController: Backable { func back() { diff --git a/BeforeGoing/Presentation/Feature/Scenario/ViewController/MyScenarioViewController.swift b/BeforeGoing/Presentation/Feature/Scenario/ViewController/MyScenarioViewController.swift index 87c289e..13b5ac7 100644 --- a/BeforeGoing/Presentation/Feature/Scenario/ViewController/MyScenarioViewController.swift +++ b/BeforeGoing/Presentation/Feature/Scenario/ViewController/MyScenarioViewController.swift @@ -113,7 +113,9 @@ extension MyScenarioViewController: NetworkRequestable, NetworkRequestErrorHandl viewController.do { $0.navigationItem.hidesBackButton = true $0.hidesBottomBarWhenPushed = true + $0.configure(scenarioNames: getScenariosViewModel.getScenarioNames()) } + self.navigationController?.pushViewController(viewController, animated: false) } @@ -149,6 +151,7 @@ extension MyScenarioViewController: NetworkRequestable, NetworkRequestErrorHandl $0.hidesBottomBarWhenPushed = true $0.configure( scenarioID: scenario.scenarioID, + scenarioNames: getScenariosViewModel.getScenarioNames(), scenarioName: scenario.scenarioName, memo: scenario.memo, missions: scenario.basicMissions.map { (missionID: $0.missionId, content: $0.content) }, diff --git a/BeforeGoing/Presentation/Feature/Scenario/ViewController/SettingScenarioViewController.swift b/BeforeGoing/Presentation/Feature/Scenario/ViewController/SettingScenarioViewController.swift index 22ee64a..a9a15b1 100644 --- a/BeforeGoing/Presentation/Feature/Scenario/ViewController/SettingScenarioViewController.swift +++ b/BeforeGoing/Presentation/Feature/Scenario/ViewController/SettingScenarioViewController.swift @@ -13,7 +13,9 @@ final class SettingScenarioViewController: BaseViewController { private let missionLimit = 20 private var scenarioID: Int? + private var originalScenarioName: String? private var enterType: SettingScenarioEnterType? + private var scenarioNames: [String]? private var isNotificationActive: Bool? private var daysOfWeek: [Int]? private var startHour: Int? @@ -31,7 +33,7 @@ final class SettingScenarioViewController: BaseViewController { self.updateScenarioViewModel = updateScnearioViewModel super.init(nibName: nil, bundle: nil) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -119,9 +121,12 @@ extension SettingScenarioViewController { func configure( scenarioType: ScenarioType, - enterType: SettingScenarioEnterType + enterType: SettingScenarioEnterType, + scenarioNames: [String]? ) { self.enterType = enterType + self.scenarioNames = scenarioNames + addScenarioViewModel.setMissions(missions: scenarioType.basicMissions) rootView.settingMissionView.updateMissionCount(addScenarioViewModel.missionsCount) @@ -145,6 +150,7 @@ extension SettingScenarioViewController { func configure( scenarioID: Int, + scenarioNames: [String]?, scenarioName: String, memo: String, missions: [(missionID: Int, content: String)], @@ -156,13 +162,15 @@ extension SettingScenarioViewController { enterType: SettingScenarioEnterType ) { self.scenarioID = scenarioID + self.scenarioNames = scenarioNames + self.originalScenarioName = scenarioName self.isNotificationActive = isNotificationActive self.daysOfWeek = daysOfWeek self.startHour = startHour self.startMinute = startMinute self.notificationMethod = notificationMethod self.enterType = enterType - + addScenarioViewModel.setMissions(missions: missions) rootView.inputScenarioView.do { @@ -191,19 +199,6 @@ extension SettingScenarioViewController: ToastPresentable { bindTextField(view: rootView.inputMemoView) } - private func bindTextField(view: InputInformationView) { - guard let text = view.textField.text else { return } - - DispatchQueue.main.async { [weak self] in - guard self != nil else { return } - - text.isBlank ? view.hideDeleteButton() : view.revealDeleteButton() - let trimmedText = view.trimText(text) - view.updateTextCount(trimmedText.count) - } - checkNextButtonState() - } - @objc private func scenarioDeleteButtonDidTap() { rootView.inputScenarioView.deleteAllText() @@ -271,7 +266,24 @@ extension SettingScenarioViewController: ToastPresentable { addScenario(scenarioName: scenarioName, memo: memo) return } - updateScenario(scenarioName: scenarioName, memo: memo) + updateScenario( + origianlScenarioName: originalScenarioName, + scenarioName: scenarioName, + memo: memo + ) + } + + private func bindTextField(view: InputInformationView) { + guard let text = view.textField.text else { return } + + DispatchQueue.main.async { [weak self] in + guard self != nil else { return } + + text.isBlank ? view.hideDeleteButton() : view.revealDeleteButton() + let trimmedText = view.trimText(text) + view.updateTextCount(trimmedText.count) + } + checkNextButtonState() } private func checkNextButtonState() { @@ -285,9 +297,15 @@ extension SettingScenarioViewController: ToastPresentable { private func addScenario(scenarioName: String, memo: String) { let scenarioName = scenarioName.removeTrailingSpaces() let memo = memo.removeTrailingSpaces() - + Task { do { + guard let scenarioNames, + !scenarioNames.contains(scenarioName) else { + self.presentToastMessage(type: .duplicateScenario) + return + } + let _ = try await addScenarioViewModel.action( input: .nextButtonInSetScenarioDidTap( scenarioName: scenarioName, @@ -302,14 +320,26 @@ extension SettingScenarioViewController: ToastPresentable { } } - private func updateScenario(scenarioName: String, memo: String) { + private func updateScenario( + origianlScenarioName: String?, + scenarioName: String, + memo: String + ) { guard let scenarioID = scenarioID else { return } - + let scenarioName = scenarioName.removeTrailingSpaces() let memo = memo.removeTrailingSpaces() Task { do { + if isDuplicateNameForUpdate( + originalScenarioName: originalScenarioName, + newScenarioName: scenarioName + ) { + self.presentToastMessage(type: .duplicateScenario) + return + } + let _ = try await updateScenarioViewModel.action( input: .nextButtonInSetScenarioDidTap( scenarioID: scenarioID, @@ -338,6 +368,19 @@ extension SettingScenarioViewController: ToastPresentable { ) self.navigationController?.pushViewController(viewController, animated: false) } + + private func isDuplicateNameForUpdate( + originalScenarioName: String?, + newScenarioName: String + ) -> Bool { + guard let originalScenarioName, + let scenarioNames else { + return false + } + + let isDuplicate = originalScenarioName != newScenarioName && scenarioNames.contains(newScenarioName) + return isDuplicate + } } extension SettingScenarioViewController: UITableViewDelegate { diff --git a/BeforeGoing/Presentation/Feature/Scenario/ViewModel/GetScenariosViewModel.swift b/BeforeGoing/Presentation/Feature/Scenario/ViewModel/GetScenariosViewModel.swift index 8819b2d..eaf7395 100644 --- a/BeforeGoing/Presentation/Feature/Scenario/ViewModel/GetScenariosViewModel.swift +++ b/BeforeGoing/Presentation/Feature/Scenario/ViewModel/GetScenariosViewModel.swift @@ -111,6 +111,10 @@ extension GetScenariosViewModel { scenariosModel.scenarios[section].scenarioName } + func getScenarioNames() -> [String] { + scenariosModel.scenarios.map { $0.scenarioName } + } + func getNotificationInformation(section: Int) -> String? { scenariosModel.getNotificationInformation(section: section) }