From 291cc1d3e55faba539b1946a20d3029dcb1c77a5 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 26 Feb 2026 18:19:41 +0900 Subject: [PATCH 1/7] =?UTF-8?q?ui:=20=EC=8B=9C=ED=8A=B8=20=EC=83=81?= =?UTF-8?q?=EB=8B=A8=EC=97=90=20=ED=88=B4=EB=B0=94=EB=A1=9C=20=EC=B7=A8?= =?UTF-8?q?=EC=86=8C,=20=ED=99=95=EC=9D=B8=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PushNotificationSettingsView.swift | 77 +++++++++++++------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/DevLog/UI/Setting/PushNotificationSettingsView.swift b/DevLog/UI/Setting/PushNotificationSettingsView.swift index 9231a645..82822130 100644 --- a/DevLog/UI/Setting/PushNotificationSettingsView.swift +++ b/DevLog/UI/Setting/PushNotificationSettingsView.swift @@ -73,31 +73,64 @@ struct PushNotificationSettingsView: View { get: { viewModel.state.showTimePicker }, set: { _ in viewModel.send(.setShowTimePicker(false)) } )) { - DatePicker( - "", - selection: Binding( - get: { viewModel.state.pushNotificationTime }, - set: { viewModel.send(.setPushNotificationTime($0)) } - ), - displayedComponents: .hourAndMinute - ) - .datePickerStyle(.wheel) - .labelsHidden() - .presentationDragIndicator(.hidden) - .presentationDetents([.height(viewModel.state.sheetHeight)]) - .onAppear { - UIDatePicker.appearance().minuteInterval = 5 + NavigationStack { + DatePicker( + "", + selection: Binding( + get: { viewModel.state.pushNotificationTime }, + set: { viewModel.send(.setPushNotificationTime($0)) } + ), + displayedComponents: .hourAndMinute + ) + .datePickerStyle(.wheel) + .labelsHidden() + .presentationDragIndicator(.hidden) + .presentationDetents([.height(viewModel.state.sheetHeight)]) + .onAppear { UIDatePicker.appearance().minuteInterval = 5 } + .onDisappear { UIDatePicker.appearance().minuteInterval = 1 /* 기본값으로 복원 */ } + .toolbar { toolbar } + .background( + GeometryReader { geometry in + Color.clear.onAppear { + viewModel.send(.setSheetHeight(geometry.size.height)) + } + } + ) } - .onDisappear { - UIDatePicker.appearance().minuteInterval = 1 // 기본값으로 복원 + } + } + + @ToolbarContentBuilder + private var toolbar: some ToolbarContent { + if #available(iOS 26.0, *) { + ToolbarItem(placement: .topBarLeading) { + Button(role: .cancel) { + + } } - .background( - GeometryReader { geometry in - Color.clear.onAppear { - viewModel.send(.setSheetHeight(geometry.size.height)) - } + + ToolbarItem(placement: .topBarTrailing) { + Button(role: .confirm) { + } - ) + } + } else { + ToolbarItem(placement: .topBarLeading) { + Button { + + } label: { + Text("취소") + } + } + + ToolbarItem(placement: .topBarTrailing) { + Button { + + } label: { + Text("확인") + .bold() + } + } } } From 23090a2fc6cd82171898d8b6639eda201b41fd66 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 26 Feb 2026 20:14:22 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=EC=8B=9C=ED=8A=B8=20=EB=82=B4=20?= =?UTF-8?q?=EB=B3=80=EB=8F=99=20=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EC=97=90=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=95=98=EA=B3=A0,=20=EC=8B=9C=ED=8A=B8=EA=B0=80=20=EB=8B=AB?= =?UTF-8?q?=ED=9E=90=20=EB=95=8C=20=EC=A0=81=EC=A0=88=ED=95=9C=20=EA=B0=92?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B6=80=EB=AA=A8=20=EB=B7=B0=20=EA=B0=92?= =?UTF-8?q?=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PushNotificationSettingsViewModel.swift | 48 ++++++++++++------- .../PushNotificationSettingsView.swift | 19 ++++---- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift index a2942b88..946b4d35 100644 --- a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift +++ b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift @@ -10,7 +10,8 @@ import Foundation final class PushNotificationSettingsViewModel: Store { struct State { var pushNotificationEnable: Bool = false - var pushNotificationTime: Date = .init() + var viewPushNotificationTime: Date = .init() + var sheetPushNotificationTime: Date = .init() var showTimePicker: Bool = false var isLoading: Bool = false var sheetHeight: CGFloat = .pi @@ -19,10 +20,10 @@ final class PushNotificationSettingsViewModel: Store { var alertTitle: String = "" var alertMessage: String = "" var pushNotificationHour: Int { - Calendar.current.component(.hour, from: pushNotificationTime) + Calendar.current.component(.hour, from: viewPushNotificationTime) } var pushNotificationMinute: Int { - Calendar.current.component(.minute, from: pushNotificationTime) + Calendar.current.component(.minute, from: viewPushNotificationTime) } } @@ -32,9 +33,11 @@ final class PushNotificationSettingsViewModel: Store { case setLoading(Bool) case setPushNotificationEnable(Bool) case setPushNotificationHour(Int) - case setPushNotificationTime(Date) + case setPushNotificationTime(view: Date? = nil, sheet: Date? = nil) case setShowTimePicker(Bool) case setSheetHeight(CGFloat) + case confirmUpdate + case rollbackUpdate } enum SideEffect { @@ -42,8 +45,8 @@ final class PushNotificationSettingsViewModel: Store { case updatePushNotificationSettings } - private let calendar = Calendar.current @Published private(set) var state: State = .init() + private let calendar = Calendar.current private let fetchPushSettingsUseCase: FetchPushSettingsUseCase private let updatePushSettingsUseCase: UpdatePushSettingsUseCase @@ -57,6 +60,7 @@ final class PushNotificationSettingsViewModel: Store { func reduce(with action: Action) -> [SideEffect] { var state = self.state + var effects: [SideEffect] = [] switch action { case .onAppear: return [.fetchPushNotificationSettings] @@ -65,28 +69,37 @@ final class PushNotificationSettingsViewModel: Store { case .setLoading(let value): state.isLoading = value case .setPushNotificationEnable(let value): - self.state.pushNotificationEnable = value - return [.updatePushNotificationSettings] + state.pushNotificationEnable = value case .setPushNotificationHour(let value): // 시간만 변경 if let newDate = calendar.date( bySettingHour: value, minute: 0, second: 0, - of: state.pushNotificationTime + of: state.viewPushNotificationTime ) { - self.state.pushNotificationTime = newDate - return [.updatePushNotificationSettings] + state.viewPushNotificationTime = newDate + } + case .setPushNotificationTime(let view, let sheet): + if let value = view { + state.viewPushNotificationTime = value + } + if let value = sheet { + state.sheetPushNotificationTime = value } - case .setPushNotificationTime(let value): - self.state.pushNotificationTime = value - return [.updatePushNotificationSettings] case .setShowTimePicker(let value): state.showTimePicker = value case .setSheetHeight(let value): state.sheetHeight = value + case .confirmUpdate: + state.showTimePicker = false + state.viewPushNotificationTime = state.sheetPushNotificationTime + effects = [.updatePushNotificationSettings] + case .rollbackUpdate: + state.showTimePicker = false + state.sheetPushNotificationTime = state.viewPushNotificationTime } self.state = state - return [] + return effects } func run(_ effect: SideEffect) { @@ -101,7 +114,7 @@ final class PushNotificationSettingsViewModel: Store { if let hour = settings.scheduledTime.hour, let minute = settings.scheduledTime.minute, let date = calendar.date(bySettingHour: hour, minute: minute, second: 0, of: Date()) { - self.send(.setPushNotificationTime(date)) + self.send(.setPushNotificationTime(view: date, sheet: date)) } } catch { send(.setAlert(true)) @@ -112,7 +125,10 @@ final class PushNotificationSettingsViewModel: Store { do { defer { send(.setLoading(false)) } send(.setLoading(true)) - let dateComponents = calendar.dateComponents([.hour, .minute], from: state.pushNotificationTime) + let dateComponents = calendar.dateComponents( + [.hour, .minute], + from: state.sheetPushNotificationTime + ) let settings = PushNotificationSettings( isEnabled: state.pushNotificationEnable, scheduledTime: dateComponents diff --git a/DevLog/UI/Setting/PushNotificationSettingsView.swift b/DevLog/UI/Setting/PushNotificationSettingsView.swift index 82822130..3bb25c20 100644 --- a/DevLog/UI/Setting/PushNotificationSettingsView.swift +++ b/DevLog/UI/Setting/PushNotificationSettingsView.swift @@ -37,14 +37,15 @@ struct PushNotificationSettingsView: View { } .contentShape(Rectangle()) .onTapGesture { - viewModel.send(.setPushNotificationHour(hour)) + viewModel.send(.setPushNotificationTime(sheet: date)) + viewModel.send(.confirmUpdate) } } } HStack { Text("사용자 설정") Spacer() - Text(formattedTimeString(viewModel.state.pushNotificationTime)) + Text(formattedTimeString(viewModel.state.viewPushNotificationTime)) .foregroundStyle(.secondary) if viewModel.state.pushNotificationMinute != 0 { Image(systemName: "checkmark") @@ -71,14 +72,14 @@ struct PushNotificationSettingsView: View { } .sheet(isPresented: Binding( get: { viewModel.state.showTimePicker }, - set: { _ in viewModel.send(.setShowTimePicker(false)) } + set: { viewModel.send(.setShowTimePicker($0)) } )) { NavigationStack { DatePicker( "", selection: Binding( - get: { viewModel.state.pushNotificationTime }, - set: { viewModel.send(.setPushNotificationTime($0)) } + get: { viewModel.state.sheetPushNotificationTime }, + set: { viewModel.send(.setPushNotificationTime(sheet: $0)) } ), displayedComponents: .hourAndMinute ) @@ -105,19 +106,19 @@ struct PushNotificationSettingsView: View { if #available(iOS 26.0, *) { ToolbarItem(placement: .topBarLeading) { Button(role: .cancel) { - + viewModel.send(.rollbackUpdate) } } ToolbarItem(placement: .topBarTrailing) { Button(role: .confirm) { - + viewModel.send(.confirmUpdate) } } } else { ToolbarItem(placement: .topBarLeading) { Button { - + viewModel.send(.rollbackUpdate) } label: { Text("취소") } @@ -125,7 +126,7 @@ struct PushNotificationSettingsView: View { ToolbarItem(placement: .topBarTrailing) { Button { - + viewModel.send(.confirmUpdate) } label: { Text("확인") .bold() From ecc66fb529b5df96bb15bba66620a2f03332aaa1 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 26 Feb 2026 20:17:33 +0900 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=EC=8B=9C=ED=8A=B8=20=EC=99=B8?= =?UTF-8?q?=EB=B6=80=EB=A5=BC=20=ED=83=AD=20=ED=95=B4=EC=84=9C=20=EB=82=B4?= =?UTF-8?q?=EB=A0=A4=EA=B0=94=EC=9D=84=20=EB=95=8C=20=EC=9B=90=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/PushNotificationSettingsViewModel.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift index 946b4d35..fcd21f6f 100644 --- a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift +++ b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift @@ -88,6 +88,9 @@ final class PushNotificationSettingsViewModel: Store { } case .setShowTimePicker(let value): state.showTimePicker = value + if !value { + state.sheetPushNotificationTime = state.viewPushNotificationTime + } case .setSheetHeight(let value): state.sheetHeight = value case .confirmUpdate: From 6947bc62bbdfaf025fda8e1008074cee83c323aa Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 26 Feb 2026 20:30:33 +0900 Subject: [PATCH 4/7] =?UTF-8?q?fix:=20=EC=84=9C=EB=B2=84=EC=97=90=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=ED=98=84=EC=83=81=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/PushNotificationSettingsViewModel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift index fcd21f6f..58339a2f 100644 --- a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift +++ b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift @@ -63,13 +63,14 @@ final class PushNotificationSettingsViewModel: Store { var effects: [SideEffect] = [] switch action { case .onAppear: - return [.fetchPushNotificationSettings] + effects = [.fetchPushNotificationSettings] case .setAlert(let isPresented): setAlert(&state, isPresented: isPresented) case .setLoading(let value): state.isLoading = value case .setPushNotificationEnable(let value): state.pushNotificationEnable = value + effects = [.updatePushNotificationSettings] case .setPushNotificationHour(let value): // 시간만 변경 if let newDate = calendar.date( From 4c0e7d779e9ccd38e05fa7d1b70b2770614e52f9 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 26 Feb 2026 20:32:57 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=EA=B0=80=20=EC=8B=A4=ED=8C=A8=ED=96=88=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=84=9C=EB=B2=84=EA=B0=92=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=98=A4=EB=B2=84=EB=9D=BC=EC=9D=B4=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/PushNotificationSettingsViewModel.swift | 5 +++-- DevLog/UI/Setting/PushNotificationSettingsView.swift | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift index 58339a2f..72c81427 100644 --- a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift +++ b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift @@ -28,7 +28,7 @@ final class PushNotificationSettingsViewModel: Store { } enum Action { - case onAppear + case fetchSettings case setAlert(Bool) case setLoading(Bool) case setPushNotificationEnable(Bool) @@ -62,7 +62,7 @@ final class PushNotificationSettingsViewModel: Store { var state = self.state var effects: [SideEffect] = [] switch action { - case .onAppear: + case .fetchSettings: effects = [.fetchPushNotificationSettings] case .setAlert(let isPresented): setAlert(&state, isPresented: isPresented) @@ -140,6 +140,7 @@ final class PushNotificationSettingsViewModel: Store { try await updatePushSettingsUseCase.execute(settings) } catch { send(.setAlert(true)) + send(.fetchSettings) } } } diff --git a/DevLog/UI/Setting/PushNotificationSettingsView.swift b/DevLog/UI/Setting/PushNotificationSettingsView.swift index 3bb25c20..3776424f 100644 --- a/DevLog/UI/Setting/PushNotificationSettingsView.swift +++ b/DevLog/UI/Setting/PushNotificationSettingsView.swift @@ -68,7 +68,7 @@ struct PushNotificationSettingsView: View { } } .onAppear { - viewModel.send(.onAppear) + viewModel.send(.fetchSettings) } .sheet(isPresented: Binding( get: { viewModel.state.showTimePicker }, From 2106322ce35a249c8972c35d1bd8e05afabe420c Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 26 Feb 2026 20:34:15 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/PushNotificationSettingsViewModel.swift | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift index 72c81427..c8b08919 100644 --- a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift +++ b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift @@ -32,7 +32,6 @@ final class PushNotificationSettingsViewModel: Store { case setAlert(Bool) case setLoading(Bool) case setPushNotificationEnable(Bool) - case setPushNotificationHour(Int) case setPushNotificationTime(view: Date? = nil, sheet: Date? = nil) case setShowTimePicker(Bool) case setSheetHeight(CGFloat) @@ -71,15 +70,6 @@ final class PushNotificationSettingsViewModel: Store { case .setPushNotificationEnable(let value): state.pushNotificationEnable = value effects = [.updatePushNotificationSettings] - case .setPushNotificationHour(let value): - // 시간만 변경 - if let newDate = calendar.date( - bySettingHour: value, - minute: 0, second: 0, - of: state.viewPushNotificationTime - ) { - state.viewPushNotificationTime = newDate - } case .setPushNotificationTime(let view, let sheet): if let value = view { state.viewPushNotificationTime = value From 6a0c067f2892a89bf4c7f93b97a71e3114005aca Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 26 Feb 2026 20:37:10 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refactor:=20=EB=B7=B0=EA=B0=80=20=EB=B7=B0?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EC=95=A1=EC=85=98=EC=9D=84=20=EB=8D=9C=20?= =?UTF-8?q?=EC=95=8C=EB=8F=84=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/PushNotificationSettingsViewModel.swift | 5 +++++ DevLog/UI/Setting/PushNotificationSettingsView.swift | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift index c8b08919..99790865 100644 --- a/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift +++ b/DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift @@ -35,6 +35,7 @@ final class PushNotificationSettingsViewModel: Store { case setPushNotificationTime(view: Date? = nil, sheet: Date? = nil) case setShowTimePicker(Bool) case setSheetHeight(CGFloat) + case selectPresetTime(Date) case confirmUpdate case rollbackUpdate } @@ -84,6 +85,10 @@ final class PushNotificationSettingsViewModel: Store { } case .setSheetHeight(let value): state.sheetHeight = value + case .selectPresetTime(let date): + state.viewPushNotificationTime = date + state.sheetPushNotificationTime = date + effects = [.updatePushNotificationSettings] case .confirmUpdate: state.showTimePicker = false state.viewPushNotificationTime = state.sheetPushNotificationTime diff --git a/DevLog/UI/Setting/PushNotificationSettingsView.swift b/DevLog/UI/Setting/PushNotificationSettingsView.swift index 3776424f..181ed29c 100644 --- a/DevLog/UI/Setting/PushNotificationSettingsView.swift +++ b/DevLog/UI/Setting/PushNotificationSettingsView.swift @@ -37,8 +37,7 @@ struct PushNotificationSettingsView: View { } .contentShape(Rectangle()) .onTapGesture { - viewModel.send(.setPushNotificationTime(sheet: date)) - viewModel.send(.confirmUpdate) + viewModel.send(.selectPresetTime(date)) } } }