diff --git a/flo/ArtistDetailView.swift b/flo/ArtistDetailView.swift index c87d211..b57c918 100644 --- a/flo/ArtistDetailView.swift +++ b/flo/ArtistDetailView.swift @@ -39,7 +39,7 @@ struct ArtistDetailView: View { .padding(.bottom, 3) .frame(maxWidth: .infinity, alignment: .leading) - Text(stripBiography(biography: artist.biography)) + Text(stripBiography(biography: artist.biography ?? "")) .customFont(.subheadline) .lineSpacing(3) .multilineTextAlignment(.leading) diff --git a/flo/ArtistsView.swift b/flo/ArtistsView.swift index c9da2eb..3287176 100644 --- a/flo/ArtistsView.swift +++ b/flo/ArtistsView.swift @@ -11,17 +11,15 @@ struct ArtistsView: View { @EnvironmentObject private var viewModel: AlbumViewModel @State private var searchArtist = "" + @State private var filterAlbumArtistOnly: Bool = true let artists: [Artist] var filteredArtists: [Artist] { - if searchArtist.isEmpty { - return artists - } else { - return artists.filter { artist in - artist.name.localizedCaseInsensitiveContains(searchArtist) - || artist.fullText.localizedCaseInsensitiveContains(searchArtist) - } + artists.filter { artist in + let matchesAlbumArtist = !filterAlbumArtistOnly || artist.stats.albumartist != nil + let matchesSearch = searchArtist.isEmpty || artist.name.localizedCaseInsensitiveContains(searchArtist) + return matchesAlbumArtist && matchesSearch } } @@ -39,16 +37,16 @@ struct ArtistsView: View { Text(artist.name) .customFont(.headline) .multilineTextAlignment(.leading) - + Spacer() - + Image(systemName: "chevron.right") .foregroundColor(.gray) .font(.caption) } .padding(.horizontal) .padding(.vertical, 5) - + Divider() } } @@ -59,6 +57,17 @@ struct ArtistsView: View { .searchable( text: $searchArtist, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search" ) + .toolbar { + Menu { + Button { + self.filterAlbumArtistOnly.toggle() + } label: { + Label("Album Artist Only", systemImage: self.filterAlbumArtistOnly ? "checkmark.circle" : "circle") + } + } label: { + Label("", systemImage: "ellipsis.circle") + } + } } } } diff --git a/flo/Resources/Localizable.xcstrings b/flo/Resources/Localizable.xcstrings index 46e5278..24112ae 100644 --- a/flo/Resources/Localizable.xcstrings +++ b/flo/Resources/Localizable.xcstrings @@ -190,6 +190,9 @@ } } } + }, + "Album Artist Only" : { + }, "Album Info" : { "localizations" : { diff --git a/flo/Shared/Models/Artist.swift b/flo/Shared/Models/Artist.swift index ffc8b2e..a5a5dab 100644 --- a/flo/Shared/Models/Artist.swift +++ b/flo/Shared/Models/Artist.swift @@ -7,18 +7,48 @@ import Foundation -struct Artist: Codable, Identifiable, Hashable { - let id: String - let name: String - let fullText: String - let biography: String +struct Artist: Codable, Hashable, Identifiable { + static func == (lhs: Artist, rhs: Artist) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + let id, name, orderArtistName: String + let stats: ArtistStats + let size, albumCount, songCount: Int + let missing: Bool + let createdAt, updatedAt: String + let sortArtistName: String? + let playCount: Int? + let playDate, mbzArtistID, biography: String? + let smallImageURL, mediumImageURL, largeImageURL: String? + let externalURL: String? + let externalInfoUpdatedAt: String? + let fullText: String? + + enum CodingKeys: String, CodingKey { + case id, name, orderArtistName, stats, size, albumCount, songCount, missing, createdAt, updatedAt, sortArtistName, playCount, playDate, fullText + case mbzArtistID = "mbzArtistId" + case biography + case smallImageURL = "smallImageUrl" + case mediumImageURL = "mediumImageUrl" + case largeImageURL = "largeImageUrl" + case externalURL = "externalUrl" + case externalInfoUpdatedAt + } +} - init(from decoder: any Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) +// MARK: - Stats +struct ArtistStats: Codable { + let producer, composer, artist, maincredit: Albumartist? + let albumartist, arranger, engineer, performer: Albumartist? + let mixer, lyricist, conductor: Albumartist? +} - self.id = try container.decode(String.self, forKey: .id) - self.name = try container.decode(String.self, forKey: .name) - self.fullText = try container.decodeIfPresent(String.self, forKey: .fullText) ?? "" - self.biography = try container.decodeIfPresent(String.self, forKey: .biography) ?? "" - } +// MARK: - Albumartist +struct Albumartist: Codable { + let songCount, albumCount, size: Int }