From 49269ca21bae15723b6804381c2d5d79ec858d57 Mon Sep 17 00:00:00 2001 From: Daniel Barber Date: Thu, 2 May 2019 22:32:20 -0400 Subject: [PATCH] Handle closing and opening windows --- Persephone.xcodeproj/project.pbxproj | 12 ++++++ Persephone/AppDelegate.swift | 14 ++++--- .../Controllers/AlbumViewController.swift | 18 +-------- .../Controllers/MPDServerController.swift | 4 +- .../Controllers/QueueViewController.swift | 2 +- Persephone/Controllers/WindowController.swift | 40 +++++++++---------- .../Resources/Base.lproj/Main.storyboard | 20 +++++++--- Persephone/State/Actions/PlayerActions.swift | 4 -- Persephone/State/Actions/UIActions.swift | 17 ++++++++ Persephone/State/AppState.swift | 1 + Persephone/State/PlayerState.swift | 5 +-- Persephone/State/Reducers/AppReducer.swift | 3 +- Persephone/State/Reducers/PlayerReducer.swift | 6 --- Persephone/State/Reducers/UIReducer.swift | 32 +++++++++++++++ Persephone/State/UIState.swift | 14 +++++++ 15 files changed, 128 insertions(+), 64 deletions(-) create mode 100644 Persephone/State/Actions/UIActions.swift create mode 100644 Persephone/State/Reducers/UIReducer.swift create mode 100644 Persephone/State/UIState.swift diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 26e26c9..c2dbaad 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -43,6 +43,9 @@ E4405196227879960090CD6F /* MPDActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4405195227879960090CD6F /* MPDActions.swift */; }; E440519822787CB40090CD6F /* MPDState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519722787CB40090CD6F /* MPDState.swift */; }; E440519A22787CF60090CD6F /* MPDReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519922787CF60090CD6F /* MPDReducer.swift */; }; + E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519B227BAF2E0090CD6F /* UIActions.swift */; }; + E440519E227BB0720090CD6F /* UIReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519D227BB0720090CD6F /* UIReducer.swift */; }; + E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519F227BB0AB0090CD6F /* UIState.swift */; }; E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; }; E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */; }; E450AD9122262C780091BED3 /* SwiftyJSON.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */; }; @@ -245,6 +248,9 @@ E4405195227879960090CD6F /* MPDActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDActions.swift; sourceTree = ""; }; E440519722787CB40090CD6F /* MPDState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDState.swift; sourceTree = ""; }; E440519922787CF60090CD6F /* MPDReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDReducer.swift; sourceTree = ""; }; + E440519B227BAF2E0090CD6F /* UIActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActions.swift; sourceTree = ""; }; + E440519D227BB0720090CD6F /* UIReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIReducer.swift; sourceTree = ""; }; + E440519F227BB0AB0090CD6F /* UIState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIState.swift; sourceTree = ""; }; E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = ""; }; E450AD8522262AE60091BED3 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = ""; }; E450AD8C22262C590091BED3 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/Mac/PromiseKit.framework; sourceTree = ""; }; @@ -603,6 +609,7 @@ E4B11B78226D346B0075461B /* AlbumListReducer.swift */, E4FF718F227601B400D4C412 /* PreferencesReducer.swift */, E440519922787CF60090CD6F /* MPDReducer.swift */, + E440519D227BB0720090CD6F /* UIReducer.swift */, ); path = Reducers; sourceTree = ""; @@ -613,6 +620,7 @@ E4B11B6B226A5AF50075461B /* Actions */, E4B11B5F226A4BED0075461B /* Reducers */, E4B11B52226928F20075461B /* AppState.swift */, + E440519F227BB0AB0090CD6F /* UIState.swift */, E4B11B65226A4F830075461B /* PlayerState.swift */, E4B11B67226A4FA00075461B /* QueueState.swift */, E4B11B69226A4FBC0075461B /* AlbumListState.swift */, @@ -630,6 +638,7 @@ E4B11BBF2275EE150075461B /* QueueActions.swift */, E4FF71912276029000D4C412 /* PreferencesActions.swift */, E4405195227879960090CD6F /* MPDActions.swift */, + E440519B227BAF2E0090CD6F /* UIActions.swift */, ); path = Actions; sourceTree = ""; @@ -909,6 +918,7 @@ E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */, E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */, E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */, + E440519E227BB0720090CD6F /* UIReducer.swift in Sources */, E4FF7190227601B400D4C412 /* PreferencesReducer.swift in Sources */, E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */, E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */, @@ -919,6 +929,7 @@ E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */, E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */, E419E2872249B96600216A8C /* Song.swift in Sources */, + E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */, E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */, E439109822640213002982E9 /* SongNotifierService.swift in Sources */, E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */, @@ -943,6 +954,7 @@ E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */, E41E530E223EF4CF00173814 /* CoverArtService+Caching.swift in Sources */, E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */, + E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */, E41E5312223EF74A00173814 /* CoverArtService+Filesystem.swift in Sources */, E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */, E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */, diff --git a/Persephone/AppDelegate.swift b/Persephone/AppDelegate.swift index 5f65561..ecabd69 100644 --- a/Persephone/AppDelegate.swift +++ b/Persephone/AppDelegate.swift @@ -24,7 +24,9 @@ class AppDelegate: NSObject, mediaKeyTap?.start() App.store.subscribe(self) { - $0.select { $0.playerState.databaseUpdating } + $0.select { + $0.uiState + } } } @@ -107,17 +109,19 @@ class AppDelegate: NSObject, @IBAction func nextTrackMenuAction(_ sender: NSMenuItem) { App.store.dispatch(MPDNextTrackAction()) } - @IBAction func prevTrackMenuAction(_ sender: Any) { + @IBAction func prevTrackMenuAction(_ sender: NSMenuItem) { App.store.dispatch(MPDPrevTrackAction()) } + @IBOutlet weak var mainWindowMenuItem: NSMenuItem! @IBOutlet weak var updateDatabaseMenuItem: NSMenuItem! } extension AppDelegate: StoreSubscriber { - typealias StoreSubscriberStateType = Bool + typealias StoreSubscriberStateType = UIState - func newState(state: Bool) { - updateDatabaseMenuItem.isEnabled = !state + func newState(state: UIState) { + updateDatabaseMenuItem.isEnabled = !state.databaseUpdating + mainWindowMenuItem.state = state.mainWindowOpen ? .on : .off } } diff --git a/Persephone/Controllers/AlbumViewController.swift b/Persephone/Controllers/AlbumViewController.swift index 00c9104..3758504 100644 --- a/Persephone/Controllers/AlbumViewController.swift +++ b/Persephone/Controllers/AlbumViewController.swift @@ -53,22 +53,6 @@ class AlbumViewController: NSViewController, layout.setScrollPosition() } - override func observeValue( - forKeyPath keyPath: String?, - of object: Any?, - change: [NSKeyValueChangeKey : Any]?, - context: UnsafeMutableRawPointer? - ) { - switch keyPath { - case "mpdLibraryDir": - albumCollectionView.reloadData() - case "fetchMissingArtworkFromInternet": - App.store.dispatch(ResetAlbumListCoverArtAction()) - default: - break - } - } - @IBOutlet var albumScrollView: NSScrollView! @IBOutlet var albumCollectionView: NSCollectionView! } @@ -78,7 +62,9 @@ extension AlbumViewController: StoreSubscriber { func newState(state: StoreSubscriberStateType) { let oldAlbums = dataSource.albums + dataSource.albums = state.albums + albumCollectionView.animateItemChanges( oldData: oldAlbums, newData: dataSource.albums diff --git a/Persephone/Controllers/MPDServerController.swift b/Persephone/Controllers/MPDServerController.swift index 414a263..1bff238 100644 --- a/Persephone/Controllers/MPDServerController.swift +++ b/Persephone/Controllers/MPDServerController.swift @@ -55,13 +55,13 @@ extension MPDServerController: MPDClientDelegate { func willStartDatabaseUpdate(mpdClient: MPDClient) { DispatchQueue.main.async { - App.store.dispatch(StartedDatabaseUpdateAction()) + App.store.dispatch(DatabaseUpdateStartedAction()) } } func didFinishDatabaseUpdate(mpdClient: MPDClient) { DispatchQueue.main.async { - App.store.dispatch(FinishedDatabaseUpdateAction()) + App.store.dispatch(DatabaseUpdateFinishedAction()) } } diff --git a/Persephone/Controllers/QueueViewController.swift b/Persephone/Controllers/QueueViewController.swift index 7da4dde..a2cf549 100644 --- a/Persephone/Controllers/QueueViewController.swift +++ b/Persephone/Controllers/QueueViewController.swift @@ -47,7 +47,7 @@ class QueueViewController: NSViewController, func outlineView( _ outlineView: NSOutlineView, selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet - ) -> IndexSet { + ) -> IndexSet { if proposedSelectionIndexes.contains(0) { return IndexSet() } else { diff --git a/Persephone/Controllers/WindowController.swift b/Persephone/Controllers/WindowController.swift index 8a9aa81..d5ec195 100644 --- a/Persephone/Controllers/WindowController.swift +++ b/Persephone/Controllers/WindowController.swift @@ -23,9 +23,13 @@ class WindowController: NSWindowController { window?.isExcludedFromWindowsMenu = true App.store.subscribe(self) { - $0.select { $0.playerState } + $0.select { + ($0.playerState, $0.uiState) + } } + App.store.dispatch(MainWindowDidOpenAction()) + trackProgress.font = .timerFont trackRemaining.font = .timerFont } @@ -68,22 +72,14 @@ class WindowController: NSWindowController { setTimeRemaining(elapsedTimeMs, totalTime * 1000) } - func setDatabaseUpdatingIndicator(_ playerState: PlayerState) { - if playerState.databaseUpdating { - startDatabaseUpdatingIndicator() + func setDatabaseUpdatingIndicator(_ uiState: UIState) { + if uiState.databaseUpdating { + databaseUpdatingIndicator.startAnimation(self) } else { - stopDatabaseUpdatingIndicator() + databaseUpdatingIndicator.stopAnimation(self) } } - func startDatabaseUpdatingIndicator() { - databaseUpdatingIndicator.startAnimation(self) - } - - func stopDatabaseUpdatingIndicator() { - databaseUpdatingIndicator.stopAnimation(self) - } - func setTimeElapsed(_ elapsedTimeMs: UInt?) { guard let elapsedTimeMs = elapsedTimeMs else { return } @@ -149,16 +145,20 @@ class WindowController: NSWindowController { @IBOutlet var databaseUpdatingIndicator: NSProgressIndicator! } +extension WindowController: NSWindowDelegate { + func windowWillClose(_ notification: Notification) { + App.store.dispatch(MainWindowDidCloseAction()) + } +} + extension WindowController: StoreSubscriber { - typealias StoreSubscriberStateType = PlayerState - - func newState(state: StoreSubscriberStateType) { - self.state = state.state + typealias StoreSubscriberStateType = (playerState: PlayerState, uiState: UIState) + func newState(state: (playerState: PlayerState, uiState: UIState)) { DispatchQueue.main.async { - self.setTransportControlState(state) - self.setTrackProgressControls(state) - self.setDatabaseUpdatingIndicator(state) + self.setTransportControlState(state.playerState) + self.setTrackProgressControls(state.playerState) + self.setDatabaseUpdatingIndicator(state.uiState) } } } diff --git a/Persephone/Resources/Base.lproj/Main.storyboard b/Persephone/Resources/Base.lproj/Main.storyboard index 6922b61..a50c07b 100644 --- a/Persephone/Resources/Base.lproj/Main.storyboard +++ b/Persephone/Resources/Base.lproj/Main.storyboard @@ -26,7 +26,7 @@ - + @@ -90,8 +90,17 @@ + + + + + - + + + + + @@ -122,6 +131,7 @@ + @@ -133,7 +143,7 @@ - + @@ -267,8 +277,8 @@ - - + + diff --git a/Persephone/State/Actions/PlayerActions.swift b/Persephone/State/Actions/PlayerActions.swift index b0249d8..76a12f2 100644 --- a/Persephone/State/Actions/PlayerActions.swift +++ b/Persephone/State/Actions/PlayerActions.swift @@ -24,7 +24,3 @@ struct UpdateElapsedTimeAction: Action { struct UpdateStatusAction: Action { var status: MPDClient.MPDStatus } - -struct StartedDatabaseUpdateAction: Action {} - -struct FinishedDatabaseUpdateAction: Action {} diff --git a/Persephone/State/Actions/UIActions.swift b/Persephone/State/Actions/UIActions.swift new file mode 100644 index 0000000..3e2adeb --- /dev/null +++ b/Persephone/State/Actions/UIActions.swift @@ -0,0 +1,17 @@ +// +// UIActions.swift +// Persephone +// +// Created by Daniel Barber on 2019/5/02. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +struct MainWindowDidOpenAction: Action {} + +struct MainWindowDidCloseAction: Action {} + +struct DatabaseUpdateStartedAction: Action {} + +struct DatabaseUpdateFinishedAction: Action {} diff --git a/Persephone/State/AppState.swift b/Persephone/State/AppState.swift index 27bf287..70fd2b5 100644 --- a/Persephone/State/AppState.swift +++ b/Persephone/State/AppState.swift @@ -14,4 +14,5 @@ struct AppState: StateType { var albumListState = AlbumListState() var preferencesState = PreferencesState() var mpdState = MPDState() + var uiState = UIState() } diff --git a/Persephone/State/PlayerState.swift b/Persephone/State/PlayerState.swift index 0e56a7a..5c9aed1 100644 --- a/Persephone/State/PlayerState.swift +++ b/Persephone/State/PlayerState.swift @@ -18,15 +18,12 @@ struct PlayerState: StateType { var totalTime: UInt? var elapsedTimeMs: UInt? - - var databaseUpdating: Bool = false } extension PlayerState: Equatable { static func == (lhs: PlayerState, rhs: PlayerState) -> Bool { return (lhs.state == rhs.state) && (lhs.totalTime == rhs.totalTime) && - (lhs.elapsedTimeMs == rhs.elapsedTimeMs) && - (lhs.databaseUpdating == rhs.databaseUpdating) + (lhs.elapsedTimeMs == rhs.elapsedTimeMs) } } diff --git a/Persephone/State/Reducers/AppReducer.swift b/Persephone/State/Reducers/AppReducer.swift index 4743748..c66228a 100644 --- a/Persephone/State/Reducers/AppReducer.swift +++ b/Persephone/State/Reducers/AppReducer.swift @@ -14,6 +14,7 @@ func appReducer(action: Action, state: AppState?) -> AppState { queueState: queueReducer(action: action, state: state?.queueState), albumListState: albumListReducer(action: action, state: state?.albumListState), preferencesState: preferencesReducer(action: action, state: state?.preferencesState), - mpdState: mpdReducer(action: action, state: state?.mpdState) + mpdState: mpdReducer(action: action, state: state?.mpdState), + uiState: uiReducer(action: action, state: state?.uiState) ) } diff --git a/Persephone/State/Reducers/PlayerReducer.swift b/Persephone/State/Reducers/PlayerReducer.swift index 1808ac1..d468407 100644 --- a/Persephone/State/Reducers/PlayerReducer.swift +++ b/Persephone/State/Reducers/PlayerReducer.swift @@ -60,12 +60,6 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState { case let action as UpdateElapsedTimeAction: state.elapsedTimeMs = action.elapsedTimeMs - case is StartedDatabaseUpdateAction: - state.databaseUpdating = true - - case is FinishedDatabaseUpdateAction: - state.databaseUpdating = false - default: break } diff --git a/Persephone/State/Reducers/UIReducer.swift b/Persephone/State/Reducers/UIReducer.swift new file mode 100644 index 0000000..8508789 --- /dev/null +++ b/Persephone/State/Reducers/UIReducer.swift @@ -0,0 +1,32 @@ +// +// UIReducer.swift +// Persephone +// +// Created by Daniel Barber on 2019/5/02. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +func uiReducer(action: Action, state: UIState?) -> UIState { + var state = state ?? UIState() + + switch action { + case is MainWindowDidOpenAction: + state.mainWindowOpen = true + + case is MainWindowDidCloseAction: + state.mainWindowOpen = false + + case is DatabaseUpdateStartedAction: + state.databaseUpdating = true + + case is DatabaseUpdateFinishedAction: + state.databaseUpdating = false + + default: + break + } + + return state +} diff --git a/Persephone/State/UIState.swift b/Persephone/State/UIState.swift new file mode 100644 index 0000000..e086696 --- /dev/null +++ b/Persephone/State/UIState.swift @@ -0,0 +1,14 @@ +// +// UIState.swift +// Persephone +// +// Created by Daniel Barber on 2019/5/02. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +struct UIState: StateType { + var mainWindowOpen: Bool = false + var databaseUpdating: Bool = false +}