feat: add restore and delete actions

This commit is contained in:
dscyrescotti
2024-06-30 01:02:37 +07:00
parent 63a619edf9
commit 542d3ea9d6
4 changed files with 92 additions and 22 deletions

View File

@@ -10,6 +10,7 @@ import SwiftUI
struct DashboardView: View { struct DashboardView: View {
@Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.horizontalSizeClass) var horizontalSizeClass
@State var memo: MemoObject?
@State var sidebarItem: SidebarItem? = .memos @State var sidebarItem: SidebarItem? = .memos
var body: some View { var body: some View {
@@ -18,12 +19,21 @@ struct DashboardView: View {
} detail: { } detail: {
switch sidebarItem { switch sidebarItem {
case .memos: case .memos:
MemosView() MemosView(memo: $memo)
case .trash: case .trash:
TrashView() TrashView(memo: $memo, sidebarItem: $sidebarItem)
default: default:
MemosView() MemosView(memo: $memo)
} }
} }
.fullScreenCover(item: $memo) { memo in
MemoView(memo: memo)
.onDisappear {
withPersistence(\.viewContext) { context in
try context.saveIfNeeded()
context.refreshAllObjects()
}
}
}
} }
} }

View File

@@ -12,10 +12,11 @@ struct MemosView: View {
@FetchRequest var memoObjects: FetchedResults<MemoObject> @FetchRequest var memoObjects: FetchedResults<MemoObject>
@State var memo: MemoObject?
@State var query: String = "" @State var query: String = ""
@State var currentDate: Date = .now @State var currentDate: Date = .now
@Binding var memo: MemoObject?
@AppStorage("memola.memo-objects.memos.sort") var sort: Sort = .recent @AppStorage("memola.memo-objects.memos.sort") var sort: Sort = .recent
@AppStorage("memola.memo-objects.memos.filter") var filter: Filter = .none @AppStorage("memola.memo-objects.memos.filter") var filter: Filter = .none
@@ -25,7 +26,8 @@ struct MemosView: View {
query.isEmpty ? .memoEmpty : .memoNotFound query.isEmpty ? .memoEmpty : .memoNotFound
} }
init() { init(memo: Binding<MemoObject?>) {
_memo = memo
let standard = UserDefaults.standard let standard = UserDefaults.standard
var descriptors: [SortDescriptor<MemoObject>] = [] var descriptors: [SortDescriptor<MemoObject>] = []
var predicates: [NSPredicate] = [NSPredicate(format: "isTrash = NO")] var predicates: [NSPredicate] = [NSPredicate(format: "isTrash = NO")]
@@ -115,15 +117,6 @@ struct MemosView: View {
} }
} }
} }
.fullScreenCover(item: $memo) { memo in
MemoView(memo: memo)
.onDisappear {
withPersistence(\.viewContext) { context in
try context.saveIfNeeded()
context.refreshAllObjects()
}
}
}
.onChange(of: sort) { oldValue, newValue in .onChange(of: sort) { oldValue, newValue in
memoObjects.sortDescriptors = newValue.memoSortDescriptors memoObjects.sortDescriptors = newValue.memoSortDescriptors
} }

View File

@@ -13,18 +13,35 @@ struct TrashView: View {
@FetchRequest var memoObjects: FetchedResults<MemoObject> @FetchRequest var memoObjects: FetchedResults<MemoObject>
@State var query: String = "" @State var query: String = ""
@State var restoredMemo: MemoObject?
@State var deletedMemo: MemoObject?
@Binding var memo: MemoObject?
@Binding var sidebarItem: SidebarItem?
var placeholder: Placeholder.Info { var placeholder: Placeholder.Info {
query.isEmpty ? .trashEmpty : .trashNotFound query.isEmpty ? .trashEmpty : .trashNotFound
} }
init() { init(memo: Binding<MemoObject?>, sidebarItem: Binding<SidebarItem?>) {
_memo = memo
_sidebarItem = sidebarItem
let descriptors = [SortDescriptor(\MemoObject.deletedAt, order: .reverse)] let descriptors = [SortDescriptor(\MemoObject.deletedAt, order: .reverse)]
let predicate = NSPredicate(format: "isTrash = YES") let predicate = NSPredicate(format: "isTrash = YES")
_memoObjects = FetchRequest(sortDescriptors: descriptors, predicate: predicate) _memoObjects = FetchRequest(sortDescriptors: descriptors, predicate: predicate)
} }
var body: some View { var body: some View {
let restoresMemo = Binding<Bool> {
restoredMemo != nil
} set: { _ in
restoredMemo = nil
}
let deletesMemo = Binding<Bool> {
deletedMemo != nil
} set: { _ in
deletedMemo = nil
}
MemoGrid(memoObjects: memoObjects, placeholder: placeholder) { memoObject in MemoGrid(memoObjects: memoObjects, placeholder: placeholder) { memoObject in
memoCard(memoObject) memoCard(memoObject)
} }
@@ -48,6 +65,31 @@ struct TrashView: View {
.onChange(of: query) { oldValue, newValue in .onChange(of: query) { oldValue, newValue in
updatePredicate() updatePredicate()
} }
.alert("Restore Memo", isPresented: restoresMemo) {
Button {
restoreMemo(for: restoredMemo)
} label: {
Text("Restore")
}
Button {
restoreAndOpenMemo(for: restoredMemo)
} label: {
Text("Restore and Open")
}
Button("Cancel", role: .cancel) { }
} message: {
Text("Would you like to restore this memo or restore and open it?")
}
.alert("Delete Memo Permanently", isPresented: deletesMemo) {
Button(role: .destructive) {
deleteMemo(for: deletedMemo)
} label: {
Text("Delete")
}
Button("Cancel", role: .cancel) { }
} message: {
Text("Are you sure you want to permanently delete this memo? This action cannot be undone.")
}
} }
func memoCard(_ memoObject: MemoObject) -> some View { func memoCard(_ memoObject: MemoObject) -> some View {
@@ -55,23 +97,25 @@ struct TrashView: View {
card card
.contextMenu { .contextMenu {
Button { Button {
restoreMemo(for: memoObject)
} label: { } label: {
Label("Restore", systemImage: "square.and.arrow.down") Label("Restore", systemImage: "square.and.arrow.down")
} }
Button(role: .destructive) { Button(role: .destructive) {
deletedMemo = memoObject
} label: { } label: {
Label("Delete Permanently", systemImage: "trash") Label("Delete Permanently", systemImage: "trash")
} }
} }
} details: { } details: {
Text("Deleted on \(memoObject.deletedAt.formatted(date: .abbreviated, time: .standard))") if let deletedAt = memoObject.deletedAt {
.font(.caption) Text("Deleted on \(deletedAt.formatted(date: .abbreviated, time: .standard))")
.foregroundStyle(.secondary) .font(.caption)
.foregroundStyle(.secondary)
}
} }
.onTapGesture { .onTapGesture {
restoredMemo = memoObject
} }
} }
@@ -82,4 +126,27 @@ struct TrashView: View {
} }
memoObjects.nsPredicate = NSCompoundPredicate(type: .and, subpredicates: predicates) memoObjects.nsPredicate = NSCompoundPredicate(type: .and, subpredicates: predicates)
} }
func restoreMemo(for memo: MemoObject?) {
guard let memo else { return }
memo.isTrash = false
memo.deletedAt = nil
withPersistence(\.viewContext) { context in
try context.saveIfNeeded()
}
}
func restoreAndOpenMemo(for memo: MemoObject?) {
restoreMemo(for: memo)
self.sidebarItem = .memos
self.memo = memo
}
func deleteMemo(for memo: MemoObject?) {
guard let memo else { return }
withPersistenceSync(\.viewContext) { context in
context.delete(memo)
try context.saveIfNeeded()
}
}
} }

View File

@@ -14,7 +14,7 @@ final class MemoObject: NSManagedObject, Identifiable {
@NSManaged var title: String @NSManaged var title: String
@NSManaged var createdAt: Date @NSManaged var createdAt: Date
@NSManaged var updatedAt: Date @NSManaged var updatedAt: Date
@NSManaged var deletedAt: Date @NSManaged var deletedAt: Date?
@NSManaged var isFavorite: Bool @NSManaged var isFavorite: Bool
@NSManaged var isTrash: Bool @NSManaged var isTrash: Bool
@NSManaged var tool: ToolObject @NSManaged var tool: ToolObject