|
@@ -1800,12 +1800,7 @@ private extension ViewController {
|
|
|
row.addArrangedSubview(spacer)
|
|
row.addArrangedSubview(spacer)
|
|
|
spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
|
|
|
- row.addArrangedSubview(iconRoundButton("?", size: 34, onClick: { [weak self] in
|
|
|
|
|
- self?.showScheduleHelp()
|
|
|
|
|
- }))
|
|
|
|
|
- row.addArrangedSubview(iconRoundButton("⟳", size: 34, onClick: { [weak self] in
|
|
|
|
|
- self?.scheduleReloadClicked()
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+ row.addArrangedSubview(makeScheduleRefreshButton())
|
|
|
|
|
|
|
|
let connectButton = makeSchedulePillButton(title: googleOAuth.loadTokens() == nil ? "Connect" : "Connected")
|
|
let connectButton = makeSchedulePillButton(title: googleOAuth.loadTokens() == nil ? "Connect" : "Connected")
|
|
|
connectButton.target = self
|
|
connectButton.target = self
|
|
@@ -1871,6 +1866,28 @@ private extension ViewController {
|
|
|
return button
|
|
return button
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private func makeScheduleRefreshButton() -> NSButton {
|
|
|
|
|
+ let button = NSButton(title: "", target: self, action: #selector(scheduleReloadButtonPressed(_:)))
|
|
|
|
|
+ button.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ button.isBordered = false
|
|
|
|
|
+ button.bezelStyle = .regularSquare
|
|
|
|
|
+ button.wantsLayer = true
|
|
|
|
|
+ button.layer?.cornerRadius = 21
|
|
|
|
|
+ button.layer?.backgroundColor = palette.inputBackground.cgColor
|
|
|
|
|
+ button.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
|
|
+ button.layer?.borderWidth = 1
|
|
|
|
|
+ button.setButtonType(.momentaryChange)
|
|
|
|
|
+ button.contentTintColor = palette.textSecondary
|
|
|
|
|
+ button.image = NSImage(systemSymbolName: "arrow.clockwise", accessibilityDescription: "Refresh meetings")
|
|
|
|
|
+ button.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 18, weight: .semibold)
|
|
|
|
|
+ button.imagePosition = .imageOnly
|
|
|
|
|
+ button.imageScaling = .scaleProportionallyDown
|
|
|
|
|
+ button.focusRingType = .none
|
|
|
|
|
+ button.heightAnchor.constraint(equalToConstant: 42).isActive = true
|
|
|
|
|
+ button.widthAnchor.constraint(equalToConstant: 42).isActive = true
|
|
|
|
|
+ return button
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
func scheduleCardsRow(meetings: [ScheduledMeeting]) -> NSView {
|
|
func scheduleCardsRow(meetings: [ScheduledMeeting]) -> NSView {
|
|
|
let scroll = NSScrollView()
|
|
let scroll = NSScrollView()
|
|
|
scroll.translatesAutoresizingMaskIntoConstraints = false
|
|
scroll.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -2485,7 +2502,7 @@ private extension ViewController {
|
|
|
return button
|
|
return button
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- func iconRoundButton(_ symbol: String, size: CGFloat, onClick: (() -> Void)? = nil) -> NSView {
|
|
|
|
|
|
|
+ func iconRoundButton(systemSymbol: String, size: CGFloat, iconPointSize: CGFloat = 16, onClick: (() -> Void)? = nil) -> NSView {
|
|
|
let button = HoverTrackingView()
|
|
let button = HoverTrackingView()
|
|
|
button.wantsLayer = true
|
|
button.wantsLayer = true
|
|
|
button.layer?.cornerRadius = size / 2
|
|
button.layer?.cornerRadius = size / 2
|
|
@@ -2495,11 +2512,16 @@ private extension ViewController {
|
|
|
button.heightAnchor.constraint(equalToConstant: size).isActive = true
|
|
button.heightAnchor.constraint(equalToConstant: size).isActive = true
|
|
|
styleSurface(button, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
styleSurface(button, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
|
|
|
|
|
- let label = textLabel(symbol, font: typography.iconButton, color: palette.textSecondary)
|
|
|
|
|
- button.addSubview(label)
|
|
|
|
|
|
|
+ let symbolConfig = NSImage.SymbolConfiguration(pointSize: iconPointSize, weight: .semibold)
|
|
|
|
|
+ let iconView = NSImageView()
|
|
|
|
|
+ iconView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ iconView.image = NSImage(systemSymbolName: systemSymbol, accessibilityDescription: "Refresh")
|
|
|
|
|
+ iconView.symbolConfiguration = symbolConfig
|
|
|
|
|
+ iconView.contentTintColor = palette.textSecondary
|
|
|
|
|
+ button.addSubview(iconView)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- label.centerXAnchor.constraint(equalTo: button.centerXAnchor),
|
|
|
|
|
- label.centerYAnchor.constraint(equalTo: button.centerYAnchor)
|
|
|
|
|
|
|
+ iconView.centerXAnchor.constraint(equalTo: button.centerXAnchor),
|
|
|
|
|
+ iconView.centerYAnchor.constraint(equalTo: button.centerYAnchor)
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
let baseColor = palette.inputBackground
|
|
let baseColor = palette.inputBackground
|
|
@@ -2518,6 +2540,10 @@ private extension ViewController {
|
|
|
// MARK: - Schedule actions (OAuth entry)
|
|
// MARK: - Schedule actions (OAuth entry)
|
|
|
|
|
|
|
|
private extension ViewController {
|
|
private extension ViewController {
|
|
|
|
|
+ @objc func scheduleReloadButtonPressed(_ sender: NSButton) {
|
|
|
|
|
+ scheduleReloadClicked()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@objc func scheduleConnectButtonPressed(_ sender: NSButton) {
|
|
@objc func scheduleConnectButtonPressed(_ sender: NSButton) {
|
|
|
scheduleConnectClicked()
|
|
scheduleConnectClicked()
|
|
|
}
|
|
}
|
|
@@ -2674,11 +2700,22 @@ private extension ViewController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func scheduleReloadClicked() {
|
|
func scheduleReloadClicked() {
|
|
|
- // Data loading is wired in the Calendar step.
|
|
|
|
|
- // For now, this triggers a sign-in if needed so the next step can fetch events.
|
|
|
|
|
Task { [weak self] in
|
|
Task { [weak self] in
|
|
|
guard let self else { return }
|
|
guard let self else { return }
|
|
|
- _ = try? await googleOAuth.validAccessToken(presentingWindow: view.window)
|
|
|
|
|
|
|
+ do {
|
|
|
|
|
+ try await ensureGoogleClientIdConfigured(presentingWindow: view.window)
|
|
|
|
|
+ _ = try await googleOAuth.validAccessToken(presentingWindow: view.window)
|
|
|
|
|
+ await MainActor.run {
|
|
|
|
|
+ scheduleDateHeadingLabel?.stringValue = "Refreshing…"
|
|
|
|
|
+ pageCache[.joinMeetings] = nil
|
|
|
|
|
+ showSidebarPage(.joinMeetings)
|
|
|
|
|
+ }
|
|
|
|
|
+ await loadSchedule()
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ await MainActor.run {
|
|
|
|
|
+ showSimpleError("Couldn’t refresh schedule.", error: error)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|