Quellcode durchsuchen

Add Zoom-specific join mode inputs and content switching.

This updates the Join Meetings UI so Zoom shows Join with ID/Join with URL tabs and renders the correct form when each mode is selected.

Made-with: Cursor
huzaifahayat12 vor 3 Monaten
Ursprung
Commit
5e4352d3df
2 geänderte Dateien mit 235 neuen und 7 gelöschten Zeilen
  1. 7 5
      meetings_app/Base.lproj/Main.storyboard
  2. 228 2
      meetings_app/ViewController.swift

+ 7 - 5
meetings_app/Base.lproj/Main.storyboard

@@ -1,7 +1,9 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="24765" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
     <dependencies>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24765"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
         <!--Application-->
@@ -673,7 +675,7 @@
                         <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
                     </connections>
                 </application>
-                <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target"/>
+                <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="meetings_app" customModuleProvider="target"/>
                 <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
                 <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
             </objects>
@@ -703,7 +705,7 @@
         <!--View Controller-->
         <scene sceneID="hIz-AP-VOD">
             <objects>
-                <viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
+                <viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="meetings_app" customModuleProvider="target" sceneMemberID="viewController">
                     <view key="view" id="m2S-Jp-Qdl">
                         <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
                         <autoresizingMask key="autoresizingMask"/>

+ 228 - 2
meetings_app/ViewController.swift

@@ -22,6 +22,11 @@ private enum MeetingProvider: Int {
     case zoho = 3
 }
 
+private enum ZoomJoinMode: Int {
+    case id = 0
+    case url = 1
+}
+
 private enum SettingsAction: Int {
     case restore = 0
     case rateUs = 1
@@ -48,9 +53,12 @@ final class ViewController: NSViewController {
     private var tabViews: [MeetingProvider: NSView] = [:]
     private var selectedSidebarPage: SidebarPage = .joinMeetings
     private var selectedMeetingProvider: MeetingProvider = .meet
+    private var selectedZoomJoinMode: ZoomJoinMode = .id
     private var pageCache: [SidebarPage: NSView] = [:]
     private var sidebarPageByView = [ObjectIdentifier: SidebarPage]()
     private var meetingProviderByView = [ObjectIdentifier: MeetingProvider]()
+    private var zoomJoinModeByView = [ObjectIdentifier: ZoomJoinMode]()
+    private var zoomJoinModeViews: [ZoomJoinMode: NSView] = [:]
     private var settingsActionByView = [ObjectIdentifier: SettingsAction]()
     private weak var centeredTitleLabel: NSTextField?
     private weak var paywallWindow: NSWindow?
@@ -166,7 +174,26 @@ private extension ViewController {
               let provider = meetingProviderByView[ObjectIdentifier(view)],
               provider != selectedMeetingProvider else { return }
         selectedMeetingProvider = provider
+        if provider == .zoom {
+            selectedZoomJoinMode = .id
+        }
         updateTabAppearance()
+        if selectedSidebarPage == .joinMeetings {
+            pageCache[.joinMeetings] = nil
+            showSidebarPage(.joinMeetings)
+        }
+    }
+
+    @objc private func zoomJoinModeClicked(_ sender: NSClickGestureRecognizer) {
+        guard let view = sender.view,
+              let mode = zoomJoinModeByView[ObjectIdentifier(view)],
+              mode != selectedZoomJoinMode else { return }
+        selectedZoomJoinMode = mode
+        updateZoomJoinModeAppearance()
+        if selectedSidebarPage == .joinMeetings, selectedMeetingProvider == .zoom {
+            pageCache[.joinMeetings] = nil
+            showSidebarPage(.joinMeetings)
+        }
     }
 
     @objc private func premiumButtonClicked(_ sender: NSClickGestureRecognizer) {
@@ -555,8 +582,17 @@ private extension ViewController {
 
         contentStack.addArrangedSubview(textLabel("Join Meetings", font: typography.pageTitle, color: palette.textPrimary))
         contentStack.addArrangedSubview(meetingTypeTabs())
-        contentStack.addArrangedSubview(joinWithURLHeading())
-        contentStack.addArrangedSubview(meetingUrlSection())
+        if selectedMeetingProvider == .zoom {
+            contentStack.addArrangedSubview(zoomJoinModeTabs())
+            if selectedZoomJoinMode == .id {
+                contentStack.addArrangedSubview(zoomMeetingIDSection())
+            } else {
+                contentStack.addArrangedSubview(meetingUrlSection())
+            }
+        } else {
+            contentStack.addArrangedSubview(joinWithURLHeading())
+            contentStack.addArrangedSubview(meetingUrlSection())
+        }
         contentStack.addArrangedSubview(scheduleHeader())
         contentStack.addArrangedSubview(textLabel("Tuesday, 14 Apr", font: typography.dateHeading, color: palette.textSecondary))
         contentStack.addArrangedSubview(scheduleCardsRow())
@@ -911,6 +947,196 @@ private extension ViewController {
         return card
     }
 
+    func zoomJoinModeTabs() -> NSView {
+        let row = NSStackView()
+        row.translatesAutoresizingMaskIntoConstraints = false
+        row.orientation = .horizontal
+        row.alignment = .centerY
+        row.spacing = 28
+        row.widthAnchor.constraint(greaterThanOrEqualToConstant: 780).isActive = true
+
+        let idTab = joinModeTab("Join with ID", mode: .id)
+        let urlTab = joinModeTab("Join with URL", mode: .url)
+        row.addArrangedSubview(idTab)
+        row.addArrangedSubview(urlTab)
+        let spacer = NSView()
+        spacer.translatesAutoresizingMaskIntoConstraints = false
+        row.addArrangedSubview(spacer)
+
+        zoomJoinModeViews[.id] = idTab
+        zoomJoinModeViews[.url] = urlTab
+        updateZoomJoinModeAppearance()
+        return row
+    }
+
+    func joinModeTab(_ title: String, mode: ZoomJoinMode) -> NSView {
+        let tab = HoverTrackingView()
+        tab.translatesAutoresizingMaskIntoConstraints = false
+        tab.wantsLayer = true
+        tab.layer?.cornerRadius = 6
+        tab.layer?.backgroundColor = NSColor.clear.cgColor
+        tab.heightAnchor.constraint(equalToConstant: 30).isActive = true
+        zoomJoinModeByView[ObjectIdentifier(tab)] = mode
+
+        let label = textLabel(title, font: NSFont.systemFont(ofSize: 33 / 2, weight: .medium), color: palette.textPrimary)
+        tab.addSubview(label)
+        NSLayoutConstraint.activate([
+            label.leadingAnchor.constraint(equalTo: tab.leadingAnchor, constant: 4),
+            label.trailingAnchor.constraint(equalTo: tab.trailingAnchor, constant: -4),
+            label.topAnchor.constraint(equalTo: tab.topAnchor, constant: 4),
+            label.bottomAnchor.constraint(equalTo: tab.bottomAnchor, constant: -6)
+        ])
+
+        let click = NSClickGestureRecognizer(target: self, action: #selector(zoomJoinModeClicked(_:)))
+        tab.addGestureRecognizer(click)
+        return tab
+    }
+
+    func updateZoomJoinModeAppearance() {
+        for (mode, tab) in zoomJoinModeViews {
+            let selected = (mode == selectedZoomJoinMode)
+            let textColor = selected ? palette.textPrimary : palette.textSecondary
+            let label = tab.subviews.first { $0 is NSTextField } as? NSTextField
+            label?.textColor = textColor
+
+            // Keep the active tab visually underlined like the reference.
+            if selected {
+                if tab.subviews.contains(where: { $0.identifier?.rawValue == "modeUnderline" }) == false {
+                    let underline = NSView()
+                    underline.identifier = NSUserInterfaceItemIdentifier("modeUnderline")
+                    underline.translatesAutoresizingMaskIntoConstraints = false
+                    underline.wantsLayer = true
+                    underline.layer?.backgroundColor = palette.primaryBlue.cgColor
+                    tab.addSubview(underline)
+                    NSLayoutConstraint.activate([
+                        underline.leadingAnchor.constraint(equalTo: tab.leadingAnchor),
+                        underline.trailingAnchor.constraint(equalTo: tab.trailingAnchor),
+                        underline.bottomAnchor.constraint(equalTo: tab.bottomAnchor),
+                        underline.heightAnchor.constraint(equalToConstant: 2)
+                    ])
+                }
+            } else {
+                tab.subviews
+                    .filter { $0.identifier?.rawValue == "modeUnderline" }
+                    .forEach { $0.removeFromSuperview() }
+            }
+        }
+    }
+
+    func joinWithIDHeading() -> NSView {
+        let container = NSView()
+        container.translatesAutoresizingMaskIntoConstraints = false
+
+        let title = textLabel("Join with ID", font: typography.joinWithURLTitle, color: palette.textPrimary)
+        title.alignment = .left
+        title.setContentHuggingPriority(.defaultHigh, for: .horizontal)
+        title.setContentCompressionResistancePriority(.required, for: .horizontal)
+
+        let bar = NSView()
+        bar.translatesAutoresizingMaskIntoConstraints = false
+        bar.wantsLayer = true
+        bar.layer?.backgroundColor = palette.primaryBlue.cgColor
+        bar.heightAnchor.constraint(equalToConstant: 3).isActive = true
+
+        container.addSubview(title)
+        container.addSubview(bar)
+
+        NSLayoutConstraint.activate([
+            title.leadingAnchor.constraint(equalTo: container.leadingAnchor),
+            title.topAnchor.constraint(equalTo: container.topAnchor),
+
+            bar.leadingAnchor.constraint(equalTo: title.leadingAnchor),
+            bar.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 6),
+            bar.widthAnchor.constraint(equalTo: title.widthAnchor),
+            bar.bottomAnchor.constraint(equalTo: container.bottomAnchor),
+
+            container.trailingAnchor.constraint(equalTo: title.trailingAnchor)
+        ])
+
+        return container
+    }
+
+    func zoomMeetingIDSection() -> NSView {
+        let wrapper = NSView()
+        wrapper.translatesAutoresizingMaskIntoConstraints = false
+
+        let fieldsRow = NSStackView()
+        fieldsRow.translatesAutoresizingMaskIntoConstraints = false
+        fieldsRow.orientation = .horizontal
+        fieldsRow.alignment = .top
+        fieldsRow.distribution = .fillEqually
+        fieldsRow.spacing = 12
+        fieldsRow.addArrangedSubview(zoomInputField(title: "Meeting ID", placeholder: "Enter meeting ID..."))
+        fieldsRow.addArrangedSubview(zoomInputField(title: "Meeting Passcode", placeholder: "Enter meeting passcode..."))
+
+        let actions = NSStackView()
+        actions.orientation = .horizontal
+        actions.spacing = 10
+        actions.translatesAutoresizingMaskIntoConstraints = false
+        actions.alignment = .centerY
+        actions.addArrangedSubview(actionButton(title: "Cancel", color: palette.cancelButton, textColor: palette.textSecondary, width: 110))
+        actions.addArrangedSubview(actionButton(title: "Join", color: palette.primaryBlue, textColor: .white, width: 116))
+
+        wrapper.addSubview(fieldsRow)
+        wrapper.addSubview(actions)
+
+        NSLayoutConstraint.activate([
+            wrapper.widthAnchor.constraint(greaterThanOrEqualToConstant: 780),
+
+            fieldsRow.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
+            fieldsRow.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
+            fieldsRow.topAnchor.constraint(equalTo: wrapper.topAnchor),
+
+            actions.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
+            actions.topAnchor.constraint(equalTo: fieldsRow.bottomAnchor, constant: 14),
+            actions.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor)
+        ])
+
+        return wrapper
+    }
+
+    func zoomInputField(title: String, placeholder: String) -> NSView {
+        let wrapper = NSView()
+        wrapper.translatesAutoresizingMaskIntoConstraints = false
+
+        let heading = textLabel(title, font: typography.fieldLabel, color: palette.textPrimary)
+        let textFieldContainer = roundedContainer(cornerRadius: 10, color: palette.inputBackground)
+        textFieldContainer.translatesAutoresizingMaskIntoConstraints = false
+        textFieldContainer.heightAnchor.constraint(equalToConstant: 40).isActive = true
+        styleSurface(textFieldContainer, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
+
+        let field = NSTextField(string: "")
+        field.translatesAutoresizingMaskIntoConstraints = false
+        field.isEditable = true
+        field.isSelectable = true
+        field.isBordered = false
+        field.drawsBackground = false
+        field.placeholderString = placeholder
+        field.font = typography.inputPlaceholder
+        field.textColor = palette.textPrimary
+        field.focusRingType = .none
+        textFieldContainer.addSubview(field)
+
+        wrapper.addSubview(heading)
+        wrapper.addSubview(textFieldContainer)
+
+        NSLayoutConstraint.activate([
+            heading.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
+            heading.topAnchor.constraint(equalTo: wrapper.topAnchor),
+
+            textFieldContainer.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
+            textFieldContainer.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
+            textFieldContainer.topAnchor.constraint(equalTo: heading.bottomAnchor, constant: 10),
+            textFieldContainer.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor),
+
+            field.leadingAnchor.constraint(equalTo: textFieldContainer.leadingAnchor, constant: 12),
+            field.trailingAnchor.constraint(equalTo: textFieldContainer.trailingAnchor, constant: -12),
+            field.centerYAnchor.constraint(equalTo: textFieldContainer.centerYAnchor)
+        ])
+
+        return wrapper
+    }
+
     func joinWithURLHeading() -> NSView {
         let container = NSView()
         container.translatesAutoresizingMaskIntoConstraints = false