Skip to content

Commit

Permalink
feat(profile): add top albums
Browse files Browse the repository at this point in the history
  • Loading branch information
angristan committed Jul 26, 2021
1 parent 4d4b5dc commit 96c9186
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 4 deletions.
12 changes: 12 additions & 0 deletions firstfm.xcodeproj/project.pbxproj
Expand Up @@ -33,6 +33,9 @@
822E9A9626A97B7200C5307B /* TagViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 822E9A9526A97B7200C5307B /* TagViewModel.swift */; };
8240989C26ADF67F00A789D9 /* ArtistBioView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8240989B26ADF67E00A789D9 /* ArtistBioView.swift */; };
82428B2426AEC53C00AAC835 /* TopUserTracksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82428B2326AEC53C00AAC835 /* TopUserTracksView.swift */; };
82428B2626AECDBC00AAC835 /* TopUserAlbumsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82428B2526AECDBC00AAC835 /* TopUserAlbumsView.swift */; };
82428B2826AECFC700AAC835 /* TopAlbum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82428B2726AECFC700AAC835 /* TopAlbum.swift */; };
82428B2A26AED01F00AAC835 /* UserTopAlbumsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82428B2926AED01F00AAC835 /* UserTopAlbumsResponse.swift */; };
82505CDF2675074E00CCCB58 /* ArtistRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82505CDC2675074E00CCCB58 /* ArtistRow.swift */; };
82505CE02675074E00CCCB58 /* ChartListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82505CDD2675074E00CCCB58 /* ChartListView.swift */; };
82505CE12675074E00CCCB58 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82505CDE2675074E00CCCB58 /* ContentView.swift */; };
Expand Down Expand Up @@ -127,6 +130,9 @@
822E9A9526A97B7200C5307B /* TagViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagViewModel.swift; sourceTree = "<group>"; };
8240989B26ADF67E00A789D9 /* ArtistBioView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistBioView.swift; sourceTree = "<group>"; };
82428B2326AEC53C00AAC835 /* TopUserTracksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopUserTracksView.swift; sourceTree = "<group>"; };
82428B2526AECDBC00AAC835 /* TopUserAlbumsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopUserAlbumsView.swift; sourceTree = "<group>"; };
82428B2726AECFC700AAC835 /* TopAlbum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopAlbum.swift; sourceTree = "<group>"; };
82428B2926AED01F00AAC835 /* UserTopAlbumsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTopAlbumsResponse.swift; sourceTree = "<group>"; };
82505CDC2675074E00CCCB58 /* ArtistRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArtistRow.swift; sourceTree = "<group>"; };
82505CDD2675074E00CCCB58 /* ChartListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartListView.swift; sourceTree = "<group>"; };
82505CDE2675074E00CCCB58 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -274,6 +280,7 @@
822E9A8926A89BFE00C5307B /* UserTopArtistsResponse.swift */,
822E9A9126A9795200C5307B /* TagInfoResponse.swift */,
822E9A9326A97B3600C5307B /* TagTopArtistsResponse.swift */,
82428B2926AED01F00AAC835 /* UserTopAlbumsResponse.swift */,
);
path = API;
sourceTree = "<group>";
Expand Down Expand Up @@ -306,6 +313,7 @@
82A006B7267960D90009BD71 /* Track.swift */,
821493A12694C38E007A21C8 /* User.swift */,
825F0F9F26A5EC82007BA84B /* Session.swift */,
82428B2726AECFC700AAC835 /* TopAlbum.swift */,
);
path = LastFM;
sourceTree = "<group>";
Expand All @@ -332,6 +340,7 @@
82B6CCE226AB800B008AFDEF /* LastUserScrobblesView.swift */,
82B6CCE426AB80B4008AFDEF /* TopUserArtistsView.swift */,
82428B2326AEC53C00AAC835 /* TopUserTracksView.swift */,
82428B2526AECDBC00AAC835 /* TopUserAlbumsView.swift */,
);
path = Profile;
sourceTree = "<group>";
Expand Down Expand Up @@ -573,6 +582,7 @@
825F0FBF26A6F4D0007BA84B /* TintOverlayView.swift in Sources */,
825F0F9526A5E938007BA84B /* ArtistStats.swift in Sources */,
825F0F9126A5E8F5007BA84B /* SimilarArtist.swift in Sources */,
82428B2A26AED01F00AAC835 /* UserTopAlbumsResponse.swift in Sources */,
82C282EC26A370D2000E5F41 /* ScobbledTrackViewModel.swift in Sources */,
82B6CCE326AB800B008AFDEF /* LastUserScrobblesView.swift in Sources */,
82505CE02675074E00CCCB58 /* ChartListView.swift in Sources */,
Expand All @@ -587,6 +597,7 @@
825F0F8B26A5B6E1007BA84B /* NumberFormat.swift in Sources */,
825F0FA926A5F122007BA84B /* LogoutButton.swift in Sources */,
4A69011826A344FD001561C8 /* ArtistAlbum.swift in Sources */,
82428B2826AECFC700AAC835 /* TopAlbum.swift in Sources */,
82D5B4D02696DB8500716931 /* ScrobblesView.swift in Sources */,
825F0FC326A721F7007BA84B /* TopArtistTracksView.swift in Sources */,
82505CDF2675074E00CCCB58 /* ArtistRow.swift in Sources */,
Expand Down Expand Up @@ -619,6 +630,7 @@
820455E0267ABC930009A418 /* AuthViewModel.swift in Sources */,
82B6CCE126AB2832008AFDEF /* SrobblesLoggedOutView.swift in Sources */,
825F0FC526A74539007BA84B /* TopArtistAlbumsView.swift in Sources */,
82428B2626AECDBC00AAC835 /* TopUserAlbumsView.swift in Sources */,
4A8C296126983B7300F55ECC /* LastFMAPI.swift in Sources */,
822E9A9626A97B7200C5307B /* TagViewModel.swift in Sources */,
82B6CCE726AB8B00008AFDEF /* ViewLoad.swift in Sources */,
Expand Down
7 changes: 7 additions & 0 deletions firstfm/Models/LastFM/API/UserTopAlbumsResponse.swift
@@ -0,0 +1,7 @@
struct UserTopAlbumsResponse: Codable {
var topalbums: UserTopAlbums
}

struct UserTopAlbums: Codable {
var album: [TopAlbum]
}
9 changes: 9 additions & 0 deletions firstfm/Models/LastFM/TopAlbum.swift
@@ -0,0 +1,9 @@
import Foundation

struct TopAlbum: Codable {
let artist: ScrobbledArtist
var image: [LastFMImage]
let playcount: String
let url: String
let name, mbid: String
}
62 changes: 60 additions & 2 deletions firstfm/ViewModel/ProfileViewModel.swift
Expand Up @@ -9,7 +9,9 @@ class ProfileViewModel: ObservableObject {
@Published var topArtists: [Artist] = []
@Published var scrobblesPeriodPicked: Int = 5
@Published var topTracksPeriodPicked: Int = 5
@Published var topAlbumsPeriodPicked: Int = 5
@Published var topTracks: [Track] = []
@Published var topAlbums: [TopAlbum] = []
var isFriendsLoading = true
// var isLoading = true
@AppStorage("lastfm_username") var storedUsername: String?
Expand All @@ -22,6 +24,7 @@ class ProfileViewModel: ObservableObject {
self.getUserScrobbles()
self.getTopArtists(period: "overall")
self.getTopTracks(period: "overall")
self.getTopAlbums(period: "overall")
}

func getProfile(_ username: String) {
Expand Down Expand Up @@ -143,7 +146,7 @@ class ProfileViewModel: ObservableObject {

if error != nil {
DispatchQueue.main.async {
FloatingNotificationBanner(title: "Failed to load charts", subtitle: error?.localizedDescription, style: .danger).show()
FloatingNotificationBanner(title: "Failed to load artists", subtitle: error?.localizedDescription, style: .danger).show()
// Force refresh, otherwise pull loader doesn't dismiss itself
self.topArtists = self.topArtists
}
Expand Down Expand Up @@ -200,7 +203,7 @@ class ProfileViewModel: ObservableObject {

if error != nil {
DispatchQueue.main.async {
FloatingNotificationBanner(title: "Failed to load charts", subtitle: error?.localizedDescription, style: .danger).show()
FloatingNotificationBanner(title: "Failed to load tracks", subtitle: error?.localizedDescription, style: .danger).show()
// Force refresh, otherwise pull loader doesn't dismiss itself
self.topTracks = self.topTracks
}
Expand Down Expand Up @@ -230,4 +233,59 @@ class ProfileViewModel: ObservableObject {
}
}
}

func getTopAlbumsForPeriodTag(tag: Int) {
// Reset tracks to show placeholder
self.topAlbums = []

switch tag {
case 0:
self.getTopAlbums(period: "7day")
case 1:
self.getTopAlbums(period: "1month")
case 2:
self.getTopAlbums(period: "3month")
case 3:
self.getTopAlbums(period: "6month")
case 4:
self.getTopAlbums(period: "12month")
default:
self.getTopAlbums(period: "overall")
}
}

func getTopAlbums(period: String) {
LastFMAPI.request(lastFMMethod: "user.getTopAlbums", args: ["user": storedUsername ?? "", "limit": "6", "period": period]) { (data: UserTopAlbumsResponse?, error) -> Void in

if error != nil {
DispatchQueue.main.async {
FloatingNotificationBanner(title: "Failed to load albums", subtitle: error?.localizedDescription, style: .danger).show()
// Force refresh, otherwise pull loader doesn't dismiss itself
self.topAlbums = self.topAlbums
}
}

if let data = data {
var albums = data.topalbums.album

for index in albums.indices where albums[index].image[0].url == "" {
albums[index].image[0].url = "https://lastfm.freetls.fastly.net/i/u/64s/4128a6eb29f94943c9d206c08e625904.webp"
}

DispatchQueue.main.async {
self.topAlbums = albums
}

for (index, track) in data.topalbums.album.enumerated() {
SpotifyImage.findImage(type: "album", name: track.name) { imageURL in
if let imageURL = imageURL {
DispatchQueue.main.async {
self.topAlbums[index].image[0].url = imageURL
}
}
}
}
}
}
}
}
5 changes: 4 additions & 1 deletion firstfm/Views/Profile/ProfileView.swift
Expand Up @@ -95,8 +95,11 @@ struct ProfileView: View {
height: g.size.height * 0.7,
alignment: .center
)
.redacted(reason: profile.topTracks.isEmpty ? .placeholder : [])
.offset(y: -100)

TopUserAlbumsView(albums: profile.topAlbums)
.environmentObject(profile)
.offset(y: -120)
}
.edgesIgnoringSafeArea(.top)
}.edgesIgnoringSafeArea(.top)
Expand Down
88 changes: 88 additions & 0 deletions firstfm/Views/Profile/TopUserAlbumsView.swift
@@ -0,0 +1,88 @@
import SwiftUI
import Kingfisher

struct TopUserAlbumsView: View {
var albums: [TopAlbum]
@EnvironmentObject var vm: ProfileViewModel

var body: some View {
Section {
HStack {
Text("Top albums").font(.headline)
Spacer()

Menu {
Picker(selection: $vm.topAlbumsPeriodPicked, label: Text("Period")) {
Text("Last 7 days").tag(0)
Text("Last 30 days").tag(1)
Text("Last 90 days").tag(2)
Text("Last 180 days").tag(3)
Text("Last 365 days").tag(4)
Text("All time").tag(5)
}.onChange(of: vm.topAlbumsPeriodPicked) {
tag in vm.getTopAlbumsForPeriodTag(tag: tag)
}
}
label: {
Label("", systemImage: "calendar").foregroundColor(.white)
}
}.unredacted()

LazyVGrid(columns: [
GridItem(.flexible(minimum: 50, maximum: 200)),
GridItem(.flexible(minimum: 50, maximum: 200))
], spacing: 30 ) {
if !albums.isEmpty {
ForEach(albums, id: \.name) { album in
NavigationLink(
destination: AlbumView(album: ArtistAlbum(mbid: album.mbid, name: album.name, playcount: UInt(album.playcount) ?? 0, url: album.url, image: album.image )),
label: {
VStack {
if let image = album.image {
KFImage.url(URL(string: image[0].url != "" ? image[0].url : "https://lastfm.freetls.fastly.net/i/u/64s/4128a6eb29f94943c9d206c08e625904.webp" )!)
.resizable()
.loadImmediately()
.cornerRadius(5)
.aspectRatio(contentMode: .fill)
} else {
KFImage.url(URL(string: "https://lastfm.freetls.fastly.net/i/u/64s/4128a6eb29f94943c9d206c08e625904.webp" )!)
.resizable()
.loadImmediately()
.cornerRadius(5)
.aspectRatio(contentMode: .fill)
}

Text(album.name).font(.headline).lineLimit(1).foregroundColor(.white)
Text("\(Int(album.playcount)?.formatted() ?? "0") scrobbles")
.font(.subheadline)
.foregroundColor(.gray).lineLimit(1)
}
})

}
} else {
// Placeholder for redacted
ForEach((1...6), id: \.self) {_ in
NavigationLink(
destination: Color(.red),
label: {
VStack {
KFImage.url(URL(string: "https://lastfm.freetls.fastly.net/i/u/64s/4128a6eb29f94943c9d206c08e625904.webp" )!)
.resizable()
.loadImmediately()
.cornerRadius(5)
.aspectRatio(contentMode: .fill)
Text("Album name").font(.headline).lineLimit(1).foregroundColor(.white)
Text("\(String(format: "%ld", locale: Locale.current, 0) ) listeners")
.font(.subheadline)
.foregroundColor(.gray).lineLimit(1)
}
})
}

}
}
}.padding()
.redacted(reason: albums.isEmpty ? .placeholder : [])
}
}
1 change: 1 addition & 0 deletions firstfm/Views/Profile/TopUserTracksView.swift
Expand Up @@ -48,5 +48,6 @@ struct TopUserTracksView: View {

}
}.hasScrollEnabled(false)
.redacted(reason: tracks.isEmpty ? .placeholder : [])
}
}
2 changes: 1 addition & 1 deletion firstfm/Views/SubViews/TrackRowView.swift
Expand Up @@ -27,7 +27,7 @@ struct TrackRow: View {
.font(.headline)
.lineLimit(1)
Spacer()
Text("\(Int(track.playcount ?? "0" )?.formatted() ?? "0") scrobbles")
Text("\(Int(track.playcount )?.formatted() ?? "0") scrobbles")
.font(.subheadline)
.foregroundColor(.gray)
}
Expand Down

0 comments on commit 96c9186

Please sign in to comment.