Explorar o código

Add day navigation to Upcoming meetings panel

Show meetings for the selected day (default today) with previous/next navigation and a Today reset control.

Made-with: Cursor
huzaifahayat12 hai 5 días
pai
achega
8e2f8d60f9
Modificáronse 1 ficheiros con 124 adicións e 7 borrados
  1. 124 7
      zoom_app/ViewController.swift

+ 124 - 7
zoom_app/ViewController.swift

@@ -45,13 +45,18 @@ class ViewController: NSViewController {
45 45
     private weak var zoomSocialButton: NSButton?
46 46
     private weak var timeLabel: NSTextField?
47 47
     private weak var dateLabel: NSTextField?
48
+    private weak var meetingsDayHeaderLabel: NSTextField?
48 49
     private weak var emptyMeetingLabel: NSTextField?
49 50
     private weak var meetingsListStack: NSStackView?
50 51
     private weak var meetingsStatusLabel: NSTextField?
51 52
     private weak var meetingsScrollView: NSScrollView?
53
+    private weak var meetingsPrevDayButton: NSButton?
54
+    private weak var meetingsNextDayButton: NSButton?
55
+    private weak var meetingsTodayButton: NSButton?
52 56
     private weak var homeSearchField: NSTextField?
53 57
     private weak var homeSearchPill: NSView?
54 58
     private var allScheduledMeetings: [ScheduledMeeting] = []
59
+    private var selectedMeetingsDayStart: Date = Calendar.current.startOfDay(for: Date())
55 60
     private var searchTextObserver: NSObjectProtocol?
56 61
     private var searchShortcutMonitor: Any?
57 62
     private var searchOutsideClickMonitor: Any?
@@ -146,6 +151,7 @@ class ViewController: NSViewController {
146 151
         homeSearchField = nil
147 152
         homeSearchPill = nil
148 153
         homeView?.removeFromSuperview()
154
+        selectedMeetingsDayStart = Calendar.current.startOfDay(for: Date())
149 155
         homeView = makeHomeView(profile: profile)
150 156
         if let homeView { attachToRoot(homeView) }
151 157
         installSearchShortcutMonitor()
@@ -441,7 +447,12 @@ class ViewController: NSViewController {
441 447
         }
442 448
 
443 449
         let query = (homeSearchField?.stringValue ?? "").trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
444
-        let source = allScheduledMeetings
450
+        let calendar = Calendar.current
451
+        let dayStart = selectedMeetingsDayStart
452
+        let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) ?? dayStart.addingTimeInterval(60 * 60 * 24)
453
+        let source = allScheduledMeetings.filter { meeting in
454
+            meeting.start >= dayStart && meeting.start < dayEnd
455
+        }
445 456
         let filtered: [ScheduledMeeting]
446 457
         if query.isEmpty {
447 458
             filtered = source
@@ -457,22 +468,75 @@ class ViewController: NSViewController {
457 468
         if ordered.isEmpty {
458 469
             emptyMeetingLabel?.isHidden = false
459 470
             if source.isEmpty {
460
-                meetingsStatusLabel?.stringValue = "No upcoming Zoom meetings found."
461
-                emptyMeetingLabel?.stringValue = "No meetings scheduled for today."
471
+                meetingsStatusLabel?.stringValue = "Upcoming meetings"
472
+                emptyMeetingLabel?.stringValue = "No meetings scheduled for \(meetingsDayDisplayName(for: dayStart))."
462 473
             } else {
463
-                meetingsStatusLabel?.stringValue = "Zoom meetings"
474
+                meetingsStatusLabel?.stringValue = "Upcoming meetings"
464 475
                 emptyMeetingLabel?.stringValue = "No meetings match your search."
465 476
             }
466 477
             return
467 478
         }
468 479
 
469 480
         emptyMeetingLabel?.isHidden = true
470
-        meetingsStatusLabel?.stringValue = "Zoom meetings"
481
+        meetingsStatusLabel?.stringValue = "Upcoming meetings"
471 482
         for meeting in ordered {
472 483
             stack.addArrangedSubview(makeMeetingRowCard(meeting))
473 484
         }
474 485
     }
475 486
 
487
+    @MainActor
488
+    private func setSelectedMeetingsDayStart(_ newDayStart: Date) {
489
+        selectedMeetingsDayStart = Calendar.current.startOfDay(for: newDayStart)
490
+        updateMeetingsDayUI()
491
+        applyFilteredMeetings()
492
+    }
493
+
494
+    @MainActor
495
+    private func updateMeetingsDayUI() {
496
+        let dayStart = selectedMeetingsDayStart
497
+        let formatter = DateFormatter()
498
+        formatter.dateFormat = "EEEE, MMM d"
499
+        meetingsDayHeaderLabel?.stringValue = formatter.string(from: dayStart)
500
+
501
+        let isToday = Calendar.current.isDate(dayStart, inSameDayAs: Date())
502
+        meetingsTodayButton?.isEnabled = isToday == false
503
+        meetingsTodayButton?.alphaValue = isToday ? 0.55 : 1.0
504
+    }
505
+
506
+    private func meetingsDayDisplayName(for dayStart: Date) -> String {
507
+        let calendar = Calendar.current
508
+        if calendar.isDate(dayStart, inSameDayAs: Date()) { return "today" }
509
+        if let tomorrow = calendar.date(byAdding: .day, value: 1, to: calendar.startOfDay(for: Date())),
510
+           calendar.isDate(dayStart, inSameDayAs: tomorrow) { return "tomorrow" }
511
+        if let yesterday = calendar.date(byAdding: .day, value: -1, to: calendar.startOfDay(for: Date())),
512
+           calendar.isDate(dayStart, inSameDayAs: yesterday) { return "yesterday" }
513
+        let formatter = DateFormatter()
514
+        formatter.dateFormat = "EEEE, MMM d"
515
+        return formatter.string(from: dayStart)
516
+    }
517
+
518
+    @objc private func meetingsPrevDayTapped() {
519
+        let calendar = Calendar.current
520
+        let prev = calendar.date(byAdding: .day, value: -1, to: selectedMeetingsDayStart) ?? selectedMeetingsDayStart.addingTimeInterval(-60 * 60 * 24)
521
+        Task { @MainActor in
522
+            self.setSelectedMeetingsDayStart(prev)
523
+        }
524
+    }
525
+
526
+    @objc private func meetingsNextDayTapped() {
527
+        let calendar = Calendar.current
528
+        let next = calendar.date(byAdding: .day, value: 1, to: selectedMeetingsDayStart) ?? selectedMeetingsDayStart.addingTimeInterval(60 * 60 * 24)
529
+        Task { @MainActor in
530
+            self.setSelectedMeetingsDayStart(next)
531
+        }
532
+    }
533
+
534
+    @objc private func meetingsTodayTapped() {
535
+        Task { @MainActor in
536
+            self.setSelectedMeetingsDayStart(Date())
537
+        }
538
+    }
539
+
476 540
     private func loadScheduledMeetings() async {
477 541
         if isLoadingMeetings { return }
478 542
         isLoadingMeetings = true
@@ -949,6 +1013,16 @@ class ViewController: NSViewController {
949 1013
         todaysDateFormatter.dateFormat = "EEEE, MMM d"
950 1014
         let panelHeader = makeLabel(todaysDateFormatter.string(from: Date()), size: 21, color: primaryText, weight: .semibold, centered: false)
951 1015
         let meetingsStatus = makeLabel("Upcoming meetings", size: 12, color: secondaryText, weight: .medium, centered: false)
1016
+        
1017
+        let meetingsDayNav = NSStackView()
1018
+        meetingsDayNav.orientation = .horizontal
1019
+        meetingsDayNav.spacing = 4
1020
+        meetingsDayNav.alignment = .centerY
1021
+        let prevDayButton = makeNavGlyphButton(symbol: "chevron.left", action: #selector(meetingsPrevDayTapped), dimension: 14, pointSize: 7, toolTip: "Previous day")
1022
+        let todayButton = makeMeetingsDayChipButton(title: "Today", symbol: "calendar", action: #selector(meetingsTodayTapped))
1023
+        let nextDayButton = makeNavGlyphButton(symbol: "chevron.right", action: #selector(meetingsNextDayTapped), dimension: 14, pointSize: 7, toolTip: "Next day")
1024
+        [prevDayButton, todayButton, nextDayButton].forEach { meetingsDayNav.addArrangedSubview($0) }
1025
+        
952 1026
         let noMeeting = makeLabel("No meetings scheduled for today.", size: 18, color: secondaryText, weight: .regular, centered: true)
953 1027
         let meetingsScrollView = NSScrollView()
954 1028
         meetingsScrollView.drawsBackground = false
@@ -989,7 +1063,7 @@ class ViewController: NSViewController {
989 1063
         [searchIcon, searchField, searchHintLabel].forEach {
990 1064
             searchPill.addSubview($0)
991 1065
         }
992
-        [welcome, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
1066
+        [welcome, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, meetingsDayNav, noMeeting, meetingsScrollView, openRecordings].forEach {
993 1067
             $0.translatesAutoresizingMaskIntoConstraints = false
994 1068
             contentColumn.addSubview($0)
995 1069
         }
@@ -1071,7 +1145,9 @@ class ViewController: NSViewController {
1071 1145
             panelHeader.topAnchor.constraint(equalTo: panel.topAnchor, constant: 20),
1072 1146
             panelHeader.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 16),
1073 1147
             meetingsStatus.centerYAnchor.constraint(equalTo: panelHeader.centerYAnchor),
1074
-            meetingsStatus.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -16),
1148
+            meetingsDayNav.centerYAnchor.constraint(equalTo: panelHeader.centerYAnchor),
1149
+            meetingsDayNav.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -16),
1150
+            meetingsStatus.trailingAnchor.constraint(equalTo: meetingsDayNav.leadingAnchor, constant: -10),
1075 1151
             noMeeting.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
1076 1152
             noMeeting.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
1077 1153
             noMeeting.centerYAnchor.constraint(equalTo: panel.centerYAnchor),
@@ -1095,11 +1171,17 @@ class ViewController: NSViewController {
1095 1171
 
1096 1172
         timeLabel = timeTitle
1097 1173
         dateLabel = dateTitle
1174
+        meetingsDayHeaderLabel = panelHeader
1098 1175
         meetingsListStack = meetingsStack
1099 1176
         meetingsStatusLabel = meetingsStatus
1100 1177
         emptyMeetingLabel = noMeeting
1178
+        meetingsPrevDayButton = prevDayButton
1179
+        meetingsTodayButton = todayButton
1180
+        meetingsNextDayButton = nextDayButton
1101 1181
         observeMeetingsScrollEdges(in: meetingsScrollView)
1102 1182
         updateClock()
1183
+        updateMeetingsDayUI()
1184
+        applyFilteredMeetings()
1103 1185
 
1104 1186
         homeSearchField = searchField
1105 1187
         homeSearchPill = searchPill
@@ -1414,6 +1496,41 @@ class ViewController: NSViewController {
1414 1496
         button.heightAnchor.constraint(equalToConstant: dimension).isActive = true
1415 1497
         return button
1416 1498
     }
1499
+    
1500
+    private func makeMeetingsDayChipButton(title: String, symbol: String? = nil, action: Selector?) -> NSButton {
1501
+        let button = NSButton(title: title, target: action == nil ? nil : self, action: action)
1502
+        button.isBordered = false
1503
+        button.bezelStyle = .shadowlessSquare
1504
+        button.focusRingType = .none
1505
+        button.wantsLayer = true
1506
+        button.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.06).cgColor
1507
+        button.layer?.cornerRadius = 10
1508
+        button.layer?.borderWidth = 1
1509
+        button.layer?.borderColor = NSColor.white.withAlphaComponent(0.08).cgColor
1510
+        let tint = NSColor(calibratedWhite: 0.9, alpha: 1)
1511
+        let font = NSFont.systemFont(ofSize: 11, weight: .semibold)
1512
+        button.contentTintColor = tint
1513
+        button.font = font
1514
+        button.attributedTitle = NSAttributedString(string: title, attributes: [
1515
+            .foregroundColor: tint,
1516
+            .font: font
1517
+        ])
1518
+        if let symbol {
1519
+            let symbolConfig = NSImage.SymbolConfiguration(pointSize: 11, weight: .semibold)
1520
+            if let base = NSImage(systemSymbolName: symbol, accessibilityDescription: title),
1521
+               let image = base.withSymbolConfiguration(symbolConfig) {
1522
+                image.isTemplate = true
1523
+                button.image = image
1524
+                button.imagePosition = .imageLeading
1525
+            }
1526
+            button.imageHugsTitle = true
1527
+            button.imageScaling = .scaleNone
1528
+        }
1529
+        button.translatesAutoresizingMaskIntoConstraints = false
1530
+        button.heightAnchor.constraint(equalToConstant: 24).isActive = true
1531
+        button.widthAnchor.constraint(greaterThanOrEqualToConstant: 92).isActive = true
1532
+        return button
1533
+    }
1417 1534
 
1418 1535
     private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
1419 1536
         let root = NSView()