Skip to content

Commit

Permalink
Update design of video queue (#282)
Browse files Browse the repository at this point in the history
* Change “UP NEXT” to “QUEUE”

* Update VideoQueueTableViewCell

* Add play icon to assets

* Update cellFor(queueTableView:at:)

* Set background color of content view

* Change separator color for queue table view

* Improve comments

* Hide separator for current video

* Change number property from Int? to String?

* Make current video editable

* Set selection style to none

* Update highlighted color

* Highlight background of current video when editing

* Start counting videos from 1 instead of 0

* Swap highlighting when a new video is selected

* Swap highlight when video ends

* Update comments and formatting

* Reset background color and separator when no longer the current video

* Hide separator of previous video

* Code cleanup

* Update row numbering after moving a video

* Reload rows to update video numbers

* Remove row numbering for now

* Scroll to current video when showing the queue

* Add helper function to highlight/unhighlight

* Set previous video when loading cells

* Add support for deleting the current video

* Code cleanup — use switch instead of if/else

* Remove ability to delete the current video

* Remove video cell numbers and play icon

* Update guard statements
  • Loading branch information
glennrfisher authored and dfirsht committed Feb 8, 2017
1 parent 092035c commit 9294f6f
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "path.pdf"
}
],
"info" : {
"author" : "zeplin",
"version" : "1"
}
}
Binary file not shown.
179 changes: 114 additions & 65 deletions iOS/Stormtrooper/Stormtrooper/Controllers/StreamViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@ class StreamViewController: UIViewController {
self.headerArrowImageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
}, completion: { complete in
self.queueView.isHidden = false
if let currentVideoIndex = self.viewModel.currentVideoIndex {
let currentVideoIndexPath = IndexPath(row: currentVideoIndex, section: 0)
self.queueTableView.scrollToRow(at: currentVideoIndexPath, at: .top, animated: true)
}
})
}
else {
Expand Down Expand Up @@ -545,7 +549,50 @@ class StreamViewController: UIViewController {
}
}
}


fileprivate func setHighlightForVideo(at row: Int, highlighted: Bool) {
// update the previous video (i.e. whether to hide its separator)
let previousIndexPath = IndexPath(row: row-1, section: 0)
let previousVideoCell = queueTableView.cellForRow(at: previousIndexPath) as? VideoQueueTableViewCell
previousVideoCell?.isPreviousVideo = highlighted

// update the current video (i.e. whether to highlight it)
let indexPath = IndexPath(row: row, section: 0)
let videoCell = queueTableView.cellForRow(at: indexPath) as? VideoQueueTableViewCell
videoCell?.isCurrentVideo = highlighted
}

fileprivate func deleteVideo(at indexPath: IndexPath) {
guard let currentVideoIndex = viewModel.currentVideoIndex else { return }
let previousIndexPath = IndexPath(row: indexPath.row - 1, section: 0)
let nextIndexPath = IndexPath(row: indexPath.row + 1, section: 0)

// deleted the previous video
if indexPath.row == currentVideoIndex - 1 {
let previousCell = queueTableView.cellForRow(at: previousIndexPath)
let previousVideoCell = previousCell as? VideoQueueTableViewCell
previousVideoCell?.isPreviousVideo = true
}

// deleted the current video
if indexPath.row == currentVideoIndex {
guard let videoQueue = viewModel.videoQueue else { return }
setHighlightForVideo(at: nextIndexPath.row, highlighted: true)
viewModel.currentVideoIndex = nextIndexPath.row
let nextVideoId = videoQueue[nextIndexPath.row].id
playerView.cueVideo(byId: nextVideoId, startSeconds: 0, suggestedQuality: .default)
playerView.playVideo()
}

// remove deleted video from queue and view model
queueTableView.beginUpdates()
queueTableView.deleteRows(at: [indexPath], with: .automatic)
viewModel.videoQueue?.remove(at: indexPath.row)
if currentVideoIndex > indexPath.row {
viewModel.currentVideoIndex = currentVideoIndex - 1
}
queueTableView.endUpdates()
}
}

extension StreamViewController: StreamViewModelDelegate {
Expand Down Expand Up @@ -643,13 +690,11 @@ extension StreamViewController: StreamViewModelDelegate {

extension StreamViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == chatTableTag {
return cellFor(chatTableView: tableView, at: indexPath)
switch tableView.tag {
case chatTableTag: return cellFor(chatTableView: tableView, at: indexPath)
case queueTableTag: return cellFor(queueTableView: tableView, at: indexPath)
default: return UITableViewCell()
}
else if tableView.tag == queueTableTag {
return cellFor(queueTableView: tableView, at: indexPath)
}
return UITableViewCell()
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
Expand All @@ -664,70 +709,62 @@ extension StreamViewController: UITableViewDelegate, UITableViewDataSource {
if isKeyboardShowing {
visualEffectView.isHidden = false
dismissView.isHidden = false
}
else {
} else {
visualEffectView.isHidden = true
dismissView.isHidden = true
}
}

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if tableView.tag == chatTableTag {
return false
} else if tableView.tag == queueTableTag {
return indexPath.row != viewModel.currentVideoIndex
} else {
return false
switch tableView.tag {
case chatTableTag: return false
case queueTableTag: return indexPath.row != viewModel.currentVideoIndex
default: return false
}
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete, indexPath.row != viewModel.currentVideoIndex {
tableView.beginUpdates()
viewModel.videoQueue?.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
if viewModel.currentVideoIndex ?? 0 > indexPath.row {
viewModel.currentVideoIndex = (viewModel.currentVideoIndex ?? 0) - 1
}
tableView.endUpdates()
switch editingStyle {
case .delete: deleteVideo(at: indexPath)
default: break
}
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == chatTableTag {
return viewModel.messages.count //TODO: limit this to 50 or whatever performance allows
}
else if tableView.tag == queueTableTag {
return viewModel.videoQueue?.count ?? 0
switch tableView.tag {
case chatTableTag: return viewModel.messages.count
case queueTableTag: return viewModel.videoQueue?.count ?? 0
default: return 0
}
return 0
}

func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
if tableView.tag == queueTableTag {
return true
switch tableView.tag {
case chatTableTag: return false
case queueTableTag: return true
default: return false
}
return false
}

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
guard let currentVideoIndex = viewModel.currentVideoIndex, let video = viewModel.videoQueue?.remove(at: sourceIndexPath.row) else {
return
guard let currentVideoIndex = viewModel.currentVideoIndex,
let video = viewModel.videoQueue?.remove(at: sourceIndexPath.row) else {
return
}
viewModel.videoQueue?.insert(video, at: destinationIndexPath.row)

// update the view model's current video index
if sourceIndexPath.row == currentVideoIndex {
// moved the current video
viewModel.currentVideoIndex = destinationIndexPath.row
}
else if destinationIndexPath.row == currentVideoIndex {
} else if destinationIndexPath.row == currentVideoIndex {
// moved a video to the current video's index
viewModel.currentVideoIndex = destinationIndexPath.row + (sourceIndexPath.row > destinationIndexPath.row ? 1 : -1)
}
// left-to-right
else if sourceIndexPath.row < currentVideoIndex, currentVideoIndex < destinationIndexPath.row {
} else if sourceIndexPath.row < currentVideoIndex, currentVideoIndex < destinationIndexPath.row {
// moved a video from the left-side of the current video to the right-side
viewModel.currentVideoIndex = currentVideoIndex - 1
}

// right-to-left
else if sourceIndexPath.row > currentVideoIndex, currentVideoIndex > destinationIndexPath.row {
} else if sourceIndexPath.row > currentVideoIndex, currentVideoIndex > destinationIndexPath.row {
// moved a video from the right-side of the current video to the left-side
viewModel.currentVideoIndex = currentVideoIndex + 1
}
}
Expand All @@ -737,11 +774,15 @@ extension StreamViewController: UITableViewDelegate, UITableViewDataSource {
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if viewModel.currentVideoIndex != indexPath.row {
viewModel.currentVideoIndex = indexPath.row
playerView.cueVideo(byId: viewModel.videoQueue?[indexPath.row].id ?? "", startSeconds: 0, suggestedQuality: .default)
playerView.playVideo()
guard viewModel.currentVideoIndex != indexPath.row,
let currentVideoIndex = viewModel.currentVideoIndex else {
return
}
setHighlightForVideo(at: currentVideoIndex, highlighted: false)
setHighlightForVideo(at: indexPath.row, highlighted: true)
viewModel.currentVideoIndex = indexPath.row
playerView.cueVideo(byId: viewModel.videoQueue?[indexPath.row].id ?? "", startSeconds: 0, suggestedQuality: .default)
playerView.playVideo()
}

private func cellFor(chatTableView tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
Expand Down Expand Up @@ -784,22 +825,22 @@ extension StreamViewController: UITableViewDelegate, UITableViewDataSource {
}

private func cellFor(queueTableView tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
guard
let cell = tableView.dequeueReusableCell(withIdentifier: "queueCell") as? VideoQueueTableViewCell,
let video = viewModel.videoQueue?[indexPath.row] else {
return UITableViewCell()
guard let cell = tableView.dequeueReusableCell(withIdentifier: "queueCell") as? VideoQueueTableViewCell,
let video = viewModel.videoQueue?[indexPath.row],
let currentVideoIndex = viewModel.currentVideoIndex else {
return UITableViewCell()
}
cell.titleLabel.text = video.title
cell.channelTitleLabel.text = video.channelTitle

cell.title = video.title
cell.channel = video.channelTitle
cell.isPreviousVideo = (currentVideoIndex-1 == indexPath.row)
cell.isCurrentVideo = (currentVideoIndex == indexPath.row)

YouTubeDataManager.sharedInstance.getThumbnailForVideo(with: video.mediumThumbnailURL) {error, image in
if let image = image {
cell.thumbnailImageView.image = image
}
}
if indexPath.row == viewModel.currentVideoIndex {
cell.isSelected = true
guard let image = image else { return }
cell.thumbnail = image
}
cell.selectionStyle = .none

return cell
}
}
Expand Down Expand Up @@ -868,11 +909,8 @@ extension StreamViewController: YTPlayerViewDelegate {
}
break
case .ended:
if viewModel.isHost, let queue = viewModel.videoQueue, var queueIndex = viewModel.currentVideoIndex {
queueIndex = queueIndex < queue.count - 1 ? queueIndex + 1 : 0
playerView.cueVideo(byId: queue[queueIndex].id, startSeconds: 0, suggestedQuality: .default)
viewModel.currentVideoIndex = queueIndex
playerView.playVideo()
if viewModel.isHost {
playNextVideo()
}
break
case .queued:
Expand All @@ -884,6 +922,17 @@ extension StreamViewController: YTPlayerViewDelegate {
}
}

private func playNextVideo() {
guard let videoQueue = viewModel.videoQueue else { return }
guard let currentVideoIndex = viewModel.currentVideoIndex else { return }
let nextVideoIndex = currentVideoIndex < videoQueue.count-1 ? currentVideoIndex+1 : 0
setHighlightForVideo(at: currentVideoIndex, highlighted: false)
setHighlightForVideo(at: nextVideoIndex, highlighted: true)
viewModel.currentVideoIndex = nextVideoIndex
let nextVideoId = videoQueue[nextVideoIndex].id
playerView.cueVideo(byId: nextVideoId, startSeconds: 0, suggestedQuality: .default)
playerView.playVideo()
}
}

extension StreamViewController: UITextFieldDelegate {
Expand Down
3 changes: 2 additions & 1 deletion iOS/Stormtrooper/Stormtrooper/Storyboards/Stream.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,13 @@
<tableView clipsSubviews="YES" tag="1" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="89" sectionHeaderHeight="1" sectionFooterHeight="1" translatesAutoresizingMaskIntoConstraints="NO" id="iC4-5G-MpH">
<rect key="frame" x="0.0" y="42" width="375" height="247"/>
<color key="backgroundColor" red="0.15686274510000001" green="0.14117647059999999" blue="0.14117647059999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="0.34901960784313724" green="0.34509803921568627" blue="0.34901960784313724" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="dataSource" destination="BYZ-38-t0r" id="2Ly-0p-jhB"/>
<outlet property="delegate" destination="BYZ-38-t0r" id="kd1-Sm-0Z4"/>
</connections>
</tableView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="UP NEXT" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zJg-2p-vtP">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="QUEUE" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zJg-2p-vtP">
<rect key="frame" x="16" y="8" width="160" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="euG-Od-hWs"/>
Expand Down
69 changes: 57 additions & 12 deletions iOS/Stormtrooper/Stormtrooper/Views/VideoQueueTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,64 @@
import UIKit

class VideoQueueTableViewCell: UITableViewCell {
@IBOutlet weak var thumbnailImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var channelTitleLabel: UILabel!

override func awakeFromNib() {
super.awakeFromNib()
// Initialization code

@IBOutlet private weak var thumbnailImageView: UIImageView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var channelLabel: UILabel!

private let highlightedBackgroundColor = UIColor(red: 59/255, green: 59/255, blue: 59/255, alpha: 1.0)
private let unhighlightedBackgroundColor = UIColor(red: 40/255, green: 36/255, blue: 36/255, alpha: 1.0)

private var _isPreviousVideo = false
private var _isCurrentVideo = false

/// The video's thumbnail.
var thumbnail: UIImage? {
get { return thumbnailImageView.image }
set { thumbnailImageView.image = newValue }
}

override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)

// Configure the view for the selected state

/// The video's title.
var title: String? {
get { return titleLabel.text }
set { titleLabel.text = newValue }
}

/// The video's channel.
var channel: String? {
get { return channelLabel.text }
set { channelLabel.text = newValue }
}

/// Is this the video immediately prior to the current video in the queue?
/// (If so, then its separator will be hidden.)
var isPreviousVideo: Bool {
get { return _isPreviousVideo }
set {
_isPreviousVideo = newValue
let visibleSeparator = UIEdgeInsetsMake(0, 15, 0, 0)
let hiddenSeparator = UIEdgeInsetsMake(0, 0, 0, bounds.size.width)
separatorInset = _isPreviousVideo ? hiddenSeparator : visibleSeparator
}
}

/// Is this the current video in the queue?
/// (If so, then its design will be updated.)
var isCurrentVideo: Bool {
get { return _isCurrentVideo }
set {
_isCurrentVideo = newValue
if _isCurrentVideo {
// update design for current video
backgroundColor = highlightedBackgroundColor
contentView.backgroundColor = highlightedBackgroundColor
separatorInset = UIEdgeInsetsMake(0, 0, 0, bounds.size.width)
} else {
// update design for queued video
backgroundColor = unhighlightedBackgroundColor
contentView.backgroundColor = unhighlightedBackgroundColor
separatorInset = UIEdgeInsetsMake(0, 15, 0, 0)
}
}
}
}
Loading

0 comments on commit 9294f6f

Please sign in to comment.