Skip to content

Commit

Permalink
fix up TrackProgressSlider
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee Jun Kit committed May 28, 2021
1 parent 36cce78 commit 81c4320
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 66 deletions.
6 changes: 3 additions & 3 deletions Spottie/ViewModels/FakePlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class FakePlayerViewModel: PlayerStateProtocol {
var artistName = "Artist Name"
var artworkURL = URL(string: "https://i.scdn.co/image/ab67616d00004851a48964b5d9a3d6968ae3e0de")

func onPlayPauseButtonTapped() {}
func onNextTrackButtonTapped() {}
func onPreviousTrackButtonTapped() {}
func previousTrack() {}
func nextTrack() {}
func togglePlayPause() {}
}
6 changes: 3 additions & 3 deletions Spottie/ViewModels/PlayerStateProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ protocol PlayerStateProtocol: ObservableObject {
var durationMs: Int { get set }
var progressMs: Int { get set }

func onPlayPauseButtonTapped() -> Void
func onNextTrackButtonTapped() -> Void
func onPreviousTrackButtonTapped() -> Void
func togglePlayPause() -> Void
func nextTrack() -> Void
func previousTrack() -> Void
}
52 changes: 31 additions & 21 deletions Spottie/ViewModels/PlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,43 +31,53 @@ class PlayerViewModel: PlayerStateProtocol {

// subscribe to events
eventBroker.onEventReceived.sink(receiveValue: {[weak self] event in
guard let self = self else { return }

switch (event.data) {
case .playbackEnded(_):
fallthrough
case .playbackPaused(_):
self?.isPlaying = false
case .playbackResumed(_):
self?.isPlaying = true
self.isPlaying = false
case let .playbackPaused(playbackPausedEvent):
self.progressMs = playbackPausedEvent.trackTime
self.isPlaying = false
case let .playbackResumed(playbackResumedEvent):
self.isPlaying = true
self.progressMs = playbackResumedEvent.trackTime
case let .trackChanged(trackChangedEvent):
self.progressMs = 0
self.isPlaying = true
if let track = trackChangedEvent.track {
self?.trackName = track.name
self?.artistName = track.artist[0].name
self.trackName = track.name
self.artistName = track.artist[0].name
}
case let .metadataAvailable(metadataAvailableEvent):
self?.trackName = metadataAvailableEvent.track.name
self?.artistName = metadataAvailableEvent.track.artist[0].name
self?.artworkURL = metadataAvailableEvent.track.album.coverGroup.getArtworkURL()
self?.durationMs = metadataAvailableEvent.track.duration
self?.progressMs = 0
self.trackName = metadataAvailableEvent.track.name
self.artistName = metadataAvailableEvent.track.artist[0].name
self.artworkURL = metadataAvailableEvent.track.album.coverGroup.getArtworkURL()
self.durationMs = metadataAvailableEvent.track.duration
default:
break;
}

print(" in event recevied: self.durationMs: \(self.durationMs) self.progressMs: \(self.progressMs)")
}).store(in: &self.cancellables)
}) {[weak self] context in
guard let self = self else { return }
if let ctx = context {
self?.isPlaying = ctx.isPlaying
self?.trackName = ctx.item.name
self?.artistName = ctx.item.artists[0].name
self?.artworkURL = ctx.item.album.getArtworkURL()
self?.durationMs = ctx.item.durationMs
self?.progressMs = ctx.progressMs
self.isPlaying = ctx.isPlaying
self.trackName = ctx.item.name
self.artistName = ctx.item.artists[0].name
self.artworkURL = ctx.item.album.getArtworkURL()
self.durationMs = ctx.item.durationMs
self.progressMs = ctx.progressMs
}

print(" in value recevied: self.durationMs: \(self.durationMs) self.progressMs: \(self.progressMs)")
}

token.store(in: &cancellables)
}

func onPlayPauseButtonTapped() {
func togglePlayPause() {
var apiCall: AnyPublisher<Nothing?, Error>;
if self.isPlaying {
apiCall = SpotifyAPI.pause()
Expand All @@ -78,11 +88,11 @@ class PlayerViewModel: PlayerStateProtocol {
apiCall.print().sink { _ in } receiveValue: { _ in }.store(in: &cancellables)
}

func onNextTrackButtonTapped() {
func nextTrack() {
SpotifyAPI.nextTrack().sink { _ in } receiveValue: { _ in }.store(in: &cancellables)
}

func onPreviousTrackButtonTapped() {
func previousTrack() {
SpotifyAPI.previousTrack().sink { _ in } receiveValue: { _ in }.store(in: &cancellables)
}
}
13 changes: 7 additions & 6 deletions Spottie/Views/Components/NextTrackButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@

import SwiftUI

struct NextTrackButton<M: PlayerStateProtocol>: View {
@EnvironmentObject var viewModel: M

struct NextTrackButton: View {
var nextTrackButtonTapped: () -> Void
var body: some View {
Button(action: {
viewModel.onNextTrackButtonTapped()
self.nextTrackButtonTapped()
}) {
Image(systemName: "forward.end.fill")
.resizable()
Expand All @@ -23,8 +22,10 @@ struct NextTrackButton<M: PlayerStateProtocol>: View {
}

struct NextTrackButton_Previews: PreviewProvider {
static func nextTrackButtonTapped() {
print("nextTrackButtonTapped")
}
static var previews: some View {
NextTrackButton<FakePlayerViewModel>()
.environmentObject(FakePlayerViewModel())
NextTrackButton(nextTrackButtonTapped: nextTrackButtonTapped)
}
}
19 changes: 12 additions & 7 deletions Spottie/Views/Components/PlayPauseButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
import SwiftUI
import Combine

struct PlayPauseButton<M: PlayerStateProtocol>: View {
@EnvironmentObject var viewModel: M
struct PlayPauseButton: View {
var playPauseButtonTapped: () -> Void
var isPlaying: Bool

var body: some View {
Button(action: {
viewModel.onPlayPauseButtonTapped()
playPauseButtonTapped()
}) {
Image(systemName: viewModel.isPlaying ? "pause.circle.fill" : "play.circle.fill")
Image(systemName: isPlaying ? "pause.circle.fill" : "play.circle.fill")
.resizable()
.clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/)
.frame(width: 32, height: 32)
Expand All @@ -25,10 +26,14 @@ struct PlayPauseButton<M: PlayerStateProtocol>: View {
}

struct PlayPauseButton_Previews: PreviewProvider {

static func playPauseButtonTapped() {
print("playPauseButtonTapped")
}

static var previews: some View {
PlayPauseButton<FakePlayerViewModel>()
.environmentObject(FakePlayerViewModel())
PlayPauseButton(
playPauseButtonTapped: playPauseButtonTapped,
isPlaying: true
)
}
}
17 changes: 13 additions & 4 deletions Spottie/Views/Components/PlayerControls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,25 @@
import SwiftUI

struct PlayerControls<M: PlayerStateProtocol>: View {
@EnvironmentObject var viewModel: M

var body: some View {
VStack {
HStack(spacing: 28) {
ShuffleButton()
PreviousTrackButton<M>()
PlayPauseButton<M>()
NextTrackButton<M>()
PreviousTrackButton(
previousTrackButtonTapped: viewModel.previousTrack
)
PlayPauseButton(
playPauseButtonTapped: viewModel.togglePlayPause,
isPlaying: viewModel.isPlaying
)
NextTrackButton(
nextTrackButtonTapped: viewModel.nextTrack
)
RepeatButton()
}
TrackProgressSlider()
TrackProgressSlider(viewModel: TrackProgressSlider.ViewModel(isPlaying: viewModel.isPlaying, progressMs: viewModel.progressMs, durationMs: viewModel.durationMs))
.padding(.leading)
.padding(.trailing)
}
Expand Down
13 changes: 7 additions & 6 deletions Spottie/Views/Components/PreviousTrackButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@

import SwiftUI

struct PreviousTrackButton<M: PlayerStateProtocol>: View {
@EnvironmentObject var viewModel: M

struct PreviousTrackButton: View {
var previousTrackButtonTapped: () -> Void
var body: some View {
Button(action: {
viewModel.onPreviousTrackButtonTapped()
self.previousTrackButtonTapped()
}) {
Image(systemName: "backward.end.fill")
.resizable()
Expand All @@ -23,8 +22,10 @@ struct PreviousTrackButton<M: PlayerStateProtocol>: View {
}

struct PreviousTrackButton_Previews: PreviewProvider {
static func onPreviousTrackTapped() {
print("onPreviousTrackTapped")
}
static var previews: some View {
PreviousTrackButton<FakePlayerViewModel>()
.environmentObject(FakePlayerViewModel())
PreviousTrackButton(previousTrackButtonTapped: onPreviousTrackTapped)
}
}
52 changes: 36 additions & 16 deletions Spottie/Views/Components/TrackProgressSlider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,32 @@
import SwiftUI

struct TrackProgressSlider: View {
@State var isPlaying = true
@State var progressMs = 0
@State var durationMs = 60000
@State var progressPercent = 0.0

@ObservedObject var viewModel: TrackProgressSlider.ViewModel

let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

var prettyProgress: String {
get {
return TrackProgressSlider.DurationFormatter.shared().string(from: Double(self.progressMs) / 1000.0)!
return TrackProgressSlider.DurationFormatter.shared().string(from: Double(self.viewModel.progressMs) / 1000.0)!
}
}

var prettyDuration: String {
get {
return TrackProgressSlider.DurationFormatter.shared().string(from: Double(self.durationMs) / 1000.0)!
return TrackProgressSlider.DurationFormatter.shared().string(from: Double(self.viewModel.durationMs) / 1000.0)!
}
}

var body: some View {
Slider(
value: $progressPercent,
value: $viewModel.progressPercent,
minimumValueLabel: Text(prettyProgress).foregroundColor(.secondary),
maximumValueLabel: Text(prettyDuration).foregroundColor(.secondary)
) {
Text("")
}
.onReceive(timer) { timer in
if isPlaying {
if (progressMs < durationMs) {
progressMs += 1000
progressPercent = Double(self.progressMs) / Double(self.durationMs)
}
}
.onReceive(timer) { _ in
viewModel.updateProgress()
}
}
}
Expand All @@ -60,10 +52,38 @@ extension TrackProgressSlider {
return sharedDurationFormatter
}
}

class ViewModel: ObservableObject {
@Published var isPlaying: Bool
@Published var progressMs: Int
@Published var durationMs: Int
@Published var progressPercent: Double

init(isPlaying: Bool, progressMs: Int, durationMs: Int) {
self.isPlaying = isPlaying
self.progressMs = progressMs
self.durationMs = durationMs
self.progressPercent = Double(progressMs) / Double(durationMs)
}

func calculateProgressPercent() {
self.progressPercent = Double(self.progressMs) / Double(self.durationMs)
}

func updateProgress() {
if (self.isPlaying) {
if (self.progressMs < self.durationMs) {
self.progressMs += 1000
self.calculateProgressPercent()
}
}
}
}
}

struct TrackProgressSlider_Previews: PreviewProvider {
static let viewModel = TrackProgressSlider.ViewModel(isPlaying: true, progressMs: 20000, durationMs: 120000)
static var previews: some View {
TrackProgressSlider()
TrackProgressSlider(viewModel: viewModel)
}
}

0 comments on commit 81c4320

Please sign in to comment.