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
23 changes: 18 additions & 5 deletions DevLog/Presentation/ViewModel/TodoEditorViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import OrderedCollections
@Observable
final class TodoEditorViewModel: Store {
private struct Draft: Equatable {
let isCompleted: Bool
let completedAt: Date?
let isPinned: Bool
let title: String
let content: String
Expand All @@ -19,6 +21,8 @@ final class TodoEditorViewModel: Store {
let kind: TodoKind

init(todo: Todo) {
self.isCompleted = todo.isCompleted
self.completedAt = todo.completedAt
self.isPinned = todo.isPinned
self.title = todo.title
self.content = todo.content
Expand All @@ -28,6 +32,8 @@ final class TodoEditorViewModel: Store {
}

init(state: State) {
self.isCompleted = state.isCompleted
self.completedAt = state.completedAt
self.isPinned = state.isPinned
self.title = state.title
self.content = state.content
Expand All @@ -38,6 +44,8 @@ final class TodoEditorViewModel: Store {
}

struct State: Equatable {
var isCompleted: Bool = false
var completedAt: Date?
var isPinned: Bool = false
var title: String = ""
var content: String = ""
Expand All @@ -61,6 +69,7 @@ final class TodoEditorViewModel: Store {
case addTag(String)
case removeTag(String)
case setContent(String)
case setCompleted(Bool)
case setDueDate(Date?)
case setKind(TodoKind)
case setPinned(Bool)
Expand All @@ -78,7 +87,6 @@ final class TodoEditorViewModel: Store {
private let isCompleted: Bool
private let isChecked: Bool
private let createdAt: Date?
private let completedAt: Date?
private let originalDraft: Draft?

var navigationTitle: String {
Expand All @@ -104,7 +112,6 @@ final class TodoEditorViewModel: Store {
self.isCompleted = false
self.isChecked = false
self.createdAt = nil
self.completedAt = nil
self.originalDraft = nil
state.kind = kind
}
Expand All @@ -115,8 +122,9 @@ final class TodoEditorViewModel: Store {
self.isCompleted = todo.isCompleted
self.isChecked = todo.isChecked
self.createdAt = todo.createdAt
self.completedAt = todo.completedAt
self.originalDraft = Draft(todo: todo)
state.isCompleted = todo.isCompleted
state.completedAt = todo.completedAt
state.isPinned = todo.isPinned
state.title = todo.title
state.content = todo.content
Expand Down Expand Up @@ -145,6 +153,11 @@ final class TodoEditorViewModel: Store {
} else {
state.dueDate = nil
}
case .setCompleted(let isCompleted):
if state.isCompleted != isCompleted {
state.completedAt = isCompleted ? Date() : nil
}
state.isCompleted = isCompleted
case .setKind(let todoKind):
state.kind = todoKind
case .setPinned(let isPinned):
Expand Down Expand Up @@ -183,13 +196,13 @@ extension TodoEditorViewModel {
return Todo(
id: self.id,
isPinned: state.isPinned,
isCompleted: self.isCompleted,
isCompleted: state.isCompleted,
isChecked: self.isChecked,
title: state.title,
content: state.content,
createdAt: self.createdAt ?? date,
updatedAt: date,
completedAt: self.completedAt,
completedAt: state.completedAt,
dueDate: state.dueDate,
tags: state.tags.map { $0 },
kind: state.kind
Expand Down
102 changes: 96 additions & 6 deletions DevLog/UI/Home/TodoDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,104 @@ struct TodoDetailView: View {
@ViewBuilder
private var sheetContent: some View {
if let todo = viewModel.state.todo {
TodoInfoSheetView(
createdAt: todo.createdAt,
completedAt: todo.completedAt,
dueDate: todo.dueDate,
tags: todo.tags
) {
TodoDetailInfoSheetView(todo: todo) {
viewModel.send(.setShowInfo(false))
}
}
}
}

private struct TodoDetailInfoSheetView: View {
let todo: Todo
let onClose: () -> Void
private let calendar = Calendar.current

var body: some View {
NavigationStack {
List {
Section("옵션") {
HStack {
Text("카테고리")
Spacer()
Text(todo.kind.localizedName)
.foregroundStyle(.secondary)
}

statusRow(
title: "완료",
systemImage: todo.isCompleted ? "checkmark.circle.fill" : "circle",
color: todo.isCompleted ? .green : .secondary
)

statusRow(
title: "중요 표시",
systemImage: todo.isPinned ? "star.fill" : "star",
color: todo.isPinned ? .orange : .secondary
)

HStack {
Text("마감일")

Spacer()

if let dueDate = todo.dueDate {
Tag(dueDateText(for: dueDate), isEditing: false)
.padding(.vertical, -4)
} else {
Text("없음")
.foregroundStyle(.secondary)
}
}
}

Section("태그") {
if todo.tags.isEmpty {
Text("태그 없음")
.foregroundStyle(.secondary)
.padding(.vertical, 4)
} else {
TagList(todo.tags)
}
}
}
.navigationTitle("세부 정보")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarLeadingButton {
onClose()
}
}
}
}

@ViewBuilder
private func statusRow(
title: String,
systemImage: String,
color: Color
) -> some View {
HStack {
Text(title)

Spacer()

Image(systemName: systemImage)
.foregroundStyle(color)
}
}

private func dueDateText(for dueDate: Date) -> String {
let currentYear = calendar.component(.year, from: Date())
let dueDateYear = calendar.component(.year, from: dueDate)

if currentYear == dueDateYear {
return dueDate.formatted(
.dateTime.month(.defaultDigits).day(.defaultDigits)
)
}

return dueDate.formatted(
.dateTime.year(.twoDigits).month(.defaultDigits).day(.defaultDigits)
)
}
}
11 changes: 9 additions & 2 deletions DevLog/UI/Home/TodoEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ private struct TodoEditorInfoSheetView: View {
}
}

Toggle(
"완료",
isOn: Binding(
get: { viewModel.state.isCompleted },
set: { viewModel.send(.setCompleted($0)) }
)
)
.tint(.blue)

Toggle(
"중요 표시",
isOn: Binding(
Expand Down Expand Up @@ -263,9 +272,7 @@ private struct TodoEditorInfoSheetView: View {
HStack {
Text("마감일")
.foregroundStyle(.primary)

Spacer()

if let dueDate = viewModel.state.dueDate {
Tag(dueDateText(for: dueDate), isEditing: true) {
viewModel.send(.setDueDate(nil))
Expand Down