|
|
@@ -1800,12 +1800,7 @@ private extension ViewController {
|
|
1800
|
1800
|
row.addArrangedSubview(spacer)
|
|
1801
|
1801
|
spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
1802
|
1802
|
|
|
1803
|
|
- row.addArrangedSubview(iconRoundButton("?", size: 34, onClick: { [weak self] in
|
|
1804
|
|
- self?.showScheduleHelp()
|
|
1805
|
|
- }))
|
|
1806
|
|
- row.addArrangedSubview(iconRoundButton("⟳", size: 34, onClick: { [weak self] in
|
|
1807
|
|
- self?.scheduleReloadClicked()
|
|
1808
|
|
- }))
|
|
|
1803
|
+ row.addArrangedSubview(makeScheduleRefreshButton())
|
|
1809
|
1804
|
|
|
1810
|
1805
|
let connectButton = makeSchedulePillButton(title: googleOAuth.loadTokens() == nil ? "Connect" : "Connected")
|
|
1811
|
1806
|
connectButton.target = self
|
|
|
@@ -1871,6 +1866,28 @@ private extension ViewController {
|
|
1871
|
1866
|
return button
|
|
1872
|
1867
|
}
|
|
1873
|
1868
|
|
|
|
1869
|
+ private func makeScheduleRefreshButton() -> NSButton {
|
|
|
1870
|
+ let button = NSButton(title: "", target: self, action: #selector(scheduleReloadButtonPressed(_:)))
|
|
|
1871
|
+ button.translatesAutoresizingMaskIntoConstraints = false
|
|
|
1872
|
+ button.isBordered = false
|
|
|
1873
|
+ button.bezelStyle = .regularSquare
|
|
|
1874
|
+ button.wantsLayer = true
|
|
|
1875
|
+ button.layer?.cornerRadius = 21
|
|
|
1876
|
+ button.layer?.backgroundColor = palette.inputBackground.cgColor
|
|
|
1877
|
+ button.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
1878
|
+ button.layer?.borderWidth = 1
|
|
|
1879
|
+ button.setButtonType(.momentaryChange)
|
|
|
1880
|
+ button.contentTintColor = palette.textSecondary
|
|
|
1881
|
+ button.image = NSImage(systemSymbolName: "arrow.clockwise", accessibilityDescription: "Refresh meetings")
|
|
|
1882
|
+ button.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 18, weight: .semibold)
|
|
|
1883
|
+ button.imagePosition = .imageOnly
|
|
|
1884
|
+ button.imageScaling = .scaleProportionallyDown
|
|
|
1885
|
+ button.focusRingType = .none
|
|
|
1886
|
+ button.heightAnchor.constraint(equalToConstant: 42).isActive = true
|
|
|
1887
|
+ button.widthAnchor.constraint(equalToConstant: 42).isActive = true
|
|
|
1888
|
+ return button
|
|
|
1889
|
+ }
|
|
|
1890
|
+
|
|
1874
|
1891
|
func scheduleCardsRow(meetings: [ScheduledMeeting]) -> NSView {
|
|
1875
|
1892
|
let scroll = NSScrollView()
|
|
1876
|
1893
|
scroll.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -2485,7 +2502,7 @@ private extension ViewController {
|
|
2485
|
2502
|
return button
|
|
2486
|
2503
|
}
|
|
2487
|
2504
|
|
|
2488
|
|
- func iconRoundButton(_ symbol: String, size: CGFloat, onClick: (() -> Void)? = nil) -> NSView {
|
|
|
2505
|
+ func iconRoundButton(systemSymbol: String, size: CGFloat, iconPointSize: CGFloat = 16, onClick: (() -> Void)? = nil) -> NSView {
|
|
2489
|
2506
|
let button = HoverTrackingView()
|
|
2490
|
2507
|
button.wantsLayer = true
|
|
2491
|
2508
|
button.layer?.cornerRadius = size / 2
|
|
|
@@ -2495,11 +2512,16 @@ private extension ViewController {
|
|
2495
|
2512
|
button.heightAnchor.constraint(equalToConstant: size).isActive = true
|
|
2496
|
2513
|
styleSurface(button, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
2497
|
2514
|
|
|
2498
|
|
- let label = textLabel(symbol, font: typography.iconButton, color: palette.textSecondary)
|
|
2499
|
|
- button.addSubview(label)
|
|
|
2515
|
+ let symbolConfig = NSImage.SymbolConfiguration(pointSize: iconPointSize, weight: .semibold)
|
|
|
2516
|
+ let iconView = NSImageView()
|
|
|
2517
|
+ iconView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
2518
|
+ iconView.image = NSImage(systemSymbolName: systemSymbol, accessibilityDescription: "Refresh")
|
|
|
2519
|
+ iconView.symbolConfiguration = symbolConfig
|
|
|
2520
|
+ iconView.contentTintColor = palette.textSecondary
|
|
|
2521
|
+ button.addSubview(iconView)
|
|
2500
|
2522
|
NSLayoutConstraint.activate([
|
|
2501
|
|
- label.centerXAnchor.constraint(equalTo: button.centerXAnchor),
|
|
2502
|
|
- label.centerYAnchor.constraint(equalTo: button.centerYAnchor)
|
|
|
2523
|
+ iconView.centerXAnchor.constraint(equalTo: button.centerXAnchor),
|
|
|
2524
|
+ iconView.centerYAnchor.constraint(equalTo: button.centerYAnchor)
|
|
2503
|
2525
|
])
|
|
2504
|
2526
|
|
|
2505
|
2527
|
let baseColor = palette.inputBackground
|
|
|
@@ -2518,6 +2540,10 @@ private extension ViewController {
|
|
2518
|
2540
|
// MARK: - Schedule actions (OAuth entry)
|
|
2519
|
2541
|
|
|
2520
|
2542
|
private extension ViewController {
|
|
|
2543
|
+ @objc func scheduleReloadButtonPressed(_ sender: NSButton) {
|
|
|
2544
|
+ scheduleReloadClicked()
|
|
|
2545
|
+ }
|
|
|
2546
|
+
|
|
2521
|
2547
|
@objc func scheduleConnectButtonPressed(_ sender: NSButton) {
|
|
2522
|
2548
|
scheduleConnectClicked()
|
|
2523
|
2549
|
}
|
|
|
@@ -2674,11 +2700,22 @@ private extension ViewController {
|
|
2674
|
2700
|
}
|
|
2675
|
2701
|
|
|
2676
|
2702
|
func scheduleReloadClicked() {
|
|
2677
|
|
- // Data loading is wired in the Calendar step.
|
|
2678
|
|
- // For now, this triggers a sign-in if needed so the next step can fetch events.
|
|
2679
|
2703
|
Task { [weak self] in
|
|
2680
|
2704
|
guard let self else { return }
|
|
2681
|
|
- _ = try? await googleOAuth.validAccessToken(presentingWindow: view.window)
|
|
|
2705
|
+ do {
|
|
|
2706
|
+ try await ensureGoogleClientIdConfigured(presentingWindow: view.window)
|
|
|
2707
|
+ _ = try await googleOAuth.validAccessToken(presentingWindow: view.window)
|
|
|
2708
|
+ await MainActor.run {
|
|
|
2709
|
+ scheduleDateHeadingLabel?.stringValue = "Refreshing…"
|
|
|
2710
|
+ pageCache[.joinMeetings] = nil
|
|
|
2711
|
+ showSidebarPage(.joinMeetings)
|
|
|
2712
|
+ }
|
|
|
2713
|
+ await loadSchedule()
|
|
|
2714
|
+ } catch {
|
|
|
2715
|
+ await MainActor.run {
|
|
|
2716
|
+ showSimpleError("Couldn’t refresh schedule.", error: error)
|
|
|
2717
|
+ }
|
|
|
2718
|
+ }
|
|
2682
|
2719
|
}
|
|
2683
|
2720
|
}
|
|
2684
|
2721
|
|