|
|
@@ -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
|