diff --git a/Memola.xcodeproj/project.pbxproj b/Memola.xcodeproj/project.pbxproj index 797e920..4eb8613 100644 --- a/Memola.xcodeproj/project.pbxproj +++ b/Memola.xcodeproj/project.pbxproj @@ -87,6 +87,7 @@ ECBE529C2C1D94A4006BDB3D /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECBE529A2C1D94A4006BDB3D /* CameraView.swift */; }; ECBE529E2C1DAB21006BDB3D /* UIImage++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECBE529D2C1DAB21006BDB3D /* UIImage++.swift */; }; ECC995A32C1E8F2800B2699A /* PhotoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC995A22C1E8F2800B2699A /* PhotoItem.swift */; }; + ECC995A52C1EB4CC00B2699A /* Data++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECC995A42C1EB4CC00B2699A /* Data++.swift */; }; ECD12A862C19EE3900B96E12 /* ElementObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD12A852C19EE3900B96E12 /* ElementObject.swift */; }; ECD12A8A2C19EFB000B96E12 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD12A892C19EFB000B96E12 /* Element.swift */; }; ECD12A8C2C1AEAA900B96E12 /* PhotoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD12A8B2C1AEAA900B96E12 /* PhotoObject.swift */; }; @@ -189,6 +190,7 @@ ECBE529A2C1D94A4006BDB3D /* CameraView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = ""; }; ECBE529D2C1DAB21006BDB3D /* UIImage++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage++.swift"; sourceTree = ""; }; ECC995A22C1E8F2800B2699A /* PhotoItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoItem.swift; sourceTree = ""; }; + ECC995A42C1EB4CC00B2699A /* Data++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data++.swift"; sourceTree = ""; }; ECD12A852C19EE3900B96E12 /* ElementObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementObject.swift; sourceTree = ""; }; ECD12A892C19EFB000B96E12 /* Element.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Element.swift; sourceTree = ""; }; ECD12A8B2C1AEAA900B96E12 /* PhotoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoObject.swift; sourceTree = ""; }; @@ -514,6 +516,7 @@ EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */, EC35655B2BF0712A00A4E0BF /* Float++.swift */, ECBE529D2C1DAB21006BDB3D /* UIImage++.swift */, + ECC995A42C1EB4CC00B2699A /* Data++.swift */, ); path = Extensions; sourceTree = ""; @@ -925,6 +928,7 @@ ECA738DE2BE610A000A4542E /* ViewPortRenderPass.swift in Sources */, EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */, EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */, + ECC995A52C1EB4CC00B2699A /* Data++.swift in Sources */, ECA738A02BE601E400A4542E /* ViewPortVertex.swift in Sources */, ECD12A8A2C19EFB000B96E12 /* Element.swift in Sources */, EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */, diff --git a/Memola/Canvas/Core/Canvas.swift b/Memola/Canvas/Core/Canvas.swift index 774de81..595fa79 100644 --- a/Memola/Canvas/Core/Canvas.swift +++ b/Memola/Canvas/Core/Canvas.swift @@ -131,42 +131,6 @@ extension Canvas { func insertPhoto(at point: CGPoint, photoItem: PhotoItem) { graphicContext.insertPhoto(at: point, photoItem: photoItem) } - - func bookmarkPhoto(of image: UIImage) -> PhotoItem? { - guard let data = image.jpegData(compressionQuality: 1) else { return nil } - let fileManager = FileManager.default - guard let directory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { - return nil - } - let fileName = "\(UUID().uuidString)-\(Date.now.timeIntervalSince1970)" - let folder = directory.appendingPathComponent(canvasID.uriRepresentation().lastPathComponent, conformingTo: .folder) - - if folder.startAccessingSecurityScopedResource(), !fileManager.fileExists(atPath: folder.path()) { - do { - try fileManager.createDirectory(at: folder, withIntermediateDirectories: true) - folder.stopAccessingSecurityScopedResource() - } catch { - NSLog("[Memola] - \(error.localizedDescription)") - folder.stopAccessingSecurityScopedResource() - return nil - } - } - let file = folder.appendingPathComponent(fileName, conformingTo: .jpeg) - do { - try data.write(to: file) - } catch { - NSLog("[Memola] - \(error.localizedDescription)") - return nil - } - var photoBookmark: PhotoItem? - do { - let bookmark = try file.bookmarkData(options: .minimalBookmark) - photoBookmark = PhotoItem(id: file, image: image, bookmark: bookmark) - } catch { - NSLog("[Memola] - \(error.localizedDescription)") - } - return photoBookmark - } } // MARK: - Rendering diff --git a/Memola/Canvas/Elements/Photo/Photo.swift b/Memola/Canvas/Elements/Photo/Photo.swift index 15df4e3..65ffb01 100644 --- a/Memola/Canvas/Elements/Photo/Photo.swift +++ b/Memola/Canvas/Elements/Photo/Photo.swift @@ -59,17 +59,6 @@ final class Photo: @unchecked Sendable, Equatable, Comparable { PhotoVertex(x: maxX, y: maxY, textCoord: CGPoint(x: 1, y: 1)), ] } - - func getBookmarkURL() -> URL? { - var isStale = false - guard let bookmark else { - return nil - } - guard let bookmarkURL = try? URL(resolvingBookmarkData: bookmark, options: .withoutUI, relativeTo: nil, bookmarkDataIsStale: &isStale) else { - return nil - } - return bookmarkURL - } } extension Photo: Drawable { @@ -78,7 +67,7 @@ extension Photo: Drawable { vertexCount = vertices.endIndex vertexBuffer = device.makeBuffer(bytes: vertices, length: vertexCount * MemoryLayout.stride, options: []) } - if texture == nil, let url = getBookmarkURL() { + if texture == nil, let url = bookmark?.getBookmarkURL() { texture = Textures.createPhotoTexture(for: url, on: device) } } diff --git a/Memola/Canvas/Tool/Core/Tool.swift b/Memola/Canvas/Tool/Core/Tool.swift index 92ffda8..1901c9d 100644 --- a/Memola/Canvas/Tool/Core/Tool.swift +++ b/Memola/Canvas/Tool/Core/Tool.swift @@ -112,4 +112,62 @@ public class Tool: NSObject, ObservableObject { } } } + + func selectPhoto(_ image: UIImage, for canvasID: NSManagedObjectID) { + let photoItem = bookmarkPhoto(of: image, with: canvasID) + withAnimation { + selectedPhotoItem = photoItem + } + } + + private func bookmarkPhoto(of image: UIImage, with canvasID: NSManagedObjectID) -> PhotoItem? { + guard let data = image.jpegData(compressionQuality: 1) else { return nil } + let fileManager = FileManager.default + guard let directory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { + return nil + } + let fileName = "\(UUID().uuidString)-\(Date.now.timeIntervalSince1970)" + let folder = directory.appendingPathComponent(canvasID.uriRepresentation().lastPathComponent, conformingTo: .folder) + + if folder.startAccessingSecurityScopedResource(), !fileManager.fileExists(atPath: folder.path()) { + do { + try fileManager.createDirectory(at: folder, withIntermediateDirectories: true) + folder.stopAccessingSecurityScopedResource() + } catch { + NSLog("[Memola] - \(error.localizedDescription)") + folder.stopAccessingSecurityScopedResource() + return nil + } + } + let file = folder.appendingPathComponent(fileName, conformingTo: .jpeg) + do { + try data.write(to: file) + } catch { + NSLog("[Memola] - \(error.localizedDescription)") + return nil + } + var photoBookmark: PhotoItem? + do { + let bookmark = try file.bookmarkData(options: .minimalBookmark) + photoBookmark = PhotoItem(id: file, image: image, bookmark: bookmark) + } catch { + NSLog("[Memola] - \(error.localizedDescription)") + } + return photoBookmark + } + + func unselectPhoto() { + guard let photoItem = selectedPhotoItem else { return } + let fileManager = FileManager.default + if let url = photoItem.bookmark.getBookmarkURL() { + do { + try fileManager.removeItem(at: url) + } catch { + NSLog("[Memola] - \(error.localizedDescription)") + } + } + withAnimation { + selectedPhotoItem = nil + } + } } diff --git a/Memola/Extensions/Data++.swift b/Memola/Extensions/Data++.swift new file mode 100644 index 0000000..97b4182 --- /dev/null +++ b/Memola/Extensions/Data++.swift @@ -0,0 +1,18 @@ +// +// Data++.swift +// Memola +// +// Created by Dscyre Scotti on 6/16/24. +// + +import Foundation + +extension Data { + func getBookmarkURL() -> URL? { + var isStale = false + guard let bookmarkURL = try? URL(resolvingBookmarkData: self, options: .withoutUI, relativeTo: nil, bookmarkDataIsStale: &isStale) else { + return nil + } + return bookmarkURL + } +} diff --git a/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift b/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift index ccdbc57..1fcf43e 100644 --- a/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift +++ b/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift @@ -26,9 +26,7 @@ struct PhotoPreview: View { .cornerRadius(5) .overlay(alignment: .topLeading) { Button { - withAnimation { - tool.selectedPhotoItem = nil - } + tool.unselectPhoto() } label: { Image(systemName: "xmark.circle.fill") .font(.title2) diff --git a/Memola/Features/Memo/Toolbar/Toolbar.swift b/Memola/Features/Memo/Toolbar/Toolbar.swift index 427d9aa..980f2a9 100644 --- a/Memola/Features/Memo/Toolbar/Toolbar.swift +++ b/Memola/Features/Memo/Toolbar/Toolbar.swift @@ -61,10 +61,7 @@ struct Toolbar: View { Task { let data = try? await newValue?.loadTransferable(type: Data.self) if let data, let image = UIImage(data: data) { - let photoItem = canvas.bookmarkPhoto(of: image) - withAnimation { - tool.selectedPhotoItem = photoItem - } + tool.selectPhoto(image, for: canvas.canvasID) } photosPickerItem = nil } @@ -75,7 +72,7 @@ struct Toolbar: View { tool.selectedPhotoItem?.image } set: { image in guard let image else { return } - tool.selectedPhotoItem = canvas.bookmarkPhoto(of: image) + tool.selectPhoto(image, for: canvas.canvasID) } CameraView(image: image, canvas: canvas) .ignoresSafeArea()