From 2e6c903d748dd3fcc37585ccdf8d533dd417b9bb Mon Sep 17 00:00:00 2001 From: Daniel Barber Date: Sat, 27 Apr 2019 15:09:24 -0400 Subject: [PATCH] Mostly working, albumlist is not showing album art --- Persephone.xcodeproj/project.pbxproj | 20 ++++- .../Actions/UpdateAlbumListAction.swift | 13 +++ Persephone/Actions/UpdateQueuePosAction.swift | 13 +++ Persephone/AppDelegate.swift | 2 +- .../Controllers/AlbumViewController.swift | 41 ++++------ .../Controllers/NotificationsController.swift | 41 ++++------ .../Controllers/QueueViewController.swift | 81 ++++++------------- Persephone/Controllers/WindowController.swift | 18 +++-- Persephone/DataSources/AlbumDataSource.swift | 57 +++++++------ Persephone/DataSources/QueueDataSource.swift | 45 ++--------- Persephone/Models/Album.swift | 7 ++ Persephone/Models/QueueItem.swift | 8 ++ Persephone/Models/Song.swift | 8 ++ Persephone/Models/TrackTimer.swift | 2 +- Persephone/Reducers/AlbumListReducer.swift | 22 +++++ Persephone/Reducers/AppReducer.swift | 6 +- Persephone/Reducers/QueueReducer.swift | 45 +++++++++++ Persephone/State/AlbumListState.swift | 7 ++ Persephone/State/AppState.swift | 4 +- Persephone/State/PlayerState.swift | 8 ++ Persephone/State/QueueState.swift | 7 ++ 21 files changed, 264 insertions(+), 191 deletions(-) create mode 100644 Persephone/Actions/UpdateAlbumListAction.swift create mode 100644 Persephone/Actions/UpdateQueuePosAction.swift create mode 100644 Persephone/Reducers/AlbumListReducer.swift create mode 100644 Persephone/Reducers/QueueReducer.swift diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 19535fb..2ef5e93 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -88,6 +88,10 @@ E4B11B6F226A5C7A0075461B /* UpdateStatusAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B6E226A5C7A0075461B /* UpdateStatusAction.swift */; }; E4B11B71226A64E60075461B /* UpdateElapsedTimeAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B70226A64E60075461B /* UpdateElapsedTimeAction.swift */; }; E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B72226A6C770075461B /* TrackTimer.swift */; }; + E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B74226CC4D30075461B /* QueueReducer.swift */; }; + E4B11B77226CC6BE0075461B /* UpdateQueuePosAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B76226CC6BE0075461B /* UpdateQueuePosAction.swift */; }; + E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B78226D346B0075461B /* AlbumListReducer.swift */; }; + E4B11B7B226D34F80075461B /* UpdateAlbumListAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B7A226D34F80075461B /* UpdateAlbumListAction.swift */; }; E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; }; E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; }; E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; }; @@ -267,6 +271,10 @@ E4B11B6E226A5C7A0075461B /* UpdateStatusAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateStatusAction.swift; sourceTree = ""; }; E4B11B70226A64E60075461B /* UpdateElapsedTimeAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateElapsedTimeAction.swift; sourceTree = ""; }; E4B11B72226A6C770075461B /* TrackTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackTimer.swift; sourceTree = ""; }; + E4B11B74226CC4D30075461B /* QueueReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueReducer.swift; sourceTree = ""; }; + E4B11B76226CC6BE0075461B /* UpdateQueuePosAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateQueuePosAction.swift; sourceTree = ""; }; + E4B11B78226D346B0075461B /* AlbumListReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListReducer.swift; sourceTree = ""; }; + E4B11B7A226D34F80075461B /* UpdateAlbumListAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateAlbumListAction.swift; sourceTree = ""; }; E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = ""; }; E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = ""; }; @@ -566,6 +574,8 @@ children = ( E4B11B60226A4BFF0075461B /* PlayerReducer.swift */, E4B11B62226A4C510075461B /* AppReducer.swift */, + E4B11B74226CC4D30075461B /* QueueReducer.swift */, + E4B11B78226D346B0075461B /* AlbumListReducer.swift */, ); path = Reducers; sourceTree = ""; @@ -584,9 +594,11 @@ E4B11B6B226A5AF50075461B /* Actions */ = { isa = PBXGroup; children = ( - E4B11B6C226A5B180075461B /* UpdateQueueAction.swift */, - E4B11B6E226A5C7A0075461B /* UpdateStatusAction.swift */, + E4B11B7A226D34F80075461B /* UpdateAlbumListAction.swift */, E4B11B70226A64E60075461B /* UpdateElapsedTimeAction.swift */, + E4B11B6C226A5B180075461B /* UpdateQueueAction.swift */, + E4B11B76226CC6BE0075461B /* UpdateQueuePosAction.swift */, + E4B11B6E226A5C7A0075461B /* UpdateStatusAction.swift */, ); path = Actions; sourceTree = ""; @@ -837,6 +849,7 @@ E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */, E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */, E40FE71B221B904300A4223F /* NSEvent.swift in Sources */, + E4B11B7B226D34F80075461B /* UpdateAlbumListAction.swift in Sources */, E4B11B6D226A5B180075461B /* UpdateQueueAction.swift in Sources */, E4B11B68226A4FA00075461B /* QueueState.swift in Sources */, E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */, @@ -848,6 +861,7 @@ E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */, E41E5310223EF6CE00173814 /* AlbumArtService+Remote.swift in Sources */, E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */, + E4B11B77226CC6BE0075461B /* UpdateQueuePosAction.swift in Sources */, E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */, E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */, E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */, @@ -870,8 +884,10 @@ E419E2872249B96600216A8C /* Song.swift in Sources */, E439109822640213002982E9 /* SongNotifierService.swift in Sources */, E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */, + E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */, E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */, E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */, + E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */, E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */, E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */, E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */, diff --git a/Persephone/Actions/UpdateAlbumListAction.swift b/Persephone/Actions/UpdateAlbumListAction.swift new file mode 100644 index 0000000..2fb7ebb --- /dev/null +++ b/Persephone/Actions/UpdateAlbumListAction.swift @@ -0,0 +1,13 @@ +// +// UpdateAlbumListAction.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/21. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +struct UpdateAlbumListAction: Action { + var albums: [MPDClient.MPDAlbum] +} diff --git a/Persephone/Actions/UpdateQueuePosAction.swift b/Persephone/Actions/UpdateQueuePosAction.swift new file mode 100644 index 0000000..39bc2b8 --- /dev/null +++ b/Persephone/Actions/UpdateQueuePosAction.swift @@ -0,0 +1,13 @@ +// +// UpdateQueuePosAction.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/21. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +struct UpdateQueuePosAction: Action { + var queuePos: Int +} diff --git a/Persephone/AppDelegate.swift b/Persephone/AppDelegate.swift index 5aa5286..267dda2 100644 --- a/Persephone/AppDelegate.swift +++ b/Persephone/AppDelegate.swift @@ -21,7 +21,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate { static let trackTimer = TrackTimer() - static let store = Store(reducer: appReducer, state: nil) + static let store = Store(reducer: appReducer, state: nil) func applicationDidFinishLaunching(_ aNotification: Notification) { connect() diff --git a/Persephone/Controllers/AlbumViewController.swift b/Persephone/Controllers/AlbumViewController.swift index e4bf9b1..0629e0b 100644 --- a/Persephone/Controllers/AlbumViewController.swift +++ b/Persephone/Controllers/AlbumViewController.swift @@ -7,10 +7,14 @@ // import Cocoa +import ReSwift class AlbumViewController: NSViewController, NSCollectionViewDelegate, - NSCollectionViewDelegateFlowLayout { + NSCollectionViewDelegateFlowLayout, + StoreSubscriber { + typealias StoreSubscriberStateType = AlbumListState + var preferences = Preferences() let paddingWidth: CGFloat = 40 @@ -21,22 +25,14 @@ class AlbumViewController: NSViewController, override func viewDidLoad() { super.viewDidLoad() + AppDelegate.store.subscribe(self) { + (subscription: Subscription) -> Subscription in + + subscription.select { state -> AlbumListState in state.albumListState } + } + albumScrollView.postsBoundsChangedNotifications = true - NotificationCenter.default.addObserver( - self, - selector: #selector(updateAlbums(_:)), - name: Notification.loadedAlbums, - object: AppDelegate.mpdClient - ) - - NotificationCenter.default.addObserver( - self, - selector: #selector(clearAlbums(_:)), - name: Notification.willDisconnect, - object: AppDelegate.mpdClient - ) - albumCollectionView.dataSource = dataSource preferences.addObserver(self, forKeyPath: "mpdLibraryDir") @@ -72,24 +68,15 @@ class AlbumViewController: NSViewController, case "mpdLibraryDir": albumCollectionView.reloadData() case "fetchMissingArtworkFromInternet": - dataSource.resetCoverArt() + // dataSource.resetCoverArt() albumCollectionView.reloadData() default: break } } - @objc func updateAlbums(_ notification: Notification) { - guard let albums = notification.userInfo?[Notification.albumsKey] as? [MPDClient.MPDAlbum] - else { return } - - dataSource.albums = albums.map { Album(mpdAlbum: $0) } - albumCollectionView.reloadData() - } - - @objc func clearAlbums(_ notification: Notification) { - dataSource.albums = [] - + func newState(state: StoreSubscriberStateType) { + print("New album list state") albumCollectionView.reloadData() } diff --git a/Persephone/Controllers/NotificationsController.swift b/Persephone/Controllers/NotificationsController.swift index ed53617..7362cf8 100644 --- a/Persephone/Controllers/NotificationsController.swift +++ b/Persephone/Controllers/NotificationsController.swift @@ -16,26 +16,16 @@ class NotificationsController: MPDClientDelegate { } func willDisconnect(mpdClient: MPDClient) { + DispatchQueue.main.async { + AppDelegate.store.dispatch(UpdateAlbumListAction(albums: [])) + } sendNotification(name: Notification.willDisconnect) } func didUpdateStatus(mpdClient: MPDClient, status: MPDClient.MPDStatus) { - AppDelegate.store.dispatch(UpdateStatusAction(status: status)) - sendNotification( - name: Notification.stateChanged, - userInfo: [Notification.stateKey: status.state] - ) - sendNotification( - name: Notification.timeChanged, - userInfo: [ - Notification.totalTimeKey: status.totalTime, - Notification.elapsedTimeMsKey: status.elapsedTimeMs - ] - ) - } - - func didUpdateTime(mpdClient: MPDClient, total: UInt, elapsedMs: UInt) { - + DispatchQueue.main.async { + AppDelegate.store.dispatch(UpdateStatusAction(status: status)) + } } func willStartDatabaseUpdate(mpdClient: MPDClient) { @@ -47,21 +37,22 @@ class NotificationsController: MPDClientDelegate { } func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.MPDSong]) { - //AppDelegate.store.dispatch(UpdateQueueAction(queue: queue)) - sendNotification( - name: Notification.queueChanged, - userInfo: [Notification.queueKey: queue] - ) + DispatchQueue.main.async { + AppDelegate.store.dispatch(UpdateQueueAction(queue: queue)) + } } func didUpdateQueuePos(mpdClient: MPDClient, song: Int) { - sendNotification( - name: Notification.queuePosChanged, - userInfo: [Notification.queuePosKey: song] - ) + DispatchQueue.main.async { + AppDelegate.store.dispatch(UpdateQueuePosAction(queuePos: song)) + } } func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum]) { + print("Albums") + DispatchQueue.main.async { + AppDelegate.store.dispatch(UpdateAlbumListAction(albums: albums)) + } sendNotification( name: Notification.loadedAlbums, userInfo: [Notification.albumsKey: albums] diff --git a/Persephone/Controllers/QueueViewController.swift b/Persephone/Controllers/QueueViewController.swift index 586be21..4197f31 100644 --- a/Persephone/Controllers/QueueViewController.swift +++ b/Persephone/Controllers/QueueViewController.swift @@ -7,10 +7,13 @@ // import Cocoa -import PromiseKit +import ReSwift class QueueViewController: NSViewController, - NSOutlineViewDelegate { + NSOutlineViewDelegate, + StoreSubscriber { + typealias StoreSubscriberStateType = QueueState + var dataSource = QueueDataSource() @IBOutlet var queueView: NSOutlineView! @@ -19,11 +22,22 @@ class QueueViewController: NSViewController, override func viewDidLoad() { super.viewDidLoad() - setupNotificationObservers() + AppDelegate.store.subscribe(self) { + (subscription: Subscription) -> Subscription in + + subscription.select { state in state.queueState } + } queueView.dataSource = dataSource queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle - } + } + + func newState(state: StoreSubscriberStateType) { + print("New queue state") + dataSource.setQueueIcon() + queueView.reloadData() + updateAlbumArt(state) + } override func keyDown(with event: NSEvent) { switch event.keyCode { @@ -42,15 +56,8 @@ class QueueViewController: NSViewController, } } - @objc func stateChanged(_ notification: Notification) { - guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.MPDStatus.State - else { return } - - dataSource.setQueueIcon(state) - } - - func notifyTrack() { - guard let currentSong = dataSource.currentSong, + func notifyTrack(_ state: QueueState) { + guard let currentSong = state.currentSong, let status = AppDelegate.mpdClient.status, status.state == .playing else { return } @@ -59,25 +66,10 @@ class QueueViewController: NSViewController, .deliver() } - @objc func queueChanged(_ notification: Notification) { - guard let queue = notification.userInfo?[Notification.queueKey] as? [MPDClient.MPDSong] - else { return } - - dataSource.updateQueue(queue) - queueView.reloadData() - } - - @objc func queuePosChanged(_ notification: Notification) { - guard let queuePos = notification.userInfo?[Notification.queuePosKey] as? Int - else { return } - - dataSource.setQueuePos(queuePos) - queueView.reloadData() - updateAlbumArt() - } - - func updateAlbumArt() { - if let playingQueueItem = dataSource.queue.first(where: { $0.isPlaying }) { + func updateAlbumArt(_ state: QueueState) { + if let playingQueueItem = state.queue.first( + where: { $0.isPlaying } + ) { let albumArtService = AlbumArtService(song: playingQueueItem.song) albumArtService.fetchBigAlbumArt() @@ -88,7 +80,7 @@ class QueueViewController: NSViewController, self.queueAlbumArtImage.image = NSImage.defaultCoverArt } - self.notifyTrack() + self.notifyTrack(state) } .cauterize() } else { @@ -168,27 +160,4 @@ func cellForSongTitle(_ outlineView: NSOutlineView, with queueItem: QueueItem) - return cellView } - - func setupNotificationObservers() { - NotificationCenter.default.addObserver( - self, - selector: #selector(stateChanged(_:)), - name: Notification.stateChanged, - object: AppDelegate.mpdClient - ) - - NotificationCenter.default.addObserver( - self, - selector: #selector(queueChanged(_:)), - name: Notification.queueChanged, - object: AppDelegate.mpdClient - ) - - NotificationCenter.default.addObserver( - self, - selector: #selector(queuePosChanged(_:)), - name: Notification.queuePosChanged, - object: AppDelegate.mpdClient - ) - } } diff --git a/Persephone/Controllers/WindowController.swift b/Persephone/Controllers/WindowController.swift index 88412c0..80f78e8 100644 --- a/Persephone/Controllers/WindowController.swift +++ b/Persephone/Controllers/WindowController.swift @@ -10,7 +10,7 @@ import Cocoa import ReSwift class WindowController: NSWindowController, StoreSubscriber { - typealias StoreSubscriberStateType = AppState + typealias StoreSubscriberStateType = PlayerState enum TransportAction: Int { case prevTrack, playPause, stop, nextTrack @@ -23,8 +23,11 @@ class WindowController: NSWindowController, StoreSubscriber { super.windowDidLoad() window?.titleVisibility = .hidden - // TODO: We will want to filter this subscribe later - AppDelegate.store.subscribe(self) + AppDelegate.store.subscribe(self) { + (subscription: Subscription) -> Subscription in + + subscription.select { state in state.playerState } + } NotificationCenter.default.addObserver( self, @@ -44,12 +47,13 @@ class WindowController: NSWindowController, StoreSubscriber { trackRemaining.font = .timerFont } - func newState(state: WindowController.StoreSubscriberStateType) { - self.state = state.playerState.state + func newState(state: StoreSubscriberStateType) { + print("New player state") + self.state = state.state DispatchQueue.main.async { - self.setTransportControlState(state.playerState) - self.setTrackProgressControls(state.playerState) + self.setTransportControlState(state) + self.setTrackProgressControls(state) } } diff --git a/Persephone/DataSources/AlbumDataSource.swift b/Persephone/DataSources/AlbumDataSource.swift index 88129e2..0aee886 100644 --- a/Persephone/DataSources/AlbumDataSource.swift +++ b/Persephone/DataSources/AlbumDataSource.swift @@ -10,45 +10,44 @@ import Cocoa import PromiseKit class AlbumDataSource: NSObject, NSCollectionViewDataSource { - var albums: [Album] = [] - func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { - return albums.count - } - - func resetCoverArt() { - albums = albums.map { - var album = $0 - album.coverArtFetched = false - return album - } + return AppDelegate.store.state.albumListState.albums.count } +// +// func resetCoverArt() { +// albums = AppDelegate.store.state.albumListState.albums.map { +// var album = $0 +// album.coverArtFetched = false +// return album +// } +// } func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { + let albums = AppDelegate.store.state.albumListState.albums let item = collectionView.makeItem(withIdentifier: .albumViewItem, for: indexPath) guard let albumViewItem = item as? AlbumViewItem else { return item } albumViewItem.view.wantsLayer = true albumViewItem.setAlbum(albums[indexPath.item]) - if albums[indexPath.item].coverArt == nil && - !albums[indexPath.item].coverArtFetched { - - AppDelegate.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) { - guard let song = $0 else { return } - - AlbumArtService(song: Song(mpdSong: song)) - .fetchAlbumArt() - .done { image in - self.albums[indexPath.item].coverArt = image - self.albums[indexPath.item].coverArtFetched = true - - DispatchQueue.main.async { - collectionView.reloadItems(at: [indexPath]) - } - } - } - } +// if albums[indexPath.item].coverArt == nil && +// !albums[indexPath.item].coverArtFetched { +// +// AppDelegate.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) { +// guard let song = $0 else { return } +// +// AlbumArtService(song: Song(mpdSong: song)) +// .fetchAlbumArt() +// .done { image in +// self.albums[indexPath.item].coverArt = image +// self.albums[indexPath.item].coverArtFetched = true +// +// DispatchQueue.main.async { +// collectionView.reloadItems(at: [indexPath]) +// } +// } +// } +// } return albumViewItem } diff --git a/Persephone/DataSources/QueueDataSource.swift b/Persephone/DataSources/QueueDataSource.swift index 0c4ed11..44d3b48 100644 --- a/Persephone/DataSources/QueueDataSource.swift +++ b/Persephone/DataSources/QueueDataSource.swift @@ -9,46 +9,13 @@ import Cocoa class QueueDataSource: NSObject, NSOutlineViewDataSource { - var queue: [QueueItem] = [] - var queuePos: Int = -1 - var currentSong: Song? - var queueIcon: NSImage? = nil - func updateQueue(_ queue: [MPDClient.MPDSong]) { - queuePos = -1 - - self.queue = queue.enumerated().map { index, mpdSong in - let song = Song(mpdSong: mpdSong) - return QueueItem( - song: song, - queuePos: index, - isPlaying: index == queuePos - ) - } - } - - func setQueuePos(_ queuePos: Int) { - let oldSongRowPos = self.queuePos - let newSongRowPos = queuePos - self.queuePos = queuePos - - if oldSongRowPos >= 0 { - queue[oldSongRowPos].isPlaying = false - } - if newSongRowPos >= 0 { - queue[newSongRowPos].isPlaying = true - currentSong = queue[newSongRowPos].song - } else { - currentSong = nil - } - } - - func setQueueIcon(_ state: MPDClient.MPDStatus.State) { - switch state { - case .playing: + func setQueueIcon() { + switch AppDelegate.store.state.playerState.state { + case .playing?: queueIcon = .playIcon - case .paused: + case .paused?: queueIcon = .pauseIcon default: queueIcon = nil @@ -56,7 +23,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { } func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { - return queue.count + 1 + return AppDelegate.store.state.queueState.queue.count + 1 } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { @@ -65,7 +32,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource { func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { if index > 0 { - return queue[index - 1] + return AppDelegate.store.state.queueState.queue[index - 1] } else { return false } diff --git a/Persephone/Models/Album.swift b/Persephone/Models/Album.swift index 881a2bc..b82d95c 100644 --- a/Persephone/Models/Album.swift +++ b/Persephone/Models/Album.swift @@ -30,3 +30,10 @@ struct Album { return "\(title) - \(artist)".sha1() } } + +extension Album: Equatable { + static func == (lhs: Album, rhs: Album) -> Bool { + return (lhs.artist == rhs.artist) && + (lhs.title == rhs.title) + } +} diff --git a/Persephone/Models/QueueItem.swift b/Persephone/Models/QueueItem.swift index a7b6932..9ffdf63 100644 --- a/Persephone/Models/QueueItem.swift +++ b/Persephone/Models/QueueItem.swift @@ -13,3 +13,11 @@ struct QueueItem { var queuePos: Int var isPlaying: Bool } + +extension QueueItem: Equatable { + static func == (lhs: QueueItem, rhs: QueueItem) -> Bool { + return (lhs.song == rhs.song) && + (lhs.queuePos == rhs.queuePos) && + (lhs.isPlaying == rhs.isPlaying) + } +} diff --git a/Persephone/Models/Song.swift b/Persephone/Models/Song.swift index b3327a7..867c027 100644 --- a/Persephone/Models/Song.swift +++ b/Persephone/Models/Song.swift @@ -23,3 +23,11 @@ struct Song { return Album(mpdAlbum: mpdSong.album) } } + +extension Song: Equatable { + static func == (lhs: Song, rhs: Song) -> Bool { + return (lhs.title == rhs.title) && + (lhs.artist == rhs.artist) && + (lhs.album == rhs.album) + } +} diff --git a/Persephone/Models/TrackTimer.swift b/Persephone/Models/TrackTimer.swift index fc696e7..574ab15 100644 --- a/Persephone/Models/TrackTimer.swift +++ b/Persephone/Models/TrackTimer.swift @@ -21,7 +21,7 @@ class TrackTimer: NSObject { startTime = CACurrentMediaTime() startElapsed = Double(elapsedTimeMs) / 1000 - DispatchQueue.main.sync { + DispatchQueue.main.async { self.timer = Timer.scheduledTimer( withTimeInterval: 0.25, repeats: true diff --git a/Persephone/Reducers/AlbumListReducer.swift b/Persephone/Reducers/AlbumListReducer.swift new file mode 100644 index 0000000..92ddcd1 --- /dev/null +++ b/Persephone/Reducers/AlbumListReducer.swift @@ -0,0 +1,22 @@ +// +// AlbumListReducer.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/21. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +func albumListReducer(action: Action, state: AlbumListState?) -> AlbumListState { + var state = state ?? AlbumListState() + + switch action { + case let action as UpdateAlbumListAction: + state.albums = action.albums.map { Album(mpdAlbum: $0) } + default: + break + } + + return state +} diff --git a/Persephone/Reducers/AppReducer.swift b/Persephone/Reducers/AppReducer.swift index f0d72cf..bce611a 100644 --- a/Persephone/Reducers/AppReducer.swift +++ b/Persephone/Reducers/AppReducer.swift @@ -6,11 +6,13 @@ // Copyright © 2019 Dan Barber. All rights reserved. // -import Foundation import ReSwift func appReducer(action: Action, state: AppState?) -> AppState { + print(action) return AppState( - playerState: playerReducer(action: action, state: state?.playerState) + playerState: playerReducer(action: action, state: state?.playerState), + queueState: queueReducer(action: action, state: state?.queueState), + albumListState: albumListReducer(action: action, state: state?.albumListState) ) } diff --git a/Persephone/Reducers/QueueReducer.swift b/Persephone/Reducers/QueueReducer.swift new file mode 100644 index 0000000..1326ddc --- /dev/null +++ b/Persephone/Reducers/QueueReducer.swift @@ -0,0 +1,45 @@ +// +// QueueReducer.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/21. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +func queueReducer(action: Action, state: QueueState?) -> QueueState { + var state = state ?? QueueState() + + switch action { + case let action as UpdateQueueAction: + state.queuePos = -1 + + state.queue = action.queue.enumerated().map { index, mpdSong in + let song = Song(mpdSong: mpdSong) + + return QueueItem( + song: song, + queuePos: index, + isPlaying: index == state.queuePos + ) + } + + case let action as UpdateQueuePosAction: + let oldSongRowPos = state.queuePos + let newSongRowPos = action.queuePos + state.queuePos = action.queuePos + + if oldSongRowPos >= 0 { + state.queue[oldSongRowPos].isPlaying = false + } + if newSongRowPos >= 0 { + state.queue[newSongRowPos].isPlaying = true + } + + default: + break + } + + return state +} diff --git a/Persephone/State/AlbumListState.swift b/Persephone/State/AlbumListState.swift index 56f38e7..a38af70 100644 --- a/Persephone/State/AlbumListState.swift +++ b/Persephone/State/AlbumListState.swift @@ -10,4 +10,11 @@ import ReSwift struct AlbumListState: StateType { var albums: [Album] = [] + var albumsWithUpdates: [Int] +} + +extension AlbumListState: Equatable { + static func == (lhs: AlbumListState, rhs: AlbumListState) -> Bool { + return lhs.albums == rhs.albums + } } diff --git a/Persephone/State/AppState.swift b/Persephone/State/AppState.swift index 56f38a2..217b095 100644 --- a/Persephone/State/AppState.swift +++ b/Persephone/State/AppState.swift @@ -10,6 +10,6 @@ import ReSwift struct AppState: StateType { var playerState = PlayerState() -// var queueState = QueueState() -// var albumListState = AlbumListState() + var queueState = QueueState() + var albumListState = AlbumListState() } diff --git a/Persephone/State/PlayerState.swift b/Persephone/State/PlayerState.swift index 30a6d6e..6a32e2f 100644 --- a/Persephone/State/PlayerState.swift +++ b/Persephone/State/PlayerState.swift @@ -17,3 +17,11 @@ struct PlayerState: StateType { var totalTime: UInt? var elapsedTimeMs: UInt? } + +extension PlayerState: Equatable { + static func == (lhs: PlayerState, rhs: PlayerState) -> Bool { + return (lhs.state == rhs.state) && + (lhs.totalTime == rhs.totalTime) && + (lhs.elapsedTimeMs == rhs.elapsedTimeMs) + } +} diff --git a/Persephone/State/QueueState.swift b/Persephone/State/QueueState.swift index af692f8..ef6acb6 100644 --- a/Persephone/State/QueueState.swift +++ b/Persephone/State/QueueState.swift @@ -12,3 +12,10 @@ struct QueueState: StateType { var queue: [QueueItem] = [] var queuePos: Int = -1 } + +extension QueueState: Equatable { + static func == (lhs: QueueState, rhs: QueueState) -> Bool { + return (lhs.queue == rhs.queue) && + (lhs.queuePos == rhs.queuePos) + } +}