diff --git a/Mac/Components/Browser/Album Detail/AlbumTracksDataSource.swift b/Mac/Components/Browser/Album Detail/AlbumTracksDataSource.swift index 59b74d0..2589d02 100644 --- a/Mac/Components/Browser/Album Detail/AlbumTracksDataSource.swift +++ b/Mac/Components/Browser/Album Detail/AlbumTracksDataSource.swift @@ -9,21 +9,6 @@ import AppKit class AlbumTracksDataSource: NSObject, NSTableViewDataSource { - struct AlbumSongItem { - let disc: String? - let song: Song? - - init(song: Song) { - self.disc = nil - self.song = song - } - - init(disc: String) { - self.disc = disc - self.song = nil - } - } - var albumSongs: [AlbumSongItem] = [] var showSongArtist: Bool = false diff --git a/Mac/Components/Shared/Extensions/CGColor.swift b/Mac/Components/Shared/Extensions/CGColor.swift index 3e508c5..27d627c 100644 --- a/Mac/Components/Shared/Extensions/CGColor.swift +++ b/Mac/Components/Shared/Extensions/CGColor.swift @@ -6,9 +6,9 @@ // Copyright © 2019 Dan Barber. All rights reserved. // -import AppKit +import CoreGraphics extension CGColor { - static let albumBorderColorLight = NSColor.black.withAlphaComponent(0.15).cgColor - static let albumBorderColorDark = NSColor.white.withAlphaComponent(0.15).cgColor + static let albumBorderColorLight = CGColor.black.copy(alpha: 0.15) + static let albumBorderColorDark = CGColor.white.copy(alpha: 0.15) } diff --git a/Mac/Components/Window/Base.lproj/Main.storyboard b/Mac/Components/Window/Base.lproj/Main.storyboard index 4ab71d9..4448111 100644 --- a/Mac/Components/Window/Base.lproj/Main.storyboard +++ b/Mac/Components/Window/Base.lproj/Main.storyboard @@ -298,7 +298,7 @@ - + @@ -320,7 +320,7 @@ - + @@ -347,7 +347,7 @@ - + @@ -369,7 +369,7 @@ - + @@ -385,7 +385,7 @@ - + @@ -398,7 +398,7 @@ - + @@ -414,7 +414,7 @@ - + @@ -516,7 +516,7 @@ - + @@ -554,7 +554,7 @@ - + @@ -566,7 +566,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -637,7 +637,7 @@ - + @@ -651,7 +651,7 @@ - + @@ -677,7 +677,7 @@ - + @@ -712,7 +712,7 @@ - + @@ -757,7 +757,7 @@ - + @@ -778,7 +778,7 @@ - + @@ -805,7 +805,7 @@ - + @@ -818,7 +818,7 @@ - + diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 63ff64e..5de1286 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 38BAC36B249CB1A7004BAEA4 /* AlbumDetailSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BAC36A249CB1A6004BAEA4 /* AlbumDetailSongTitleView.swift */; }; + E403E63E246F4C3900200F58 /* NowPlayingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E403E63C246F4C3900200F58 /* NowPlayingViewController.swift */; }; + E403E63F246F4C3900200F58 /* NowPlayingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E403E63D246F4C3900200F58 /* NowPlayingViewController.xib */; }; E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E407861B2110CE6E006887B1 /* AppDelegate.swift */; }; E40786202110CE70006887B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E407861F2110CE70006887B1 /* Assets.xcassets */; }; E40786232110CE70006887B1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E40786212110CE70006887B1 /* Main.storyboard */; }; @@ -19,6 +21,25 @@ E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C1220E134F0006D9BE /* AlbumViewController.swift */; }; E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */; }; E40FE71B221B904300A4223F /* NSEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40FE71A221B904300A4223F /* NSEvent.swift */; }; + E41222112431425400473C1D /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41221FD2431425400473C1D /* App.swift */; }; + E41222122431425400473C1D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41221FE2431425400473C1D /* AppDelegate.swift */; }; + E41222132431425400473C1D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E41221FF2431425400473C1D /* LaunchScreen.storyboard */; }; + E41222142431425400473C1D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E41222012431425400473C1D /* Main.storyboard */; }; + E41222162431425400473C1D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E41222042431425400473C1D /* Assets.xcassets */; }; + E41222172431425400473C1D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41222052431425400473C1D /* SceneDelegate.swift */; }; + E41222182431425400473C1D /* AlbumItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41222092431425400473C1D /* AlbumItemCell.swift */; }; + E41222192431425400473C1D /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E412220A2431425400473C1D /* AlbumViewController.swift */; }; + E412221A2431425400473C1D /* AlbumDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E412220B2431425400473C1D /* AlbumDataSource.swift */; }; + E412221B2431425400473C1D /* AlbumViewController+UICollectionViewDelegateFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E412220C2431425400473C1D /* AlbumViewController+UICollectionViewDelegateFlowLayout.swift */; }; + E412221C2431431500473C1D /* Persephone_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41221F72431425300473C1D /* Persephone_iOSTests.swift */; }; + E412221F2431432100473C1D /* Persephone_iOSUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41221FB2431425300473C1D /* Persephone_iOSUITests.swift */; }; + E41222232431530900473C1D /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = E41222222431530900473C1D /* Kingfisher */; }; + E41222242431535E00473C1D /* MPDAlbumArtImageDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */; }; + E4122228243153B200473C1D /* CGSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41222272431539800473C1D /* CGSize.swift */; }; + E41222292431555100473C1D /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; }; + E41222302432B0A300473C1D /* AlbumTracksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E412222F2432B0A300473C1D /* AlbumTracksDataSource.swift */; }; + E41222322432B14000473C1D /* AlbumSongItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41222312432B14000473C1D /* AlbumSongItem.swift */; }; + E41222332432B15100473C1D /* AlbumSongItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41222312432B14000473C1D /* AlbumSongItem.swift */; }; E419E2872249B96600216A8C /* Song.swift in Sources */ = {isa = PBXBuildFile; fileRef = E419E2862249B96600216A8C /* Song.swift */; }; E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; }; E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FC223BF87300173814 /* MPDClient+Connection.swift */; }; @@ -250,7 +271,6 @@ E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39C22B9CF0000CA8CBD /* NSView.swift */; }; E489E3A422B9D31800CA8CBD /* DraggedSongView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */; }; E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */; }; - E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; }; E49A5482233E580800EED353 /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5481233E580800EED353 /* PromiseKit */; }; E49A5485233E5ADC00EED353 /* Differ in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5484233E5ADC00EED353 /* Differ */; }; E49A5488233E5B0000EED353 /* ReSwift in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5487233E5B0000EED353 /* ReSwift */; }; @@ -275,11 +295,17 @@ E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBD2275EDAA0075461B /* PlayerActions.swift */; }; E4B11BC02275EE150075461B /* QueueActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBF2275EE150075461B /* QueueActions.swift */; }; E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; }; + E4B28EE02436D56E003B28AE /* AlbumDetailFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B28EDE2436D548003B28AE /* AlbumDetailFooterView.swift */; }; + E4B28EE224379606003B28AE /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B28EE124379606003B28AE /* CGColor.swift */; }; + E4B3B3642432DB7A007E25D2 /* AlbumSongCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B3B3622432DA4B007E25D2 /* AlbumSongCell.swift */; }; + E4B3B3672432DF1B007E25D2 /* AlbumSongListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B3B3652432DEDB007E25D2 /* AlbumSongListViewController.swift */; }; E4B3DF6523D66A4400728F6B /* QueueSongCoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */; }; E4B46F8F2402E89800152157 /* MPDError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B46F8E2402E89800152157 /* MPDError.swift */; }; E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */; }; E4BB7F8F23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */; }; E4BB7F9323E9150A00906E2F /* CoverArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB7F9223E9150A00906E2F /* CoverArtService.swift */; }; + E4C51F08243422380093FB31 /* AlbumDiscCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C51F07243422380093FB31 /* AlbumDiscCell.swift */; }; + E4C51F0A243428B60093FB31 /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C51F09243428B60093FB31 /* UIFont.swift */; }; E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.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 */; }; @@ -296,6 +322,9 @@ E4F2EFEE24076A2700198159 /* ServerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F2EFED24076A2700198159 /* ServerState.swift */; }; E4F2EFF024076B0900198159 /* ServerActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F2EFEF24076B0900198159 /* ServerActions.swift */; }; E4F2EFF224076B5E00198159 /* ServerReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F2EFF124076B5E00198159 /* ServerReducer.swift */; }; + E4F365D224942CC7006A8C4A /* NowPlayingTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F365D024942C6A006A8C4A /* NowPlayingTabBarController.swift */; }; + E4F365D4249432C0006A8C4A /* NowPlayingTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F365D3249432C0006A8C4A /* NowPlayingTabBar.swift */; }; + E4F365D6249C4CDC006A8C4A /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F365D5249C4CDC006A8C4A /* UIImage.swift */; }; E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */; }; E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B462221E125900ACF42A /* QueueItem.swift */; }; E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B466221E233200ACF42A /* AlbumDataSource.swift */; }; @@ -370,6 +399,8 @@ /* Begin PBXFileReference section */ 38BAC36A249CB1A6004BAEA4 /* AlbumDetailSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongTitleView.swift; sourceTree = ""; }; + E403E63C246F4C3900200F58 /* NowPlayingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingViewController.swift; sourceTree = ""; }; + E403E63D246F4C3900200F58 /* NowPlayingViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NowPlayingViewController.xib; sourceTree = ""; }; E40786182110CE6E006887B1 /* Persephone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Persephone.app; sourceTree = BUILT_PRODUCTS_DIR; }; E407861B2110CE6E006887B1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; E407861F2110CE70006887B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -391,6 +422,24 @@ E411C26D241C10F0008B9682 /* Persephone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Persephone.app; sourceTree = BUILT_PRODUCTS_DIR; }; E411C282241C10F5008B9682 /* Persephone-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Persephone-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; E411C28D241C10F5008B9682 /* Persephone-iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Persephone-iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + E41221F72431425300473C1D /* Persephone_iOSTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persephone_iOSTests.swift; sourceTree = ""; }; + E41221F82431425300473C1D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E41221FA2431425300473C1D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E41221FB2431425300473C1D /* Persephone_iOSUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persephone_iOSUITests.swift; sourceTree = ""; }; + E41221FD2431425400473C1D /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; + E41221FE2431425400473C1D /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + E41222002431425400473C1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + E41222022431425400473C1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + E41222032431425400473C1D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E41222042431425400473C1D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E41222052431425400473C1D /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + E41222092431425400473C1D /* AlbumItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumItemCell.swift; sourceTree = ""; }; + E412220A2431425400473C1D /* AlbumViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = ""; }; + E412220B2431425400473C1D /* AlbumDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumDataSource.swift; sourceTree = ""; }; + E412220C2431425400473C1D /* AlbumViewController+UICollectionViewDelegateFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AlbumViewController+UICollectionViewDelegateFlowLayout.swift"; sourceTree = ""; }; + E41222272431539800473C1D /* CGSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSize.swift; sourceTree = ""; }; + E412222F2432B0A300473C1D /* AlbumTracksDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumTracksDataSource.swift; sourceTree = ""; }; + E41222312432B14000473C1D /* AlbumSongItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumSongItem.swift; sourceTree = ""; }; E419E2862249B96600216A8C /* Song.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Song.swift; sourceTree = ""; }; E41B22C421FB715A00D544F6 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; E41B22C521FB932700D544F6 /* MPDClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDClient.swift; sourceTree = ""; }; @@ -613,11 +662,17 @@ E4B11BBD2275EDAA0075461B /* PlayerActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerActions.swift; sourceTree = ""; }; E4B11BBF2275EE150075461B /* QueueActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueActions.swift; sourceTree = ""; }; E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = ""; }; + E4B28EDE2436D548003B28AE /* AlbumDetailFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailFooterView.swift; sourceTree = ""; }; + E4B28EE124379606003B28AE /* CGColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = ""; }; + E4B3B3622432DA4B007E25D2 /* AlbumSongCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumSongCell.swift; sourceTree = ""; }; + E4B3B3652432DEDB007E25D2 /* AlbumSongListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumSongListViewController.swift; sourceTree = ""; }; E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongCoverView.swift; sourceTree = ""; }; E4B46F8E2402E89800152157 /* MPDError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDError.swift; sourceTree = ""; }; E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerDelegate.swift; sourceTree = ""; }; E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDAlbumArtImageDataProvider.swift; sourceTree = ""; }; E4BB7F9223E9150A00906E2F /* CoverArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtService.swift; sourceTree = ""; }; + E4C51F07243422380093FB31 /* AlbumDiscCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDiscCell.swift; sourceTree = ""; }; + E4C51F09243428B60093FB31 /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; }; E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = ""; }; E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueViewController+NSOutlineViewDelegate.swift"; sourceTree = ""; }; @@ -635,6 +690,9 @@ E4F2EFED24076A2700198159 /* ServerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerState.swift; sourceTree = ""; }; E4F2EFEF24076B0900198159 /* ServerActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerActions.swift; sourceTree = ""; }; E4F2EFF124076B5E00198159 /* ServerReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerReducer.swift; sourceTree = ""; }; + E4F365D024942C6A006A8C4A /* NowPlayingTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingTabBarController.swift; sourceTree = ""; }; + E4F365D3249432C0006A8C4A /* NowPlayingTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingTabBar.swift; sourceTree = ""; }; + E4F365D5249C4CDC006A8C4A /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueDataSource.swift; sourceTree = ""; }; E4F6B462221E125900ACF42A /* QueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueItem.swift; sourceTree = ""; }; E4F6B466221E233200ACF42A /* AlbumDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDataSource.swift; sourceTree = ""; }; @@ -678,6 +736,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E41222232431530900473C1D /* Kingfisher in Frameworks */, E480513824255CE000362CF3 /* ReSwift in Frameworks */, E480513A24255CF200362CF3 /* CryptoSwift in Frameworks */, ); @@ -705,6 +764,9 @@ children = ( E45E4FD822515D87004B537F /* Brewfile */, E42A8F3922176D6400A13ED9 /* LICENSE.md */, + E41221FC2431425300473C1D /* iOS */, + E41221F62431425300473C1D /* iOSTests */, + E41221F92431425300473C1D /* iOSUITests */, E407861A2110CE6E006887B1 /* Mac */, E407862D2110CE70006887B1 /* MacTests */, E40786382110CE70006887B1 /* MacUITests */, @@ -765,15 +827,15 @@ E408D3B7220DE8CC0006D9BE /* Extensions */ = { isa = PBXGroup; children = ( - E4928E0A2218D62A001D4BEA /* CGColor.swift */, + E4B28EE124379606003B28AE /* CGColor.swift */, E40FE71A221B904300A4223F /* NSEvent.swift */, E435E3E1221CD4E200184CFC /* NSFont.swift */, E435E3E3221CD75D00184CFC /* NSImage.swift */, - E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */, E489E39822B85D0400CA8CBD /* NSPasteboard.swift */, - E489E39C22B9CF0000CA8CBD /* NSView.swift */, E43AC1F022C68E6A001E483C /* NSPasteboardItem.swift */, E4DA820523D6236200C1EE58 /* NSSize.swift */, + E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */, + E489E39C22B9CF0000CA8CBD /* NSView.swift */, ); path = Extensions; sourceTree = ""; @@ -804,6 +866,7 @@ E411C268241C02B2008B9682 /* Shared */ = { isa = PBXGroup; children = ( + E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */, E483CE67242FEF82001F742E /* Delegates */, E48056C22426D73300362CF3 /* libmpdclient */, E48051512425607100362CF3 /* Controllers */, @@ -825,6 +888,100 @@ path = Extensions; sourceTree = ""; }; + E41221F62431425300473C1D /* iOSTests */ = { + isa = PBXGroup; + children = ( + E41221F82431425300473C1D /* Info.plist */, + E41221F72431425300473C1D /* Persephone_iOSTests.swift */, + ); + path = iOSTests; + sourceTree = ""; + }; + E41221F92431425300473C1D /* iOSUITests */ = { + isa = PBXGroup; + children = ( + E41221FA2431425300473C1D /* Info.plist */, + E41221FB2431425300473C1D /* Persephone_iOSUITests.swift */, + ); + path = iOSUITests; + sourceTree = ""; + }; + E41221FC2431425300473C1D /* iOS */ = { + isa = PBXGroup; + children = ( + E41221FD2431425400473C1D /* App.swift */, + E41221FE2431425400473C1D /* AppDelegate.swift */, + E41222042431425400473C1D /* Assets.xcassets */, + E41222062431425400473C1D /* Components */, + E41222032431425400473C1D /* Info.plist */, + E41221FF2431425400473C1D /* LaunchScreen.storyboard */, + E41222012431425400473C1D /* Main.storyboard */, + E41222052431425400473C1D /* SceneDelegate.swift */, + ); + path = iOS; + sourceTree = ""; + }; + E41222062431425400473C1D /* Components */ = { + isa = PBXGroup; + children = ( + E4D4FAE6246F4B1B00CD02AF /* Now Playing */, + E41222252431538700473C1D /* Shared */, + E41222072431425400473C1D /* Browser */, + ); + path = Components; + sourceTree = ""; + }; + E41222072431425400473C1D /* Browser */ = { + isa = PBXGroup; + children = ( + E412222A243182F700473C1D /* Album Detail */, + E41222082431425400473C1D /* Album Browser */, + ); + path = Browser; + sourceTree = ""; + }; + E41222082431425400473C1D /* Album Browser */ = { + isa = PBXGroup; + children = ( + E41222092431425400473C1D /* AlbumItemCell.swift */, + E412220A2431425400473C1D /* AlbumViewController.swift */, + E412220B2431425400473C1D /* AlbumDataSource.swift */, + E412220C2431425400473C1D /* AlbumViewController+UICollectionViewDelegateFlowLayout.swift */, + ); + path = "Album Browser"; + sourceTree = ""; + }; + E41222252431538700473C1D /* Shared */ = { + isa = PBXGroup; + children = ( + E41222262431539000473C1D /* Extensions */, + ); + path = Shared; + sourceTree = ""; + }; + E41222262431539000473C1D /* Extensions */ = { + isa = PBXGroup; + children = ( + E4928E0A2218D62A001D4BEA /* CGColor.swift */, + E41222272431539800473C1D /* CGSize.swift */, + E4C51F09243428B60093FB31 /* UIFont.swift */, + E4F365D5249C4CDC006A8C4A /* UIImage.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + E412222A243182F700473C1D /* Album Detail */ = { + isa = PBXGroup; + children = ( + E4C51F07243422380093FB31 /* AlbumDiscCell.swift */, + E4B3B3622432DA4B007E25D2 /* AlbumSongCell.swift */, + E4B3B3652432DEDB007E25D2 /* AlbumSongListViewController.swift */, + E412222F2432B0A300473C1D /* AlbumTracksDataSource.swift */, + E4B28EDE2436D548003B28AE /* AlbumDetailFooterView.swift */, + ); + path = "Album Detail"; + sourceTree = ""; + }; E41B22C721FB966C00D544F6 /* include */ = { isa = PBXGroup; children = ( @@ -929,10 +1086,10 @@ E442CCC82347D65300004E0C /* Album Detail */ = { isa = PBXGroup; children = ( - E43B67A922909793007DCF55 /* AlbumDetailView.xib */, E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */, E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */, E43B67A822909793007DCF55 /* AlbumDetailView.swift */, + E43B67A922909793007DCF55 /* AlbumDetailView.xib */, E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */, E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */, 38BAC36A249CB1A6004BAEA4 /* AlbumDetailSongTitleView.swift */, @@ -943,7 +1100,6 @@ E442CCC92347D6FD00004E0C /* Shared */ = { isa = PBXGroup; children = ( - E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */, E4E13C2C2350D8CB00092A6E /* Layouts */, E408D3B7220DE8CC0006D9BE /* Extensions */, E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */, @@ -1274,6 +1430,17 @@ path = Protocols; sourceTree = ""; }; + E4D4FAE6246F4B1B00CD02AF /* Now Playing */ = { + isa = PBXGroup; + children = ( + E403E63C246F4C3900200F58 /* NowPlayingViewController.swift */, + E403E63D246F4C3900200F58 /* NowPlayingViewController.xib */, + E4F365D024942C6A006A8C4A /* NowPlayingTabBarController.swift */, + E4F365D3249432C0006A8C4A /* NowPlayingTabBar.swift */, + ); + path = "Now Playing"; + sourceTree = ""; + }; E4E13C2C2350D8CB00092A6E /* Layouts */ = { isa = PBXGroup; children = ( @@ -1286,6 +1453,7 @@ isa = PBXGroup; children = ( E450AD7D222620A10091BED3 /* Album.swift */, + E41222312432B14000473C1D /* AlbumSongItem.swift */, E43AC1F422C6A4F4001E483C /* DraggedAlbum.swift */, E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */, E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */, @@ -1385,6 +1553,7 @@ packageProductDependencies = ( E480513724255CE000362CF3 /* ReSwift */, E480513924255CF200362CF3 /* CryptoSwift */, + E41222222431530900473C1D /* Kingfisher */, ); productName = "Persephone-iOS"; productReference = E411C26D241C10F0008B9682 /* Persephone.app */; @@ -1546,12 +1715,16 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + E41222132431425400473C1D /* LaunchScreen.storyboard in Resources */, + E41222142431425400473C1D /* Main.storyboard in Resources */, E48059112426D73600362CF3 /* win64.txt in Resources */, E480590F2426D73600362CF3 /* configure.py in Resources */, E48058292426D73500362CF3 /* version.h.in in Resources */, E48059152426D73600362CF3 /* build.sh in Resources */, + E403E63F246F4C3900200F58 /* NowPlayingViewController.xib in Resources */, E48059132426D73600362CF3 /* win32.txt in Resources */, E48059C12426D73600362CF3 /* libmpdclient.vapi in Resources */, + E41222162431425400473C1D /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1601,6 +1774,7 @@ E48059EE2426D73600362CF3 /* socket.c in Sources */, E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */, E4B11BA92274EDE30075461B /* Loading.swift in Sources */, + E41222332432B15100473C1D /* AlbumSongItem.swift in Sources */, E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */, E42A4D4F22E20D7D001C6CAD /* MPDTag.swift in Sources */, E48059D82426D73600362CF3 /* database.c in Sources */, @@ -1609,7 +1783,6 @@ E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */, E43AC1F622C6AD0B001E483C /* AlbumViewController+NSCollectionViewDelegate.swift in Sources */, E4B11B68226A4FA00075461B /* QueueState.swift in Sources */, - E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */, E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */, E4405192227644340090CD6F /* MPDServerController.swift in Sources */, E4805A102426D73600362CF3 /* tag.c in Sources */, @@ -1742,6 +1915,7 @@ E48059FC2426D73600362CF3 /* neighbor.c in Sources */, E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */, E4805A162426D73600362CF3 /* song.c in Sources */, + E4B28EE224379606003B28AE /* CGColor.swift in Sources */, E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */, E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */, E4805A0C2426D73600362CF3 /* settings.c in Sources */, @@ -1782,17 +1956,20 @@ E48059EB2426D73600362CF3 /* coutput.c in Sources */, E48059FB2426D73600362CF3 /* recv.c in Sources */, E480511A24255BAF00362CF3 /* Time.swift in Sources */, + E41222112431425400473C1D /* App.swift in Sources */, E480514C24255E7D00362CF3 /* QueueActions.swift in Sources */, E480511E24255BD500362CF3 /* RawRepresentable.swift in Sources */, E4805A1D2426D73600362CF3 /* kvlist.c in Sources */, E4805A2B2426D73600362CF3 /* message.c in Sources */, E48059C92426D73600362CF3 /* async.c in Sources */, + E4122228243153B200473C1D /* CGSize.swift in Sources */, E480514924255E7D00362CF3 /* AlbumListActions.swift in Sources */, E480511624255BAF00362CF3 /* Loading.swift in Sources */, E480514324255E7700362CF3 /* AppReducer.swift in Sources */, E4805A292426D73600362CF3 /* player.c in Sources */, E480514724255E7700362CF3 /* ServerReducer.swift in Sources */, E480514E24255E7D00362CF3 /* UIActions.swift in Sources */, + E4B28EE02436D56E003B28AE /* AlbumDetailFooterView.swift in Sources */, E480513C24255E7200362CF3 /* AppState.swift in Sources */, E480514024255E7200362CF3 /* ServerState.swift in Sources */, E480514224255E7700362CF3 /* AlbumListReducer.swift in Sources */, @@ -1802,31 +1979,42 @@ E480512724255BDB00362CF3 /* MPDClient+Queue.swift in Sources */, E480513D24255E7200362CF3 /* PlayerState.swift in Sources */, E480511D24255BD200362CF3 /* MPDClientWrapper.c in Sources */, + E41222242431535E00473C1D /* MPDAlbumArtImageDataProvider.swift in Sources */, E480512924255BDB00362CF3 /* MPDClient+Status.swift in Sources */, E48059FD2426D73600362CF3 /* neighbor.c in Sources */, E4805A252426D73600362CF3 /* iso8601.c in Sources */, E480512C24255BDF00362CF3 /* MPDCommand.swift in Sources */, E48059F52426D73600362CF3 /* search.c in Sources */, E4805A1F2426D73600362CF3 /* idle.c in Sources */, + E4F365D224942CC7006A8C4A /* NowPlayingTabBarController.swift in Sources */, + E41222182431425400473C1D /* AlbumItemCell.swift in Sources */, + E4F365D4249432C0006A8C4A /* NowPlayingTabBar.swift in Sources */, E480511724255BAF00362CF3 /* MPDServer.swift in Sources */, E48059C32426D73600362CF3 /* directory.c in Sources */, E480514A24255E7D00362CF3 /* PlayerActions.swift in Sources */, E48059DF2426D73600362CF3 /* audio_format.c in Sources */, E48059CF2426D73600362CF3 /* cpartition.c in Sources */, + E412221B2431425400473C1D /* AlbumViewController+UICollectionViewDelegateFlowLayout.swift in Sources */, E4805A012426D73600362CF3 /* quote.c in Sources */, E4805A092426D73600362CF3 /* fd_util.c in Sources */, E48059D52426D73600362CF3 /* resolver.c in Sources */, E48059D72426D73600362CF3 /* ierror.c in Sources */, E48059F32426D73600362CF3 /* capabilities.c in Sources */, E483CE69242FEFB8001F742E /* Notification.swift in Sources */, + E41222322432B14000473C1D /* AlbumSongItem.swift in Sources */, E480511B24255BAF00362CF3 /* TrackTimer.swift in Sources */, E480513124255BDF00362CF3 /* MPDStatus.swift in Sources */, E480514424255E7700362CF3 /* PlayerReducer.swift in Sources */, + E4B3B3672432DF1B007E25D2 /* AlbumSongListViewController.swift in Sources */, E480514524255E7700362CF3 /* PreferencesReducer.swift in Sources */, + E41222292431555100473C1D /* CGColor.swift in Sources */, + E4F365D6249C4CDC006A8C4A /* UIImage.swift in Sources */, E480511F24255BDB00362CF3 /* MPDClient+Album.swift in Sources */, E4805A0B2426D73600362CF3 /* sync.c in Sources */, E4805A052426D73600362CF3 /* connection.c in Sources */, + E403E63E246F4C3900200F58 /* NowPlayingViewController.swift in Sources */, E480512324255BDB00362CF3 /* MPDClient+Database.swift in Sources */, + E41222302432B0A300473C1D /* AlbumTracksDataSource.swift in Sources */, E48059ED2426D73600362CF3 /* stats.c in Sources */, E480514824255E7700362CF3 /* UIReducer.swift in Sources */, E480512124255BDB00362CF3 /* MPDClient+Command.swift in Sources */, @@ -1841,11 +2029,16 @@ E480513E24255E7200362CF3 /* PreferencesState.swift in Sources */, E4805A132426D73600362CF3 /* rplaylist.c in Sources */, E480513024255BDF00362CF3 /* MPDSong.swift in Sources */, + E4B3B3642432DB7A007E25D2 /* AlbumSongCell.swift in Sources */, + E4C51F0A243428B60093FB31 /* UIFont.swift in Sources */, E480511524255BAF00362CF3 /* DraggedSongType.swift in Sources */, E480512B24255BDF00362CF3 /* MPDAlbum.swift in Sources */, + E412221A2431425400473C1D /* AlbumDataSource.swift in Sources */, + E41222172431425400473C1D /* SceneDelegate.swift in Sources */, E48059DB2426D73600362CF3 /* entity.c in Sources */, E48051522425626300362CF3 /* MPDServerController.swift in Sources */, E480511324255BAF00362CF3 /* DraggedAlbum.swift in Sources */, + E41222122431425400473C1D /* AppDelegate.swift in Sources */, E4805A072426D73600362CF3 /* error.c in Sources */, E480514124255E7200362CF3 /* UIState.swift in Sources */, E48059E72426D73600362CF3 /* playlist.c in Sources */, @@ -1855,9 +2048,11 @@ E4805A1B2426D73600362CF3 /* cmessage.c in Sources */, E48059E52426D73600362CF3 /* run.c in Sources */, E480514D24255E7D00362CF3 /* ServerActions.swift in Sources */, + E4C51F08243422380093FB31 /* AlbumDiscCell.swift in Sources */, E480511C24255BBF00362CF3 /* MPDClient.swift in Sources */, E480513B24255E7200362CF3 /* AlbumListState.swift in Sources */, E480512224255BDB00362CF3 /* MPDClient+Connection.swift in Sources */, + E41222192431425400473C1D /* AlbumViewController.swift in Sources */, E480511124255BA900362CF3 /* MachTime.swift in Sources */, E4805A112426D73600362CF3 /* tag.c in Sources */, E4805A192426D73600362CF3 /* status.c in Sources */, @@ -1888,6 +2083,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E412221C2431431500473C1D /* Persephone_iOSTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1895,6 +2091,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E412221F2431432100473C1D /* Persephone_iOSUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1932,6 +2129,22 @@ name = Main.storyboard; sourceTree = ""; }; + E41221FF2431425400473C1D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E41222002431425400473C1D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + E41222012431425400473C1D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E41222022431425400473C1D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -2527,6 +2740,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + E41222222431530900473C1D /* Kingfisher */ = { + isa = XCSwiftPackageProductDependency; + package = E43BEC9E238835DC00CAF1EB /* XCRemoteSwiftPackageReference "Kingfisher" */; + productName = Kingfisher; + }; E43BEC9F238835DC00CAF1EB /* Kingfisher */ = { isa = XCSwiftPackageProductDependency; package = E43BEC9E238835DC00CAF1EB /* XCRemoteSwiftPackageReference "Kingfisher" */; diff --git a/Persephone.xcodeproj/xcshareddata/xcschemes/Persephone-iOS.xcscheme b/Persephone.xcodeproj/xcshareddata/xcschemes/Persephone-iOS.xcscheme new file mode 100644 index 0000000..68ab8fd --- /dev/null +++ b/Persephone.xcodeproj/xcshareddata/xcschemes/Persephone-iOS.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/export/iOS-1024x1024.png b/Resources/export/iOS-1024x1024.png new file mode 100644 index 0000000..ab9d583 Binary files /dev/null and b/Resources/export/iOS-1024x1024.png differ diff --git a/Resources/export/iOS-120x120.png b/Resources/export/iOS-120x120.png new file mode 100644 index 0000000..3113588 Binary files /dev/null and b/Resources/export/iOS-120x120.png differ diff --git a/Resources/export/iOS-152x152.png b/Resources/export/iOS-152x152.png new file mode 100644 index 0000000..ef93bfb Binary files /dev/null and b/Resources/export/iOS-152x152.png differ diff --git a/Resources/export/iOS-167x167.png b/Resources/export/iOS-167x167.png new file mode 100644 index 0000000..e8e6f5a Binary files /dev/null and b/Resources/export/iOS-167x167.png differ diff --git a/Resources/export/iOS-180x180.png b/Resources/export/iOS-180x180.png new file mode 100644 index 0000000..4933413 Binary files /dev/null and b/Resources/export/iOS-180x180.png differ diff --git a/Resources/export/iOS-20x20.png b/Resources/export/iOS-20x20.png new file mode 100644 index 0000000..5826066 Binary files /dev/null and b/Resources/export/iOS-20x20.png differ diff --git a/Resources/export/iOS-29x29.png b/Resources/export/iOS-29x29.png new file mode 100644 index 0000000..b711b1d Binary files /dev/null and b/Resources/export/iOS-29x29.png differ diff --git a/Resources/export/iOS-40x40.png b/Resources/export/iOS-40x40.png new file mode 100644 index 0000000..80df49d Binary files /dev/null and b/Resources/export/iOS-40x40.png differ diff --git a/Resources/export/iOS-58x58.png b/Resources/export/iOS-58x58.png new file mode 100644 index 0000000..88ae461 Binary files /dev/null and b/Resources/export/iOS-58x58.png differ diff --git a/Resources/export/iOS-60x60.png b/Resources/export/iOS-60x60.png new file mode 100644 index 0000000..cbd7eac Binary files /dev/null and b/Resources/export/iOS-60x60.png differ diff --git a/Resources/export/iOS-76x76.png b/Resources/export/iOS-76x76.png new file mode 100644 index 0000000..1281205 Binary files /dev/null and b/Resources/export/iOS-76x76.png differ diff --git a/Resources/export/iOS-80x80.png b/Resources/export/iOS-80x80.png new file mode 100644 index 0000000..72fa309 Binary files /dev/null and b/Resources/export/iOS-80x80.png differ diff --git a/Resources/export/iOS-87x87.png b/Resources/export/iOS-87x87.png new file mode 100644 index 0000000..55159f9 Binary files /dev/null and b/Resources/export/iOS-87x87.png differ diff --git a/Resources/export/iosAppIcon.png b/Resources/export/iosAppIcon.png new file mode 100644 index 0000000..2e41614 Binary files /dev/null and b/Resources/export/iosAppIcon.png differ diff --git a/Resources/export/iosAppIcon@2x.png b/Resources/export/iosAppIcon@2x.png new file mode 100644 index 0000000..7b6a09a Binary files /dev/null and b/Resources/export/iosAppIcon@2x.png differ diff --git a/Resources/export/nextTrackButtonHugeiOS.pdf b/Resources/export/nextTrackButtonHugeiOS.pdf new file mode 100644 index 0000000..c1687a6 Binary files /dev/null and b/Resources/export/nextTrackButtonHugeiOS.pdf differ diff --git a/Resources/export/nextTrackButtonHugeiOS.png b/Resources/export/nextTrackButtonHugeiOS.png new file mode 100644 index 0000000..d3e38bf Binary files /dev/null and b/Resources/export/nextTrackButtonHugeiOS.png differ diff --git a/Resources/export/nextTrackButtonHugeiOS@2x.png b/Resources/export/nextTrackButtonHugeiOS@2x.png new file mode 100644 index 0000000..3342900 Binary files /dev/null and b/Resources/export/nextTrackButtonHugeiOS@2x.png differ diff --git a/Resources/export/nextTrackButtonHugeiOS@3x.png b/Resources/export/nextTrackButtonHugeiOS@3x.png new file mode 100644 index 0000000..5131eda Binary files /dev/null and b/Resources/export/nextTrackButtonHugeiOS@3x.png differ diff --git a/Resources/export/nextTrackButtonLarge.pdf b/Resources/export/nextTrackButtonLarge.pdf new file mode 100644 index 0000000..364255c Binary files /dev/null and b/Resources/export/nextTrackButtonLarge.pdf differ diff --git a/Resources/export/nextTrackButtonLarge.png b/Resources/export/nextTrackButtonLarge.png new file mode 100644 index 0000000..3e05e82 Binary files /dev/null and b/Resources/export/nextTrackButtonLarge.png differ diff --git a/Resources/export/nextTrackButtonLarge@2x.png b/Resources/export/nextTrackButtonLarge@2x.png new file mode 100644 index 0000000..e3e7b10 Binary files /dev/null and b/Resources/export/nextTrackButtonLarge@2x.png differ diff --git a/Resources/export/nextTrackButtonLarge@3x.png b/Resources/export/nextTrackButtonLarge@3x.png new file mode 100644 index 0000000..89a7813 Binary files /dev/null and b/Resources/export/nextTrackButtonLarge@3x.png differ diff --git a/Resources/export/nextTrackButtonLargeiOS.pdf b/Resources/export/nextTrackButtonLargeiOS.pdf new file mode 100644 index 0000000..9f2f974 Binary files /dev/null and b/Resources/export/nextTrackButtonLargeiOS.pdf differ diff --git a/Resources/export/nextTrackButtonLargeiOS.png b/Resources/export/nextTrackButtonLargeiOS.png new file mode 100644 index 0000000..895f2d4 Binary files /dev/null and b/Resources/export/nextTrackButtonLargeiOS.png differ diff --git a/Resources/export/nextTrackButtonLargeiOS@2x.png b/Resources/export/nextTrackButtonLargeiOS@2x.png new file mode 100644 index 0000000..fa49346 Binary files /dev/null and b/Resources/export/nextTrackButtonLargeiOS@2x.png differ diff --git a/Resources/export/nextTrackButtonLargeiOS@3x.png b/Resources/export/nextTrackButtonLargeiOS@3x.png new file mode 100644 index 0000000..49dd944 Binary files /dev/null and b/Resources/export/nextTrackButtonLargeiOS@3x.png differ diff --git a/Resources/export/pauseButtonHugeiOS.pdf b/Resources/export/pauseButtonHugeiOS.pdf new file mode 100644 index 0000000..b47299f Binary files /dev/null and b/Resources/export/pauseButtonHugeiOS.pdf differ diff --git a/Resources/export/pauseButtonHugeiOS.png b/Resources/export/pauseButtonHugeiOS.png new file mode 100644 index 0000000..29d5497 Binary files /dev/null and b/Resources/export/pauseButtonHugeiOS.png differ diff --git a/Resources/export/pauseButtonHugeiOS@2x.png b/Resources/export/pauseButtonHugeiOS@2x.png new file mode 100644 index 0000000..cf1ffcd Binary files /dev/null and b/Resources/export/pauseButtonHugeiOS@2x.png differ diff --git a/Resources/export/pauseButtonHugeiOS@3x.png b/Resources/export/pauseButtonHugeiOS@3x.png new file mode 100644 index 0000000..6115b41 Binary files /dev/null and b/Resources/export/pauseButtonHugeiOS@3x.png differ diff --git a/Resources/export/pauseButtonLargeiOS.pdf b/Resources/export/pauseButtonLargeiOS.pdf new file mode 100644 index 0000000..4f49630 Binary files /dev/null and b/Resources/export/pauseButtonLargeiOS.pdf differ diff --git a/Resources/export/pauseButtonLargeiOS.png b/Resources/export/pauseButtonLargeiOS.png new file mode 100644 index 0000000..c56a775 Binary files /dev/null and b/Resources/export/pauseButtonLargeiOS.png differ diff --git a/Resources/export/pauseButtonLargeiOS@2x.png b/Resources/export/pauseButtonLargeiOS@2x.png new file mode 100644 index 0000000..792814e Binary files /dev/null and b/Resources/export/pauseButtonLargeiOS@2x.png differ diff --git a/Resources/export/pauseButtonLargeiOS@3x.png b/Resources/export/pauseButtonLargeiOS@3x.png new file mode 100644 index 0000000..605efca Binary files /dev/null and b/Resources/export/pauseButtonLargeiOS@3x.png differ diff --git a/Resources/export/playButtonHugeiOS.pdf b/Resources/export/playButtonHugeiOS.pdf new file mode 100644 index 0000000..81115af Binary files /dev/null and b/Resources/export/playButtonHugeiOS.pdf differ diff --git a/Resources/export/playButtonHugeiOS.png b/Resources/export/playButtonHugeiOS.png new file mode 100644 index 0000000..a8e7f2c Binary files /dev/null and b/Resources/export/playButtonHugeiOS.png differ diff --git a/Resources/export/playButtonHugeiOS@2x.png b/Resources/export/playButtonHugeiOS@2x.png new file mode 100644 index 0000000..9638f00 Binary files /dev/null and b/Resources/export/playButtonHugeiOS@2x.png differ diff --git a/Resources/export/playButtonHugeiOS@3x.png b/Resources/export/playButtonHugeiOS@3x.png new file mode 100644 index 0000000..a620610 Binary files /dev/null and b/Resources/export/playButtonHugeiOS@3x.png differ diff --git a/Resources/export/playButtonLarge.pdf b/Resources/export/playButtonLarge.pdf index a04b172..b354ccb 100644 Binary files a/Resources/export/playButtonLarge.pdf and b/Resources/export/playButtonLarge.pdf differ diff --git a/Resources/export/playButtonLarge.png b/Resources/export/playButtonLarge.png index a489b7b..9e29316 100644 Binary files a/Resources/export/playButtonLarge.png and b/Resources/export/playButtonLarge.png differ diff --git a/Resources/export/playButtonLarge@2x.png b/Resources/export/playButtonLarge@2x.png index 109a9ca..4604852 100644 Binary files a/Resources/export/playButtonLarge@2x.png and b/Resources/export/playButtonLarge@2x.png differ diff --git a/Resources/export/playButtonLarge@3x.png b/Resources/export/playButtonLarge@3x.png new file mode 100644 index 0000000..32fe1e6 Binary files /dev/null and b/Resources/export/playButtonLarge@3x.png differ diff --git a/Resources/export/playButtonLargeiOS.pdf b/Resources/export/playButtonLargeiOS.pdf new file mode 100644 index 0000000..38ed773 Binary files /dev/null and b/Resources/export/playButtonLargeiOS.pdf differ diff --git a/Resources/export/playButtonLargeiOS.png b/Resources/export/playButtonLargeiOS.png new file mode 100644 index 0000000..8eaa316 Binary files /dev/null and b/Resources/export/playButtonLargeiOS.png differ diff --git a/Resources/export/playButtonLargeiOS@2x.png b/Resources/export/playButtonLargeiOS@2x.png new file mode 100644 index 0000000..a00fe2d Binary files /dev/null and b/Resources/export/playButtonLargeiOS@2x.png differ diff --git a/Resources/export/playButtonLargeiOS@3x.png b/Resources/export/playButtonLargeiOS@3x.png new file mode 100644 index 0000000..41ab03f Binary files /dev/null and b/Resources/export/playButtonLargeiOS@3x.png differ diff --git a/Resources/export/prevTrackButtonHugeiOS.pdf b/Resources/export/prevTrackButtonHugeiOS.pdf new file mode 100644 index 0000000..d4eb770 Binary files /dev/null and b/Resources/export/prevTrackButtonHugeiOS.pdf differ diff --git a/Resources/export/prevTrackButtonHugeiOS.png b/Resources/export/prevTrackButtonHugeiOS.png new file mode 100644 index 0000000..1f813bc Binary files /dev/null and b/Resources/export/prevTrackButtonHugeiOS.png differ diff --git a/Resources/export/prevTrackButtonHugeiOS@2x.png b/Resources/export/prevTrackButtonHugeiOS@2x.png new file mode 100644 index 0000000..1959763 Binary files /dev/null and b/Resources/export/prevTrackButtonHugeiOS@2x.png differ diff --git a/Resources/icons.sketch b/Resources/icons.sketch index 3ac177b..fb6fb32 100644 Binary files a/Resources/icons.sketch and b/Resources/icons.sketch differ diff --git a/Shared/Controllers/MPDServerController.swift b/Shared/Controllers/MPDServerController.swift index 909890d..ac1eeea 100644 --- a/Shared/Controllers/MPDServerController.swift +++ b/Shared/Controllers/MPDServerController.swift @@ -21,8 +21,10 @@ class MPDServerController { func connect() { let mpdServer = App.store.state.preferencesState.mpdServer + guard let host = mpdServer.hostOrDefault else { return } + App.mpdClient.connect( - host: mpdServer.hostOrDefault, + host: host, port: mpdServer.portOrDefault ) } diff --git a/Mac/Components/Shared/ImageDataProviders/MPDAlbumArtImageDataProvider.swift b/Shared/ImageDataProviders/MPDAlbumArtImageDataProvider.swift similarity index 100% rename from Mac/Components/Shared/ImageDataProviders/MPDAlbumArtImageDataProvider.swift rename to Shared/ImageDataProviders/MPDAlbumArtImageDataProvider.swift diff --git a/Shared/MPDClient/Extensions/MPDClient+Album.swift b/Shared/MPDClient/Extensions/MPDClient+Album.swift index 7c3a1bc..bf1e68c 100644 --- a/Shared/MPDClient/Extensions/MPDClient+Album.swift +++ b/Shared/MPDClient/Extensions/MPDClient+Album.swift @@ -57,7 +57,7 @@ extension MPDClient { func albums(filter: String) { var albums: [MPDAlbum] = [] - mpd_search_db_songs(self.connection, false) + mpd_search_db_songs(connection, false) if filter != "" { let escapedFilter = filter.replacingOccurrences(of: "'", with: "\\'") @@ -66,11 +66,12 @@ extension MPDClient { "(any contains '\(escapedFilter)')" ) } - mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1") + mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1") + mpd_search_add_sort_tag(connection, MPD_TAG_ALBUM_ARTIST_SORT, false) mpd_search_commit(self.connection) - while let song = mpd_recv_song(self.connection) { + while let song = mpd_recv_song(connection) { let mpdSong = MPDSong(song) let mpdAlbum = MPDAlbum( @@ -92,10 +93,10 @@ extension MPDClient { var firstSong: MPDSong? - 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_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1") + mpd_search_db_songs(connection, true) + mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title) + mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist) + mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1") mpd_search_commit(self.connection) diff --git a/Shared/MPDClient/Extensions/MPDClient+Connection.swift b/Shared/MPDClient/Extensions/MPDClient+Connection.swift index 589cc2b..1a87b99 100644 --- a/Shared/MPDClient/Extensions/MPDClient+Connection.swift +++ b/Shared/MPDClient/Extensions/MPDClient+Connection.swift @@ -35,19 +35,21 @@ extension MPDClient { self.delegate?.willDisconnect(mpdClient: self) - mpd_connection_free(self.connection) - self.isConnected = false + mpd_connection_free(self.connection) + self.isConnected = false } func connect(host: String, port: Int) { - let commandOperation = BlockOperation() { [unowned self] in - self.sendCommand(command: .connect, userData: ["host": host, "port": port]) + if !isConnected { + let commandOperation = BlockOperation() { [unowned self] in + self.sendCommand(command: .connect, userData: ["host": host, "port": port]) - if self.isConnected { - self.idle() + if self.isConnected { + self.idle() + } } + commandQueue.addOperation(commandOperation) } - commandQueue.addOperation(commandOperation) } func disconnect() { diff --git a/Shared/Models/AlbumSongItem.swift b/Shared/Models/AlbumSongItem.swift new file mode 100644 index 0000000..e9b5fd2 --- /dev/null +++ b/Shared/Models/AlbumSongItem.swift @@ -0,0 +1,24 @@ +// +// AlbumSongItem.swift +// Persephone-iOS +// +// Created by Daniel Barber on 2020-3-30. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import Foundation + +struct AlbumSongItem { + let disc: String? + let song: Song? + + init(song: Song) { + self.disc = nil + self.song = song + } + + init(disc: String) { + self.disc = disc + self.song = nil + } +} diff --git a/Shared/Models/MPDServer.swift b/Shared/Models/MPDServer.swift index 63f00ea..5af31db 100644 --- a/Shared/Models/MPDServer.swift +++ b/Shared/Models/MPDServer.swift @@ -7,12 +7,17 @@ // struct MPDServer { - let hostDefault = "127.0.0.1" + #if os(macOS) + let hostDefault: String? = "127.0.0.1" + #else + let hostDefault: String? = nil + #endif + let portDefault = 6600 var host: String? - var hostOrDefault: String { + var hostOrDefault: String? { return host ?? hostDefault } diff --git a/Shared/Models/Time.swift b/Shared/Models/Time.swift index 4c2596f..5629691 100644 --- a/Shared/Models/Time.swift +++ b/Shared/Models/Time.swift @@ -25,4 +25,8 @@ struct Time { return formatter.string(from: TimeInterval(timeInSeconds))! } + + var hours: Int { timeInSeconds / 3600 } + + var minutes: Int { timeInSeconds % 3600 / 60 } } diff --git a/iOS/App.swift b/iOS/App.swift index 89861e4..fcc70ac 100644 --- a/iOS/App.swift +++ b/iOS/App.swift @@ -11,7 +11,6 @@ import ReSwift struct App { static let store = Store(reducer: appReducer, state: nil) static let trackTimer = TrackTimer() - //static let userNotificationsController = UserNotificationsController() static let mpdServerDelegate = MPDServerDelegate() static let mpdServerController = MPDServerController(delegate: mpdServerDelegate) static var mpdClient: MPDClient! diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..065e2ac --- /dev/null +++ b/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "filename" : "iOS-40x40-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "iOS-60x60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "iOS-58x58-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "iOS-87x87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "iOS-80x80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "iOS-120x120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "iOS-120x120-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "iOS-180x180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "iOS-20x20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "iOS-40x40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "iOS-29x29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "iOS-58x58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "iOS-40x40-2.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "iOS-80x80-1.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "iOS-76x76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "iOS-152x152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "iOS-167x167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "iOS-1024x1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-1024x1024.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-1024x1024.png new file mode 100644 index 0000000..ab9d583 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-1024x1024.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-120x120-1.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-120x120-1.png new file mode 100644 index 0000000..3113588 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-120x120-1.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-120x120.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-120x120.png new file mode 100644 index 0000000..3113588 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-120x120.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-152x152.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-152x152.png new file mode 100644 index 0000000..ef93bfb Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-152x152.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-167x167.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-167x167.png new file mode 100644 index 0000000..e8e6f5a Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-167x167.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-180x180.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-180x180.png new file mode 100644 index 0000000..4933413 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-180x180.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-20x20.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-20x20.png new file mode 100644 index 0000000..5826066 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-20x20.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-29x29.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-29x29.png new file mode 100644 index 0000000..b711b1d Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-29x29.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40-1.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40-1.png new file mode 100644 index 0000000..80df49d Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40-1.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40-2.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40-2.png new file mode 100644 index 0000000..80df49d Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40-2.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40.png new file mode 100644 index 0000000..80df49d Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-40x40.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-58x58-1.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-58x58-1.png new file mode 100644 index 0000000..88ae461 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-58x58-1.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-58x58.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-58x58.png new file mode 100644 index 0000000..88ae461 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-58x58.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-60x60.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-60x60.png new file mode 100644 index 0000000..cbd7eac Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-60x60.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-76x76.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-76x76.png new file mode 100644 index 0000000..1281205 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-76x76.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-80x80-1.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-80x80-1.png new file mode 100644 index 0000000..72fa309 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-80x80-1.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-80x80.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-80x80.png new file mode 100644 index 0000000..72fa309 Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-80x80.png differ diff --git a/iOS/Assets.xcassets/AppIcon.appiconset/iOS-87x87.png b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-87x87.png new file mode 100644 index 0000000..95371dc Binary files /dev/null and b/iOS/Assets.xcassets/AppIcon.appiconset/iOS-87x87.png differ diff --git a/iOS/Assets.xcassets/Contents.json b/iOS/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/iOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/Assets.xcassets/defaultCoverArt.imageset/Contents.json b/iOS/Assets.xcassets/defaultCoverArt.imageset/Contents.json new file mode 100644 index 0000000..3312479 --- /dev/null +++ b/iOS/Assets.xcassets/defaultCoverArt.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "blankAlbumLight.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "blankAlbumDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto", + "preserves-vector-representation" : true + } +} diff --git a/iOS/Assets.xcassets/defaultCoverArt.imageset/blankAlbumDark.pdf b/iOS/Assets.xcassets/defaultCoverArt.imageset/blankAlbumDark.pdf new file mode 100644 index 0000000..01942a6 Binary files /dev/null and b/iOS/Assets.xcassets/defaultCoverArt.imageset/blankAlbumDark.pdf differ diff --git a/iOS/Assets.xcassets/defaultCoverArt.imageset/blankAlbumLight.pdf b/iOS/Assets.xcassets/defaultCoverArt.imageset/blankAlbumLight.pdf new file mode 100644 index 0000000..ed083f0 Binary files /dev/null and b/iOS/Assets.xcassets/defaultCoverArt.imageset/blankAlbumLight.pdf differ diff --git a/iOS/Assets.xcassets/nextTrackButtonHugeiOS.imageset/Contents.json b/iOS/Assets.xcassets/nextTrackButtonHugeiOS.imageset/Contents.json new file mode 100644 index 0000000..1e7587e --- /dev/null +++ b/iOS/Assets.xcassets/nextTrackButtonHugeiOS.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "nextTrackButtonHugeiOS.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/nextTrackButtonHugeiOS.imageset/nextTrackButtonHugeiOS.pdf b/iOS/Assets.xcassets/nextTrackButtonHugeiOS.imageset/nextTrackButtonHugeiOS.pdf new file mode 100644 index 0000000..c1687a6 Binary files /dev/null and b/iOS/Assets.xcassets/nextTrackButtonHugeiOS.imageset/nextTrackButtonHugeiOS.pdf differ diff --git a/iOS/Assets.xcassets/nextTrackButtonLarge.imageset/Contents.json b/iOS/Assets.xcassets/nextTrackButtonLarge.imageset/Contents.json new file mode 100644 index 0000000..6f87921 --- /dev/null +++ b/iOS/Assets.xcassets/nextTrackButtonLarge.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "nextTrackButtonLargeiOS.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/nextTrackButtonLarge.imageset/nextTrackButtonLargeiOS.pdf b/iOS/Assets.xcassets/nextTrackButtonLarge.imageset/nextTrackButtonLargeiOS.pdf new file mode 100644 index 0000000..d560bd4 Binary files /dev/null and b/iOS/Assets.xcassets/nextTrackButtonLarge.imageset/nextTrackButtonLargeiOS.pdf differ diff --git a/iOS/Assets.xcassets/pauseButtonHugeiOS.imageset/Contents.json b/iOS/Assets.xcassets/pauseButtonHugeiOS.imageset/Contents.json new file mode 100644 index 0000000..62a34b5 --- /dev/null +++ b/iOS/Assets.xcassets/pauseButtonHugeiOS.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "pauseButtonHugeiOS.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/pauseButtonHugeiOS.imageset/pauseButtonHugeiOS.pdf b/iOS/Assets.xcassets/pauseButtonHugeiOS.imageset/pauseButtonHugeiOS.pdf new file mode 100644 index 0000000..b47299f Binary files /dev/null and b/iOS/Assets.xcassets/pauseButtonHugeiOS.imageset/pauseButtonHugeiOS.pdf differ diff --git a/iOS/Assets.xcassets/pauseButtonLarge.imageset/Contents.json b/iOS/Assets.xcassets/pauseButtonLarge.imageset/Contents.json new file mode 100644 index 0000000..56a5a2c --- /dev/null +++ b/iOS/Assets.xcassets/pauseButtonLarge.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "pauseButtonLargeiOS.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/pauseButtonLarge.imageset/pauseButtonLargeiOS.pdf b/iOS/Assets.xcassets/pauseButtonLarge.imageset/pauseButtonLargeiOS.pdf new file mode 100644 index 0000000..0680892 Binary files /dev/null and b/iOS/Assets.xcassets/pauseButtonLarge.imageset/pauseButtonLargeiOS.pdf differ diff --git a/iOS/Assets.xcassets/playButtonHugeiOS.imageset/Contents.json b/iOS/Assets.xcassets/playButtonHugeiOS.imageset/Contents.json new file mode 100644 index 0000000..c9a0c7c --- /dev/null +++ b/iOS/Assets.xcassets/playButtonHugeiOS.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "playButtonHugeiOS.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/playButtonHugeiOS.imageset/playButtonHugeiOS.pdf b/iOS/Assets.xcassets/playButtonHugeiOS.imageset/playButtonHugeiOS.pdf new file mode 100644 index 0000000..81115af Binary files /dev/null and b/iOS/Assets.xcassets/playButtonHugeiOS.imageset/playButtonHugeiOS.pdf differ diff --git a/iOS/Assets.xcassets/playButtonLarge.imageset/Contents.json b/iOS/Assets.xcassets/playButtonLarge.imageset/Contents.json new file mode 100644 index 0000000..2276e67 --- /dev/null +++ b/iOS/Assets.xcassets/playButtonLarge.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "playButtonLargeiOS.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/playButtonLarge.imageset/playButtonLargeiOS.pdf b/iOS/Assets.xcassets/playButtonLarge.imageset/playButtonLargeiOS.pdf new file mode 100644 index 0000000..84497df Binary files /dev/null and b/iOS/Assets.xcassets/playButtonLarge.imageset/playButtonLargeiOS.pdf differ diff --git a/iOS/Assets.xcassets/prevTrackButtonHugeiOS.imageset/Contents.json b/iOS/Assets.xcassets/prevTrackButtonHugeiOS.imageset/Contents.json new file mode 100644 index 0000000..57d239b --- /dev/null +++ b/iOS/Assets.xcassets/prevTrackButtonHugeiOS.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "prevTrackButtonHugeiOS.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/prevTrackButtonHugeiOS.imageset/prevTrackButtonHugeiOS.pdf b/iOS/Assets.xcassets/prevTrackButtonHugeiOS.imageset/prevTrackButtonHugeiOS.pdf new file mode 100644 index 0000000..d4eb770 Binary files /dev/null and b/iOS/Assets.xcassets/prevTrackButtonHugeiOS.imageset/prevTrackButtonHugeiOS.pdf differ diff --git a/iOS/Assets.xcassets/speakerHigh.imageset/Contents.json b/iOS/Assets.xcassets/speakerHigh.imageset/Contents.json new file mode 100644 index 0000000..8a644e7 --- /dev/null +++ b/iOS/Assets.xcassets/speakerHigh.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "speakerHigh.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/speakerHigh.imageset/speakerHigh.pdf b/iOS/Assets.xcassets/speakerHigh.imageset/speakerHigh.pdf new file mode 100644 index 0000000..08ac5f1 Binary files /dev/null and b/iOS/Assets.xcassets/speakerHigh.imageset/speakerHigh.pdf differ diff --git a/iOS/Assets.xcassets/speakerOff.imageset/Contents.json b/iOS/Assets.xcassets/speakerOff.imageset/Contents.json new file mode 100644 index 0000000..5e29376 --- /dev/null +++ b/iOS/Assets.xcassets/speakerOff.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "speakerOff.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Assets.xcassets/speakerOff.imageset/speakerOff.pdf b/iOS/Assets.xcassets/speakerOff.imageset/speakerOff.pdf new file mode 100644 index 0000000..a258eea Binary files /dev/null and b/iOS/Assets.xcassets/speakerOff.imageset/speakerOff.pdf differ diff --git a/iOS/Base.lproj/LaunchScreen.storyboard b/iOS/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/iOS/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard new file mode 100644 index 0000000..3a12470 --- /dev/null +++ b/iOS/Base.lproj/Main.storyboard @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Components/Browser/Album Browser/AlbumDataSource.swift b/iOS/Components/Browser/Album Browser/AlbumDataSource.swift new file mode 100644 index 0000000..304ed4a --- /dev/null +++ b/iOS/Components/Browser/Album Browser/AlbumDataSource.swift @@ -0,0 +1,26 @@ +// +// AlbumDataSource.swift +// Persephone +// +// Created by Daniel Barber on 2019/2/20. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import UIKit + +class AlbumDataSource: NSObject, UICollectionViewDataSource { + var albums: [Album] = [] + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return albums.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let item = collectionView.dequeueReusableCell(withReuseIdentifier: "AlbumViewCell", for: indexPath) + guard let albumViewCell = item as? AlbumItemCell else { return item } + + albumViewCell.setAlbum(albums[indexPath.item]) + + return albumViewCell + } +} diff --git a/iOS/Components/Browser/Album Browser/AlbumItemCell.swift b/iOS/Components/Browser/Album Browser/AlbumItemCell.swift new file mode 100644 index 0000000..44a4f1b --- /dev/null +++ b/iOS/Components/Browser/Album Browser/AlbumItemCell.swift @@ -0,0 +1,64 @@ +// +// AlbumItemCell.swift +// Persephone +// +// Created by Daniel Barber on 2020-3-28. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit +import Kingfisher + +class AlbumItemCell: UICollectionViewCell { + var album: Album? + + override func didMoveToSuperview() { + albumCoverView.layer.backgroundColor = UIColor.black.cgColor + albumCoverView.layer.cornerRadius = 4 + albumCoverView.layer.borderWidth = 1 / traitCollection.displayScale + albumCoverView.layer.masksToBounds = true + setAppearance() + } + + func setAlbum(_ album: Album) { + self.album = album + albumTitle.text = album.title + albumArtist.text = album.artist + setAlbumCover(album) + } + + func setAlbumCover(_ album: Album) { + guard let song = album.mpdAlbum.firstSong + else { return } + + let provider = MPDAlbumArtImageDataProvider( + songUri: song.uriString, + cacheKey: album.hash + ) + + albumCoverView.kf.setImage( + with: .provider(provider), + placeholder: UIImage(named: "defaultCoverArt"), + options: [ + .processor(DownsamplingImageProcessor(size: .albumListCoverSize)), + .scaleFactor(traitCollection.displayScale), + ] + ) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { + setAppearance() + } + } + + func setAppearance() { + let darkMode = traitCollection.userInterfaceStyle == .dark + + albumCoverView.layer.borderColor = darkMode ? CGColor.albumBorderColorDark : CGColor.albumBorderColorLight + } + + @IBOutlet var albumCoverView: UIImageView! + @IBOutlet var albumTitle: UILabel! + @IBOutlet var albumArtist: UILabel! +} diff --git a/iOS/Components/Browser/Album Browser/AlbumViewController+UICollectionViewDelegateFlowLayout.swift b/iOS/Components/Browser/Album Browser/AlbumViewController+UICollectionViewDelegateFlowLayout.swift new file mode 100644 index 0000000..70643c7 --- /dev/null +++ b/iOS/Components/Browser/Album Browser/AlbumViewController+UICollectionViewDelegateFlowLayout.swift @@ -0,0 +1,33 @@ +// +// AlbumViewController+UICollectionViewDelegateFlowLayout.swift +// Persephone +// +// Created by Daniel Barber on 2020-3-28. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +extension AlbumViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + sizeForItemAt indexPath: IndexPath) -> CGSize { + let paddingSpace = sectionInsets.left * (itemsPerRow + 1) + let availableWidth = view.frame.width - paddingSpace + let widthPerItem = availableWidth / itemsPerRow + + return CGSize(width: widthPerItem, height: widthPerItem + 48) + } + + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + insetForSectionAt section: Int) -> UIEdgeInsets { + return sectionInsets + } + + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return sectionInsets.left / 2 + } +} diff --git a/iOS/Components/Browser/Album Browser/AlbumViewController.swift b/iOS/Components/Browser/Album Browser/AlbumViewController.swift new file mode 100644 index 0000000..5480a89 --- /dev/null +++ b/iOS/Components/Browser/Album Browser/AlbumViewController.swift @@ -0,0 +1,60 @@ +// +// ViewController.swift +// Persephone-iOS +// +// Created by Daniel Barber on 2020-3-13. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit +import ReSwift + +class AlbumViewController: UICollectionViewController { + let dataSource = AlbumDataSource() + + let itemsPerRow: CGFloat = 2 + + let sectionInsets = UIEdgeInsets( + top: 20.0, + left: 20.0, + bottom: 30.0, + right: 20.0 + ) + + override func viewDidLoad() { + super.viewDidLoad() + + albumCollectionView.dataSource = dataSource + + App.store.subscribe(self) { + $0.select { $0.albumListState } + } + + NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil) + } + + @objc func didConnect() { + print("Album view controller - Connected!") + App.mpdClient.fetchAllAlbums() + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard let detailView = segue.destination as? AlbumSongListViewController, + let sender = sender as? AlbumItemCell + else { return } + + detailView.setAlbum(sender.album) + } + + @IBOutlet var albumCollectionView: UICollectionView! +} + +extension AlbumViewController: StoreSubscriber { + typealias StoreSubscriberStateType = AlbumListState + + func newState(state: StoreSubscriberStateType) { + dataSource.albums = state.albums + + albumCollectionView.reloadData() + } +} diff --git a/iOS/Components/Browser/Album Detail/AlbumDetailFooterView.swift b/iOS/Components/Browser/Album Detail/AlbumDetailFooterView.swift new file mode 100644 index 0000000..451f2e8 --- /dev/null +++ b/iOS/Components/Browser/Album Detail/AlbumDetailFooterView.swift @@ -0,0 +1,25 @@ +// +// AlbumDetailFooterView.swift +// Persephone +// +// Created by Daniel Barber on 2020-4-2. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +class AlbumDetailFooterView: UIView { + override func didMoveToSuperview() { + super.didMoveToSuperview() + + let separator = CALayer() + separator.frame = CGRect( + x: 20, + y: -0.5, + width: UIScreen.main.bounds.width - 20, + height: 0.5 + ) + separator.backgroundColor = UIColor.separator.cgColor + layer.addSublayer(separator) + } +} diff --git a/iOS/Components/Browser/Album Detail/AlbumDiscCell.swift b/iOS/Components/Browser/Album Detail/AlbumDiscCell.swift new file mode 100644 index 0000000..4accafa --- /dev/null +++ b/iOS/Components/Browser/Album Detail/AlbumDiscCell.swift @@ -0,0 +1,21 @@ +// +// AlbumDiscCell.swift +// Persephone-iOS +// +// Created by Daniel Barber on 2020-3-31. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +class AlbumDiscCell: UITableViewCell { + var albumSongItem: AlbumSongItem? + + func setSongItem(songItem: AlbumSongItem) { + albumSongItem = songItem + + albumDisc.text = "Disc \(songItem.disc ?? "0")" + } + + @IBOutlet var albumDisc: UILabel! +} diff --git a/iOS/Components/Browser/Album Detail/AlbumSongCell.swift b/iOS/Components/Browser/Album Detail/AlbumSongCell.swift new file mode 100644 index 0000000..11edc51 --- /dev/null +++ b/iOS/Components/Browser/Album Detail/AlbumSongCell.swift @@ -0,0 +1,29 @@ +// +// AlbumSongCell.swift +// Persephone-iOS +// +// Created by Daniel Barber on 2020-3-30. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +class AlbumSongCell: UITableViewCell { + var albumSongItem: AlbumSongItem? + + func setSongItem(songItem: AlbumSongItem) { + albumSongItem = songItem + + guard let song = songItem.song else { return } + + songDuration.font = .timerFont + + trackNumber.text = song.trackNumber + songTitle.text = song.title + songDuration.text = song.duration.formattedTime + } + + @IBOutlet var trackNumber: UILabel! + @IBOutlet var songTitle: UILabel! + @IBOutlet var songDuration: UILabel! +} diff --git a/iOS/Components/Browser/Album Detail/AlbumSongListViewController.swift b/iOS/Components/Browser/Album Detail/AlbumSongListViewController.swift new file mode 100644 index 0000000..26c9f3d --- /dev/null +++ b/iOS/Components/Browser/Album Detail/AlbumSongListViewController.swift @@ -0,0 +1,155 @@ +// +// AlbumSongListViewController.swift +// Persephone +// +// Created by Daniel Barber on 2020-3-30. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit +import Kingfisher + +class AlbumSongListViewController: UITableViewController { + var album: Album? + var albumSongs: [Song] = [] + var dataSource = AlbumTracksDataSource() + + override func viewDidLoad() { + super.viewDidLoad() + + albumTracksView.dataSource = dataSource + albumTracksView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) + + navigationItem.largeTitleDisplayMode = .never + + albumCoverView.layer.backgroundColor = UIColor.black.cgColor + albumCoverView.layer.cornerRadius = 4 + albumCoverView.layer.borderWidth = 1 / traitCollection.displayScale + albumCoverView.layer.masksToBounds = true + + playAlbumButton.layer.cornerRadius = 8 + addAlbumToQueueButton.layer.cornerRadius = 8 + setAppearance() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + guard let album = album else { return } + + getAlbumSongs(for: album) + + albumTitle.text = album.title + albumMetadata.text = "\(album.artist) · \(album.date)" + } + + override func viewWillLayoutSubviews() { + var layoutSize = UIView.layoutFittingExpandedSize + layoutSize.width = view.bounds.width + + let headerViewSize = albumTracksView.tableHeaderView? + .systemLayoutSizeFitting( + layoutSize, + withHorizontalFittingPriority: .defaultHigh, + verticalFittingPriority: .defaultLow + ) + + albumTracksView.tableHeaderView?.frame.size = headerViewSize! + } + + func setAlbum(_ album: Album?) { + guard let album = album else { return } + + self.album = album + } + + func getAlbumSongs(for album: Album) { + App.mpdClient.getAlbumSongs(for: album.mpdAlbum) { [weak self] (mpdSongs: [MPDClient.MPDSong]) in + guard let self = self else { return } + + DispatchQueue.main.async { + var totalDurationInSeconds = 0 + + self.dataSource.setAlbumSongs( + mpdSongs.map { + totalDurationInSeconds += $0.duration + return Song(mpdSong: $0) + } + ) + + let totalDuration = Time(timeInSeconds: totalDurationInSeconds) + + self.albumTracksView.reloadData() + + guard let mpdSong = album.mpdAlbum.firstSong + else { return } + + self.getBigCoverArt(song: Song(mpdSong: mpdSong), album: album) + + var metadata = "\(mpdSongs.count) song" + metadata += mpdSongs.count > 1 ? "s," : "," + if totalDuration.hours > 0 { + metadata += " \(totalDuration.hours) hour" + metadata += totalDuration.hours > 1 ? "s" : "" + } + metadata += " \(totalDuration.minutes) minute" + metadata += totalDuration.minutes > 1 ? "s" : "" + self.songListMetadata.text = metadata + } + } + } + + func getBigCoverArt(song: Song, album: Album) { + let provider = MPDAlbumArtImageDataProvider( + songUri: song.mpdSong.uriString, + cacheKey: album.hash + ) + + albumCoverView.kf.setImage( + with: .provider(provider), + placeholder: UIImage(named: "defaultCoverArt"), + options: [ + .processor(DownsamplingImageProcessor(size: .albumDetailCoverSize)), + .scaleFactor(traitCollection.displayScale), + ] + ) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { + setAppearance() + } + } + + func setAppearance() { + let darkMode = traitCollection.userInterfaceStyle == .dark + + albumCoverView.layer.borderColor = darkMode ? CGColor.albumBorderColorDark : CGColor.albumBorderColorLight + } + + @IBAction func playAlbumAction(_ sender: Any) { + guard let album = album else { return } + + App.mpdClient.playAlbum(album.mpdAlbum) + } + + @IBAction func addAlbumToQueueAction(_ sender: Any) { + guard let album = album else { return } + + let queueLength = App.store.state.queueState.queue.count + + App.mpdClient.addAlbumToQueue(album: album.mpdAlbum, at: queueLength) + } + + @IBOutlet var songListMetadata: UILabel! + + @IBOutlet var addAlbumToQueueButton: UIButton! + @IBOutlet var playAlbumButton: UIButton! + @IBOutlet var albumTitle: UILabel! + @IBOutlet var albumMetadata: UILabel! + + @IBOutlet var albumCoverView: UIImageView! + @IBOutlet var albumTracksView: UITableView! +} diff --git a/iOS/Components/Browser/Album Detail/AlbumTracksDataSource.swift b/iOS/Components/Browser/Album Detail/AlbumTracksDataSource.swift new file mode 100644 index 0000000..b12725c --- /dev/null +++ b/iOS/Components/Browser/Album Detail/AlbumTracksDataSource.swift @@ -0,0 +1,52 @@ +// +// AlbumTracksDataSource.swift +// Persephone +// +// Created by Daniel Barber on 2019/5/19. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import UIKit + +class AlbumTracksDataSource: NSObject, UITableViewDataSource { + var albumSongs: [AlbumSongItem] = [] + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return albumSongs.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let albumSongItem = albumSongs[indexPath.row] + + if let song = albumSongItem.song { + guard let albumSongCell = tableView.dequeueReusableCell(withIdentifier: "albumSongCell") as? AlbumSongCell + else { return AlbumSongCell() } + + albumSongCell.setSongItem(songItem: albumSongItem) + + return albumSongCell + } else if let disc = albumSongItem.disc { + guard let albumDiscCell = tableView.dequeueReusableCell(withIdentifier: "albumDiscCell") as? AlbumDiscCell + else { return AlbumDiscCell() } + + albumDiscCell.setSongItem(songItem: albumSongItem) + + return albumDiscCell + } + + return UITableViewCell() + } + + func setAlbumSongs(_ songs: [Song]) { + var disc: String? = "" + + songs.forEach { song in + if song.disc != disc && song.disc != "0" { + disc = song.disc + albumSongs.append(AlbumSongItem(disc: song.disc)) + } + + albumSongs.append(AlbumSongItem(song: song)) + } + } +} diff --git a/iOS/Components/Now Playing/NowPlayingBarViewController.swift b/iOS/Components/Now Playing/NowPlayingBarViewController.swift new file mode 100644 index 0000000..b41a81b --- /dev/null +++ b/iOS/Components/Now Playing/NowPlayingBarViewController.swift @@ -0,0 +1,134 @@ +// +// NowPlayingViewController.swift +// Persephone-iOS +// +// Created by Dan Barber on 2020-5-15. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit +import ReSwift +import Kingfisher + +class NowPlayingBarViewController: UIViewController { + @IBOutlet var separatorHeight: NSLayoutConstraint! + @IBOutlet var playPauseButton: UIButton! + @IBOutlet var nextButton: UIButton! + @IBOutlet var songTitle: UILabel! + @IBOutlet var albumCoverView: UIImageView! + + override func viewDidLoad() { + super.viewDidLoad() + + separatorHeight.constant = 1 / traitCollection.displayScale + + App.store.subscribe(self) { + $0.select { + ($0.playerState, $0.queueState) + } + } + + NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(didReloadAlbumArt), name: .didReloadAlbumArt, object: nil) + + albumCoverView.layer.backgroundColor = UIColor.black.cgColor + albumCoverView.layer.cornerRadius = 4 + albumCoverView.layer.borderWidth = 1 / traitCollection.displayScale + albumCoverView.layer.masksToBounds = true + + setAppearance() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + separatorHeight.constant = 1 / traitCollection.displayScale + + if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { + setAppearance() + } + } + + func setAppearance() { + let darkMode = traitCollection.userInterfaceStyle == .dark + + albumCoverView.layer.borderColor = darkMode ? CGColor.albumBorderColorDark : CGColor.albumBorderColorLight + } + + @objc func didConnect() { + App.mpdClient.fetchQueue() + } + + @objc func willDisconnect() { + DispatchQueue.main.async { + App.store.dispatch(UpdateQueuePosAction(queuePos: -1)) + App.store.dispatch(UpdateQueueAction(queue: [])) + } + } + + @objc func didReloadAlbumArt() { + // NO-OP + } + + func setTransportControlState(_ state: PlayerState) { + guard let state = state.state else { return } + + playPauseButton.isEnabled = state.isOneOf([.playing, .paused, .stopped]) + nextButton.isEnabled = state.isOneOf([.playing, .paused]) + + if state.isOneOf([.paused, .stopped, .unknown]) { + playPauseButton.setImage(.playIconLarge, for: .normal) + } else { + playPauseButton.setImage(.pauseIconLarge, for: .normal) + } + } + + func setSong(_ song: Song?) { + guard let song = song else { + self.songTitle.text = "Not Playing" + self.albumCoverView.image = .defaultCoverArt + return + } + + songTitle.text = song.title + + let provider = MPDAlbumArtImageDataProvider( + songUri: song.mpdSong.uriString, + cacheKey: song.album.hash + ) + + albumCoverView.kf.setImage( + with: .provider(provider), + placeholder: UIImage.defaultCoverArt, + options: [ + .processor(DownsamplingImageProcessor(size: .queueSongCoverSize)), + .scaleFactor(traitCollection.displayScale), + ] + ) + } + + @IBAction func playPauseButtonAction(_ sender: Any) { + App.mpdClient.playPause() + } + + @IBAction func nextButtonAction(_ sender: Any) { + App.mpdClient.nextTrack() + } + + @IBAction func expandNowPlaying(_ sender: Any) { + } +} + +extension NowPlayingBarViewController: StoreSubscriber { + typealias StoreSubscriberStateType = ( + playerState: PlayerState, queueState: QueueState + ) + + func newState(state: StoreSubscriberStateType) { + DispatchQueue.main.async { + self.setTransportControlState(state.playerState) + self.setSong(state.playerState.currentSong) + } + } +} diff --git a/iOS/Components/Now Playing/NowPlayingBarViewController.xib b/iOS/Components/Now Playing/NowPlayingBarViewController.xib new file mode 100644 index 0000000..3078075 --- /dev/null +++ b/iOS/Components/Now Playing/NowPlayingBarViewController.xib @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Components/Now Playing/NowPlayingTabBar.swift b/iOS/Components/Now Playing/NowPlayingTabBar.swift new file mode 100644 index 0000000..7a40209 --- /dev/null +++ b/iOS/Components/Now Playing/NowPlayingTabBar.swift @@ -0,0 +1,47 @@ +// +// NowPlayingTabBar.swift +// Persephone-iOS +// +// Created by Dan Barber on 2020-6-12. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +class NowPlayingControlBackground: UIControl { +// override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { +// +// } +// +// override func cancelTracking(with event: UIEvent?) { +// +// } +// +// override func endTracking(_ touch: UITouch?, with event: UIEvent?) { +// +// // if touch is inside your control +// sendActions(for: .touchUpInside) +// } +} + +class NowPlayingTabBar: UITabBar { + static let barHeight: CGFloat = 56 + + override func awakeFromNib() { + super.awakeFromNib() + + let bounds = CGRect(x: 0, y: 0, width: 1, height: 1) + NowPlayingTabBar.appearance().shadowImage = UIGraphicsImageRenderer(bounds: bounds).image { context in + UIColor.systemRed.setFill() + context.fill(bounds) + } + } + + override func layoutSubviews() { + super.layoutSubviews() + + for case let control as UIControl in subviews { + control.frame.origin.y += Self.barHeight + } + } +} diff --git a/iOS/Components/Now Playing/NowPlayingTabBarController.swift b/iOS/Components/Now Playing/NowPlayingTabBarController.swift new file mode 100644 index 0000000..c446de8 --- /dev/null +++ b/iOS/Components/Now Playing/NowPlayingTabBarController.swift @@ -0,0 +1,30 @@ +// +// NowPlayingTabBarController.swift +// Persephone +// +// Created by Dan Barber on 2020-6-12. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +class NowPlayingTabBarController: UITabBarController { + let nowPlayingViewController = NowPlayingBarViewController() + + override func viewDidLoad() { + super.viewDidLoad() + + let subview = nowPlayingViewController.view! + view.addSubview(subview) + subview.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + subview.leadingAnchor.constraint(equalTo: view.leadingAnchor), + subview.trailingAnchor.constraint(equalTo: view.trailingAnchor), + subview.topAnchor.constraint(equalTo: tabBar.topAnchor), + subview.heightAnchor.constraint(equalToConstant: NowPlayingTabBar.barHeight), + ]) + + additionalSafeAreaInsets.bottom = NowPlayingTabBar.barHeight + } +} diff --git a/iOS/Components/Now Playing/NowPlayingViewController.swift b/iOS/Components/Now Playing/NowPlayingViewController.swift new file mode 100644 index 0000000..d1ff710 --- /dev/null +++ b/iOS/Components/Now Playing/NowPlayingViewController.swift @@ -0,0 +1,30 @@ +// +// NowPlayingViewController.swift +// Persephone-iOS +// +// Created by Dan Barber on 2020-12-01. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +class NowPlayingViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/iOS/Components/Now Playing/NowPlayingViewController.xib b/iOS/Components/Now Playing/NowPlayingViewController.xib new file mode 100644 index 0000000..68fb285 --- /dev/null +++ b/iOS/Components/Now Playing/NowPlayingViewController.xib @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Components/Shared/Extensions/CGColor.swift b/iOS/Components/Shared/Extensions/CGColor.swift new file mode 100644 index 0000000..3e296b1 --- /dev/null +++ b/iOS/Components/Shared/Extensions/CGColor.swift @@ -0,0 +1,14 @@ +// +// NSColor.swift +// Persephone +// +// Created by Daniel Barber on 2019/2/16. +// Copyright © 2019 Dan Barber. All rights reserved. +// + +import CoreGraphics + +extension CGColor { + static let albumBorderColorLight = CGColor.init(srgbRed: 0, green: 0, blue: 0, alpha: 0.15) + static let albumBorderColorDark = CGColor.init(srgbRed: 255, green: 255, blue: 255, alpha: 0.15) +} diff --git a/iOS/Components/Shared/Extensions/CGSize.swift b/iOS/Components/Shared/Extensions/CGSize.swift new file mode 100644 index 0000000..82bd7f8 --- /dev/null +++ b/iOS/Components/Shared/Extensions/CGSize.swift @@ -0,0 +1,20 @@ +// +// CGSize.swift +// Persephone +// +// Created by Daniel Barber on 1/20/20. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +extension CGSize { + static let albumListWidth = (UIScreen.main.bounds.width - 60) / 2 + static let albumDetailWidth = UIScreen.main.bounds.width - 40 + + static let queueSongCoverSize = CGSize(width: 32, height: 32) + static let albumListCoverSize = CGSize(width: albumListWidth, height: albumListWidth) + static let albumDetailCoverSize = CGSize(width: albumDetailWidth, height: albumDetailWidth) + static let currentlyPlayingCoverSize = albumDetailCoverSize + static let notificationCoverSize = albumListCoverSize +} diff --git a/iOS/Components/Shared/Extensions/UIFont.swift b/iOS/Components/Shared/Extensions/UIFont.swift new file mode 100644 index 0000000..ee12806 --- /dev/null +++ b/iOS/Components/Shared/Extensions/UIFont.swift @@ -0,0 +1,13 @@ +// +// UIFont.swift +// Persephone-iOS +// +// Created by Daniel Barber on 2020-3-31. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +extension UIFont { + static let timerFont = monospacedDigitSystemFont(ofSize: 16, weight: .regular) +} diff --git a/iOS/Components/Shared/Extensions/UIImage.swift b/iOS/Components/Shared/Extensions/UIImage.swift new file mode 100644 index 0000000..ea3b955 --- /dev/null +++ b/iOS/Components/Shared/Extensions/UIImage.swift @@ -0,0 +1,15 @@ +// +// UIImage.swift +// Persephone-iOS +// +// Created by Dan Barber on 2020-6-18. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +extension UIImage { + static let defaultCoverArt = UIImage(named: "defaultCoverArt") + static let playIconLarge = UIImage(named: "playButtonLarge") + static let pauseIconLarge = UIImage(named: "pauseButtonLarge") +} diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift new file mode 100644 index 0000000..a601459 --- /dev/null +++ b/iOS/SceneDelegate.swift @@ -0,0 +1,59 @@ +// +// SceneDelegate.swift +// Persephone-iOS +// +// Created by Daniel Barber on 2020-3-13. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + + _ = App.mpdServerController + + App.store.dispatch(UpdateServerHost(host: "192.168.4.31")) + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + App.mpdServerController.disconnect() + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + App.mpdServerController.connect() + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} +