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

[Hack Week] Downloads sort #2504

Draft
wants to merge 4 commits into
base: trunk
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public enum PlaylistSort: Int32 {
case newestToOldest = 0, oldestToNewest = 1, shortestToLongest = 2, longestToShortest = 3
}

public enum DownloadsSort: Int32 {
case newestToOldest = 0, oldestToNewest = 1, largestToSmallest = 2, smallestToLargest = 3
}

public struct EpisodeBasicData {
public init() {}

Expand Down
15 changes: 15 additions & 0 deletions podcasts/Analytics/AnalyticsDescribable+Modules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,18 @@ extension SocialAuthProvider: AnalyticsDescribable {
}
}
}

extension DownloadsSort: AnalyticsDescribable {
var analyticsDescription: String {
switch self {
case .newestToOldest:
return "newest_to_oldest"
case .oldestToNewest:
return "oldest_to_newest"
case .largestToSmallest:
return "largest_to_smallest"
case .smallestToLargest:
return "smallest_to_largest"
}
}
}
1 change: 1 addition & 0 deletions podcasts/Analytics/AnalyticsEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ enum AnalyticsEvent: String {
case downloadsMultiSelectEntered
case downloadsSelectAllButtonTapped
case downloadsMultiSelectExited
case downloadsSortByChanged

// MARK: - Downloads Clean Up View

Expand Down
1 change: 1 addition & 0 deletions podcasts/DownloadsViewController+Table.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extension DownloadsViewController: UITableViewDelegate, UITableViewDataSource {
if let episode = episodeAtIndexPath(indexPath) {
cell.populateFrom(episode: episode, tintColor: ThemeColor.primaryIcon01())
cell.shouldShowSelect = isMultiSelectEnabled
cell.showsFileSize = true
if isMultiSelectEnabled {
cell.showTick = selectedEpisodesContains(uuid: episode.uuid)
}
Expand Down
45 changes: 44 additions & 1 deletion podcasts/DownloadsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class DownloadsViewController: PCViewController {
operationQueue.addOperation { [weak self] in
guard let self else { return }

let newData = self.episodesDataManager.downloadedEpisodes()
let newData = self.episodesDataManager.downloadedEpisodes(sort: sort)

DispatchQueue.main.sync {
self.downloadsTable.isHidden = (newData.count == 0)
Expand All @@ -230,11 +230,39 @@ class DownloadsViewController: PCViewController {
dismiss(animated: true, completion: nil)
}

func showSortByPicker() {
let optionsPicker = OptionsPicker(title: L10n.sortBy.localizedUppercase)

addSortAction(to: optionsPicker, sortOrder: .newestToOldest)
addSortAction(to: optionsPicker, sortOrder: .oldestToNewest)
addSortAction(to: optionsPicker, sortOrder: .largestToSmallest)
addSortAction(to: optionsPicker, sortOrder: .smallestToLargest)

optionsPicker.show(statusBarStyle: AppTheme.defaultStatusBarStyle())
}

private var sort: DownloadsSort = .newestToOldest

private func addSortAction(to optionPicker: OptionsPicker, sortOrder: DownloadsSort) {
let action = OptionAction(label: sortOrder.description, selected: sort == sortOrder) {
Analytics.track(.downloadsSortByChanged, properties: ["sort_order": sortOrder])
self.sort = sortOrder
self.reloadEpisodes()
}
optionPicker.addAction(action: action)
}

@objc private func menuTapped(_ sender: UIBarButtonItem) {
Analytics.track(.downloadsOptionsButtonTapped)

let optionsPicker = OptionsPicker(title: nil)

let sortAction = OptionAction(label: L10n.sortBy, secondaryLabel: sort.description, icon: "podcastlist_sort") { [weak self] in
Analytics.track(.downloadsOptionsModalOptionTapped, properties: ["option": "sort_by"])
self?.showSortByPicker()
}
optionsPicker.addAction(action: sortAction)

let MultiSelectAction = OptionAction(label: L10n.selectEpisodes, icon: "option-multiselect") { [weak self] in
Analytics.track(.downloadsOptionsModalOptionTapped, properties: ["option": "select_episodes"])
self?.isMultiSelectEnabled = true
Expand Down Expand Up @@ -336,3 +364,18 @@ extension DownloadsViewController: AnalyticsSourceProvider {
.downloads
}
}

public extension DownloadsSort {
var description: String {
switch self {
case .newestToOldest:
return "Newest to Oldest"
case .oldestToNewest:
return "Oldest to Newest"
case .largestToSmallest:
return "Largest to Smallest"
case .smallestToLargest:
return "Smallest to Largest"
}
}
}
5 changes: 3 additions & 2 deletions podcasts/EpisodeCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class EpisodeCell: ThemeableSwipeCell, MainEpisodeActionViewDelegate {
}

var hidesArtwork = false
var showsFileSize = false

var playlist: AutoplayHelper.Playlist?

Expand Down Expand Up @@ -258,9 +259,9 @@ class EpisodeCell: ThemeableSwipeCell, MainEpisodeActionViewDelegate {
if episode.archived {
informationLabel.text = L10n.podcastArchived + " • " + episode.displayableInfo(includeSize: false)
} else if let userEpisode = episode as? UserEpisode {
informationLabel.text = userEpisode.displayableInfo(includeSize: Settings.primaryRowAction() == .download)
informationLabel.text = userEpisode.displayableInfo(includeSize: Settings.primaryRowAction() == .download || showsFileSize)
} else {
informationLabel.text = episode.displayableInfo(includeSize: Settings.primaryRowAction() == .download)
informationLabel.text = episode.displayableInfo(includeSize: Settings.primaryRowAction() == .download || showsFileSize)
}

if episode.downloading(), !downloadingIndicator.isAnimating {
Expand Down
59 changes: 55 additions & 4 deletions podcasts/EpisodesDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class EpisodesDataManager {
return episodes(for: filter).map { $0.episode }
}
case .downloads:
return downloadedEpisodes().flatMap { $0.elements.map { $0.episode } }
return downloadedEpisodes(sort: .newestToOldest).flatMap { $0.elements.map { $0.episode } }
case .files:
return uploadedEpisodes()
case .starred:
Expand Down Expand Up @@ -131,17 +131,68 @@ class EpisodesDataManager {

// MARK: - Downloads

func downloadedEpisodes() -> [ArraySection<String, ListEpisode>] {
let query = "( ((downloadTaskId IS NOT NULL AND autoDownloadStatus <> \(AutoDownloadStatus.playerDownloadedForStreaming.rawValue) ) OR episodeStatus = \(DownloadStatus.downloaded.rawValue) OR episodeStatus = \(DownloadStatus.waitingForWifi.rawValue)) OR (episodeStatus = \(DownloadStatus.downloadFailed.rawValue) AND lastDownloadAttemptDate > ?) ) ORDER BY lastDownloadAttemptDate DESC LIMIT 1000"
func downloadedEpisodes(sort: DownloadsSort) -> [ArraySection<String, ListEpisode>] {
let orderByColumn: String
let orderByOrder: String

switch sort {
case .newestToOldest:
orderByColumn = "lastDownloadAttemptDate"
orderByOrder = "DESC"
case .oldestToNewest:
orderByColumn = "lastDownloadAttemptDate"
orderByOrder = "ASC"
case .largestToSmallest:
orderByColumn = "sizeInBytes"
orderByOrder = "DESC"
case .smallestToLargest:
orderByColumn = "sizeInBytes"
orderByOrder = "ASC"
}

let query = "( ((downloadTaskId IS NOT NULL AND autoDownloadStatus <> \(AutoDownloadStatus.playerDownloadedForStreaming.rawValue) ) OR episodeStatus = \(DownloadStatus.downloaded.rawValue) OR episodeStatus = \(DownloadStatus.waitingForWifi.rawValue)) OR (episodeStatus = \(DownloadStatus.downloadFailed.rawValue) AND lastDownloadAttemptDate > ?) ) ORDER BY \(orderByColumn) \(orderByOrder) LIMIT 1000"

let arguments = [Date().weeksAgo(1)] as [Any]

let newData = EpisodeTableHelper.loadSectionedEpisodes(query: query, arguments: arguments, episodeShortKey: { episode -> String in
episode.shortLastDownloadAttemptDate()
switch sort {
case .newestToOldest, .oldestToNewest:
episode.shortLastDownloadAttemptDate()
case .smallestToLargest, .largestToSmallest:
categorizeFileSize(sizeInBytes: episode.sizeInBytes)
}
})

return newData
}

private func categorizeFileSize(sizeInBytes: Int64) -> String {
switch sizeInBytes {
case 0..<1_000_000: // Less than 1MB
return "< \(formattedFileSize(sizeInBytes: 1_000_000))"
case 1_000_000..<10_000_000: // 1MB to < 10MB
return "\(formattedFileSize(sizeInBytes: 1_000_000))-\(formattedFileSize(sizeInBytes: 10_000_000))"
case 10_000_000..<50_000_000: // 10MB to < 50MB
return "\(formattedFileSize(sizeInBytes: 10_000_000))-\(formattedFileSize(sizeInBytes: 50_000_000))"
case 50_000_000...100_000_000: // 50MB-100MB
return "\(formattedFileSize(sizeInBytes: 50_000_000))-\(formattedFileSize(sizeInBytes: 100_000_000))"
case 100_000_000...: // Greater than or equal to 100MB
return "> \(formattedFileSize(sizeInBytes: 100_000_000))"
default:
return ""
}
}

func formattedFileSize(sizeInBytes: Int) -> String {
let sizeInMB = Measurement(value: Double(sizeInBytes), unit: UnitInformationStorage.bytes).converted(to: .megabytes)

let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
formatter.numberFormatter.maximumFractionDigits = 2

return formatter.string(from: sizeInMB)
}

// MARK: - Listening History

func listeningHistoryEpisodes() -> [ArraySection<String, ListEpisode>] {
Expand Down
Loading