diff options
| author | Ruben Beltran del Rio <ruben@unlimited.pizza> | 2021-02-03 23:53:12 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <ruben@unlimited.pizza> | 2021-02-03 23:53:12 +0100 |
| commit | 5e8ff4850c4827125fe12788dd5b153c4f636f48 (patch) | |
| tree | f37c90338f33e7ddc84cc855a20dca1ca503476e /Map/State | |
| parent | 1b85f723b48d38cf345bb9a4f3fd01aa6039b50b (diff) | |
Add code for first release
Diffstat (limited to 'Map/State')
| -rw-r--r-- | Map/State/AppState.swift | 103 | ||||
| -rw-r--r-- | Map/State/Persistence.swift | 42 | ||||
| -rw-r--r-- | Map/State/Store.swift | 18 |
3 files changed, 163 insertions, 0 deletions
diff --git a/Map/State/AppState.swift b/Map/State/AppState.swift new file mode 100644 index 0000000..e10efea --- /dev/null +++ b/Map/State/AppState.swift @@ -0,0 +1,103 @@ +import Cocoa +import Foundation +import SwiftUI + +struct AppState { + var selectedMap: Map? = nil + var mapBeingDeleted: Map? = nil +} + +enum AppAction { + case selectMap(map: Map?) + case deleteMap(map: Map) + case exportMapAsImage(map: Map, evolution: StageType) + case exportMapAsText(map: Map) +} + +func appStateReducer(state: inout AppState, action: AppAction) { + + switch action { + + case .selectMap(let map): + state.selectedMap = map + + case .deleteMap(let map): + let context = PersistenceController.shared.container.viewContext + + context.delete(map) + try? context.save() + + case .exportMapAsImage(let map, let evolution): + let window = NSWindow( + contentRect: .init( + origin: .zero, + size: .init( + width: NSScreen.main!.frame.width, + height: NSScreen.main!.frame.height)), + styleMask: [.closable], + backing: .buffered, + defer: false) + + window.title = map.title ?? "Untitled Map" + window.isOpaque = true + window.center() + window.isMovableByWindowBackground = true + window.makeKeyAndOrderFront(nil) + + let renderView = MapRenderView(map: map, evolution: Stage.stages(evolution)) + + let view = NSHostingView(rootView: renderView) + window.contentView = view + + let imageRepresentation = view.bitmapImageRepForCachingDisplay(in: view.bounds)! + view.cacheDisplay(in: view.bounds, to: imageRepresentation) + let image = NSImage(cgImage: imageRepresentation.cgImage!, size: view.bounds.size) + + let dialog = NSSavePanel() + + dialog.title = "Save Map" + dialog.showsResizeIndicator = false + dialog.canCreateDirectories = true + dialog.showsHiddenFiles = false + dialog.allowedFileTypes = ["png"] + dialog.nameFieldStringValue = map.title ?? "Untitled Map" + + if dialog.runModal() == NSApplication.ModalResponse.OK { + let result = dialog.url + + if result != nil { + + image.writePNG(toURL: result!) + print("saved at \(result!)") + } + } else { + print("Cancel") + } + window.orderOut(nil) + + case .exportMapAsText(let map): + let dialog = NSSavePanel() + + dialog.title = "Save Map Text" + dialog.showsResizeIndicator = false + dialog.canCreateDirectories = true + dialog.showsHiddenFiles = false + dialog.allowedFileTypes = ["txt"] + dialog.nameFieldStringValue = map.title ?? "Untitled Map" + + if let content = map.content { + + if dialog.runModal() == NSApplication.ModalResponse.OK { + let result = dialog.url + + if let result = result { + try? content.write(to: result, atomically: true, encoding: String.Encoding.utf8) + } + } else { + print("Cancel") + } + } + } +} + +typealias AppStore = Store<AppState, AppAction> diff --git a/Map/State/Persistence.swift b/Map/State/Persistence.swift new file mode 100644 index 0000000..1eb09e8 --- /dev/null +++ b/Map/State/Persistence.swift @@ -0,0 +1,42 @@ +import CoreData + +struct PersistenceController { + static let shared = PersistenceController() + + static var preview: PersistenceController = { + let result = PersistenceController(inMemory: true) + let viewContext = result.container.viewContext + for _ in 0..<10 { + let newMap = Map(context: viewContext) + newMap.uuid = UUID() + newMap.createdAt = Date() + newMap.title = "Map \(newMap.createdAt!.format())" + newMap.content = "" + } + do { + try viewContext.save() + } catch { + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + return result + }() + + let container: NSPersistentCloudKitContainer + + init(inMemory: Bool = false) { + container = NSPersistentCloudKitContainer(name: "Map") + + container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + container.viewContext.automaticallyMergesChangesFromParent = true + + if inMemory { + container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") + } + container.loadPersistentStores(completionHandler: { (storeDescription, error) in + if let error = error as NSError? { + fatalError("Unresolved error \(error), \(error.userInfo)") + } + }) + } +} diff --git a/Map/State/Store.swift b/Map/State/Store.swift new file mode 100644 index 0000000..7860f33 --- /dev/null +++ b/Map/State/Store.swift @@ -0,0 +1,18 @@ +import Foundation + +final class Store<State, Action>: ObservableObject { + @Published private(set) var state: State + + private let reducer: Reducer<State, Action> + + init(initialState: State, reducer: @escaping Reducer<State, Action>) { + self.state = initialState + self.reducer = reducer + } + + func send(_ action: Action) { + reducer(&state, action) + } +} + +typealias Reducer<State, Action> = (inout State, Action) -> Void |