diff --git a/Persephone/AppDelegate.swift b/Persephone/AppDelegate.swift
index 883d708..dd321b4 100644
--- a/Persephone/AppDelegate.swift
+++ b/Persephone/AppDelegate.swift
@@ -18,7 +18,7 @@ class AppDelegate: NSObject,
func applicationDidFinishLaunching(_ aNotification: Notification) {
App.mpdServerController.connect()
- _ = App.userNotificationsController
+ instantiateUserNotificationsController()
mediaKeyTap = MediaKeyTap(delegate: self)
mediaKeyTap?.start()
@@ -30,6 +30,10 @@ class AppDelegate: NSObject,
}
}
+ func instantiateUserNotificationsController() {
+ _ = App.userNotificationsController
+ }
+
func applicationWillTerminate(_ aNotification: Notification) {
App.mpdServerController.disconnect()
}
diff --git a/Persephone/Controllers/WindowController.swift b/Persephone/Controllers/WindowController.swift
index 7ff5f21..b126160 100644
--- a/Persephone/Controllers/WindowController.swift
+++ b/Persephone/Controllers/WindowController.swift
@@ -17,6 +17,16 @@ class WindowController: NSWindowController {
var state: MPDClient.MPDStatus.State?
var trackTimer: Timer?
+ @IBOutlet var transportControls: NSSegmentedCell!
+
+ @IBOutlet var trackProgress: NSTextField!
+ @IBOutlet var trackProgressBar: NSSlider!
+ @IBOutlet var trackRemaining: NSTextField!
+ @IBOutlet var databaseUpdatingIndicator: NSProgressIndicator!
+
+ @IBOutlet var shuffleState: NSButton!
+ @IBOutlet var repeatState: NSButton!
+
override func windowDidLoad() {
super.windowDidLoad()
window?.titleVisibility = .hidden
@@ -137,12 +147,14 @@ class WindowController: NSWindowController {
}
}
- @IBOutlet var transportControls: NSSegmentedCell!
-
- @IBOutlet var trackProgress: NSTextField!
- @IBOutlet var trackProgressBar: NSSlider!
- @IBOutlet var trackRemaining: NSTextField!
- @IBOutlet var databaseUpdatingIndicator: NSProgressIndicator!
+ @IBAction func handleShuffleButton(_ sender: NSButton) {
+ App.store.dispatch(MPDSetShuffleAction(shuffleState: sender.state == .on))
+ }
+
+ @IBAction func handleRepeatButton(_ sender: NSButton) {
+ App.store.dispatch(MPDSetRepeatAction(repeatState: sender.state == .on))
+ }
+
}
extension WindowController: NSWindowDelegate {
diff --git a/Persephone/MPDClient/Extensions/MPDClient+Command.swift b/Persephone/MPDClient/Extensions/MPDClient+Command.swift
index 8eadda1..f5b0cca 100644
--- a/Persephone/MPDClient/Extensions/MPDClient+Command.swift
+++ b/Persephone/MPDClient/Extensions/MPDClient+Command.swift
@@ -29,6 +29,16 @@ extension MPDClient {
else { return }
sendSeekCurrentSong(timeInSeconds: timeInSeconds)
+ case .setShuffleState:
+ guard let shuffleState = userData["shuffleState"] as? Bool
+ else { return }
+ sendShuffleState(shuffleState: shuffleState)
+
+ case .setRepeatState:
+ guard let repeatState = userData["repeatState"] as? Bool
+ else { return }
+ sendRepeatState(repeatState: repeatState)
+
// Database commands
case .updateDatabase:
sendUpdateDatabase()
diff --git a/Persephone/MPDClient/Extensions/MPDClient+Transport.swift b/Persephone/MPDClient/Extensions/MPDClient+Transport.swift
index cb53fa9..2c97a82 100644
--- a/Persephone/MPDClient/Extensions/MPDClient+Transport.swift
+++ b/Persephone/MPDClient/Extensions/MPDClient+Transport.swift
@@ -33,6 +33,20 @@ extension MPDClient {
)
}
+ func setShuffleState(shuffleState: Bool) {
+ enqueueCommand(
+ command: .setShuffleState,
+ userData: ["shuffleState": shuffleState]
+ )
+ }
+
+ func setRepeatState(repeatState: Bool) {
+ enqueueCommand(
+ command: .setRepeatState,
+ userData: ["repeatState": repeatState]
+ )
+ }
+
func sendNextTrack() {
guard let state = status?.state,
state.isOneOf([.playing, .paused])
@@ -64,4 +78,12 @@ extension MPDClient {
func sendSeekCurrentSong(timeInSeconds: Float) {
mpd_run_seek_current(self.connection, timeInSeconds, false)
}
+
+ func sendShuffleState(shuffleState: Bool) {
+ mpd_run_random(self.connection, shuffleState)
+ }
+
+ func sendRepeatState(repeatState: Bool) {
+ mpd_run_repeat(self.connection, repeatState)
+ }
}
diff --git a/Persephone/MPDClient/Models/MPDCommand.swift b/Persephone/MPDClient/Models/MPDCommand.swift
index 2631a93..b938056 100644
--- a/Persephone/MPDClient/Models/MPDCommand.swift
+++ b/Persephone/MPDClient/Models/MPDCommand.swift
@@ -17,6 +17,9 @@ extension MPDClient {
case stop
case seekCurrentSong
+ case setShuffleState
+ case setRepeatState
+
// Database commands
case updateDatabase
diff --git a/Persephone/Resources/Base.lproj/Main.storyboard b/Persephone/Resources/Base.lproj/Main.storyboard
index 58bf652..b032e35 100644
--- a/Persephone/Resources/Base.lproj/Main.storyboard
+++ b/Persephone/Resources/Base.lproj/Main.storyboard
@@ -222,9 +222,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -239,6 +274,8 @@
+
+
@@ -749,6 +786,8 @@
+
+
diff --git a/Persephone/State/Actions/MPDActions.swift b/Persephone/State/Actions/MPDActions.swift
index e4be191..f13fb37 100644
--- a/Persephone/State/Actions/MPDActions.swift
+++ b/Persephone/State/Actions/MPDActions.swift
@@ -29,3 +29,11 @@ struct MPDSeekCurrentSong: Action {
}
struct MPDUpdateDatabaseAction: Action {}
+
+struct MPDSetShuffleAction: Action {
+ let shuffleState: Bool
+}
+
+struct MPDSetRepeatAction: Action {
+ let repeatState: Bool
+}
diff --git a/Persephone/State/Actions/PlayerActions.swift b/Persephone/State/Actions/PlayerActions.swift
index 76a12f2..6fa3991 100644
--- a/Persephone/State/Actions/PlayerActions.swift
+++ b/Persephone/State/Actions/PlayerActions.swift
@@ -24,3 +24,11 @@ struct UpdateElapsedTimeAction: Action {
struct UpdateStatusAction: Action {
var status: MPDClient.MPDStatus
}
+
+struct UpdateShuffleAction: Action {
+ var shuffleState: Bool
+}
+
+struct UpdateRepeatAction: Action {
+ var repeatState: Bool
+}
diff --git a/Persephone/State/PlayerState.swift b/Persephone/State/PlayerState.swift
index 5c9aed1..6c58766 100644
--- a/Persephone/State/PlayerState.swift
+++ b/Persephone/State/PlayerState.swift
@@ -15,6 +15,8 @@ struct PlayerState: StateType {
var currentArtwork: NSImage?
var state: MPDClient.MPDStatus.State?
+ var shuffleState: Bool = false
+ var repeatState: Bool = false
var totalTime: UInt?
var elapsedTimeMs: UInt?
@@ -24,6 +26,8 @@ extension PlayerState: Equatable {
static func == (lhs: PlayerState, rhs: PlayerState) -> Bool {
return (lhs.state == rhs.state) &&
(lhs.totalTime == rhs.totalTime) &&
- (lhs.elapsedTimeMs == rhs.elapsedTimeMs)
+ (lhs.elapsedTimeMs == rhs.elapsedTimeMs) &&
+ (lhs.shuffleState == rhs.shuffleState) &&
+ (lhs.repeatState == rhs.repeatState)
}
}
diff --git a/Persephone/State/Reducers/MPDReducer.swift b/Persephone/State/Reducers/MPDReducer.swift
index aa26ff8..3e85308 100644
--- a/Persephone/State/Reducers/MPDReducer.swift
+++ b/Persephone/State/Reducers/MPDReducer.swift
@@ -39,6 +39,12 @@ func mpdReducer(action: Action, state: MPDState?) -> MPDState {
case let action as MPDSeekCurrentSong:
App.mpdClient.seekCurrentSong(timeInSeconds: action.timeInSeconds)
+ case let action as MPDSetShuffleAction:
+ App.mpdClient.setShuffleState(shuffleState: action.shuffleState)
+
+ case let action as MPDSetRepeatAction:
+ App.mpdClient.setRepeatState(repeatState: action.repeatState)
+
case is MPDUpdateDatabaseAction:
App.mpdClient.updateDatabase()
diff --git a/Persephone/State/Reducers/PlayerReducer.swift b/Persephone/State/Reducers/PlayerReducer.swift
index d468407..eecde2a 100644
--- a/Persephone/State/Reducers/PlayerReducer.swift
+++ b/Persephone/State/Reducers/PlayerReducer.swift
@@ -60,6 +60,12 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
case let action as UpdateElapsedTimeAction:
state.elapsedTimeMs = action.elapsedTimeMs
+ case let action as UpdateShuffleAction:
+ state.shuffleState = action.shuffleState
+
+ case let action as UpdateRepeatAction:
+ state.repeatState = action.repeatState
+
default:
break
}
diff --git a/Resources/export/repeatButton.pdf b/Resources/export/repeatButton.pdf
new file mode 100644
index 0000000..2a8bf58
Binary files /dev/null and b/Resources/export/repeatButton.pdf differ
diff --git a/Resources/export/repeatButton.png b/Resources/export/repeatButton.png
new file mode 100644
index 0000000..ac2ad96
Binary files /dev/null and b/Resources/export/repeatButton.png differ
diff --git a/Resources/export/repeatButton1.pdf b/Resources/export/repeatButton1.pdf
new file mode 100644
index 0000000..bcd790e
Binary files /dev/null and b/Resources/export/repeatButton1.pdf differ
diff --git a/Resources/export/repeatButton1.png b/Resources/export/repeatButton1.png
new file mode 100644
index 0000000..a9a672b
Binary files /dev/null and b/Resources/export/repeatButton1.png differ
diff --git a/Resources/export/repeatButton1@2x.png b/Resources/export/repeatButton1@2x.png
new file mode 100644
index 0000000..08810d0
Binary files /dev/null and b/Resources/export/repeatButton1@2x.png differ
diff --git a/Resources/export/repeatButton@2x.png b/Resources/export/repeatButton@2x.png
new file mode 100644
index 0000000..40f668f
Binary files /dev/null and b/Resources/export/repeatButton@2x.png differ
diff --git a/Resources/export/shuffleButton.pdf b/Resources/export/shuffleButton.pdf
new file mode 100644
index 0000000..56b78f1
Binary files /dev/null and b/Resources/export/shuffleButton.pdf differ
diff --git a/Resources/export/shuffleButton.png b/Resources/export/shuffleButton.png
new file mode 100644
index 0000000..df7bb8d
Binary files /dev/null and b/Resources/export/shuffleButton.png differ
diff --git a/Resources/export/shuffleButton@2x.png b/Resources/export/shuffleButton@2x.png
new file mode 100644
index 0000000..4f6c2d1
Binary files /dev/null and b/Resources/export/shuffleButton@2x.png differ
diff --git a/Resources/icons.sketch b/Resources/icons.sketch
index 44a3c54..f7c8c5d 100644
Binary files a/Resources/icons.sketch and b/Resources/icons.sketch differ