Bläddra i källkod

Polish the home dashboard UI with cleaner cards and controls.

Improve top-bar, action tile, and meetings panel styling while wiring the Schedule action to open Zoom scheduling.

Made-with: Cursor
huzaifahayat12 6 dagar sedan
förälder
incheckning
b85cc18710
1 ändrade filer med 93 tillägg och 46 borttagningar
  1. 93 46
      zoom_app/ViewController.swift

+ 93 - 46
zoom_app/ViewController.swift

@@ -165,6 +165,14 @@ class ViewController: NSViewController {
165 165
         }
166 166
     }
167 167
 
168
+    @objc private func scheduleMeetingWebTapped() {
169
+        guard let url = URL(string: "https://zoom.us/meeting/schedule") else { return }
170
+        let opened = NSWorkspace.shared.open(url)
171
+        if opened == false {
172
+            meetingsStatusLabel?.stringValue = "Unable to open Zoom schedule page."
173
+        }
174
+    }
175
+
168 176
     @MainActor
169 177
     private func resetLoginSigningInState() {
170 178
         isSigningIn = false
@@ -533,36 +541,50 @@ class ViewController: NSViewController {
533 541
         let topBar = NSView()
534 542
         topBar.wantsLayer = true
535 543
         topBar.layer?.backgroundColor = cardBackground.cgColor
536
-        topBar.layer?.cornerRadius = 10
537
-        let search = makeLabel("Search (\u{2318}E)", size: 17, color: mutedText, weight: .regular, centered: true)
538
-        let name = makeLabel(profile?.name ?? "User", size: 14, color: primaryText, weight: .medium, centered: false)
539
-        name.wantsLayer = true
540
-        name.layer?.backgroundColor = accentBlue.withAlphaComponent(0.28).cgColor
541
-        name.layer?.cornerRadius = 10
542
-
543
-        let timeTitle = makeLabel("--:--", size: 54, color: primaryText, weight: .bold, centered: true)
544
-        let dateTitle = makeLabel("-", size: 18, color: secondaryText, weight: .regular, centered: true)
544
+        topBar.layer?.cornerRadius = 12
545
+        topBar.layer?.borderWidth = 1
546
+        topBar.layer?.borderColor = NSColor.white.withAlphaComponent(0.06).cgColor
547
+
548
+        let searchPill = NSView()
549
+        searchPill.wantsLayer = true
550
+        searchPill.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor
551
+        searchPill.layer?.cornerRadius = 9
552
+        let search = makeLabel("Search meetings, people (\u{2318}E)", size: 13, color: mutedText, weight: .regular, centered: false)
553
+
554
+        let profileChip = NSView()
555
+        profileChip.wantsLayer = true
556
+        profileChip.layer?.backgroundColor = accentBlue.withAlphaComponent(0.22).cgColor
557
+        profileChip.layer?.cornerRadius = 9
558
+        let name = makeLabel(profile?.name ?? "User", size: 13, color: primaryText, weight: .semibold, centered: false)
559
+
560
+        let welcome = makeLabel("Home", size: 15, color: secondaryText, weight: .medium, centered: false)
561
+        let timeTitle = makeLabel("--:--", size: 50, color: primaryText, weight: .bold, centered: true)
562
+        let dateTitle = makeLabel("-", size: 16, color: secondaryText, weight: .regular, centered: true)
545 563
 
546 564
         let actions = NSStackView(views: [
547 565
             makeActionTile(title: "New meeting", symbol: "video.fill", color: accentOrange),
548 566
             makeActionTile(title: "Join", symbol: "plus", color: accentBlue),
549
-            makeActionTile(title: "Schedule", symbol: "calendar", color: accentBlue),
567
+            makeActionTile(title: "Schedule", symbol: "calendar", color: accentBlue, action: #selector(scheduleMeetingWebTapped)),
550 568
             makeActionTile(title: "Share screen", symbol: "rectangle.portrait.and.arrow.forward", color: accentBlue),
551 569
             makeActionTile(title: "My notes", symbol: "pencil", color: accentBlue)
552 570
         ])
553 571
         actions.orientation = .horizontal
554
-        actions.spacing = 26
572
+        actions.spacing = 16
555 573
         actions.alignment = .centerY
556 574
         actions.distribution = .fillEqually
557 575
 
558 576
         let panel = NSView()
559 577
         panel.wantsLayer = true
560 578
         panel.layer?.backgroundColor = secondaryCardBackground.cgColor
561
-        panel.layer?.cornerRadius = 14
562
-
563
-        let panelHeader = makeLabel("Today, Apr 14", size: 32, color: primaryText, weight: .semibold, centered: true)
564
-        let meetingsStatus = makeLabel("Zoom meetings", size: 12, color: secondaryText, weight: .regular, centered: false)
565
-        let noMeeting = makeLabel("No meetings scheduled.", size: 32, color: secondaryText, weight: .regular, centered: true)
579
+        panel.layer?.cornerRadius = 16
580
+        panel.layer?.borderWidth = 1
581
+        panel.layer?.borderColor = NSColor.white.withAlphaComponent(0.07).cgColor
582
+
583
+        let todaysDateFormatter = DateFormatter()
584
+        todaysDateFormatter.dateFormat = "EEEE, MMM d"
585
+        let panelHeader = makeLabel(todaysDateFormatter.string(from: Date()), size: 22, color: primaryText, weight: .semibold, centered: false)
586
+        let meetingsStatus = makeLabel("Upcoming meetings", size: 12, color: secondaryText, weight: .medium, centered: false)
587
+        let noMeeting = makeLabel("No meetings scheduled for today.", size: 18, color: secondaryText, weight: .regular, centered: true)
566 588
         let meetingsScrollView = NSScrollView()
567 589
         meetingsScrollView.drawsBackground = false
568 590
         meetingsScrollView.hasVerticalScroller = true
@@ -574,15 +596,21 @@ class ViewController: NSViewController {
574 596
         meetingsStack.orientation = .vertical
575 597
         meetingsStack.spacing = 10
576 598
         meetingsStack.alignment = .leading
577
-        let openRecordings = makeLabel("Open recordings  ›", size: 30, color: secondaryText, weight: .regular, centered: false)
599
+        let openRecordings = NSButton(title: "Open recordings", target: nil, action: nil)
600
+        openRecordings.isBordered = false
601
+        openRecordings.font = .systemFont(ofSize: 14, weight: .semibold)
602
+        openRecordings.contentTintColor = primaryText
578 603
         openRecordings.wantsLayer = true
579 604
         openRecordings.layer?.backgroundColor = NSColor(calibratedRed: 31 / 255, green: 33 / 255, blue: 39 / 255, alpha: 1).cgColor
605
+        openRecordings.layer?.cornerRadius = 11
606
+        openRecordings.layer?.borderWidth = 1
607
+        openRecordings.layer?.borderColor = NSColor.white.withAlphaComponent(0.07).cgColor
580 608
 
581 609
         let contentColumn = NSView()
582 610
         contentColumn.translatesAutoresizingMaskIntoConstraints = false
583 611
         content.addSubview(contentColumn)
584 612
 
585
-        [topBar, search, name, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
613
+        [topBar, searchPill, search, profileChip, name, welcome, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
586 614
             $0.translatesAutoresizingMaskIntoConstraints = false
587 615
             contentColumn.addSubview($0)
588 616
         }
@@ -598,36 +626,52 @@ class ViewController: NSViewController {
598 626
             contentColumn.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
599 627
 
600 628
             topBar.topAnchor.constraint(equalTo: contentColumn.topAnchor, constant: 12),
601
-            topBar.leadingAnchor.constraint(equalTo: contentColumn.leadingAnchor, constant: 20),
602
-            topBar.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -80),
603
-            topBar.heightAnchor.constraint(equalToConstant: 36),
604
-
605
-            search.centerXAnchor.constraint(equalTo: topBar.centerXAnchor),
606
-            search.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
607
-
608
-            name.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -8),
609
-            name.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
610
-
611
-            timeTitle.topAnchor.constraint(equalTo: topBar.bottomAnchor, constant: 62),
629
+            topBar.leadingAnchor.constraint(equalTo: contentColumn.leadingAnchor, constant: 12),
630
+            topBar.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -12),
631
+            topBar.heightAnchor.constraint(equalToConstant: 50),
632
+
633
+            searchPill.leadingAnchor.constraint(equalTo: topBar.leadingAnchor, constant: 12),
634
+            searchPill.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
635
+            searchPill.heightAnchor.constraint(equalToConstant: 32),
636
+            searchPill.widthAnchor.constraint(equalToConstant: 320),
637
+            search.leadingAnchor.constraint(equalTo: searchPill.leadingAnchor, constant: 12),
638
+            search.trailingAnchor.constraint(equalTo: searchPill.trailingAnchor, constant: -12),
639
+            search.centerYAnchor.constraint(equalTo: searchPill.centerYAnchor),
640
+
641
+            profileChip.trailingAnchor.constraint(equalTo: topBar.trailingAnchor, constant: -10),
642
+            profileChip.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
643
+            profileChip.heightAnchor.constraint(equalToConstant: 32),
644
+            profileChip.widthAnchor.constraint(greaterThanOrEqualToConstant: 92),
645
+            name.leadingAnchor.constraint(equalTo: profileChip.leadingAnchor, constant: 12),
646
+            name.trailingAnchor.constraint(equalTo: profileChip.trailingAnchor, constant: -12),
647
+            name.centerYAnchor.constraint(equalTo: profileChip.centerYAnchor),
648
+
649
+            welcome.topAnchor.constraint(equalTo: topBar.bottomAnchor, constant: 18),
650
+            welcome.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
651
+
652
+            timeTitle.topAnchor.constraint(equalTo: welcome.bottomAnchor, constant: 14),
612 653
             timeTitle.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
613
-            dateTitle.topAnchor.constraint(equalTo: timeTitle.bottomAnchor, constant: 8),
654
+            dateTitle.topAnchor.constraint(equalTo: timeTitle.bottomAnchor, constant: 6),
614 655
             dateTitle.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
615 656
 
616
-            actions.topAnchor.constraint(equalTo: dateTitle.bottomAnchor, constant: 42),
657
+            actions.topAnchor.constraint(equalTo: dateTitle.bottomAnchor, constant: 30),
617 658
             actions.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
618
-            actions.widthAnchor.constraint(equalToConstant: 560),
659
+            actions.leadingAnchor.constraint(greaterThanOrEqualTo: contentColumn.leadingAnchor, constant: 18),
660
+            actions.trailingAnchor.constraint(lessThanOrEqualTo: contentColumn.trailingAnchor, constant: -18),
619 661
             actions.heightAnchor.constraint(equalToConstant: 96),
620 662
 
621
-            panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 32),
622
-            panel.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
623
-            panel.widthAnchor.constraint(equalToConstant: 640),
624
-            panel.heightAnchor.constraint(equalToConstant: 285),
663
+            panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 24),
664
+            panel.leadingAnchor.constraint(equalTo: contentColumn.leadingAnchor, constant: 28),
665
+            panel.trailingAnchor.constraint(equalTo: contentColumn.trailingAnchor, constant: -28),
666
+            panel.heightAnchor.constraint(equalToConstant: 300),
667
+            panel.bottomAnchor.constraint(lessThanOrEqualTo: contentColumn.bottomAnchor, constant: -20),
625 668
 
626
-            panelHeader.topAnchor.constraint(equalTo: panel.topAnchor, constant: 15),
627
-            panelHeader.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
669
+            panelHeader.topAnchor.constraint(equalTo: panel.topAnchor, constant: 16),
670
+            panelHeader.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
628 671
             meetingsStatus.centerYAnchor.constraint(equalTo: panelHeader.centerYAnchor),
629
-            meetingsStatus.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
630
-            noMeeting.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
672
+            meetingsStatus.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
673
+            noMeeting.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 18),
674
+            noMeeting.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
631 675
             noMeeting.centerYAnchor.constraint(equalTo: panel.centerYAnchor),
632 676
 
633 677
             meetingsScrollView.topAnchor.constraint(equalTo: panel.topAnchor, constant: 58),
@@ -641,10 +685,10 @@ class ViewController: NSViewController {
641 685
             meetingsStack.trailingAnchor.constraint(equalTo: meetingsDocument.trailingAnchor),
642 686
             meetingsStack.bottomAnchor.constraint(equalTo: meetingsDocument.bottomAnchor),
643 687
 
644
-            openRecordings.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
645
-            openRecordings.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
646
-            openRecordings.bottomAnchor.constraint(equalTo: panel.bottomAnchor),
647
-            openRecordings.heightAnchor.constraint(equalToConstant: 44)
688
+            openRecordings.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 14),
689
+            openRecordings.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -14),
690
+            openRecordings.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -12),
691
+            openRecordings.heightAnchor.constraint(equalToConstant: 38)
648 692
         ])
649 693
 
650 694
         timeLabel = timeTitle
@@ -722,17 +766,20 @@ class ViewController: NSViewController {
722 766
         return sidebar
723 767
     }
724 768
 
725
-    private func makeActionTile(title: String, symbol: String, color: NSColor) -> NSView {
769
+    private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
726 770
         let root = NSView()
727 771
         root.translatesAutoresizingMaskIntoConstraints = false
728 772
         root.widthAnchor.constraint(equalToConstant: 100).isActive = true
729 773
         root.heightAnchor.constraint(equalToConstant: 96).isActive = true
730 774
 
731
-        let iconButton = NSButton()
775
+        let iconButton = NSButton(title: "", target: action == nil ? nil : self, action: action)
732 776
         iconButton.isBordered = false
733 777
         iconButton.wantsLayer = true
734 778
         iconButton.layer?.backgroundColor = color.cgColor
735 779
         iconButton.layer?.cornerRadius = 18
780
+        iconButton.layer?.shadowOpacity = 0.2
781
+        iconButton.layer?.shadowRadius = 7
782
+        iconButton.layer?.shadowOffset = NSSize(width: 0, height: -1)
736 783
         iconButton.image = NSImage(systemSymbolName: symbol, accessibilityDescription: title)
737 784
         iconButton.contentTintColor = .white
738 785
         iconButton.imageScaling = .scaleProportionallyUpOrDown