Skip to content

Commit f4ee717

Browse files
Create a document launch view model
The view model will handle most of the facilities the view themselves hosted.
1 parent 8885e1f commit f4ee717

File tree

7 files changed

+169
-132
lines changed

7 files changed

+169
-132
lines changed

MCMaps/MCMapsApp.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ struct MCMapsApp: App {
114114
}
115115
#endif
116116

117-
DocumentLaunchView(displayCreationWindow: $displayCreationWindow, proxyMap: $proxyMap)
117+
DocumentLaunchView(
118+
viewModel: DocumentLaunchViewModel(displayCreationWindow: $displayCreationWindow, proxyMap: $proxyMap)
119+
)
118120

119121
#if os(macOS)
120122
Window("About \(Self.appName)", id: "about") {

MCMaps/Resources/Alidade.docc/Alidade.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ contributors to read through.
8383
- ``CartographyRoute``
8484
- ``CartographyMapViewModel``
8585
- ``CartographyPinViewModel``
86+
- ``DocumentLaunchViewModel``
8687

8788
### Services
8889

@@ -96,7 +97,6 @@ contributors to read through.
9697
### Maps
9798

9899
- ``CartographyOrnamentMap``
99-
- ``MapOrnament``
100100
- ``CartographyMapView``
101101
- ``CartographyMapViewState``
102102

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// DocumentLaunchViewModel.swift
3+
// MCMaps
4+
//
5+
// Created by Marquis Kurt on 15-03-2025.
6+
//
7+
8+
import SwiftUI
9+
10+
/// A view model used to handle creating documents in a document launch view.
11+
///
12+
/// This is generally used by the document launch view to handle creating a new file, and displaying the form for
13+
/// setting the Minecraft version and seed.
14+
class DocumentLaunchViewModel {
15+
/// The currently selected file.
16+
var selectedFileURL: Binding<URL?>
17+
18+
/// Whether to display the creation window form.
19+
var displayCreationWindow: Binding<Bool>
20+
21+
/// A proxy map used to create the file with a specified Minecraft version and seed.
22+
var proxyMap: Binding<CartographyMap>
23+
24+
private var selectedFile: URL?
25+
26+
init(displayCreationWindow: Binding<Bool>, proxyMap: Binding<CartographyMap>) {
27+
self.selectedFileURL = .constant(.documentsDirectory)
28+
self.displayCreationWindow = displayCreationWindow
29+
self.proxyMap = proxyMap
30+
self.selectedFileURL = Binding { [self] in
31+
return selectedFile
32+
} set: { [self] newValue in
33+
selectedFile = newValue
34+
}
35+
}
36+
37+
/// Sanitizes the specified URL, dropping the `mcmap` suffix.
38+
func sanitize(url: URL) -> String {
39+
return url.lastPathComponent.replacingOccurrences(of: ".mcmap", with: "")
40+
}
41+
42+
/// Displays a human-friendly version of a file URL.
43+
func friendlyUrl(_ url: URL, for currentUser: String = NSUserName()) -> String {
44+
let originalDirectory = url.standardizedFileURL.deletingLastPathComponent()
45+
if !url.contains(components: ["Users", currentUser]) {
46+
return originalDirectory.standardizedFileURL.relativePath
47+
}
48+
var newPath = URL(filePath: "~/")
49+
var newComponents = originalDirectory.pathComponents.dropFirst(3)
50+
if newComponents.contains("com~apple~CloudDocs") {
51+
newPath = URL(filePath: "iCloud Drive/")
52+
newComponents = newComponents.dropFirst(3)
53+
}
54+
for component in newComponents {
55+
newPath = newPath.appending(component: component)
56+
}
57+
return newPath.relativePath
58+
}
59+
60+
/// Whether the specified file URL is in the Mobile Documents directory.
61+
func isInMobileDocuments(_ url: URL, for currentUser: String = NSUserName()) -> Bool {
62+
return url.standardizedFileURL.contains(components: ["Users", currentUser, "com~apple~CloudDocs"])
63+
}
64+
65+
#if os(macOS)
66+
@available(macOS 15.0, *)
67+
func showInFinder(url: URL) async {
68+
NSWorkspace.shared.activateFileViewerSelecting([url])
69+
}
70+
#endif
71+
}

MCMaps/Views/DocumentLaunchView/DocumentLaunchView+Desktop.swift

Lines changed: 32 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,20 @@
55
// Created by Marquis Kurt on 15-03-2025.
66
//
77

8+
import Observation
89
import SwiftUI
910

1011
#if os(macOS)
1112
/// A launch view that lets players create and open files easily.
1213
///
1314
/// This will generally be displayed when the app is first loaded.
1415
struct DocumentLaunchView: Scene {
15-
/// Whether the creation window should be visible.
16-
///
17-
/// Applicable to iOS and iPadOS.
18-
@Binding var displayCreationWindow: Bool
19-
20-
/// The proxy map used to create a file temporarily.
21-
///
22-
/// Applicable to iOS and iPadOS.
23-
@Binding var proxyMap: CartographyMap
16+
var viewModel: DocumentLaunchViewModel
2417

2518
@Environment(\.dismissWindow) private var dismissWindow
2619
@Environment(\.newDocument) private var newDocument
2720
@Environment(\.openDocument) private var openDocument
2821

29-
@State private var selectedFile: URL?
30-
@State private var displayCreationSheet = false
31-
3222
@ScaledMetric private var fileHeight = 36.0
3323
@ScaledMetric private var filePaddingH = 4.0
3424
@ScaledMetric private var filePaddingV = 2.0
@@ -69,23 +59,27 @@ import SwiftUI
6959
.frame(width: 600, height: 400)
7060
.toolbarVisibility(.hidden, for: .windowToolbar)
7161
.containerBackground(.thinMaterial, for: .window)
72-
.sheet(isPresented: $displayCreationSheet) {
62+
.sheet(isPresented: viewModel.displayCreationWindow) {
7363
NavigationStack {
74-
MapCreatorForm(worldName: $proxyMap.name, mcVersion: $proxyMap.mcVersion, seed: $proxyMap.seed)
75-
.navigationTitle("Create Map")
76-
.formStyle(.grouped)
77-
.toolbar {
78-
ToolbarItem(placement: .confirmationAction) {
79-
Button("Create") {
80-
createDocument()
81-
}
64+
MapCreatorForm(
65+
worldName: viewModel.proxyMap.name,
66+
mcVersion: viewModel.proxyMap.mcVersion,
67+
seed: viewModel.proxyMap.seed
68+
)
69+
.navigationTitle("Create Map")
70+
.formStyle(.grouped)
71+
.toolbar {
72+
ToolbarItem(placement: .confirmationAction) {
73+
Button("Create") {
74+
createDocument()
8275
}
83-
ToolbarItem(placement: .cancellationAction) {
84-
Button("Cancel", role: .cancel) {
85-
displayCreationSheet = false
86-
}
76+
}
77+
ToolbarItem(placement: .cancellationAction) {
78+
Button("Cancel", role: .cancel) {
79+
viewModel.displayCreationWindow.wrappedValue = false
8780
}
8881
}
82+
}
8983
}
9084
}
9185
}
@@ -122,7 +116,7 @@ import SwiftUI
122116
Spacer()
123117
VStack(alignment: .leading) {
124118
Button {
125-
displayCreationSheet.toggle()
119+
viewModel.displayCreationWindow.wrappedValue.toggle()
126120
} label: {
127121
HStack {
128122
Label("Create a Map", systemImage: "document.badge.plus")
@@ -167,7 +161,7 @@ import SwiftUI
167161
}
168162

169163
private var recentDocumentsList: some View {
170-
List(selection: $selectedFile) {
164+
List(selection: viewModel.selectedFileURL) {
171165
ForEach(recentDocuments, id: \.self) { url in
172166
HStack {
173167
Image("File Preview")
@@ -178,14 +172,14 @@ import SwiftUI
178172
.padding(.horizontal, filePaddingH)
179173
.frame(height: fileHeight)
180174
VStack(alignment: .leading) {
181-
Text(sanitize(url: url))
175+
Text(viewModel.sanitize(url: url))
182176
.font(.headline)
183177
HStack {
184-
if isInMobileDocuments(url) {
178+
if viewModel.isInMobileDocuments(url) {
185179
Image(systemName: "icloud")
186180
.foregroundStyle(.blue)
187181
}
188-
Text(friendlyUrl(url))
182+
Text(viewModel.friendlyUrl(url))
189183
.foregroundStyle(.secondary)
190184
}
191185
}
@@ -202,7 +196,7 @@ import SwiftUI
202196
if urls.count == 1, let url = urls.first {
203197
Button {
204198
Task {
205-
await showInFinder(url: url)
199+
await viewModel.showInFinder(url: url)
206200
}
207201
} label: {
208202
Label("Show in Finder", systemImage: "folder")
@@ -216,11 +210,11 @@ import SwiftUI
216210
}
217211

218212
private func createDocument() {
219-
proxyMap.pins.append(CartographyMapPin(position: .zero, name: "Spawn Point"))
220-
proxyMap.recentLocations?.append(.zero)
221-
let newFile = CartographyMapFile(map: proxyMap)
213+
viewModel.proxyMap.wrappedValue.pins.append(CartographyMapPin(position: .zero, name: "Spawn Point"))
214+
viewModel.proxyMap.wrappedValue.recentLocations?.append(.zero)
215+
let newFile = CartographyMapFile(map: viewModel.proxyMap.wrappedValue)
222216
newDocument(newFile)
223-
displayCreationSheet = false
217+
viewModel.displayCreationWindow.wrappedValue = false
224218
Task {
225219
// NOTE(alicerunsonfedora): WTF is this bullshit? Can't you just dismiss normally you dirty fuck?
226220
try await Task.sleep(nanoseconds: 1000)
@@ -236,35 +230,6 @@ import SwiftUI
236230
} catch {}
237231
}
238232
}
239-
240-
private func sanitize(url: URL) -> String {
241-
return url.lastPathComponent.replacingOccurrences(of: ".mcmap", with: "")
242-
}
243-
244-
private func friendlyUrl(_ url: URL, for currentUser: String = NSUserName()) -> String {
245-
let originalDirectory = url.standardizedFileURL.deletingLastPathComponent()
246-
if !url.contains(components: ["Users", currentUser]) {
247-
return originalDirectory.standardizedFileURL.relativePath
248-
}
249-
var newPath = URL(filePath: "~/")
250-
var newComponents = originalDirectory.pathComponents.dropFirst(3)
251-
if newComponents.contains("com~apple~CloudDocs") {
252-
newPath = URL(filePath: "iCloud Drive/")
253-
newComponents = newComponents.dropFirst(3)
254-
}
255-
for component in newComponents {
256-
newPath = newPath.appending(component: component)
257-
}
258-
return newPath.relativePath
259-
}
260-
261-
private func isInMobileDocuments(_ url: URL, for currentUser: String = NSUserName()) -> Bool {
262-
return url.standardizedFileURL.contains(components: ["Users", currentUser, "com~apple~CloudDocs"])
263-
}
264-
265-
private func showInFinder(url: URL) async {
266-
NSWorkspace.shared.activateFileViewerSelecting([url])
267-
}
268233
}
269234

270235
#if DEBUG
@@ -280,17 +245,17 @@ import SwiftUI
280245

281246
@available(macOS 15.0, *)
282247
func friendlyUrl(_ url: URL, for currentUser: String) -> String {
283-
target.friendlyUrl(url, for: currentUser)
248+
target.viewModel.friendlyUrl(url, for: currentUser)
284249
}
285250

286251
@available(macOS 15.0, *)
287252
func isInMobileDocuments(_ url: URL, for currentUser: String) -> Bool {
288-
target.isInMobileDocuments(url, for: currentUser)
253+
target.viewModel.isInMobileDocuments(url, for: currentUser)
289254
}
290255

291256
@available(macOS 15.0, *)
292257
func sanitize(url: URL) -> String {
293-
target.sanitize(url: url)
258+
target.viewModel.sanitize(url: url)
294259
}
295260
}
296261
}

MCMaps/Views/DocumentLaunchView/DocumentLaunchView+Mobile.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,43 @@ import SwiftUI
1313
/// This will generally be displayed when the app is first loaded.
1414
@available(iOS 18.0, *)
1515
struct DocumentLaunchView: Scene {
16-
@State private var creationContinuation: CheckedContinuation<CartographyMapFile?, any Error>?
17-
/// Whether the creation window should be visible.
18-
///
19-
/// Applicable to iOS and iPadOS.
20-
@Binding var displayCreationWindow: Bool
16+
typealias DocumentContinuation = CheckedContinuation<CartographyMapFile, any Error>
17+
@State private var creationContinuation: DocumentContinuation?
2118

22-
/// The proxy map used to create a file temporarily.
23-
///
24-
/// Applicable to iOS and iPadOS.
25-
@Binding var proxyMap: CartographyMap
19+
var viewModel: DocumentLaunchViewModel
2620

2721
var body: some Scene {
2822
DocumentGroupLaunchScene {
2923
NewDocumentButton("Create Map", for: CartographyMapFile.self) {
30-
try await withCheckedThrowingContinuation { continuation in
24+
try await withCheckedThrowingContinuation { (continuation: DocumentContinuation) in
3125
self.creationContinuation = continuation
32-
self.displayCreationWindow = true
26+
self.viewModel.displayCreationWindow.wrappedValue = true
3327
}
3428
}
35-
.sheet(isPresented: $displayCreationWindow) {
29+
.sheet(isPresented: viewModel.displayCreationWindow) {
3630
NavigationStack {
37-
MapCreatorForm(worldName: $proxyMap.name, mcVersion: $proxyMap.mcVersion, seed: $proxyMap.seed)
31+
MapCreatorForm(
32+
worldName: viewModel.proxyMap.name,
33+
mcVersion: viewModel.proxyMap.mcVersion,
34+
seed: viewModel.proxyMap.seed
35+
)
3836
.navigationTitle("Create Map")
3937
.toolbar {
4038
ToolbarItem(placement: .confirmationAction) {
4139
Button("Create") {
42-
creationContinuation?.resume(returning: .init(map: proxyMap))
40+
let file: CartographyMapFile = .init(map: viewModel.proxyMap.wrappedValue)
41+
creationContinuation?
42+
.resume(returning: file)
4343
creationContinuation = nil
44-
displayCreationWindow = false
44+
viewModel.displayCreationWindow.wrappedValue = false
4545
}
4646
}
4747

4848
ToolbarItem(placement: .cancellationAction) {
4949
Button("Cancel") {
5050
creationContinuation?.resume(throwing: CocoaError(CocoaError.userCancelled))
5151
creationContinuation = nil
52-
displayCreationWindow = false
52+
viewModel.displayCreationWindow.wrappedValue = false
5353
}
5454
}
5555
}
@@ -74,7 +74,7 @@ import SwiftUI
7474
self.target = target
7575
}
7676
@available(iOS 18.0, *)
77-
var creationContinuation: CheckedContinuation<CartographyMapFile?, any Error>? {
77+
var creationContinuation: DocumentContinuation? {
7878
target.creationContinuation
7979
}
8080
}

0 commit comments

Comments
 (0)