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: Longest Episode #2347

Merged
merged 4 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -1721,6 +1721,7 @@
F5F6DA702BBE1109009B1934 /* CategoryButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F6DA6F2BBE1109009B1934 /* CategoryButtonStyle.swift */; };
F5F6DA822BC0B512009B1934 /* CategoriesModalPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F6DA812BC0B512009B1934 /* CategoriesModalPicker.swift */; };
F5F884632CC9EAA6002BED2C /* Humane-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = F5F884622CC9EAA6002BED2C /* Humane-Bold.otf */; };
F5F884652CCA86AE002BED2C /* LongestEpisode2024Story.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F884642CCA86AA002BED2C /* LongestEpisode2024Story.swift */; };
F5F89B1E2C88B40A00013118 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F89B1D2C88B40A00013118 /* ShareButton.swift */; };
F5F8F3182CC314250071DD0E /* IntroStory2024.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F8F3172CC314250071DD0E /* IntroStory2024.swift */; };
F5FE747B2C223A6100DF2EAA /* ShareImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FF61222BD07E3A00190711 /* ShareImageView.swift */; };
Expand Down Expand Up @@ -3659,6 +3660,7 @@
F5F6DA6F2BBE1109009B1934 /* CategoryButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryButtonStyle.swift; sourceTree = "<group>"; };
F5F6DA812BC0B512009B1934 /* CategoriesModalPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriesModalPicker.swift; sourceTree = "<group>"; };
F5F884622CC9EAA6002BED2C /* Humane-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Humane-Bold.otf"; sourceTree = "<group>"; };
F5F884642CCA86AA002BED2C /* LongestEpisode2024Story.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongestEpisode2024Story.swift; sourceTree = "<group>"; };
F5F89B1D2C88B40A00013118 /* ShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = "<group>"; };
F5F8F3172CC314250071DD0E /* IntroStory2024.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroStory2024.swift; sourceTree = "<group>"; };
F5FF611F2BD076BA00190711 /* Sharing.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Sharing.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7874,6 +7876,7 @@
F53C3E822CC73538004F3581 /* TopSpotStory2024.swift */,
F581ED412CC6F19300A19860 /* ListeningTime2024Story.swift */,
F5D527372CC81AA700682CD5 /* EpilogueStory2024.swift */,
F5F884642CCA86AA002BED2C /* LongestEpisode2024Story.swift */,
F581ED432CC6F3A400A19860 /* Top5Podcasts2024Story.swift */,
);
path = 2024;
Expand Down Expand Up @@ -9491,6 +9494,7 @@
C7FAFF5D2941844C00329B40 /* CancelConfirmationViewModel.swift in Sources */,
BD14CCDF1D7D3CB800DB4547 /* SelectedPodcastCell.swift in Sources */,
BD93FDA120157B2000F6EF55 /* PodcastImageView.swift in Sources */,
F5F884652CCA86AE002BED2C /* LongestEpisode2024Story.swift in Sources */,
BDD5253A20477E4400AAD211 /* NSObject+AppDelegate.swift in Sources */,
8B14E3B029B9159B0069B6F2 /* SearchHistoryModel.swift in Sources */,
C7080C5D2923070200D7A432 /* PlusAccountUpgradePrompt.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ class EndOfYear2024StoriesModel: StoryModel {
data.listeningTime = listeningTime
}

// Longest episode
if let longestEpisode = dataManager.longestEpisode(in: Self.year),
let podcast = longestEpisode.parentPodcast() {
data.longestEpisode = longestEpisode
data.longestEpisodePodcast = podcast
stories.append(.longestEpisode)
}
}

func story(for storyNumber: Int) -> any StoryView {
Expand All @@ -34,6 +41,8 @@ class EndOfYear2024StoriesModel: StoryModel {
return Top5Podcasts2024Story(top5Podcasts: data.topPodcasts)
case .listeningTime:
return ListeningTime2024Story(listeningTime: data.listeningTime)
case .longestEpisode:
return LongestEpisode2024Story(episode: data.longestEpisode, podcast: data.longestEpisodePodcast)
case .epilogue:
return EpilogueStory2024()
}
Expand Down Expand Up @@ -91,4 +100,9 @@ class EndOfYear2024StoriesData {
var topPodcasts: [TopPodcast] = []

var listeningTime: Double = 0

var longestEpisode: Episode!

var longestEpisodePodcast: Podcast!

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ enum EndOfYear2024Story: CaseIterable {
case intro
case top5Podcasts
case listeningTime
case longestEpisode
case epilogue
}
134 changes: 134 additions & 0 deletions podcasts/End of Year/Stories/2024/LongestEpisode2024Story.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import SwiftUI
import PocketCastsDataModel

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

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

var identifier: String = "longest_episode"

let episode: Episode

let podcast: Podcast

@State var firstCover: Double = 0.4
@State var secondCover: Double = 0.32
@State var thirdCover: Double = 0.24
@State var fourthCover: Double = 0.16
@State var fifthCover: Double = 0.08
@State var sixthCover: Double = 0

var backgroundColor = Color(hex: "#E0EFAD")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this color ever change? Can it be a let variable?


var tintColor: Color {
.white
}

var body: some View {
GeometryReader { geometry in
let isSmallScreen = geometry.size.height <= 600
VStack(alignment: .leading) {
Spacer()
ZStack {
covers()
let stickerSize = CGSize(width: 194, height: 135)
Image("playback-sticker-phew")
.resizable()
.frame(width: stickerSize.width, height: stickerSize.height)
.position(x: -6, y: 0, for: stickerSize, in: geometry.frame(in: .global), corner: .topTrailing)
}
.frame(width: geometry.size.width * 0.9)
.padding(.top, isSmallScreen ? 0 : 20)
VStack(alignment: .leading, spacing: isSmallScreen ? 4 : 16) {
let timeString = episode.playedUpTo.storyTimeDescriptionForSharing
Text("The longest episode you listened to was \(timeString)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the EOY English only? No need to localize these?

.font(.system(size: 31, weight: .bold))
Text("It was \"\(episode.title ?? "unknown")\" from \"\(podcast.title ?? "unknown")\"")
.font(.system(size: 15, weight: .light))
}
.padding(.horizontal, 24)
.padding(.bottom, isSmallScreen ? 4 : 16)
}
}
.background(backgroundColor)
.onAppear {
if animated {
setInitialCoverOffsetForAnimation()
animationViewModel.play()
}
}
}

@ViewBuilder func covers() -> some View {
GeometryReader { geometry in
PodcastCoverContainer(geometry: geometry) {
ZStack {
PodcastCover(podcastUuid: podcast.uuid)
.frame(width: geometry.size.width * 0.5, height: geometry.size.width * 0.5)
.offset(x: -geometry.size.width * firstCover, y: geometry.size.width * firstCover)
.modifier(animationViewModel.animate($firstCover, to: 0.4))

PodcastCover(podcastUuid: podcast.uuid)
.frame(width: geometry.size.width * 0.55, height: geometry.size.width * 0.55)
.offset(x: -geometry.size.width * secondCover, y: geometry.size.width * secondCover)
.modifier(animationViewModel.animate($secondCover, to: 0.32))

PodcastCover(podcastUuid: podcast.uuid)
.frame(width: geometry.size.width * 0.6, height: geometry.size.width * 0.6)
.offset(x: -geometry.size.width * thirdCover, y: geometry.size.width * thirdCover)
.modifier(animationViewModel.animate($thirdCover, to: 0.24))

PodcastCover(podcastUuid: podcast.uuid)
.frame(width: geometry.size.width * 0.65, height: geometry.size.width * 0.65)
.offset(x: -geometry.size.width * fourthCover, y: geometry.size.width * fourthCover)
.modifier(animationViewModel.animate($fourthCover, to: 0.16))

PodcastCover(podcastUuid: podcast.uuid)
.frame(width: geometry.size.width * 0.7, height: geometry.size.width * 0.7)
.offset(x: -geometry.size.width * fifthCover, y: geometry.size.width * fifthCover)
.modifier(animationViewModel.animate($fifthCover, to: 0.08))

PodcastCover(podcastUuid: podcast.uuid, higherQuality: true)
.frame(width: geometry.size.width * 0.75, height: geometry.size.width * 0.75)
.offset(x: -geometry.size.width * sixthCover, y: geometry.size.width * sixthCover)
.modifier(animationViewModel.animate($sixthCover, to: 0))
}
.offset(x: geometry.size.width * 0.04, y: geometry.size.height * 0.09)
}
}
}

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

func onPause() {
animationViewModel.pause()
}

func onResume() {
animationViewModel.play()
}

func willShare() {
Analytics.track(.endOfYearStoryShare, story: identifier)
}

func sharingAssets() -> [Any] {
[
StoryShareableProvider.new(AnyView(self)),
StoryShareableText(L10n.eoyStoryLongestEpisodeShareText("%1$@"), episode: episode)
]
}

private func setInitialCoverOffsetForAnimation() {
firstCover = 0.8
secondCover = 0.8
thirdCover = 0.8
fourthCover = 0.8
fifthCover = 0.8
sixthCover = 0.8
}
}
29 changes: 1 addition & 28 deletions podcasts/End of Year/Stories/Views/PodcastCover.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ struct PodcastCover: View {
/// If the artwork needs a bigger image with higher quality
var higherQuality: Bool = false

@State private var image: UIImage?
@Environment(\.renderForSharing) var renderForSharing: Bool

private var rectangleColor: Color? {
Expand All @@ -40,36 +39,10 @@ struct PodcastCover: View {
.modifier(NormalCoverShadow())
}
}
.opacity(image != nil ? 1 : 0.2)
.blendMode(.multiply)

ImageView(image: image)
PodcastImage(uuid: podcastUuid, size: .page)
.cornerRadius(big ? 8 : 4)

.onAppear {
if renderForSharing {
loadImage()
}
}

Action {
if !renderForSharing {
loadImage()
}
}
}
}

private func loadImage() {
image = nil
let size = higherQuality ? 680 : 280
KingfisherManager.shared.retrieveImage(with: ServerHelper.imageUrl(podcastUuid: podcastUuid, size: size)) { result in
switch result {
case .success(let result):
image = result.image
default:
break
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion podcasts/End of Year/Views/StoriesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ struct StoriesView: View {
// Manually set the zIndex order to ensure we can change the order when needed
model.story(index: model.currentStoryIndex)
.zIndex(3)
.ignoresSafeArea(edges: .bottom)
.modify {
if model.overlaidShareView() != nil {
$0.ignoresSafeArea(edges: .bottom)
}
}
.environment(\.animated, true)

if model.shouldShowUpsell() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "playback-sticker-phew.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.