1
1
mirror of https://github.com/danbee/persephone synced 2025-03-04 08:39:11 +00:00

Compare commits

...

15 Commits

70 changed files with 883 additions and 397 deletions

View File

@ -35,12 +35,17 @@
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; }; E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongTitleView.swift */; }; E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongTitleView.swift */; };
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42410B52241B956005ED6DF /* MPDClient+Database.swift */; }; E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42410B52241B956005ED6DF /* MPDClient+Database.swift */; };
E42A4D4F22E20D7D001C6CAD /* MPDTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */; };
E42A4D5122E2167E001C6CAD /* MPDClient+Songs.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */; };
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3922176D6400A13ED9 /* LICENSE.md */; }; E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3922176D6400A13ED9 /* LICENSE.md */; };
E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; }; E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; };
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; }; E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; }; E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; };
E439109822640213002982E9 /* SongNotifierService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E439109722640213002982E9 /* SongNotifierService.swift */; }; E439109822640213002982E9 /* SongNotifierService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E439109722640213002982E9 /* SongNotifierService.swift */; };
E43AC1F122C68E6A001E483C /* NSPasteboardItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43AC1F022C68E6A001E483C /* NSPasteboardItem.swift */; }; E43AC1F122C68E6A001E483C /* NSPasteboardItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43AC1F022C68E6A001E483C /* NSPasteboardItem.swift */; };
E43AC1F522C6A4F4001E483C /* DraggedAlbum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43AC1F422C6A4F4001E483C /* DraggedAlbum.swift */; };
E43AC1F622C6AD0B001E483C /* AlbumViewController+NSCollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43AC1F222C6A439001E483C /* AlbumViewController+NSCollectionViewDelegate.swift */; };
E43AC1F822C7065A001E483C /* AlbumCoverButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43AC1F722C7065A001E483C /* AlbumCoverButton.swift */; };
E43B67AA22909793007DCF55 /* AlbumDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67A822909793007DCF55 /* AlbumDetailView.swift */; }; E43B67AA22909793007DCF55 /* AlbumDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67A822909793007DCF55 /* AlbumDetailView.swift */; };
E43B67AB22909793007DCF55 /* AlbumDetailView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E43B67A922909793007DCF55 /* AlbumDetailView.xib */; }; E43B67AB22909793007DCF55 /* AlbumDetailView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E43B67A922909793007DCF55 /* AlbumDetailView.xib */; };
E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */; }; E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */; };
@ -49,6 +54,7 @@
E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519B227BAF2E0090CD6F /* UIActions.swift */; }; E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519B227BAF2E0090CD6F /* UIActions.swift */; };
E440519E227BB0720090CD6F /* UIReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519D227BB0720090CD6F /* UIReducer.swift */; }; E440519E227BB0720090CD6F /* UIReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519D227BB0720090CD6F /* UIReducer.swift */; };
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519F227BB0AB0090CD6F /* UIState.swift */; }; E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519F227BB0AB0090CD6F /* UIState.swift */; };
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E442CCCC2347E73C00004E0C /* Artist.swift */; };
E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; }; E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; };
E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* CoverArtQueue.swift */; }; E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* CoverArtQueue.swift */; };
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */; }; E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */; };
@ -62,13 +68,13 @@
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */; }; E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */; };
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD222205D2500F747E6 /* MainWindow.swift */; }; E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD222205D2500F747E6 /* MainWindow.swift */; };
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */; }; E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */; };
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD62220720300F747E6 /* AlbumItemView.swift */; };
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FDC2220A6D100F747E6 /* Time.swift */; }; E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FDC2220A6D100F747E6 /* Time.swift */; };
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; }; E47E2FE52220AA0700F747E6 /* FlexibleGridViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* FlexibleGridViewLayout.swift */; };
E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39822B85D0400CA8CBD /* NSPasteboard.swift */; }; E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39822B85D0400CA8CBD /* NSPasteboard.swift */; };
E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39C22B9CF0000CA8CBD /* NSView.swift */; }; E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39C22B9CF0000CA8CBD /* NSView.swift */; };
E489E3A422B9D31800CA8CBD /* DraggedSongView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */; }; E489E3A422B9D31800CA8CBD /* DraggedSongView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */; };
E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */; }; E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */; };
E48E92D7235113DF00A5E1BB /* Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = E48E92D6235113DF00A5E1BB /* Metadata.swift */; };
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; }; E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
E49A5482233E580800EED353 /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5481233E580800EED353 /* PromiseKit */; }; E49A5482233E580800EED353 /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5481233E580800EED353 /* PromiseKit */; };
E49A5485233E5ADC00EED353 /* Differ in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5484233E5ADC00EED353 /* Differ */; }; E49A5485233E5ADC00EED353 /* Differ in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5484233E5ADC00EED353 /* Differ */; };
@ -98,6 +104,7 @@
E4B11BC02275EE150075461B /* QueueActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBF2275EE150075461B /* QueueActions.swift */; }; E4B11BC02275EE150075461B /* QueueActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBF2275EE150075461B /* QueueActions.swift */; };
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; }; E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; };
E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */; }; E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */; };
E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BBD2F223357C0700702C16 /* ArtistListState.swift */; };
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; }; E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; }; E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */; }; E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */; };
@ -108,6 +115,9 @@
E4E96D13233E630800AFD36F /* PMKFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = E4E96D12233E630800AFD36F /* PMKFoundation */; }; E4E96D13233E630800AFD36F /* PMKFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = E4E96D12233E630800AFD36F /* PMKFoundation */; };
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB2378220F10B8008C70C0 /* MPDPair.swift */; }; E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB2378220F10B8008C70C0 /* MPDPair.swift */; };
E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */; }; E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */; };
E4F26F7723411AE300D45FF9 /* ArtistListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F26F7623411AE300D45FF9 /* ArtistListActions.swift */; };
E4F26F7923411B1500D45FF9 /* ArtistReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F26F7823411B1500D45FF9 /* ArtistReducer.swift */; };
E4F26F7B23411D5400D45FF9 /* MPDClient+Artist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F26F7A23411D5400D45FF9 /* MPDClient+Artist.swift */; };
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */; }; E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */; };
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B462221E125900ACF42A /* QueueItem.swift */; }; E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B462221E125900ACF42A /* QueueItem.swift */; };
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B466221E233200ACF42A /* AlbumDataSource.swift */; }; E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B466221E233200ACF42A /* AlbumDataSource.swift */; };
@ -230,12 +240,17 @@
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; }; E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
E423563F228623D2001216D6 /* QueueSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongTitleView.swift; sourceTree = "<group>"; }; E423563F228623D2001216D6 /* QueueSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongTitleView.swift; sourceTree = "<group>"; };
E42410B52241B956005ED6DF /* MPDClient+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Database.swift"; sourceTree = "<group>"; }; E42410B52241B956005ED6DF /* MPDClient+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Database.swift"; sourceTree = "<group>"; };
E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDTag.swift; sourceTree = "<group>"; };
E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Songs.swift"; sourceTree = "<group>"; };
E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; }; E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
E42A8F3A22176D6400A13ED9 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; E42A8F3A22176D6400A13ED9 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
E435E3E1221CD4E200184CFC /* NSFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSFont.swift; sourceTree = "<group>"; }; E435E3E1221CD4E200184CFC /* NSFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSFont.swift; sourceTree = "<group>"; };
E435E3E3221CD75D00184CFC /* NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; }; E435E3E3221CD75D00184CFC /* NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
E439109722640213002982E9 /* SongNotifierService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongNotifierService.swift; sourceTree = "<group>"; }; E439109722640213002982E9 /* SongNotifierService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongNotifierService.swift; sourceTree = "<group>"; };
E43AC1F022C68E6A001E483C /* NSPasteboardItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboardItem.swift; sourceTree = "<group>"; }; E43AC1F022C68E6A001E483C /* NSPasteboardItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboardItem.swift; sourceTree = "<group>"; };
E43AC1F222C6A439001E483C /* AlbumViewController+NSCollectionViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumViewController+NSCollectionViewDelegate.swift"; sourceTree = "<group>"; };
E43AC1F422C6A4F4001E483C /* DraggedAlbum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedAlbum.swift; sourceTree = "<group>"; };
E43AC1F722C7065A001E483C /* AlbumCoverButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumCoverButton.swift; sourceTree = "<group>"; };
E43B67A822909793007DCF55 /* AlbumDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailView.swift; sourceTree = "<group>"; }; E43B67A822909793007DCF55 /* AlbumDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailView.swift; sourceTree = "<group>"; };
E43B67A922909793007DCF55 /* AlbumDetailView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumDetailView.xib; sourceTree = "<group>"; }; E43B67A922909793007DCF55 /* AlbumDetailView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumDetailView.xib; sourceTree = "<group>"; };
E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumTracksDataSource.swift; sourceTree = "<group>"; }; E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumTracksDataSource.swift; sourceTree = "<group>"; };
@ -244,6 +259,7 @@
E440519B227BAF2E0090CD6F /* UIActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActions.swift; sourceTree = "<group>"; }; E440519B227BAF2E0090CD6F /* UIActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActions.swift; sourceTree = "<group>"; };
E440519D227BB0720090CD6F /* UIReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIReducer.swift; sourceTree = "<group>"; }; E440519D227BB0720090CD6F /* UIReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIReducer.swift; sourceTree = "<group>"; };
E440519F227BB0AB0090CD6F /* UIState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIState.swift; sourceTree = "<group>"; }; E440519F227BB0AB0090CD6F /* UIState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIState.swift; sourceTree = "<group>"; };
E442CCCC2347E73C00004E0C /* Artist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Artist.swift; sourceTree = "<group>"; };
E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; }; E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
E450AD9422262DF10091BED3 /* CoverArtQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtQueue.swift; sourceTree = "<group>"; }; E450AD9422262DF10091BED3 /* CoverArtQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtQueue.swift; sourceTree = "<group>"; };
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; }; E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
@ -257,13 +273,13 @@
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; }; E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; };
E47E2FD222205D2500F747E6 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; }; E47E2FD222205D2500F747E6 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewItem.swift; sourceTree = "<group>"; }; E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewItem.swift; sourceTree = "<group>"; };
E47E2FD62220720300F747E6 /* AlbumItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumItemView.swift; sourceTree = "<group>"; };
E47E2FDC2220A6D100F747E6 /* Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = "<group>"; }; E47E2FDC2220A6D100F747E6 /* Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = "<group>"; };
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = "<group>"; }; E47E2FE42220AA0700F747E6 /* FlexibleGridViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlexibleGridViewLayout.swift; sourceTree = "<group>"; };
E489E39822B85D0400CA8CBD /* NSPasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboard.swift; sourceTree = "<group>"; }; E489E39822B85D0400CA8CBD /* NSPasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboard.swift; sourceTree = "<group>"; };
E489E39C22B9CF0000CA8CBD /* NSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSView.swift; sourceTree = "<group>"; }; E489E39C22B9CF0000CA8CBD /* NSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSView.swift; sourceTree = "<group>"; };
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongView.swift; sourceTree = "<group>"; }; E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongView.swift; sourceTree = "<group>"; };
E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DraggedSongView.xib; sourceTree = "<group>"; }; E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DraggedSongView.xib; sourceTree = "<group>"; };
E48E92D6235113DF00A5E1BB /* Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metadata.swift; sourceTree = "<group>"; };
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; }; E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongListView.swift; sourceTree = "<group>"; }; E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongListView.swift; sourceTree = "<group>"; };
E4A642D922090CBE00067D21 /* MPDStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDStatus.swift; sourceTree = "<group>"; }; E4A642D922090CBE00067D21 /* MPDStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDStatus.swift; sourceTree = "<group>"; };
@ -286,6 +302,7 @@
E4B11BBF2275EE150075461B /* QueueActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueActions.swift; sourceTree = "<group>"; }; E4B11BBF2275EE150075461B /* QueueActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueActions.swift; sourceTree = "<group>"; };
E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = "<group>"; }; E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = "<group>"; };
E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerDelegate.swift; sourceTree = "<group>"; }; E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerDelegate.swift; sourceTree = "<group>"; };
E4BBD2F223357C0700702C16 /* ArtistListState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistListState.swift; sourceTree = "<group>"; };
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; }; E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; }; E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueViewController+NSOutlineViewDelegate.swift"; sourceTree = "<group>"; }; E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueViewController+NSOutlineViewDelegate.swift"; sourceTree = "<group>"; };
@ -295,6 +312,9 @@
E4E8CC9922075D370024217A /* MPDSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDSong.swift; sourceTree = "<group>"; }; E4E8CC9922075D370024217A /* MPDSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDSong.swift; sourceTree = "<group>"; };
E4EB2378220F10B8008C70C0 /* MPDPair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDPair.swift; sourceTree = "<group>"; }; E4EB2378220F10B8008C70C0 /* MPDPair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDPair.swift; sourceTree = "<group>"; };
E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDAlbum.swift; sourceTree = "<group>"; }; E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDAlbum.swift; sourceTree = "<group>"; };
E4F26F7623411AE300D45FF9 /* ArtistListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistListActions.swift; sourceTree = "<group>"; };
E4F26F7823411B1500D45FF9 /* ArtistReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistReducer.swift; sourceTree = "<group>"; };
E4F26F7A23411D5400D45FF9 /* MPDClient+Artist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Artist.swift"; sourceTree = "<group>"; };
E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueDataSource.swift; sourceTree = "<group>"; }; E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueDataSource.swift; sourceTree = "<group>"; };
E4F6B462221E125900ACF42A /* QueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueItem.swift; sourceTree = "<group>"; }; E4F6B462221E125900ACF42A /* QueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueItem.swift; sourceTree = "<group>"; };
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDataSource.swift; sourceTree = "<group>"; }; E4F6B466221E233200ACF42A /* AlbumDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDataSource.swift; sourceTree = "<group>"; };
@ -365,25 +385,19 @@
E407861A2110CE6E006887B1 /* Persephone */ = { E407861A2110CE6E006887B1 /* Persephone */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E407861B2110CE6E006887B1 /* AppDelegate.swift */,
E44051932278765A0090CD6F /* App.swift */, E44051932278765A0090CD6F /* App.swift */,
E407861B2110CE6E006887B1 /* AppDelegate.swift */,
E407861F2110CE70006887B1 /* Assets.xcassets */, E407861F2110CE70006887B1 /* Assets.xcassets */,
E4D1B597220BA3A20026F233 /* Controllers */, E442CCC42347D5B900004E0C /* Components */,
E4F6B45E221E117600ACF42A /* DataSources */,
E408D3B7220DE8CC0006D9BE /* Extensions */,
E41B22C721FB966C00D544F6 /* include */, E41B22C721FB966C00D544F6 /* include */,
E40786242110CE70006887B1 /* Info.plist */, E40786242110CE70006887B1 /* Info.plist */,
E47E2FE32220AA0700F747E6 /* Layouts */,
E4F6B461221E124700ACF42A /* Models */, E4F6B461221E124700ACF42A /* Models */,
E4A642DB220912FA00067D21 /* MPDClient */, E4A642DB220912FA00067D21 /* MPDClient */,
E450AD8922262B420091BED3 /* Operations */, E450AD8922262B420091BED3 /* Operations */,
E40786252110CE70006887B1 /* Persephone.entitlements */, E40786252110CE70006887B1 /* Persephone.entitlements */,
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */, E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */,
E4A83BEC2221F5DD0098FED6 /* Preferences */,
E4D1B598220BA3C90026F233 /* Resources */,
E4A83BF2222207BE0098FED6 /* Services */, E4A83BF2222207BE0098FED6 /* Services */,
E4B11B64226A4F460075461B /* State */, E4B11B64226A4F460075461B /* State */,
E408D3C3220E138B0006D9BE /* Views */,
); );
path = Persephone; path = Persephone;
sourceTree = "<group>"; sourceTree = "<group>";
@ -426,12 +440,14 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E41E530A223C033700173814 /* MPDClient+Album.swift */, E41E530A223C033700173814 /* MPDClient+Album.swift */,
E4F26F7A23411D5400D45FF9 /* MPDClient+Artist.swift */,
E41E5308223C020400173814 /* MPDClient+Command.swift */, E41E5308223C020400173814 /* MPDClient+Command.swift */,
E41E52FC223BF87300173814 /* MPDClient+Connection.swift */, E41E52FC223BF87300173814 /* MPDClient+Connection.swift */,
E42410B52241B956005ED6DF /* MPDClient+Database.swift */, E42410B52241B956005ED6DF /* MPDClient+Database.swift */,
E41E5304223BFB0700173814 /* MPDClient+Error.swift */, E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */, E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
E41E5300223BF99300173814 /* MPDClient+Queue.swift */, E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */,
E41E5306223C019100173814 /* MPDClient+Status.swift */, E41E5306223C019100173814 /* MPDClient+Status.swift */,
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */, E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */, E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
@ -439,21 +455,6 @@
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E408D3C3220E138B0006D9BE /* Views */ = {
isa = PBXGroup;
children = (
E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */,
E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */,
E47E2FD62220720300F747E6 /* AlbumItemView.swift */,
E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */,
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */,
E47E2FD222205D2500F747E6 /* MainWindow.swift */,
E423563F228623D2001216D6 /* QueueSongTitleView.swift */,
E4120D6B22AD8139004CB1F8 /* QueueView.swift */,
);
path = Views;
sourceTree = "<group>";
};
E41B22BE21FB6B3300D544F6 /* Frameworks */ = { E41B22BE21FB6B3300D544F6 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -523,6 +524,91 @@
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E442CCC42347D5B900004E0C /* Components */ = {
isa = PBXGroup;
children = (
E442CCCB2347D77A00004E0C /* Browser */,
E4A83BEC2221F5DD0098FED6 /* Preferences */,
E442CCC62347D5E700004E0C /* Queue */,
E442CCC92347D6FD00004E0C /* Shared */,
E442CCC52347D5CA00004E0C /* Window */,
);
path = Components;
sourceTree = "<group>";
};
E442CCC52347D5CA00004E0C /* Window */ = {
isa = PBXGroup;
children = (
E40786212110CE70006887B1 /* Main.storyboard */,
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */,
E47E2FD222205D2500F747E6 /* MainWindow.swift */,
E465049921E94DF500A70F4C /* WindowController.swift */,
);
path = Window;
sourceTree = "<group>";
};
E442CCC62347D5E700004E0C /* Queue */ = {
isa = PBXGroup;
children = (
E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */,
E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */,
E423563F228623D2001216D6 /* QueueSongTitleView.swift */,
E4120D6B22AD8139004CB1F8 /* QueueView.swift */,
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */,
);
path = Queue;
sourceTree = "<group>";
};
E442CCC72347D62F00004E0C /* Album Browser */ = {
isa = PBXGroup;
children = (
E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */,
E43AC1F722C7065A001E483C /* AlbumCoverButton.swift */,
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */,
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
E43AC1F222C6A439001E483C /* AlbumViewController+NSCollectionViewDelegate.swift */,
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */,
);
path = "Album Browser";
sourceTree = "<group>";
};
E442CCC82347D65300004E0C /* Album Detail */ = {
isa = PBXGroup;
children = (
E43B67A922909793007DCF55 /* AlbumDetailView.xib */,
E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */,
E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */,
E43B67A822909793007DCF55 /* AlbumDetailView.swift */,
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */,
E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */,
);
path = "Album Detail";
sourceTree = "<group>";
};
E442CCC92347D6FD00004E0C /* Shared */ = {
isa = PBXGroup;
children = (
E4E13C2C2350D8CB00092A6E /* Layouts */,
E408D3B7220DE8CC0006D9BE /* Extensions */,
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */,
E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */,
E4405191227644340090CD6F /* MPDServerController.swift */,
E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */,
E4B11BB52275374B0075461B /* UserNotificationsController.swift */,
);
path = Shared;
sourceTree = "<group>";
};
E442CCCB2347D77A00004E0C /* Browser */ = {
isa = PBXGroup;
children = (
E442CCC82347D65300004E0C /* Album Detail */,
E442CCC72347D62F00004E0C /* Album Browser */,
);
path = Browser;
sourceTree = "<group>";
};
E450AD8922262B420091BED3 /* Operations */ = { E450AD8922262B420091BED3 /* Operations */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -531,14 +617,6 @@
path = Operations; path = Operations;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E47E2FE32220AA0700F747E6 /* Layouts */ = {
isa = PBXGroup;
children = (
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */,
);
path = Layouts;
sourceTree = "<group>";
};
E4A642DB220912FA00067D21 /* MPDClient */ = { E4A642DB220912FA00067D21 /* MPDClient */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -551,14 +629,6 @@
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E4A83BEC2221F5DD0098FED6 /* Preferences */ = { E4A83BEC2221F5DD0098FED6 /* Preferences */ = {
isa = PBXGroup;
children = (
E4A83BED2221F5E60098FED6 /* Controllers */,
);
path = Preferences;
sourceTree = "<group>";
};
E4A83BED2221F5E60098FED6 /* Controllers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */, E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */,
@ -566,15 +636,15 @@
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */, E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */,
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */, E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */,
); );
path = Controllers; path = Preferences;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E4A83BF2222207BE0098FED6 /* Services */ = { E4A83BF2222207BE0098FED6 /* Services */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E41E530C223EF4BA00173814 /* Extensions */,
E4A83BF3222207D50098FED6 /* CoverArtService.swift */, E4A83BF3222207D50098FED6 /* CoverArtService.swift */,
E439109722640213002982E9 /* SongNotifierService.swift */, E439109722640213002982E9 /* SongNotifierService.swift */,
E41E530C223EF4BA00173814 /* Extensions */,
); );
path = Services; path = Services;
sourceTree = "<group>"; sourceTree = "<group>";
@ -582,11 +652,12 @@
E4B11B5F226A4BED0075461B /* Reducers */ = { E4B11B5F226A4BED0075461B /* Reducers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E4B11B60226A4BFF0075461B /* PlayerReducer.swift */,
E4B11B62226A4C510075461B /* AppReducer.swift */,
E4B11B74226CC4D30075461B /* QueueReducer.swift */,
E4B11B78226D346B0075461B /* AlbumListReducer.swift */, E4B11B78226D346B0075461B /* AlbumListReducer.swift */,
E4B11B62226A4C510075461B /* AppReducer.swift */,
E4F26F7823411B1500D45FF9 /* ArtistReducer.swift */,
E4B11B60226A4BFF0075461B /* PlayerReducer.swift */,
E4FF718F227601B400D4C412 /* PreferencesReducer.swift */, E4FF718F227601B400D4C412 /* PreferencesReducer.swift */,
E4B11B74226CC4D30075461B /* QueueReducer.swift */,
E440519D227BB0720090CD6F /* UIReducer.swift */, E440519D227BB0720090CD6F /* UIReducer.swift */,
); );
path = Reducers; path = Reducers;
@ -597,12 +668,13 @@
children = ( children = (
E4B11B6B226A5AF50075461B /* Actions */, E4B11B6B226A5AF50075461B /* Actions */,
E4B11B5F226A4BED0075461B /* Reducers */, E4B11B5F226A4BED0075461B /* Reducers */,
E4B11B52226928F20075461B /* AppState.swift */,
E440519F227BB0AB0090CD6F /* UIState.swift */,
E4B11B65226A4F830075461B /* PlayerState.swift */,
E4B11B67226A4FA00075461B /* QueueState.swift */,
E4B11B69226A4FBC0075461B /* AlbumListState.swift */, E4B11B69226A4FBC0075461B /* AlbumListState.swift */,
E4B11B52226928F20075461B /* AppState.swift */,
E4BBD2F223357C0700702C16 /* ArtistListState.swift */,
E4B11B65226A4F830075461B /* PlayerState.swift */,
E4FF718D2276010E00D4C412 /* PreferencesState.swift */, E4FF718D2276010E00D4C412 /* PreferencesState.swift */,
E4B11B67226A4FA00075461B /* QueueState.swift */,
E440519F227BB0AB0090CD6F /* UIState.swift */,
); );
path = State; path = State;
sourceTree = "<group>"; sourceTree = "<group>";
@ -611,9 +683,10 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E4B11BC12275EE410075461B /* AlbumListActions.swift */, E4B11BC12275EE410075461B /* AlbumListActions.swift */,
E4F26F7623411AE300D45FF9 /* ArtistListActions.swift */,
E4B11BBD2275EDAA0075461B /* PlayerActions.swift */, E4B11BBD2275EDAA0075461B /* PlayerActions.swift */,
E4B11BBF2275EE150075461B /* QueueActions.swift */,
E4FF71912276029000D4C412 /* PreferencesActions.swift */, E4FF71912276029000D4C412 /* PreferencesActions.swift */,
E4B11BBF2275EE150075461B /* QueueActions.swift */,
E440519B227BAF2E0090CD6F /* UIActions.swift */, E440519B227BAF2E0090CD6F /* UIActions.swift */,
); );
path = Actions; path = Actions;
@ -628,6 +701,7 @@
E4EB2378220F10B8008C70C0 /* MPDPair.swift */, E4EB2378220F10B8008C70C0 /* MPDPair.swift */,
E4E8CC9922075D370024217A /* MPDSong.swift */, E4E8CC9922075D370024217A /* MPDSong.swift */,
E4A642D922090CBE00067D21 /* MPDStatus.swift */, E4A642D922090CBE00067D21 /* MPDStatus.swift */,
E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@ -640,57 +714,29 @@
path = Protocols; path = Protocols;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E4D1B597220BA3A20026F233 /* Controllers */ = { E4E13C2C2350D8CB00092A6E /* Layouts */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E43B67A822909793007DCF55 /* AlbumDetailView.swift */, E47E2FE42220AA0700F747E6 /* FlexibleGridViewLayout.swift */,
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */,
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */,
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */,
E4405191227644340090CD6F /* MPDServerController.swift */,
E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */,
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */,
E4B11BB52275374B0075461B /* UserNotificationsController.swift */,
E465049921E94DF500A70F4C /* WindowController.swift */,
); );
path = Controllers; path = Layouts;
sourceTree = "<group>";
};
E4D1B598220BA3C90026F233 /* Resources */ = {
isa = PBXGroup;
children = (
E43B67A922909793007DCF55 /* AlbumDetailView.xib */,
E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */,
E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */,
E40786212110CE70006887B1 /* Main.storyboard */,
);
path = Resources;
sourceTree = "<group>";
};
E4F6B45E221E117600ACF42A /* DataSources */ = {
isa = PBXGroup;
children = (
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */,
E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */,
E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */,
);
path = DataSources;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E4F6B461221E124700ACF42A /* Models */ = { E4F6B461221E124700ACF42A /* Models */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E450AD7D222620A10091BED3 /* Album.swift */, E450AD7D222620A10091BED3 /* Album.swift */,
E442CCCC2347E73C00004E0C /* Artist.swift */,
E43AC1F422C6A4F4001E483C /* DraggedAlbum.swift */,
E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */,
E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */,
E4B11BA82274EDE30075461B /* Loading.swift */, E4B11BA82274EDE30075461B /* Loading.swift */,
E4FF71932276043A00D4C412 /* MPDServer.swift */, E4FF71932276043A00D4C412 /* MPDServer.swift */,
E4F6B462221E125900ACF42A /* QueueItem.swift */, E4F6B462221E125900ACF42A /* QueueItem.swift */,
E419E2862249B96600216A8C /* Song.swift */, E419E2862249B96600216A8C /* Song.swift */,
E47E2FDC2220A6D100F747E6 /* Time.swift */, E47E2FDC2220A6D100F747E6 /* Time.swift */,
E4B11B72226A6C770075461B /* TrackTimer.swift */, E4B11B72226A6C770075461B /* TrackTimer.swift */,
E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */, E48E92D6235113DF00A5E1BB /* Metadata.swift */,
E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@ -888,8 +934,11 @@
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */, E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */,
E4B11BA92274EDE30075461B /* Loading.swift in Sources */, E4B11BA92274EDE30075461B /* Loading.swift in Sources */,
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */, E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
E42A4D4F22E20D7D001C6CAD /* MPDTag.swift in Sources */,
E43AC1F522C6A4F4001E483C /* DraggedAlbum.swift in Sources */,
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */, E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */, E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */,
E43AC1F622C6AD0B001E483C /* AlbumViewController+NSCollectionViewDelegate.swift in Sources */,
E4B11B68226A4FA00075461B /* QueueState.swift in Sources */, E4B11B68226A4FA00075461B /* QueueState.swift in Sources */,
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */, E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */, E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */,
@ -900,18 +949,20 @@
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */, E4B11B63226A4C510075461B /* AppReducer.swift in Sources */,
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */, E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
E41E5310223EF6CE00173814 /* CoverArtService+Remote.swift in Sources */, E41E5310223EF6CE00173814 /* CoverArtService+Remote.swift in Sources */,
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */,
E48E92D7235113DF00A5E1BB /* Metadata.swift in Sources */,
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */, E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */, E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */, E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */,
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */, E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */,
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */, E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
E4B11BC02275EE150075461B /* QueueActions.swift in Sources */, E4B11BC02275EE150075461B /* QueueActions.swift in Sources */,
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */, E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */,
E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */, E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */,
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */, E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */,
E450AD7E222620A10091BED3 /* Album.swift in Sources */, E450AD7E222620A10091BED3 /* Album.swift in Sources */,
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */, E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
E43AC1F822C7065A001E483C /* AlbumCoverButton.swift in Sources */,
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */, E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */,
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */, E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */, E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */,
@ -923,6 +974,7 @@
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */, E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */,
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */, E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */, E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */,
E4F26F7B23411D5400D45FF9 /* MPDClient+Artist.swift in Sources */,
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */, E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */,
E4FF71922276029000D4C412 /* PreferencesActions.swift in Sources */, E4FF71922276029000D4C412 /* PreferencesActions.swift in Sources */,
E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */, E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */,
@ -932,16 +984,18 @@
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */, E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
E419E2872249B96600216A8C /* Song.swift in Sources */, E419E2872249B96600216A8C /* Song.swift in Sources */,
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */, E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */,
E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */,
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */, E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */,
E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */, E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */,
E439109822640213002982E9 /* SongNotifierService.swift in Sources */, E439109822640213002982E9 /* SongNotifierService.swift in Sources */,
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */, E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */, E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */,
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */, E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */,
E4F26F7923411B1500D45FF9 /* ArtistReducer.swift in Sources */,
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */, E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */,
E44051942278765A0090CD6F /* App.swift in Sources */, E44051942278765A0090CD6F /* App.swift in Sources */,
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */, E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */,
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */, E47E2FE52220AA0700F747E6 /* FlexibleGridViewLayout.swift in Sources */,
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */, E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */, E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */, E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
@ -956,6 +1010,7 @@
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */, E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
E4B11B66226A4F830075461B /* PlayerState.swift in Sources */, E4B11B66226A4F830075461B /* PlayerState.swift in Sources */,
E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */, E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */,
E4F26F7723411AE300D45FF9 /* ArtistListActions.swift in Sources */,
E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */, E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */,
E41E530E223EF4CF00173814 /* CoverArtService+Caching.swift in Sources */, E41E530E223EF4CF00173814 /* CoverArtService+Caching.swift in Sources */,
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */, E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
@ -972,6 +1027,7 @@
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */, E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */, E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */, E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */,
E42A4D5122E2167E001C6CAD /* MPDClient+Songs.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1142,10 +1198,7 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = BDEE7ZBFZ3; DEVELOPMENT_TEAM = BDEE7ZBFZ3;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = "$(inherited)";
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
INFOPLIST_FILE = Persephone/Info.plist; INFOPLIST_FILE = Persephone/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -1173,10 +1226,7 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = BDEE7ZBFZ3; DEVELOPMENT_TEAM = BDEE7ZBFZ3;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = "$(inherited)";
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
);
INFOPLIST_FILE = Persephone/Info.plist; INFOPLIST_FILE = Persephone/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",

View File

@ -15,5 +15,5 @@ struct App {
static let userNotificationsController = UserNotificationsController() static let userNotificationsController = UserNotificationsController()
static let mpdServerController = MPDServerController() static let mpdServerController = MPDServerController()
static let mpdServerDelegate = MPDServerDelegate() static let mpdServerDelegate = MPDServerDelegate()
static let mpdClient = MPDClient(withDelegate: mpdServerDelegate) static var mpdClient: MPDClient!
} }

View File

@ -39,10 +39,13 @@ class AppDelegate: NSObject,
func connectToMPDServer() { func connectToMPDServer() {
let mpdServer = App.store.state.preferencesState.mpdServer let mpdServer = App.store.state.preferencesState.mpdServer
App.mpdClient.connect( App.mpdClient = MPDClient(
host: mpdServer.hostOrDefault, host: mpdServer.hostOrDefault,
port: mpdServer.portOrDefault port: mpdServer.portOrDefault,
withDelegate: App.mpdServerDelegate
) )
App.mpdClient.connect()
} }
func instantiateControllers() { func instantiateControllers() {

View File

@ -0,0 +1,43 @@
//
// AlbumCoverButton.swift
// Persephone
//
// Created by Daniel Barber on 2019/6/28.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
class AlbumCoverButton: NSButton {
var dragging = false
var deltaX: CGFloat = 0
var deltaY: CGFloat = 0
override func mouseDown(with event: NSEvent) {
nextResponder?.mouseDown(with: event)
}
override func mouseDragged(with event: NSEvent) {
deltaX = deltaX + event.deltaX
deltaY = deltaY + event.deltaY
if (deltaX > 5 || deltaX < -5 || deltaY > 5 || deltaY < -5) {
dragging = true
}
nextResponder?.mouseDragged(with: event)
}
override func mouseUp(with event: NSEvent) {
deltaX = 0
deltaY = 0
if dragging {
dragging = false
nextResponder?.mouseUp(with: event)
} else {
super.mouseDown(with: event)
super.mouseUp(with: event)
}
}
}

View File

@ -28,6 +28,15 @@ class AlbumDataSource: NSObject, NSCollectionViewDataSource {
App.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) { mpdSong in App.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) { mpdSong in
guard let mpdSong = mpdSong else { return } guard let mpdSong = mpdSong else { return }
DispatchQueue.main.async {
App.store.dispatch(
UpdateAlbumMetaData(
metadata: Metadata(date: mpdSong.date),
albumIndex: indexPath.item
)
)
}
CoverArtService(song: Song(mpdSong: mpdSong)) CoverArtService(song: Song(mpdSong: mpdSong))
.fetchCoverArt() .fetchCoverArt()
.done { image in .done { image in

View File

@ -0,0 +1,32 @@
//
// AlbumViewController+NSCollectionViewDelegate.swift
// Persephone
//
// Created by Daniel Barber on 2019/6/28.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
extension AlbumViewController: NSCollectionViewDelegate {
func registerForDragAndDrop(_ collectionView: NSCollectionView) {
collectionView.registerForDraggedTypes([.albumPasteboardType])
collectionView.setDraggingSourceOperationMask(.copy, forLocal: true)
}
func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
let album = dataSource.albums[indexPath.item]
return NSPasteboardItem(
draggedAlbum: DraggedAlbum(
title: album.title,
artist: album.artist
),
ofType: .albumPasteboardType
)
}
func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {
return true
}
}

View File

@ -11,12 +11,9 @@ import ReSwift
import Differ import Differ
class AlbumViewController: NSViewController, class AlbumViewController: NSViewController,
NSCollectionViewDelegate,
NSCollectionViewDelegateFlowLayout { NSCollectionViewDelegateFlowLayout {
let paddingWidth: CGFloat = 40
let gutterWidth: CGFloat = 20
var dataSource = AlbumDataSource() var dataSource = AlbumDataSource()
let layout = FlexibleGridViewLayout(coder: NSCoder())
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -25,9 +22,16 @@ class AlbumViewController: NSViewController,
$0.select { $0.albumListState } $0.select { $0.albumListState }
} }
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
albumScrollView.postsBoundsChangedNotifications = true albumScrollView.postsBoundsChangedNotifications = true
albumCollectionView.dataSource = dataSource albumCollectionView.dataSource = dataSource
layout?.extraHeight = 44
albumCollectionView.collectionViewLayout = layout
registerForDragAndDrop(albumCollectionView)
} }
deinit { deinit {
@ -37,7 +41,7 @@ class AlbumViewController: NSViewController,
override func viewWillLayout() { override func viewWillLayout() {
super.viewWillLayout() super.viewWillLayout()
if let layout = albumCollectionView.collectionViewLayout as? AlbumViewLayout { if let layout = albumCollectionView.collectionViewLayout as? FlexibleGridViewLayout {
layout.saveScrollPosition() layout.saveScrollPosition()
} }
@ -47,12 +51,22 @@ class AlbumViewController: NSViewController,
override func viewDidLayout() { override func viewDidLayout() {
super.viewDidLayout() super.viewDidLayout()
guard let layout = albumCollectionView.collectionViewLayout as? AlbumViewLayout guard let layout = albumCollectionView.collectionViewLayout as? FlexibleGridViewLayout
else { return } else { return }
layout.setScrollPosition() layout.setScrollPosition()
} }
@objc func didConnect() {
App.mpdClient.fetchAllAlbums()
}
@objc func willDisconnect() {
DispatchQueue.main.async {
App.store.dispatch(UpdateAlbumListAction(albums: []))
}
}
@IBOutlet var albumScrollView: NSScrollView! @IBOutlet var albumScrollView: NSScrollView!
@IBOutlet var albumCollectionView: NSCollectionView! @IBOutlet var albumCollectionView: NSCollectionView!
} }

View File

@ -12,12 +12,23 @@ class AlbumViewItem: NSCollectionViewItem {
var observer: NSKeyValueObservation? var observer: NSKeyValueObservation?
var album: Album? var album: Album?
override var isSelected: Bool {
didSet {
albumCoverBox.layer?.borderWidth = isSelected ? 5 : 0
}
}
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
albumCoverView.wantsLayer = true albumCoverView.wantsLayer = true
albumCoverView.layer?.cornerRadius = 3 albumCoverView.layer?.cornerRadius = 3
albumCoverView.layer?.borderWidth = 1 albumCoverView.layer?.borderWidth = 1
albumCoverBox.wantsLayer = true
albumCoverBox.layer?.cornerRadius = 5
albumCoverBox.layer?.borderWidth = 0
setAppearance() setAppearance()
if #available(OSX 10.14, *) { if #available(OSX 10.14, *) {
@ -52,8 +63,10 @@ class AlbumViewItem: NSCollectionViewItem {
[.darkAqua, .aqua]) == .darkAqua [.darkAqua, .aqua]) == .darkAqua
albumCoverView.layer?.borderColor = darkMode ? .albumBorderColorDark : .albumBorderColorLight albumCoverView.layer?.borderColor = darkMode ? .albumBorderColorDark : .albumBorderColorLight
albumCoverBox.layer?.borderColor = NSColor.controlAccentColor.cgColor
} else { } else {
albumCoverView.layer?.borderColor = .albumBorderColorLight albumCoverView.layer?.borderColor = .albumBorderColorLight
albumCoverBox.layer?.borderColor = NSColor.selectedControlColor.cgColor
} }
} }
@ -65,12 +78,13 @@ class AlbumViewItem: NSCollectionViewItem {
AlbumDetailView.popover.contentViewController = AlbumDetailView.shared AlbumDetailView.popover.contentViewController = AlbumDetailView.shared
AlbumDetailView.popover.behavior = .transient AlbumDetailView.popover.behavior = .transient
AlbumDetailView.popover.show( AlbumDetailView.popover.show(
relativeTo: sender.bounds, relativeTo: albumCoverView.bounds,
of: sender, of: albumCoverView,
preferredEdge: .maxY preferredEdge: .maxY
) )
} }
@IBOutlet var albumCoverBox: NSBox!
@IBOutlet var albumCoverView: NSButton! @IBOutlet var albumCoverView: NSButton!
@IBOutlet var albumTitle: NSTextField! @IBOutlet var albumTitle: NSTextField!
@IBOutlet var albumArtist: NSTextField! @IBOutlet var albumArtist: NSTextField!

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target">
<connections>
<outlet property="albumArtist" destination="5Uu-j1-qyT" id="2Et-tX-InT"/>
<outlet property="albumCoverBox" destination="oat-Vd-t1v" id="aDy-tW-PFO"/>
<outlet property="albumCoverView" destination="juD-33-Glf" id="zPG-vz-USc"/>
<outlet property="albumTitle" destination="KEh-NL-c2W" id="SI3-hm-H2B"/>
<outlet property="view" destination="Hz6-mo-xeY" id="v7W-XA-Emc"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView identifier="albumViewItem" id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="158" height="192"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<box boxType="custom" borderType="none" cornerRadius="4" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="oat-Vd-t1v">
<rect key="frame" x="5" y="49" width="148" height="138"/>
<view key="contentView" wantsLayer="YES" id="h1X-3X-S5v">
<rect key="frame" x="0.0" y="0.0" width="148" height="138"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="juD-33-Glf" customClass="AlbumCoverButton" customModule="Persephone" customModuleProvider="target">
<rect key="frame" x="5" y="5" width="138" height="128"/>
<shadow key="shadow" blurRadius="4">
<size key="offset" width="0.0" height="2"/>
<color key="color" white="0.0" alpha="0.34603323063380281" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</shadow>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="defaultCoverArt" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" refusesFirstResponder="YES" state="on" transparent="YES" imageScaling="proportionallyUpOrDown" inset="2" id="t8A-Hz-L38">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showAlbumDetail:" target="-2" id="A4Q-gb-B45"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="juD-33-Glf" firstAttribute="top" secondItem="h1X-3X-S5v" secondAttribute="top" constant="5" id="aa9-xp-ssN"/>
<constraint firstAttribute="bottom" secondItem="juD-33-Glf" secondAttribute="bottom" constant="5" id="chP-h2-dVN"/>
<constraint firstItem="juD-33-Glf" firstAttribute="leading" secondItem="h1X-3X-S5v" secondAttribute="leading" constant="5" id="wpA-lb-lHS"/>
<constraint firstAttribute="trailing" secondItem="juD-33-Glf" secondAttribute="trailing" constant="5" id="z1e-o3-Xt0"/>
</constraints>
</view>
</box>
<textField identifier="albumTitle" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEh-NL-c2W">
<rect key="frame" x="8" y="28" width="142" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="pDs-0t-e1j">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="albumArtist" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5Uu-j1-qyT">
<rect key="frame" x="8" y="10" width="142" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="yZn-e9-zyP">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="5Uu-j1-qyT" firstAttribute="trailing" secondItem="KEh-NL-c2W" secondAttribute="trailing" id="64z-uz-4nY"/>
<constraint firstAttribute="bottom" secondItem="KEh-NL-c2W" secondAttribute="bottom" constant="28" id="8Kg-1r-wNp"/>
<constraint firstAttribute="bottom" secondItem="oat-Vd-t1v" secondAttribute="bottom" constant="49" id="9be-hX-hie"/>
<constraint firstItem="oat-Vd-t1v" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="5" id="HYh-A2-m7g"/>
<constraint firstItem="5Uu-j1-qyT" firstAttribute="leading" secondItem="KEh-NL-c2W" secondAttribute="leading" id="MUo-0i-fX9"/>
<constraint firstItem="oat-Vd-t1v" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="5" id="PHC-uH-EH6"/>
<constraint firstAttribute="bottom" secondItem="5Uu-j1-qyT" secondAttribute="bottom" constant="10" id="gci-4h-pDZ"/>
<constraint firstItem="KEh-NL-c2W" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="hG2-cb-UqL"/>
<constraint firstAttribute="trailing" secondItem="oat-Vd-t1v" secondAttribute="trailing" constant="5" id="nD3-RG-rfr"/>
<constraint firstItem="KEh-NL-c2W" firstAttribute="top" secondItem="oat-Vd-t1v" secondAttribute="bottom" constant="4" id="rOI-E8-Odh"/>
<constraint firstItem="KEh-NL-c2W" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="ubc-MA-g7H"/>
</constraints>
<point key="canvasLocation" x="-20" y="112"/>
</customView>
<collectionViewItem id="Qgu-aI-55A" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target"/>
</objects>
<resources>
<image name="defaultCoverArt" width="128" height="128"/>
</resources>
</document>

View File

@ -17,7 +17,7 @@ class AlbumDetailView: NSViewController {
@IBOutlet var albumTracksView: NSTableView! @IBOutlet var albumTracksView: NSTableView!
@IBOutlet var albumTitle: NSTextField! @IBOutlet var albumTitle: NSTextField!
@IBOutlet var albumArtist: NSTextField! @IBOutlet var albumMetadata: NSTextFieldCell!
@IBOutlet var albumCoverView: NSImageView! @IBOutlet var albumCoverView: NSImageView!
override func viewDidLoad() { override func viewDidLoad() {
@ -41,8 +41,10 @@ class AlbumDetailView: NSViewController {
getAlbumSongs(for: album) getAlbumSongs(for: album)
let date = album.metadata?.date ?? ""
albumTitle.stringValue = album.title albumTitle.stringValue = album.title
albumArtist.stringValue = album.artist albumMetadata.stringValue = "\(album.artist) · \(date)"
switch album.coverArt { switch album.coverArt {
case .loaded(let coverArt): case .loaded(let coverArt):
@ -58,7 +60,7 @@ class AlbumDetailView: NSViewController {
dataSource.albumSongs = [] dataSource.albumSongs = []
albumTracksView.reloadData() albumTracksView.reloadData()
albumTitle.stringValue = "" albumTitle.stringValue = ""
albumArtist.stringValue = "" albumMetadata.stringValue = ""
albumCoverView.image = .defaultCoverArt albumCoverView.image = .defaultCoverArt
App.store.dispatch(SetSelectedSong(selectedSong: nil)) App.store.dispatch(SetSelectedSong(selectedSong: nil))

View File

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="AlbumDetailView" customModule="Persephone" customModuleProvider="target"> <customObject id="-2" userLabel="File's Owner" customClass="AlbumDetailView" customModule="Persephone" customModuleProvider="target">
<connections> <connections>
<outlet property="albumArtist" destination="4Jx-I5-Nkv" id="mct-x3-yYC"/>
<outlet property="albumCoverView" destination="FWd-vZ-5CT" id="aHh-Bz-XQW"/> <outlet property="albumCoverView" destination="FWd-vZ-5CT" id="aHh-Bz-XQW"/>
<outlet property="albumMetadata" destination="ztJ-4E-qvI" id="YGK-TG-RPt"/>
<outlet property="albumTitle" destination="m2v-pR-e9v" id="M5i-u6-Nev"/> <outlet property="albumTitle" destination="m2v-pR-e9v" id="M5i-u6-Nev"/>
<outlet property="albumTracksView" destination="ehr-qh-87Q" id="fSa-Di-CqI"/> <outlet property="albumTracksView" destination="ehr-qh-87Q" id="fSa-Di-CqI"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/> <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
@ -17,12 +17,12 @@
</customObject> </customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY"> <customView wantsLayer="YES" id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="823" height="568"/> <rect key="frame" x="0.0" y="0.0" width="823" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="m2v-pR-e9v"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="m2v-pR-e9v">
<rect key="frame" x="357" y="514" width="448" height="29"/> <rect key="frame" x="357" y="515" width="448" height="28"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="444" id="erC-QS-9hc"/> <constraint firstAttribute="width" constant="444" id="erC-QS-9hc"/>
</constraints> </constraints>
@ -33,8 +33,8 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4Jx-I5-Nkv"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4Jx-I5-Nkv">
<rect key="frame" x="357" y="487" width="448" height="19"/> <rect key="frame" x="357" y="488" width="448" height="19"/>
<textFieldCell key="cell" title="Artist Name" id="ztJ-4E-qvI"> <textFieldCell key="cell" title="Metadata" id="ztJ-4E-qvI">
<font key="font" metaFont="system" size="16"/> <font key="font" metaFont="system" size="16"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -47,30 +47,20 @@
<constraint firstAttribute="width" constant="300" id="8XY-bQ-C7X"/> <constraint firstAttribute="width" constant="300" id="8XY-bQ-C7X"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="300" id="cyo-wr-hV8"/> <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="300" id="cyo-wr-hV8"/>
</constraints> </constraints>
<shadow key="shadow" blurRadius="4">
<size key="offset" width="0.0" height="-2"/>
<color key="color" white="0.0" alpha="0.34795884683098594" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</shadow>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="defaultCoverArt" id="scE-kj-gex"/> <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="defaultCoverArt" id="scE-kj-gex"/>
</imageView> </imageView>
<button verticalHuggingPriority="750" imageHugsTitle="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jMU-bv-TNF">
<rect key="frame" x="31" y="184" width="119" height="35"/>
<constraints>
<constraint firstAttribute="height" constant="33" id="2uQ-mC-4QY"/>
<constraint firstAttribute="width" constant="119" id="h2n-ZB-Ufr"/>
</constraints>
<buttonCell key="cell" type="smallSquare" title="Play Album" bezelStyle="smallSquare" image="playButton" imagePosition="left" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Rtg-Zd-JYc">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="playAlbum:" target="-2" id="LTw-Lg-yH2"/>
</connections>
</button>
<scrollView horizontalCompressionResistancePriority="250" borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="BOb-Lr-10M"> <scrollView horizontalCompressionResistancePriority="250" borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="BOb-Lr-10M">
<rect key="frame" x="359" y="33" width="444" height="425"/> <rect key="frame" x="359" y="33" width="444" height="426"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="9QN-UB-b4l"> <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="9QN-UB-b4l">
<rect key="frame" x="0.0" y="0.0" width="444" height="425"/> <rect key="frame" x="0.0" y="0.0" width="444" height="426"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="ehr-qh-87Q" customClass="AlbumDetailSongListView" customModule="Persephone" customModuleProvider="target"> <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="ehr-qh-87Q" customClass="AlbumDetailSongListView" customModule="Persephone" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="444" height="425"/> <rect key="frame" x="0.0" y="0.0" width="444" height="426"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/> <size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
@ -94,7 +84,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="6eU-Jx-HDR"> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="6eU-Jx-HDR">
<rect key="frame" x="0.0" y="0.0" width="40" height="17"/> <rect key="frame" x="0.0" y="1" width="40" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="1." id="Z5y-oS-Qm8"> <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="1." id="Z5y-oS-Qm8">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -116,7 +106,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nwx-zY-r5o"> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nwx-zY-r5o">
<rect key="frame" x="0.0" y="0.0" width="441" height="17"/> <rect key="frame" x="0.0" y="1" width="441" height="16"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="437" id="irN-AG-Pcj"/> <constraint firstAttribute="width" constant="437" id="irN-AG-Pcj"/>
</constraints> </constraints>
@ -155,7 +145,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="R8t-bV-9LI"> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="R8t-bV-9LI">
<rect key="frame" x="0.0" y="0.0" width="353" height="17"/> <rect key="frame" x="0.0" y="1" width="353" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="My Song Title" id="Sdi-jJ-EOM"> <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="My Song Title" id="Sdi-jJ-EOM">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -192,7 +182,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="pCr-f1-wNs"> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="pCr-f1-wNs">
<rect key="frame" x="0.0" y="0.0" width="42" height="17"/> <rect key="frame" x="0.0" y="1" width="42" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="right" title="0:00" id="Qe2-WO-eXr"> <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="right" title="0:00" id="Qe2-WO-eXr">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -229,22 +219,36 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
</scrollView> </scrollView>
<button verticalHuggingPriority="750" imageHugsTitle="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jMU-bv-TNF">
<rect key="frame" x="31" y="184" width="119" height="35"/>
<constraints>
<constraint firstAttribute="height" constant="33" id="2uQ-mC-4QY"/>
<constraint firstAttribute="width" constant="119" id="h2n-ZB-Ufr"/>
</constraints>
<buttonCell key="cell" type="smallSquare" title="Play Album" bezelStyle="smallSquare" image="playButton" imagePosition="left" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Rtg-Zd-JYc">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="systemMedium" size="13"/>
</buttonCell>
<connections>
<action selector="playAlbum:" target="-2" id="LTw-Lg-yH2"/>
</connections>
</button>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="BOb-Lr-10M" firstAttribute="top" secondItem="4Jx-I5-Nkv" secondAttribute="bottom" constant="29" id="1ty-6R-dGL"/>
<constraint firstItem="FWd-vZ-5CT" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="31" id="694-aS-G4N"/> <constraint firstItem="FWd-vZ-5CT" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="31" id="694-aS-G4N"/>
<constraint firstItem="jMU-bv-TNF" firstAttribute="leading" secondItem="FWd-vZ-5CT" secondAttribute="leading" id="AwZ-M4-fep"/> <constraint firstItem="jMU-bv-TNF" firstAttribute="leading" secondItem="FWd-vZ-5CT" secondAttribute="leading" id="AwZ-M4-fep"/>
<constraint firstItem="BOb-Lr-10M" firstAttribute="leading" secondItem="4Jx-I5-Nkv" secondAttribute="leading" id="GEK-R3-Sw6"/>
<constraint firstItem="BOb-Lr-10M" firstAttribute="top" secondItem="4Jx-I5-Nkv" secondAttribute="bottom" constant="29" id="K5g-Kd-iHK"/>
<constraint firstAttribute="bottom" secondItem="BOb-Lr-10M" secondAttribute="bottom" constant="33" id="MZ6-81-2qe"/>
<constraint firstItem="4Jx-I5-Nkv" firstAttribute="leading" secondItem="m2v-pR-e9v" secondAttribute="leading" id="NaJ-VT-uln"/> <constraint firstItem="4Jx-I5-Nkv" firstAttribute="leading" secondItem="m2v-pR-e9v" secondAttribute="leading" id="NaJ-VT-uln"/>
<constraint firstAttribute="bottom" secondItem="BOb-Lr-10M" secondAttribute="bottom" constant="33" id="Q1M-yZ-LHD"/>
<constraint firstItem="FWd-vZ-5CT" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="25" id="QA9-xn-fzY"/> <constraint firstItem="FWd-vZ-5CT" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="25" id="QA9-xn-fzY"/>
<constraint firstItem="m2v-pR-e9v" firstAttribute="top" secondItem="FWd-vZ-5CT" secondAttribute="top" id="bqi-HD-KZW"/> <constraint firstItem="m2v-pR-e9v" firstAttribute="top" secondItem="FWd-vZ-5CT" secondAttribute="top" id="bqi-HD-KZW"/>
<constraint firstItem="jMU-bv-TNF" firstAttribute="top" secondItem="FWd-vZ-5CT" secondAttribute="bottom" constant="25" id="dd1-6b-TEN"/> <constraint firstItem="jMU-bv-TNF" firstAttribute="top" secondItem="FWd-vZ-5CT" secondAttribute="bottom" constant="25" id="dd1-6b-TEN"/>
<constraint firstItem="4Jx-I5-Nkv" firstAttribute="trailing" secondItem="m2v-pR-e9v" secondAttribute="trailing" id="dmh-TC-Ncr"/> <constraint firstItem="4Jx-I5-Nkv" firstAttribute="trailing" secondItem="m2v-pR-e9v" secondAttribute="trailing" id="dmh-TC-Ncr"/>
<constraint firstItem="m2v-pR-e9v" firstAttribute="leading" secondItem="FWd-vZ-5CT" secondAttribute="trailing" constant="28" id="icS-vq-PkK"/> <constraint firstItem="m2v-pR-e9v" firstAttribute="leading" secondItem="FWd-vZ-5CT" secondAttribute="trailing" constant="28" id="icS-vq-PkK"/>
<constraint firstItem="4Jx-I5-Nkv" firstAttribute="top" secondItem="m2v-pR-e9v" secondAttribute="bottom" constant="8" symbolic="YES" id="nTZ-Ew-sDQ"/> <constraint firstItem="4Jx-I5-Nkv" firstAttribute="top" secondItem="m2v-pR-e9v" secondAttribute="bottom" constant="8" symbolic="YES" id="nTZ-Ew-sDQ"/>
<constraint firstItem="BOb-Lr-10M" firstAttribute="leading" secondItem="4Jx-I5-Nkv" secondAttribute="leading" id="pXJ-Jl-uAG"/>
<constraint firstAttribute="trailing" secondItem="m2v-pR-e9v" secondAttribute="trailing" constant="20" symbolic="YES" id="qyi-X9-6B9"/> <constraint firstAttribute="trailing" secondItem="m2v-pR-e9v" secondAttribute="trailing" constant="20" symbolic="YES" id="qyi-X9-6B9"/>
<constraint firstItem="BOb-Lr-10M" firstAttribute="trailing" secondItem="4Jx-I5-Nkv" secondAttribute="trailing" id="sPx-cY-MeX"/> <constraint firstItem="BOb-Lr-10M" firstAttribute="trailing" secondItem="4Jx-I5-Nkv" secondAttribute="trailing" id="zOM-i7-cog"/>
</constraints> </constraints>
<point key="canvasLocation" x="262.5" y="121"/> <point key="canvasLocation" x="262.5" y="121"/>
</customView> </customView>

View File

@ -18,7 +18,6 @@ class GeneralPrefsViewController: NSViewController {
} }
if let mpdPort = App.store.state.preferencesState.mpdServer.port { if let mpdPort = App.store.state.preferencesState.mpdServer.port {
print(mpdPort)
mpdPortField.stringValue = "\(mpdPort)" mpdPortField.stringValue = "\(mpdPort)"
} }

View File

@ -57,9 +57,11 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
var newQueuePos = index - 1 var newQueuePos = index - 1
guard newQueuePos >= 0, guard newQueuePos >= 0,
let draggingTypes = info.draggingPasteboard.types, let draggingTypes = info.draggingPasteboard.types
draggingTypes.contains(.songPasteboardType), else { return [] }
let pasteboardItem = info.draggingPasteboard.pasteboardItems?.first,
if draggingTypes.contains(.songPasteboardType) {
guard let pasteboardItem = info.draggingPasteboard.pasteboardItems?.first,
let draggedSong = pasteboardItem.draggedSong(forType: .songPasteboardType) let draggedSong = pasteboardItem.draggedSong(forType: .songPasteboardType)
else { return [] } else { return [] }
@ -74,14 +76,21 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
case .albumSongItem: case .albumSongItem:
return .copy return .copy
} }
} else if draggingTypes.contains(.albumPasteboardType) {
return .copy
}
return []
} }
func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool { func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
var newQueuePos = index - 1 var newQueuePos = index - 1
guard let draggingTypes = info.draggingPasteboard.types, guard let draggingTypes = info.draggingPasteboard.types
draggingTypes.contains(.songPasteboardType), else { return false }
let data = info.draggingPasteboard.data(forType: .songPasteboardType),
if draggingTypes.contains(.songPasteboardType) {
guard let data = info.draggingPasteboard.data(forType: .songPasteboardType),
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data) let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data)
else { return false } else { return false }
@ -98,6 +107,18 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
App.mpdClient.addSongToQueue(songUri: uri, at: newQueuePos) App.mpdClient.addSongToQueue(songUri: uri, at: newQueuePos)
return true return true
} }
} else if draggingTypes.contains(.albumPasteboardType) {
guard let data = info.draggingPasteboard.data(forType: .albumPasteboardType),
let draggedAlbum = try? PropertyListDecoder().decode(DraggedAlbum.self, from: data)
else { return false }
let mpdAlbum = MPDClient.MPDAlbum(title: draggedAlbum.title, artist: draggedAlbum.artist)
App.mpdClient.addAlbumToQueue(album: mpdAlbum, at: newQueuePos)
return true
}
return false
} }
func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItems draggedItems: [Any]) { func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItems draggedItems: [Any]) {
@ -125,5 +146,4 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
} }
} }
} }
} }

View File

@ -22,12 +22,19 @@ class QueueViewController: NSViewController {
$0.select { $0.queueState } $0.select { $0.queueState }
} }
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
queueView.dataSource = dataSource queueView.dataSource = dataSource
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
queueView.registerForDraggedTypes([.songPasteboardType]) queueView.registerForDraggedTypes([.songPasteboardType, .albumPasteboardType])
queueView.draggingDestinationFeedbackStyle = .regular queueView.draggingDestinationFeedbackStyle = .regular
} }
deinit {
App.store.unsubscribe(self)
}
override func keyDown(with event: NSEvent) { override func keyDown(with event: NSEvent) {
switch event.keyCode { switch event.keyCode {
case NSEvent.keyCodeSpace: case NSEvent.keyCodeSpace:
@ -43,6 +50,17 @@ class QueueViewController: NSViewController {
} }
} }
@objc func didConnect() {
App.mpdClient.fetchQueue()
}
@objc func willDisconnect() {
DispatchQueue.main.async {
App.store.dispatch(UpdateQueuePosAction(queuePos: -1))
App.store.dispatch(UpdateQueueAction(queue: []))
}
}
@IBAction func playTrack(_ sender: Any) { @IBAction func playTrack(_ sender: Any) {
let queuePos = queueView.selectedRow - 1 let queuePos = queueView.selectedRow - 1

View File

@ -9,5 +9,6 @@
import AppKit import AppKit
extension NSPasteboard.PasteboardType { extension NSPasteboard.PasteboardType {
static let songPasteboardType = NSPasteboard.PasteboardType("me.danbarber.persephone") static let songPasteboardType = NSPasteboard.PasteboardType("me.danbarber.persephone.song")
static let albumPasteboardType = NSPasteboard.PasteboardType("me.danbarber.persephone.album")
} }

View File

@ -14,12 +14,23 @@ extension NSPasteboardItem {
self.setDraggedSong(draggedSong, forType: type) self.setDraggedSong(draggedSong, forType: type)
} }
convenience init(draggedAlbum: DraggedAlbum, ofType type: NSPasteboard.PasteboardType) {
self.init()
self.setDraggedAlbum(draggedAlbum, forType: type)
}
func setDraggedSong(_ draggedSong: DraggedSong, forType type: NSPasteboard.PasteboardType) { func setDraggedSong(_ draggedSong: DraggedSong, forType type: NSPasteboard.PasteboardType) {
let data = try! PropertyListEncoder().encode(draggedSong) let data = try! PropertyListEncoder().encode(draggedSong)
setData(data, forType: type) setData(data, forType: type)
} }
func setDraggedAlbum(_ draggedAlbum: DraggedAlbum, forType type: NSPasteboard.PasteboardType) {
let data = try! PropertyListEncoder().encode(draggedAlbum)
setData(data, forType: type)
}
func draggedSong(forType type: NSPasteboard.PasteboardType) -> DraggedSong? { func draggedSong(forType type: NSPasteboard.PasteboardType) -> DraggedSong? {
guard let itemData = data(forType: type) guard let itemData = data(forType: type)
else { return nil } else { return nil }

View File

@ -17,9 +17,12 @@ extension NSUserInterfaceItemIdentifier {
static let queueSongTitle = NSUserInterfaceItemIdentifier("songTitleCell") static let queueSongTitle = NSUserInterfaceItemIdentifier("songTitleCell")
static let albumViewItem = NSUserInterfaceItemIdentifier("AlbumViewItem") static let albumViewItem = NSUserInterfaceItemIdentifier("AlbumViewItem")
static let artistViewItem = NSUserInterfaceItemIdentifier("ArtistViewItem")
static let discNumber = NSUserInterfaceItemIdentifier("discNumberCell") static let discNumber = NSUserInterfaceItemIdentifier("discNumberCell")
static let trackNumber = NSUserInterfaceItemIdentifier("trackNumberCell") static let trackNumber = NSUserInterfaceItemIdentifier("trackNumberCell")
static let songTitle = NSUserInterfaceItemIdentifier("songTitleCell") static let songTitle = NSUserInterfaceItemIdentifier("songTitleCell")
static let songDuration = NSUserInterfaceItemIdentifier("songDurationCell") static let songDuration = NSUserInterfaceItemIdentifier("songDurationCell")
static let artistListName = NSUserInterfaceItemIdentifier("artistCell")
} }

View File

@ -0,0 +1,14 @@
//
// Notification.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/08.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
extension Notification.Name {
static let didConnect = Notification.Name("MPDClientDidConnect")
static let willDisconnect = Notification.Name("MPDClientWillDisconnect")
}

View File

@ -8,21 +8,21 @@
import AppKit import AppKit
class AlbumViewLayout: NSCollectionViewFlowLayout { class FlexibleGridViewLayout: NSCollectionViewFlowLayout {
let maxItemWidth: CGFloat = 180 let maxItemWidth: CGFloat = 200
let albumInfoHeight: CGFloat = 39 var extraHeight: CGFloat = 0
var scrollPosition: CGFloat = 0 var scrollPosition: CGFloat = 0
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
super.init() super.init()
minimumLineSpacing = 20 minimumLineSpacing = 0
minimumInteritemSpacing = 20 minimumInteritemSpacing = 0
sectionInset = NSEdgeInsets( sectionInset = NSEdgeInsets(
top: 20, top: 10,
left: 40, left: 30,
bottom: 60, bottom: 50,
right: 40 right: 30
) )
} }
@ -43,7 +43,7 @@ class AlbumViewLayout: NSCollectionViewFlowLayout {
divider = divider + 1 divider = divider + 1
} while itemWidth > maxItemWidth } while itemWidth > maxItemWidth
let itemHeight = itemWidth + albumInfoHeight let itemHeight = itemWidth + extraHeight
itemSize = NSSize(width: itemWidth, height: itemHeight) itemSize = NSSize(width: itemWidth, height: itemHeight)
} }

View File

@ -11,9 +11,9 @@ import ReSwift
class MPDServerController { class MPDServerController {
init() { init() {
App.store.subscribe(self) { // App.store.subscribe(self) {
$0.select { $0.preferencesState.mpdServer } // $0.select { $0.preferencesState.mpdServer }
} // }
} }
} }
@ -22,9 +22,6 @@ extension MPDServerController: StoreSubscriber {
func newState(state: MPDServer) { func newState(state: MPDServer) {
App.mpdClient.disconnect() App.mpdClient.disconnect()
App.mpdClient.connect( App.mpdClient.connect()
host: state.hostOrDefault,
port: state.portOrDefault
)
} }
} }

View File

@ -9,12 +9,12 @@
import Foundation import Foundation
class MPDServerDelegate: MPDClientDelegate { class MPDServerDelegate: MPDClientDelegate {
func didConnect(mpdClient: MPDClient) {} func didConnect(mpdClient: MPDClient) {
NotificationCenter.default.post(name: .didConnect, object: nil)
}
func willDisconnect(mpdClient: MPDClient) { func willDisconnect(mpdClient: MPDClient) {
DispatchQueue.main.async { NotificationCenter.default.post(name: .willDisconnect, object: nil)
App.store.dispatch(UpdateAlbumListAction(albums: []))
}
} }
func didUpdateStatus(mpdClient: MPDClient, status: MPDClient.MPDStatus) { func didUpdateStatus(mpdClient: MPDClient, status: MPDClient.MPDStatus) {
@ -52,4 +52,10 @@ class MPDServerDelegate: MPDClientDelegate {
App.store.dispatch(UpdateAlbumListAction(albums: albums)) App.store.dispatch(UpdateAlbumListAction(albums: albums))
} }
} }
func didLoadArtists(mpdClient: MPDClient, artists: [String]) {
DispatchQueue.main.async {
App.store.dispatch(UpdateArtistListAction(artists: artists))
}
}
} }

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS"> <document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
@ -187,7 +187,7 @@
<customObject id="YLy-65-1bz" customClass="NSFontManager"/> <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="81" y="-332"/> <point key="canvasLocation" x="-183" y="-242"/>
</scene> </scene>
<!--Window Controller--> <!--Window Controller-->
<scene sceneID="R2V-B0-nI4"> <scene sceneID="R2V-B0-nI4">
@ -341,7 +341,7 @@
<splitViewController id="fnD-7K-pHK" customClass="MainSplitViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController"> <splitViewController id="fnD-7K-pHK" customClass="MainSplitViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
<splitViewItems> <splitViewItems>
<splitViewItem holdingPriority="255" behavior="contentList" id="CWo-v7-gd2"/> <splitViewItem holdingPriority="255" behavior="contentList" id="CWo-v7-gd2"/>
<splitViewItem id="y8g-4F-czS"/> <splitViewItem id="Mdr-U0-Vci"/>
</splitViewItems> </splitViewItems>
<splitView key="splitView" dividerStyle="thin" vertical="YES" id="g34-ef-XN0"> <splitView key="splitView" dividerStyle="thin" vertical="YES" id="g34-ef-XN0">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/> <rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
@ -353,12 +353,12 @@
<connections> <connections>
<outlet property="splitView" destination="g34-ef-XN0" id="YEc-LL-DoS"/> <outlet property="splitView" destination="g34-ef-XN0" id="YEc-LL-DoS"/>
<segue destination="KIP-rq-4dM" kind="relationship" relationship="splitItems" id="Vmb-hY-d12"/> <segue destination="KIP-rq-4dM" kind="relationship" relationship="splitItems" id="Vmb-hY-d12"/>
<segue destination="gPn-fP-LFc" kind="relationship" relationship="splitItems" id="zKs-UD-ltC"/> <segue destination="SjO-VS-1bb" kind="relationship" relationship="splitItems" id="peC-gL-WJ1"/>
</connections> </connections>
</splitViewController> </splitViewController>
<customObject id="Dag-kO-ps3" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="Dag-kO-ps3" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="74" y="873"/> <point key="canvasLocation" x="886" y="182"/>
</scene> </scene>
<!--Window Controller--> <!--Window Controller-->
<scene sceneID="Rpk-bo-5kf"> <scene sceneID="Rpk-bo-5kf">
@ -378,7 +378,7 @@
</windowController> </windowController>
<customObject id="0sd-8B-etN" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="0sd-8B-etN" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="915" y="-155"/> <point key="canvasLocation" x="346" y="-242"/>
</scene> </scene>
<!--General--> <!--General-->
<scene sceneID="5er-B6-hoB"> <scene sceneID="5er-B6-hoB">
@ -405,7 +405,7 @@
</tabViewController> </tabViewController>
<customObject id="XtF-QO-9W0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="XtF-QO-9W0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="916" y="236"/> <point key="canvasLocation" x="870" y="-242"/>
</scene> </scene>
<!--Cover Art--> <!--Cover Art-->
<scene sceneID="pQx-0G-WVt"> <scene sceneID="pQx-0G-WVt">
@ -416,7 +416,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zZn-Rm-e1f"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zZn-Rm-e1f">
<rect key="frame" x="53" y="130" width="104" height="17"/> <rect key="frame" x="53" y="129" width="104" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Music Directory:" id="sPn-V6-CfK"> <textFieldCell key="cell" lineBreakMode="clipping" title="Music Directory:" id="sPn-V6-CfK">
<font key="font" usesAppearanceFont="YES"/> <font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -424,7 +424,7 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gDk-ca-eOa"> <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gDk-ca-eOa">
<rect key="frame" x="162" y="126" width="288" height="22"/> <rect key="frame" x="162" y="125" width="288" height="21"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="~/Music" drawsBackground="YES" id="7WZ-b7-GUs"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="~/Music" drawsBackground="YES" id="7WZ-b7-GUs">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -435,7 +435,7 @@
</connections> </connections>
</textField> </textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
<rect key="frame" x="160" y="95" width="264" height="18"/> <rect key="frame" x="160" y="94" width="264" height="18"/>
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" inset="2" id="LpD-Ew-HMd"> <buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" inset="2" id="LpD-Ew-HMd">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -445,7 +445,7 @@
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="z1g-nP-ksw"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="z1g-nP-ksw">
<rect key="frame" x="160" y="64" width="264" height="18"/> <rect key="frame" x="160" y="63" width="264" height="18"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="260" id="gK0-aW-CJy"/> <constraint firstAttribute="width" constant="260" id="gK0-aW-CJy"/>
</constraints> </constraints>
@ -455,7 +455,7 @@
</buttonCell> </buttonCell>
</button> </button>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgS-Kg-8KR"> <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgS-Kg-8KR">
<rect key="frame" x="162" y="26" width="144" height="22"/> <rect key="frame" x="162" y="26" width="144" height="21"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="144" id="DSX-th-Wn1"/> <constraint firstAttribute="width" constant="144" id="DSX-th-Wn1"/>
</constraints> </constraints>
@ -466,7 +466,7 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SmH-w6-5QI"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SmH-w6-5QI">
<rect key="frame" x="37" y="30" width="119" height="17"/> <rect key="frame" x="37" y="30" width="119" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" enabled="NO" alignment="right" title="Cover art filename:" id="b4u-u7-iWD"> <textFieldCell key="cell" lineBreakMode="clipping" enabled="NO" alignment="right" title="Cover art filename:" id="b4u-u7-iWD">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -498,7 +498,57 @@
</viewController> </viewController>
<customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="1626" y="373"/> <point key="canvasLocation" x="1459" y="-242"/>
</scene>
<!--View Controller-->
<scene sceneID="VvW-vT-alQ">
<objects>
<customObject id="MSG-y7-cKU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<viewController id="SjO-VS-1bb" sceneMemberID="viewController">
<view key="view" id="BRY-0R-F3u">
<rect key="frame" x="0.0" y="0.0" width="478" height="558"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tabView type="noTabsNoBorder" initialItem="XgS-cX-SDH" translatesAutoresizingMaskIntoConstraints="NO" id="ARv-cj-xlz">
<rect key="frame" x="0.0" y="0.0" width="478" height="558"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="Albums" identifier="" id="XgS-cX-SDH">
<view key="view" id="hB7-hN-SbB">
<rect key="frame" x="0.0" y="0.0" width="478" height="558"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<containerView translatesAutoresizingMaskIntoConstraints="NO" id="moE-bb-Zvg">
<rect key="frame" x="-3" y="-3" width="481" height="561"/>
<connections>
<segue destination="gPn-fP-LFc" kind="embed" id="2iB-9y-I9h"/>
</connections>
</containerView>
</subviews>
<constraints>
<constraint firstItem="moE-bb-Zvg" firstAttribute="top" secondItem="hB7-hN-SbB" secondAttribute="top" id="DUI-jy-8D7"/>
<constraint firstItem="moE-bb-Zvg" firstAttribute="leading" secondItem="hB7-hN-SbB" secondAttribute="leading" constant="-3" id="dCS-Kx-2UP"/>
<constraint firstAttribute="trailing" secondItem="moE-bb-Zvg" secondAttribute="trailing" id="qey-4e-xfu"/>
<constraint firstAttribute="bottom" secondItem="moE-bb-Zvg" secondAttribute="bottom" constant="-3" id="ziA-Xh-dlz"/>
</constraints>
</view>
</tabViewItem>
</tabViewItems>
</tabView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="ARv-cj-xlz" secondAttribute="trailing" id="nXk-bi-ua4"/>
<constraint firstAttribute="bottom" secondItem="ARv-cj-xlz" secondAttribute="bottom" id="nzf-Jw-Bpk"/>
<constraint firstItem="ARv-cj-xlz" firstAttribute="top" secondItem="BRY-0R-F3u" secondAttribute="top" id="v6W-0L-kQ1"/>
<constraint firstItem="ARv-cj-xlz" firstAttribute="leading" secondItem="BRY-0R-F3u" secondAttribute="leading" id="w2Z-xv-Fwz"/>
</constraints>
</view>
<connections>
<outlet property="browseTabView" destination="ARv-cj-xlz" id="h93-fi-yY7"/>
</connections>
</viewController>
</objects>
<point key="canvasLocation" x="1436" y="238"/>
</scene> </scene>
<!--General--> <!--General-->
<scene sceneID="xTC-Y5-Agk"> <scene sceneID="xTC-Y5-Agk">
@ -509,7 +559,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wPm-sJ-e9E"> <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wPm-sJ-e9E">
<rect key="frame" x="162" y="58" width="184" height="22"/> <rect key="frame" x="162" y="57" width="184" height="21"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="left" placeholderString="127.0.0.1" drawsBackground="YES" id="MSX-mn-2ma"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="left" placeholderString="127.0.0.1" drawsBackground="YES" id="MSX-mn-2ma">
<font key="font" usesAppearanceFont="YES"/> <font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -520,7 +570,7 @@
</connections> </connections>
</textField> </textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IbX-oV-soD"> <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IbX-oV-soD">
<rect key="frame" x="162" y="26" width="80" height="22"/> <rect key="frame" x="162" y="26" width="80" height="21"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="80" id="vW2-G6-2vi"/> <constraint firstAttribute="width" constant="80" id="vW2-G6-2vi"/>
</constraints> </constraints>
@ -538,7 +588,7 @@
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kvB-99-zwY"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kvB-99-zwY">
<rect key="frame" x="76" y="62" width="80" height="17"/> <rect key="frame" x="76" y="61" width="80" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Host:" id="AVi-g9-Irz"> <textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Host:" id="AVi-g9-Irz">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -546,7 +596,7 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AU9-wN-kbU"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AU9-wN-kbU">
<rect key="frame" x="78" y="30" width="76" height="17"/> <rect key="frame" x="78" y="30" width="76" height="16"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="72" id="Of6-Ls-knP"/> <constraint firstAttribute="width" constant="72" id="Of6-Ls-knP"/>
</constraints> </constraints>
@ -576,7 +626,7 @@
</viewController> </viewController>
<customObject id="lzf-yO-5pP" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="lzf-yO-5pP" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="1574" y="69"/> <point key="canvasLocation" x="1407" y="-501"/>
</scene> </scene>
<!--Queue View Controller--> <!--Queue View Controller-->
<scene sceneID="QcX-dC-cTZ"> <scene sceneID="QcX-dC-cTZ">
@ -646,7 +696,7 @@
<imageCell key="cell" refusesFirstResponder="YES" imageScaling="proportionallyDown" image="playButton" id="ckK-gW-Vhx"/> <imageCell key="cell" refusesFirstResponder="YES" imageScaling="proportionallyDown" image="playButton" id="ckK-gW-Vhx"/>
</imageView> </imageView>
<textField identifier="queuePosition" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mc4-Xr-oUl" userLabel="Queue Position View"> <textField identifier="queuePosition" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mc4-Xr-oUl" userLabel="Queue Position View">
<rect key="frame" x="4" y="0.0" width="33" height="17"/> <rect key="frame" x="4" y="1" width="33" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="888." id="wpZ-1t-Do7"> <textFieldCell key="cell" lineBreakMode="clipping" title="888." id="wpZ-1t-Do7">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -654,7 +704,7 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ" userLabel="Song Title View"> <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ" userLabel="Song Title View">
<rect key="frame" x="40" y="0.0" width="160" height="17"/> <rect key="frame" x="40" y="1" width="160" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="ei8-1e-ErK"> <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="ei8-1e-ErK">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -699,7 +749,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tBe-Q9-3Rw"> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tBe-Q9-3Rw">
<rect key="frame" x="0.0" y="0.0" width="127" height="17"/> <rect key="frame" x="0.0" y="1" width="127" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Ceb-ec-ydU"> <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Ceb-ec-ydU">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -757,9 +807,6 @@
<constraint firstAttribute="width" secondItem="iUb-eV-Qws" secondAttribute="height" multiplier="1:1" id="f4L-V4-5x2"/> <constraint firstAttribute="width" secondItem="iUb-eV-Qws" secondAttribute="height" multiplier="1:1" id="f4L-V4-5x2"/>
<constraint firstItem="Dw3-M5-tWY" firstAttribute="top" secondItem="iUb-eV-Qws" secondAttribute="top" id="ue4-Gb-CaX"/> <constraint firstItem="Dw3-M5-tWY" firstAttribute="top" secondItem="iUb-eV-Qws" secondAttribute="top" id="ue4-Gb-CaX"/>
</constraints> </constraints>
<shadow key="shadow">
<color key="color" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</shadow>
</customView> </customView>
</subviews> </subviews>
<holdingPriorities> <holdingPriorities>
@ -790,7 +837,7 @@
</items> </items>
</menu> </menu>
</objects> </objects>
<point key="canvasLocation" x="796" y="848"/> <point key="canvasLocation" x="825" y="746"/>
</scene> </scene>
<!--Album View Controller--> <!--Album View Controller-->
<scene sceneID="7Ua-Hj-zWt"> <scene sceneID="7Ua-Hj-zWt">
@ -806,10 +853,10 @@
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/> <rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<collectionView identifier="albumCollectionView" id="lfq-AB-epE"> <collectionView identifier="albumCollectionView" selectable="YES" id="lfq-AB-epE">
<rect key="frame" x="0.0" y="0.0" width="450" height="158"/> <rect key="frame" x="0.0" y="0.0" width="450" height="158"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
<collectionViewLayout key="collectionViewLayout" id="YE8-sD-l5P" customClass="AlbumViewLayout" customModule="Persephone" customModuleProvider="target"/> <collectionViewLayout key="collectionViewLayout" id="YE8-sD-l5P" customClass="FlexibleGridViewLayout" customModule="Persephone" customModuleProvider="target"/>
<color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<connections> <connections>
<outlet property="delegate" destination="gPn-fP-LFc" id="LQ2-Vl-r08"/> <outlet property="delegate" destination="gPn-fP-LFc" id="LQ2-Vl-r08"/>
@ -852,7 +899,7 @@
</items> </items>
</menu> </menu>
</objects> </objects>
<point key="canvasLocation" x="329" y="1370"/> <point key="canvasLocation" x="2038" y="87"/>
</scene> </scene>
</scenes> </scenes>
<resources> <resources>

View File

@ -27,6 +27,8 @@ class WindowController: NSWindowController {
@IBOutlet var shuffleState: NSButton! @IBOutlet var shuffleState: NSButton!
@IBOutlet var repeatState: NSButton! @IBOutlet var repeatState: NSButton!
@IBOutlet var browseViewControls: NSSegmentedControl!
override func windowDidLoad() { override func windowDidLoad() {
super.windowDidLoad() super.windowDidLoad()
window?.titleVisibility = .hidden window?.titleVisibility = .hidden
@ -159,7 +161,6 @@ class WindowController: NSWindowController {
@IBAction func handleRepeatButton(_ sender: NSButton) { @IBAction func handleRepeatButton(_ sender: NSButton) {
App.mpdClient.setRepeatState(repeatState: sender.state == .on) App.mpdClient.setRepeatState(repeatState: sender.state == .on)
} }
} }
extension WindowController: NSWindowDelegate { extension WindowController: NSWindowDelegate {
@ -179,7 +180,7 @@ extension WindowController: NSWindowDelegate {
extension WindowController: StoreSubscriber { extension WindowController: StoreSubscriber {
typealias StoreSubscriberStateType = (playerState: PlayerState, uiState: UIState) typealias StoreSubscriberStateType = (playerState: PlayerState, uiState: UIState)
func newState(state: (playerState: PlayerState, uiState: UIState)) { func newState(state: StoreSubscriberStateType) {
DispatchQueue.main.async { DispatchQueue.main.async {
self.setTransportControlState(state.playerState) self.setTransportControlState(state.playerState)
self.setShuffleRepeatState(state.playerState) self.setShuffleRepeatState(state.playerState)

View File

@ -1,29 +0,0 @@
//
// Notification.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/08.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
extension Notification {
static let didConnect = Name("MPDClientDidConnect")
static let willDisconnect = Name("MPDClientWillDisconnect")
static let stateChanged = Name("MPDClientStateChanged")
static let timeChanged = Name("MPDClientTimeChanged")
static let databaseUpdateStarted = Name("MPDClientDatabaseUpdateStarted")
static let databaseUpdateFinished = Name("MPDClientDatabaseUpdateFinished")
static let queueChanged = Name("MPDClientQueueChanged")
static let queuePosChanged = Name("MPDClientQueuePosChanged")
static let loadedAlbums = Name("MPDClientLoadedAlbums")
static let stateKey = "state"
static let queueKey = "queue"
static let queuePosKey = "song"
static let albumsKey = "albums"
static let totalTimeKey = "totalTime"
static let elapsedTimeMsKey = "elapsedTimeMs"
}

View File

@ -101,16 +101,7 @@ extension MPDClient {
func albumSongs(for album: MPDAlbum, callback: ([MPDSong]) -> Void) { func albumSongs(for album: MPDAlbum, callback: ([MPDSong]) -> Void) {
guard isConnected else { return } guard isConnected else { return }
var songs: [MPDSong] = [] let songs = searchSongs([MPDTag.album: album.title, MPDTag.artist: album.artist])
mpd_search_db_songs(self.connection, true)
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title)
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist)
mpd_search_commit(self.connection)
while let song = mpd_recv_song(self.connection) {
songs.append(MPDSong(song))
}
callback(songs) callback(songs)
} }

View File

@ -0,0 +1,38 @@
//
// MPDClient+Artist.swift
// Persephone
//
// Created by Daniel Barber on 2019/9/29.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import mpdclient
extension MPDClient {
func fetchAllArtists() {
enqueueCommand(command: .fetchAllArtists)
}
func allArtists() {
var artists: [String] = []
mpd_search_db_tags(self.connection, MPD_TAG_ALBUM_ARTIST)
mpd_search_commit(self.connection)
while let pair = mpd_recv_pair(self.connection) {
let pair = MPDPair(pair)
switch pair.name {
case "AlbumArtist":
artists.append(pair.value)
default:
break
}
mpd_return_pair(self.connection, pair.pair)
}
self.delegate?.didLoadArtists(mpdClient: self, artists: artists)
}
}

View File

@ -81,6 +81,16 @@ extension MPDClient {
else { return } else { return }
sendAddSongToQueue(uri: songUri, at: queuePos) sendAddSongToQueue(uri: songUri, at: queuePos)
case .addAlbumToQueue:
guard let album = userData["album"] as? MPDAlbum,
let queuePos = userData["queuePos"] as? Int
else { return }
sendAddAlbumToQueue(album: album, at: queuePos)
// Artist commands
case .fetchAllArtists:
allArtists()
// Album commands // Album commands
case .fetchAllAlbums: case .fetchAllAlbums:
allAlbums() allAlbums()
@ -101,6 +111,7 @@ extension MPDClient {
albumSongs(for: album, callback: callback) albumSongs(for: album, callback: callback)
} }
} }
func enqueueCommand( func enqueueCommand(

View File

@ -10,8 +10,8 @@ import Foundation
import mpdclient import mpdclient
extension MPDClient { extension MPDClient {
func connect(host: String, port: Int) { func makeConnectionOperation(host: String, port: Int) -> BlockOperation {
commandQueue.addOperation { [unowned self] in BlockOperation { [unowned self] in
guard let connection = mpd_connection_new(host, UInt32(port), 10000), guard let connection = mpd_connection_new(host, UInt32(port), 10000),
mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS
else { return } else { return }
@ -24,17 +24,17 @@ extension MPDClient {
self.connection = connection self.connection = connection
self.status = MPDStatus(status) self.status = MPDStatus(status)
self.fetchQueue()
self.fetchAllAlbums()
self.idle()
self.delegate?.didConnect(mpdClient: self) self.delegate?.didConnect(mpdClient: self)
self.delegate?.didUpdateStatus(mpdClient: self, status: self.status!) self.delegate?.didUpdateStatus(mpdClient: self, status: self.status!)
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song) self.idle()
} }
} }
func connect() {
commandQueue.addOperation(connectionOperation)
}
func disconnect() { func disconnect() {
guard isConnected else { return } guard isConnected else { return }

View File

@ -11,7 +11,7 @@ import mpdclient
extension MPDClient { extension MPDClient {
func fetchQueue() { func fetchQueue() {
sendCommand(command: .fetchQueue) enqueueCommand(command: .fetchQueue)
} }
func clearQueue() { func clearQueue() {
@ -38,6 +38,10 @@ extension MPDClient {
enqueueCommand(command: .addSongToQueue, userData: ["uri": songUri, "queuePos": queuePos]) enqueueCommand(command: .addSongToQueue, userData: ["uri": songUri, "queuePos": queuePos])
} }
func addAlbumToQueue(album: MPDAlbum, at queuePos: Int) {
enqueueCommand(command: .addAlbumToQueue, userData: ["album": album, "queuePos": queuePos])
}
func sendPlayTrack(at queuePos: Int) { func sendPlayTrack(at queuePos: Int) {
mpd_run_play_pos(self.connection, UInt32(queuePos)) mpd_run_play_pos(self.connection, UInt32(queuePos))
} }
@ -50,6 +54,8 @@ extension MPDClient {
let song = MPDSong(mpdSong) let song = MPDSong(mpdSong)
self.queue.append(song) self.queue.append(song)
} }
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
} }
func sendClearQueue() { func sendClearQueue() {
@ -80,4 +86,16 @@ extension MPDClient {
func sendAddSongToQueue(uri: String, at queuePos: Int) { func sendAddSongToQueue(uri: String, at queuePos: Int) {
mpd_run_add_id_to(self.connection, uri, UInt32(queuePos)) mpd_run_add_id_to(self.connection, uri, UInt32(queuePos))
} }
func sendAddAlbumToQueue(album: MPDAlbum, at queuePos: Int) {
let songs = searchSongs([MPDTag.album: album.title, MPDTag.artist: album.artist])
var insertPos = UInt32(queuePos)
for song in songs {
mpd_run_add_id_to(self.connection, song.uri, insertPos)
insertPos += 1
}
}
} }

View File

@ -0,0 +1,28 @@
//
// MPDClient+Songs.swift
// Persephone
//
// Created by Daniel Barber on 2019/7/19.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import mpdclient
extension MPDClient {
func searchSongs(_ terms: [MPDClient.MPDTag: String]) -> [MPDSong] {
var songs: [MPDSong] = []
mpd_search_db_songs(self.connection, true)
for (tag, term) in terms {
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, tag.mpdTag(), term)
}
mpd_search_commit(self.connection)
while let song = mpd_recv_song(self.connection) {
songs.append(MPDSong(song))
}
return songs
}
}

View File

@ -10,6 +10,8 @@ import Foundation
import mpdclient import mpdclient
class MPDClient { class MPDClient {
var connectionOperation: BlockOperation!
var delegate: MPDClientDelegate? var delegate: MPDClientDelegate?
var connection: OpaquePointer? var connection: OpaquePointer?
@ -20,8 +22,9 @@ class MPDClient {
let commandQueue = OperationQueue() let commandQueue = OperationQueue()
init(withDelegate delegate: MPDClientDelegate?) { init(host: String, port: Int, withDelegate delegate: MPDClientDelegate?) {
commandQueue.maxConcurrentOperationCount = 1 commandQueue.maxConcurrentOperationCount = 1
self.delegate = delegate self.delegate = delegate
self.connectionOperation = makeConnectionOperation(host: host, port: port)
} }
} }

View File

@ -35,6 +35,10 @@ extension MPDClient {
case removeSong case removeSong
case moveSongInQueue case moveSongInQueue
case addSongToQueue case addSongToQueue
case addAlbumToQueue
// Artist commands
case fetchAllArtists
// Album commands // Album commands
case fetchAllAlbums case fetchAllAlbums

View File

@ -13,26 +13,6 @@ extension MPDClient {
class MPDSong { class MPDSong {
let song: OpaquePointer let song: OpaquePointer
enum TagType: Int {
case unknown = -1
case artist, album, albumArtist, title, track, name,
genre, date, composer, performer, comment, disc
case musicBrainzArtistId
case musicBrainzAlbumId
case musicBrainzAlbumArtistId
case musicBrainzTrackId
case musicBrainzReleaseTrackId
case originalDate
case artistSort
case albumArtistSort
case albumSort
case tagCount
}
init(_ song: OpaquePointer) { init(_ song: OpaquePointer) {
self.song = song self.song = song
} }
@ -68,10 +48,12 @@ extension MPDClient {
} }
} }
func getTag(_ tagType: TagType) -> String { var date: String {
let mpdTagType = mpd_tag_type(rawValue: Int32(tagType.rawValue)) return getTag(.date)
}
guard let tag = mpd_song_get_tag(song, mpdTagType, 0) func getTag(_ tagType: MPDTag) -> String {
guard let tag = mpd_song_get_tag(song, tagType.mpdTag(), 0)
else { return "" } else { return "" }
return String(cString: tag) return String(cString: tag)

View File

@ -0,0 +1,36 @@
//
// MPDTag.swift
// Persephone
//
// Created by Daniel Barber on 2019/7/19.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import mpdclient
extension MPDClient {
enum MPDTag: Int {
case unknown = -1
case artist, album, albumArtist, title, track, name,
genre, date, composer, performer, comment, disc
case musicBrainzArtistId
case musicBrainzAlbumId
case musicBrainzAlbumArtistId
case musicBrainzTrackId
case musicBrainzReleaseTrackId
case originalDate
case artistSort
case albumArtistSort
case albumSort
case tagCount
func mpdTag() -> mpd_tag_type {
return mpd_tag_type(rawValue: Int32(self.rawValue))
}
}
}

View File

@ -21,4 +21,6 @@ protocol MPDClientDelegate {
func didUpdateQueuePos(mpdClient: MPDClient, song: Int) func didUpdateQueuePos(mpdClient: MPDClient, song: Int)
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum]) func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum])
func didLoadArtists(mpdClient: MPDClient, artists: [String])
} }

View File

@ -12,6 +12,7 @@ import CryptoSwift
struct Album { struct Album {
var mpdAlbum: MPDClient.MPDAlbum var mpdAlbum: MPDClient.MPDAlbum
var coverArt: Loading<NSImage?> = .notLoaded var coverArt: Loading<NSImage?> = .notLoaded
var metadata: Metadata?
init(mpdAlbum: MPDClient.MPDAlbum) { init(mpdAlbum: MPDClient.MPDAlbum) {
self.mpdAlbum = mpdAlbum self.mpdAlbum = mpdAlbum

View File

@ -0,0 +1,24 @@
//
// Artist.swift
// Persephone
//
// Created by Daniel Barber on 2019/10/04.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
struct Artist {
var name: String
var image: Loading<NSImage?> = .notLoaded
init(name: String) {
self.name = name
}
}
extension Artist: Equatable {
static func == (lhs: Artist, rhs: Artist) -> Bool {
return (lhs.name == rhs.name)
}
}

View File

@ -0,0 +1,19 @@
//
// DraggedAlbum.swift
// Persephone
//
// Created by Daniel Barber on 2019/6/28.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import CryptoSwift
struct DraggedAlbum: Codable {
var title: String
var artist: String
var hash: String {
return "\(title) - \(artist)".sha1()
}
}

View File

@ -0,0 +1,13 @@
//
// Metadata.swift
// Persephone
//
// Created by Daniel Barber on 2019/10/11.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
struct Metadata {
var date: String?
}

View File

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target">
<connections>
<outlet property="albumArtist" destination="5Uu-j1-qyT" id="2Et-tX-InT"/>
<outlet property="albumCoverView" destination="Kfb-8f-ean" id="CXx-gB-gz8"/>
<outlet property="albumTitle" destination="KEh-NL-c2W" id="SI3-hm-H2B"/>
<outlet property="imageView" destination="Kfb-8f-ean" id="Ur0-hX-wJm"/>
<outlet property="view" destination="Hz6-mo-xeY" id="v7W-XA-Emc"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY" customClass="AlbumItemView" customModule="Persephone" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="128" height="167"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField identifier="albumTitle" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEh-NL-c2W">
<rect key="frame" x="-2" y="18" width="132" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="pDs-0t-e1j">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="albumArtist" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5Uu-j1-qyT">
<rect key="frame" x="-2" y="0.0" width="132" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="yZn-e9-zyP">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView identifier="albumArtwork" horizontalHuggingPriority="750" verticalHuggingPriority="750" placeholderIntrinsicWidth="128" placeholderIntrinsicHeight="128" translatesAutoresizingMaskIntoConstraints="NO" id="Kfb-8f-ean">
<rect key="frame" x="0.0" y="39" width="128" height="128"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="defaultCoverArt" id="FsA-JX-BFh"/>
</imageView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="x5e-56-uVO" userLabel="Album Detail Button">
<rect key="frame" x="0.0" y="39" width="128" height="128"/>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="MTh-fn-aCH">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showAlbumDetail:" target="-2" id="nO1-4H-LHS"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="x5e-56-uVO" firstAttribute="leading" secondItem="Kfb-8f-ean" secondAttribute="leading" id="1Hi-Uk-rkL"/>
<constraint firstItem="5Uu-j1-qyT" firstAttribute="trailing" secondItem="KEh-NL-c2W" secondAttribute="trailing" id="64z-uz-4nY"/>
<constraint firstAttribute="bottom" secondItem="KEh-NL-c2W" secondAttribute="bottom" constant="18" id="8Kg-1r-wNp"/>
<constraint firstItem="x5e-56-uVO" firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="BYd-Fg-DVb"/>
<constraint firstItem="Kfb-8f-ean" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="JMi-4i-dgs"/>
<constraint firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="KQC-Wz-Bsg"/>
<constraint firstItem="5Uu-j1-qyT" firstAttribute="leading" secondItem="KEh-NL-c2W" secondAttribute="leading" id="MUo-0i-fX9"/>
<constraint firstItem="Kfb-8f-ean" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="Qbk-jx-zAi"/>
<constraint firstItem="KEh-NL-c2W" firstAttribute="trailing" secondItem="Kfb-8f-ean" secondAttribute="trailing" id="U0w-G4-ggX"/>
<constraint firstItem="KEh-NL-c2W" firstAttribute="leading" secondItem="Kfb-8f-ean" secondAttribute="leading" id="V8r-Rc-Dx7"/>
<constraint firstAttribute="bottom" secondItem="5Uu-j1-qyT" secondAttribute="bottom" id="gci-4h-pDZ"/>
<constraint firstItem="x5e-56-uVO" firstAttribute="top" secondItem="Kfb-8f-ean" secondAttribute="top" id="hw2-ik-6VW"/>
<constraint firstItem="x5e-56-uVO" firstAttribute="bottom" secondItem="Kfb-8f-ean" secondAttribute="bottom" id="iVQ-Vn-dSV"/>
<constraint firstAttribute="bottom" secondItem="Kfb-8f-ean" secondAttribute="bottom" constant="39" id="sid-zJ-YMA"/>
</constraints>
<connections>
<outlet property="imageView" destination="Kfb-8f-ean" id="T7Z-En-dU3"/>
</connections>
<point key="canvasLocation" x="-22" y="125.5"/>
</customView>
<collectionViewItem id="Qgu-aI-55A" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target"/>
</objects>
<resources>
<image name="defaultCoverArt" width="128" height="128"/>
</resources>
</document>

View File

@ -16,6 +16,11 @@ struct UpdateCoverArtAction: Action {
var albumIndex: Int var albumIndex: Int
} }
struct UpdateAlbumMetaData: Action {
var metadata: Metadata
var albumIndex: Int
}
struct UpdateAlbumListAction: Action { struct UpdateAlbumListAction: Action {
var albums: [MPDClient.MPDAlbum] var albums: [MPDClient.MPDAlbum]
} }

View File

@ -0,0 +1,14 @@
//
// ArtistListActions.swift
// Persephone
//
// Created by Daniel Barber on 2019/9/29.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import ReSwift
struct UpdateArtistListAction: Action {
var artists: [String]
}

View File

@ -12,6 +12,7 @@ struct AppState: StateType {
var playerState = PlayerState() var playerState = PlayerState()
var queueState = QueueState() var queueState = QueueState()
var albumListState = AlbumListState() var albumListState = AlbumListState()
var artistListState = ArtistListState()
var preferencesState = PreferencesState() var preferencesState = PreferencesState()
var uiState = UIState() var uiState = UIState()
} }

View File

@ -0,0 +1,13 @@
//
// ArtistListState.swift
// Persephone
//
// Created by Daniel Barber on 2019/9/20.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import ReSwift
struct ArtistListState: StateType, Equatable {
var artists: [Artist] = []
}

View File

@ -18,6 +18,9 @@ func albumListReducer(action: Action, state: AlbumListState?) -> AlbumListState
case let action as UpdateCoverArtAction: case let action as UpdateCoverArtAction:
state.albums[action.albumIndex].coverArt = .loaded(action.coverArt) state.albums[action.albumIndex].coverArt = .loaded(action.coverArt)
case let action as UpdateAlbumMetaData:
state.albums[action.albumIndex].metadata = action.metadata
case is ResetAlbumListCoverArtAction: case is ResetAlbumListCoverArtAction:
state.albums = state.albums.map { state.albums = state.albums.map {
var album = $0 var album = $0

View File

@ -13,6 +13,7 @@ func appReducer(action: Action, state: AppState?) -> AppState {
playerState: playerReducer(action: action, state: state?.playerState), playerState: playerReducer(action: action, state: state?.playerState),
queueState: queueReducer(action: action, state: state?.queueState), queueState: queueReducer(action: action, state: state?.queueState),
albumListState: albumListReducer(action: action, state: state?.albumListState), albumListState: albumListReducer(action: action, state: state?.albumListState),
artistListState: artistListReducer(action: action, state: state?.artistListState),
preferencesState: preferencesReducer(action: action, state: state?.preferencesState), preferencesState: preferencesReducer(action: action, state: state?.preferencesState),
uiState: uiReducer(action: action, state: state?.uiState) uiState: uiReducer(action: action, state: state?.uiState)
) )

View File

@ -0,0 +1,25 @@
//
// ArtistReducer.swift
// Persephone
//
// Created by Daniel Barber on 2019/9/29.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import ReSwift
func artistListReducer(action: Action, state: ArtistListState?) -> ArtistListState {
var state = state ?? ArtistListState()
switch action {
case let action as UpdateArtistListAction:
state.artists = action.artists.map { Artist(name: $0) }
default:
break
}
return state
}

View File

@ -31,6 +31,9 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
App.store.dispatch( App.store.dispatch(
UpdateQueuePlayerStateAction(state: state.state) UpdateQueuePlayerStateAction(state: state.state)
) )
if let queuePos = state.status?.song {
App.store.dispatch(UpdateQueuePosAction(queuePos: queuePos))
}
} }
case let action as UpdateCurrentSongAction: case let action as UpdateCurrentSongAction:

View File

@ -1,17 +0,0 @@
//
// AlbumItemView.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/17.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
class AlbumItemView: NSView {
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
}
@IBOutlet var imageView: NSImageView!
}