diff --git a/Persephone/AppDelegate.swift b/Persephone/AppDelegate.swift index 142ec6e..b097f24 100644 --- a/Persephone/AppDelegate.swift +++ b/Persephone/AppDelegate.swift @@ -16,19 +16,21 @@ class AppDelegate: NSObject, MediaKeyTapDelegate { var mediaKeyTap: MediaKeyTap? + @IBOutlet weak var connectMenuItem: NSMenuItem! + @IBOutlet weak var disconnectMenuItem: NSMenuItem! @IBOutlet weak var mainWindowMenuItem: NSMenuItem! @IBOutlet weak var updateDatabaseMenuItem: NSMenuItem! @IBOutlet weak var playSelectedSongMenuItem: NSMenuItem! @IBOutlet weak var playSelectedSongNextMenuItem: NSMenuItem! @IBOutlet weak var addSelectedSongToQueueMenuItem: NSMenuItem! - + func applicationDidFinishLaunching(_ aNotification: Notification) { mediaKeyTap = MediaKeyTap(delegate: self) mediaKeyTap?.start() App.store.subscribe(self) { $0.select { - $0.uiState + ($0.serverState, $0.uiState) } } @@ -105,6 +107,11 @@ class AppDelegate: NSObject, playSelectedSongNextMenuItem.isEnabled = selectedSong != nil addSelectedSongToQueueMenuItem.isEnabled = selectedSong != nil } + + func setConnectMenuItemsState(connected: Bool) { + connectMenuItem.isEnabled = !connected + disconnectMenuItem.isEnabled = connected + } func handle(mediaKey: MediaKey, event: KeyEvent) { switch mediaKey { @@ -117,6 +124,13 @@ class AppDelegate: NSObject, } } + @IBAction func connectMenuAction(_ sender: NSMenuItem) { + App.mpdServerController.connect() + } + @IBAction func disconnectMenuAction(_ sender: NSMenuItem) { + App.mpdServerController.disconnect() + } + @IBAction func updateDatabase(_ sender: NSMenuItem) { App.mpdClient.updateDatabase() } @@ -182,11 +196,14 @@ class AppDelegate: NSObject, } extension AppDelegate: StoreSubscriber { - typealias StoreSubscriberStateType = UIState + typealias StoreSubscriberStateType = ( + serverState: ServerState, uiState: UIState + ) - func newState(state: UIState) { - updateDatabaseMenuItem.isEnabled = !state.databaseUpdating - setMainWindowStateMenuItem(state: state.mainWindowState) - setSongMenuItemsState(selectedSong: state.selectedSong) + func newState(state: StoreSubscriberStateType) { + updateDatabaseMenuItem.isEnabled = !state.uiState.databaseUpdating + setMainWindowStateMenuItem(state: state.uiState.mainWindowState) + setSongMenuItemsState(selectedSong: state.uiState.selectedSong) + setConnectMenuItemsState(connected: state.serverState.connected) } } diff --git a/Persephone/Components/Window/Base.lproj/Main.storyboard b/Persephone/Components/Window/Base.lproj/Main.storyboard index 3c71fee..9fb40d1 100644 --- a/Persephone/Components/Window/Base.lproj/Main.storyboard +++ b/Persephone/Components/Window/Base.lproj/Main.storyboard @@ -14,7 +14,7 @@ - + @@ -29,7 +29,19 @@ - + + + + + + + + + + + + + @@ -178,6 +190,8 @@ + + @@ -208,7 +222,7 @@ - + @@ -230,7 +244,7 @@ - + @@ -257,7 +271,7 @@ - + @@ -279,7 +293,7 @@ - + @@ -295,7 +309,7 @@ - + @@ -308,7 +322,7 @@ - + @@ -324,7 +338,7 @@ - + @@ -426,7 +440,7 @@ - + @@ -464,7 +478,7 @@ - + @@ -476,7 +490,7 @@ - + @@ -512,7 +526,7 @@ - + @@ -571,7 +585,7 @@ - + @@ -594,7 +608,7 @@ - + @@ -608,7 +622,7 @@ - + @@ -634,7 +648,7 @@ - + @@ -669,7 +683,7 @@ - + @@ -714,7 +728,7 @@ - + @@ -735,7 +749,7 @@ - + @@ -762,7 +776,7 @@ - + @@ -775,7 +789,7 @@ - + diff --git a/Persephone/Components/Window/WindowController.swift b/Persephone/Components/Window/WindowController.swift index 6cb1af6..0b321a9 100644 --- a/Persephone/Components/Window/WindowController.swift +++ b/Persephone/Components/Window/WindowController.swift @@ -165,13 +165,28 @@ class WindowController: NSWindowController { DispatchQueue.main.async { let alert = NSAlert(error: error) - alert.informativeText = error.message + alert.messageText = error.message alert.alertStyle = error.recovered ? .warning : .critical + + if !error.recovered { + alert.addButton(withTitle: "Reconnect") + alert.addButton(withTitle: "Dismiss") + } - guard let window = NSApplication.shared.mainWindow + guard let window = NSApplication.shared.mainWindow ?? self.window else { return } - alert.beginSheetModal(for: window) { _ in } + + alert.beginSheetModal(for: window) { response in + switch response { + case .alertFirstButtonReturn: + if !error.recovered { + App.mpdServerController.connect() + } + default: + break + } + } } } diff --git a/Persephone/MPDClient/Extensions/MPDClient+Command.swift b/Persephone/MPDClient/Extensions/MPDClient+Command.swift index 5f8afdb..8135766 100644 --- a/Persephone/MPDClient/Extensions/MPDClient+Command.swift +++ b/Persephone/MPDClient/Extensions/MPDClient+Command.swift @@ -13,6 +13,8 @@ extension MPDClient { command: MPDCommand, userData: Dictionary = [:] ) { + guard command == .connect || isConnected else { return } + switch command { case .connect: diff --git a/Persephone/MPDClient/Extensions/MPDClient+Connection.swift b/Persephone/MPDClient/Extensions/MPDClient+Connection.swift index 48e0e6c..589cc2b 100644 --- a/Persephone/MPDClient/Extensions/MPDClient+Connection.swift +++ b/Persephone/MPDClient/Extensions/MPDClient+Connection.swift @@ -53,4 +53,10 @@ extension MPDClient { func disconnect() { enqueueCommand(command: .disconnect) } + + func resetConnection() { + delegate?.willDisconnect(mpdClient: self) + mpd_connection_free(connection) + self.isConnected = false; + } } diff --git a/Persephone/MPDClient/Extensions/MPDClient+Error.swift b/Persephone/MPDClient/Extensions/MPDClient+Error.swift index 68f28c7..2d1c6d0 100644 --- a/Persephone/MPDClient/Extensions/MPDClient+Error.swift +++ b/Persephone/MPDClient/Extensions/MPDClient+Error.swift @@ -34,6 +34,10 @@ extension MPDClient { message: message ) delegate?.didRaiseError(mpdClient: self, error: error) + + if !recovered { + resetConnection() + } return recovered } diff --git a/Persephone/MPDClient/Extensions/MPDClient+Idle.swift b/Persephone/MPDClient/Extensions/MPDClient+Idle.swift index ee9f28f..362086e 100644 --- a/Persephone/MPDClient/Extensions/MPDClient+Idle.swift +++ b/Persephone/MPDClient/Extensions/MPDClient+Idle.swift @@ -11,6 +11,8 @@ import mpdclient extension MPDClient { func noIdle() { + guard isConnected else { return } + do { idleLock.lock() defer { idleLock.unlock() } @@ -22,6 +24,8 @@ extension MPDClient { } func idle(_ force: Bool = false) { + guard isConnected else { return } + let shouldIdle: Bool do { @@ -50,8 +54,8 @@ extension MPDClient { wasIdle = isIdle isIdle = false } - - if wasIdle { + + if checkError() && wasIdle { if mpdIdle.contains(.database) { self.fetchAllAlbums() }