Archived
1

Reorganized model for 'Albums' section

This commit is contained in:
Kim Wittenburg
2015-09-11 16:38:17 +02:00
committed by Kim Wittenburg
parent 80f177807d
commit 5704d0c0e5
2 changed files with 217 additions and 61 deletions

View File

@@ -71,32 +71,35 @@ internal class MainViewController: NSViewController {
/// The error that occured during searching, if any.
internal private(set) var searchError: NSError?
/// The URL tasks currently loading the tracks for the respective albums.
private var trackTasks = [Album: NSURLSessionDataTask]()
/// Errors that occured during loading the tracks for the respective album.
private var trackErrors = [Album: NSError]()
// MARK: View Life Cycle
private var observerObject: NSObjectProtocol?
override internal func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserverForName(AlbumCollection.AlbumFinishedLoadingNotificationName, object: albumCollection, queue: NSOperationQueue.mainQueue(), usingBlock: albumCollectionDidFinishLoadingTracks)
outlineView.setDraggingSourceOperationMask(.Move, forLocal: true)
outlineView.registerForDraggedTypes([OutlineViewConstants.pasteboardType])
}
deinit {
if let observer = observerObject {
NSNotificationCenter.defaultCenter().removeObserver(observer)
}
}
// MARK: Outline View Content
internal private(set) var searchResults = [SearchResult]()
internal private(set) var albums = [Album]()
internal let albumCollection = AlbumCollection()
internal private(set) var unsortedTracks = [iTunesTrack]()
/// Returns all `iTunesTrack` objects that are somewhere down the outline
/// view.
private var allITunesTracks: Set<iTunesTrack> {
return Set(unsortedTracks).union(albums.flatMap({ $0.tracks.flatMap { $0.associatedTracks } }))
return Set(unsortedTracks).union(albumCollection.flatMap({ $0.tracks.flatMap { $0.associatedTracks } }))
}
/// Returns all contents of the outline view.
@@ -117,11 +120,11 @@ internal class MainViewController: NSViewController {
} else {
contents.append(OutlineViewConstants.Items.noResultsItem)
}
if !albums.isEmpty {
if !albumCollection.isEmpty {
contents.append(OutlineViewConstants.Items.albumsHeaderItem)
}
}
contents.appendContentsOf(albums as [AnyObject])
contents.appendContentsOf(albumCollection.albums as [AnyObject])
if !unsortedTracks.isEmpty {
contents.append(OutlineViewConstants.Items.unsortedTracksHeaderItem)
contents.appendContentsOf(unsortedTracks as [AnyObject])
@@ -159,15 +162,16 @@ internal class MainViewController: NSViewController {
return nil
}
// TODO: Can this algorithm be improved?
/// Returns the section the specified item resides in or `nil` if the item is
/// not part of the outline view's contents.
internal func sectionOfItem(item: AnyObject) -> Section? {
if let album = item as? Album where albums.contains(album) {
if let album = item as? Album where albumCollection.contains(album) {
return .Albums
} else if let track = item as? Track where albums.contains(track.album) {
} else if let track = item as? Track where albumCollection.contains(track.album) {
return .Albums
} else if let track = item as? iTunesTrack {
if let parentTrack = outlineView.parentForItem(track) as? Track where albums.contains(parentTrack.album) {
if let parentTrack = outlineView.parentForItem(track) as? Track where albumCollection.contains(parentTrack.album) {
return .Albums
} else {
return unsortedTracks.contains(track) ? .UnsortedTracks : nil
@@ -181,11 +185,6 @@ internal class MainViewController: NSViewController {
}
}
/// Returns `true` if the specified `album` is currently loading its tracks.
internal func isAlbumLoading(album: Album) -> Bool {
return trackTasks[album] != nil
}
// MARK: Searching
/// Starts a search for the specified search term. Calling this method
@@ -246,51 +245,24 @@ internal class MainViewController: NSViewController {
showsSearch = false
}
var albumAlreadyPresent = false
for album in albums {
for album in albumCollection {
if album == searchResult {
albumAlreadyPresent = true
}
}
if !albumAlreadyPresent {
albums.append(beginLoadingTracksForSearchResult(searchResult))
let album = Album(searchResult: searchResult)
albumCollection.addAlbum(album, beginLoading: true)
}
outlineView.reloadData()
}
private func albumCollectionDidFinishLoadingTracks(notification: NSNotification) {
outlineView.reloadData()
}
// MARK: Albums
private func beginLoadingTracksForSearchResult(searchResult: SearchResult) -> Album {
let album = Album(searchResult: searchResult)
let url = iTunesAPI.createAlbumLookupURLForId(album.id)
let task = urlSession.dataTaskWithURL(url) { (data, response, var error) -> Void in
self.trackTasks[album] = nil
do {
if let theData = data where error == nil {
let newAlbum = try iTunesAPI.parseAPIData(theData)[0]
let index = self.albums.indexOf(album)!
self.albums.removeAtIndex(index)
self.albums.insert(newAlbum, atIndex: index)
}
} catch let theError as NSError {
error = theError
} catch _ {
// Will never happen
}
self.trackErrors[album] = error
self.outlineView.reloadData()
}
trackTasks[album] = task
task.resume()
return album
}
func cancelLoadingTracksForAlbum(album: Album) {
trackTasks[album]?.cancel()
trackTasks[album] = nil
trackErrors[album] = NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: nil)
}
private func saveTracks(tracks: [Track: [iTunesTrack]]) {
let numberOfTracks = tracks.reduce(0) { (count: Int, element: (key: Track, value: [iTunesTrack])) -> Int in
return count + element.value.count
@@ -434,9 +406,7 @@ internal class MainViewController: NSViewController {
for (row, item) in items {
if sectionOfRow(row)! != .SearchResults {
if let album = item as? Album {
cancelLoadingTracksForAlbum(album)
trackErrors[album] = nil
albums.removeElement(album)
albumCollection.removeAlbum(album)
} else if let track = item as? Track {
track.associatedTracks = []
} else if let track = item as? iTunesTrack {
@@ -465,7 +435,7 @@ internal class MainViewController: NSViewController {
if let theError = item as? NSError {
error = theError
} else if let album = item as? Album {
if let theError = trackErrors[album] {
if let theError = albumCollection.errorForAlbum(album) {
error = theError
} else {
return
@@ -506,8 +476,9 @@ extension MainViewController {
if error == searchError {
} else {
for (album, trackError) in trackErrors {
if error == trackError {
for album in albumCollection {
let albumError = albumCollection.errorForAlbum(album)
if error == albumError {
}
}
@@ -671,7 +642,7 @@ extension MainViewController: NSOutlineViewDataSource, NSOutlineViewDelegate {
view = AlbumTableCellView()
view?.identifier = OutlineViewConstants.ViewIdentifiers.albumTableCellViewIdentifier
}
view?.setupForAlbum(album, loading: isAlbumLoading(album), error: trackErrors[album])
view?.setupForAlbum(album, loading: albumCollection.isAlbumLoading(album), error: albumCollection.errorForAlbum(album))
view?.errorButton?.target = self
view?.errorButton?.action = "showErrorDetails:"
return view
@@ -684,7 +655,7 @@ extension MainViewController: NSOutlineViewDataSource, NSOutlineViewDelegate {
}
view?.button.target = self
view?.button.action = "selectSearchResult:"
let selectable = albums.filter { $0.id == searchResult.id }.isEmpty
let selectable = albumCollection.filter { $0.id == searchResult.id }.isEmpty
view?.setupForSearchResult(searchResult, selectable: selectable)
return view
}
@@ -751,7 +722,7 @@ extension MainViewController: NSOutlineViewDataSource, NSOutlineViewDelegate {
if sectionOfRow(row) == .SearchResults {
return .None
}
if let album = item as? Album where isAlbumLoading(album) || trackErrors[album] != nil {
if let album = item as? Album where albumCollection.isAlbumLoading(album) || albumCollection.errorForAlbum(album) != nil {
return .None
}
return .Every