Skip to content

Commit

Permalink
wire up repeat and shuffle buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee Jun Kit committed Jun 3, 2021
1 parent 066142d commit 79188d5
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 12 deletions.
4 changes: 4 additions & 0 deletions Spottie.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
473061A6265B4A10001E3A1F /* WebAPITrackObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 473061A5265B4A10001E3A1F /* WebAPITrackObject.swift */; };
474BAFB8266876030006EB16 /* VolumeSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 474BAFB7266876030006EB16 /* VolumeSlider.swift */; };
474BAFBA26687AB60006EB16 /* WebAPIDeviceObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 474BAFB926687AB60006EB16 /* WebAPIDeviceObject.swift */; };
474BAFBC2668B0170006EB16 /* RepeatMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 474BAFBB2668B0170006EB16 /* RepeatMode.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -112,6 +113,7 @@
473061A5265B4A10001E3A1F /* WebAPITrackObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebAPITrackObject.swift; sourceTree = "<group>"; };
474BAFB7266876030006EB16 /* VolumeSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeSlider.swift; sourceTree = "<group>"; };
474BAFB926687AB60006EB16 /* WebAPIDeviceObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebAPIDeviceObject.swift; sourceTree = "<group>"; };
474BAFBB2668B0170006EB16 /* RepeatMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatMode.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -260,6 +262,7 @@
470201B4265B56350030ECA9 /* WebAPIImageObject.swift */,
470201B6265B56860030ECA9 /* WebAPIArtistObject.swift */,
474BAFB926687AB60006EB16 /* WebAPIDeviceObject.swift */,
474BAFBB2668B0170006EB16 /* RepeatMode.swift */,
);
path = Base;
sourceTree = "<group>";
Expand Down Expand Up @@ -364,6 +367,7 @@
470201C3265CF29B0030ECA9 /* NowPlaying.swift in Sources */,
4730616A26565BB7001E3A1F /* BottomBar.swift in Sources */,
473061722656629F001E3A1F /* Nothing.swift in Sources */,
474BAFBC2668B0170006EB16 /* RepeatMode.swift in Sources */,
473061A12659218F001E3A1F /* SpottieError.swift in Sources */,
4730616F26566076001E3A1F /* SpotifyAPI.swift in Sources */,
473061A42659FFF5001E3A1F /* CurrentlyPlayingContextObject.swift in Sources */,
Expand Down
20 changes: 20 additions & 0 deletions Spottie/Backend/SpotifyAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,24 @@ extension SpotifyAPI {
req.httpMethod = "POST"
return client.run(req).print().map(\.value).eraseToAnyPublisher()
}

static func setShuffle(shuffle: Bool) -> AnyPublisher<Nothing?, Error> {
let queryItems = [URLQueryItem(name: "val", value: shuffle ? "true" : "false")]
var urlComponents = URLComponents(url: base.appendingPathComponent("/player/shuffle"), resolvingAgainstBaseURL: false)!
urlComponents.queryItems = queryItems

var req = URLRequest(url: urlComponents.url!)
req.httpMethod = "POST"
return client.run(req).print().map(\.value).eraseToAnyPublisher()
}

static func setRepeatMode(mode: RepeatMode) -> AnyPublisher<Nothing?, Error> {
let queryItems = [URLQueryItem(name: "val", value: mode.rawValue)]
var urlComponents = URLComponents(url: base.appendingPathComponent("/player/repeat"), resolvingAgainstBaseURL: false)!
urlComponents.queryItems = queryItems

var req = URLRequest(url: urlComponents.url!)
req.httpMethod = "POST"
return client.run(req).print().map(\.value).eraseToAnyPublisher()
}
}
10 changes: 10 additions & 0 deletions Spottie/Backend/Types/Base/RepeatMode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// RepeatMode.swift
// Spottie
//
// Created by Lee Jun Kit on 3/6/21.
//

enum RepeatMode: String, Codable {
case none, track, context
}
5 changes: 4 additions & 1 deletion Spottie/ViewModels/FakePlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ final class FakePlayerViewModel: PlayerStateProtocol {
var isPlaying = false
var durationMs = 1200
var progressMs = 3600

var isShuffling = false
var repeatMode = RepeatMode.none
var trackName = "Track Name"
var artistName = "Artist Name"
var artworkURL = URL(string: "https://i.scdn.co/image/ab67616d00004851a48964b5d9a3d6968ae3e0de")
Expand All @@ -22,4 +23,6 @@ final class FakePlayerViewModel: PlayerStateProtocol {
func togglePlayPause() {}
func seek(toPercent: Double) {}
func setVolume(volumePercent: Float) {}
func toggleShuffle() {}
func cycleRepeatMode() {}
}
4 changes: 4 additions & 0 deletions Spottie/ViewModels/PlayerStateProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ protocol PlayerStateProtocol: ObservableObject {
var artworkURL: URL? { get set }
var durationMs: Int { get set }
var progressMs: Int { get set }
var isShuffling: Bool { get set }
var repeatMode: RepeatMode { get set }

func togglePlayPause() -> Void
func nextTrack() -> Void
func previousTrack() -> Void
func seek(toPercent: Double) -> Void
func setVolume(volumePercent: Float) -> Void
func toggleShuffle() -> Void
func cycleRepeatMode() -> Void
}
32 changes: 28 additions & 4 deletions Spottie/ViewModels/PlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class PlayerViewModel: PlayerStateProtocol {
@Published var trackName = ""
@Published var artistName = ""
@Published var artworkURL: URL?
@Published var isShuffling = false
@Published var repeatMode = RepeatMode.none

private var cancellables = [AnyCancellable]()
private var eventBroker: EventBroker
Expand Down Expand Up @@ -85,6 +87,10 @@ class PlayerViewModel: PlayerStateProtocol {
}
}

private func connectNothingPublisher(_ publisher: AnyPublisher<Nothing?, Error>) {
publisher.sink { _ in } receiveValue: { _ in }.store(in: &cancellables)
}

func togglePlayPause() {
var apiCall: AnyPublisher<Nothing?, Error>;
if self.isPlaying {
Expand All @@ -93,11 +99,11 @@ class PlayerViewModel: PlayerStateProtocol {
apiCall = SpotifyAPI.resume()
}

apiCall.print().sink { _ in } receiveValue: { _ in }.store(in: &cancellables)
self.connectNothingPublisher(apiCall)
}

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

func previousTrack() {
Expand All @@ -110,11 +116,29 @@ class PlayerViewModel: PlayerStateProtocol {
func seek(toPercent: Double) {
// calculate posMs
let posMs = Int(Double(self.durationMs) * toPercent)
SpotifyAPI.seek(posMs: posMs).sink { _ in } receiveValue: { _ in }.store(in: &cancellables)
self.connectNothingPublisher(SpotifyAPI.seek(posMs: posMs))
}

func setVolume(volumePercent: Float) {
self.volumePercent = volumePercent
SpotifyAPI.setVolume(volumePercent: volumePercent).sink { _ in } receiveValue: { _ in }.store(in: &cancellables)
self.connectNothingPublisher(SpotifyAPI.setVolume(volumePercent: volumePercent))
}

func toggleShuffle() {
self.isShuffling = !self.isShuffling
self.connectNothingPublisher(SpotifyAPI.setShuffle(shuffle: self.isShuffling))
}

func cycleRepeatMode() {
switch self.repeatMode {
case .none:
self.repeatMode = .track
case .track:
self.repeatMode = .context
case .context:
self.repeatMode = .none
}

self.connectNothingPublisher(SpotifyAPI.setRepeatMode(mode: self.repeatMode))
}
}
10 changes: 8 additions & 2 deletions Spottie/Views/Components/PlayerControls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ struct PlayerControls<M: PlayerStateProtocol>: View {
var body: some View {
VStack {
HStack(spacing: 28) {
ShuffleButton()
ShuffleButton(
isShuffling: viewModel.isShuffling,
toggle: viewModel.toggleShuffle
)
PreviousTrackButton(
previousTrackButtonTapped: viewModel.previousTrack
)
Expand All @@ -24,7 +27,10 @@ struct PlayerControls<M: PlayerStateProtocol>: View {
NextTrackButton(
nextTrackButtonTapped: viewModel.nextTrack
)
RepeatButton()
RepeatButton(
repeatMode: viewModel.repeatMode,
onRepeatButtonTapped: viewModel.cycleRepeatMode
)
}
TrackProgressSlider(viewModel: TrackProgressSlider.ViewModel(isPlaying: viewModel.isPlaying, progressMs: viewModel.progressMs, durationMs: viewModel.durationMs, onScrubToNewProgressPercent: viewModel.seek))
.padding(.leading)
Expand Down
33 changes: 29 additions & 4 deletions Spottie/Views/Components/RepeatButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,44 @@
import SwiftUI

struct RepeatButton: View {
var repeatMode: RepeatMode
var onRepeatButtonTapped: () -> Void
var imageName: String {
get {
switch repeatMode {
case .none:
return "repeat"
case .track:
return "repeat.1"
case .context:
return "repeat"
}
}
}

var body: some View {
Button(action: {
onRepeatButtonTapped()
}) {
Image(systemName: "repeat")
.resizable()
.frame(width: 12, height: 12)
HStack {
Image(systemName: imageName)
.resizable()
.frame(width: 12, height: 12)
if repeatMode == .context {
Text("ALL")
.font(.footnote)
}
}

}
.buttonStyle(BorderlessButtonStyle())
.foregroundColor(repeatMode == .none ? .secondary : .green)
}
}

struct RepeatButton_Previews: PreviewProvider {
static func onRepeatButtonTapped() {}
static var previews: some View {
RepeatButton()
RepeatButton(repeatMode: .none, onRepeatButtonTapped: onRepeatButtonTapped)
}
}
8 changes: 7 additions & 1 deletion Spottie/Views/Components/ShuffleButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,25 @@
import SwiftUI

struct ShuffleButton: View {
var isShuffling: Bool
var toggle: () -> Void

var body: some View {
Button(action: {
toggle()
}) {
Image(systemName: "shuffle")
.resizable()
.frame(width: 12, height: 12)
}
.buttonStyle(BorderlessButtonStyle())
.foregroundColor(isShuffling ? .green : .secondary)
}
}

struct ShuffleButton_Previews: PreviewProvider {
static func toggle() {}
static var previews: some View {
ShuffleButton()
ShuffleButton(isShuffling: false, toggle: toggle)
}
}

0 comments on commit 79188d5

Please sign in to comment.