|
|
@@ -334,6 +334,10 @@ final class ViewController: NSViewController {
|
|
334
|
334
|
private var scheduleGoogleAuthHostPadHeightConstraint: NSLayoutConstraint?
|
|
335
|
335
|
private var scheduleGoogleAuthButtonWidthConstraint: NSLayoutConstraint?
|
|
336
|
336
|
private var scheduleGoogleAuthButtonHeightConstraint: NSLayoutConstraint?
|
|
|
337
|
+ private weak var settingsReminderMasterSwitch: NSSwitch?
|
|
|
338
|
+ private weak var settingsReminder1DaySwitch: NSSwitch?
|
|
|
339
|
+ private weak var settingsReminder12HoursSwitch: NSSwitch?
|
|
|
340
|
+ private weak var settingsReminder1HourSwitch: NSSwitch?
|
|
337
|
341
|
/// Circular avatar size when signed in (top-right, Google-style).
|
|
338
|
342
|
private let scheduleGoogleSignedInAvatarSize: CGFloat = 36
|
|
339
|
343
|
private var scheduleGoogleAuthHovering = false
|
|
|
@@ -1465,8 +1469,13 @@ private extension ViewController {
|
|
1465
|
1469
|
if hasCompletedInitialStoreKitSync {
|
|
1466
|
1470
|
scheduleRatingPromptAfterPremiumUpgrade()
|
|
1467
|
1471
|
}
|
|
|
1472
|
+ MeetingReminderManager.shared.requestPermissionIfNeeded { [weak self] _ in
|
|
|
1473
|
+ guard let self else { return }
|
|
|
1474
|
+ MeetingReminderManager.shared.scheduleReminders(for: self.scheduleCachedMeetings)
|
|
|
1475
|
+ }
|
|
1468
|
1476
|
}
|
|
1469
|
1477
|
if hadPremiumAccess && !hasPremiumAccess {
|
|
|
1478
|
+ MeetingReminderManager.shared.cancelAllReminders()
|
|
1470
|
1479
|
showPaywall()
|
|
1471
|
1480
|
}
|
|
1472
|
1481
|
}
|
|
|
@@ -1833,6 +1842,15 @@ private extension ViewController {
|
|
1833
|
1842
|
stack.setCustomSpacing(24, after: shareButton)
|
|
1834
|
1843
|
}
|
|
1835
|
1844
|
|
|
|
1845
|
+ if storeKitCoordinator.hasPremiumAccess {
|
|
|
1846
|
+ let notificationsTitle = textLabel("Notifications", font: typography.joinWithURLTitle, color: palette.textPrimary)
|
|
|
1847
|
+ stack.addArrangedSubview(notificationsTitle)
|
|
|
1848
|
+ let remindersSection = makeSettingsRemindersSection()
|
|
|
1849
|
+ stack.addArrangedSubview(remindersSection)
|
|
|
1850
|
+ remindersSection.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
1851
|
+ stack.setCustomSpacing(24, after: remindersSection)
|
|
|
1852
|
+ }
|
|
|
1853
|
+
|
|
1836
|
1854
|
let legalTitle = textLabel("Help & Legal", font: typography.joinWithURLTitle, color: palette.textPrimary)
|
|
1837
|
1855
|
stack.addArrangedSubview(legalTitle)
|
|
1838
|
1856
|
let privacyButton = makeSettingsActionButton(icon: "🔒", title: "Privacy Policy", action: .privacyPolicy)
|
|
|
@@ -1901,6 +1919,130 @@ private extension ViewController {
|
|
1901
|
1919
|
return row
|
|
1902
|
1920
|
}
|
|
1903
|
1921
|
|
|
|
1922
|
+ private func makeSettingsRemindersSection() -> NSView {
|
|
|
1923
|
+ let container = NSStackView()
|
|
|
1924
|
+ container.translatesAutoresizingMaskIntoConstraints = false
|
|
|
1925
|
+ container.orientation = .vertical
|
|
|
1926
|
+ container.spacing = 1
|
|
|
1927
|
+ container.alignment = .leading
|
|
|
1928
|
+
|
|
|
1929
|
+ let masterRow = makeReminderToggleRow(
|
|
|
1930
|
+ icon: "🔔",
|
|
|
1931
|
+ title: "Reminders",
|
|
|
1932
|
+ state: ReminderPreferences.remindersEnabled,
|
|
|
1933
|
+ action: #selector(settingsReminderMasterToggled(_:)),
|
|
|
1934
|
+ isSubRow: false
|
|
|
1935
|
+ )
|
|
|
1936
|
+ container.addArrangedSubview(masterRow)
|
|
|
1937
|
+ masterRow.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true
|
|
|
1938
|
+
|
|
|
1939
|
+ let subAlpha: CGFloat = ReminderPreferences.remindersEnabled ? 1.0 : 0.4
|
|
|
1940
|
+
|
|
|
1941
|
+ let day1Row = makeReminderToggleRow(
|
|
|
1942
|
+ icon: "📅",
|
|
|
1943
|
+ title: "1 Day Before",
|
|
|
1944
|
+ state: ReminderPreferences.remind1Day,
|
|
|
1945
|
+ action: #selector(settingsReminder1DayToggled(_:)),
|
|
|
1946
|
+ isSubRow: true
|
|
|
1947
|
+ )
|
|
|
1948
|
+ day1Row.alphaValue = subAlpha
|
|
|
1949
|
+ container.addArrangedSubview(day1Row)
|
|
|
1950
|
+ day1Row.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true
|
|
|
1951
|
+
|
|
|
1952
|
+ let hours12Row = makeReminderToggleRow(
|
|
|
1953
|
+ icon: "🕛",
|
|
|
1954
|
+ title: "12 Hours Before",
|
|
|
1955
|
+ state: ReminderPreferences.remind12Hours,
|
|
|
1956
|
+ action: #selector(settingsReminder12HoursToggled(_:)),
|
|
|
1957
|
+ isSubRow: true
|
|
|
1958
|
+ )
|
|
|
1959
|
+ hours12Row.alphaValue = subAlpha
|
|
|
1960
|
+ container.addArrangedSubview(hours12Row)
|
|
|
1961
|
+ hours12Row.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true
|
|
|
1962
|
+
|
|
|
1963
|
+ let hour1Row = makeReminderToggleRow(
|
|
|
1964
|
+ icon: "⏰",
|
|
|
1965
|
+ title: "1 Hour Before",
|
|
|
1966
|
+ state: ReminderPreferences.remind1Hour,
|
|
|
1967
|
+ action: #selector(settingsReminder1HourToggled(_:)),
|
|
|
1968
|
+ isSubRow: true
|
|
|
1969
|
+ )
|
|
|
1970
|
+ hour1Row.alphaValue = subAlpha
|
|
|
1971
|
+ container.addArrangedSubview(hour1Row)
|
|
|
1972
|
+ hour1Row.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true
|
|
|
1973
|
+
|
|
|
1974
|
+ settingsReminderMasterSwitch = masterRow.subviews.compactMap { $0 as? NSSwitch }.first
|
|
|
1975
|
+ settingsReminder1DaySwitch = day1Row.subviews.compactMap { $0 as? NSSwitch }.first
|
|
|
1976
|
+ settingsReminder12HoursSwitch = hours12Row.subviews.compactMap { $0 as? NSSwitch }.first
|
|
|
1977
|
+ settingsReminder1HourSwitch = hour1Row.subviews.compactMap { $0 as? NSSwitch }.first
|
|
|
1978
|
+
|
|
|
1979
|
+ return container
|
|
|
1980
|
+ }
|
|
|
1981
|
+
|
|
|
1982
|
+ private func makeReminderToggleRow(icon: String, title: String, state: Bool, action: Selector, isSubRow: Bool) -> NSView {
|
|
|
1983
|
+ let row = roundedContainer(cornerRadius: 10, color: isSubRow ? palette.inputBackground.withAlphaComponent(0.6) : palette.inputBackground)
|
|
|
1984
|
+ row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
1985
|
+ row.heightAnchor.constraint(equalToConstant: 48).isActive = true
|
|
|
1986
|
+ styleSurface(row, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
1987
|
+
|
|
|
1988
|
+ let iconLabel = textLabel(icon, font: NSFont.systemFont(ofSize: isSubRow ? 15 : 17, weight: .medium), color: palette.textPrimary)
|
|
|
1989
|
+ let titleLabel = textLabel(title, font: NSFont.systemFont(ofSize: isSubRow ? 14 : 15, weight: isSubRow ? .regular : .semibold), color: palette.textPrimary)
|
|
|
1990
|
+ if isSubRow {
|
|
|
1991
|
+ titleLabel.textColor = palette.textSecondary
|
|
|
1992
|
+ }
|
|
|
1993
|
+
|
|
|
1994
|
+ let toggle = NSSwitch()
|
|
|
1995
|
+ toggle.translatesAutoresizingMaskIntoConstraints = false
|
|
|
1996
|
+ toggle.state = state ? .on : .off
|
|
|
1997
|
+ toggle.target = self
|
|
|
1998
|
+ toggle.action = action
|
|
|
1999
|
+
|
|
|
2000
|
+ row.addSubview(iconLabel)
|
|
|
2001
|
+ row.addSubview(titleLabel)
|
|
|
2002
|
+ row.addSubview(toggle)
|
|
|
2003
|
+ NSLayoutConstraint.activate([
|
|
|
2004
|
+ iconLabel.leadingAnchor.constraint(equalTo: row.leadingAnchor, constant: isSubRow ? 30 : 14),
|
|
|
2005
|
+ iconLabel.centerYAnchor.constraint(equalTo: row.centerYAnchor),
|
|
|
2006
|
+ titleLabel.leadingAnchor.constraint(equalTo: iconLabel.trailingAnchor, constant: 10),
|
|
|
2007
|
+ titleLabel.centerYAnchor.constraint(equalTo: row.centerYAnchor),
|
|
|
2008
|
+ toggle.trailingAnchor.constraint(equalTo: row.trailingAnchor, constant: -14),
|
|
|
2009
|
+ toggle.centerYAnchor.constraint(equalTo: row.centerYAnchor)
|
|
|
2010
|
+ ])
|
|
|
2011
|
+ return row
|
|
|
2012
|
+ }
|
|
|
2013
|
+
|
|
|
2014
|
+ @objc private func settingsReminderMasterToggled(_ sender: NSSwitch) {
|
|
|
2015
|
+ let enabled = sender.state == .on
|
|
|
2016
|
+ ReminderPreferences.remindersEnabled = enabled
|
|
|
2017
|
+ let subAlpha: CGFloat = enabled ? 1.0 : 0.4
|
|
|
2018
|
+ settingsReminder1DaySwitch?.superview?.alphaValue = subAlpha
|
|
|
2019
|
+ settingsReminder12HoursSwitch?.superview?.alphaValue = subAlpha
|
|
|
2020
|
+ settingsReminder1HourSwitch?.superview?.alphaValue = subAlpha
|
|
|
2021
|
+ if enabled {
|
|
|
2022
|
+ MeetingReminderManager.shared.requestPermissionIfNeeded { [weak self] _ in
|
|
|
2023
|
+ guard let self else { return }
|
|
|
2024
|
+ MeetingReminderManager.shared.scheduleReminders(for: self.scheduleCachedMeetings)
|
|
|
2025
|
+ }
|
|
|
2026
|
+ } else {
|
|
|
2027
|
+ MeetingReminderManager.shared.cancelAllReminders()
|
|
|
2028
|
+ }
|
|
|
2029
|
+ }
|
|
|
2030
|
+
|
|
|
2031
|
+ @objc private func settingsReminder1DayToggled(_ sender: NSSwitch) {
|
|
|
2032
|
+ ReminderPreferences.remind1Day = sender.state == .on
|
|
|
2033
|
+ MeetingReminderManager.shared.scheduleReminders(for: scheduleCachedMeetings)
|
|
|
2034
|
+ }
|
|
|
2035
|
+
|
|
|
2036
|
+ @objc private func settingsReminder12HoursToggled(_ sender: NSSwitch) {
|
|
|
2037
|
+ ReminderPreferences.remind12Hours = sender.state == .on
|
|
|
2038
|
+ MeetingReminderManager.shared.scheduleReminders(for: scheduleCachedMeetings)
|
|
|
2039
|
+ }
|
|
|
2040
|
+
|
|
|
2041
|
+ @objc private func settingsReminder1HourToggled(_ sender: NSSwitch) {
|
|
|
2042
|
+ ReminderPreferences.remind1Hour = sender.state == .on
|
|
|
2043
|
+ MeetingReminderManager.shared.scheduleReminders(for: scheduleCachedMeetings)
|
|
|
2044
|
+ }
|
|
|
2045
|
+
|
|
1904
|
2046
|
private func makeSettingsGoogleAccountRow() -> NSView {
|
|
1905
|
2047
|
let row = roundedContainer(cornerRadius: 10, color: palette.inputBackground)
|
|
1906
|
2048
|
row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -6503,6 +6645,7 @@ private extension ViewController {
|
|
6503
|
6645
|
renderScheduleCards(into: stack, meetings: [])
|
|
6504
|
6646
|
}
|
|
6505
|
6647
|
scheduleCachedMeetings = []
|
|
|
6648
|
+ MeetingReminderManager.shared.cancelAllReminders()
|
|
6506
|
6649
|
applySchedulePageFiltersAndRender()
|
|
6507
|
6650
|
if calendarPageGridStack != nil {
|
|
6508
|
6651
|
calendarPageMonthLabel?.stringValue = calendarMonthTitleText(for: calendarPageMonthAnchor)
|
|
|
@@ -6526,6 +6669,9 @@ private extension ViewController {
|
|
6526
|
6669
|
renderScheduleCards(into: stack, meetings: filtered)
|
|
6527
|
6670
|
}
|
|
6528
|
6671
|
scheduleCachedMeetings = meetings
|
|
|
6672
|
+ if storeKitCoordinator.hasPremiumAccess {
|
|
|
6673
|
+ MeetingReminderManager.shared.scheduleReminders(for: meetings)
|
|
|
6674
|
+ }
|
|
6529
|
6675
|
applySchedulePageFiltersAndRender()
|
|
6530
|
6676
|
if calendarPageGridStack != nil {
|
|
6531
|
6677
|
calendarPageMonthLabel?.stringValue = calendarMonthTitleText(for: calendarPageMonthAnchor)
|
|
|
@@ -6545,6 +6691,7 @@ private extension ViewController {
|
|
6545
|
6691
|
renderScheduleCards(into: stack, meetings: [])
|
|
6546
|
6692
|
}
|
|
6547
|
6693
|
scheduleCachedMeetings = []
|
|
|
6694
|
+ MeetingReminderManager.shared.cancelAllReminders()
|
|
6548
|
6695
|
applySchedulePageFiltersAndRender()
|
|
6549
|
6696
|
if calendarPageGridStack != nil {
|
|
6550
|
6697
|
calendarPageMonthLabel?.stringValue = calendarMonthTitleText(for: calendarPageMonthAnchor)
|
|
|
@@ -6648,6 +6795,7 @@ private extension ViewController {
|
|
6648
|
6795
|
|
|
6649
|
6796
|
private func performGoogleSignOut() {
|
|
6650
|
6797
|
do {
|
|
|
6798
|
+ MeetingReminderManager.shared.cancelAllReminders()
|
|
6651
|
6799
|
try googleOAuth.signOut()
|
|
6652
|
6800
|
applyGoogleProfile(nil)
|
|
6653
|
6801
|
updateGoogleAuthButtonTitle()
|