diff --git a/taskfolio/DynamicWidget/DynamicWidgetLiveActivity.swift b/taskfolio/DynamicWidget/DynamicWidgetLiveActivity.swift index 5541a0c..89eb284 100644 --- a/taskfolio/DynamicWidget/DynamicWidgetLiveActivity.swift +++ b/taskfolio/DynamicWidget/DynamicWidgetLiveActivity.swift @@ -12,14 +12,13 @@ import SwiftUI struct DynamicWidgetAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { // Dynamic stateful properties about your activity go here! - var time: Int + var title: String + var colorType: Int16 + var time: Int32 var isTimerActive: Bool = true } // Fixed non-changing properties about your activity go here! - var id: UUID - var title: String - var colorType: Int16 } struct DynamicWidgetLiveActivity: Widget { @@ -29,9 +28,9 @@ struct DynamicWidgetLiveActivity: Widget { HStack { Divider() .frame(width: 3, height: 15) - .overlay(ColorType.toDomain(int16: context.attributes.colorType).color) + .overlay(ColorType.toDomain(int16: context.state.colorType).color) - Text(context.attributes.title) + Text(context.state.title) .font(.title2) Spacer() @@ -40,7 +39,7 @@ struct DynamicWidgetLiveActivity: Widget { Image(systemName: context.state.isTimerActive ? "pause.circle" : "play.circle") .font(.title2) - Text(TimeManager.shared.toString(second: context.state.time)) + Text(TimeManager.shared.toString(second: Int(context.state.time))) .font(.caption) } } @@ -56,13 +55,13 @@ struct DynamicWidgetLiveActivity: Widget { HStack { Divider() .frame(width: 3, height: 15) - .overlay(ColorType.toDomain(int16: context.attributes.colorType).color) + .overlay(ColorType.toDomain(int16: context.state.colorType).color) - Text(context.attributes.title) + Text(context.state.title) } } DynamicIslandExpandedRegion(.trailing) { - Text(TimeManager.shared.toString(second: context.state.time)) + Text(TimeManager.shared.toString(second: Int(context.state.time))) } DynamicIslandExpandedRegion(.bottom) { @@ -71,12 +70,12 @@ struct DynamicWidgetLiveActivity: Widget { HStack { Divider() .frame(width: 3, height: 15) - .overlay(ColorType.toDomain(int16: context.attributes.colorType).color) + .overlay(ColorType.toDomain(int16: context.state.colorType).color) - Text(context.attributes.title) + Text(context.state.title) } } compactTrailing: { - Text(TimeManager.shared.toString(second: context.state.time)) + Text(TimeManager.shared.toString(second: Int(context.state.time))) .font(.caption) } minimal: { VStack(alignment: .center) { diff --git a/taskfolio/taskfolio.xcodeproj/project.xcworkspace/xcuserdata/moyoung.xcuserdatad/UserInterfaceState.xcuserstate b/taskfolio/taskfolio.xcodeproj/project.xcworkspace/xcuserdata/moyoung.xcuserdatad/UserInterfaceState.xcuserstate index cac7d4c..e7a2387 100644 Binary files a/taskfolio/taskfolio.xcodeproj/project.xcworkspace/xcuserdata/moyoung.xcuserdatad/UserInterfaceState.xcuserstate and b/taskfolio/taskfolio.xcodeproj/project.xcworkspace/xcuserdata/moyoung.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/taskfolio/taskfolio/Feature/Home/HomeStore.swift b/taskfolio/taskfolio/Feature/Home/HomeStore.swift index 418059d..e0eadb9 100644 --- a/taskfolio/taskfolio/Feature/Home/HomeStore.swift +++ b/taskfolio/taskfolio/Feature/Home/HomeStore.swift @@ -43,10 +43,10 @@ struct HomeStore: ReducerProtocol { var taskListCells: IdentifiedArrayOf = [] var filteredTaskListCells: IdentifiedArrayOf = [] var editTask: EditTaskStore.State? + var setting: SettingStore.State? //MARK: ActivityKit var activity : Activity? - var activityContent: ActivityContent? } enum Action: BindableAction, Equatable { @@ -55,6 +55,7 @@ struct HomeStore: ReducerProtocol { case addButtonTapped case leftButtonTapped case rightButtonTapped + case settingButtonTapped case dateChanged(Date) case delete(IndexSet) case setSheet(isPresented: Bool) @@ -73,10 +74,11 @@ struct HomeStore: ReducerProtocol { //MARK: Child Action case taskListCell(id: TaskCellStore.State.ID, action: TaskCellStore.Action) case editTask(EditTaskStore.Action) + case setting(SettingStore.Action) //MARK: ActivityKit - case activityRequest(id: UUID, title: String, colorType: Int16) - case activityUpdateRequest(time: Int, isTimerActive: Bool) + case activityRequest + case activityUpdateRequest(title: String, colorType: Int16, time: Int32, isTimerActive: Bool) case activityUpdateResponse } @@ -93,31 +95,6 @@ struct HomeStore: ReducerProtocol { case .binding: return .none - case let .activityRequest(id, title, colorType): - if state.activity?.attributes.id == id { return .none } - - let initialContentState = DynamicWidgetAttributes.ContentState(time: 0) - let activityAttributes = DynamicWidgetAttributes(id: id, title: title, colorType: colorType) - - let content = ActivityContent(state: initialContentState, staleDate: Calendar.current.date(byAdding: .hour, value: 7, to: Date())!) - do { - state.activity = try Activity.request(attributes: activityAttributes, content: content) - } catch { - print(error) - } - return .none - - case let .activityUpdateRequest(time, isTimerActive): - let content = ActivityContent.init(state: .init(time: time, isTimerActive: isTimerActive), staleDate: nil) - guard let activity = state.activity else { return .none } - return .task { [activity = activity] in - await activity.update(content) - return .activityUpdateResponse - } - - case .activityUpdateResponse: - return .none - case .addButtonTapped: let newTask = taskClient.newTask() newTask.title = "untitled" @@ -134,6 +111,11 @@ struct HomeStore: ReducerProtocol { case .rightButtonTapped: return .send(.dateChanged(state.currentDate.add(byAdding: .day, value: 7))) + case .settingButtonTapped: + state.path.append(.setting) + state.setting = .init() + return .none + case let .dateChanged(date): state.currentDate = date state.currentWeekDates = date.weekDates() @@ -141,7 +123,7 @@ struct HomeStore: ReducerProtocol { case let .delete(indexSet): for index in indexSet { - let id = state.taskListCells[index].task.objectID + let id = state.filteredTaskListCells[index].task.objectID taskClient.delete(id) } return .send(.refresh) @@ -168,7 +150,7 @@ struct HomeStore: ReducerProtocol { return .init(id: $0.id, task: $0.task, isTimerActive: $0.isTimerActive) } })))), - .send(.activityUpdateRequest(time: Int(task?.time ?? 0), isTimerActive: state.isTimerActive)) + .send(.activityUpdateRequest(title: task?.title ?? "", colorType: task?.colorType ?? 0, time: task?.time ?? 0, isTimerActive: state.isTimerActive)) ]) case .refresh: @@ -213,7 +195,7 @@ struct HomeStore: ReducerProtocol { state.isTimerActive = false let task = state.taskListCells.first(where: { $0.id == id })?.task return .concatenate([ - .send(.activityRequest(id: id, title: task?.title ?? "Task", colorType: task?.colorType ?? 0)), + .send(.activityRequest), .send(.updateTaskListCells(.init(uniqueElements: state.taskListCells.map({ let isTimerActive = $0.isTimerActive ? false : (id == $0.id) state.timerTaskCellID = isTimerActive ? id : state.timerTaskCellID @@ -227,11 +209,39 @@ struct HomeStore: ReducerProtocol { } } .cancellable(id: TimerID.self, cancelInFlight: true), - .send(.activityUpdateRequest(time: Int(task?.time ?? 0), isTimerActive: state.isTimerActive)) + .send(.activityUpdateRequest(title: task?.title ?? "", colorType: task?.colorType ?? 0, time: task?.time ?? 0, isTimerActive: state.isTimerActive)) ]) } case .editTask: return .none + + case .setting: + return .none + + case .activityRequest: + if state.activity != nil { return .none } + + let initialContentState = DynamicWidgetAttributes.ContentState(title: "Task", colorType: 0, time: 0) + let activityAttributes = DynamicWidgetAttributes() + + let content = ActivityContent(state: initialContentState, staleDate: Calendar.current.date(byAdding: .hour, value: 7, to: Date())!) + do { + state.activity = try Activity.request(attributes: activityAttributes, content: content) + } catch { + print(error) + } + return .none + + case let .activityUpdateRequest(title, colorType, time, isTimerActive): + let content = ActivityContent.init(state: .init(title: title, colorType: colorType, time: time, isTimerActive: isTimerActive), staleDate: nil) + guard let activity = state.activity else { return .none } + return .task { [activity = activity] in + await activity.update(content) + return .activityUpdateResponse + } + + case .activityUpdateResponse: + return .none } } .forEach(\.filteredTaskListCells, action: /Action.taskListCell(id:action:)) { diff --git a/taskfolio/taskfolio/Feature/Home/HomeView.swift b/taskfolio/taskfolio/Feature/Home/HomeView.swift index 6c534b2..a03ebf3 100644 --- a/taskfolio/taskfolio/Feature/Home/HomeView.swift +++ b/taskfolio/taskfolio/Feature/Home/HomeView.swift @@ -126,6 +126,7 @@ struct HomeView: View { EditButton() Button(action:{ + viewStore.send(.settingButtonTapped) }) { Image(systemName: "gearshape") .imageScale(.medium) @@ -134,6 +135,11 @@ struct HomeView: View { ) .navigationDestination(for: HomeScene.self) { scene in switch scene { + case .setting: + IfLetStore(self.store.scope(state: \.setting, action: { .setting($0) })) { + SettingView(store: $0) + } + default: EmptyView() } diff --git a/taskfolio/taskfolio/Feature/Setting/SettingStore.swift b/taskfolio/taskfolio/Feature/Setting/SettingStore.swift index b4a22fb..7b5814a 100644 --- a/taskfolio/taskfolio/Feature/Setting/SettingStore.swift +++ b/taskfolio/taskfolio/Feature/Setting/SettingStore.swift @@ -6,3 +6,26 @@ // import Foundation + +import ComposableArchitecture + +struct SettingStore: ReducerProtocol { + struct State: Equatable { + init() { } + } + + enum Action: BindableAction, Equatable { + case binding(BindingAction) + } + + var body: some ReducerProtocol { + BindingReducer() + + Reduce { state, action in + switch action { + case .binding: + return .none + } + } + } +} diff --git a/taskfolio/taskfolio/Feature/Setting/SettingView.swift b/taskfolio/taskfolio/Feature/Setting/SettingView.swift index 8848f80..8d1f04e 100644 --- a/taskfolio/taskfolio/Feature/Setting/SettingView.swift +++ b/taskfolio/taskfolio/Feature/Setting/SettingView.swift @@ -5,4 +5,39 @@ // Created by 송영모 on 2023/05/25. // -import Foundation +import SwiftUI + +import ComposableArchitecture + +struct SettingView: View { + public let store: StoreOf + + public var body: some View { + WithViewStore(self.store, observe: { $0 }) { viewStore in + VStack(spacing: .zero) { + Form { + Text("iCloud를 허용 해주시면. iPad, WatchOS(지원예정), MacOS(지원 예정) 에서 사용가능 합니다.") + + Section { + DisclosureGroup("TMI") { + Text("다이나믹 아일랜드를 사용한 타이머가 필요하다고 생각했습니다. 하지만 너무 복잡할 필요는 없어요. 걱정마세요. 이 이상으로 복잡해지지 않습니다.") + .font(.subheadline) + } + + DisclosureGroup("Update Note") { + Text("다음 업데이트 예정 사항입니다.") + .font(.subheadline) + } + } + } + } + .navigationTitle("Setting") + } + } +} + +struct SettingView_Previews: PreviewProvider { + static var previews: some View { + SettingView(store: .init(initialState: .init(), reducer: SettingStore()._printChanges())) + } +} diff --git a/taskfolio/taskfolio/Feature/TaskList/TaskCellView.swift b/taskfolio/taskfolio/Feature/TaskList/TaskCellView.swift index 380a417..aef6ac7 100644 --- a/taskfolio/taskfolio/Feature/TaskList/TaskCellView.swift +++ b/taskfolio/taskfolio/Feature/TaskList/TaskCellView.swift @@ -41,6 +41,7 @@ struct TaskCellView: View { .font(.caption) } } + .contentShape(Rectangle()) .onTapGesture { viewStore.send(.tapped) }