diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 541758e..dcd1e5c 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -100,6 +100,10 @@ E4B11BB02274F71A0075461B /* EnumEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BAF2274F71A0075461B /* EnumEquatable.swift */; }; E4B11BB22274F9520075461B /* ResetAlbumListArt.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */; }; E4B11BB42275002D0075461B /* UpdateMPDDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */; }; + E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB52275374B0075461B /* UserNotificationsController.swift */; }; + E4B11BB8227538FA0075461B /* CurrentArtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB7227538FA0075461B /* CurrentArtView.swift */; }; + E4B11BBA22753BF10075461B /* UpdateCurrentSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB922753BF10075461B /* UpdateCurrentSong.swift */; }; + E4B11BBC227541C40075461B /* UpdateCurrentArtwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBB227541C40075461B /* UpdateCurrentArtwork.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 */; }; @@ -291,6 +295,10 @@ E4B11BAF2274F71A0075461B /* EnumEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumEquatable.swift; sourceTree = ""; }; E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetAlbumListArt.swift; sourceTree = ""; }; E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMPDDatabase.swift; sourceTree = ""; }; + E4B11BB52275374B0075461B /* UserNotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationsController.swift; sourceTree = ""; }; + E4B11BB7227538FA0075461B /* CurrentArtView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentArtView.swift; sourceTree = ""; }; + E4B11BB922753BF10075461B /* UpdateCurrentSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCurrentSong.swift; sourceTree = ""; }; + E4B11BBB227541C40075461B /* UpdateCurrentArtwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCurrentArtwork.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 = ""; }; @@ -444,6 +452,7 @@ children = ( E47E2FD62220720300F747E6 /* AlbumItemView.swift */, E47E2FD222205D2500F747E6 /* MainWindow.swift */, + E4B11BB7227538FA0075461B /* CurrentArtView.swift */, ); path = Views; sourceTree = ""; @@ -622,6 +631,8 @@ E4B11BAC2274F2E80075461B /* UpdateAlbumArt.swift */, E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */, E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */, + E4B11BB922753BF10075461B /* UpdateCurrentSong.swift */, + E4B11BBB227541C40075461B /* UpdateCurrentArtwork.swift */, ); path = Actions; sourceTree = ""; @@ -664,6 +675,7 @@ E4E8CC932206097F0024217A /* NotificationsController.swift */, E4E8CC912204F4B80024217A /* QueueViewController.swift */, E465049921E94DF500A70F4C /* WindowController.swift */, + E4B11BB52275374B0075461B /* UserNotificationsController.swift */, ); path = Controllers; sourceTree = ""; @@ -883,6 +895,7 @@ E4B11BA92274EDE30075461B /* Loading.swift in Sources */, E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */, E40FE71B221B904300A4223F /* NSEvent.swift in Sources */, + E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */, E4B11B7B226D34F80075461B /* UpdateAlbumListAction.swift in Sources */, E4B11B6D226A5B180075461B /* UpdateQueueAction.swift in Sources */, E4B11B68226A4FA00075461B /* QueueState.swift in Sources */, @@ -913,6 +926,7 @@ E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */, E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */, E465049A21E94DF500A70F4C /* WindowController.swift in Sources */, + E4B11BBA22753BF10075461B /* UpdateCurrentSong.swift in Sources */, E4B11B71226A64E60075461B /* UpdateElapsedTimeAction.swift in Sources */, E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */, E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */, @@ -929,6 +943,7 @@ E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */, E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */, E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */, + E4B11BB8227538FA0075461B /* CurrentArtView.swift in Sources */, E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */, E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */, E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */, @@ -948,6 +963,7 @@ E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */, E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */, E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */, + E4B11BBC227541C40075461B /* UpdateCurrentArtwork.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Persephone/Actions/UpdateCurrentArtwork.swift b/Persephone/Actions/UpdateCurrentArtwork.swift new file mode 100644 index 0000000..812f610 --- /dev/null +++ b/Persephone/Actions/UpdateCurrentArtwork.swift @@ -0,0 +1,14 @@ +// +// UpdateCurrentArtwork.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/27. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import Cocoa +import ReSwift + +struct UpdateCurrentArtwork: Action { + var coverArt: NSImage? +} diff --git a/Persephone/Actions/UpdateCurrentSong.swift b/Persephone/Actions/UpdateCurrentSong.swift new file mode 100644 index 0000000..3d83e10 --- /dev/null +++ b/Persephone/Actions/UpdateCurrentSong.swift @@ -0,0 +1,13 @@ +// +// UpdateCurrentSong.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/27. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import ReSwift + +struct UpdateCurrentSong: Action { + var currentSong: Song +} diff --git a/Persephone/AppDelegate.swift b/Persephone/AppDelegate.swift index 01ba4a6..9144006 100644 --- a/Persephone/AppDelegate.swift +++ b/Persephone/AppDelegate.swift @@ -16,6 +16,7 @@ class AppDelegate: NSObject, MediaKeyTapDelegate { var preferences = Preferences() var mediaKeyTap: MediaKeyTap? + var userNotificationsController: UserNotificationsController? static let mpdClient = MPDClient( withDelegate: NotificationsController() @@ -35,10 +36,10 @@ class AppDelegate: NSObject, mediaKeyTap?.start() AppDelegate.store.subscribe(self) { - (subscription: Subscription) -> Subscription in - - subscription.select { state in state.playerState } + $0.select { $0.playerState } } + + userNotificationsController = UserNotificationsController() } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/Persephone/Controllers/AlbumViewController.swift b/Persephone/Controllers/AlbumViewController.swift index b339495..d974b19 100644 --- a/Persephone/Controllers/AlbumViewController.swift +++ b/Persephone/Controllers/AlbumViewController.swift @@ -24,9 +24,7 @@ class AlbumViewController: NSViewController, super.viewDidLoad() AppDelegate.store.subscribe(self) { - (subscription: Subscription) -> Subscription in - - subscription.select { state -> AlbumListState in state.albumListState } + $0.select { $0.albumListState } } albumScrollView.postsBoundsChangedNotifications = true diff --git a/Persephone/Controllers/AlbumViewItem.swift b/Persephone/Controllers/AlbumViewItem.swift index 30b7609..a6ac9ce 100644 --- a/Persephone/Controllers/AlbumViewItem.swift +++ b/Persephone/Controllers/AlbumViewItem.swift @@ -34,7 +34,7 @@ class AlbumViewItem: NSCollectionViewItem { switch album.coverArt { case .loaded(let coverArt): - albumCoverView.image = coverArt + albumCoverView.image = coverArt ?? .defaultCoverArt default: albumCoverView.image = .defaultCoverArt } diff --git a/Persephone/Controllers/QueueViewController.swift b/Persephone/Controllers/QueueViewController.swift index ba05f21..45c3920 100644 --- a/Persephone/Controllers/QueueViewController.swift +++ b/Persephone/Controllers/QueueViewController.swift @@ -20,9 +20,7 @@ class QueueViewController: NSViewController, super.viewDidLoad() AppDelegate.store.subscribe(self) { - (subscription: Subscription) -> Subscription in - - subscription.select { state in state.queueState } + $0.select { $0.queueState } } queueView.dataSource = dataSource @@ -52,38 +50,6 @@ class QueueViewController: NSViewController, } } - func notifyTrack(_ state: QueueState) { - guard let currentSong = state.currentSong, - let status = AppDelegate.mpdClient.status, - status.state == .playing - else { return } - - SongNotifierService(song: currentSong, image: queueAlbumArtImage.image) - .deliver() - } - - func updateAlbumArt(_ state: QueueState) { - if let playingQueueItem = state.queue.first( - where: { $0.isPlaying } - ) { - let albumArtService = AlbumArtService(song: playingQueueItem.song) - - albumArtService.fetchBigAlbumArt() - .done() { - if let image = $0 { - self.queueAlbumArtImage.image = image - } else { - self.queueAlbumArtImage.image = NSImage.defaultCoverArt - } - - self.notifyTrack(state) - } - .cauterize() - } else { - queueAlbumArtImage.image = NSImage.defaultCoverArt - } - } - func outlineView( _ outlineView: NSOutlineView, selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet @@ -164,6 +130,5 @@ extension QueueViewController: StoreSubscriber { func newState(state: StoreSubscriberStateType) { dataSource.setQueueIcon() queueView.reloadData() - updateAlbumArt(state) } } diff --git a/Persephone/Controllers/UserNotificationsController.swift b/Persephone/Controllers/UserNotificationsController.swift new file mode 100644 index 0000000..10ac9f8 --- /dev/null +++ b/Persephone/Controllers/UserNotificationsController.swift @@ -0,0 +1,42 @@ +// +// UserNotificationsController.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/27. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import Foundation +import ReSwift + +class UserNotificationsController { + init() { + AppDelegate.store.subscribe(self) { + $0.select { $0.playerState.currentSong } + } + } + + func notifyTrack(_ state: Song?) { + guard let currentSong = state, + let status = AppDelegate.mpdClient.status, + status.state == .playing + else { return } + + let albumArtService = AlbumArtService(song: currentSong) + + albumArtService.fetchBigAlbumArt() + .done() { + SongNotifierService(song: currentSong, image: $0) + .deliver() + } + .cauterize() + } +} + +extension UserNotificationsController: StoreSubscriber { + typealias StoreSubscriberStateType = Song? + + func newState(state: Song?) { + notifyTrack(state) + } +} diff --git a/Persephone/Controllers/WindowController.swift b/Persephone/Controllers/WindowController.swift index 280cd98..a7234c3 100644 --- a/Persephone/Controllers/WindowController.swift +++ b/Persephone/Controllers/WindowController.swift @@ -22,9 +22,7 @@ class WindowController: NSWindowController { window?.titleVisibility = .hidden AppDelegate.store.subscribe(self) { - (subscription: Subscription) -> Subscription in - - subscription.select { state in state.playerState } + $0.select { $0.playerState } } trackProgress.font = .timerFont diff --git a/Persephone/Info.plist b/Persephone/Info.plist index 5302812..c2909c0 100644 --- a/Persephone/Info.plist +++ b/Persephone/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.11.2-alpha + 0.12.0-pre-alpha CFBundleVersion 1 LSApplicationCategoryType diff --git a/Persephone/Reducers/PlayerReducer.swift b/Persephone/Reducers/PlayerReducer.swift index 1f0b4da..a4b05d1 100644 --- a/Persephone/Reducers/PlayerReducer.swift +++ b/Persephone/Reducers/PlayerReducer.swift @@ -25,6 +25,32 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState { AppDelegate.trackTimer.stop(elapsedTimeMs: state.elapsedTimeMs) } + case let action as UpdateCurrentSong: + state.currentSong = action.currentSong + + if let currentSong = state.currentSong { + let albumArtService = AlbumArtService(song: currentSong) + + albumArtService.fetchBigAlbumArt() + .done() { image in + DispatchQueue.main.async { + if let image = image { + AppDelegate.store.dispatch(UpdateCurrentArtwork(coverArt: image)) + } else { + AppDelegate.store.dispatch(UpdateCurrentArtwork(coverArt: .defaultCoverArt)) + } + } + } + .cauterize() + } else { + DispatchQueue.main.async { + AppDelegate.store.dispatch(UpdateCurrentArtwork(coverArt: .defaultCoverArt)) + } + } + + case let action as UpdateCurrentArtwork: + state.currentArtwork = action.coverArt + case let action as UpdateElapsedTimeAction: state.elapsedTimeMs = action.elapsedTimeMs @@ -40,13 +66,3 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState { return state } - -func updateElapsedTime(_ timer: Timer) { - guard let userInfo = timer.userInfo as? Dictionary, - let elapsedTimeMs = userInfo["elapsedTimeMs"] as? UInt - else { return } - - AppDelegate.store.dispatch( - UpdateElapsedTimeAction(elapsedTimeMs: elapsedTimeMs) - ) -} diff --git a/Persephone/Reducers/QueueReducer.swift b/Persephone/Reducers/QueueReducer.swift index 1326ddc..f29ffc3 100644 --- a/Persephone/Reducers/QueueReducer.swift +++ b/Persephone/Reducers/QueueReducer.swift @@ -6,6 +6,7 @@ // Copyright © 2019 Dan Barber. All rights reserved. // +import Cocoa import ReSwift func queueReducer(action: Action, state: QueueState?) -> QueueState { @@ -37,6 +38,12 @@ func queueReducer(action: Action, state: QueueState?) -> QueueState { state.queue[newSongRowPos].isPlaying = true } + DispatchQueue.main.async { + AppDelegate.store.dispatch( + UpdateCurrentSong(currentSong: state.queue[newSongRowPos].song) + ) + } + default: break } diff --git a/Persephone/Resources/Base.lproj/Main.storyboard b/Persephone/Resources/Base.lproj/Main.storyboard index 073710a..c3c3e8f 100644 --- a/Persephone/Resources/Base.lproj/Main.storyboard +++ b/Persephone/Resources/Base.lproj/Main.storyboard @@ -635,7 +635,7 @@ - + diff --git a/Persephone/State/PlayerState.swift b/Persephone/State/PlayerState.swift index de5bdc2..6c7397f 100644 --- a/Persephone/State/PlayerState.swift +++ b/Persephone/State/PlayerState.swift @@ -11,6 +11,8 @@ import ReSwift struct PlayerState: StateType { var status: MPDClient.MPDStatus? + var currentSong: Song? + var currentArtwork: NSImage? var state: MPDClient.MPDStatus.State? diff --git a/Persephone/Views/CurrentArtView.swift b/Persephone/Views/CurrentArtView.swift new file mode 100644 index 0000000..9e6ffd5 --- /dev/null +++ b/Persephone/Views/CurrentArtView.swift @@ -0,0 +1,32 @@ +// +// CurrentArtView.swift +// Persephone +// +// Created by Daniel Barber on 2019/4/27. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import Cocoa +import ReSwift + +class CurrentArtView: NSImageView { + required init?(coder: NSCoder) { + super.init(coder: coder) + + AppDelegate.store.subscribe(self) { + $0.select { $0.playerState.currentArtwork } + } + } +} + +extension CurrentArtView: StoreSubscriber { + typealias StoreSubscriberStateType = NSImage? + + func newState(state: NSImage?) { + if let coverArt = state { + image = coverArt + } else { + image = .defaultCoverArt + } + } +}