|
@@ -398,8 +398,8 @@ final class ViewController: NSViewController {
|
|
|
onToggleDarkMode: { [weak self] enabled in
|
|
onToggleDarkMode: { [weak self] enabled in
|
|
|
self?.setDarkMode(enabled)
|
|
self?.setDarkMode(enabled)
|
|
|
},
|
|
},
|
|
|
- onAction: { [weak self] action in
|
|
|
|
|
- self?.handleSettingsAction(action)
|
|
|
|
|
|
|
+ onAction: { [weak self] action, sourceView, clickPoint in
|
|
|
|
|
+ self?.handleSettingsAction(action, sourceView: sourceView, clickLocationInSourceView: clickPoint)
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
return popover
|
|
return popover
|
|
@@ -763,7 +763,7 @@ private extension ViewController {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private func shareAppFromSettingsMenu() {
|
|
|
|
|
|
|
+ private func shareAppFromSettingsMenu(sourceView: NSView? = nil, clickLocationInSourceView: NSPoint? = nil) {
|
|
|
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
|
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
|
|
?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
|
|
?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
|
|
|
?? "Meetings App"
|
|
?? "Meetings App"
|
|
@@ -772,8 +772,11 @@ private extension ViewController {
|
|
|
let shareItems: [Any] = appURL.map { [message, $0] } ?? [message]
|
|
let shareItems: [Any] = appURL.map { [message, $0] } ?? [message]
|
|
|
|
|
|
|
|
let picker = NSSharingServicePicker(items: shareItems)
|
|
let picker = NSSharingServicePicker(items: shareItems)
|
|
|
- let anchorView = sidebarRowViews[.settings] ?? view
|
|
|
|
|
- picker.show(relativeTo: anchorView.bounds, of: anchorView, preferredEdge: .maxX)
|
|
|
|
|
|
|
+ let anchorView = sourceView ?? sidebarRowViews[.settings] ?? view
|
|
|
|
|
+ let anchorPoint = clickLocationInSourceView
|
|
|
|
|
+ ?? NSPoint(x: anchorView.bounds.midX, y: anchorView.bounds.midY)
|
|
|
|
|
+ let anchorRect = NSRect(x: anchorPoint.x, y: anchorPoint.y, width: 1, height: 1)
|
|
|
|
|
+ picker.show(relativeTo: anchorRect, of: anchorView, preferredEdge: .minY)
|
|
|
|
|
|
|
|
let clipboardText = ([message, appURL?.absoluteString].compactMap { $0 }).joined(separator: "\n")
|
|
let clipboardText = ([message, appURL?.absoluteString].compactMap { $0 }).joined(separator: "\n")
|
|
|
NSPasteboard.general.clearContents()
|
|
NSPasteboard.general.clearContents()
|
|
@@ -855,7 +858,7 @@ private extension ViewController {
|
|
|
showSidebarPage(selectedSidebarPage)
|
|
showSidebarPage(selectedSidebarPage)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private func handleSettingsAction(_ action: SettingsAction) {
|
|
|
|
|
|
|
+ private func handleSettingsAction(_ action: SettingsAction, sourceView: NSView? = nil, clickLocationInSourceView: NSPoint? = nil) {
|
|
|
switch action {
|
|
switch action {
|
|
|
case .restore:
|
|
case .restore:
|
|
|
Task { [weak self] in
|
|
Task { [weak self] in
|
|
@@ -878,7 +881,7 @@ private extension ViewController {
|
|
|
openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
}
|
|
}
|
|
|
case .shareApp:
|
|
case .shareApp:
|
|
|
- shareAppFromSettingsMenu()
|
|
|
|
|
|
|
+ shareAppFromSettingsMenu(sourceView: sourceView, clickLocationInSourceView: clickLocationInSourceView)
|
|
|
case .upgrade:
|
|
case .upgrade:
|
|
|
showPaywall(upgradeFlow: true, preferredPlan: .lifetime)
|
|
showPaywall(upgradeFlow: true, preferredPlan: .lifetime)
|
|
|
}
|
|
}
|
|
@@ -1228,13 +1231,9 @@ private extension ViewController {
|
|
|
premiumUpgradeRatingPromptWorkItem?.cancel()
|
|
premiumUpgradeRatingPromptWorkItem?.cancel()
|
|
|
refreshPaywallStoreUI()
|
|
refreshPaywallStoreUI()
|
|
|
refreshScheduleCardsForPremiumStateChange()
|
|
refreshScheduleCardsForPremiumStateChange()
|
|
|
-
|
|
|
|
|
- pageCache[.joinMeetings] = nil
|
|
|
|
|
- pageCache[.video] = nil
|
|
|
|
|
- if selectedSidebarPage == .joinMeetings {
|
|
|
|
|
- showSidebarPage(.joinMeetings)
|
|
|
|
|
- } else if selectedSidebarPage == .video {
|
|
|
|
|
- showSidebarPage(.video)
|
|
|
|
|
|
|
+ refreshPagesAfterPremiumStateUpdate()
|
|
|
|
|
+ Task { [weak self] in
|
|
|
|
|
+ await self?.loadSchedule()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if !hadPremiumAccess && hasPremiumAccess {
|
|
if !hadPremiumAccess && hasPremiumAccess {
|
|
@@ -1254,6 +1253,14 @@ private extension ViewController {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private func refreshPagesAfterPremiumStateUpdate() {
|
|
|
|
|
+ pageCache[.joinMeetings] = nil
|
|
|
|
|
+ pageCache[.photo] = nil
|
|
|
|
|
+ pageCache[.video] = nil
|
|
|
|
|
+ pageCache[.settings] = nil
|
|
|
|
|
+ showSidebarPage(selectedSidebarPage)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private var userHasRated: Bool {
|
|
private var userHasRated: Bool {
|
|
|
UserDefaults.standard.bool(forKey: userHasRatedDefaultsKey)
|
|
UserDefaults.standard.bool(forKey: userHasRatedDefaultsKey)
|
|
|
}
|
|
}
|
|
@@ -1426,6 +1433,10 @@ private extension ViewController {
|
|
|
self.refreshPaywallStoreUI()
|
|
self.refreshPaywallStoreUI()
|
|
|
switch result {
|
|
switch result {
|
|
|
case .success:
|
|
case .success:
|
|
|
|
|
+ self.refreshPagesAfterPremiumStateUpdate()
|
|
|
|
|
+ Task { [weak self] in
|
|
|
|
|
+ await self?.loadSchedule()
|
|
|
|
|
+ }
|
|
|
self.showSimpleAlert(title: "Purchase Complete", message: "Premium has been unlocked successfully.")
|
|
self.showSimpleAlert(title: "Purchase Complete", message: "Premium has been unlocked successfully.")
|
|
|
self.paywallWindow?.performClose(nil)
|
|
self.paywallWindow?.performClose(nil)
|
|
|
self.scheduleRatingPromptAfterPremiumUpgrade()
|
|
self.scheduleRatingPromptAfterPremiumUpgrade()
|
|
@@ -1583,7 +1594,14 @@ private extension ViewController {
|
|
|
let shareButton = makeSettingsActionButton(icon: "⤴︎", title: "Share App", action: .shareApp)
|
|
let shareButton = makeSettingsActionButton(icon: "⤴︎", title: "Share App", action: .shareApp)
|
|
|
stack.addArrangedSubview(shareButton)
|
|
stack.addArrangedSubview(shareButton)
|
|
|
shareButton.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
shareButton.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
- stack.setCustomSpacing(24, after: shareButton)
|
|
|
|
|
|
|
+ if storeKitCoordinator.hasPremiumAccess && !storeKitCoordinator.hasLifetimeAccess {
|
|
|
|
|
+ let upgradeButton = makeSettingsActionButton(icon: "⬆︎", title: "Upgrade", action: .upgrade)
|
|
|
|
|
+ stack.addArrangedSubview(upgradeButton)
|
|
|
|
|
+ upgradeButton.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
|
|
+ stack.setCustomSpacing(24, after: upgradeButton)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ stack.setCustomSpacing(24, after: shareButton)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
let legalTitle = textLabel("Help & Legal", font: typography.joinWithURLTitle, color: palette.textPrimary)
|
|
let legalTitle = textLabel("Help & Legal", font: typography.joinWithURLTitle, color: palette.textPrimary)
|
|
|
stack.addArrangedSubview(legalTitle)
|
|
stack.addArrangedSubview(legalTitle)
|
|
@@ -1596,12 +1614,6 @@ private extension ViewController {
|
|
|
let termsButton = makeSettingsActionButton(icon: "📄", title: "Terms of Services", action: .termsOfServices)
|
|
let termsButton = makeSettingsActionButton(icon: "📄", title: "Terms of Services", action: .termsOfServices)
|
|
|
stack.addArrangedSubview(termsButton)
|
|
stack.addArrangedSubview(termsButton)
|
|
|
termsButton.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
termsButton.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
- if storeKitCoordinator.hasPremiumAccess && !storeKitCoordinator.hasLifetimeAccess {
|
|
|
|
|
- let upgradeButton = makeSettingsActionButton(icon: "⬆︎", title: "Upgrade", action: .upgrade)
|
|
|
|
|
- stack.addArrangedSubview(upgradeButton)
|
|
|
|
|
- upgradeButton.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
scroll.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
|
|
scroll.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
|
|
|
scroll.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
|
|
scroll.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
|
|
@@ -1725,7 +1737,14 @@ private extension ViewController {
|
|
|
|
|
|
|
|
@objc private func settingsPageActionButtonClicked(_ sender: NSButton) {
|
|
@objc private func settingsPageActionButtonClicked(_ sender: NSButton) {
|
|
|
guard let action = SettingsAction(rawValue: sender.tag) else { return }
|
|
guard let action = SettingsAction(rawValue: sender.tag) else { return }
|
|
|
- handleSettingsAction(action)
|
|
|
|
|
|
|
+ let clickPoint: NSPoint?
|
|
|
|
|
+ if let event = NSApp.currentEvent {
|
|
|
|
|
+ let pointInWindow = event.locationInWindow
|
|
|
|
|
+ clickPoint = sender.convert(pointInWindow, from: nil)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ clickPoint = nil
|
|
|
|
|
+ }
|
|
|
|
|
+ handleSettingsAction(action, sourceView: sender, clickLocationInSourceView: clickPoint)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@objc private func settingsGoogleActionButtonClicked(_ sender: NSButton) {
|
|
@objc private func settingsGoogleActionButtonClicked(_ sender: NSButton) {
|
|
@@ -4503,7 +4522,7 @@ private final class SettingsMenuViewController: NSViewController {
|
|
|
private let palette: Palette
|
|
private let palette: Palette
|
|
|
private let typography: Typography
|
|
private let typography: Typography
|
|
|
private let onToggleDarkMode: (Bool) -> Void
|
|
private let onToggleDarkMode: (Bool) -> Void
|
|
|
- private let onAction: (SettingsAction) -> Void
|
|
|
|
|
|
|
+ private let onAction: (SettingsAction, NSView?, NSPoint?) -> Void
|
|
|
|
|
|
|
|
private var darkToggle: NSSwitch?
|
|
private var darkToggle: NSSwitch?
|
|
|
|
|
|
|
@@ -4514,7 +4533,7 @@ private final class SettingsMenuViewController: NSViewController {
|
|
|
showRateUsInSettings: Bool,
|
|
showRateUsInSettings: Bool,
|
|
|
showUpgradeInSettings: Bool,
|
|
showUpgradeInSettings: Bool,
|
|
|
onToggleDarkMode: @escaping (Bool) -> Void,
|
|
onToggleDarkMode: @escaping (Bool) -> Void,
|
|
|
- onAction: @escaping (SettingsAction) -> Void
|
|
|
|
|
|
|
+ onAction: @escaping (SettingsAction, NSView?, NSPoint?) -> Void
|
|
|
) {
|
|
) {
|
|
|
self.palette = palette
|
|
self.palette = palette
|
|
|
self.typography = typography
|
|
self.typography = typography
|
|
@@ -4690,7 +4709,14 @@ private final class SettingsMenuViewController: NSViewController {
|
|
|
|
|
|
|
|
@objc private func settingsActionButtonPressed(_ sender: NSButton) {
|
|
@objc private func settingsActionButtonPressed(_ sender: NSButton) {
|
|
|
guard let action = SettingsAction(rawValue: sender.tag) else { return }
|
|
guard let action = SettingsAction(rawValue: sender.tag) else { return }
|
|
|
- onAction(action)
|
|
|
|
|
|
|
+ let clickPoint: NSPoint?
|
|
|
|
|
+ if let event = NSApp.currentEvent {
|
|
|
|
|
+ let pointInWindow = event.locationInWindow
|
|
|
|
|
+ clickPoint = sender.convert(pointInWindow, from: nil)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ clickPoint = nil
|
|
|
|
|
+ }
|
|
|
|
|
+ onAction(action, sender, clickPoint)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|