From 8fb98f4f7678d7e290193e1a66eb16d30bfbee8f Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Tue, 7 May 2024 13:20:21 +0700 Subject: [PATCH] feat: add memo grid view --- Memola/App/MemolaApp.swift | 3 +- Memola/Canvas/Core/Canvas.swift | 16 +++--- Memola/Features/Memo/MemoView.swift | 42 ++++++++++---- Memola/Features/Memos/MemosView.swift | 82 +++++++++++++++++++++++---- 4 files changed, 113 insertions(+), 30 deletions(-) diff --git a/Memola/App/MemolaApp.swift b/Memola/App/MemolaApp.swift index a5f44a4..119dd03 100644 --- a/Memola/App/MemolaApp.swift +++ b/Memola/App/MemolaApp.swift @@ -11,8 +11,7 @@ import SwiftUI struct MemolaApp: App { var body: some Scene { WindowGroup { - MemoView() - .environmentObject(Canvas()) + MemosView() .environment(\.managedObjectContext, Persistence.shared.viewContext) } } diff --git a/Memola/Canvas/Core/Canvas.swift b/Memola/Canvas/Core/Canvas.swift index f8d2381..d3c9a46 100644 --- a/Memola/Canvas/Core/Canvas.swift +++ b/Memola/Canvas/Core/Canvas.swift @@ -58,16 +58,16 @@ final class Canvas: NSObject, ObservableObject, Identifiable, Codable, GraphicCo // MARK: - Actions extension Canvas { func load() { -// guard let graphicLoader else { return } + guard let graphicLoader else { return } Task(priority: .high) { [unowned self, graphicLoader] in await MainActor.run { self.state = .loading } do { -// let graphicContext = try graphicLoader() + let graphicContext = try graphicLoader() graphicContext.delegate = self await MainActor.run { -// self.graphicContext = graphicContext + self.graphicContext = graphicContext self.state = .loaded } } catch { @@ -91,11 +91,11 @@ extension Canvas { } func listen(on managedObjectContext: NSManagedObjectContext) { - Task(priority: .background) { [unowned self] in - for await _ in didUpdate.values { - await save(on: managedObjectContext) - } - } +// Task(priority: .utility) { [unowned self] in +// for await _ in didUpdate.throttle(for: 500, scheduler: DispatchQueue.global(qos: .utility), latest: false).values { +// await save(on: managedObjectContext) +// } +// } } } diff --git a/Memola/Features/Memo/MemoView.swift b/Memola/Features/Memo/MemoView.swift index e3ddcdc..b8fe602 100644 --- a/Memola/Features/Memo/MemoView.swift +++ b/Memola/Features/Memo/MemoView.swift @@ -29,7 +29,7 @@ struct MemoView: View { } .overlay(alignment: .topLeading) { Button { - dismiss() + closeMemo() } label: { Image(systemName: "xmark") .padding(15) @@ -39,16 +39,15 @@ struct MemoView: View { .hoverEffect(.lift) .padding() } - .disabled(canvas.state == .loading) + .disabled(canvas.state == .loading || canvas.state == .closing) .overlay { - if canvas.state == .loading { - ProgressView { - Text("Loading memo...") - } - .progressViewStyle(.circular) - .padding(20) - .background(.regularMaterial) - .clipShape(RoundedRectangle(cornerRadius: 20)) + switch canvas.state { + case .loading: + progressView("Loading memo...") + case .closing: + progressView("Saving memo...") + default: + EmptyView() } } .environmentObject(tool) @@ -80,4 +79,27 @@ struct MemoView: View { .background(.regularMaterial) .clipShape(RoundedRectangle(cornerRadius: 20)) } + + func progressView(_ title: String) -> some View { + ProgressView { + Text(title) + } + .progressViewStyle(.circular) + .padding(20) + .background(.regularMaterial) + .clipShape(RoundedRectangle(cornerRadius: 20)) + } + + func closeMemo() { + Task(priority: .high) { + await MainActor.run { + canvas.state = .closing + } + await canvas.save(on: managedObjectContext) + await MainActor.run { + canvas.state = .closed + dismiss() + } + } + } } diff --git a/Memola/Features/Memos/MemosView.swift b/Memola/Features/Memos/MemosView.swift index 6d5c43f..fbc64f6 100644 --- a/Memola/Features/Memos/MemosView.swift +++ b/Memola/Features/Memos/MemosView.swift @@ -8,18 +8,80 @@ import SwiftUI struct MemosView: View { - @State var isPresented: Bool = false + @Environment(\.managedObjectContext) var managedObjectContext + + @FetchRequest(sortDescriptors: []) var memos: FetchedResults + + @State var canvas: Canvas? + var body: some View { - VStack { - Text("Memos View") - Button { - isPresented.toggle() - } label: { - Text("Open Memo") - } - .fullScreenCover(isPresented: $isPresented) { - MemoView() + NavigationStack { + memoGrid + .navigationTitle("Memos") + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button { + createMemo(title: "Untitled") + } label: { + Image(systemName: "plus") + } + .hoverEffect() + } + } + } + .fullScreenCover(item: $canvas) { canvas in + MemoView() + .environmentObject(canvas) + } + } + + var memoGrid: some View { + ScrollView { + LazyVGrid(columns: .init(repeating: GridItem(.flexible()), count: 3)) { + ForEach(memos) { memo in + memoCard(memo) + } } + .padding() + } + } + + func memoCard(_ memo: Memo) -> some View { + VStack(alignment: .leading) { + Rectangle() + .frame(height: 150) + Text(memo.title) + } + .onTapGesture { + openMemo(for: memo) + } + } + + func createMemo(title: String) { + do { + let data = try JSONEncoder().encode(Canvas()) + let memo = Memo(context: managedObjectContext) + memo.id = UUID() + memo.title = title + memo.data = data + memo.createdAt = .now + memo.updatedAt = .now + + try managedObjectContext.save() + openMemo(for: memo) + } catch { + NSLog("[SketchNote] - \(error.localizedDescription)") + } + } + + func openMemo(for memo: Memo) { + do { + let data = memo.data + let canvas = try JSONDecoder().decode(Canvas.self, from: data) + canvas.memo = memo + self.canvas = canvas + } catch { + NSLog("[SketchNote] - \(error.localizedDescription)") } } }