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

[Podcast Feed Reload] Add tooltip #2735

Merged
merged 5 commits into from
Feb 10, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
7.83
-----
- Add the functionality to reload the podcast feed [#2703](https://github.com/Automattic/pocket-casts-ios/issues/2703)


7.82
Expand Down
2 changes: 2 additions & 0 deletions podcasts/Analytics/AnalyticsEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -838,4 +838,6 @@ enum AnalyticsEvent: String {
case podcastScreenRefreshEpisodeList
case podcastScreenRefreshNoEpisodesFound
case podcastScreenRefreshNewEpisodeFound
case podcastRefreshEpisodeTooltipShown
case podcastRefreshEpisodeTooltipDismissed
}
3 changes: 3 additions & 0 deletions podcasts/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ struct Constants {
static let lastCheckDate = "manageDownloadsLastCheckDate"
}

enum podcastFeedReload {
static let showTip = "podcastFeedReload.showtip"
}
}

enum Values {
Expand Down
6 changes: 6 additions & 0 deletions podcasts/PodcastViewController+NetworkLoad.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ extension PodcastViewController {
self.podcast = podcast
summaryExpanded = !podcast.isSubscribed()

forceCollapsingHeaderIfNeeded()

if SyncManager.isUserLoggedIn() {
guard let episodes = ApiServerHandler.shared.retrieveEpisodeTaskSynchronouusly(podcastUuid: uuid) else { return }

Expand Down Expand Up @@ -81,6 +83,10 @@ extension PodcastViewController {
self.loadingIndicator.stopAnimating()
UIView.animate(withDuration: 0.5) {
self.episodesTable.alpha = 1
} completion: { success in
if FeatureFlag.podcastFeedUpdate.enabled {
self.showPodcastFeedReloadTipIfNeeded()
}
}
}
}
Expand Down
92 changes: 88 additions & 4 deletions podcasts/PodcastViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PocketCastsServer
import PocketCastsUtils
import UIKit
import UIDeviceIdentifier
import SwiftUI

enum PodcastFeedReloadSource {
case menu
Expand Down Expand Up @@ -193,6 +194,7 @@ class PodcastViewController: FakeNavViewController, PodcastActionsDelegate, Sync
private var cancellables = Set<AnyCancellable>()
private var podcastFeedViewModel: PodcastFeedViewModel?
private var refreshControl: CustomRefreshControl?
private var podcastFeedReloadTooltip: UIViewController?

lazy var ratingView: UIView = {
let view = StarRatingView(viewModel: podcastRatingViewModel,
Expand Down Expand Up @@ -253,6 +255,9 @@ class PodcastViewController: FakeNavViewController, PodcastActionsDelegate, Sync

if FeatureFlag.podcastFeedUpdate.enabled {
podcastFeedViewModel = PodcastFeedViewModel(uuid: podcast?.uuid ?? podcastInfo?.uuid)

// Let's collapse the header if the tooltip has never been showed before
forceCollapsingHeaderIfNeeded()
}

closeTapped = { [weak self] in
Expand Down Expand Up @@ -351,10 +356,6 @@ class PodcastViewController: FakeNavViewController, PodcastActionsDelegate, Sync
addCustomObserver(Constants.Notifications.podcastColorsDownloaded, selector: #selector(colorsDidDownload(_:)))
updateColors()

if FeatureFlag.podcastFeedUpdate.enabled {
refreshControl?.parentViewControllerDidAppear()
}

addCustomObserver(Constants.Notifications.episodeArchiveStatusChanged, selector: #selector(refreshEpisodes))
addCustomObserver(Constants.Notifications.manyEpisodesChanged, selector: #selector(refreshEpisodes))
addCustomObserver(Constants.Notifications.episodeStarredChanged, selector: #selector(refreshEpisodes))
Expand Down Expand Up @@ -389,6 +390,11 @@ class PodcastViewController: FakeNavViewController, PodcastActionsDelegate, Sync
properties["list_id"] = listUuid
}
Analytics.track(.podcastScreenShown, properties: properties)

if FeatureFlag.podcastFeedUpdate.enabled {
refreshControl?.parentViewControllerDidAppear()
showPodcastFeedReloadTipIfNeeded()
}
}

override func viewWillDisappear(_ animated: Bool) {
Expand Down Expand Up @@ -1033,6 +1039,73 @@ class PodcastViewController: FakeNavViewController, PodcastActionsDelegate, Sync
reloadPodcastFeed(source: .refreshControl)
}

private func dismissPodcastFeedReloadTip() {
guard Settings.shouldShowPodcastFeeReloadTip else {
return
}
Analytics.track(.podcastRefreshEpisodeTooltipDismissed)
Settings.shouldShowPodcastFeeReloadTip = false
podcastFeedReloadTooltip?.dismiss(animated: true) { [weak self] in
self?.podcastFeedReloadTooltip = nil
}
}

func forceCollapsingHeaderIfNeeded() {
if FeatureFlag.podcastFeedUpdate.enabled {
if Settings.shouldShowPodcastFeeReloadTip, summaryExpanded {
summaryExpanded = false
}
}
}

func showPodcastFeedReloadTipIfNeeded() {
guard
Settings.shouldShowPodcastFeeReloadTip,
FeatureFlag.podcastFeedUpdate.enabled,
podcastFeedReloadTooltip == nil
else {
return
}
if let vc = showPodcastFeedReloadTip() {
present(vc, animated: true) {
Analytics.track(.podcastRefreshEpisodeTooltipShown)
}
podcastFeedReloadTooltip = vc
}
}

private func showPodcastFeedReloadTip() -> UIViewController? {
guard let button = searchController?.overflowButton else {
return nil
}
let vc = UIHostingController(rootView: AnyView (EmptyView()) )
let idealSize = CGSizeMake(290, 100)
let tipView = TipViewStatic(title: L10n.podcastFeedReloadTipTitle,
message: L10n.podcastFeedReloadTipMessage,
onTap: { [weak self] in
self?.dismissPodcastFeedReloadTip()
})
.frame(idealWidth: idealSize.width, minHeight: idealSize.height)
.setupDefaultEnvironment()
vc.rootView = AnyView(tipView)
vc.view.backgroundColor = .clear
vc.view.clipsToBounds = false
vc.modalPresentationStyle = .popover
if #available(iOS 16.0, *) {
vc.sizingOptions = [.preferredContentSize]
} else {
vc.preferredContentSize = idealSize
}
if let popoverPresentationController = vc.popoverPresentationController {
popoverPresentationController.delegate = self
popoverPresentationController.permittedArrowDirections = [.down]
popoverPresentationController.sourceView = button
popoverPresentationController.sourceRect = button.bounds
popoverPresentationController.backgroundColor = ThemeColor.primaryUi01()
}
return vc
}

// MARK: - Long press actions

func archiveAll(startingAt: Episode) {
Expand Down Expand Up @@ -1091,3 +1164,14 @@ private extension PodcastViewController {
podcast?.uuid ?? podcastInfo?.analyticsDescription ?? "unknown"
}
}

extension PodcastViewController: UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
// Return no adaptive presentation style, use default presentation behaviour
return .none
}

func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
dismissPodcastFeedReloadTip()
}
}
11 changes: 11 additions & 0 deletions podcasts/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,17 @@ class Settings: NSObject {
}
}

// MARK: - Podcast Feed Reload

static var shouldShowPodcastFeeReloadTip: Bool {
get {
UserDefaults.standard.value(forKey: Constants.UserDefaults.podcastFeedReload.showTip) as? Bool ?? true
}
set {
UserDefaults.standard.setValue(newValue, forKey: Constants.UserDefaults.podcastFeedReload.showTip)
}
}

// MARK: - Manage Downloads

class var manageDownloadsLastCheckDate: Date? {
Expand Down
4 changes: 4 additions & 0 deletions podcasts/Strings+Generated.swift

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 36 additions & 23 deletions podcasts/TipView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,47 @@ struct TipView: View {

var body: some View {
ContentSizeGeometryReader { proxy in
VStack {
HStack {
VStack(alignment: .leading) {
Text(title)
.font(size: 15, style: .body, weight: .bold)
.foregroundColor(theme.primaryText01)
.lineLimit(2)
TipViewStatic(title: title, message: message, onTap: onTap)
} contentSizeUpdated: { size in
sizeChanged(size)
}
}
}

struct TipViewStatic: View {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@SergioEstevao I extracted the body to a new View as I had many problems with the size returned by the geometry proxy. Using a static view and setting this sizingOptions = [.preferredContentSize] seems to work properly each time.

let title: String
let message: String?
let onTap: (()->())?

@EnvironmentObject var theme: Theme

var body: some View {
VStack {
HStack {
VStack(alignment: .leading) {
Text(title)
.font(size: 15, style: .body, weight: .bold)
.foregroundColor(theme.primaryText01)
.lineLimit(2)
.multilineTextAlignment(.leading)
.fixedSize(horizontal: false, vertical: true)
if let message {
Text(message)
.font(size: 14, style: .body, weight: .regular)
.foregroundColor(theme.primaryText02)
.lineLimit(4)
.multilineTextAlignment(.leading)
.fixedSize(horizontal: false, vertical: true)
if let message {
Text(message)
.font(size: 14, style: .body, weight: .regular)
.foregroundColor(theme.primaryText02)
.lineLimit(4)
.multilineTextAlignment(.leading)
.fixedSize(horizontal: false, vertical: true)
}
.padding(.top, 2)
}
Spacer()
}
.padding(16)
.frame(maxHeight: .infinity)
.onTapGesture {
onTap?()
}
Spacer()
}
.padding(16)
.frame(maxHeight: .infinity)
.onTapGesture {
onTap?()
}
} contentSizeUpdated: { size in
sizeChanged(size)
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions podcasts/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -4752,3 +4752,9 @@

/* After the podcast feed reloading completes, this is the message we display if there's no new episodes to load */
"podcast_feed_reload_no_episodes_found" = "No episodes found.";

/* Title used in the tooltip showed in the podcast view controller the first time the view appears */
"podcast_feed_reload_tip_title" = "Fresh episodes, coming right up!";

/* Description used in the tooltip showed in the podcast view controller the first time the view appears */
"podcast_feed_reload_tip_message" = "Pull down or use this menu to see if there's something new.";