Bez popisu

AppDelegate.swift 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. //
  2. // AppDelegate.swift
  3. // App for Indeed
  4. //
  5. // Created by Mql Mac 2 on 11/05/2026.
  6. //
  7. import Cocoa
  8. enum AppWindowConfiguration {
  9. static let defaultContentSize = NSSize(width: 1120, height: 750)
  10. static let minimumContentSize = NSSize(width: 1120, height: 750)
  11. @MainActor
  12. static func apply(to window: NSWindow) {
  13. window.minSize = minimumContentSize
  14. window.isRestorable = false
  15. window.title = "App for Indeed"
  16. window.styleMask.insert(.fullSizeContentView)
  17. window.titlebarAppearsTransparent = true
  18. window.titleVisibility = .hidden
  19. window.isMovableByWindowBackground = true
  20. let targetContent = NSRect(origin: .zero, size: defaultContentSize)
  21. let targetFrame = window.frameRect(forContentRect: targetContent)
  22. var frame = targetFrame
  23. let current = window.frame
  24. frame.origin.x = current.midX - frame.width / 2
  25. frame.origin.y = current.midY - frame.height / 2
  26. window.setFrame(frame, display: false, animate: false)
  27. window.setContentSize(defaultContentSize)
  28. }
  29. @MainActor
  30. static func mainWindow(in app: NSApplication = .shared) -> NSWindow? {
  31. app.mainWindow
  32. ?? app.keyWindow
  33. ?? app.windows.first(where: { $0.isVisible && $0.canBecomeKey })
  34. }
  35. }
  36. @main
  37. class AppDelegate: NSObject, NSApplicationDelegate {
  38. /// Avoids hammering StoreKit when `didBecomeActive` fires in quick succession (e.g. after system sheets).
  39. private var lastSubscriptionRefreshAt: Date?
  40. func applicationWillFinishLaunching(_ notification: Notification) {
  41. // Dashboard is light-themed; without this, a Dark Mode Mac paints a dark title bar.
  42. NSApp.appearance = NSAppearance(named: .aqua)
  43. }
  44. func applicationDidFinishLaunching(_ aNotification: Notification) {
  45. NotificationCenter.default.addObserver(
  46. forName: NSApplication.didBecomeActiveNotification,
  47. object: nil,
  48. queue: .main
  49. ) { [weak self] _ in
  50. Task { @MainActor in
  51. guard let self else { return }
  52. let now = Date()
  53. if let last = self.lastSubscriptionRefreshAt, now.timeIntervalSince(last) < 2.5 {
  54. return
  55. }
  56. self.lastSubscriptionRefreshAt = now
  57. await SubscriptionStore.shared.refreshEntitlements(deep: true)
  58. }
  59. }
  60. Task { @MainActor in
  61. // Do not call `AppStore.sync()` here — it prompts "Sign in with Apple Account" in Xcode / StoreKit
  62. // testing and can repeat when the app re-activates after dismissing the sheet. Sync only from
  63. // explicit "Restore purchases" in `SubscriptionStore.restorePurchases()`.
  64. lastSubscriptionRefreshAt = Date()
  65. await SubscriptionStore.shared.refreshEntitlements(deep: true)
  66. NotificationCenter.default.post(name: .subscriptionStatusDidChange, object: nil)
  67. }
  68. NSApp.activate(ignoringOtherApps: true)
  69. applyDefaultWindowSize()
  70. }
  71. @MainActor
  72. private func applyDefaultWindowSize() {
  73. DispatchQueue.main.async { [weak self] in
  74. guard let self, let window = AppWindowConfiguration.mainWindow() else { return }
  75. AppWindowConfiguration.apply(to: window)
  76. window.center()
  77. window.makeKeyAndOrderFront(nil)
  78. // Layout can run after the first pass; enforce default size once more.
  79. DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
  80. guard self != nil, let window = AppWindowConfiguration.mainWindow() else { return }
  81. AppWindowConfiguration.apply(to: window)
  82. window.center()
  83. }
  84. }
  85. }
  86. func applicationWillTerminate(_ aNotification: Notification) {
  87. // Insert code here to tear down your application
  88. }
  89. func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
  90. // Opt out until real `NSWindowRestoration` is implemented (avoids null className restore logs).
  91. return false
  92. }
  93. }