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
14 changes: 9 additions & 5 deletions DevLog/Presentation/ViewModel/TodoDetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ import Foundation
final class TodoDetailViewModel: Store {
struct State {
var todo: Todo?
var isLoading = false
var showEditor = false
var showAlert = false
var alertTitle = ""
var alertMessage = ""
var isLoading: Bool = false
var showAlert: Bool = false
var showEditor: Bool = false
var showInfo: Bool = false
var alertTitle: String = ""
var alertMessage: String = ""
}

enum Action {
case onAppear
case setAlert(Bool)
case setShowEditor(Bool)
case setShowInfo(Bool)
case setTodo(Todo)
case setLoading(Bool)
case upsertTodo(Todo)
Expand Down Expand Up @@ -57,6 +59,8 @@ final class TodoDetailViewModel: Store {
setAlert(&state, isPresented: isPresented)
case .setShowEditor(let isPresented):
state.showEditor = isPresented
case .setShowInfo(let presented):
state.showInfo = presented
case .setTodo(let todo):
state.todo = todo
case .setLoading(let value):
Expand Down
59 changes: 0 additions & 59 deletions DevLog/UI/Common/Componeent/SheetToolbar.swift

This file was deleted.

62 changes: 62 additions & 0 deletions DevLog/UI/Common/Componeent/ToolbarButton+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// ToolbarButton+.swift
// DevLog
//
// Created by 최윤진 on 3/1/26.
//

import SwiftUI

struct ToolbarLeadingButton: ToolbarContent {
var action: (() -> Void)?

var body: some ToolbarContent {
ToolbarItem(placement: .topBarLeading) {
if #available(iOS 26.0, *) {
Button(role: .cancel) {
action?()
}
Comment on lines +15 to +18

Choose a reason for hiding this comment

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

critical

#available 확인에서 iOS 버전이 26.0으로 잘못 지정되어 있습니다. Button(role:)은 iOS 15.0부터 사용 가능하므로, iOS 15.0으로 수정해야 합니다. 현재 코드는 의도한 대로 동작하지 않으며, else 블록의 레거시 코드만 실행됩니다.

Suggested change
if #available(iOS 26.0, *) {
Button(role: .cancel) {
action?()
}
if #available(iOS 15.0, *) {
Button(role: .cancel) {
action?()
}

} else {
Button {
action?()
} label: {
Text("취소")
}
}
}
}
}

struct ToolbarTrailingButton: ToolbarContent {
var action: (() -> Void)?
private var isDisabled: Bool = false

init(action: (() -> Void)? = nil) {
self.action = action
}

func disabled(_ isDisabled: Bool) -> ToolbarTrailingButton {
var copy = self
copy.isDisabled = isDisabled
return copy
}

var body: some ToolbarContent {
ToolbarItem(placement: .topBarTrailing) {
if #available(iOS 26.0, *) {
Button(role: .confirm) {
action?()
}
.disabled(isDisabled)
Comment on lines +46 to +50

Choose a reason for hiding this comment

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

critical

여기에서도 #available 확인의 iOS 버전이 26.0으로 잘못 지정되어 있습니다. Button(role:)은 iOS 15.0부터 사용 가능하므로 iOS 15.0으로 수정해야 합니다.

Suggested change
if #available(iOS 26.0, *) {
Button(role: .confirm) {
action?()
}
.disabled(isDisabled)
if #available(iOS 15.0, *) {
Button(role: .confirm) {
action?()
}
.disabled(isDisabled)

} else {
Button {
action?()
} label: {
Text("확인")
.bold()
}
.disabled(isDisabled)
}
}
}
}
119 changes: 85 additions & 34 deletions DevLog/UI/Home/TodoDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,8 @@ struct TodoDetailView: View {
ScrollView {
LazyVStack(alignment: .leading, spacing: 10) {
Text(todo.title)
.font(.title3)
.font(.title3.bold())
.padding(.horizontal)
if let date = todo.dueDate {
Divider()
HStack {
Text("마감일")
Spacer()
Text(date.formatted(date: .long, time: .omitted))
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
)
}
.padding(.horizontal)
}
Divider()
HStack {
Text("태그")
Divider()
ScrollView(.horizontal) {
HStack {
ForEach(todo.tags, id: \.self) { tag in
Tag(tag, isEditing: false)
}
}
}
.scrollIndicators(.never)
}
.padding(.horizontal)
Divider()
Markdown(todo.content)
.padding(.horizontal)
Expand All @@ -57,6 +30,12 @@ struct TodoDetailView: View {
}
}
.onAppear { viewModel.send(.onAppear) }
.sheet(isPresented: Binding(
get: { viewModel.state.showInfo },
set: { viewModel.send(.setShowInfo($0)) }
)) {
sheetContent
}
.fullScreenCover(isPresented: Binding(
get: { viewModel.state.showEditor },
set: { viewModel.send(.setShowEditor($0)) }
Expand All @@ -68,12 +47,84 @@ struct TodoDetailView: View {
)
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
viewModel.send(.setShowEditor(true))
} label: {
Text("수정")
.toolbar { toolbarContent }
}

@ToolbarContentBuilder
private var toolbarContent: some ToolbarContent {
ToolbarItem(placement: .topBarTrailing) {
Button {
viewModel.send(.setShowInfo(true))
} label: {
Image(systemName: "info.circle")
}
}
if #available(iOS 26.0, *) {
ToolbarSpacer(.fixed, placement: .topBarTrailing)
}
Comment on lines +62 to +64

Choose a reason for hiding this comment

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

critical

#available 확인에서 iOS 버전이 26.0으로 잘못 지정되어 있습니다. ToolbarSpacer는 iOS 16.0부터 사용 가능하므로, iOS 16.0으로 수정해야 합니다. 이 코드는 현재 어떤 iOS 버전에서도 실행되지 않습니다.

        if #available(iOS 16.0, *) {
            ToolbarSpacer(.fixed, placement: .topBarTrailing)
        }

ToolbarItem(placement: .topBarTrailing) {
Button {
viewModel.send(.setShowEditor(true))
} label: {
Text("수정")
}
}
}

private var sheetContent: some View {
NavigationStack {
ScrollView {
LazyVStack(spacing: 32) {
VStack {
HStack {
Text("마감일")
.font(.subheadline)
.foregroundStyle(.secondary)
Spacer()
}
HStack(spacing: 8) {
Image(systemName: "calendar")
.foregroundStyle(.secondary)
Text(
viewModel.state.todo?.dueDate?
.formatted(date: .abbreviated, time: .omitted)
?? "마감일 없음"
)
.foregroundStyle(
viewModel.state.todo?.dueDate == nil ? .secondary : .primary
)
Spacer()
}
.padding(.vertical, 10)
.padding(.horizontal, 12)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.tertiarySystemFill))
)
Divider()
}
VStack {
HStack {
Text("태그")
.font(.subheadline)
.foregroundStyle(.secondary)
Spacer()
}
Divider()
if let tags = viewModel.state.todo?.tags, !tags.isEmpty {
TagLayout {
ForEach(tags, id: \.self) { tag in
Tag(tag, isEditing: false)
}
}
}
}
}
.padding(.horizontal)
}
.toolbar {
ToolbarLeadingButton {
viewModel.send(.setShowInfo(false))
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions DevLog/UI/Home/TodoEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ struct TodoEditorView: View {
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.background, for: .navigationBar)
.toolbar {
SheetToolbar(
onCancel: { dismiss() },
onConfirm: {
onSubmit?(viewModel.upsertTodo())
dismiss()
},
isConfirmEnabled: viewModel.state.isValidToSave
)
ToolbarLeadingButton {
dismiss()
}
ToolbarTrailingButton {
onSubmit?(viewModel.upsertTodo())
dismiss()
}
.disabled(!viewModel.state.isValidToSave)
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions DevLog/UI/Setting/PushNotificationSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ struct PushNotificationSettingsView: View {
.onAppear { UIDatePicker.appearance().minuteInterval = 5 }
.onDisappear { UIDatePicker.appearance().minuteInterval = 1 /* 기본값으로 복원 */ }
.toolbar {
SheetToolbar(
onCancel: { viewModel.send(.rollbackUpdate) },
onConfirm: { viewModel.send(.confirmUpdate) }
)
ToolbarLeadingButton {
viewModel.send(.rollbackUpdate)
}
ToolbarTrailingButton {
viewModel.send(.confirmUpdate)
}
}
.background(
GeometryReader { geometry in
Expand Down