Skip to content

Commit 275fa7b

Browse files
PR refinements, updated CONTRIBUTING.md
1 parent 3fdf2e4 commit 275fa7b

11 files changed

+134
-49
lines changed

CONTRIBUTING.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ Do not add dependencies to the project. In an ideal world, we would be masters o
159159

160160
Do not bring in assets (icons, images) for which you don't have an appropriate license. Icons and images must have a Creative Commons or similar license to be used in this project. A good place to find free icons is [Icon Finder](https://iconfinder.com).
161161

162+
#### VIII. AI-Assisted Contributions
163+
164+
If you use AI assistance (ChatGPT, GitHub Copilot, etc.) when writing code, please disclose this in your pull request description.
165+
166+
- **Understand your code**: Review and understand all AI-generated code before submitting
167+
- **Follow project patterns**: Adapt AI suggestions to match existing conventions and architecture
168+
- **Test thoroughly**: AI-generated code must still pass all quality standards and testing requirements
169+
- **No blind copy-paste**: Don't submit code you don't understand or haven't reviewed
170+
162171
## 3. Conduct
163172

164173
The project has a [code of conduct](./CODE_OF_CONDUCT.md) that should be kept in mind when contributing -- whether that contribution is in the form of issues, comments, documentation or code. We reckon the fact that we've never had to enforce this is a pretty good sign of a healthy community, so let's keep it that way. 😉

Packages/ConfCore/ConfCore/Session.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,17 @@ public class Session: Object, Decodable {
6969

7070
/// Fetches and returns the transcript object associated with the session
7171
public func transcript() -> Transcript? {
72+
guard let transcripts = transcripts() else { return nil }
73+
74+
return transcripts.first
75+
}
76+
77+
/// Fetches and returns the transcripts associated with the session. There should only ever be one.
78+
public func transcripts() -> Results<Transcript>? {
7279
guard let realm = realm else { return nil }
7380
guard !transcriptIdentifier.isEmpty else { return nil }
7481

75-
return realm.objects(Transcript.self).filter("identifier == %@ AND annotations.@count > 0", transcriptIdentifier).first
82+
return realm.objects(Transcript.self).filter("identifier == %@ AND annotations.@count > 0", transcriptIdentifier)
7683
}
7784

7885
/// The session's track

WWDC.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
914367202A4C6B0E004E4392 /* Sequence+GroupedBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9143671F2A4C6B0E004E4392 /* Sequence+GroupedBy.swift */; };
3131
914C83B42E1ECBFF001AEE5C /* SessionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 914C83B32E1ECBFF001AEE5C /* SessionDetailsView.swift */; };
3232
91C2A0BC2A4DE9B60049B6B7 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 91C2A0BB2A4DE9B60049B6B7 /* OrderedCollections */; };
33+
91DB93E82E1FF610002E8716 /* IsSelected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91DB93E72E1FF60C002E8716 /* IsSelected.swift */; };
3334
91EF6A2A2A33FBF8003A71A3 /* Realm+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91EF6A292A33FBF8003A71A3 /* Realm+Combine.swift */; };
3435
DD0159A71ECFE26200F980F1 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0159A61ECFE26200F980F1 /* DeepLink.swift */; };
3536
DD0159A91ED09F5D00F980F1 /* AppCoordinator+Bookmarks.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0159A81ED09F5D00F980F1 /* AppCoordinator+Bookmarks.swift */; };
@@ -315,6 +316,7 @@
315316
911C72C82A52169A00CB3757 /* CombineLatestMany.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineLatestMany.swift; sourceTree = "<group>"; };
316317
9143671F2A4C6B0E004E4392 /* Sequence+GroupedBy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+GroupedBy.swift"; sourceTree = "<group>"; };
317318
914C83B32E1ECBFF001AEE5C /* SessionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsView.swift; sourceTree = "<group>"; };
319+
91DB93E72E1FF60C002E8716 /* IsSelected.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsSelected.swift; sourceTree = "<group>"; };
318320
91EF6A292A33FBF8003A71A3 /* Realm+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Realm+Combine.swift"; sourceTree = "<group>"; };
319321
DD0159A61ECFE26200F980F1 /* DeepLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
320322
DD0159A81ED09F5D00F980F1 /* AppCoordinator+Bookmarks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppCoordinator+Bookmarks.swift"; sourceTree = "<group>"; };
@@ -832,6 +834,7 @@
832834
DD7F387E1EAC15A1002D8C00 /* Util */ = {
833835
isa = PBXGroup;
834836
children = (
837+
91DB93E72E1FF60C002E8716 /* IsSelected.swift */,
835838
9104BDFD2A25165A00860C08 /* Combine+UI.swift */,
836839
DDB28F841EAD20A10077703F /* UIDebugger.h */,
837840
DDB28F851EAD20A10077703F /* UIDebugger.m */,
@@ -1599,6 +1602,7 @@
15991602
4DDF6A782177A00C008E5539 /* DownloadsManagementTableCellView.swift in Sources */,
16001603
F4578D9F2A26A218005B311A /* WWDCAppCommand.swift in Sources */,
16011604
F4FB06BF2A216C1F00799F84 /* RemoteGlyph.swift in Sources */,
1605+
91DB93E82E1FF610002E8716 /* IsSelected.swift in Sources */,
16021606
DDDF807E20BA4FFA007284F8 /* WWDCHorizontalScrollView.swift in Sources */,
16031607
F4F2792A2C0F777200A029A3 /* DownloadManagerView.swift in Sources */,
16041608
DD7F387D1EAC113A002D8C00 /* WWDCTextField.swift in Sources */,

WWDC/AppCoordinator+Shelf.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ extension AppCoordinator: ShelfViewControllerDelegate {
1919
var shelfViewController: ShelfViewController?
2020
switch tab {
2121
case .schedule:
22-
shelfViewController = scheduleController.splitViewController.detailViewController.detailsViewModel.shelfController
22+
shelfViewController = scheduleController.splitViewController.detailViewController.shelfController
2323
case .videos:
24-
shelfViewController = videosController.detailViewController.detailsViewModel.shelfController
24+
shelfViewController = videosController.detailViewController.shelfController
2525
default: ()
2626
}
2727

WWDC/IsSelected.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// IsSelected.swift
3+
// WWDC
4+
//
5+
// Created by Allen Humphreys on 7/10/25.
6+
// Copyright © 2025 Guilherme Rambo. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
11+
struct IsSelectedEnvironmentKey: EnvironmentKey {
12+
static let defaultValue: Bool = false
13+
}
14+
15+
extension EnvironmentValues {
16+
var isSelected: Bool {
17+
get { self[IsSelectedEnvironmentKey.self] }
18+
set { self[IsSelectedEnvironmentKey.self] = newValue }
19+
}
20+
}
21+
22+
extension View {
23+
/// Similar to `enabled(_:)`, but for selection state.
24+
func selected(_ isSelected: Bool) -> some View {
25+
environment(\.isSelected, isSelected)
26+
}
27+
}

WWDC/SessionDetailsView.swift

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,14 @@ struct SessionDetailsView: View {
4343
.frame(minHeight: 280, maxHeight: .infinity)
4444
.padding(.top, 22)
4545

46-
VStack(spacing: 0) {
47-
if detailsViewModel.isTranscriptAvailable || detailsViewModel.isBookmarksAvailable {
48-
tabButtons
49-
}
46+
if detailsViewModel.isTranscriptAvailable || detailsViewModel.isBookmarksAvailable {
47+
tabButtons
48+
}
5049

51-
Divider()
50+
Divider()
5251

53-
tabContent
54-
.padding(.top, 16)
55-
}
52+
tabContent
53+
.padding(.top, 16)
5654
}
5755
.padding([.bottom, .horizontal], 46)
5856
}
@@ -62,24 +60,25 @@ struct SessionDetailsView: View {
6260
Button("Overview") {
6361
detailsViewModel.selectedTab = .overview
6462
}
65-
.buttonStyle(WWDCTextButtonStyle(isSelected: detailsViewModel.selectedTab == .overview))
66-
63+
.selected(detailsViewModel.selectedTab == .overview)
64+
6765
if detailsViewModel.isTranscriptAvailable {
6866
Button("Transcript") {
6967
detailsViewModel.selectedTab = .transcript
7068
}
71-
.buttonStyle(WWDCTextButtonStyle(isSelected: detailsViewModel.selectedTab == .transcript))
69+
.selected(detailsViewModel.selectedTab == .transcript)
7270
}
7371

7472
if detailsViewModel.isBookmarksAvailable {
7573
Button("Bookmarks") {
7674
detailsViewModel.selectedTab = .bookmarks
7775
}
78-
.buttonStyle(WWDCTextButtonStyle(isSelected: detailsViewModel.selectedTab == .bookmarks))
76+
.selected(detailsViewModel.selectedTab == .bookmarks)
7977
}
8078
}
79+
.buttonStyle(WWDCTextButtonStyle())
8180
.frame(maxWidth: .infinity)
82-
.padding(.vertical, 12)
81+
.padding(.vertical, 8)
8382
}
8483

8584
@ViewBuilder
@@ -95,3 +94,9 @@ struct SessionDetailsView: View {
9594
}
9695
}
9796
}
97+
98+
struct SessionDetailsView_Previews: PreviewProvider {
99+
static var previews: some View {
100+
SessionDetailsView(detailsViewModel: SessionDetailsViewModel(session: .preview))
101+
}
102+
}

WWDC/SessionDetailsViewController.swift

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,6 @@ class SessionDetailsViewModel: ObservableObject {
1414

1515
var viewModel: SessionViewModel? {
1616
didSet {
17-
cancellables = []
18-
19-
viewModel?.rxTranscriptUpdated.replaceError(with: "").sink { [weak self] _ in
20-
self?.isTranscriptAvailable = self?.viewModel?.session.transcript() != nil
21-
}
22-
.store(in: &cancellables)
23-
2417
shelfController.viewModel = viewModel
2518
summaryController.viewModel = viewModel
2619
transcriptController.viewModel = viewModel
@@ -32,8 +25,11 @@ class SessionDetailsViewModel: ObservableObject {
3225
if viewModel.identifier != oldValue?.identifier {
3326
selectedTab = .overview
3427
}
28+
viewModel.rxTranscript.replaceError(with: nil).map {
29+
$0 != nil
30+
}
31+
.assign(to: &$isTranscriptAvailable)
3532

36-
isTranscriptAvailable = viewModel.session.transcript() != nil
3733
isBookmarksAvailable = false
3834
}
3935
}
@@ -46,12 +42,14 @@ class SessionDetailsViewModel: ObservableObject {
4642
let summaryController: SessionSummaryViewController
4743
let transcriptController: SessionTranscriptViewController
4844

49-
private var cancellables = Set<AnyCancellable>()
50-
51-
init() {
52-
shelfController = ShelfViewController()
53-
summaryController = SessionSummaryViewController()
54-
transcriptController = SessionTranscriptViewController()
45+
init(session: SessionViewModel? = nil) {
46+
self.shelfController = ShelfViewController()
47+
self.summaryController = SessionSummaryViewController()
48+
self.transcriptController = SessionTranscriptViewController()
49+
50+
defer {
51+
self.viewModel = session
52+
}
5553
}
5654
}
5755

@@ -63,11 +61,7 @@ extension SessionDetailsViewModel {
6361

6462
final class SessionDetailsViewController: NSViewController {
6563

66-
private struct Metrics {
67-
static let padding: CGFloat = 46
68-
}
69-
70-
let detailsViewModel = SessionDetailsViewModel()
64+
private let detailsViewModel = SessionDetailsViewModel()
7165

7266
var viewModel: SessionViewModel? {
7367
didSet {

WWDC/SessionSummaryViewController.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ class SessionSummaryViewController: NSViewController {
217217

218218
relatedSessionsViewController.scrollToBeginningOfDocument(nil)
219219

220-
// TODO: Not even sure what this does
220+
// https://github.com/insidegui/WWDC/issues/724
221221
// I believe this is a dead feature, it appears to have been showing a link to sign up for a lab.
222222
// The API has since been updated, we could restore the feature because there's other data available now.
223223
viewModel.rxActionPrompt.replaceNilAndError(with: "").driveUI(\.stringValue, on: actionLinkLabel).store(in: &cancellables)
@@ -245,8 +245,12 @@ struct SessionSummaryViewControllerWrapper: NSViewControllerRepresentable {
245245

246246
@available(macOS 13.0, *)
247247
extension SessionSummaryViewControllerWrapper {
248+
/// Without this, the VStack in ``SessionDetailsView`` always equally distributes available space to the shelf and the summary.
249+
///
250+
/// But the AppKit implementation was set up to allow and uneven distribution of space. Since our deployment target is macOS 12, there will
251+
/// be a slight change in behavior when running on macOS 12. But I don't *think* it's going to be a big deal. And once more SwiftUI conversion is done
252+
/// I think we'll be able to get the proper behavior natively in SwiftUI.
248253
func sizeThatFits(_ proposal: ProposedViewSize, nsViewController: Self.NSViewControllerType, context: Self.Context) -> CGSize? {
249-
// Ensure the summary view fits its content
250254
.init(width: proposal.width ?? .zero, height: nsViewController.view.fittingSize.height)
251255
}
252256
}

WWDC/SessionViewModel.swift

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,36 @@ final class SessionViewModel {
2626
var imageUrl: URL?
2727
let trackName: String
2828

29-
lazy var rxTranscriptUpdated: some Publisher<String, Error> = {
30-
return rxSession.map(\.transcriptText).removeDuplicates()
31-
}()
32-
3329
lazy var rxSession: some Publisher<Session, Error> = {
3430
return session.valuePublisher()
3531
}()
3632

37-
lazy var rxTranscriptAnnotations: AnyPublisher<List<TranscriptAnnotation>, Error> = {
38-
guard let annotations = session.transcript()?.annotations else {
39-
return Just(List<TranscriptAnnotation>()).setFailureType(to: Error.self).eraseToAnyPublisher()
40-
}
33+
lazy var rxTranscript: some Publisher<Transcript?, Error> = {
34+
return rxSession
35+
.map(\.transcriptIdentifier)
36+
.compacted()
37+
.removeDuplicates()
38+
.flatMap { [weak self] _ in
39+
guard let results = self?.session.transcripts() else {
40+
return Just<Transcript?>(nil).setFailureType(to: Error.self).eraseToAnyPublisher()
41+
}
42+
43+
return results.collectionPublisher.map(\.first).eraseToAnyPublisher()
44+
}
45+
}()
4146

42-
return annotations.collectionPublisher.eraseToAnyPublisher()
47+
lazy var rxTranscriptAnnotations: some Publisher<List<TranscriptAnnotation>, Error> = {
48+
return rxSession
49+
.map(\.transcriptIdentifier)
50+
.compacted()
51+
.removeDuplicates()
52+
.flatMap { [weak self] _ in
53+
guard let annotations = self?.session.transcript()?.annotations else {
54+
return Just(List<TranscriptAnnotation>()).setFailureType(to: Error.self).eraseToAnyPublisher()
55+
}
56+
57+
return annotations.collectionPublisher.eraseToAnyPublisher()
58+
}
4359
}()
4460

4561
lazy var rxSessionInstance: some Publisher<SessionInstance, Error> = {
@@ -350,3 +366,22 @@ extension SessionViewModel {
350366
var isFavorite: Bool { session.isFavorite }
351367

352368
}
369+
370+
extension SessionViewModel {
371+
/// Challenges with previews
372+
///
373+
/// 1. app boot up
374+
/// 2. realm needs objects to be managed
375+
///
376+
/// I think we can build a preview helper that does a Boot().bootstrapDependencies(then:), but it's async
377+
/// so it's a bit of effort. For now, just brute force to get a session.
378+
static var preview: SessionViewModel {
379+
let delegate = (NSApplication.shared.delegate as! AppDelegate) // swiftlint:disable:this force_cast
380+
Thread.sleep(forTimeInterval: 0.5) // TODO: Get access to storage in a better way
381+
let coordinator = delegate.coordinator!
382+
383+
return Self.init(
384+
session: coordinator.storage.sessions.first { $0.transcript() != nil }!
385+
)!
386+
}
387+
}

WWDC/ShelfViewController.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ struct ShelfViewControllerWrapper: NSViewControllerRepresentable {
275275
let controller: ShelfViewController
276276

277277
func makeNSViewController(context: Context) -> ShelfViewController {
278-
controller.view.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
279278
return controller
280279
}
281280

0 commit comments

Comments
 (0)