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

Add album list and basic album detail view

This commit is contained in:
Daniel Barber 2020-03-30 14:09:07 -04:00 committed by Dan Barber
parent 84a452931e
commit d91d768735
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
52 changed files with 962 additions and 19 deletions

View File

@ -1,14 +0,0 @@
//
// NSColor.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/16.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
extension CGColor {
static let albumBorderColorLight = NSColor.black.withAlphaComponent(0.15).cgColor
static let albumBorderColorDark = NSColor.white.withAlphaComponent(0.15).cgColor
}

View File

@ -19,6 +19,23 @@
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 /* AlbumViewController+UICollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E412220B2431425400473C1D /* AlbumViewController+UICollectionViewDataSource.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 */; };
E412222C2431830500473C1D /* AlbumDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E412222B2431830500473C1D /* AlbumDetailViewController.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 */; };
@ -391,6 +408,23 @@
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 = "<group>"; };
E41221F82431425300473C1D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E41221FA2431425300473C1D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E41221FB2431425300473C1D /* Persephone_iOSUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persephone_iOSUITests.swift; sourceTree = "<group>"; };
E41221FD2431425400473C1D /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
E41221FE2431425400473C1D /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
E41222002431425400473C1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
E41222022431425400473C1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
E41222032431425400473C1D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E41222042431425400473C1D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E41222052431425400473C1D /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
E41222092431425400473C1D /* AlbumItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumItemCell.swift; sourceTree = "<group>"; };
E412220A2431425400473C1D /* AlbumViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = "<group>"; };
E412220B2431425400473C1D /* AlbumViewController+UICollectionViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AlbumViewController+UICollectionViewDataSource.swift"; sourceTree = "<group>"; };
E412220C2431425400473C1D /* AlbumViewController+UICollectionViewDelegateFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AlbumViewController+UICollectionViewDelegateFlowLayout.swift"; sourceTree = "<group>"; };
E41222272431539800473C1D /* CGSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSize.swift; sourceTree = "<group>"; };
E412222B2431830500473C1D /* AlbumDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailViewController.swift; sourceTree = "<group>"; };
E419E2862249B96600216A8C /* Song.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Song.swift; sourceTree = "<group>"; };
E41B22C421FB715A00D544F6 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
E41B22C521FB932700D544F6 /* MPDClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDClient.swift; sourceTree = "<group>"; };
@ -678,6 +712,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E41222232431530900473C1D /* Kingfisher in Frameworks */,
E480513824255CE000362CF3 /* ReSwift in Frameworks */,
E480513A24255CF200362CF3 /* CryptoSwift in Frameworks */,
);
@ -705,6 +740,9 @@
children = (
E45E4FD822515D87004B537F /* Brewfile */,
E42A8F3922176D6400A13ED9 /* LICENSE.md */,
E41221FC2431425300473C1D /* iOS */,
E41221F62431425300473C1D /* iOSTests */,
E41221F92431425300473C1D /* iOSUITests */,
E407861A2110CE6E006887B1 /* Mac */,
E407862D2110CE70006887B1 /* MacTests */,
E40786382110CE70006887B1 /* MacUITests */,
@ -765,15 +803,14 @@
E408D3B7220DE8CC0006D9BE /* Extensions */ = {
isa = PBXGroup;
children = (
E4928E0A2218D62A001D4BEA /* 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 = "<group>";
@ -804,6 +841,7 @@
E411C268241C02B2008B9682 /* Shared */ = {
isa = PBXGroup;
children = (
E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */,
E483CE67242FEF82001F742E /* Delegates */,
E48056C22426D73300362CF3 /* libmpdclient */,
E48051512425607100362CF3 /* Controllers */,
@ -820,11 +858,98 @@
E411C29D241C123C008B9682 /* Extensions */ = {
isa = PBXGroup;
children = (
E4928E0A2218D62A001D4BEA /* CGColor.swift */,
E408D3B5220DD8970006D9BE /* Notification.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
E41221F62431425300473C1D /* iOSTests */ = {
isa = PBXGroup;
children = (
E41221F82431425300473C1D /* Info.plist */,
E41221F72431425300473C1D /* Persephone_iOSTests.swift */,
);
path = iOSTests;
sourceTree = "<group>";
};
E41221F92431425300473C1D /* iOSUITests */ = {
isa = PBXGroup;
children = (
E41221FA2431425300473C1D /* Info.plist */,
E41221FB2431425300473C1D /* Persephone_iOSUITests.swift */,
);
path = iOSUITests;
sourceTree = "<group>";
};
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 = "<group>";
};
E41222062431425400473C1D /* Components */ = {
isa = PBXGroup;
children = (
E41222252431538700473C1D /* Shared */,
E41222072431425400473C1D /* Browser */,
);
path = Components;
sourceTree = "<group>";
};
E41222072431425400473C1D /* Browser */ = {
isa = PBXGroup;
children = (
E412222A243182F700473C1D /* Album Detail */,
E41222082431425400473C1D /* Album Browser */,
);
path = Browser;
sourceTree = "<group>";
};
E41222082431425400473C1D /* Album Browser */ = {
isa = PBXGroup;
children = (
E41222092431425400473C1D /* AlbumItemCell.swift */,
E412220A2431425400473C1D /* AlbumViewController.swift */,
E412220B2431425400473C1D /* AlbumViewController+UICollectionViewDataSource.swift */,
E412220C2431425400473C1D /* AlbumViewController+UICollectionViewDelegateFlowLayout.swift */,
);
path = "Album Browser";
sourceTree = "<group>";
};
E41222252431538700473C1D /* Shared */ = {
isa = PBXGroup;
children = (
E41222262431539000473C1D /* Extensions */,
);
path = Shared;
sourceTree = "<group>";
};
E41222262431539000473C1D /* Extensions */ = {
isa = PBXGroup;
children = (
E41222272431539800473C1D /* CGSize.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
E412222A243182F700473C1D /* Album Detail */ = {
isa = PBXGroup;
children = (
E412222B2431830500473C1D /* AlbumDetailViewController.swift */,
);
path = "Album Detail";
sourceTree = "<group>";
};
E41B22C721FB966C00D544F6 /* include */ = {
isa = PBXGroup;
children = (
@ -929,10 +1054,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 +1068,6 @@
E442CCC92347D6FD00004E0C /* Shared */ = {
isa = PBXGroup;
children = (
E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */,
E4E13C2C2350D8CB00092A6E /* Layouts */,
E408D3B7220DE8CC0006D9BE /* Extensions */,
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */,
@ -1385,6 +1509,7 @@
packageProductDependencies = (
E480513724255CE000362CF3 /* ReSwift */,
E480513924255CF200362CF3 /* CryptoSwift */,
E41222222431530900473C1D /* Kingfisher */,
);
productName = "Persephone-iOS";
productReference = E411C26D241C10F0008B9682 /* Persephone.app */;
@ -1546,12 +1671,15 @@
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 */,
E48059132426D73600362CF3 /* win32.txt in Resources */,
E48059C12426D73600362CF3 /* libmpdclient.vapi in Resources */,
E41222162431425400473C1D /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1782,11 +1910,13 @@
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 */,
@ -1802,17 +1932,20 @@
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 */,
E41222182431425400473C1D /* AlbumItemCell.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 */,
@ -1823,6 +1956,7 @@
E480513124255BDF00362CF3 /* MPDStatus.swift in Sources */,
E480514424255E7700362CF3 /* PlayerReducer.swift in Sources */,
E480514524255E7700362CF3 /* PreferencesReducer.swift in Sources */,
E41222292431555100473C1D /* CGColor.swift in Sources */,
E480511F24255BDB00362CF3 /* MPDClient+Album.swift in Sources */,
E4805A0B2426D73600362CF3 /* sync.c in Sources */,
E4805A052426D73600362CF3 /* connection.c in Sources */,
@ -1843,21 +1977,26 @@
E480513024255BDF00362CF3 /* MPDSong.swift in Sources */,
E480511524255BAF00362CF3 /* DraggedSongType.swift in Sources */,
E480512B24255BDF00362CF3 /* MPDAlbum.swift in Sources */,
E412221A2431425400473C1D /* AlbumViewController+UICollectionViewDataSource.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 */,
E480512E24255BDF00362CF3 /* MPDIdle.swift in Sources */,
E48059E92426D73600362CF3 /* queue.c in Sources */,
E48059D12426D73600362CF3 /* send.c in Sources */,
E412222C2431830500473C1D /* AlbumDetailViewController.swift in Sources */,
E4805A1B2426D73600362CF3 /* cmessage.c in Sources */,
E48059E52426D73600362CF3 /* run.c in Sources */,
E480514D24255E7D00362CF3 /* ServerActions.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 +2027,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E412221C2431431500473C1D /* Persephone_iOSTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1895,6 +2035,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E412221F2431432100473C1D /* Persephone_iOSUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1932,6 +2073,22 @@
name = Main.storyboard;
sourceTree = "<group>";
};
E41221FF2431425400473C1D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
E41222002431425400473C1D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
E41222012431425400473C1D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
E41222022431425400473C1D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@ -2527,6 +2684,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" */;

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

View File

@ -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)
}

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"filename" : "blankAlbumLight.pdf",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "blankAlbumDark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "playButtonLarge.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="K6U-7P-Keb">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="collection view cell content view" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Album View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="AlbumViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="u9f-Xg-Xs3">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="iSU-Ww-625">
<size key="itemSize" width="171" height="207"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AlbumViewCell" id="AqS-aJ-qh6" customClass="AlbumItemCell" customModule="Persephone" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="171" height="207"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<collectionViewCellContentView key="contentView" autoresizesSubviews="NO" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="gmr-FF-FMp">
<rect key="frame" x="0.0" y="0.0" width="171" height="207"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="defaultCoverArt" translatesAutoresizingMaskIntoConstraints="NO" id="eNz-Ou-6la">
<rect key="frame" x="0.0" y="0.0" width="171" height="171"/>
<constraints>
<constraint firstAttribute="width" secondItem="eNz-Ou-6la" secondAttribute="height" multiplier="1:1" id="n5p-sc-9rg"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Album Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NLE-dn-N7W">
<rect key="frame" x="0.0" y="177" width="171" height="18"/>
<fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Album Artist" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RUQ-Qx-VQi">
<rect key="frame" x="0.0" y="191" width="171" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="RUQ-Qx-VQi" firstAttribute="trailing" secondItem="NLE-dn-N7W" secondAttribute="trailing" id="3ah-xV-cff"/>
<constraint firstAttribute="bottom" secondItem="RUQ-Qx-VQi" secondAttribute="bottom" id="VW9-i1-Y7B"/>
<constraint firstItem="eNz-Ou-6la" firstAttribute="leading" secondItem="gmr-FF-FMp" secondAttribute="leading" id="XMv-8p-ZCn"/>
<constraint firstItem="RUQ-Qx-VQi" firstAttribute="leading" secondItem="NLE-dn-N7W" secondAttribute="leading" id="cTY-NU-nXy"/>
<constraint firstItem="NLE-dn-N7W" firstAttribute="leading" secondItem="gmr-FF-FMp" secondAttribute="leading" id="cXj-dh-Rdo"/>
<constraint firstAttribute="trailing" secondItem="eNz-Ou-6la" secondAttribute="trailing" id="dPc-Nv-BZY"/>
<constraint firstAttribute="trailing" secondItem="NLE-dn-N7W" secondAttribute="trailing" id="jjb-av-JT8"/>
<constraint firstItem="eNz-Ou-6la" firstAttribute="top" secondItem="gmr-FF-FMp" secondAttribute="top" id="pYf-gW-8m7"/>
<constraint firstItem="RUQ-Qx-VQi" firstAttribute="top" secondItem="eNz-Ou-6la" secondAttribute="bottom" constant="20" id="utB-aD-k6K"/>
<constraint firstItem="NLE-dn-N7W" firstAttribute="top" secondItem="eNz-Ou-6la" secondAttribute="bottom" constant="6" id="zyQ-R1-v35"/>
</constraints>
</collectionViewCellContentView>
<size key="customSize" width="171" height="207"/>
<connections>
<outlet property="albumArtist" destination="RUQ-Qx-VQi" id="r0K-2i-kak"/>
<outlet property="albumCoverView" destination="eNz-Ou-6la" id="fPd-Sn-Dke"/>
<outlet property="albumTitle" destination="NLE-dn-N7W" id="Chx-gy-kPL"/>
<segue destination="ii3-nU-e6h" kind="show" id="Fww-At-iOj"/>
</connections>
</collectionViewCell>
</cells>
</collectionView>
<navigationItem key="navigationItem" id="tsN-Dk-Jan"/>
<connections>
<outlet property="albumCollectionView" destination="u9f-Xg-Xs3" id="Gaj-EK-kIM"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1815.9420289855075" y="138.61607142857142"/>
</scene>
<!--Album Detail View Controller-->
<scene sceneID="7Uf-kw-bl3">
<objects>
<viewController id="ii3-nU-e6h" customClass="AlbumDetailViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="VGR-kF-UUm">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XIW-jv-ioG">
<rect key="frame" x="0.0" y="88" width="414" height="725"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="defaultCoverArt" translatesAutoresizingMaskIntoConstraints="NO" id="x4m-cJ-Efb">
<rect key="frame" x="20" y="30" width="179" height="179"/>
<constraints>
<constraint firstAttribute="width" secondItem="x4m-cJ-Efb" secondAttribute="height" multiplier="1:1" id="3yv-0S-X2C"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Album Artist" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HNh-tV-Sqi">
<rect key="frame" x="215" y="53" width="179" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Album Title" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hz5-e6-wXe">
<rect key="frame" x="215" y="30" width="179" height="21.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MqK-AJ-gyc">
<rect key="frame" x="20" y="225" width="179" height="45"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="jk9-EB-TOt"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
<state key="normal" title="Play" image="playButtonLarge">
<color key="titleColor" systemColor="linkColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="playAlbumAction:" destination="ii3-nU-e6h" eventType="touchUpInside" id="xSY-YL-t6L"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="HNh-tV-Sqi" firstAttribute="leading" secondItem="x4m-cJ-Efb" secondAttribute="trailing" constant="16" id="5Pa-0J-SCI"/>
<constraint firstItem="hz5-e6-wXe" firstAttribute="leading" secondItem="x4m-cJ-Efb" secondAttribute="trailing" constant="16" id="ACA-EC-ptE"/>
<constraint firstItem="HNh-tV-Sqi" firstAttribute="top" secondItem="hz5-e6-wXe" secondAttribute="bottom" constant="1.5" id="DUa-dT-pJ1"/>
<constraint firstItem="hz5-e6-wXe" firstAttribute="width" secondItem="x4m-cJ-Efb" secondAttribute="width" id="FiT-Cz-jtL"/>
<constraint firstItem="MqK-AJ-gyc" firstAttribute="leading" secondItem="UzT-Em-gMm" secondAttribute="leading" constant="20" id="Hsf-wJ-Jgi"/>
<constraint firstItem="HNh-tV-Sqi" firstAttribute="width" secondItem="hz5-e6-wXe" secondAttribute="width" id="MnT-pH-wBT"/>
<constraint firstItem="MqK-AJ-gyc" firstAttribute="width" secondItem="x4m-cJ-Efb" secondAttribute="width" id="OVe-wp-v9Y"/>
<constraint firstItem="x4m-cJ-Efb" firstAttribute="leading" secondItem="UzT-Em-gMm" secondAttribute="leading" constant="20" id="OtV-id-weM"/>
<constraint firstItem="x4m-cJ-Efb" firstAttribute="top" secondItem="UzT-Em-gMm" secondAttribute="top" constant="30" id="U0O-oU-iaU"/>
<constraint firstItem="MqK-AJ-gyc" firstAttribute="top" secondItem="x4m-cJ-Efb" secondAttribute="bottom" constant="16" id="akc-3M-fpI"/>
<constraint firstItem="UzT-Em-gMm" firstAttribute="trailing" secondItem="HNh-tV-Sqi" secondAttribute="trailing" constant="20" id="hcQ-WE-uZP"/>
<constraint firstItem="hz5-e6-wXe" firstAttribute="top" secondItem="UzT-Em-gMm" secondAttribute="top" constant="30" id="na1-d0-EdD"/>
<constraint firstItem="UzT-Em-gMm" firstAttribute="trailing" secondItem="hz5-e6-wXe" secondAttribute="trailing" constant="20" id="uE2-Pg-mgq"/>
</constraints>
<viewLayoutGuide key="contentLayoutGuide" id="fqj-4d-6vE"/>
<viewLayoutGuide key="frameLayoutGuide" id="UzT-Em-gMm"/>
</scrollView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="XIW-jv-ioG" firstAttribute="trailing" secondItem="xgx-ch-8Vx" secondAttribute="trailing" id="9JH-zm-iwv"/>
<constraint firstItem="XIW-jv-ioG" firstAttribute="leading" secondItem="xgx-ch-8Vx" secondAttribute="leading" id="Ee7-z5-6hs"/>
<constraint firstItem="XIW-jv-ioG" firstAttribute="top" secondItem="xgx-ch-8Vx" secondAttribute="top" id="N81-n7-eIq"/>
<constraint firstItem="XIW-jv-ioG" firstAttribute="bottom" secondItem="xgx-ch-8Vx" secondAttribute="bottom" id="NaK-3i-hav"/>
<constraint firstItem="XIW-jv-ioG" firstAttribute="top" secondItem="xgx-ch-8Vx" secondAttribute="top" id="VQc-pH-bzV"/>
<constraint firstItem="xgx-ch-8Vx" firstAttribute="trailing" secondItem="XIW-jv-ioG" secondAttribute="trailing" id="WsF-4E-Zjl"/>
<constraint firstItem="XIW-jv-ioG" firstAttribute="trailing" secondItem="xgx-ch-8Vx" secondAttribute="trailing" id="cvb-Ov-jPf"/>
<constraint firstItem="XIW-jv-ioG" firstAttribute="leading" secondItem="xgx-ch-8Vx" secondAttribute="leading" id="ldd-Mx-jjs"/>
<constraint firstItem="XIW-jv-ioG" firstAttribute="bottom" secondItem="xgx-ch-8Vx" secondAttribute="bottom" id="tp0-RF-fNB"/>
</constraints>
<viewLayoutGuide key="safeArea" id="xgx-ch-8Vx"/>
</view>
<navigationItem key="navigationItem" id="6fY-yh-Q6C"/>
<connections>
<outlet property="albumArtist" destination="HNh-tV-Sqi" id="3Bm-Kp-Tyf"/>
<outlet property="albumCoverView" destination="x4m-cJ-Efb" id="KBK-5d-7jT"/>
<outlet property="albumTitle" destination="hz5-e6-wXe" id="tsd-FD-rbX"/>
<outlet property="playAlbumButton" destination="MqK-AJ-gyc" id="05F-91-XS7"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ru6-mu-Q6h" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2553.6231884057975" y="138.61607142857142"/>
</scene>
<!--Tab Bar Controller-->
<scene sceneID="MfI-fd-cqh">
<objects>
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="K6U-7P-Keb" sceneMemberID="viewController">
<toolbarItems/>
<tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="7Nx-g8-JPS">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tabBar>
<connections>
<segue destination="9Vi-iO-P5G" kind="relationship" relationship="viewControllers" id="d6O-R2-Xvc"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="knf-OK-U9P" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="131.8840579710145" y="138.61607142857142"/>
</scene>
<!--Albums-->
<scene sceneID="fsq-t8-GDw">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="9Vi-iO-P5G" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Albums" image="square.grid.2x2.fill" catalog="system" id="neh-3P-aiC"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="fhl-gx-pxj">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="ZGc-1O-hMf"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Hvo-yX-dBI" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="905.79710144927549" y="138.61607142857142"/>
</scene>
</scenes>
<resources>
<image name="defaultCoverArt" width="128" height="128"/>
<image name="playButtonLarge" width="22" height="22"/>
<image name="square.grid.2x2.fill" catalog="system" width="128" height="114"/>
</resources>
</document>

View File

@ -0,0 +1,76 @@
//
// 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 = 0.5
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),
]
) { result in
switch result {
case .success(let imageResult):
guard let imageData = imageResult.image.pngData()
else { return }
let _ = RawImageDataProvider(
data: imageData,
cacheKey: album.hash
)
case .failure(_):
break
}
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
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!
}

View File

@ -0,0 +1,33 @@
//
// AlbumDataSource.swift
// Persephone
//
// Created by Daniel Barber on 2019/2/20.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import UIKit
extension AlbumViewController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return albums.count
}
override 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 }
//albumViewItem.view.wantsLayer = true
albumViewCell.setAlbum(albums[indexPath.item])
return albumViewCell
}
//
// override func indexTitles(for collectionView: UICollectionView) -> [String]? {
// return ["#"]
// }
//
// override func collectionView(_ collectionView: UICollectionView, indexPathForIndexTitle title: String, at index: Int) -> IndexPath {
// return IndexPath(index: 0)
// }
}

View File

@ -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
}
}

View File

@ -0,0 +1,67 @@
//
// 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 {
var albums: [Album] = []
let itemsPerRow: CGFloat = 2
let sectionInsets = UIEdgeInsets(
top: 20.0,
left: 20.0,
bottom: 30.0,
right: 20.0
)
override func viewDidLoad() {
super.viewDidLoad()
App.store.subscribe(self) {
$0.select { $0.albumListState }
}
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
title = "Albums"
navigationController?.navigationBar.prefersLargeTitles = true
}
@objc func didConnect() {
App.mpdClient.fetchAllAlbums()
}
@objc func willDisconnect() {
DispatchQueue.main.async {
App.store.dispatch(UpdateAlbumListAction(albums: []))
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let detailView = segue.destination as? AlbumDetailViewController,
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) {
albums = state.albums
albumCollectionView.reloadData()
}
}

View File

@ -0,0 +1,98 @@
//
// AlbumDetailController.swift
// Persephone-iOS
//
// Created by Daniel Barber on 2020-3-29.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import UIKit
import Kingfisher
class AlbumDetailViewController: UIViewController {
var album: Album?
var albumSongs: [Song] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.largeTitleDisplayMode = .never
albumCoverView.layer.backgroundColor = UIColor.black.cgColor
albumCoverView.layer.cornerRadius = 4
albumCoverView.layer.borderWidth = 0.5
albumCoverView.layer.masksToBounds = true
playAlbumButton.layer.cornerRadius = 8
setAppearance()
}
override func viewWillAppear(_ animated: Bool) {
guard let album = album else { return }
albumTitle.text = album.title
albumArtist.text = album.artist
getAlbumSongs(for: album)
}
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 {
self.albumSongs = mpdSongs.map { Song(mpdSong: $0) }
//self.albumTracksView.reloadData()
guard let mpdSong = album.mpdAlbum.firstSong
else { return }
self.getBigCoverArt(song: Song(mpdSong: mpdSong), album: album)
}
}
}
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?) {
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)
}
@IBOutlet var playAlbumButton: UIButton!
@IBOutlet var albumCoverView: UIImageView!
@IBOutlet var albumTitle: UILabel!
@IBOutlet var albumArtist: UILabel!
}

View File

@ -0,0 +1,17 @@
//
// NSSize.swift
// Persephone
//
// Created by Daniel Barber on 1/20/20.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import CoreGraphics
extension CGSize {
static let queueSongCoverSize = CGSize(width: 32, height: 32)
static let albumListCoverSize = CGSize(width: 180, height: 180)
static let albumDetailCoverSize = CGSize(width: 500, height: 500)
static let currentlyPlayingCoverSize = albumDetailCoverSize
static let notificationCoverSize = albumListCoverSize
}

57
iOS/SceneDelegate.swift Normal file
View File

@ -0,0 +1,57 @@
//
// 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.1.42"))
}
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).
}
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.
}
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.
}
}