no chart padding, no charts when no last.fm, rename, closes #20

This commit is contained in:
andy 2022-08-13 22:07:59 +01:00
parent 702d9b1733
commit a8842eefc7
15 changed files with 195 additions and 111 deletions

View File

@ -27,7 +27,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// Use a UIHostingController as window root view controller. // Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene { if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene) let window = UIWindow(windowScene: windowScene)
let liveUser = LiveUser(playlists: [], tags: [], username: keychain["username"] ?? "", loggedIn: false).loadUserDefaults() let liveUser = LiveUser(playlists: [], tags: [], username: keychain["username"] ?? "", loggedIn: false).load_user_defaults()
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(liveUser)) window.rootViewController = UIHostingController(rootView: contentView.environmentObject(liveUser))
self.window = window self.window = window

View File

@ -43,6 +43,16 @@ class LiveUser: ObservableObject {
self.user = user self.user = user
} }
func lastfm_connected() -> Bool {
if let username = user?.lastfm_username {
if username.count > 0 {
return true
}
}
return false
}
func logout() { func logout() {
let keychain = Keychain(service: "xyz.sarsoo.music.login") let keychain = Keychain(service: "xyz.sarsoo.music.login")
@ -65,20 +75,20 @@ class LiveUser: ObservableObject {
} }
} }
func updatePlaylist(playlistIn: Playlist) { func update_playlist(playlistIn: Playlist) {
guard let index = self.playlists.firstIndex(of: playlistIn) else { guard let index = self.playlists.firstIndex(of: playlistIn) else {
fatalError("\(playlistIn) not found") fatalError("\(playlistIn) not found")
} }
self.playlists[index] = playlistIn self.playlists[index] = playlistIn
} }
func refreshUser(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) { func refresh_user(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) {
self.isRefreshingUser = true self.isRefreshingUser = true
let api = UserApi.getUser let api = UserApi.getUser
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.checkNetworkResponse(response: response) { if self.check_network_response(response: response) {
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting user") fatalError("error getting user")
@ -106,13 +116,13 @@ class LiveUser: ObservableObject {
} }
} }
func refreshPlaylists(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) { func refresh_playlists(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) {
self.isRefreshingPlaylists = true self.isRefreshingPlaylists = true
let api = PlaylistApi.getPlaylists let api = PlaylistApi.getPlaylists
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.checkNetworkResponse(response: response) { if self.check_network_response(response: response) {
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting playlists") fatalError("error getting playlists")
@ -149,13 +159,13 @@ class LiveUser: ObservableObject {
} }
} }
func refreshTags(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) { func refresh_tags(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) {
self.isRefreshingTags = true self.isRefreshingTags = true
let api = TagApi.getTags let api = TagApi.getTags
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.checkNetworkResponse(response: response) { if self.check_network_response(response: response) {
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting tags") fatalError("error getting tags")
@ -192,7 +202,7 @@ class LiveUser: ObservableObject {
} }
} }
func checkNetworkResponse(response: AFDataResponse<Any>) -> Bool { func check_network_response(response: AFDataResponse<Any>) -> Bool {
if let statusCode = response.response?.statusCode { if let statusCode = response.response?.statusCode {
switch statusCode { switch statusCode {
@ -211,7 +221,7 @@ class LiveUser: ObservableObject {
return false return false
} }
func loadUserDefaults() -> LiveUser { func load_user_defaults() -> LiveUser {
let defaults = UserDefaults.standard let defaults = UserDefaults.standard
let decoder = JSONDecoder() let decoder = JSONDecoder()

View File

@ -26,7 +26,7 @@ class User: Identifiable, Decodable {
var last_keygen: String var last_keygen: String
var spotify_linked: Bool var spotify_linked: Bool
var lastfm_username: String? @Published var lastfm_username: String?
//MARK: Initialization //MARK: Initialization
@ -48,5 +48,51 @@ class User: Identifiable, Decodable {
self.spotify_linked = spotify_linked self.spotify_linked = spotify_linked
self.lastfm_username = lastfm_username self.lastfm_username = lastfm_username
} }
private enum CodingKeys: String, CodingKey {
case username
case email
case type
case last_login
case last_keygen
case spotify_linked
case lastfm_username
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
username = try container.decode(String.self, forKey: .username)
do{
email = try container.decode(String.self, forKey: .email)
}catch {
email = nil
debugPrint("failed to parse email")
}
type = try container.decode(UserType.self, forKey: .type)
last_login = try container.decode(String.self, forKey: .last_login)
last_keygen = try container.decode(String.self, forKey: .last_keygen)
spotify_linked = try container.decode(Bool.self, forKey: .spotify_linked)
lastfm_username = try container.decode(String.self, forKey: .lastfm_username)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.username, forKey: .username)
try container.encode(self.email, forKey: .email)
try container.encode(self.type.rawValue, forKey: .type)
try container.encode(self.last_login, forKey: .last_login)
try container.encode(self.last_keygen, forKey: .last_keygen)
try container.encode(self.spotify_linked, forKey: .spotify_linked)
try container.encode(self.lastfm_username, forKey: .lastfm_username)
}
} }

View File

@ -69,9 +69,9 @@ struct AppSkeleton: View {
} }
private func fetchAll() { private func fetchAll() {
self.liveUser.refreshUser() self.liveUser.refresh_user()
self.liveUser.refreshPlaylists() self.liveUser.refresh_playlists()
self.liveUser.refreshTags() self.liveUser.refresh_tags()
} }
} }

View File

@ -94,7 +94,7 @@ struct AddPlaylistSheet: View {
type: PlaylistType(rawValue: selectedType) ?? .defaultPlaylist) type: PlaylistType(rawValue: selectedType) ?? .defaultPlaylist)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
self.playlists.append(playlist) self.playlists.append(playlist)
self.playlists = self.playlists.sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) self.playlists = self.playlists.sorted(by: { $0.name.lowercased() < $1.name.lowercased() })

View File

@ -13,14 +13,21 @@ struct Name: Identifiable, Hashable {
var name: String var name: String
} }
enum PlaylistInputType {
case MixonomerPlaylists
case SpotifyPlaylists
}
struct PlaylistInputList: View { struct PlaylistInputList: View {
@Binding var names: [String] @Binding var names: [String]
var nameType: String var nameType: String
var type: PlaylistInputType
init(names: Binding<[String]>, nameType: String){ init(names: Binding<[String]>, nameType: String, type: PlaylistInputType){
self.nameType = nameType self.nameType = nameType
self._names = names self._names = names
self.type = type
} }
var body: some View { var body: some View {
@ -41,12 +48,14 @@ struct PlaylistInputList: View {
} }
// .id(UUID()) // .id(UUID())
.navigationBarTitle(nameType) .navigationBarTitle(nameType)
.navigationBarItems(trailing: // .navigationBarItems(trailing:
Button( // Button(
action: { }, // action: {
label: { Image(systemName: "plus.circle") } //
) // },
) // label: { Image(systemName: "plus.circle") }
// )
// )
} }
} }
@ -54,6 +63,6 @@ struct PlaylistInputList_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
PlaylistInputList(names: .constant([ PlaylistInputList(names: .constant([
"name" "name"
]), nameType: "Spotify Playlists") ]), nameType: "Spotify Playlists", type: .MixonomerPlaylists)
} }
} }

View File

@ -42,7 +42,7 @@ struct PlaylistList: View {
let api = PlaylistApi.deletePlaylist(name: self.liveUser.playlists[index].name) let api = PlaylistApi.deletePlaylist(name: self.liveUser.playlists[index].name)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
} else { } else {
@ -58,7 +58,7 @@ struct PlaylistList: View {
} }
.refreshable .refreshable
{ {
self.liveUser.refreshPlaylists(onSuccess: { self.liveUser.refresh_playlists(onSuccess: {
toastText = "Refreshed!" toastText = "Refreshed!"
toastSuccess = true toastSuccess = true

View File

@ -31,7 +31,7 @@ struct PlaylistRow: View {
.validate() .validate()
.responseJSON{ response in .responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
} }
else { else {

View File

@ -24,9 +24,23 @@ struct PlaylistView: View {
@State private var toastText = "" @State private var toastText = ""
@State private var toastSuccess = true @State private var toastSuccess = true
var chartStyle: ChartStyle { var trackChartStyle: ChartStyle {
get { get {
let _style = ChartStyle(backgroundColor: .white, accentColor: .red, gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray) let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.4765, green: 0.5976, blue: 0.7578), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray)
return _style
}
}
var albumChartStyle: ChartStyle {
get {
let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.6367, green: 0.2968, blue: 0.4648), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray)
return _style
}
}
var artistChartStyle: ChartStyle {
get {
let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.3476, green: 0.5195, blue: 0.3359), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray)
return _style return _style
} }
} }
@ -35,6 +49,9 @@ struct PlaylistView: View {
var body: some View { var body: some View {
Form { Form {
if liveUser.lastfm_connected() {
Section(header: Text("Stats")){ Section(header: Text("Stats")){
HStack { HStack {
Text("Track Count") Text("Track Count")
@ -80,27 +97,29 @@ struct PlaylistView: View {
data: [Double(self.playlist.lastfm_stat_percent), Double(100 - self.playlist.lastfm_stat_percent)], data: [Double(self.playlist.lastfm_stat_percent), Double(100 - self.playlist.lastfm_stat_percent)],
title: "Tracks", title: "Tracks",
legend:"Listening", legend:"Listening",
style: chartStyle, style: trackChartStyle,
form: chartSize) form: chartSize)
Spacer(minLength: 20) Spacer(minLength: 20)
PieChartView( PieChartView(
data: [Double(self.playlist.lastfm_stat_album_percent), Double(100 - self.playlist.lastfm_stat_album_percent)], data: [Double(self.playlist.lastfm_stat_album_percent), Double(100 - self.playlist.lastfm_stat_album_percent)],
title: "Albums", title: "Albums",
legend:"Listening", legend:"Listening",
style: chartStyle, style: albumChartStyle,
form: chartSize) form: chartSize)
Spacer(minLength: 20) Spacer(minLength: 20)
PieChartView( PieChartView(
data: [Double(self.playlist.lastfm_stat_artist_percent), Double(100 - self.playlist.lastfm_stat_artist_percent)], data: [Double(self.playlist.lastfm_stat_artist_percent), Double(100 - self.playlist.lastfm_stat_artist_percent)],
title: "Artists", title: "Artists",
legend:"Listening", legend:"Listening",
style: chartStyle, style: artistChartStyle,
form: chartSize) form: chartSize)
Spacer() Spacer()
} }
.padding([.vertical], 20) .padding([.vertical], 20)
.padding([.horizontal], 10) .padding([.horizontal], 10)
} }
.listRowInsets(EdgeInsets())
}
Section(header: Text("Options")){ Section(header: Text("Options")){
Toggle(isOn: self.$playlist.include_recommendations) { Toggle(isOn: self.$playlist.include_recommendations) {
@ -168,7 +187,7 @@ struct PlaylistView: View {
} }
} }
Section(header: Text("Inputs")){ Section(header: Text("Inputs")){
NavigationLink(destination: PlaylistInputList(names: self.$playlist.playlist_references, nameType: "Managed Playlists")) { NavigationLink(destination: PlaylistInputList(names: self.$playlist.playlist_references, nameType: "Managed Playlists", type: .MixonomerPlaylists)) {
HStack { HStack {
Text("Managed Playlists") Text("Managed Playlists")
Spacer() Spacer()
@ -177,7 +196,7 @@ struct PlaylistView: View {
} }
} }
NavigationLink(destination: PlaylistInputList(names: self.$playlist.parts, nameType: "Spotify Playlists")) { NavigationLink(destination: PlaylistInputList(names: self.$playlist.parts, nameType: "Spotify Playlists", type: .SpotifyPlaylists)) {
HStack { HStack {
Text("Spotify Playlists") Text("Spotify Playlists")
Spacer() Spacer()
@ -225,7 +244,7 @@ struct PlaylistView: View {
.validate() .validate()
.responseJSON{ response in .responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
toastText = "Running!" toastText = "Running!"
toastSuccess = true toastSuccess = true
@ -244,7 +263,7 @@ struct PlaylistView: View {
let api = PlaylistApi.refreshStats(name: playlist.name) let api = PlaylistApi.refreshStats(name: playlist.name)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
toastText = "Refreshing Stats!" toastText = "Refreshing Stats!"
toastSuccess = true toastSuccess = true
@ -270,7 +289,7 @@ struct PlaylistView: View {
let api = PlaylistApi.updatePlaylist(name: playlist.name, updates: updates) let api = PlaylistApi.updatePlaylist(name: playlist.name, updates: updates)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
debugPrint("success") debugPrint("success")
} else { } else {
debugPrint("error") debugPrint("error")
@ -283,7 +302,7 @@ struct PlaylistView: View {
let api = PlaylistApi.getPlaylist(name: self.playlist.name) let api = PlaylistApi.getPlaylist(name: self.playlist.name)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting playlist") fatalError("error getting playlist")

View File

@ -53,7 +53,7 @@ struct SettingsList: View {
let api = UserApi.deleteUser let api = UserApi.deleteUser
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
self.liveUser.logout() self.liveUser.logout()
} }

View File

@ -76,7 +76,7 @@ struct AddTagSheet: View {
let api = TagApi.newTag(tag_id: tag_id) let api = TagApi.newTag(tag_id: tag_id)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
self.tags.append(tag) self.tags.append(tag)
self.tags = self.tags.sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) self.tags = self.tags.sorted(by: { $0.name.lowercased() < $1.name.lowercased() })

View File

@ -31,7 +31,7 @@ struct TagList: View {
let api = TagApi.deleteTag(tag_id: self.liveUser.tags[index].tag_id) let api = TagApi.deleteTag(tag_id: self.liveUser.tags[index].tag_id)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
} }
else { else {
@ -47,7 +47,7 @@ struct TagList: View {
} }
} }
.refreshable { .refreshable {
self.liveUser.refreshTags(onSuccess: { self.liveUser.refresh_tags(onSuccess: {
toastText = "Refreshed!" toastText = "Refreshed!"
toastSuccess = true toastSuccess = true

View File

@ -64,12 +64,12 @@ struct TagObjList: View {
} }
} }
.navigationBarTitle(Text(objType)) .navigationBarTitle(Text(objType))
.navigationBarItems(trailing: // .navigationBarItems(trailing:
Button( // Button(
action: { }, // action: { },
label: { Image(systemName: "plus.circle") } // label: { Image(systemName: "plus.circle") }
) // )
) // )
} }
} }

View File

@ -31,7 +31,7 @@ struct TagRow: View {
.validate() .validate()
.responseJSON{ response in .responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
} else { } else {
self.showingNetworkError = true self.showingNetworkError = true

View File

@ -120,7 +120,7 @@ struct TagView: View {
let api = TagApi.runTag(tag_id: tag.tag_id) let api = TagApi.runTag(tag_id: tag.tag_id)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
} else { } else {
@ -133,7 +133,7 @@ struct TagView: View {
let api = TagApi.updateTag(tag_id: tag.tag_id, updates: updates) let api = TagApi.updateTag(tag_id: tag.tag_id, updates: updates)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
} else { } else {
@ -146,7 +146,7 @@ struct TagView: View {
let api = TagApi.getTag(tag_id: self.tag.tag_id) let api = TagApi.getTag(tag_id: self.tag.tag_id)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.checkNetworkResponse(response: response) { if self.liveUser.check_network_response(response: response) {
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting tag") fatalError("error getting tag")