mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-04-24 01:28:40 +02:00
feat: add restore and delete actions
This commit is contained in:
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user