140 lines
4.1 KiB
Swift
140 lines
4.1 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
|
|
#if DEBUG
|
|
#error("debug builds are unsupported: build with mac/bundle.sh")
|
|
#endif
|
|
|
|
typealias ViewState = State
|
|
|
|
@MainActor
|
|
final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
private var quitting = false
|
|
|
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
Notifier.shared.install()
|
|
Updater.shared.start()
|
|
}
|
|
|
|
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
|
false
|
|
}
|
|
|
|
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
|
if flag {
|
|
return true
|
|
}
|
|
Servers.shared.openAll()
|
|
return false
|
|
}
|
|
|
|
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
|
let stores = Servers.shared.connections.values.compactMap(\.store)
|
|
guard !stores.isEmpty, !quitting else {
|
|
return .terminateNow
|
|
}
|
|
quitting = true
|
|
var replied = false
|
|
let reply = {
|
|
if !replied {
|
|
replied = true
|
|
sender.reply(toApplicationShouldTerminate: true)
|
|
}
|
|
}
|
|
Task {
|
|
await withTaskGroup(of: Void.self) { group in
|
|
for store in stores {
|
|
group.addTask {
|
|
await store.stop()
|
|
}
|
|
}
|
|
}
|
|
reply()
|
|
}
|
|
Task {
|
|
try? await Task.sleep(for: .seconds(2))
|
|
reply()
|
|
}
|
|
return .terminateLater
|
|
}
|
|
}
|
|
|
|
@main
|
|
struct ByreApp: App {
|
|
@NSApplicationDelegateAdaptor private var delegate: AppDelegate
|
|
|
|
var body: some Scene {
|
|
WindowGroup(id: "main", for: String.self) { $origin in
|
|
ServerWindow(origin: $origin)
|
|
}
|
|
.defaultSize(width: 1280, height: 820)
|
|
.restorationBehavior(.disabled)
|
|
.defaultLaunchBehavior(.presented)
|
|
.commands {
|
|
ByreCommands()
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ByreCommands: Commands {
|
|
@FocusedValue(\.serverWindow) private var focus
|
|
@Environment(\.openWindow) private var openWindow
|
|
|
|
var body: some Commands {
|
|
CommandGroup(after: .appInfo) {
|
|
Button("Check for Updates…") {
|
|
Updater.shared.check()
|
|
}
|
|
Button("Forget Server") {
|
|
focus?.forget?()
|
|
}
|
|
.disabled(focus?.forget == nil)
|
|
}
|
|
CommandGroup(replacing: .newItem) {
|
|
Button("New Server Window") {
|
|
openWindow(id: "main")
|
|
}
|
|
.keyboardShortcut("n")
|
|
Menu("Open Server") {
|
|
ForEach(Servers.shared.origins, id: \.self) { origin in
|
|
Button(URL(string: origin)?.host() ?? origin) {
|
|
openWindow(id: "main", value: origin)
|
|
}
|
|
}
|
|
}
|
|
.disabled(Servers.shared.origins.isEmpty)
|
|
}
|
|
PasteboardCommands()
|
|
CommandMenu("Go") {
|
|
goButton("Switch Task", "k")
|
|
goButton("Project", "p")
|
|
goButton("Lead", "l")
|
|
goButton("Merge", "s")
|
|
serverButton("Previous Server", "[", -1)
|
|
serverButton("Next Server", "]", 1)
|
|
Divider()
|
|
goButton("Keyboard Shortcuts", "/")
|
|
}
|
|
}
|
|
|
|
private func serverButton(_ label: String, _ key: Character, _ step: Int) -> some View {
|
|
Button(label) {
|
|
if let origin = Servers.shared.step(from: focus?.origin, by: step) {
|
|
openWindow(id: "main", value: origin)
|
|
}
|
|
}
|
|
.keyboardShortcut(KeyEquivalent(key))
|
|
.disabled(Servers.shared.origins.isEmpty)
|
|
}
|
|
|
|
private func goButton(_ label: String, _ key: String) -> some View {
|
|
Button(label) {
|
|
if let store = focus?.store, let overlays = focus?.overlays {
|
|
runGo(key, store, overlays)
|
|
}
|
|
}
|
|
.keyboardShortcut(KeyEquivalent(Character(key)))
|
|
.disabled(focus?.store == nil || focus?.overlays.dialogOpen == true)
|
|
}
|
|
}
|