feat: add menu bar mode
This commit is contained in:
@@ -276,7 +276,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 22;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -290,7 +290,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.1.10;
|
||||
MARKETING_VERSION = 1.1.11;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
||||
@@ -312,7 +312,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 22;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -326,7 +326,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.1.10;
|
||||
MARKETING_VERSION = 1.1.11;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
import Sparkle
|
||||
import PostHog
|
||||
|
||||
@main
|
||||
struct MeetingnotesApp: App {
|
||||
private let updaterController: SPUStandardUpdaterController
|
||||
@StateObject private var recordingSessionManager = RecordingSessionManager.shared
|
||||
|
||||
init() {
|
||||
updaterController = SPUStandardUpdaterController(updaterDelegate: nil, userDriverDelegate: nil)
|
||||
@@ -34,9 +36,10 @@ struct MeetingnotesApp: App {
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
WindowGroup("Meetingnotes", id: "main") {
|
||||
ContentView()
|
||||
.frame(minWidth: 700, minHeight: 400)
|
||||
.background(MainWindowConfigurator())
|
||||
}
|
||||
.windowResizability(.automatic)
|
||||
.defaultSize(width: 1000, height: 600)
|
||||
@@ -45,6 +48,123 @@ struct MeetingnotesApp: App {
|
||||
CheckForUpdatesView(updater: updaterController.updater)
|
||||
}
|
||||
}
|
||||
|
||||
MenuBarExtra {
|
||||
MeetingnotesMenu(
|
||||
recordingSessionManager: recordingSessionManager,
|
||||
updater: updaterController.updater
|
||||
)
|
||||
} label: {
|
||||
Image(systemName: recordingSessionManager.isRecording ? "record.circle.fill" : "waveform")
|
||||
.accessibilityLabel(recordingSessionManager.isRecording ? "Meetingnotes recording" : "Meetingnotes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct MeetingnotesMenu: View {
|
||||
@ObservedObject var recordingSessionManager: RecordingSessionManager
|
||||
let updater: SPUUpdater
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
|
||||
var body: some View {
|
||||
if recordingSessionManager.isRecording {
|
||||
Label("Recording", systemImage: "record.circle.fill")
|
||||
} else if recordingSessionManager.isProcessing {
|
||||
Label("Processing audio", systemImage: "hourglass")
|
||||
}
|
||||
|
||||
Button {
|
||||
MainWindowController.shared.show(using: openWindow)
|
||||
} label: {
|
||||
Label("Open Meetingnotes", systemImage: "macwindow")
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
Button {
|
||||
updater.checkForUpdates()
|
||||
} label: {
|
||||
Label("Check for Updates...", systemImage: "arrow.triangle.2.circlepath")
|
||||
}
|
||||
.keyboardShortcut("u")
|
||||
|
||||
Button {
|
||||
NSApplication.shared.terminate(nil)
|
||||
} label: {
|
||||
Label("Quit Meetingnotes", systemImage: "power")
|
||||
}
|
||||
.keyboardShortcut("q")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class MainWindowController: NSObject {
|
||||
static let shared = MainWindowController()
|
||||
|
||||
private weak var window: NSWindow?
|
||||
|
||||
func configure(_ window: NSWindow) {
|
||||
if self.window !== window {
|
||||
if let previousWindow = self.window {
|
||||
NotificationCenter.default.removeObserver(
|
||||
self,
|
||||
name: NSWindow.willMiniaturizeNotification,
|
||||
object: previousWindow
|
||||
)
|
||||
}
|
||||
self.window = window
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(windowWillMiniaturize(_:)),
|
||||
name: NSWindow.willMiniaturizeNotification,
|
||||
object: window
|
||||
)
|
||||
}
|
||||
|
||||
if let minimizeButton = window.standardWindowButton(.miniaturizeButton) {
|
||||
minimizeButton.target = self
|
||||
minimizeButton.action = #selector(hideWindow(_:))
|
||||
}
|
||||
}
|
||||
|
||||
func show(using openWindow: OpenWindowAction) {
|
||||
if let window {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
} else {
|
||||
openWindow(id: "main")
|
||||
}
|
||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
@objc private func hideWindow(_ sender: NSButton) {
|
||||
sender.window?.orderOut(nil)
|
||||
}
|
||||
|
||||
@objc private func windowWillMiniaturize(_ notification: Notification) {
|
||||
guard let window = notification.object as? NSWindow else { return }
|
||||
DispatchQueue.main.async {
|
||||
window.deminiaturize(nil)
|
||||
window.orderOut(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct MainWindowConfigurator: NSViewRepresentable {
|
||||
func makeNSView(context: Context) -> NSView {
|
||||
let view = NSView()
|
||||
configureWindow(for: view)
|
||||
return view
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: NSView, context: Context) {
|
||||
configureWindow(for: nsView)
|
||||
}
|
||||
|
||||
private func configureWindow(for view: NSView) {
|
||||
DispatchQueue.main.async {
|
||||
guard let window = view.window else { return }
|
||||
MainWindowController.shared.configure(window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user