-
-
Notifications
You must be signed in to change notification settings - Fork 295
/
AppState.swift
142 lines (117 loc) Β· 3.48 KB
/
AppState.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import SwiftUI
import UserNotifications
import DockProgress
@MainActor
@Observable
final class AppState {
static let shared = AppState()
var navigationPath = [Route]()
var isFileImporterPresented = false
// TODO: This can be inferred by checking the last element of navigationPath.
var isConverting = false
var error: Error?
init() {
DockProgress.style = .squircle(color: .white.withAlphaComponent(0.7))
DispatchQueue.main.async { [self] in
didLaunch()
}
}
private func didLaunch() {
NSApp.servicesProvider = self
// We have to include `.badge` otherwise system settings does not show the checkbox to turn off sounds. (macOS 12.4)
UNUserNotificationCenter.current().requestAuthorization(options: [.sound, .badge]) { _, _ in }
}
func start(_ url: URL) {
_ = url.startAccessingSecurityScopedResource()
// We have to nil it out first and dispatch, otherwise it shows the old video. (macOS 14.3)
navigationPath = []
Task { @MainActor [self] in
do {
// TODO: Simplify the validator.
let (asset, metadata) = try await VideoValidator.validate(url)
navigationPath = [.edit(url, asset, metadata)]
} catch {
self.error = error
}
}
}
/**
Returns `nil` if it should not continue.
*/
fileprivate func extractSharedVideoUrlIfAny(from url: URL) -> URL? {
guard url.host == "shareExtension" else {
return url
}
guard
let path = url.queryDictionary["path"],
let appGroupShareVideoUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Shared.videoShareGroupIdentifier)?.appendingPathComponent(path, isDirectory: false)
else {
NSAlert.showModal(
for: SSApp.swiftUIMainWindow,
title: "Could not retrieve the shared video."
)
return nil
}
return appGroupShareVideoUrl
}
}
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
// Set launch completions option if the notification center could not be set up already.
LaunchCompletions.applicationDidLaunch()
}
// TODO: Try to migrate to `.onOpenURL` when targeting macOS 15.
func application(_ application: NSApplication, open urls: [URL]) {
guard
urls.count == 1,
let videoUrl = urls.first
else {
NSAlert.showModal(
for: SSApp.swiftUIMainWindow,
title: "Gifski can only convert a single file at the time."
)
return
}
guard let videoUrl2 = AppState.shared.extractSharedVideoUrlIfAny(from: videoUrl) else {
return
}
// Start video conversion on launch
LaunchCompletions.add {
AppState.shared.start(videoUrl2)
}
}
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
if AppState.shared.isConverting {
let response = NSAlert.showModal(
for: SSApp.swiftUIMainWindow,
title: "Do you want to continue converting?",
message: "Gifski is currently converting a video. If you quit, the conversion will be cancelled.",
buttonTitles: [
"Continue",
"Quit"
]
)
if response == .alertFirstButtonReturn {
return .terminateCancel
}
}
return .terminateNow
}
func applicationWillTerminate(_ notification: Notification) {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}
}
extension AppState {
/**
This is called from NSApp as a service resolver.
*/
@objc
func convertToGIF(_ pasteboard: NSPasteboard, userData: String, error: NSErrorPointer) {
guard let url = pasteboard.fileURLs().first else {
return
}
Task { @MainActor in
start(url)
}
}
}