message: consolidate if statements into single lines, cleanup

diff:
@@ -40,22 +40,18 @@ class BookmarkViewController: UIViewController, UITableViewDelegate, UITableView
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
- }
-
- override func viewDidAppear(_ animated: Bool) {
- super.viewDidAppear(animated)
// check before reloading data whether the Player has stopped playing the currentlyPlayingIndexPath
- if let indexPath = currentlyPlayingIndexPath {
- if let card = cards[indexPath.row] as? EpisodeCard {
- if Player.sharedInstance.currentEpisode?.id != card.episode.id {
+ if let indexPath = currentlyPlayingIndexPath, let card = cards[indexPath.row] as? EpisodeCard, Player.sharedInstance.currentEpisode?.id != card.episode.id {
currentlyPlayingIndexPath = nil
}
- }
- }
bookmarkTableView.reloadData()
}
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ }
+
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
@@ -110,18 +106,13 @@ class BookmarkViewController: UIViewController, UITableViewDelegate, UITableView
func cardTableViewCellDidPressPlayPauseButton(cardTableViewCell: CardTableViewCell) {
- guard let cardIndexPath = bookmarkTableView.indexPath(for: cardTableViewCell), let card = cards[cardIndexPath.row] as? EpisodeCard else { return }
-
- if cardIndexPath == currentlyPlayingIndexPath { return }
+ guard let cardIndexPath = bookmarkTableView.indexPath(for: cardTableViewCell), let card = cards[cardIndexPath.row] as? EpisodeCard, cardIndexPath != currentlyPlayingIndexPath, let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
- if let indexPath = currentlyPlayingIndexPath {
- if let cell = bookmarkTableView.cellForRow(at: indexPath) as? CardTableViewCell {
+ if let indexPath = currentlyPlayingIndexPath, let cell = bookmarkTableView.cellForRow(at: indexPath) as? CardTableViewCell {
cell.setPlayButtonToState(isPlaying: false)
}
- }
currentlyPlayingIndexPath = cardIndexPath
cardTableViewCell.setPlayButtonToState(isPlaying: true)
- let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.showPlayer(animated: true)
Player.sharedInstance.playEpisode(episode: card.episode)
}

diff:
@@ -48,22 +48,18 @@ class FeedViewController: UIViewController, UITableViewDelegate, UITableViewData
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
- }
-
- override func viewDidAppear(_ animated: Bool) {
- super.viewDidAppear(animated)
// check before reloading data whether the Player has stopped playing the currentlyPlayingIndexPath
- if let indexPath = currentlyPlayingIndexPath {
- if let card = cards[indexPath.row] as? EpisodeCard {
- if Player.sharedInstance.currentEpisode?.id != card.episode.id {
+ if let indexPath = currentlyPlayingIndexPath, let card = cards[indexPath.row] as? EpisodeCard, Player.sharedInstance.currentEpisode?.id != card.episode.id {
currentlyPlayingIndexPath = nil
}
- }
- }
feedTableView.reloadData()
}
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ }
+
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
@@ -118,18 +114,13 @@ class FeedViewController: UIViewController, UITableViewDelegate, UITableViewData
func cardTableViewCellDidPressPlayPauseButton(cardTableViewCell: CardTableViewCell) {
- guard let cardIndexPath = feedTableView.indexPath(for: cardTableViewCell), let card = cards[cardIndexPath.row] as? EpisodeCard else { return }
-
- if cardIndexPath == currentlyPlayingIndexPath { return }
+ guard let cardIndexPath = feedTableView.indexPath(for: cardTableViewCell), let card = cards[cardIndexPath.row] as? EpisodeCard, cardIndexPath != currentlyPlayingIndexPath, let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
- if let indexPath = currentlyPlayingIndexPath {
- if let cell = feedTableView.cellForRow(at: indexPath) as? CardTableViewCell {
+ if let indexPath = currentlyPlayingIndexPath, let cell = feedTableView.cellForRow(at: indexPath) as? CardTableViewCell {
cell.setPlayButtonToState(isPlaying: false)
}
- }
currentlyPlayingIndexPath = cardIndexPath
cardTableViewCell.setPlayButtonToState(isPlaying: true)
- let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.showPlayer(animated: true)
Player.sharedInstance.playEpisode(episode: card.episode)
}


message: avoid waiting to minimize stalling when avplayeritem is ready

change_type: MODIFY

diff:
@@ -40,7 +40,7 @@ class Episode: NSObject {
self.smallArtworkImageURL = smallArtworkImageURL
self.largeArtworkImageURL = largeArtworkImageURL
if audioURL == nil { //TAKE THIS OUT LATER ONLY FOR PLAYER STATIC DATA
- self.audioURL = URL(string: "https://play.podtrac.com/npr-344098539/npr.mc.tritondigital.com/WAITWAIT_PODCAST/media/anon.npr-podcasts/podcast/344098539/517256651/npr_517256651.mp3?orgId=1&d=2981&p=344098539&story=517256651&t=podcast&e=517256651&ft=pod&f=344098539")
+ self.audioURL = URL(string: "https://play.podtrac.com/npr-510298/npr.mc.tritondigital.com/NPR_510298/media/anon.npr-mp3/npr/ted/2017/02/20170215_ted_tedpod.mp3?orgId=1&d=3241&p=510298&story=515438384&t=podcast&e=515438384&ft=pod&f=510298")
} else {
self.audioURL = audioURL
}

diff:
@@ -12,6 +12,9 @@ class Player: NSObject {
static let sharedInstance = Player()
private override init() {
player = AVPlayer()
+ if #available(iOS 10, *) {
+ player.automaticallyWaitsToMinimizeStalling = false
+ }
autoplayEnabled = true
currentItemPrepared = false
isScrubbing = false
@@ -59,6 +62,9 @@ class Player: NSObject {
print(error)
}
player = AVPlayer()
+ if #available(iOS 10, *) {
+ player.automaticallyWaitsToMinimizeStalling = false
+ }
}
// cleanup any previous AVPlayerItem

license: MIT License

repo: cuappdev/podcast-ios


message: connect recommended episode cells to the player

diff:
@@ -73,6 +73,11 @@ class DiscoverViewController: UIViewController, UITableViewDelegate, UITableView
episodes = Array(repeating: episode, count: 5)
}
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ tableView.reloadData()
+ }
+
//MARK: - TableView DataSource & Delegate
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@@ -86,6 +91,7 @@ class DiscoverViewController: UIViewController, UITableViewDelegate, UITableView
} else if let cell = cell as? RecommendedEpisodesOuterTableViewCell {
cell.dataSource = self
cell.delegate = self
+ cell.updateUIForNowPlayingEpisode(episode: Player.sharedInstance.currentEpisode)
}
return cell
}
@@ -173,7 +179,9 @@ class DiscoverViewController: UIViewController, UITableViewDelegate, UITableView
}
func recommendedEpisodeOuterTableViewCellDidPressPlayButton(episodeTableViewCell: EpisodeTableViewCell, episode: Episode) {
-
+ guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+ appDelegate.showPlayer(animated: true)
+ Player.sharedInstance.playEpisode(episode: episode)
}
func recommendedEpisodeOuterTableViewCellDidPressBookmarkButton(episodeTableViewCell: EpisodeTableViewCell, episode: Episode) {

diff:
@@ -27,6 +27,7 @@ class RecommendedEpisodesOuterTableViewCell: UITableViewCell, UITableViewDelegat
var tableView: UITableView!
var dataSource: RecommendedEpisodesOuterTableViewCellDataSource?
var delegate: RecommendedEpisodesOuterTableViewCellDelegate?
+ var currentlyPlayingIndexPath: IndexPath?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
@@ -56,6 +57,9 @@ class RecommendedEpisodesOuterTableViewCell: UITableViewCell, UITableViewDelegat
let episode = dataSource?.recommendedEpisodesTableViewCell(cell: self, dataForItemAt: indexPath) ?? Episode()
cell.setupWithEpisode(episode: episode)
cell.delegate = self
+ if indexPath == currentlyPlayingIndexPath {
+ cell.setPlayButtonToState(isPlaying: true)
+ }
return cell
}
@@ -80,6 +84,13 @@ class RecommendedEpisodesOuterTableViewCell: UITableViewCell, UITableViewDelegat
func episodeTableViewCellDidPressPlayPauseButton(episodeTableViewCell: EpisodeTableViewCell) {
guard let episodeIndexPath = tableView.indexPath(for: episodeTableViewCell), let episode = dataSource?.recommendedEpisodesTableViewCell(cell: self, dataForItemAt: episodeIndexPath) else { return }
+ if let previousIndexPath = currentlyPlayingIndexPath, previousIndexPath != episodeIndexPath, let previousCell = tableView.cellForRow(at: previousIndexPath) as? EpisodeTableViewCell {
+ previousCell.setPlayButtonToState(isPlaying: false)
+ }
+
+ currentlyPlayingIndexPath = episodeIndexPath
+
+ episodeTableViewCell.setPlayButtonToState(isPlaying: true)
delegate?.recommendedEpisodeOuterTableViewCellDidPressPlayButton(episodeTableViewCell: episodeTableViewCell, episode: episode)
}
@@ -104,4 +115,12 @@ class RecommendedEpisodesOuterTableViewCell: UITableViewCell, UITableViewDelegat
func episodeTableViewCellDidPressMoreActionsButton(episodeTableViewCell: EpisodeTableViewCell) {
delegate?.recommendedEpisodesOuterTableViewCellDidPressShowActionSheet(episodeTableViewCell: episodeTableViewCell)
}
+
+ func updateUIForNowPlayingEpisode(episode: Episode?) {
+ guard let indexPath = currentlyPlayingIndexPath, let previousEpisode = dataSource?.recommendedEpisodesTableViewCell(cell: self, dataForItemAt: indexPath) else { return }
+ // check if previously playing episode is no longer playing
+ if previousEpisode.id != episode?.id {
+ (tableView.cellForRow(at: indexPath) as? EpisodeTableViewCell)?.setPlayButtonToState(isPlaying: false)
+ }
+ }
}

diff:
@@ -85,8 +85,14 @@ class SearchEpisodeTableViewCell: UITableViewCell {
}
func didPressPlayButton() {
- playButtonActivated = !playButtonActivated
- playButton.isSelected = playButtonActivated
delegate?.searchEpisodeTableViewCell(cell: self, didSetPlayButton: playButtonActivated)
}
+
+ func setPlayButtonToState(isPlaying: Bool) {
+ if isPlaying {
+ playButton.isSelected = true
+ } else {
+ playButton.isSelected = false
+ }
+ }
}


message: connect search cells to player

diff:
import UIKit
protocol SearchEpisodeTableViewCellDelegate: class {
- func searchEpisodeTableViewCell(cell: SearchEpisodeTableViewCell, didSetPlayButton toNewValue: Bool)
+ func searchEpisodeTableViewCellDidPressPlayButton(cell: SearchEpisodeTableViewCell)
}
class SearchEpisodeTableViewCell: UITableViewCell {
@@ -85,7 +85,7 @@ class SearchEpisodeTableViewCell: UITableViewCell {
}
func didPressPlayButton() {
- delegate?.searchEpisodeTableViewCell(cell: self, didSetPlayButton: playButtonActivated)
+ delegate?.searchEpisodeTableViewCellDidPressPlayButton(cell: self)
}
func setPlayButtonToState(isPlaying: Bool) {

diff:
@@ -36,7 +36,7 @@ protocol SearchTableViewControllerDelegate {
func searchTableViewControllerNeedsFetch(controller: SearchTableViewController)
}
-class SearchTableViewController: UITableViewController {
+class SearchTableViewController: UITableViewController, SearchEpisodeTableViewCellDelegate {
var searchType: SearchType = .episodes
let cellIdentifiersClasses: [SearchType: (String, AnyClass)] =
@@ -58,6 +58,8 @@ class SearchTableViewController: UITableViewController {
var cellDelegate: SearchTableViewControllerDelegate?
+ var currentlyPlayingIndexPath: IndexPath?
+
override func viewDidLoad() {
super.viewDidLoad()
guard let (cellIdentifier, cellClass) = cellIdentifiersClasses[searchType] else { return }
@@ -69,6 +71,13 @@ class SearchTableViewController: UITableViewController {
}
}
+ override func viewDidAppear(_ animated: Bool) {
+ if let indexPath = currentlyPlayingIndexPath, let cell = tableView.cellForRow(at: indexPath) as? SearchEpisodeTableViewCell {
+ cell.setPlayButtonToState(isPlaying: false)
+ }
+ currentlyPlayingIndexPath = nil
+ }
+
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
@@ -88,6 +97,12 @@ class SearchTableViewController: UITableViewController {
case .episodes:
guard let episodes = results as? [Episode], let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? SearchEpisodeTableViewCell else { return UITableViewCell() }
cell.configure(for: episodes[indexPath.row], index: indexPath.row)
+ if indexPath == currentlyPlayingIndexPath {
+ cell.setPlayButtonToState(isPlaying: true)
+ } else {
+ cell.setPlayButtonToState(isPlaying: false)
+ }
+ cell.delegate = self
return cell
case .series:
guard let series = results as? [Series], let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? SearchSeriesTableViewCell else { return UITableViewCell() }
@@ -130,4 +145,16 @@ class SearchTableViewController: UITableViewController {
return [searchTableViewControllerEpisodes, searchTableViewControllerSeries, searchTableViewControllerPeople, searchTableViewControllerTags]
}
+
+ func searchEpisodeTableViewCellDidPressPlayButton(cell: SearchEpisodeTableViewCell) {
+ guard let indexPath = tableView.indexPath(for: cell), indexPath != currentlyPlayingIndexPath, let episode = searchResults[.episodes]?[indexPath.row] as? Episode, let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+
+ if let previousIndexPath = currentlyPlayingIndexPath, let previousCell = tableView.cellForRow(at: previousIndexPath) as? SearchEpisodeTableViewCell {
+ previousCell.setPlayButtonToState(isPlaying: false)
+ }
+ currentlyPlayingIndexPath = indexPath
+ cell.setPlayButtonToState(isPlaying: true)
+ appDelegate.showPlayer(animated: true)
+ Player.sharedInstance.playEpisode(episode: episode)
+ }
}

original_message: connect search cells to player


message: implement basic episode detail VC without comments

diff:
objects = {
/* Begin PBXBuildFile section */
+ 420BC2DE1E9C8E650096A9A0 /* EpisodeDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420BC2DD1E9C8E650096A9A0 /* EpisodeDetailViewController.swift */; };
+ 420BC2E01E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */; };
421501271D9F5FE1002839D1 /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421501261D9F5FE1002839D1 /* Player.swift */; };
4253114D1E5E4CC500DF06F6 /* MiniPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4253114C1E5E4CC500DF06F6 /* MiniPlayerView.swift */; };
425B393D1E512F5D00283CA4 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425B393C1E512F5D00283CA4 /* PlayerControlsView.swift */; };
- 425C90621E5A5827004BB7AD /* EpisodeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425C90611E5A5827004BB7AD /* EpisodeDetailView.swift */; };
+ 425C90621E5A5827004BB7AD /* PlayerEpisodeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425C90611E5A5827004BB7AD /* PlayerEpisodeDetailView.swift */; };
4290028A1E5CA6A20069F0BF /* PlayerHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429002891E5CA6A20069F0BF /* PlayerHeaderView.swift */; };
42D7D98D1D80C557000691A4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D7D98C1D80C557000691A4 /* AppDelegate.swift */; };
42D7D9941D80C557000691A4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42D7D9931D80C557000691A4 /* Assets.xcassets */; };
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 420BC2DD1E9C8E650096A9A0 /* EpisodeDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailViewController.swift; sourceTree = "<group>"; };
+ 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailHeaderView.swift; sourceTree = "<group>"; };
421501261D9F5FE1002839D1 /* Player.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = "<group>"; };
4253114C1E5E4CC500DF06F6 /* MiniPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiniPlayerView.swift; sourceTree = "<group>"; };
425B393C1E512F5D00283CA4 /* PlayerControlsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerControlsView.swift; sourceTree = "<group>"; };
- 425C90611E5A5827004BB7AD /* EpisodeDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailView.swift; sourceTree = "<group>"; };
+ 425C90611E5A5827004BB7AD /* PlayerEpisodeDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerEpisodeDetailView.swift; sourceTree = "<group>"; };
429002891E5CA6A20069F0BF /* PlayerHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerHeaderView.swift; sourceTree = "<group>"; };
42D7D9891D80C557000691A4 /* Podcast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Podcast.app; sourceTree = BUILT_PRODUCTS_DIR; };
42D7D98C1D80C557000691A4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
isa = PBXGroup;
children = (
425B393C1E512F5D00283CA4 /* PlayerControlsView.swift */,
- 425C90611E5A5827004BB7AD /* EpisodeDetailView.swift */,
+ 425C90611E5A5827004BB7AD /* PlayerEpisodeDetailView.swift */,
429002891E5CA6A20069F0BF /* PlayerHeaderView.swift */,
4253114C1E5E4CC500DF06F6 /* MiniPlayerView.swift */,
);
9522B0801E709D57009B5584 /* ActionSheetViewController.swift */,
D30D37481E75FF8900E9260B /* TagViewController.swift */,
D3B9A0571E861DB200B634FF /* ListeningHistoryViewController.swift */,
+ 420BC2DD1E9C8E650096A9A0 /* EpisodeDetailViewController.swift */,
);
name = Controllers;
sourceTree = "<group>";
FDE8D6EE1E6FC23000DF7548 /* InternalProfileHeaderView.swift */,
443ED30C1E745C5D001CFFF5 /* PlayButton.swift */,
D30D374A1E76082700E9260B /* TagTableViewHeader.swift */,
+ 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */,
);
name = Views;
sourceTree = "<group>";
9522B0811E709D57009B5584 /* ActionSheetViewController.swift in Sources */,
447DA8571E5C99610002BCA0 /* DiscoverViewController.swift in Sources */,
44E471091E63D85300CC7101 /* Int+.swift in Sources */,
+ 420BC2DE1E9C8E650096A9A0 /* EpisodeDetailViewController.swift in Sources */,
D3E269271E5E7AF200793569 /* Card.swift in Sources */,
D3E2692D1E5E7FA400793569 /* ReleaseCard.swift in Sources */,
D39BEE1B1D8A050100131936 /* Episode.swift in Sources */,
443ED3051E6E3319001CFFF5 /* SearchPeopleTableViewCell.swift in Sources */,
443ED31B1E7D9BA0001CFFF5 /* SearchUsersEndpointRequest.swift in Sources */,
9522B0831E70ECE2009B5584 /* UIViewController+.swift in Sources */,
+ 420BC2E01E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift in Sources */,
FD295FDD1E636A3D00520526 /* FillButton.swift in Sources */,
D3C31B4C1E6D28A00056E926 /* SubscriptionsViewController.swift in Sources */,
956D851C1E51942500D95F7A /* CMTime+.swift in Sources */,
441278C71E69E03C00D5B2AC /* UnderlineTabBarView.swift in Sources */,
FD8F7DFC1DDD20FC00DCC511 /* SeriesCollectionViewCell.swift in Sources */,
D354024C1E67C131008B8A0C /* SearchUser.swift in Sources */,
- 425C90621E5A5827004BB7AD /* EpisodeDetailView.swift in Sources */,
+ 425C90621E5A5827004BB7AD /* PlayerEpisodeDetailView.swift in Sources */,
FD015A001DCBFC80003E7B4E /* ProfileMiniHeader.swift in Sources */,
42F684ED1D80CC9F0035217A /* FeedViewController.swift in Sources */,
443ED3011E6E10E6001CFFF5 /* SearchEpisodeTableViewCell.swift in Sources */,

diff:
@@ -179,7 +179,9 @@ class DiscoverViewController: UIViewController, UITableViewDelegate, UITableView
}
func recommendedEpisodesOuterTableViewCell(cell: RecommendedEpisodesOuterTableViewCell, didSelectItemAt indexPath: IndexPath) {
- print("Selected episode at \(indexPath.row)")
+ let episodeViewController = EpisodeDetailViewController()
+ episodeViewController.episode = episodes[indexPath.row]
+ navigationController?.pushViewController(episodeViewController, animated: true)
}
func recommendedEpisodeOuterTableViewCellDidPressPlayButton(episodeTableViewCell: EpisodeTableViewCell, episode: Episode) {

change_type: RENAME

diff:
import UIKit
-class EpisodeDetailView: UIView {
+class PlayerEpisodeDetailView: UIView {
var episodeArtworkImageView: UIImageView!
var seriesTitleLabel: UILabel!
@@ -72,7 +72,7 @@ class EpisodeDetailView: UIView {
episodeArtworkImageView.image = #imageLiteral(resourceName: "sample_series_artwork")
seriesTitleLabel.text = episode.seriesTitle
episodeTitleLabel.text = episode.title
- dateLabel.text = "November 7, 2016"
+ dateLabel.text = episode.dateTimeSeriesString()
descriptionLabel.text = episode.descriptionText
layoutUI()
}

diff:
@@ -5,7 +5,7 @@ import CoreMedia
class PlayerViewController: TabBarAccessoryViewController, PlayerDelegate, PlayerHeaderViewDelegate, MiniPlayerViewDelegate, PlayerControlsDelegate {
var controlsView: PlayerControlsView!
- var episodeDetailView: EpisodeDetailView!
+ var episodeDetailView: PlayerEpisodeDetailView!
var playerHeaderView: PlayerHeaderView!
var miniPlayerView: MiniPlayerView!
var isCollapsed: Bool = false
@@ -26,7 +26,7 @@ class PlayerViewController: TabBarAccessoryViewController, PlayerDelegate, Playe
miniPlayerView.delegate = self
view.addSubview(miniPlayerView)
- episodeDetailView = EpisodeDetailView(frame: .zero)
+ episodeDetailView = PlayerEpisodeDetailView(frame: .zero)
episodeDetailView.frame.size.width = view.frame.width
episodeDetailView.frame.origin.y = playerHeaderView.frame.maxY
view.addSubview(episodeDetailView)


message: add episode detail vc transition to search

diff:
@@ -244,4 +244,10 @@ class DiscoverViewController: UIViewController, UITableViewDelegate, UITableView
tagViewController.tag = tag
navigationController?.pushViewController(tagViewController, animated: true)
}
+
+ func didTapOnEpisodeCell(episode: Episode) {
+ let episodeViewController = EpisodeDetailViewController()
+ episodeViewController.episode = episode
+ navigationController?.pushViewController(episodeViewController, animated: true)
+ }
}

diff:
@@ -84,8 +84,21 @@ class EpisodeDetailHeaderView: UIView {
func setupForEpisode(episode: Episode) {
// TODO: load from URL
episodeArtworkImageView.image = #imageLiteral(resourceName: "filler_image")
+ if let url = episode.smallArtworkImageURL {
+ if let data = try? Data(contentsOf: url) {
+ episodeArtworkImageView.image = UIImage(data: data)
+ }
+ }
+
+ if episode.seriesTitle != "" {
+ seriesTitleLabel.text = "Wait Wait Don't Tell Me"
+ } else {
seriesTitleLabel.text = episode.seriesTitle
- publisherLabel.text = "Publisher >"
+ }
+
+ // TODO: implement publishers
+ publisherLabel.text = "NPR >"
+
episodeTitleLabel.text = episode.title
episodeTitleLabel.sizeToFit()
dateLabel.text = episode.dateTimeSeriesString()

diff:
@@ -19,6 +19,7 @@ protocol TabbedPageViewControllerScrollDelegate: class {
protocol TabbedViewControllerSearchResultsControllerDelegate: class {
func didTapOnSeriesCell(series: Series)
func didTapOnTagCell(tag: Tag)
+ func didTapOnEpisodeCell(episode: Episode)
}
class TabbedPageViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UISearchResultsUpdating, TabBarDelegate, SearchTableViewControllerDelegate, UINavigationControllerDelegate {
@@ -260,9 +261,8 @@ class TabbedPageViewController: UIViewController, UIPageViewControllerDataSource
//add new views here
switch(searchType) {
case .episodes:
- //present episode detail view here
- let dummyViewController = UIViewController()
- presentingViewController?.navigationController?.pushViewController(dummyViewController, animated: true)
+ guard let episode = searchResults[.episodes]?[index] as? Episode else { return }
+ searchResultsDelegate?.didTapOnEpisodeCell(episode: episode)
case .series:
guard let series = searchResults[.series]?[index] as? Series else { return }
searchResultsDelegate?.didTapOnSeriesCell(series: series)


message: add rough comment to episode detail

diff:
420BC2DE1E9C8E650096A9A0 /* EpisodeDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420BC2DD1E9C8E650096A9A0 /* EpisodeDetailViewController.swift */; };
420BC2E01E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */; };
421501271D9F5FE1002839D1 /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421501261D9F5FE1002839D1 /* Player.swift */; };
+ 422575171E9DEDE10071E419 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422575161E9DEDE10071E419 /* Comment.swift */; };
+ 422575191E9DF0260071E419 /* CommentsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422575181E9DF0260071E419 /* CommentsTableViewCell.swift */; };
+ 4225751B1E9E71630071E419 /* CommentsTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4225751A1E9E71630071E419 /* CommentsTableViewHeader.swift */; };
4253114D1E5E4CC500DF06F6 /* MiniPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4253114C1E5E4CC500DF06F6 /* MiniPlayerView.swift */; };
425B393D1E512F5D00283CA4 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425B393C1E512F5D00283CA4 /* PlayerControlsView.swift */; };
425C90621E5A5827004BB7AD /* PlayerEpisodeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 425C90611E5A5827004BB7AD /* PlayerEpisodeDetailView.swift */; };
420BC2DD1E9C8E650096A9A0 /* EpisodeDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailViewController.swift; sourceTree = "<group>"; };
420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailHeaderView.swift; sourceTree = "<group>"; };
421501261D9F5FE1002839D1 /* Player.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = "<group>"; };
+ 422575161E9DEDE10071E419 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = "<group>"; };
+ 422575181E9DF0260071E419 /* CommentsTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentsTableViewCell.swift; sourceTree = "<group>"; };
+ 4225751A1E9E71630071E419 /* CommentsTableViewHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentsTableViewHeader.swift; sourceTree = "<group>"; };
4253114C1E5E4CC500DF06F6 /* MiniPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiniPlayerView.swift; sourceTree = "<group>"; };
425B393C1E512F5D00283CA4 /* PlayerControlsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerControlsView.swift; sourceTree = "<group>"; };
425C90611E5A5827004BB7AD /* PlayerEpisodeDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerEpisodeDetailView.swift; sourceTree = "<group>"; };
D360AB891E6F80400036B051 /* SeriesGridCollectionViewCell.swift */,
FDE8D6EC1E6FAB2F00DF7548 /* InternalProfileTableViewCell.swift */,
D3B9A0591E861EB800B634FF /* ListeningHistoryTableViewCell.swift */,
+ 422575181E9DF0260071E419 /* CommentsTableViewCell.swift */,
);
name = Cells;
sourceTree = "<group>";
D354024D1E67C224008B8A0C /* SearchSeries.swift */,
D354024F1E67C5C4008B8A0C /* Session.swift */,
D353AF361E8F5CBF003B3D88 /* SubscriptionSeries.swift */,
+ 422575161E9DEDE10071E419 /* Comment.swift */,
);
name = Models;
sourceTree = "<group>";
443ED30C1E745C5D001CFFF5 /* PlayButton.swift */,
D30D374A1E76082700E9260B /* TagTableViewHeader.swift */,
420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */,
+ 4225751A1E9E71630071E419 /* CommentsTableViewHeader.swift */,
);
name = Views;
sourceTree = "<group>";
447DA8621E5C99790002BCA0 /* RecommendedTagsCollectionViewCell.swift in Sources */,
D30761721E8D9DE30047EA6F /* FetchEpisodesBySeriesIdEndpointRequest.swift in Sources */,
D357FB4D1E5105AA00C71857 /* CardTableViewCell.swift in Sources */,
+ 422575171E9DEDE10071E419 /* Comment.swift in Sources */,
443ED3111E78E908001CFFF5 /* SearchAllEndpointRequest.swift in Sources */,
9568DFE11E5F5ADD006D4131 /* TabBarAccessoryViewController.swift in Sources */,
D3DB5A681DA1B19800E550E8 /* PodcastCollectionViewFlowLayout.swift in Sources */,
447DA8661E5C99810002BCA0 /* RecommendedSeriesCollectionViewFlowLayout.swift in Sources */,
FD8F7DFE1DDD36D600DCC511 /* ProfileSectionHeaderView.swift in Sources */,
+ 4225751B1E9E71630071E419 /* CommentsTableViewHeader.swift in Sources */,
44D0EA951E6C8F3C00C3339B /* SearchTagTableViewCell.swift in Sources */,
FDD71F341E801BBE0060BBB0 /* BookmarkTableViewCell.swift in Sources */,
956D85141E51926800D95F7A /* EndpointRequestQueue.swift in Sources */,
95FD28F61E77AF8800A52506 /* UpdateSessionEndpointRequest.swift in Sources */,
D307616C1E8CCA000047EA6F /* CreateUserSubscriptioEndpointRequest.swift in Sources */,
44AA26091E842F2A00C74FAD /* SearchEndpointRequest.swift in Sources */,
+ 422575191E9DF0260071E419 /* CommentsTableViewCell.swift in Sources */,
D353AF371E8F5CBF003B3D88 /* SubscriptionSeries.swift in Sources */,
D354024E1E67C224008B8A0C /* SearchSeries.swift in Sources */,
447DA85C1E5C996E0002BCA0 /* RecommendedEpisodesOuterTableViewCell.swift in Sources */,

diff:
@@ -80,9 +80,9 @@ class BookmarkViewController: UIViewController, UITableViewDelegate, UITableView
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-// guard let cell = bookmarkTableView.cellForRow(at: indexPath) as? BookmarkTableViewCell else { return }
-
- // Open Episode Detail View here
+ let episodeViewController = EpisodeDetailViewController()
+ episodeViewController.episode = episodes[indexPath.row]
+ navigationController?.pushViewController(episodeViewController, animated: true)
}

diff:
@@ -15,6 +15,8 @@ class EpisodeDetailHeaderView: UIView {
var episodeTitleLabel: UILabel!
var dateLabel: UILabel!
var descriptionLabel: UILabel!
+ var playButton: PlayButton!
+ // TODO: likeButton, bookmarkButton, moreButton
let marginSpacing: CGFloat = 18
let artworkDimension: CGFloat = 79
@@ -31,11 +33,16 @@ class EpisodeDetailHeaderView: UIView {
let dateLabelYSpacing: CGFloat = 6
let descriptionLabelYSpacing: CGFloat = 6
+
+ let playButtonYSpacing: CGFloat = 24
+ let playButtonWidth: CGFloat = 47
+ let playButtonHeight: CGFloat = 18
+
let bottomSpacing: CGFloat = 20
override init(frame: CGRect) {
super.init(frame: frame)
- backgroundColor = .white
+ backgroundColor = UIColor.colorFromCode(0xfcfcfe)
episodeArtworkImageView = UIImageView(frame: CGRect(x: marginSpacing,
y: marginSpacing,
@@ -75,6 +82,9 @@ class EpisodeDetailHeaderView: UIView {
descriptionLabel.lineBreakMode = .byWordWrapping
descriptionLabel.numberOfLines = 0
addSubview(descriptionLabel)
+
+ playButton = PlayButton(frame: CGRect(x: marginSpacing, y: 0, width: playButtonWidth, height: playButtonHeight))
+ addSubview(playButton)
}
required init?(coder aDecoder: NSCoder) {
@@ -82,9 +92,8 @@ class EpisodeDetailHeaderView: UIView {
}
func setupForEpisode(episode: Episode) {
- // TODO: load from URL
episodeArtworkImageView.image = #imageLiteral(resourceName: "filler_image")
- if let url = episode.smallArtworkImageURL {
+ if let url = episode.largeArtworkImageURL {
if let data = try? Data(contentsOf: url) {
episodeArtworkImageView.image = UIImage(data: data)
}
@@ -101,13 +110,14 @@ class EpisodeDetailHeaderView: UIView {
episodeTitleLabel.text = episode.title
episodeTitleLabel.sizeToFit()
- dateLabel.text = episode.dateTimeSeriesString()
+ dateLabel.text = episode.dateString()
dateLabel.sizeToFit()
dateLabel.frame.origin.y = episodeTitleLabel.frame.maxY + dateLabelYSpacing
descriptionLabel.text = episode.descriptionText
descriptionLabel.sizeToFit()
descriptionLabel.frame.origin.y = dateLabel.frame.maxY + descriptionLabelYSpacing
- frame.size.height = descriptionLabel.frame.maxY + bottomSpacing
+ playButton.frame.origin.y = descriptionLabel.frame.maxY + playButtonYSpacing
+ frame.size.height = playButton.frame.maxY + bottomSpacing
}
}

diff:
import UIKit
-class EpisodeDetailViewController: UIViewController {
+class EpisodeDetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
+ var scrollView: UIScrollView!
var episodeDetailHeaderView: EpisodeDetailHeaderView!
var episode: Episode?
+ var commentsTableViewHeader: CommentsTableViewHeader!
+ var commentsTableView: UITableView!
+ var comments: [Comment] = Array.init(repeating: Comment(episodeId: "", creator: "Mark Bryan", text: "Great point here!!", creationDate: "5 months ago", time: "5:13"), count: 10)
override func viewDidLoad() {
super.viewDidLoad()
- view.backgroundColor = .white
+ scrollView = UIScrollView(frame: view.frame)
+ scrollView.backgroundColor = .white
+ scrollView.contentSize = CGSize(width: view.frame.width, height: 0)
+ view.addSubview(scrollView)
- episodeDetailHeaderView = EpisodeDetailHeaderView(frame: CGRect(x: 0, y: navigationController?.navigationBar.frame.maxY ?? 0, width: self.view.frame.width, height: 0))
- view.addSubview(episodeDetailHeaderView)
+ episodeDetailHeaderView = EpisodeDetailHeaderView(frame: CGRect(x: 0, y: 0, width: scrollView.frame.width, height: 0))
+ scrollView.addSubview(episodeDetailHeaderView)
+
+ commentsTableViewHeader = CommentsTableViewHeader(frame: CGRect(x: 0, y: 0, width: scrollView.frame.width, height: 0))
+ scrollView.addSubview(commentsTableViewHeader)
+
+ commentsTableView = UITableView(frame: .zero)
+ commentsTableView.dataSource = self
+ commentsTableView.delegate = self
+ commentsTableView.register(CommentsTableViewCell.self, forCellReuseIdentifier: "CommentsTableViewCellIdentifier")
+ scrollView.addSubview(commentsTableView)
}
override func viewWillAppear(_ animated: Bool) {
guard let episode = episode else { return }
episodeDetailHeaderView.setupForEpisode(episode: episode)
+ commentsTableViewHeader.frame.origin = CGPoint(x: 0, y: episodeDetailHeaderView.frame.maxY)
+ commentsTableView.frame = CGRect(x: 0, y: commentsTableViewHeader.frame.maxY, width: scrollView.frame.width, height: 300)
+ scrollView.contentSize.height = commentsTableView.frame.maxY
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ guard let cell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableViewCellIdentifier") as? CommentsTableViewCell else { return UITableViewCell() }
+ cell.setupForComment(comment: comments[indexPath.row])
+ commentsTableView.rowHeight = cell.frame.height
+ return cell
+ }
+
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return comments.count
}
}

diff:
@@ -10,8 +10,12 @@ import UIKit
class PlayButton: UIButton {
- init() {
- super.init(frame: .zero)
+ convenience init() {
+ self.init(frame: .zero)
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
setImage(#imageLiteral(resourceName: "play_feed_icon"), for: .normal)
setImage(#imageLiteral(resourceName: "play_feed_icon_selected"), for: .selected)
setTitle(" Play", for: .normal)

diff:
@@ -72,7 +72,7 @@ class PlayerEpisodeDetailView: UIView {
episodeArtworkImageView.image = #imageLiteral(resourceName: "sample_series_artwork")
seriesTitleLabel.text = episode.seriesTitle
episodeTitleLabel.text = episode.title
- dateLabel.text = episode.dateTimeSeriesString()
+ dateLabel.text = episode.dateString()
descriptionLabel.text = episode.descriptionText
layoutUI()
}

original_message: add rough comment to episode detail


message: addresses pr comments

diff:
import UIKit
-class Comment: NSObject {
+class Comment {
var episodeId: String
var creator: String
var text: String

diff:
@@ -15,7 +15,7 @@ protocol CommentsTableViewCellDelegate {
class CommentsTableViewCell: UITableViewCell {
let minimumHeight: CGFloat = 200
- let imageViewDimension: CGFloat = 30
+ let imageViewSize: CGSize = CGSize(width: 30, height: 30)
let marginSpacing: CGFloat = 18
let padding: CGFloat = 12
let leftInset: CGFloat = 60
@@ -36,7 +36,9 @@ class CommentsTableViewCell: UITableViewCell {
backgroundColor = UIColor.colorFromCode(0xfcfcfe)
frame.size.height = minimumHeight
- commenterImageView = UIImageView(frame: CGRect(x: marginSpacing, y: marginSpacing, width: imageViewDimension, height: imageViewDimension))
+ commenterImageView = UIImageView(frame: CGRect(origin: CGPoint.init(x: marginSpacing, y: marginSpacing), size: imageViewSize))
+ commenterImageView.layer.cornerRadius = imageViewSize.height/2
+ commenterImageView.clipsToBounds = true
addSubview(commenterImageView)
commenterNameLabel = UILabel(frame: CGRect(x: commenterImageView.frame.maxX + padding, y: marginSpacing, width: 0, height: 0))

diff:
@@ -45,7 +45,6 @@ class MiniPlayerView: UIView {
}
arrowButton = UIButton(frame: CGRect(x: marginSpacing, y: arrowYValue, width: arrowWidth, height: arrowHeight))
- arrowButton.addTarget(self, action: #selector(arrowButtonTapped), for: .touchUpInside)
arrowButton.setBackgroundImage(#imageLiteral(resourceName: "down_arrow_icon"), for: .normal)
addSubview(arrowButton)
@@ -59,6 +58,8 @@ class MiniPlayerView: UIView {
episodeTitleLabel.textAlignment = .left
episodeTitleLabel.lineBreakMode = .byWordWrapping
addSubview(episodeTitleLabel)
+
+ addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(viewTapped)))
}
func updateUIForPlayback(isPlaying: Bool) {
@@ -78,7 +79,7 @@ class MiniPlayerView: UIView {
playPauseButton.setBackgroundImage(#imageLiteral(resourceName: "mini_play_icon"), for: .normal)
}
- func arrowButtonTapped() {
+ func viewTapped() {
delegate?.miniPlayerViewDidTapExpandButton()
}

diff:
@@ -10,10 +10,6 @@ import UIKit
class PlayButton: UIButton {
- convenience init() {
- self.init(frame: .zero)
- }
-
override init(frame: CGRect) {
super.init(frame: frame)
setImage(#imageLiteral(resourceName: "play_feed_icon"), for: .normal)

original_message: addresses pr comments


message: fix episode detail and add html labels

diff:
/* Begin PBXBuildFile section */
420BC2DE1E9C8E650096A9A0 /* EpisodeDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420BC2DD1E9C8E650096A9A0 /* EpisodeDetailViewController.swift */; };
- 420BC2E01E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */; };
+ 420BC2E01E9D4B900096A9A0 /* EpisodeDetailHeaderViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderViewCell.swift */; };
421501271D9F5FE1002839D1 /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421501261D9F5FE1002839D1 /* Player.swift */; };
422575171E9DEDE10071E419 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422575161E9DEDE10071E419 /* Comment.swift */; };
422575191E9DF0260071E419 /* CommentsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422575181E9DF0260071E419 /* CommentsTableViewCell.swift */; };
/* Begin PBXFileReference section */
420BC2DD1E9C8E650096A9A0 /* EpisodeDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailViewController.swift; sourceTree = "<group>"; };
- 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailHeaderView.swift; sourceTree = "<group>"; };
+ 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeDetailHeaderViewCell.swift; sourceTree = "<group>"; };
421501261D9F5FE1002839D1 /* Player.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = "<group>"; };
422575161E9DEDE10071E419 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = "<group>"; };
422575181E9DF0260071E419 /* CommentsTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentsTableViewCell.swift; sourceTree = "<group>"; };
D3B9A0591E861EB800B634FF /* ListeningHistoryTableViewCell.swift */,
44DA91CE1EA04D07001FE6AC /* PastSearchTableViewCell.swift */,
422575181E9DF0260071E419 /* CommentsTableViewCell.swift */,
+ 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderViewCell.swift */,
);
name = Cells;
sourceTree = "<group>";
FDE8D6EE1E6FC23000DF7548 /* InternalProfileHeaderView.swift */,
443ED30C1E745C5D001CFFF5 /* PlayButton.swift */,
D30D374A1E76082700E9260B /* TagTableViewHeader.swift */,
- 420BC2DF1E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift */,
4225751A1E9E71630071E419 /* CommentsTableViewHeader.swift */,
);
name = Views;
443ED3051E6E3319001CFFF5 /* SearchPeopleTableViewCell.swift in Sources */,
443ED31B1E7D9BA0001CFFF5 /* SearchUsersEndpointRequest.swift in Sources */,
9522B0831E70ECE2009B5584 /* UIViewController+.swift in Sources */,
- 420BC2E01E9D4B900096A9A0 /* EpisodeDetailHeaderView.swift in Sources */,
+ 420BC2E01E9D4B900096A9A0 /* EpisodeDetailHeaderViewCell.swift in Sources */,
FD295FDD1E636A3D00520526 /* FillButton.swift in Sources */,
D3C31B4C1E6D28A00056E926 /* SubscriptionsViewController.swift in Sources */,
956D851C1E51942500D95F7A /* CMTime+.swift in Sources */,

diff:
@@ -14,7 +14,7 @@ protocol CommentsTableViewCellDelegate {
}
class CommentsTableViewCell: UITableViewCell {
- let minimumHeight: CGFloat = 200
+ static let minimumHeight: CGFloat = 95
let imageViewSize: CGSize = CGSize(width: 30, height: 30)
let marginSpacing: CGFloat = 18
let padding: CGFloat = 12
@@ -34,7 +34,8 @@ class CommentsTableViewCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor.colorFromCode(0xfcfcfe)
- frame.size.height = minimumHeight
+ selectionStyle = .none
+ frame.size.height = CommentsTableViewCell.minimumHeight
commenterImageView = UIImageView(frame: CGRect(origin: CGPoint.init(x: marginSpacing, y: marginSpacing), size: imageViewSize))
commenterImageView.layer.cornerRadius = imageViewSize.height/2

diff:
import UIKit
class CommentsTableViewHeader: UIView {
- let headerHeight: CGFloat = 48
+ static let headerHeight: CGFloat = 48
let commentsLabelXValue: CGFloat = 18
let commentsLabelYValue: CGFloat = 18
@@ -17,7 +17,7 @@ class CommentsTableViewHeader: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
- self.frame.size.height = headerHeight
+ self.frame.size.height = CommentsTableViewHeader.headerHeight
backgroundColor = UIColor.colorFromCode(0xf0f1f4)
commentsLabel = UILabel(frame: CGRect(x: commentsLabelXValue, y: commentsLabelYValue, width: 0, height: 0))

diff:
@@ -108,4 +108,15 @@ class Episode: NSObject {
return dateFormatter.string(from: dateCreated)
}
+ func attributedDescriptionString() -> NSAttributedString {
+ let modifiedFont = NSString(format:"%@" as NSString, descriptionText) as String
+
+ let attrStr = try! NSAttributedString(
+ data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
+ options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue],
+ documentAttributes: nil)
+
+ return attrStr
+ }
+
}

diff:
import UIKit
-class EpisodeDetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
- var scrollView: UIScrollView!
- var episodeDetailHeaderView: EpisodeDetailHeaderView!
+class EpisodeDetailViewController: UITableViewController, EpisodeDetailHeaderViewCellDelegate {
var episode: Episode?
- var commentsTableViewHeader: CommentsTableViewHeader!
- var commentsTableView: UITableView!
var comments: [Comment] = Array.init(repeating: Comment(episodeId: "", creator: "Mark Bryan", text: "Great point here!!", creationDate: "5 months ago", time: "5:13"), count: 10)
+ var episodeDetailViewHeaderHeight: CGFloat?
override func viewDidLoad() {
super.viewDidLoad()
- scrollView = UIScrollView(frame: view.frame)
- scrollView.backgroundColor = .white
- scrollView.contentSize = CGSize(width: view.frame.width, height: 0)
- view.addSubview(scrollView)
-
- episodeDetailHeaderView = EpisodeDetailHeaderView(frame: CGRect(x: 0, y: 0, width: scrollView.frame.width, height: 0))
- scrollView.addSubview(episodeDetailHeaderView)
-
- commentsTableViewHeader = CommentsTableViewHeader(frame: CGRect(x: 0, y: 0, width: scrollView.frame.width, height: 0))
- scrollView.addSubview(commentsTableViewHeader)
-
- commentsTableView = UITableView(frame: .zero)
- commentsTableView.dataSource = self
- commentsTableView.delegate = self
- commentsTableView.register(CommentsTableViewCell.self, forCellReuseIdentifier: "CommentsTableViewCellIdentifier")
- scrollView.addSubview(commentsTableView)
+ guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+ tableView.dataSource = self
+ tableView.delegate = self
+ tableView.register(CommentsTableViewCell.self, forCellReuseIdentifier: "CommentsTableViewCellIdentifier")
+ tableView.register(EpisodeDetailHeaderViewCell.self, forCellReuseIdentifier: "EpisodeDetailHeaderViewCellIdentifier")
+ tableView.contentInset = UIEdgeInsetsMake(0, 0, appDelegate.tabBarController.tabBarHeight, 0)
}
override func viewWillAppear(_ animated: Bool) {
- guard let episode = episode else { return }
- episodeDetailHeaderView.setupForEpisode(episode: episode)
- commentsTableViewHeader.frame.origin = CGPoint(x: 0, y: episodeDetailHeaderView.frame.maxY)
- commentsTableView.frame = CGRect(x: 0, y: commentsTableViewHeader.frame.maxY, width: scrollView.frame.width, height: 300)
- scrollView.contentSize.height = commentsTableView.frame.maxY
+ super.viewWillAppear(animated)
+ tableView.reloadData()
+ tableView.setNeedsLayout()
+ tableView.layoutIfNeeded()
+ tableView.reloadData()
}
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- guard let cell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableViewCellIdentifier") as? CommentsTableViewCell else { return UITableViewCell() }
+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ if indexPath.section == 0 {
+ if let cell = tableView.dequeueReusableCell(withIdentifier: "EpisodeDetailHeaderViewCellIdentifier") as? EpisodeDetailHeaderViewCell, let episode = episode {
+ cell.setupForEpisode(episode: episode)
+ cell.delegate = self
+ cell.layoutSubviews()
+ episodeDetailViewHeaderHeight = cell.frame.height
+ return cell
+ }
+ } else {
+ if let cell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableViewCellIdentifier") as? CommentsTableViewCell {
cell.setupForComment(comment: comments[indexPath.row])
- commentsTableView.rowHeight = cell.frame.height
return cell
}
+ }
+ return UITableViewCell()
+ }
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ if section == 0 {
+ return 1
+ }
return comments.count
}
+ override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+ if indexPath.section == 0 {
+ return episodeDetailViewHeaderHeight ?? 0
+ } else {
+ return CommentsTableViewCell.minimumHeight
+ }
+ }
+
+ override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+ if section == 1 {
+ return CommentsTableViewHeader(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: CommentsTableViewHeader.headerHeight))
+ }
+ return nil
+ }
+
+ override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+ if section == 1 {
+ return CommentsTableViewHeader.headerHeight
+ }
+ return 0
+ }
+
+ override func numberOfSections(in tableView: UITableView) -> Int {
+ return 2
+ }
+
+ // EpisodeDetailHeaderViewCellDelegate methods
+
+ func episodeDetailHeaderDidPressRecommendButton(cell: EpisodeDetailHeaderViewCell) {
+
+ }
+
+ func episodeDetailHeaderDidPressMoreButton(cell: EpisodeDetailHeaderViewCell) {
+
+ }
+
+ func episodeDetailHeaderDidPressPlayButton(cell: EpisodeDetailHeaderViewCell) {
+ guard let episode = episode, let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+ cell.setPlaying(playing: true)
+ appDelegate.showPlayer(animated: true)
+ Player.sharedInstance.playEpisode(episode: episode)
+ }
+
+ func episodeDetailHeaderDidPressBookmarkButton(cell: EpisodeDetailHeaderViewCell) {
+
+ }
+
}

diff:
@@ -17,6 +17,7 @@ class PlayButton: UIButton {
setTitle(" Play", for: .normal)
setTitle(" Now Playing", for: .selected)
titleLabel?.font = .systemFont(ofSize: 12, weight: UIFontWeightRegular)
+ contentHorizontalAlignment = .left
setTitleColor(.podcastBlack, for: .normal)
setTitleColor(.podcastGreenBlue, for: .selected)
}

original_message: fix episode detail and add html labels


message: connect episode detail to feed

diff:
@@ -86,9 +86,10 @@ class FeedViewController: UIViewController, UITableViewDelegate, UITableViewData
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-
-// guard let cell = feedTableView.cellForRow(at: indexPath) as? CardTableViewCell else { return }
-
+ guard let episodeCard = cards[indexPath.row] as? EpisodeCard else { return }
+ let episodeViewController = EpisodeDetailViewController()
+ episodeViewController.episode = episodeCard.episode
+ navigationController?.pushViewController(episodeViewController, animated: true)
}

original_message: connect episode detail to feed


message: connect episode detail to series detail

diff:
@@ -224,6 +224,14 @@ class SeriesDetailViewController: UIViewController, SeriesDetailHeaderViewDelega
return headerView
}
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ if let episode = series?.episodes[indexPath.row] {
+ let episodeViewController = EpisodeDetailViewController()
+ episodeViewController.episode = episode
+ navigationController?.pushViewController(episodeViewController, animated: true)
+ }
+ }
+
func episodeTableViewCellDidPressPlayPauseButton(episodeTableViewCell: EpisodeTableViewCell) {
// guard let episodeIndexPath = epsiodeTableView.indexPath(for: episodeTableViewCell) else { return }

original_message: connect episode detail to series detail


message: connect episode detail to listening history

diff:
@@ -75,8 +75,8 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
y: seriesTitleLabelYValue,
width: frame.width - marginSpacing - seriesTitleLabelXValue,
height: seriesTitleHeight))
- seriesTitleLabel.font = UIFont.boldSystemFont(ofSize: 16)
- seriesTitleLabel.textColor = UIColor.colorFromCode(0x4aaea9)
+ seriesTitleLabel.font = UIFont.boldSystemFont(ofSize: 20)
+ seriesTitleLabel.textColor = UIColor.colorFromCode(0x30303c)
addSubview(seriesTitleLabel)
publisherLabel = UILabel(frame: CGRect(x: publisherLabelXValue,
@@ -118,8 +118,8 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
addSubview(moreButton)
bookmarkButton = UIButton(frame: .zero)
- bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_unselected_icon"), for: .normal)
- bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_selected_icon"), for: .selected)
+ bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_feed_icon_unselected"), for: .normal)
+ bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_feed_icon_selected"), for: .selected)
bookmarkButton.addTarget(self, action: #selector(bookmarkButtonTapped), for: .touchUpInside)
addSubview(bookmarkButton)
@@ -184,14 +184,17 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
func bookmarkButtonTapped() {
delegate?.episodeDetailHeaderDidPressBookmarkButton(cell: self)
+ bookmarkButton.isSelected = !bookmarkButton.isSelected
}
func moreButtonTapped() {
delegate?.episodeDetailHeaderDidPressMoreButton(cell: self)
+ moreButton.isSelected = !moreButton.isSelected
}
func recommendedButtonTapped() {
delegate?.episodeDetailHeaderDidPressRecommendButton(cell: self)
+ recommendedButton.isSelected = !recommendedButton.isSelected
}
}

diff:
@@ -11,7 +11,7 @@ import UIKit
class EpisodeDetailViewController: UITableViewController, EpisodeDetailHeaderViewCellDelegate {
var episode: Episode?
var comments: [Comment] = Array.init(repeating: Comment(episodeId: "", creator: "Mark Bryan", text: "Great point here!!", creationDate: "5 months ago", time: "5:13"), count: 10)
- var episodeDetailViewHeaderHeight: CGFloat?
+ var episodeDetailViewHeaderHeight: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
@@ -32,20 +32,16 @@ class EpisodeDetailViewController: UITableViewController, EpisodeDetailHeaderVie
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- if indexPath.section == 0 {
- if let cell = tableView.dequeueReusableCell(withIdentifier: "EpisodeDetailHeaderViewCellIdentifier") as? EpisodeDetailHeaderViewCell, let episode = episode {
+ if indexPath.section == 0, let cell = tableView.dequeueReusableCell(withIdentifier: "EpisodeDetailHeaderViewCellIdentifier") as? EpisodeDetailHeaderViewCell, let episode = episode {
cell.setupForEpisode(episode: episode)
cell.delegate = self
cell.layoutSubviews()
episodeDetailViewHeaderHeight = cell.frame.height
return cell
- }
- } else {
- if let cell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableViewCellIdentifier") as? CommentsTableViewCell {
+ } else if let cell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableViewCellIdentifier") as? CommentsTableViewCell {
cell.setupForComment(comment: comments[indexPath.row])
return cell
}
- }
return UITableViewCell()
}
@@ -58,7 +54,7 @@ class EpisodeDetailViewController: UITableViewController, EpisodeDetailHeaderVie
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
- return episodeDetailViewHeaderHeight ?? 0
+ return episodeDetailViewHeaderHeight
} else {
return CommentsTableViewCell.minimumHeight
}

diff:
@@ -61,9 +61,9 @@ class ListeningHistoryViewController: UIViewController, UITableViewDelegate, UIT
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-// guard let cell = listeningHistoryTableView.cellForRow(at: indexPath) as? ListeningHistoryTableViewCell else { return }
-
- // Open Episode Detail View here
+ let episodeViewController = EpisodeDetailViewController()
+ episodeViewController.episode = episodes[indexPath.row]
+ navigationController?.pushViewController(episodeViewController, animated: true)
}

diff:
@@ -46,6 +46,7 @@ class MiniPlayerView: UIView {
arrowButton = UIButton(frame: CGRect(x: marginSpacing, y: arrowYValue, width: arrowWidth, height: arrowHeight))
arrowButton.setBackgroundImage(#imageLiteral(resourceName: "down_arrow_icon"), for: .normal)
+ arrowButton.addTarget(self, action: #selector(viewTapped), for: .touchUpInside)
addSubview(arrowButton)
playPauseButton = UIButton(frame: CGRect(x: frame.size.width - marginSpacing - buttonDimension, y: 17, width: buttonDimension, height: buttonDimension))


message: use attributed strings in all episode cells

diff:
@@ -355,7 +355,7 @@ class CardTableViewCell: UITableViewCell {
}
dateTimeLabel.text = episodeCard.episode.dateTimeSeriesString()
- descriptionLabel.text = episodeCard.episode.descriptionText
+ descriptionLabel.attributedText = episodeCard.episode.attributedDescriptionString()
recommendedButton.setTitle(episodeCard.episode.numberOfRecommendations.shortString(), for: .normal)
if let url = episodeCard.episode.smallArtworkImageURL {
if let data = try? Data(contentsOf: url) {

diff:
@@ -246,7 +246,7 @@ class EpisodeTableViewCell: UITableViewCell {
}
dateTimeLabel.text = episode.dateTimeSeriesString()
- descriptionLabel.text = episode.descriptionText
+ descriptionLabel.attributedText = episode.attributedDescriptionString()
recommendedButton.setTitle(episode.numberOfRecommendations.shortString(), for: .normal)
if let url = episode.smallArtworkImageURL {
if let data = try? Data(contentsOf: url) {


message: refactor to use custom buttons

diff:
42D7D9AD1D80C557000691A4 /* PodcastUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D7D9AC1D80C557000691A4 /* PodcastUITests.swift */; };
42F684EB1D80CC7E0035217A /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F684EA1D80CC7E0035217A /* PlayerViewController.swift */; };
42F684ED1D80CC9F0035217A /* FeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F684EC1D80CC9F0035217A /* FeedViewController.swift */; };
+ 42F9942E1EB1B3DB00B78BB2 /* MoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F9942D1EB1B3DB00B78BB2 /* MoreButton.swift */; };
+ 42F994301EB1B57600B78BB2 /* BookmarkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F9942F1EB1B57600B78BB2 /* BookmarkButton.swift */; };
+ 42F994321EB1B5B100B78BB2 /* RecommendButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F994311EB1B5B100B78BB2 /* RecommendButton.swift */; };
+ 42F994341EB1BCE200B78BB2 /* FeedControlButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F994331EB1BCE200B78BB2 /* FeedControlButton.swift */; };
441278C71E69E03C00D5B2AC /* UnderlineTabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 441278C61E69E03C00D5B2AC /* UnderlineTabBarView.swift */; };
441278C91E69FC0400D5B2AC /* TabbedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 441278C81E69FC0400D5B2AC /* TabbedPageViewController.swift */; };
441278CB1E6A112B00D5B2AC /* SearchTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 441278CA1E6A112B00D5B2AC /* SearchTableViewController.swift */; };
42D7D9AE1D80C557000691A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
42F684EA1D80CC7E0035217A /* PlayerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = "<group>"; };
42F684EC1D80CC9F0035217A /* FeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedViewController.swift; sourceTree = "<group>"; };
+ 42F9942D1EB1B3DB00B78BB2 /* MoreButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoreButton.swift; sourceTree = "<group>"; };
+ 42F9942F1EB1B57600B78BB2 /* BookmarkButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkButton.swift; sourceTree = "<group>"; };
+ 42F994311EB1B5B100B78BB2 /* RecommendButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecommendButton.swift; sourceTree = "<group>"; };
+ 42F994331EB1BCE200B78BB2 /* FeedControlButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedControlButton.swift; sourceTree = "<group>"; };
441278C61E69E03C00D5B2AC /* UnderlineTabBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnderlineTabBarView.swift; sourceTree = "<group>"; };
441278C81E69FC0400D5B2AC /* TabbedPageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabbedPageViewController.swift; sourceTree = "<group>"; };
441278CA1E6A112B00D5B2AC /* SearchTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchTableViewController.swift; sourceTree = "<group>"; };
path = PodcastUITests;
sourceTree = "<group>";
};
+ 42F9942C1EB1B3C500B78BB2 /* Buttons */ = {
+ isa = PBXGroup;
+ children = (
+ 443ED30C1E745C5D001CFFF5 /* PlayButton.swift */,
+ 42F9942D1EB1B3DB00B78BB2 /* MoreButton.swift */,
+ 42F9942F1EB1B57600B78BB2 /* BookmarkButton.swift */,
+ 42F994311EB1B5B100B78BB2 /* RecommendButton.swift */,
+ 42F994331EB1BCE200B78BB2 /* FeedControlButton.swift */,
+ FD295FDC1E636A3D00520526 /* FillButton.swift */,
+ );
+ name = Buttons;
+ sourceTree = "<group>";
+ };
956D85151E51926C00D95F7A /* Networking */ = {
isa = PBXGroup;
children = (
FD9421401DB5306200685E14 /* Views */ = {
isa = PBXGroup;
children = (
+ 42F9942C1EB1B3C500B78BB2 /* Buttons */,
FD9421411DB5307B00685E14 /* ProfileHeaderView.swift */,
FD0159FF1DCBFC80003E7B4E /* ProfileMiniHeader.swift */,
425B393B1E512E8000283CA4 /* Player */,
FD8F7DFD1DDD36D600DCC511 /* ProfileSectionHeaderView.swift */,
447DA8681E5C998E0002BCA0 /* DiscoverTableViewHeaderView.swift */,
FD20F5731E52186500721C46 /* SeriesDetailHeaderView.swift */,
- FD295FDC1E636A3D00520526 /* FillButton.swift */,
441278C61E69E03C00D5B2AC /* UnderlineTabBarView.swift */,
D3738B011E67D793002ECD3F /* TagButtonsView.swift */,
FDE8D6EE1E6FC23000DF7548 /* InternalProfileHeaderView.swift */,
- 443ED30C1E745C5D001CFFF5 /* PlayButton.swift */,
D30D374A1E76082700E9260B /* TagTableViewHeader.swift */,
4225751A1E9E71630071E419 /* CommentsTableViewHeader.swift */,
D34731611EB1136C00E61E56 /* LoginBackgroundGradientView.swift */,
D3B9A05A1E861EB800B634FF /* ListeningHistoryTableViewCell.swift in Sources */,
7F97FBDA1D85A99F0073FC0F /* Constants.swift in Sources */,
D3B41F501E689D9500F68BD6 /* DateFormatter+.swift in Sources */,
+ 42F994301EB1B57600B78BB2 /* BookmarkButton.swift in Sources */,
D3628F721E6F6C5200F96C68 /* Date+.swift in Sources */,
FDE8D6EF1E6FC23000DF7548 /* InternalProfileHeaderView.swift in Sources */,
447DA8621E5C99790002BCA0 /* RecommendedTagsCollectionViewCell.swift in Sources */,
952BD1181E66A2AE006C0370 /* BookmarkViewController.swift in Sources */,
FDE8D6ED1E6FAB2F00DF7548 /* InternalProfileTableViewCell.swift in Sources */,
FD4A9BAF1E70C751001BA0BD /* SettingsViewController.swift in Sources */,
+ 42F994341EB1BCE200B78BB2 /* FeedControlButton.swift in Sources */,
D3E269291E5E7B4200793569 /* EpisodeCard.swift in Sources */,
D35402501E67C5C4008B8A0C /* Session.swift in Sources */,
D3E2692B1E5E7CBF00793569 /* RecommendedCard.swift in Sources */,
D307616C1E8CCA000047EA6F /* CreateUserSubscriptioEndpointRequest.swift in Sources */,
44AA26091E842F2A00C74FAD /* SearchEndpointRequest.swift in Sources */,
422575191E9DF0260071E419 /* CommentsTableViewCell.swift in Sources */,
+ 42F994321EB1B5B100B78BB2 /* RecommendButton.swift in Sources */,
D353AF371E8F5CBF003B3D88 /* GridSeries.swift in Sources */,
D354024E1E67C224008B8A0C /* SearchSeries.swift in Sources */,
447DA85C1E5C996E0002BCA0 /* RecommendedEpisodesOuterTableViewCell.swift in Sources */,
441278C91E69FC0400D5B2AC /* TabbedPageViewController.swift in Sources */,
42D7D98D1D80C557000691A4 /* AppDelegate.swift in Sources */,
D39BEE1D1D8A073200131936 /* Series.swift in Sources */,
+ 42F9942E1EB1B3DB00B78BB2 /* MoreButton.swift in Sources */,
42F684EB1D80CC7E0035217A /* PlayerViewController.swift in Sources */,
D39BEE1F1D8A0B3B00131936 /* User.swift in Sources */,
FDF1D1CF1DAEE2E300697379 /* ExternalProfileViewController.swift in Sources */,

diff:
import UIKit
protocol BookmarkTableViewCellDelegate: class {
-
func bookmarkTableViewCellDidPressPlayPauseButton(bookmarksTableViewCell: BookmarkTableViewCell)
func bookmarkTableViewCellDidPressRecommendButton(bookmarksTableViewCell: BookmarkTableViewCell)
func bookmarkTableViewCellDidPressMoreActionsButton(bookmarksTableViewCell: BookmarkTableViewCell)
@@ -32,8 +31,6 @@ class BookmarkTableViewCell: UITableViewCell {
let dateTimeLabelY: CGFloat = 41.5
let dateTimeLabelHeight: CGFloat = 14.5
- let buttonTitlePadding: CGFloat = 7
-
let playButtonX: CGFloat = 90
let playButtonBottomY: CGFloat = 18
let playButtonHeight: CGFloat = 15
@@ -57,9 +54,9 @@ class BookmarkTableViewCell: UITableViewCell {
var episodeImage: UIImageView!
var episodeNameLabel: UILabel!
var dateTimeLabel: UILabel!
- var recommendedButton: UIButton!
- var moreButton: UIButton!
- var playButton: UIButton!
+ var recommendedButton: RecommendButton!
+ var moreButton: MoreButton!
+ var playButton: PlayButton!
var separator: UIView!
weak var delegate: BookmarkTableViewCellDelegate?
@@ -98,36 +95,16 @@ class BookmarkTableViewCell: UITableViewCell {
dateTimeLabel.numberOfLines = 1
addSubview(dateTimeLabel)
- playButton = UIButton(type: .custom)
- playButton.setTitleColor(.podcastGrayDark, for: .normal)
- playButton.setImage(#imageLiteral(resourceName: "bookmarks_play_small"), for: .normal)
- playButton.setTitle("Play", for: .normal)
- playButton.setTitleColor(.podcastGrayDark, for: .disabled)
- playButton.setImage(#imageLiteral(resourceName: "play_feed_icon_selected"), for: .disabled)
- playButton.setTitle("Playing", for: .disabled)
- playButton.adjustsImageWhenDisabled = false
- playButton.contentHorizontalAlignment = .left
- playButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: buttonTitlePadding, bottom: 0, right: 0)
- playButton.titleLabel?.textColor = .podcastGrayDark
- playButton.titleLabel?.font = .systemFont(ofSize: 12)
- playButton.addTarget(self, action: #selector(didPressPlayButton), for: .touchUpInside)
- addSubview(playButton)
+ playButton = PlayButton(frame: .zero)
+ recommendedButton = RecommendButton(frame: .zero)
+ moreButton = MoreButton(frame: .zero)
- recommendedButton = UIButton(frame: CGRect.zero)
- recommendedButton.setImage(#imageLiteral(resourceName: "bookmarks_recommend_small"), for: .normal)
- recommendedButton.setImage(#imageLiteral(resourceName: "bookmarks_recommend_small_selected"), for: .selected)
- recommendedButton.contentHorizontalAlignment = .left
- recommendedButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: buttonTitlePadding, bottom: 0, right: 0)
- recommendedButton.setTitleColor(.podcastGrayDark, for: .normal)
- recommendedButton.setTitleColor(.cancelButtonRed, for: .selected)
- recommendedButton.setTitle("0", for: .normal)
- recommendedButton.titleLabel?.font = .systemFont(ofSize: 12)
+ playButton.addTarget(self, action: #selector(didPressPlayButton), for: .touchUpInside)
recommendedButton.addTarget(self, action: #selector(didPressRecommendedButton), for: .touchUpInside)
- addSubview(recommendedButton)
-
- moreButton = UIButton(frame: CGRect.zero)
- moreButton.setImage(#imageLiteral(resourceName: "more_icon"), for: .normal)
moreButton.addTarget(self, action: #selector(didPressMoreButton), for: .touchUpInside)
+
+ addSubview(playButton)
+ addSubview(recommendedButton)
addSubview(moreButton)
}

diff:
@@ -75,29 +75,26 @@ class CardTableViewCell: UITableViewCell {
var bottomViewHeight: CGFloat = CardTableViewCell.bottomViewHeight
var mainViewHeight: CGFloat = 195
- let buttonTitlePadding: CGFloat = 7
-
///
/// Mark: Variables
///
var episodeNameLabel: UILabel!
var dateTimeLabel: UILabel!
var descriptionLabel: UILabel!
- var recommendedButton: UIButton!
- var bookmarkButton: UIButton!
- var moreInfoButton: UIButton!
+ var recommendedButton: RecommendButton!
+ var bookmarkButton: BookmarkButton!
var seperator: UIView!
var podcastImageView: UIImageView!
var lineSeperator: UIView!
var topLineSeperator: UIView!
- var moreButton: UIButton!
- var playButton: UIButton!
+ var moreButton: MoreButton!
+ var playButton: PlayButton!
var contextLabel: UILabel!
var contextImages: [UIImageView] = []
var contextView: UIView! //view for upper context bar of feed cell
var mainView: UIView! //main view
var bottomView: UIView! //bottom bar view with buttons
- var feedControlButton: UIButton!
+ var feedControlButton: FeedControlButton!
var tagButtonsView: TagButtonsView!
var cardID: String?
@@ -177,45 +174,22 @@ class CardTableViewCell: UITableViewCell {
podcastImageView = UIImageView(frame: CGRect.zero)
mainView.addSubview(podcastImageView)
- bookmarkButton = UIButton(frame: CGRect.zero)
- bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_feed_icon_unselected"), for: .normal)
- bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_feed_icon_selected"), for: .selected)
- bookmarkButton.addTarget(self, action: #selector(didPressBookmarkButton), for: .touchUpInside)
- bottomView.addSubview(bookmarkButton)
+ bookmarkButton = BookmarkButton(frame: .zero)
+ recommendedButton = RecommendButton(frame: .zero)
+ moreButton = MoreButton(frame: .zero)
+ playButton = PlayButton(frame: .zero)
+ feedControlButton = FeedControlButton(frame: .zero)
- recommendedButton = UIButton(frame: CGRect.zero)
- recommendedButton.setImage(#imageLiteral(resourceName: "heart_icon"), for: .normal)
- recommendedButton.setImage(#imageLiteral(resourceName: "heart_icon_selected"), for: .selected)
- recommendedButton.contentHorizontalAlignment = .left
- recommendedButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: buttonTitlePadding, bottom: 0, right: 0)
- recommendedButton.setTitleColor(.podcastGrayDark, for: .normal)
- recommendedButton.setTitleColor(.cancelButtonRed, for: .selected)
- recommendedButton.setTitle("0", for: .normal)
- recommendedButton.titleLabel?.font = .systemFont(ofSize: 12)
+ bookmarkButton.addTarget(self, action: #selector(didPressBookmarkButton), for: .touchUpInside)
recommendedButton.addTarget(self, action: #selector(didPressRecommendedButton), for: .touchUpInside)
- bottomView.addSubview(recommendedButton)
-
- moreButton = UIButton(frame: CGRect.zero)
- moreButton.setImage(#imageLiteral(resourceName: "more_icon"), for: .normal)
moreButton.addTarget(self, action: #selector(didPressMoreButton), for: .touchUpInside)
- bottomView.addSubview(moreButton)
-
- playButton = UIButton(type: .custom)
- playButton.setImage(#imageLiteral(resourceName: "play_feed_icon"), for: .normal)
- playButton.setTitle("Play", for: .normal)
- playButton.setImage(#imageLiteral(resourceName: "play_feed_icon_selected"), for: .disabled)
- playButton.setTitle("Playing", for: .disabled)
- playButton.adjustsImageWhenDisabled = false
playButton.addTarget(self, action: #selector(didPressPlayButton), for: .touchUpInside)
- playButton.setTitleColor(.podcastTealBackground, for: .normal)
- playButton.contentHorizontalAlignment = .left
- playButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: buttonTitlePadding, bottom: 0, right: 0)
- playButton.titleLabel?.font = .systemFont(ofSize: 12)
- bottomView.addSubview(playButton)
-
- feedControlButton = UIButton(frame: CGRect.zero)
- feedControlButton.setImage(#imageLiteral(resourceName: "feed_control_icon"), for: .normal)
feedControlButton.addTarget(self, action: #selector(didPressFeedControlButton), for: .touchUpInside)
+
+ bottomView.addSubview(bookmarkButton)
+ bottomView.addSubview(recommendedButton)
+ bottomView.addSubview(moreButton)
+ bottomView.addSubview(playButton)
contextView.addSubview(feedControlButton)
}

diff:
@@ -39,8 +39,8 @@ class DiscoverTableViewCell: UITableViewCell {
var seriesNameLabel: UILabel!
var episodeDateLabel: UILabel!
var episodeDescriptionLabel: UILabel!
- var likeButton: UIButton!
- var moreButton: UIButton!
+ var likeButton: RecommendButton!
+ var moreButton: MoreButton!
var clickToPlayImageButton: UIButton!
var seperator: UIView!
var isExpanded: Bool!
@@ -113,12 +113,12 @@ class DiscoverTableViewCell: UITableViewCell {
episodeDescriptionLabel.font = .systemFont(ofSize: 11.0)
contentView.addSubview(episodeDescriptionLabel)
- likeButton = UIButton(frame: CGRect.zero)
+ likeButton = RecommendButton(frame: .zero)
likeButton.addTarget(self, action: #selector(likeButtonPress), for: .touchUpInside)
likeButton.setImage(UIImage(named: "heart_icon"), for: UIControlState())
contentView.addSubview(likeButton)
- moreButton = UIButton(frame: CGRect.zero)
+ moreButton = MoreButton(frame: .zero)
moreButton.addTarget(self, action: #selector(moreButtonPress), for: .touchUpInside)
moreButton.setImage(UIImage(named: "more_icon"), for: UIControlState())
contentView.addSubview(moreButton)

diff:
@@ -23,7 +23,7 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
var dateLabel: UILabel!
var descriptionLabel: UILabel!
var playButton: PlayButton!
- var recommendedButton: UIButton!
+ var recommendButton: UIButton!
var moreButton: UIButton!
var bookmarkButton: UIButton!
var bottomLineSeparator: UIView!
@@ -54,8 +54,8 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
let moreButtonRightX: CGFloat = 33
let bookmarkButtonWidth: CGFloat = 18
let bookmarkButtonRightX: CGFloat = 74
- let recommendedButtonWidth: CGFloat = 18
- let recommendedButtonRightX: CGFloat = 110
+ let recommendButtonWidth: CGFloat = 18
+ let recommendButtonRightX: CGFloat = 110
var buttonPadding: CGFloat = 10
let bottomViewInnerPadding: CGFloat = 18
@@ -123,11 +123,11 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
bookmarkButton.addTarget(self, action: #selector(bookmarkButtonTapped), for: .touchUpInside)
addSubview(bookmarkButton)
- recommendedButton = UIButton(frame: .zero)
- recommendedButton.setImage(#imageLiteral(resourceName: "heart_icon"), for: .normal)
- recommendedButton.setImage(#imageLiteral(resourceName: "heart_icon_selected"), for: .selected)
- recommendedButton.addTarget(self, action: #selector(recommendedButtonTapped), for: .touchUpInside)
- addSubview(recommendedButton)
+ recommendButton = RecommendButton(frame: .zero)
+ recommendButton.setImage(#imageLiteral(resourceName: "heart_icon"), for: .normal)
+ recommendButton.setImage(#imageLiteral(resourceName: "heart_icon_selected"), for: .selected)
+ recommendButton.addTarget(self, action: #selector(recommendButtonTapped), for: .touchUpInside)
+ addSubview(recommendButton)
}
required init?(coder aDecoder: NSCoder) {
@@ -152,7 +152,7 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
bookmarkButton.frame = CGRect(x: frame.width - bookmarkButtonRightX, y: playButton.frame.origin.y, width: bookmarkButtonWidth, height: bottomSectionHeight)
- recommendedButton.frame = CGRect(x: frame.width - recommendedButtonRightX, y: playButton.frame.origin.y, width: recommendedButtonWidth, height: bottomSectionHeight)
+ recommendButton.frame = CGRect(x: frame.width - recommendButtonRightX, y: playButton.frame.origin.y, width: recommendButtonWidth, height: bottomSectionHeight)
frame.size.height = playButton.frame.maxY
}
@@ -192,9 +192,9 @@ class EpisodeDetailHeaderViewCell: UITableViewCell {
moreButton.isSelected = !moreButton.isSelected
}
- func recommendedButtonTapped() {
+ func recommendButtonTapped() {
delegate?.episodeDetailHeaderDidPressRecommendButton(cell: self)
- recommendedButton.isSelected = !recommendedButton.isSelected
+ recommendButton.isSelected = !recommendButton.isSelected
}
}

diff:
@@ -74,23 +74,20 @@ class EpisodeTableViewCell: UITableViewCell {
var bottomViewHeight: CGFloat = EpisodeTableViewCell.bottomViewHeight
var mainViewHeight: CGFloat = 195
- let buttonTitlePadding: CGFloat = 7
-
///
/// Mark: Variables
///
var episodeNameLabel: UILabel!
var dateTimeLabel: UILabel!
var descriptionLabel: UILabel!
- var recommendedButton: UIButton!
- var bookmarkButton: UIButton!
- var moreInfoButton: UIButton!
+ var recommendedButton: RecommendButton!
+ var bookmarkButton: BookmarkButton!
var seperator: UIView!
var podcastImage: UIImageView!
var lineSeperator: UIView!
var bottomLineSeperator: UIView!
- var moreButton: UIButton!
- var playButton: UIButton!
+ var moreButton: MoreButton!
+ var playButton: PlayButton!
var mainView: UIView! //main view
var bottomView: UIView! //bottom bar view with buttons
var tagButtonsView: TagButtonsView!
@@ -160,41 +157,24 @@ class EpisodeTableViewCell: UITableViewCell {
podcastImage = UIImageView(frame: CGRect.zero)
mainView.addSubview(podcastImage)
- bookmarkButton = UIButton(frame: CGRect.zero)
- bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_feed_icon_unselected"), for: .normal)
- bookmarkButton.setImage(#imageLiteral(resourceName: "bookmark_feed_icon_selected"), for: .selected)
- bookmarkButton.addTarget(self, action: #selector(didPressBookmarkButton), for: .touchUpInside)
- bottomView.addSubview(bookmarkButton)
+ // buttons
+ bookmarkButton = BookmarkButton(frame: .zero)
+ recommendedButton = RecommendButton(frame: .zero)
+ moreButton = MoreButton(frame: .zero)
+ playButton = PlayButton(frame: .zero)
- recommendedButton = UIButton(frame: CGRect.zero)
- recommendedButton.setImage(#imageLiteral(resourceName: "heart_icon"), for: .normal)
- recommendedButton.setImage(#imageLiteral(resourceName: "heart_icon_selected"), for: .selected)
- recommendedButton.contentHorizontalAlignment = .left
- recommendedButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: buttonTitlePadding, bottom: 0, right: 0)
- recommendedButton.setTitleColor(.podcastGrayDark, for: .normal)
- recommendedButton.setTitleColor(.cancelButtonRed, for: .selected)
- recommendedButton.setTitle("0", for: .normal)
- recommendedButton.titleLabel?.font = .systemFont(ofSize: 12)
+ bookmarkButton.addTarget(self, action: #selector(didPressBookmarkButton), for: .touchUpInside)
recommendedButton.addTarget(self, action: #selector(didPressRecommendedButton), for: .touchUpInside)
- bottomView.addSubview(recommendedButton)
-
- moreButton = UIButton(frame: CGRect.zero)
- moreButton.setImage(#imageLiteral(resourceName: "more_icon"), for: .normal)
moreButton.addTarget(self, action: #selector(didPressMoreButton), for: .touchUpInside)
- bottomView.addSubview(moreButton)
-
- playButton = UIButton(type: .custom)
- playButton.setImage(#imageLiteral(resourceName: "play_feed_icon"), for: .normal)
- playButton.setTitle("Play", for: .normal)
- playButton.setImage(#imageLiteral(resourceName: "play_feed_icon_selected"), for: .disabled)
- playButton.setTitle("Playing", for: .disabled)
- playButton.adjustsImageWhenDisabled = false
playButton.addTarget(self, action: #selector(didPressPlayButton), for: .touchUpInside)
- playButton.setTitleColor(.podcastTealBackground, for: .normal)
- playButton.contentHorizontalAlignment = .left
- playButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: buttonTitlePadding, bottom: 0, right: 0)
- playButton.titleLabel?.font = .systemFont(ofSize: 12)
+
+ bottomView.addSubview(bookmarkButton)
+ bottomView.addSubview(recommendedButton)
+ bottomView.addSubview(moreButton)
bottomView.addSubview(playButton)
+
+// playButton.setTitle("Play", for: .normal)
+
}
@@ -236,9 +216,7 @@ class EpisodeTableViewCell: UITableViewCell {
}
func setupWithEpisode(episode: Episode) {
-
episodeNameLabel.text = episode.title
-
tagButtonsView.setupTagButtons(tags: episode.tags)
for tagButton in tagButtonsView.tagButtons {
@@ -256,7 +234,6 @@ class EpisodeTableViewCell: UITableViewCell {
podcastImage.image = #imageLiteral(resourceName: "filler_image")
}
-
bookmarkButton.isSelected = episode.isBookmarked
recommendedButton.isSelected = episode.isRecommended
}

diff:
@@ -29,7 +29,7 @@ class ListeningHistoryTableViewCell: UITableViewCell {
var episodeImageView: UIImageView!
var titleLabel: UILabel!
var detailLabel: UILabel!
- var moreButton: UIButton!
+ var moreButton: MoreButton!
var seperator: UIView!
weak var delegate: ListeningHistoryTableViewCellDelegate?
@@ -49,9 +49,8 @@ class ListeningHistoryTableViewCell: UITableViewCell {
detailLabel.textColor = .podcastGrayDark
contentView.addSubview(detailLabel)
- moreButton = UIButton(frame: CGRect.zero)
+ moreButton = MoreButton(frame: CGRect.zero)
moreButton.addTarget(self, action: #selector(didPressMoreButton), for: .touchUpInside)
- moreButton.setImage(#imageLiteral(resourceName: "more_icon"), for: .normal)
contentView.addSubview(moreButton)
seperator = UIView(frame: CGRect.zero)

diff:
import UIKit
class PlayButton: UIButton {
+ let buttonTitlePadding: CGFloat = 7
override init(frame: CGRect) {
super.init(frame: frame)
@@ -20,6 +21,7 @@ class PlayButton: UIButton {
contentHorizontalAlignment = .left
setTitleColor(.podcastBlack, for: .normal)
setTitleColor(.podcastGreenBlue, for: .selected)
+ titleEdgeInsets = UIEdgeInsets(top: 0, left: buttonTitlePadding, bottom: 0, right: 0)
}
required init?(coder aDecoder: NSCoder) {

diff:
@@ -28,7 +28,7 @@ class SearchEpisodeTableViewCell: UITableViewCell {
var episodeImageView: UIImageView!
var titleLabel: UILabel!
var detailLabel: UILabel!
- var playButton: UIButton!
+ var playButton: PlayButton!
var playButtonActivated = false
var index: Int!

original_message: refactor to use custom buttons


message: separate widths for selected and unselected play button

diff:
@@ -20,10 +20,12 @@ class SearchEpisodeTableViewCell: UITableViewCell {
let imageViewHeight: CGFloat = 48
let imageViewLabelPadding: CGFloat = 12
let titleLabelHeight: CGFloat = 36
+ let titleLabelPlayButtonSpacing: CGFloat = 8
let playButtonPaddingX: CGFloat = 18
let playButtonPaddingY: CGFloat = 30
let playButtonHeight: CGFloat = 30
- let playButtonWidth: CGFloat = 90
+ let playButtonWidthForSelected: CGFloat = 65
+ let playButtonWidthForUnselected: CGFloat = 45
var episodeImageView: UIImageView!
var titleLabel: UILabel!
@@ -63,10 +65,10 @@ class SearchEpisodeTableViewCell: UITableViewCell {
super.layoutSubviews()
episodeImageView.frame = CGRect(x: imageViewPaddingX, y: imageViewPaddingY, width: imageViewWidth, height: imageViewHeight)
let titleLabelX: CGFloat = episodeImageView.frame.maxX + imageViewLabelPadding
- let playButtonX: CGFloat = frame.width - playButtonPaddingX - playButtonWidth
- titleLabel.frame = CGRect(x: titleLabelX, y: imageViewPaddingY-2, width: playButtonX - titleLabelX, height: titleLabelHeight)
+ let playButtonX: CGFloat = frame.width - playButtonPaddingX - (playButton.isSelected ? playButtonWidthForSelected : playButtonWidthForUnselected)
+ titleLabel.frame = CGRect(x: titleLabelX, y: imageViewPaddingY-2, width: playButtonX - titleLabelX - titleLabelPlayButtonSpacing, height: titleLabelHeight)
detailLabel.frame = CGRect(x: titleLabelX, y: titleLabel.frame.maxY, width: titleLabel.frame.width, height:episodeImageView.frame.height - titleLabel.frame.height + 2)
- playButton.frame = CGRect(x: playButtonX, y: playButtonPaddingY, width: playButtonWidth, height: playButtonHeight)
+ playButton.frame = CGRect(x: playButtonX, y: playButtonPaddingY, width: (playButton.isSelected ? playButtonWidthForSelected : playButtonWidthForUnselected), height: playButtonHeight)
separatorInset = UIEdgeInsets(top: 0, left: titleLabelX, bottom: 0, right: 0)
}
@@ -95,10 +97,7 @@ class SearchEpisodeTableViewCell: UITableViewCell {
}
func setPlayButtonToState(isPlaying: Bool) {
- if isPlaying {
- playButton.isSelected = true
- } else {
- playButton.isSelected = false
- }
+ playButton.isSelected = isPlaying
+ setNeedsLayout()
}
}


message: quick fix for bookmark cell

diff:
@@ -171,7 +171,7 @@ class BookmarkTableViewCell: UITableViewCell {
}
func setPlayButtonToState(isPlaying: Bool) {
- playButton.isEnabled = !isPlaying
+ playButton.isSelected = isPlaying
if isPlaying {
recommendedButton.frame = CGRect(x: recommendedButtonXPlaying, y: frame.height - recommendedButtonBottomY - recommendedButtonHeight, width: recommendedButtonWidth, height: recommendedButtonHeight)
} else {

original_message: quick fix for bookmark cell


message: fix isEnabled bug

diff:
@@ -114,7 +114,7 @@ class BookmarkTableViewCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
- playButton.isEnabled = true
+ playButton.isSelected = false
recommendedButton.isSelected = false
}

diff:
@@ -208,7 +208,7 @@ class CardTableViewCell: UITableViewCell {
contextImages = []
tagButtonsView.prepareForReuse()
- playButton.isEnabled = true
+ playButton.isSelected = false
recommendedButton.isSelected = false
bookmarkButton.isSelected = false
}
@@ -288,7 +288,7 @@ class CardTableViewCell: UITableViewCell {
}
func setPlayButtonToState(isPlaying: Bool) {
- playButton.isEnabled = !isPlaying
+ playButton.isSelected = isPlaying
}
func didPressMoreButton() {

diff:
@@ -186,7 +186,7 @@ class EpisodeTableViewCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
tagButtonsView.prepareForReuse()
- playButton.isEnabled = true
+ playButton.isSelected = false
recommendedButton.isSelected = false
bookmarkButton.isSelected = false
}
@@ -262,7 +262,7 @@ class EpisodeTableViewCell: UITableViewCell {
}
func setPlayButtonToState(isPlaying: Bool) {
- playButton.isEnabled = !isPlaying
+ playButton.isSelected = isPlaying
}
func didPressMoreButton() {

original_message: fix isEnabled bug


message: make html links clickable

diff:
@@ -112,7 +112,7 @@ class Episode: NSObject {
let modifiedFont = NSString(format:"%@" as NSString, descriptionText) as String
let attrStr = try! NSAttributedString(
- data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
+ data: modifiedFont.data(using: .utf8, allowLossyConversion: true)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue],
documentAttributes: nil)

diff:
@@ -50,7 +50,7 @@ class PlayerControlsView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.frame.size.height = playerControlsViewHeight
- backgroundColor = UIColor.colorFromCode(0xf4f4f7)
+ backgroundColor = .podcastPlayerGray
slider = UISlider(frame: .zero)
slider.frame.origin = CGPoint(x: marginSpacing, y: self.frame.maxY - sliderYInset)

diff:
@@ -56,7 +56,6 @@ class PlayerEpisodeDetailView: UIView {
descriptionTextView = UITextView(frame: .zero)
descriptionTextView.isEditable = false
- descriptionTextView.isSelectable = false
descriptionTextView.backgroundColor = .clear
addSubview(descriptionTextView)
@@ -68,7 +67,6 @@ class PlayerEpisodeDetailView: UIView {
}
func updateUIForEpisode(episode: Episode) {
- episodeArtworkImageView.image = #imageLiteral(resourceName: "filler_image")
if let url = episode.largeArtworkImageURL {
episodeArtworkImageView.setImageAsynchronously(url: url, completion: nil)
}

original_message: make html links clickable


message:
add error extensions support and export exceptions file
host applications can't effectively catch errors if they can't see the
types.

diff:
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
+ <excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
+ <excludeFolder url="file://$MODULE_DIR$/example/.pub" />
+ <excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
- <orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
+ <orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
\ No newline at end of file

diff:
@@ -14,3 +14,5 @@ export 'package:graphql_flutter/src/widgets/graphql_consumer.dart';
export 'package:graphql_flutter/src/widgets/query.dart';
export 'package:graphql_flutter/src/widgets/mutation.dart';
export 'package:graphql_flutter/src/widgets/subscription.dart';
+
+export 'package:graphql_flutter/src/exceptions.dart';

diff:
@@ -26,12 +26,15 @@ class GQLError {
/// The path of the field in error.
final List<dynamic> path;
+ final Map<String, dynamic> extensions;
+
/// Constructs a [GQLError] from a JSON map.
GQLError.fromJSON(Map data)
: message = data['message'],
locations = new List.from(
(data['locations']).map((d) => new Location.fromJSON(d))),
- path = data['path'];
+ path = data['path'],
+ extensions = data['extensions'];
@override
String toString() =>

repo: zino-hofmann/graphql-flutter


message: fix cache bugs

diff:
@@ -23,8 +23,12 @@ class InMemoryCache implements Cache {
/// Writes an entity to the internal HashMap.
@override
void write(String key, dynamic value) {
+ if (_inMemoryCache.containsKey(key) && _inMemoryCache[key] is Map) {
+ _inMemoryCache[key].addAll(value);
+ } else {
_inMemoryCache[key] = value;
}
+ }
/// Saves the internal HashMap to a file.
@override
@@ -56,8 +60,15 @@ class InMemoryCache implements Cache {
return File('$path/cache.txt');
}
+ bool writing = false;
Future<dynamic> _writeToStorage() async {
+ if (!writing) {
+ writing = true;
+ } else {
+ return;
+ }
final File file = await _localStorageFile;
+
final IOSink sink = file.openWrite();
_inMemoryCache.forEach((String key, dynamic value) {
@@ -65,7 +76,7 @@ class InMemoryCache implements Cache {
});
await sink.close();
-
+ writing = false;
return;
}
@@ -76,7 +87,6 @@ class InMemoryCache implements Cache {
if (file.existsSync()) {
final Stream<List<int>> inputStream = file.openRead();
-
inputStream
.transform(utf8.decoder) // Decode bytes to UTF8.
.transform(

diff:
@@ -66,6 +66,47 @@ final Map<String, Object> updatedCOperationData = <String, Object>{
'aField': <String, Object>{'field': false}
};
+final Map<String, Object> smallerAValue = <String, Object>{
+ 'a': <String, Object>{
+ '__typename': 'A',
+ 'id': 1,
+ 'list': <Object>[
+ 5,
+ 6,
+ 7,
+ <String, Object>{
+ '__typename': 'Item',
+ 'id': 8,
+ 'value': 8,
+ }
+ ],
+ },
+};
+
+final Map<String, Object> updatedAOperationData = <String, Object>{
+ 'a': <String, Object>{
+ '__typename': 'A',
+ 'id': 1,
+ 'list': <Object>[
+ 5,
+ 6,
+ 7,
+ <String, Object>{
+ '__typename': 'Item',
+ 'id': 8,
+ 'value': 8,
+ }
+ ],
+ 'b': <String, Object>{
+ '__typename': 'B',
+ 'id': 5,
+ 'c': updatedCValue,
+ 'bField': <String, Object>{'field': true}
+ },
+ },
+ 'aField': <String, Object>{'field': false}
+};
+
void main() {
group('Normalizes writes', () {
final NormalizedInMemoryCache cache = NormalizedInMemoryCache(
@@ -79,5 +120,10 @@ void main() {
cache.write('C/6', updatedCValue);
expect(cache.read(rawOperationKey), equals(updatedCOperationData));
});
+
+ test('updating smaller query update parent', () {
+ cache.write('anotherKey', smallerAValue);
+ expect(cache.read(rawOperationKey), equals(updatedAOperationData));
+ });
});
}

original_message: fix cache bugs


message: add better name to test

diff:
@@ -121,7 +121,7 @@ void main() {
expect(cache.read(rawOperationKey), equals(updatedCOperationData));
});
- test('updating smaller query update parent', () {
+ test('updating smaller query does not override parent', () {
cache.write('anotherKey', smallerAValue);
expect(cache.read(rawOperationKey), equals(updatedAOperationData));
});

original_message: add better name to test


message: change test name, naming stuff is hard

diff:
@@ -121,7 +121,7 @@ void main() {
expect(cache.read(rawOperationKey), equals(updatedCOperationData));
});
- test('updating smaller query does not override parent', () {
+ test('updating smaller query does not override bigger query', () {
cache.write('anotherKey', smallerAValue);
expect(cache.read(rawOperationKey), equals(updatedAOperationData));
});

original_message: change test name, naming stuff is hard


message: make writing lock private

diff:
@@ -9,6 +9,7 @@ import 'package:graphql_flutter/src/cache/cache.dart';
class InMemoryCache implements Cache {
HashMap<String, dynamic> _inMemoryCache = HashMap<String, dynamic>();
+ bool _writing = false;
/// Reads an entity from the internal HashMap.
@override
@@ -60,10 +61,9 @@ class InMemoryCache implements Cache {
return File('$path/cache.txt');
}
- bool writing = false;
Future<dynamic> _writeToStorage() async {
- if (!writing) {
- writing = true;
+ if (!_writing) {
+ _writing = true;
} else {
return;
}
@@ -76,7 +76,7 @@ class InMemoryCache implements Cache {
});
await sink.close();
- writing = false;
+ _writing = false;
return;
}

original_message: make writing lock private


message: change variable naming and prevent type error

diff:
@@ -9,7 +9,7 @@ import 'package:graphql_flutter/src/cache/cache.dart';
class InMemoryCache implements Cache {
HashMap<String, dynamic> _inMemoryCache = HashMap<String, dynamic>();
- bool _writing = false;
+ bool _writingToStorage = false;
/// Reads an entity from the internal HashMap.
@override
@@ -24,7 +24,9 @@ class InMemoryCache implements Cache {
/// Writes an entity to the internal HashMap.
@override
void write(String key, dynamic value) {
- if (_inMemoryCache.containsKey(key) && _inMemoryCache[key] is Map) {
+ if (_inMemoryCache.containsKey(key) &&
+ _inMemoryCache[key] is Map &&
+ value is Map) {
_inMemoryCache[key].addAll(value);
} else {
_inMemoryCache[key] = value;
@@ -62,8 +64,8 @@ class InMemoryCache implements Cache {
}
Future<dynamic> _writeToStorage() async {
- if (!_writing) {
- _writing = true;
+ if (!_writingToStorage) {
+ _writingToStorage = true;
} else {
return;
}
@@ -76,7 +78,7 @@ class InMemoryCache implements Cache {
});
await sink.close();
- _writing = false;
+ _writingToStorage = false;
return;
}


message: dont lock forever if has error

diff:
@@ -64,13 +64,12 @@ class InMemoryCache implements Cache {
}
Future<dynamic> _writeToStorage() async {
- if (!_writingToStorage) {
- _writingToStorage = true;
- } else {
+ if (_writingToStorage) {
return;
}
+ _writingToStorage = true;
+ try {
final File file = await _localStorageFile;
-
final IOSink sink = file.openWrite();
_inMemoryCache.forEach((String key, dynamic value) {
@@ -78,6 +77,9 @@ class InMemoryCache implements Cache {
});
await sink.close();
+ } catch (err) {
+ // TODO: handle error
+ }
_writingToStorage = false;
return;
}

original_message: dont lock forever if has error


message: better naming on test and avoid type error

diff:
@@ -26,6 +26,7 @@ class InMemoryCache implements Cache {
void write(String key, dynamic value) {
if (_inMemoryCache.containsKey(key) &&
_inMemoryCache[key] is Map &&
+ value != null &&
value is Map) {
// Avoid overriding a superset with a subset of a field (#155)
_inMemoryCache[key].addAll(value);
@@ -72,7 +73,7 @@ class InMemoryCache implements Cache {
// Catching errors to avoid locking forever.
// Maybe the device couldn't write in the past
- // but in the future it may.
+ // but it may in the future.
try {
final File file = await _localStorageFile;
final IOSink sink = file.openWrite();

diff:
@@ -66,7 +66,7 @@ final Map<String, Object> updatedCOperationData = <String, Object>{
'aField': <String, Object>{'field': false}
};
-final Map<String, Object> smallerAValue = <String, Object>{
+final Map<String, Object> subsetAValue = <String, Object>{
'a': <String, Object>{
'__typename': 'A',
'id': 1,
@@ -83,7 +83,7 @@ final Map<String, Object> smallerAValue = <String, Object>{
},
};
-final Map<String, Object> updatedAOperationData = <String, Object>{
+final Map<String, Object> updatedSubsetOperationData = <String, Object>{
'a': <String, Object>{
'__typename': 'A',
'id': 1,
@@ -121,9 +121,9 @@ void main() {
expect(cache.read(rawOperationKey), equals(updatedCOperationData));
});
- test('updating smaller query does not override bigger query', () {
- cache.write('anotherKey', smallerAValue);
- expect(cache.read(rawOperationKey), equals(updatedAOperationData));
+ test('updating subset query does not override superset query', () {
+ cache.write('anotherUnrelatedKey', subsetAValue);
+ expect(cache.read(rawOperationKey), equals(updatedSubsetOperationData));
});
});
}


message: dont swallow error

diff:
@@ -83,10 +83,11 @@ class InMemoryCache implements Cache {
});
await sink.close();
+ _writingToStorage = false;
} catch (err) {
- // XXX Is there anything to be done?
- }
_writingToStorage = false;
+ rethrow;
+ }
return;
}

original_message: dont swallow error


message: add tests for concurrent saves

diff:
@@ -8,6 +8,15 @@ import 'package:path_provider/path_provider.dart';
import 'package:graphql_flutter/src/cache/cache.dart';
class InMemoryCache implements Cache {
+ InMemoryCache({
+ this.customStorageDirectory,
+ });
+
+ /// A directory to be used for storage.
+ /// This is used for testing, on regular usage
+ /// 'path_provider' will return the storage directory.
+ final Directory customStorageDirectory;
+
HashMap<String, dynamic> _inMemoryCache = HashMap<String, dynamic>();
bool _writingToStorage = false;
@@ -37,13 +46,13 @@ class InMemoryCache implements Cache {
/// Saves the internal HashMap to a file.
@override
- void save() async {
+ Future<void> save() async {
await _writeToStorage();
}
/// Restores the internal HashMap to a file.
@override
- void restore() async {
+ Future<void> restore() async {
_inMemoryCache = await _readFromStorage();
}
@@ -54,8 +63,12 @@ class InMemoryCache implements Cache {
}
Future<String> get _localStoragePath async {
- final Directory directory = await getApplicationDocumentsDirectory();
+ if (customStorageDirectory != null) {
+ // Used for testing
+ return customStorageDirectory.path;
+ }
+ final Directory directory = await getApplicationDocumentsDirectory();
return directory.path;
}
@@ -77,7 +90,6 @@ class InMemoryCache implements Cache {
try {
final File file = await _localStorageFile;
final IOSink sink = file.openWrite();
-
_inMemoryCache.forEach((String key, dynamic value) {
sink.writeln(json.encode(<dynamic>[key, value]));
});
@@ -98,16 +110,15 @@ class InMemoryCache implements Cache {
if (file.existsSync()) {
final Stream<List<int>> inputStream = file.openRead();
- inputStream
+
+ await for (String line in inputStream
.transform(utf8.decoder) // Decode bytes to UTF8.
.transform(
const LineSplitter(),
- ) // Convert stream to individual lines.
- .listen((String line) {
+ )) {
final List<dynamic> keyAndValue = json.decode(line);
-
storedHashMap[keyAndValue[0]] = keyAndValue[1];
- });
+ }
}
return storedHashMap;

change_type: ADD

diff:
+import 'dart:io' show Directory, File, Platform;
+import 'package:test/test.dart';
+import 'package:graphql_flutter/src/cache/in_memory.dart';
+
+const String rawOperationKey = 'rawOperationKey';
+
+final Map<String, Object> rawOperationData = <String, Object>{
+ 'a': <String, Object>{
+ '__typename': 'A',
+ 'id': 1,
+ 'list': <Object>[
+ 1,
+ 2,
+ 3,
+ <String, Object>{
+ '__typename': 'Item',
+ 'id': 4,
+ 'value': 4,
+ }
+ ],
+ 'b': <String, Object>{
+ '__typename': 'B',
+ 'id': 5,
+ 'c': <String, Object>{
+ '__typename': 'C',
+ 'id': 6,
+ 'cField': 'value',
+ },
+ 'bField': <String, Object>{'field': true}
+ },
+ },
+ 'aField': <String, Object>{'field': false}
+};
+
+void main() {
+ group('Normalizes writes', () {
+ final Directory customStorageDirectory =
+ Directory.systemTemp.createTempSync('file_test_');
+
+ final InMemoryCache cache = InMemoryCache(
+ customStorageDirectory: customStorageDirectory,
+ );
+
+ test('.write .read round trip', () async {
+ cache.write(rawOperationKey, rawOperationData);
+ await cache.save();
+ cache.reset();
+ await cache.restore();
+ expect(cache.read(rawOperationKey), equals(rawOperationData));
+ });
+
+ test('saving concurrently wont error', () async {
+ cache.write(rawOperationKey, rawOperationData);
+
+ await Future.wait(<Future<void>>[
+ cache.save(),
+ cache.save(),
+ cache.save(),
+ cache.save(),
+ cache.save(),
+ ]);
+
+ cache.reset();
+ await cache.restore();
+ expect(cache.read(rawOperationKey), equals(rawOperationData));
+ });
+ });
+}

original_message: add tests for concurrent saves