Add shared profile viewer with deep link routing#4385
Conversation
Scaffold the in-app view for viewing someone else's shared profile, presented modally when opening a pca.st/user/<slug> universal link. Includes profile header, followed podcasts, and recent episodes sections with AsyncImage artwork loading and "Show All" drill-downs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generated by 🚫 Danger |
Podcast rows now show a "+" button to subscribe (or checkmark if already subscribed). Episode rows show a play button that loads the podcast and streams the episode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Horizontal scrolling podcast cards with artwork overlay subscribe buttons - Tappable podcasts and episodes that present detail pages on top of modal - Close button on presented detail pages using standard cancel asset - Custom play button and bidirectional follow/unfollow support - Refined spacing and layout to match discover screen patterns Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add accessibility label to episode play button - Fix stale subscribe button state by observing podcastAdded/podcastDeleted notifications - Show podcast title in episode rows (e.g. "Only a Game · 40m") - Fix placeholder circle visibility with systemGray5/systemGray2 - Add comment explaining forced view load in addCloseButton - Remove unnecessary [self] capture in struct closure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| navigateToPodcast(uuid: podcast.uuid) | ||
| } label: { | ||
| VStack(alignment: .leading, spacing: 0) { | ||
| podcastArtwork(url: podcast.artworkURL, size: cardWidth) |
There was a problem hiding this comment.
While this work we have an existing PodcastImage component that can will display the podcast image based on the uuid of the podcast. He has advantage of supporting placeholder images and cache across the system
| navigateToPodcast(uuid: podcast.uuid) | ||
| } label: { | ||
| HStack(spacing: 12) { | ||
| podcastArtwork(url: podcast.artworkURL, size: 52) |
There was a problem hiding this comment.
PodcastImage can also be used here
| } | ||
|
|
||
| @ViewBuilder | ||
| private func allEpisodesView() -> some View { |
There was a problem hiding this comment.
This should be split to a separate file and component to show All Episodes, ex: SharedProfileEpisodeList
| // MARK: - Full List Views | ||
|
|
||
| @ViewBuilder | ||
| private func allPodcastsView() -> some View { |
There was a problem hiding this comment.
This should be split to a separate file and component to show All Podcasts, ex: SharedProfilePodcastsList
| } | ||
|
|
||
| private extension UIViewController { | ||
| @objc func dismissAnimated() { |
There was a problem hiding this comment.
This extension method feels redudant why not just call the method above directly?
There was a problem hiding this comment.
Done in 8ea6da1 — replaced with UIAction-based UIBarButtonItem to eliminate the extension.
| } | ||
| } | ||
|
|
||
| private struct SharedProfileSubscribeButton: View { |
There was a problem hiding this comment.
This should go to a separate file. Ex: ShareProfileSubscribeButton
There was a problem hiding this comment.
Done in e3e0bd0 — moved SharedProfileSubscribeButton, EpisodePlayButton, PlayTriangle, and ScaleButtonStyle to SharedProfileComponents.swift.
| Button { | ||
| viewModel.playEpisode(uuid: episode.uuid, podcastUuid: episode.podcastUuid) | ||
| } label: { | ||
| EpisodePlayButton() |
There was a problem hiding this comment.
Should the play button change display after you press play? show a pause state? Will you be able to pause directly here?
There was a problem hiding this comment.
Good question — I think for this initial version, keeping it as a simple play button makes sense since we're just launching playback from a shared profile context. We can add pause state tracking in a follow-up once we wire up the real playback state observation.
SergioEstevao
left a comment
There was a problem hiding this comment.
In general the functionality is working with the mock data.
I left some questions regarding code organization and about the play button states.
Tell me if you want to address it on this PR or do a follow up.
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Use UIAction-based UIBarButtonItem instead of target-action pattern, eliminating the need for a UIViewController extension. Co-Authored-By: Claude <noreply@anthropic.com>
Extracts SharedProfileSubscribeButton, EpisodePlayButton, PlayTriangle, and ScaleButtonStyle to a dedicated file. Co-Authored-By: Claude <noreply@anthropic.com>
|
@SergioEstevao Re: using PodcastImage instead of AsyncImage for podcast card artwork and episode row artwork — Done in 669a59a |
Review feedback addressed
|
|
| App Name | Pocket Casts Prototype Build | |
| Build Number | 15138 | |
| Version | PR #4385 | |
| Bundle ID | au.com.shiftyjelly.podcasts.prototype | |
| Commit | e3e0bd0 | |
| Installation URL | 64u5s877s8q48 |
SergioEstevao
left a comment
There was a problem hiding this comment.
Code looking much better! ![]()

Summary
SharedProfileViewandSharedProfileViewModel— the in-app viewer for when someone opens a shared profile link (pca.st/user/<slug>)AppDelegate+SiriShortcuts.swiftto intercept/user/<slug>paths and present the viewer modally viaPCHostingControllerMainEpisodeActionViewvisual styleNavigationStackFeatureFlag.shareProfileDemo and Designs
View.Shared.Profile.iOS.mov
Test plan
xcrun simctl openurl booted "https://pca.st/user/dom"— SharedProfileView should present modally with mock dataFeatureFlag.shareProfileis disabled🤖 Generated with Claude Code