Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playback 2024: Top 5 Podcasts List #2340

Merged
merged 6 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions podcasts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,7 @@
F57D8F162C66CBB80004C4DF /* UnsafeTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57D8F152C66CBB80004C4DF /* UnsafeTransfer.swift */; };
F57D8F182C66CDF40004C4DF /* View+CVPixelBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57D8F172C66CDF40004C4DF /* View+CVPixelBuffer.swift */; };
F581ED422CC6F1A200A19860 /* ListeningTime2024Story.swift in Sources */ = {isa = PBXBuildFile; fileRef = F581ED412CC6F19300A19860 /* ListeningTime2024Story.swift */; };
F581ED442CC6F3A900A19860 /* Top5Podcasts2024Story.swift in Sources */ = {isa = PBXBuildFile; fileRef = F581ED432CC6F3A400A19860 /* Top5Podcasts2024Story.swift */; };
F586959A2C04320100E0754A /* PodcastManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58695992C04320100E0754A /* PodcastManagerTests.swift */; };
F59503B42C93590A007FB3C8 /* ActivityItemSourceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F59503B32C93590A007FB3C8 /* ActivityItemSourceItem.swift */; };
F5952FCA2C057C6400754BC3 /* FMDatabaseQueue+Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5952FC92C057C6400754BC3 /* FMDatabaseQueue+Test.swift */; };
Expand Down Expand Up @@ -3627,6 +3628,7 @@
F57D8F152C66CBB80004C4DF /* UnsafeTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsafeTransfer.swift; sourceTree = "<group>"; };
F57D8F172C66CDF40004C4DF /* View+CVPixelBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+CVPixelBuffer.swift"; sourceTree = "<group>"; };
F581ED412CC6F19300A19860 /* ListeningTime2024Story.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListeningTime2024Story.swift; sourceTree = "<group>"; };
F581ED432CC6F3A400A19860 /* Top5Podcasts2024Story.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Top5Podcasts2024Story.swift; sourceTree = "<group>"; };
F58695992C04320100E0754A /* PodcastManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastManagerTests.swift; sourceTree = "<group>"; };
F59503B32C93590A007FB3C8 /* ActivityItemSourceItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityItemSourceItem.swift; sourceTree = "<group>"; };
F5952FC92C057C6400754BC3 /* FMDatabaseQueue+Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FMDatabaseQueue+Test.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7872,6 +7874,7 @@
F53C3E822CC73538004F3581 /* TopSpotStory2024.swift */,
F581ED412CC6F19300A19860 /* ListeningTime2024Story.swift */,
F5D527372CC81AA700682CD5 /* EpilogueStory2024.swift */,
F581ED432CC6F3A400A19860 /* Top5Podcasts2024Story.swift */,
);
path = 2024;
sourceTree = "<group>";
Expand Down Expand Up @@ -10128,6 +10131,7 @@
10DFE9382C5A8D1300957D0A /* ABTestProviding.swift in Sources */,
C7D8AE632A60818600C9EBAF /* ActionBarStyles.swift in Sources */,
8B633D2F2A57294E00526AF0 /* AutoplayWhatsNewHeader.swift in Sources */,
F581ED442CC6F3A900A19860 /* Top5Podcasts2024Story.swift in Sources */,
FF91A0FA2B6BBFD1002A0590 /* UpgradeCard.swift in Sources */,
C73215FB291EA13400AB1FE5 /* PlusPurchaseModal.swift in Sources */,
40B26AC7213FED4300386173 /* DiscoverPeekViewController.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ class EndOfYear2024StoriesModel: StoryModel {
func populate(with dataManager: DataManager) {
// First, search for top 5 podcasts
let topPodcasts = dataManager.topPodcasts(in: Self.year, limit: 5)
if !topPodcasts.isEmpty {
stories.append(.top5Podcasts)
data.topPodcasts = Array(topPodcasts.prefix(5))
}

// Listening time
if let listeningTime = dataManager.listeningTime(in: Self.year),
Expand All @@ -26,6 +30,8 @@ class EndOfYear2024StoriesModel: StoryModel {
switch stories[storyNumber] {
case .intro:
return IntroStory2024()
case .top5Podcasts:
return Top5Podcasts2024Story(top5Podcasts: data.topPodcasts)
case .listeningTime:
return ListeningTime2024Story(listeningTime: data.listeningTime)
case .epilogue:
Expand All @@ -37,6 +43,8 @@ class EndOfYear2024StoriesModel: StoryModel {
switch stories[storyNumber] {
case .epilogue:
return true
case .top5Podcasts:
return true
default:
return false
}
Expand Down Expand Up @@ -80,5 +88,7 @@ class EndOfYear2024StoriesModel: StoryModel {

/// An entity that holds data to present EoY 2024 stories
class EndOfYear2024StoriesData {
var topPodcasts: [TopPodcast] = []

var listeningTime: Double = 0
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
enum EndOfYear2024Story: CaseIterable {
case intro
case top5Podcasts
case listeningTime
case epilogue
}
121 changes: 121 additions & 0 deletions podcasts/End of Year/Stories/2024/Top5Podcasts2024Story.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import SwiftUI
import PocketCastsDataModel

struct Top5Podcasts2024Story: ShareableStory {
@Environment(\.renderForSharing) var renderForSharing: Bool

let top5Podcasts: [TopPodcast]

let identifier: String = "top_five_podcast"

private let shapeColor = Color.green

private let backgroundColor = Color(hex: "#E0EFAD")
private let shapeImages = ["playback-2024-shape-pentagon",
"playback-2024-shape-two-ovals",
"playback-2024-shape-wavy-circle"]

@ObservedObject private var animationViewModel = PlayPauseAnimationViewModel(duration: 0.8, animation: Animation.spring(_:))

@State private var visible = false

@State private var itemScale: Double = 0
@State private var itemOpacity: Double = 0

var body: some View {
GeometryReader { geometry in
let isSmallScreen = geometry.size.height <= 700
VStack(alignment: .leading) {
ScrollView(.vertical) {
VStack(alignment: .leading) {
podcastList()
.modifier(animationViewModel.animate($itemOpacity, to: 1, after: 0.1))
.modifier(animationViewModel.animate($itemScale, to: 1))
}
}
.modify {
if #available(iOS 16.4, *) {
$0.scrollIndicators(.never)
.scrollBounceBehavior(.basedOnSize)
}
}
.disabled(!isSmallScreen) // Disable scrolling on larger where we shouldn't be clipping.
.frame(height: geometry.size.height * 0.65)

Text("And you were big on these shows too!")
.font(.system(size: 30, weight: .bold))
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.ignoresSafeArea()
.enableProportionalValueScaling()
.background(
Rectangle()
.fill(backgroundColor)
.ignoresSafeArea()
.allowsHitTesting(false)
)
.onAppear {
animationViewModel.play()
}
}

@ViewBuilder func podcastList() -> some View {
ForEach(Array(zip(top5Podcasts.indices, top5Podcasts)), id: \.1.podcast.uuid) { index, item in
listCell(index: index, item: item)
}
}

@ViewBuilder func listCell(index: Int, item: TopPodcast) -> some View {
HStack {
let imageSize: Double = 72
let textAnimationOffset = imageSize/2
Text("#\(index + 1)")
.font(.system(size: 22, weight: .semibold))
.opacity(itemOpacity)
.offset(x: (1 - itemScale) * textAnimationOffset)

ZStack {
Image(shapeImages[index % shapeImages.count])
.foregroundStyle(shapeColor)
PodcastImage(uuid: item.podcast.uuid, size: .grid)
.frame(width: imageSize, height: imageSize)
.clipShape(RoundedRectangle(cornerRadius: 4))
}
.scaleEffect(itemScale)

VStack(alignment: .leading) {
if let author = item.podcast.author {
Text(author)
.font(.system(size: 15))
}
if let title = item.podcast.title {
Text(title)
.font(.system(size: 18, weight: .medium))
}
}
.opacity(itemOpacity)
.offset(x: (1 - itemScale) * -textAnimationOffset)
}
}

func onAppear() {
Analytics.track(.endOfYearStoryShown, story: identifier)
}

func onPause() {
animationViewModel.pause()
}

func onResume() {
animationViewModel.play()
}

func sharingAssets() -> [Any] {
[
StoryShareableProvider.new(AnyView(self)),
StoryShareableText(L10n.eoyStoryTopPodcastsShareText("%1$@"), podcasts: top5Podcasts.map { $0.podcast })
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "playback-2024-shape-pentagon.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "playback-2024-shape-two-ovals.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "playback-2024-shape-wavy-circle.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.