Преглед на файлове

Enable Ai companion audio playback and stable scrolling layout.

Make ended-meeting audio items clickable via a Play Audio action and wrap the page content in a fixed-height scroll view so window height stays constant while cards overflow.

Made-with: Cursor
huzaifahayat12 преди 1 месец
родител
ревизия
03d6e9349c
променени са 1 файла, в които са добавени 58 реда и са изтрити 10 реда
  1. 58 10
      meetings_app/ViewController.swift

+ 58 - 10
meetings_app/ViewController.swift

@@ -266,6 +266,7 @@ final class ViewController: NSViewController {
266 266
     private var zoomJoinModeByView = [ObjectIdentifier: ZoomJoinMode]()
267 267
     private var zoomJoinModeViews: [ZoomJoinMode: NSView] = [:]
268 268
     private var settingsActionByView = [ObjectIdentifier: SettingsAction]()
269
+    private var aiCompanionAudioURLByView = [ObjectIdentifier: URL]()
269 270
     private var paywallWindow: NSWindow?
270 271
     private weak var paywallOverlayView: NSView?
271 272
     private let paywallContentWidth: CGFloat = 520
@@ -1944,6 +1945,24 @@ private extension ViewController {
1944 1945
         panel.translatesAutoresizingMaskIntoConstraints = false
1945 1946
         panel.userInterfaceLayoutDirection = .leftToRight
1946 1947
 
1948
+        let scroll = NSScrollView()
1949
+        scroll.translatesAutoresizingMaskIntoConstraints = false
1950
+        scroll.drawsBackground = false
1951
+        scroll.hasHorizontalScroller = false
1952
+        scroll.hasVerticalScroller = true
1953
+        scroll.autohidesScrollers = true
1954
+        scroll.borderType = .noBorder
1955
+        scroll.scrollerStyle = .overlay
1956
+        scroll.automaticallyAdjustsContentInsets = false
1957
+        let clip = TopAlignedClipView()
1958
+        clip.drawsBackground = false
1959
+        scroll.contentView = clip
1960
+        panel.addSubview(scroll)
1961
+
1962
+        let content = NSView()
1963
+        content.translatesAutoresizingMaskIntoConstraints = false
1964
+        scroll.documentView = content
1965
+
1947 1966
         let contentStack = NSStackView()
1948 1967
         contentStack.translatesAutoresizingMaskIntoConstraints = false
1949 1968
         contentStack.userInterfaceLayoutDirection = .leftToRight
@@ -1987,12 +2006,23 @@ private extension ViewController {
1987 2006
             }
1988 2007
         }
1989 2008
 
1990
-        panel.addSubview(contentStack)
2009
+        content.addSubview(contentStack)
1991 2010
         NSLayoutConstraint.activate([
1992
-            contentStack.leftAnchor.constraint(equalTo: panel.leftAnchor, constant: 28),
1993
-            contentStack.rightAnchor.constraint(equalTo: panel.rightAnchor, constant: -28),
1994
-            contentStack.topAnchor.constraint(equalTo: panel.topAnchor),
1995
-            contentStack.bottomAnchor.constraint(lessThanOrEqualTo: panel.bottomAnchor, constant: -16)
2011
+            scroll.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
2012
+            scroll.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
2013
+            scroll.topAnchor.constraint(equalTo: panel.topAnchor),
2014
+            scroll.bottomAnchor.constraint(equalTo: panel.bottomAnchor),
2015
+
2016
+            content.leadingAnchor.constraint(equalTo: scroll.contentView.leadingAnchor),
2017
+            content.trailingAnchor.constraint(equalTo: scroll.contentView.trailingAnchor),
2018
+            content.topAnchor.constraint(equalTo: scroll.contentView.topAnchor),
2019
+            content.bottomAnchor.constraint(greaterThanOrEqualTo: scroll.contentView.bottomAnchor),
2020
+            content.widthAnchor.constraint(equalTo: scroll.contentView.widthAnchor),
2021
+
2022
+            contentStack.leftAnchor.constraint(equalTo: content.leftAnchor, constant: 28),
2023
+            contentStack.rightAnchor.constraint(equalTo: content.rightAnchor, constant: -28),
2024
+            contentStack.topAnchor.constraint(equalTo: content.topAnchor),
2025
+            contentStack.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -16)
1996 2026
         ])
1997 2027
 
1998 2028
         return panel
@@ -2019,14 +2049,27 @@ private extension ViewController {
2019 2049
         dateLabel.alignment = .left
2020 2050
 
2021 2051
         let audioLink = mockAudioURLString(for: meeting)
2022
-        let audioLabel = textLabel("Mock Audio: \(audioLink)", font: typography.fieldLabel, color: palette.primaryBlue)
2023
-        audioLabel.alignment = .left
2024
-        audioLabel.maximumNumberOfLines = 2
2025
-        audioLabel.lineBreakMode = .byTruncatingTail
2052
+        let audioButton = NSButton(title: "Play Audio", target: self, action: #selector(aiCompanionAudioTapped(_:)))
2053
+        audioButton.translatesAutoresizingMaskIntoConstraints = false
2054
+        audioButton.isBordered = false
2055
+        audioButton.bezelStyle = .inline
2056
+        audioButton.font = NSFont.systemFont(ofSize: 13, weight: .semibold)
2057
+        audioButton.contentTintColor = palette.primaryBlue
2058
+        audioButton.alignment = .left
2059
+        audioButton.setButtonType(.momentaryPushIn)
2060
+        if let audioURL = URL(string: audioLink) {
2061
+            aiCompanionAudioURLByView[ObjectIdentifier(audioButton)] = audioURL
2062
+        }
2063
+
2064
+        let audioURLLabel = textLabel(audioLink, font: typography.fieldLabel, color: palette.textMuted)
2065
+        audioURLLabel.alignment = .left
2066
+        audioURLLabel.maximumNumberOfLines = 2
2067
+        audioURLLabel.lineBreakMode = .byTruncatingTail
2026 2068
 
2027 2069
         stack.addArrangedSubview(title)
2028 2070
         stack.addArrangedSubview(dateLabel)
2029
-        stack.addArrangedSubview(audioLabel)
2071
+        stack.addArrangedSubview(audioButton)
2072
+        stack.addArrangedSubview(audioURLLabel)
2030 2073
 
2031 2074
         card.addSubview(stack)
2032 2075
         NSLayoutConstraint.activate([
@@ -2039,6 +2082,11 @@ private extension ViewController {
2039 2082
         return card
2040 2083
     }
2041 2084
 
2085
+    @objc private func aiCompanionAudioTapped(_ sender: NSButton) {
2086
+        guard let url = aiCompanionAudioURLByView[ObjectIdentifier(sender)] else { return }
2087
+        NSWorkspace.shared.open(url)
2088
+    }
2089
+
2042 2090
     private func mockAudioURLString(for meeting: ScheduledMeeting) -> String {
2043 2091
         let slug = meeting.id.replacingOccurrences(of: "[^A-Za-z0-9_-]", with: "-", options: .regularExpression)
2044 2092
         return "https://mock-audio.local/\(slug).mp3"