Skip to content

Commit f1fb053

Browse files
authored
Merge pull request #723 from insidegui/ah/swiftui-session-details
Partial conversion of SessionDetails to SwiftUI
2 parents d86a9a9 + 275fa7b commit f1fb053

15 files changed

+344
-247
lines changed

.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ opt_in_rules:
2121

2222
excluded:
2323
- Carthage
24+
- Packages/ConfCore/.build
2425

2526
force_cast:
2627
severity: warning

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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
9104BDFE2A25165A00860C08 /* Combine+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9104BDFD2A25165A00860C08 /* Combine+UI.swift */; };
2929
911C72C92A52169A00CB3757 /* CombineLatestMany.swift in Sources */ = {isa = PBXBuildFile; fileRef = 911C72C82A52169A00CB3757 /* CombineLatestMany.swift */; };
3030
914367202A4C6B0E004E4392 /* Sequence+GroupedBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9143671F2A4C6B0E004E4392 /* Sequence+GroupedBy.swift */; };
31+
914C83B42E1ECBFF001AEE5C /* SessionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 914C83B32E1ECBFF001AEE5C /* SessionDetailsView.swift */; };
3132
91C2A0BC2A4DE9B60049B6B7 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 91C2A0BB2A4DE9B60049B6B7 /* OrderedCollections */; };
33+
91DB93E82E1FF610002E8716 /* IsSelected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91DB93E72E1FF60C002E8716 /* IsSelected.swift */; };
3234
91EF6A2A2A33FBF8003A71A3 /* Realm+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91EF6A292A33FBF8003A71A3 /* Realm+Combine.swift */; };
3335
DD0159A71ECFE26200F980F1 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0159A61ECFE26200F980F1 /* DeepLink.swift */; };
3436
DD0159A91ED09F5D00F980F1 /* AppCoordinator+Bookmarks.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0159A81ED09F5D00F980F1 /* AppCoordinator+Bookmarks.swift */; };
@@ -313,6 +315,8 @@
313315
9104BDFD2A25165A00860C08 /* Combine+UI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Combine+UI.swift"; sourceTree = "<group>"; };
314316
911C72C82A52169A00CB3757 /* CombineLatestMany.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineLatestMany.swift; sourceTree = "<group>"; };
315317
9143671F2A4C6B0E004E4392 /* Sequence+GroupedBy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+GroupedBy.swift"; sourceTree = "<group>"; };
318+
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>"; };
316320
91EF6A292A33FBF8003A71A3 /* Realm+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Realm+Combine.swift"; sourceTree = "<group>"; };
317321
DD0159A61ECFE26200F980F1 /* DeepLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
318322
DD0159A81ED09F5D00F980F1 /* AppCoordinator+Bookmarks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppCoordinator+Bookmarks.swift"; sourceTree = "<group>"; };
@@ -830,6 +834,7 @@
830834
DD7F387E1EAC15A1002D8C00 /* Util */ = {
831835
isa = PBXGroup;
832836
children = (
837+
91DB93E72E1FF60C002E8716 /* IsSelected.swift */,
833838
9104BDFD2A25165A00860C08 /* Combine+UI.swift */,
834839
DDB28F841EAD20A10077703F /* UIDebugger.h */,
835840
DDB28F851EAD20A10077703F /* UIDebugger.m */,
@@ -1102,6 +1107,7 @@
11021107
DDFA10C71EBFD897001DCF66 /* Detail */ = {
11031108
isa = PBXGroup;
11041109
children = (
1110+
914C83B32E1ECBFF001AEE5C /* SessionDetailsView.swift */,
11051111
DD7F38771EAC0C98002D8C00 /* ShelfViewController.swift */,
11061112
DD7F38631EABD6DF002D8C00 /* SessionDetailsViewController.swift */,
11071113
DD6E06F91EDBC636000EAEA4 /* Tabs */,
@@ -1538,6 +1544,7 @@
15381544
DD0159A71ECFE26200F980F1 /* DeepLink.swift in Sources */,
15391545
DDA60E1720A9083E002EECF5 /* RelatedSessionsViewController.swift in Sources */,
15401546
F44C82312A2285AC00FDE980 /* OffsetObservingScrollView.swift in Sources */,
1547+
914C83B42E1ECBFF001AEE5C /* SessionDetailsView.swift in Sources */,
15411548
DDA60E1520A907B6002EECF5 /* SessionCollectionViewItem.swift in Sources */,
15421549
DD7F38881EAC2275002D8C00 /* PathUtil.swift in Sources */,
15431550
DDB352821EC7C55300254815 /* DateProvider.swift in Sources */,
@@ -1595,6 +1602,7 @@
15951602
4DDF6A782177A00C008E5539 /* DownloadsManagementTableCellView.swift in Sources */,
15961603
F4578D9F2A26A218005B311A /* WWDCAppCommand.swift in Sources */,
15971604
F4FB06BF2A216C1F00799F84 /* RemoteGlyph.swift in Sources */,
1605+
91DB93E82E1FF610002E8716 /* IsSelected.swift in Sources */,
15981606
DDDF807E20BA4FFA007284F8 /* WWDCHorizontalScrollView.swift in Sources */,
15991607
F4F2792A2C0F777200A029A3 /* DownloadManagerView.swift in Sources */,
16001608
DD7F387D1EAC113A002D8C00 /* WWDCTextField.swift in Sources */,

WWDC/GeneralPreferencesViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ final class GeneralPreferencesViewController: WWDCWindowContentViewController {
236236

237237
var rootFileCount = 0
238238

239-
while enumerator.nextObject() as? String != nil {
239+
while enumerator.nextObject() is String {
240240
rootFileCount += 1
241241
}
242242

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: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//
2+
// SessionDetailsView.swift
3+
// WWDC
4+
//
5+
// Created by Allen Humphreys on 7/9/25.
6+
// Copyright © 2025 Guilherme Rambo. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
11+
/**
12+
View for displaying session details with video player and tabbed content.
13+
14+
This view is the right hand side of the split views for each of schedule and sessions app-level tabs.
15+
16+
Visual Structure:
17+
┌─────────────────────────────────────────────────┐
18+
│ │
19+
│ ShelfViewControllerWrapper │
20+
│ (Video Player) │
21+
│ │
22+
└─────────────────────────────────────────────────┘
23+
24+
┌─────────────────────────────────────────────────┐
25+
│ [Overview] [Transcript] [Bookmarks] │
26+
│ ─────────────────────────────────────────────── │
27+
│ │
28+
│ Tab Content Area │
29+
│ │
30+
│ • Overview: SessionSummaryViewControllerWrapper│
31+
│ • Transcript: SessionTranscriptViewController │
32+
│ • Bookmarks: Placeholder text │
33+
│ │
34+
└─────────────────────────────────────────────────┘
35+
*/
36+
struct SessionDetailsView: View {
37+
@ObservedObject var detailsViewModel: SessionDetailsViewModel
38+
39+
var body: some View {
40+
VStack(spacing: 0) {
41+
ShelfViewControllerWrapper(controller: detailsViewModel.shelfController)
42+
.layoutPriority(1)
43+
.frame(minHeight: 280, maxHeight: .infinity)
44+
.padding(.top, 22)
45+
46+
if detailsViewModel.isTranscriptAvailable || detailsViewModel.isBookmarksAvailable {
47+
tabButtons
48+
}
49+
50+
Divider()
51+
52+
tabContent
53+
.padding(.top, 16)
54+
}
55+
.padding([.bottom, .horizontal], 46)
56+
}
57+
58+
private var tabButtons: some View {
59+
HStack(spacing: 32) {
60+
Button("Overview") {
61+
detailsViewModel.selectedTab = .overview
62+
}
63+
.selected(detailsViewModel.selectedTab == .overview)
64+
65+
if detailsViewModel.isTranscriptAvailable {
66+
Button("Transcript") {
67+
detailsViewModel.selectedTab = .transcript
68+
}
69+
.selected(detailsViewModel.selectedTab == .transcript)
70+
}
71+
72+
if detailsViewModel.isBookmarksAvailable {
73+
Button("Bookmarks") {
74+
detailsViewModel.selectedTab = .bookmarks
75+
}
76+
.selected(detailsViewModel.selectedTab == .bookmarks)
77+
}
78+
}
79+
.buttonStyle(WWDCTextButtonStyle())
80+
.frame(maxWidth: .infinity)
81+
.padding(.vertical, 8)
82+
}
83+
84+
@ViewBuilder
85+
private var tabContent: some View {
86+
switch detailsViewModel.selectedTab {
87+
case .overview:
88+
SessionSummaryViewControllerWrapper(controller: detailsViewModel.summaryController)
89+
case .transcript:
90+
SessionTranscriptViewControllerWrapper(controller: detailsViewModel.transcriptController)
91+
case .bookmarks:
92+
Text("Bookmarks view coming soon")
93+
.foregroundColor(.secondary)
94+
}
95+
}
96+
}
97+
98+
struct SessionDetailsView_Previews: PreviewProvider {
99+
static var previews: some View {
100+
SessionDetailsView(detailsViewModel: SessionDetailsViewModel(session: .preview))
101+
}
102+
}

0 commit comments

Comments
 (0)