Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checklists #3

Merged
merged 6 commits into from
Jan 2, 2023
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
24 changes: 24 additions & 0 deletions Deadlines.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
04478C47295560DA00F76701 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04478C46295560DA00F76701 /* Preview Assets.xcassets */; };
04478C49295560DA00F76701 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04478C48295560DA00F76701 /* Persistence.swift */; };
04478C4C295560DA00F76701 /* Deadlines.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 04478C4A295560DA00F76701 /* Deadlines.xcdatamodeld */; };
044A45762963431700AEC0F3 /* CheckboxToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044A45752963431700AEC0F3 /* CheckboxToggleStyle.swift */; };
044A457829634ED100AEC0F3 /* DeadlineStatusExplainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044A457729634ED100AEC0F3 /* DeadlineStatusExplainView.swift */; };
04561CAA295F1C060087ABE7 /* NotesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04561CA9295F1C060087ABE7 /* NotesView.swift */; };
04561CB2296092520087ABE7 /* SwiftUIX in Frameworks */ = {isa = PBXBuildFile; productRef = 04561CB1296092520087ABE7 /* SwiftUIX */; };
0468732B2958040900B12C4C /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0468732A2958040900B12C4C /* Settings.swift */; };
0468732D295805B600B12C4C /* NewDeadline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0468732C295805B600B12C4C /* NewDeadline.swift */; };
0468733229580C3000B12C4C /* DeadlineLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0468733129580C3000B12C4C /* DeadlineLinkView.swift */; };
046873362958EC3100B12C4C /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046873352958EC3100B12C4C /* Store.swift */; };
046873382958FFD400B12C4C /* NewDeadlineLinkSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046873372958FFD400B12C4C /* NewDeadlineLinkSheet.swift */; };
04C627BF2962477F00130206 /* DeadlineTodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C627BE2962477F00130206 /* DeadlineTodoView.swift */; };
04C627C12962488400130206 /* NewDeadlineTodoSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C627C02962488400130206 /* NewDeadlineTodoSheet.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -31,13 +35,17 @@
04478C46295560DA00F76701 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
04478C48295560DA00F76701 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
04478C4B295560DA00F76701 /* Deadlines.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Deadlines.xcdatamodel; sourceTree = "<group>"; };
044A45752963431700AEC0F3 /* CheckboxToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxToggleStyle.swift; sourceTree = "<group>"; };
044A457729634ED100AEC0F3 /* DeadlineStatusExplainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeadlineStatusExplainView.swift; sourceTree = "<group>"; };
04561CA9295F1C060087ABE7 /* NotesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesView.swift; sourceTree = "<group>"; };
0468732A2958040900B12C4C /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
0468732C295805B600B12C4C /* NewDeadline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDeadline.swift; sourceTree = "<group>"; };
0468733129580C3000B12C4C /* DeadlineLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeadlineLinkView.swift; sourceTree = "<group>"; };
046873352958EC3100B12C4C /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
046873372958FFD400B12C4C /* NewDeadlineLinkSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDeadlineLinkSheet.swift; sourceTree = "<group>"; };
046873392959CC0200B12C4C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
04C627BE2962477F00130206 /* DeadlineTodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeadlineTodoView.swift; sourceTree = "<group>"; };
04C627C02962488400130206 /* NewDeadlineTodoSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDeadlineTodoSheet.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -73,6 +81,7 @@
children = (
046873392959CC0200B12C4C /* Info.plist */,
04561CA8295F1BEB0087ABE7 /* Notes */,
04C627BD296240F100130206 /* Todos */,
0468732E29580BBC00B12C4C /* Links */,
0429D9412957A6E900D6F8D2 /* Deadlines.entitlements */,
04478C3F295560D900F76701 /* DeadlinesApp.swift */,
Expand Down Expand Up @@ -113,6 +122,17 @@
path = Links;
sourceTree = "<group>";
};
04C627BD296240F100130206 /* Todos */ = {
isa = PBXGroup;
children = (
04C627BE2962477F00130206 /* DeadlineTodoView.swift */,
04C627C02962488400130206 /* NewDeadlineTodoSheet.swift */,
044A45752963431700AEC0F3 /* CheckboxToggleStyle.swift */,
044A457729634ED100AEC0F3 /* DeadlineStatusExplainView.swift */,
);
path = Todos;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -193,9 +213,13 @@
0468732D295805B600B12C4C /* NewDeadline.swift in Sources */,
04561CAA295F1C060087ABE7 /* NotesView.swift in Sources */,
04478C49295560DA00F76701 /* Persistence.swift in Sources */,
04C627BF2962477F00130206 /* DeadlineTodoView.swift in Sources */,
046873362958EC3100B12C4C /* Store.swift in Sources */,
04478C42295560D900F76701 /* ContentView.swift in Sources */,
044A45762963431700AEC0F3 /* CheckboxToggleStyle.swift in Sources */,
04478C40295560D900F76701 /* DeadlinesApp.swift in Sources */,
04C627C12962488400130206 /* NewDeadlineTodoSheet.swift in Sources */,
044A457829634ED100AEC0F3 /* DeadlineStatusExplainView.swift in Sources */,
04478C4C295560DA00F76701 /* Deadlines.xcdatamodeld in Sources */,
046873382958FFD400B12C4C /* NewDeadlineLinkSheet.swift in Sources */,
0468732B2958040900B12C4C /* Settings.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Deadlines/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct ContentView: View {
}
Section {
// Todos
NavigationLink(destination: NotesView(item: item)) {
NavigationLink(destination: DeadlineTodoView(item: item)) {
Label("Checklist", systemImage: "checklist")
}
// Notes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@
<attribute name="url" optional="YES" attributeType="URI"/>
<relationship name="deadline" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Item" inverseName="links" inverseEntity="Item"/>
</entity>
<entity name="DeadlineTodo" representedClassName="DeadlineTodo" syncable="YES" codeGenerationType="class">
<attribute name="done" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="id" attributeType="UUID" usesScalarValueType="NO"/>
<attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="placement" optional="YES" attributeType="Integer 16" defaultValueString="-1" usesScalarValueType="YES"/>
<relationship name="deadline" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Item" inverseName="todos" inverseEntity="Item"/>
</entity>
<entity name="Item" representedClassName="Item" syncable="YES" codeGenerationType="class">
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
<attribute name="name" optional="YES" attributeType="String" defaultValueString="?" spotlightIndexingEnabled="YES"/>
<attribute name="note" attributeType="String" defaultValueString=""/>
<attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="links" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="DeadlineLink" inverseName="deadline" inverseEntity="DeadlineLink"/>
<relationship name="todos" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="DeadlineTodo" inverseName="deadline" inverseEntity="DeadlineTodo"/>
</entity>
</model>
28 changes: 28 additions & 0 deletions Deadlines/Todos/CheckboxToggleStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// CheckboxToggleStyle.swift
// Deadlines
//
// Created by Jack Devey on 02/01/2023.
//

import SwiftUI

struct CheckboxToggleStyle: ToggleStyle {
@Environment(\.isEnabled) var isEnabled

func makeBody(configuration: Configuration) -> some View {
Button(action: {
configuration.isOn.toggle() // toggle the state binding
}, label: {
HStack {
Image(systemName: configuration.isOn ? "checkmark.circle.fill" : "circle")
.imageScale(.large)
.foregroundColor(configuration.isOn ? .accentColor : .secondary)
configuration.label
}
})
.buttonStyle(PlainButtonStyle()) // remove any implicit styling from the button
.disabled(!isEnabled)
}

}
80 changes: 80 additions & 0 deletions Deadlines/Todos/DeadlineStatusExplainView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// DeadlineTodoStatusExplanationView.swift
// Deadlines
//
// Created by Jack Devey on 02/01/2023.
//

import SwiftUI

struct DeadlineStatusExplainView: View {
var body: some View {
List {
Section {
Text("A deadline's status represents how complete a deadline is, taking into account how much time is left to complete it.")
.foregroundColor(.secondary)
.font(.headline)
Text("Deadlines can fall in to 1 of the 5 categories below")
.foregroundColor(.secondary)
}
Section {
// Submitted
VStack(alignment: .leading) {
HStack {
Image(systemName: "paperplane.circle")
.imageScale(.large)
.foregroundColor(.systemBlue)
Text("Submitted")
}
Text("Work has been submitted")
.foregroundColor(.secondary)
}
// Complete
VStack(alignment: .leading) {
HStack {
Image(systemName: "checkmark.circle")
.imageScale(.large)
.foregroundColor(.systemGreen)
Text("Completed")
}
Text("All required work is completed")
.foregroundColor(.secondary)
}
// Progressing
VStack(alignment: .leading) {
HStack {
Image(systemName: "bolt.circle")
.imageScale(.large)
.foregroundColor(.systemPurple)
Text("Progressing")
}
Text("Work is happening at a suitable pace")
.foregroundColor(.secondary)
}
// Warning
VStack(alignment: .leading) {
HStack {
Image(systemName: "exclamationmark.circle")
.imageScale(.large)
.foregroundColor(.systemYellow)
Text("Stale")
}
Text("No progress made in a few days")
.foregroundColor(.secondary)
}
// Past due
VStack(alignment: .leading) {
HStack {
Image(systemName: "calendar.circle")
.imageScale(.large)
.foregroundColor(.systemRed)
Text("Past due")
}
Text("Due date has passed")
.foregroundColor(.secondary)
}
}
}
.navigationTitle("Status explained")
}
}
107 changes: 107 additions & 0 deletions Deadlines/Todos/DeadlineTodoView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//
// DeadlineLinkView.swift
// Deadlines
//
// Created by Jack Devey on 25/12/2022.
//

import SwiftUI

struct DeadlineTodoView: View {

@Environment(\.managedObjectContext) private var viewContext
@State private var showingNewLink = false

// Item to show links for
@ObservedObject var item: Item
// The links themselves
// (State so supports changes)
@State var todos: [DeadlineTodo]
// The store class functions
var store = Store()

@State var isOn1 = false

// Constructor hack to allow the use
// of links as state (this is so the
// list will update)
init(item: Item) {
self.item = item
self.todos = item.todos?.array as! [DeadlineTodo]
}

var body: some View {

List {
Section {
HStack {
Image(systemName: "exclamationmark.circle")
.imageScale(.large)
.foregroundColor(.systemYellow)
Text("Partial")
Spacer()
Text("33%")
.monospacedDigit()
.foregroundColor(.secondary)
}
NavigationLink(destination: DeadlineStatusExplainView()) {
Image(systemName: "questionmark.circle")
.imageScale(.large)
Text("About status")
}
.foregroundColor(.secondary)
}
// Show each todo for the item
ForEach(todos.sorted(using: SortDescriptor(\DeadlineTodo.placement))) { todo in
// Show each link as a link (with label)
Toggle(todo.name!, isOn: $isOn1)
.toggleStyle(CheckboxToggleStyle())
}
// When a todo is moved (order has changed)
.onMove { from, to in
// Move the links around as changed
from.map { todos[$0] }.forEach{ link in
todos.move(fromOffsets: from, toOffset: to)
}
// Loop through todos and update placement
for idx in todos.indices {
todos[idx].placement = Int16(idx)
}
// Save changes
store.save(viewContext: viewContext)
}
// When a todo has been deleted
.onDelete { offsets in
// Delete a todo from the deadline
withAnimation {
offsets.map { todos[$0] }.forEach(viewContext.delete)
todos.remove(atOffsets: offsets)
store.save(viewContext: viewContext) // Save changes
}
}
}
.navigationTitle("Checklist")
.toolbar {
EditButton()
// New button
Button() {
showingNewLink.toggle()
} label: {
Label("New", systemImage: "plus")
}
}
.sheet(isPresented: $showingNewLink) {
NewDeadlineTodoSheet { todo in
// Set placement to end
todo.placement = Int16(todos.endIndex)
// Save changes
item.addToTodos(todo)
store.save(viewContext: viewContext)
// Add todo to todos todo
withAnimation {
todos.append(todo)
}
}
}
}
}
61 changes: 61 additions & 0 deletions Deadlines/Todos/NewDeadlineTodoSheet.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// NewDeadline.swift
// Deadlines
//
// Created by Jack Devey on 25/12/2022.
//

import SwiftUI

struct NewDeadlineTodoSheet: View {

@Environment(\.dismiss) private var dismiss
@Environment(\.managedObjectContext) private var viewContext

@State private var name: String = ""

@State private var showURLInvalid: Bool = false
@State private var showEmptyName: Bool = false
var create: (DeadlineTodo) -> ()


var body: some View {
NavigationView {
Form {
TextField("Name", text: $name)
}
.navigationTitle("New item")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(id: "cancel", placement: .cancellationAction) {
Button {
dismiss()
} label: {
Text("Cancel")
}
}
ToolbarItem(id: "create", placement: .confirmationAction) {
Button {
// Name is empty
if name.isEmpty {
showEmptyName = true
return
}
// New DeadlinTodo object
let todo = DeadlineTodo(context: viewContext)
todo.id = UUID()
todo.name = name
// Pass to DeadlineTodoView to manage
// placement & save
create(todo)
dismiss()
} label: {
Text("Create")
}
}
}
// If name is empty
.alert("Name is empty", isPresented: $showEmptyName) {}
}
}
}