added admin page, better net calls, toasts

This commit is contained in:
andy 2022-08-11 20:15:21 +01:00
parent 7477ff0a75
commit 9b59baa8f1
17 changed files with 314 additions and 134 deletions

View File

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
A10C8D29281302050018AE12 /* ToastUI in Frameworks */ = {isa = PBXBuildFile; productRef = A10C8D28281302050018AE12 /* ToastUI */; }; A10C8D29281302050018AE12 /* ToastUI in Frameworks */ = {isa = PBXBuildFile; productRef = A10C8D28281302050018AE12 /* ToastUI */; };
A11AC70628A188AE00645043 /* AuthApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11AC70528A188AE00645043 /* AuthApi.swift */; }; A11AC70628A188AE00645043 /* AuthApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11AC70528A188AE00645043 /* AuthApi.swift */; };
A1DBCDA628A51869002CF730 /* AdminList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DBCDA528A51869002CF730 /* AdminList.swift */; };
E906F7F42414019C004E1E31 /* NetworkPersister.swift in Sources */ = {isa = PBXBuildFile; fileRef = E906F7F32414019C004E1E31 /* NetworkPersister.swift */; }; E906F7F42414019C004E1E31 /* NetworkPersister.swift in Sources */ = {isa = PBXBuildFile; fileRef = E906F7F32414019C004E1E31 /* NetworkPersister.swift */; };
E906F7F724143AA7004E1E31 /* SwiftUICharts in Frameworks */ = {isa = PBXBuildFile; productRef = E906F7F624143AA7004E1E31 /* SwiftUICharts */; }; E906F7F724143AA7004E1E31 /* SwiftUICharts in Frameworks */ = {isa = PBXBuildFile; productRef = E906F7F624143AA7004E1E31 /* SwiftUICharts */; };
E934AC99240DD0E4009869F4 /* AddTagSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E934AC98240DD0E4009869F4 /* AddTagSheet.swift */; }; E934AC99240DD0E4009869F4 /* AddTagSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E934AC98240DD0E4009869F4 /* AddTagSheet.swift */; };
@ -65,6 +66,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
A11AC70528A188AE00645043 /* AuthApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthApi.swift; sourceTree = "<group>"; }; A11AC70528A188AE00645043 /* AuthApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthApi.swift; sourceTree = "<group>"; };
A146915A28118F940052999D /* Mixonomer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mixonomer.entitlements; sourceTree = "<group>"; }; A146915A28118F940052999D /* Mixonomer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mixonomer.entitlements; sourceTree = "<group>"; };
A1DBCDA528A51869002CF730 /* AdminList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminList.swift; sourceTree = "<group>"; };
E906F7F32414019C004E1E31 /* NetworkPersister.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPersister.swift; sourceTree = "<group>"; }; E906F7F32414019C004E1E31 /* NetworkPersister.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPersister.swift; sourceTree = "<group>"; };
E934AC98240DD0E4009869F4 /* AddTagSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTagSheet.swift; sourceTree = "<group>"; }; E934AC98240DD0E4009869F4 /* AddTagSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTagSheet.swift; sourceTree = "<group>"; };
E971F8B8245462D500B543B6 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; }; E971F8B8245462D500B543B6 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
@ -133,6 +135,14 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
A1DBCDA428A5184D002CF730 /* Admin */ = {
isa = PBXGroup;
children = (
A1DBCDA528A51869002CF730 /* AdminList.swift */,
);
path = Admin;
sourceTree = "<group>";
};
E98254BE23F9BD540056D9D3 /* Model */ = { E98254BE23F9BD540056D9D3 /* Model */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -160,6 +170,7 @@
E98254C023F9FFDD0056D9D3 /* Views */ = { E98254C023F9FFDD0056D9D3 /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A1DBCDA428A5184D002CF730 /* Admin */,
E9E30C3423FF256100574EEF /* Settings */, E9E30C3423FF256100574EEF /* Settings */,
E9E30C2F23FEACF700574EEF /* Tag */, E9E30C2F23FEACF700574EEF /* Tag */,
E9E30C2E23FEACDE00574EEF /* Playlist */, E9E30C2E23FEACDE00574EEF /* Playlist */,
@ -431,6 +442,7 @@
E9CCD5BD2454C64300B5CD6C /* TagList.swift in Sources */, E9CCD5BD2454C64300B5CD6C /* TagList.swift in Sources */,
E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */, E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */,
E9CCD5BB2454C57300B5CD6C /* PlaylistList.swift in Sources */, E9CCD5BB2454C57300B5CD6C /* PlaylistList.swift in Sources */,
A1DBCDA628A51869002CF730 /* AdminList.swift in Sources */,
E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */, E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */,
A11AC70628A188AE00645043 /* AuthApi.swift in Sources */, A11AC70628A188AE00645043 /* AuthApi.swift in Sources */,
E9EA690F23F9A5430012C3E8 /* AppSkeleton.swift in Sources */, E9EA690F23F9A5430012C3E8 /* AppSkeleton.swift in Sources */,

View File

@ -15,6 +15,7 @@ class LiveUser: ObservableObject {
@Published var playlists: [Playlist] @Published var playlists: [Playlist]
@Published var tags: [Tag] @Published var tags: [Tag]
@Published var username: String @Published var username: String
@Published var user: User?
@Published var loggedIn: Bool { @Published var loggedIn: Bool {
didSet { didSet {
@ -22,6 +23,7 @@ class LiveUser: ObservableObject {
} }
} }
@Published var isRefreshingUser = false
@Published var isRefreshingPlaylists = false @Published var isRefreshingPlaylists = false
@Published var isRefreshingTags = false @Published var isRefreshingTags = false
@ -39,17 +41,48 @@ class LiveUser: ObservableObject {
self.playlists[index] = playlistIn self.playlists[index] = playlistIn
} }
func refreshPlaylists() { func refreshUser(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) {
self.isRefreshingUser = true
let api = UserApi.getUser
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.checkNetworkResponse(response: response) {
guard let data = response.data else {
fatalError("error getting user")
}
guard let json = try? JSON(data: data) else {
fatalError("error parsing user")
}
// update state
self.user = UserApi.fromJSON(user: json)
self.isRefreshingUser = false
if let success = onSuccess {
success()
}
} else {
if let failure = onFailure {
failure()
}
}
}
}
func refreshPlaylists(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
self.checkNetworkResponse(response: response) if self.checkNetworkResponse(response: response) {
switch response.response?.statusCode {
case 200, 201:
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting playlists") fatalError("error getting playlists")
} }
@ -65,6 +98,10 @@ class LiveUser: ObservableObject {
self.isRefreshingPlaylists = false self.isRefreshingPlaylists = false
if let success = onSuccess {
success()
}
let encoder = JSONEncoder() let encoder = JSONEncoder()
do { do {
UserDefaults.standard.set(String(data: try encoder.encode(playlists), encoding: .utf8), forKey: "playlists") UserDefaults.standard.set(String(data: try encoder.encode(playlists), encoding: .utf8), forKey: "playlists")
@ -72,23 +109,23 @@ class LiveUser: ObservableObject {
print("error encoding playlists: \(error)") print("error encoding playlists: \(error)")
} }
case _: } else {
break
if let failure = onFailure {
failure()
}
} }
} }
} }
func refreshTags() { func refreshTags(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
self.checkNetworkResponse(response: response) if self.checkNetworkResponse(response: response) {
switch response.response?.statusCode {
case 200, 201:
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting tags") fatalError("error getting tags")
} }
@ -104,6 +141,10 @@ class LiveUser: ObservableObject {
self.isRefreshingTags = false self.isRefreshingTags = false
if let success = onSuccess {
success()
}
let encoder = JSONEncoder() let encoder = JSONEncoder()
do { do {
UserDefaults.standard.set(String(data: try encoder.encode(tags), encoding: .utf8), forKey: "tags") UserDefaults.standard.set(String(data: try encoder.encode(tags), encoding: .utf8), forKey: "tags")
@ -111,20 +152,32 @@ class LiveUser: ObservableObject {
print("error encoding tags: \(error)") print("error encoding tags: \(error)")
} }
case _: } else {
break
if let failure = onFailure {
failure()
}
} }
} }
} }
func checkNetworkResponse(response: AFDataResponse<Any>) { func checkNetworkResponse(response: AFDataResponse<Any>) -> Bool {
switch response.response?.statusCode { if let statusCode = response.response?.statusCode {
case 401: switch statusCode {
self.loggedIn = false case 401: // token has expired
case _: self.loggedIn = false
print("error making request") return false
case 400..<500:
return false
case 500..<600:
return false
case _: // 200 -> Success
return true
}
} }
return false
} }
func loadUserDefaults() -> LiveUser { func loadUserDefaults() -> LiveUser {

View File

@ -116,7 +116,7 @@ class Playlist: Identifiable, Equatable, Codable, ObservableObject {
func updatePlaylist(updates: JSON) { func updatePlaylist(updates: JSON) {
let api = PlaylistApi.updatePlaylist(name: self.name, updates: updates) let api = PlaylistApi.updatePlaylist(name: self.name, updates: updates)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
switch response.response?.statusCode { switch response.response?.statusCode {
case 200, 201: case 200, 201:
break break
@ -269,13 +269,13 @@ class Playlist: Identifiable, Equatable, Codable, ObservableObject {
do{ do{
description_overwrite = try container.decode(String.self, forKey: .description_overwrite) description_overwrite = try container.decode(String.self, forKey: .description_overwrite)
}catch { }catch {
debugPrint("no description overwrite") // debugPrint("no description overwrite")
} }
do{ do{
description_suffix = try container.decode(String.self, forKey: .description_suffix) description_suffix = try container.decode(String.self, forKey: .description_suffix)
}catch { }catch {
debugPrint("no description suffix") // debugPrint("no description suffix")
} }
last_updated = try container.decode(String.self, forKey: .last_updated) last_updated = try container.decode(String.self, forKey: .last_updated)

View File

@ -23,6 +23,8 @@ class User: Identifiable, Decodable {
var type: UserType var type: UserType
var last_login: String var last_login: String
var last_keygen: String
var spotify_linked: Bool var spotify_linked: Bool
var lastfm_username: String? var lastfm_username: String?
@ -33,6 +35,7 @@ class User: Identifiable, Decodable {
type: UserType = .user, type: UserType = .user,
last_login: String, last_login: String,
last_keygen: String,
spotify_linked: Bool, spotify_linked: Bool,
lastfm_username: String?){ lastfm_username: String?){
@ -41,6 +44,7 @@ class User: Identifiable, Decodable {
self.type = type self.type = type
self.last_login = last_login self.last_login = last_login
self.last_keygen = last_keygen
self.spotify_linked = spotify_linked self.spotify_linked = spotify_linked
self.lastfm_username = lastfm_username self.lastfm_username = lastfm_username
} }

View File

@ -144,7 +144,6 @@ extension PlaylistApi: ApiRequest {
print(error) print(error)
} }
} }
print(playlist)
return nil return nil
} }

View File

@ -30,7 +30,7 @@ extension TagApi: ApiRequest {
return "api/tag" return "api/tag"
case .runTag(let tag_id): case .runTag(let tag_id):
return "api/tag/\(tag_id)/update" return "api/tag/\(tag_id)/update"
case .updateTag(let tag_id): case .updateTag(let tag_id, _):
return "api/tag/\(tag_id)" return "api/tag/\(tag_id)"
case .deleteTag(let tag_id): case .deleteTag(let tag_id):
return "api/tag/\(tag_id)" return "api/tag/\(tag_id)"
@ -64,7 +64,7 @@ extension TagApi: ApiRequest {
return nil return nil
case .runTag: case .runTag:
return nil return nil
case .updateTag(let _, let updates): case .updateTag(_, let updates):
return updates return updates
case .deleteTag: case .deleteTag:
return nil return nil

View File

@ -0,0 +1,29 @@
//
// AdminList.swift
// Mixonomer
//
// Created by Andy Pack on 11/08/2022.
// Copyright © 2022 Sarsoo. All rights reserved.
//
import SwiftUI
struct AdminList: View {
var body: some View {
NavigationView {
List{
Section {
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle(Text("Admin 🚨"))
}
}
}
struct AdminList_Previews: PreviewProvider {
static var previews: some View {
AdminList()
}
}

View File

@ -28,14 +28,31 @@ struct AppSkeleton: View {
} }
.tag(0) .tag(0)
TagList() if let user = liveUser.user {
.tabItem { if let _ = user.lastfm_username {
VStack { TagList()
Image(systemName: "tag") .tabItem {
Text("Tags") VStack {
} Image(systemName: "tag")
Text("Tags")
}
}
.tag(1)
} }
.tag(1) }
if let user = liveUser.user {
if user.type == .admin {
AdminList()
.tabItem( {
VStack {
Image(systemName: "person.badge.key.fill")
Text("Admin")
}
})
.tag(2)
}
}
SettingsList() SettingsList()
.tabItem { .tabItem {
@ -44,7 +61,7 @@ struct AppSkeleton: View {
Text("Settings") Text("Settings")
} }
} }
.tag(2) .tag(3)
}.onAppear { }.onAppear {
self.fetchAll() self.fetchAll()
@ -52,6 +69,7 @@ struct AppSkeleton: View {
} }
private func fetchAll() { private func fetchAll() {
self.liveUser.refreshUser()
self.liveUser.refreshPlaylists() self.liveUser.refreshPlaylists()
self.liveUser.refreshTags() self.liveUser.refreshTags()
} }
@ -60,5 +78,6 @@ struct AppSkeleton: View {
struct RootView_Previews: PreviewProvider { struct RootView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AppSkeleton() AppSkeleton()
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -184,6 +184,11 @@ struct LoginScreen: View {
.buttonStyle(.bordered) .buttonStyle(.bordered)
} }
} }
.toast(isPresented: $showingToast, dismissAfter: 3.0){
ToastView(toastText)
.toastViewStyle(.failure)
}
.toastDimmedBackground(false)
.padding() .padding()
} }
} }

View File

@ -94,19 +94,16 @@ 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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode {
case 200, 201:
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() })
self.isLoading = false self.isLoading = false
self.presentationMode.wrappedValue.dismiss() self.presentationMode.wrappedValue.dismiss()
case _: } else {
break
} }
} }
} }
@ -115,5 +112,6 @@ struct AddPlaylistSheet: View {
struct AddPlaylistSheet_Previews: PreviewProvider { struct AddPlaylistSheet_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AddPlaylistSheet(playlists: .constant([]), username: .constant("username")) AddPlaylistSheet(playlists: .constant([]), username: .constant("username"))
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -7,12 +7,17 @@
// //
import SwiftUI import SwiftUI
import ToastUI
struct PlaylistList: View { struct PlaylistList: View {
@EnvironmentObject var liveUser: LiveUser @EnvironmentObject var liveUser: LiveUser
@State private var showAdd = false // State for showing add modal view @State private var showAdd = false // State for showing add modal view
@State private var showingToast = false
@State private var toastText = ""
@State private var toastSuccess = true
var body: some View { var body: some View {
NavigationView { NavigationView {
List{ List{
@ -25,13 +30,11 @@ struct PlaylistList: View {
indexSet.forEach { index in indexSet.forEach { index in
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
self.liveUser.checkNetworkResponse(response: response)
switch response.response?.statusCode { if self.liveUser.checkNetworkResponse(response: response) {
case 200, 201:
break } else {
case _:
break
} }
} }
} }
@ -44,7 +47,19 @@ struct PlaylistList: View {
} }
.refreshable .refreshable
{ {
self.liveUser.refreshPlaylists() self.liveUser.refreshPlaylists(onSuccess: {
toastText = "Refreshed!"
toastSuccess = true
showingToast = true
}, onFailure: {
toastText = "Refresh Failed"
toastSuccess = false
showingToast = true
})
} }
.navigationBarTitle(Text("Playlists 📻")) .navigationBarTitle(Text("Playlists 📻"))
.navigationBarItems(trailing: .navigationBarItems(trailing:
@ -55,6 +70,18 @@ struct PlaylistList: View {
AddPlaylistSheet(playlists: self.$liveUser.playlists, username: self.$liveUser.username) AddPlaylistSheet(playlists: self.$liveUser.playlists, username: self.$liveUser.username)
} }
) )
.toast(isPresented: $showingToast, dismissAfter: 3.0){
if toastSuccess {
ToastView(toastText)
.toastViewStyle(.success)
}
else {
ToastView(toastText)
.toastViewStyle(.failure)
}
}
.toastDimmedBackground(false)
} }
} }
} }
@ -62,5 +89,6 @@ struct PlaylistList: View {
struct PlaylistList_Previews: PreviewProvider { struct PlaylistList_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
PlaylistList() PlaylistList()
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -31,14 +31,12 @@ struct PlaylistRow: View {
.validate() .validate()
.responseJSON{ response in .responseJSON{ response in
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode { }
case 200, 201: else {
break self.showingNetworkError = true
case _: }
self.showingNetworkError = true
}
} }
}) { }) {
Text("Refresh") Text("Refresh")
@ -65,5 +63,6 @@ struct PlaylistRow_Previews: PreviewProvider {
PlaylistView(playlist: .constant( PlaylistView(playlist: .constant(
Playlist(name: "playlist name", username: "username") Playlist(name: "playlist name", username: "username")
)) ))
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -7,6 +7,7 @@
// //
import SwiftUI import SwiftUI
import ToastUI
import SwiftyJSON import SwiftyJSON
import SwiftUICharts import SwiftUICharts
@ -17,7 +18,11 @@ struct PlaylistView: View {
@State private var showingSheet = false @State private var showingSheet = false
@State private var isRefreshing = false @State private var isRefreshing = false
@State private var showingNetworkError = false
// TOAST
@State private var showingToast = false
@State private var toastText = ""
@State private var toastSuccess = true
var chartStyle: ChartStyle { var chartStyle: ChartStyle {
get { get {
@ -194,14 +199,18 @@ struct PlaylistView: View {
Text("Open") Text("Open")
} }
} }
.toast(isPresented: $showingToast, dismissAfter: 3.0){
// alert seems to need to be within list root element if toastSuccess {
// else weird crash on half drag back ToastView(toastText)
.alert(isPresented: $showingNetworkError) { .toastViewStyle(.success)
Alert(title: Text("Network Error"), }
message: Text("Could not refresh playlist")) else {
ToastView(toastText)
.toastViewStyle(.failure)
}
} }
.toastDimmedBackground(false)
} }
.navigationBarTitle(Text(playlist.name)) .navigationBarTitle(Text(playlist.name))
@ -216,32 +225,39 @@ struct PlaylistView: View {
.validate() .validate()
.responseJSON{ response in .responseJSON{ response in
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode { toastText = "Running!"
case 200, 201: toastSuccess = true
break showingToast = true
case _:
self.showingNetworkError = true } else {
}
toastText = "Run Request Failed"
toastSuccess = false
showingToast = true
}
} }
//TODO: do better error checking
} }
func refreshStats() { func refreshStats() {
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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode { toastText = "Refreshing Stats!"
case 200, 201: toastSuccess = true
break showingToast = true
case _:
break } else {
toastText = "Stat Refresh Failed"
toastSuccess = false
showingToast = true
} }
} }
//TODO: do better error checking
} }
func openPlaylist() { func openPlaylist() {
@ -254,12 +270,9 @@ 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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode {
case 200, 201:
debugPrint("success") debugPrint("success")
case _: } else {
debugPrint("error") debugPrint("error")
} }
} }
@ -270,10 +283,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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode {
case 200, 201:
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting playlist") fatalError("error getting playlist")
@ -281,13 +291,20 @@ struct PlaylistView: View {
self.playlist = PlaylistApi.fromJSON(playlist: data)! self.playlist = PlaylistApi.fromJSON(playlist: data)!
case _: toastText = "Refreshed!"
break toastSuccess = true
showingToast = true
} else {
toastText = "Refresh Failed"
toastSuccess = false
showingToast = true
} }
self.isRefreshing = false self.isRefreshing = false
} }
//TODO: do better error checking
} }
} }
@ -301,6 +318,7 @@ struct PlaylistView_Previews: PreviewProvider {
lastfm_stat_artist_percent: 80 lastfm_stat_artist_percent: 80
) )
)) ))
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -76,19 +76,16 @@ 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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode {
case 200, 201:
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() })
self.isLoading = false self.isLoading = false
self.presentationMode.wrappedValue.dismiss() self.presentationMode.wrappedValue.dismiss()
case _: } else {
break
} }
} }
} }
@ -97,5 +94,6 @@ struct AddTagSheet: View {
struct AddTagSheet_Previews: PreviewProvider { struct AddTagSheet_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AddTagSheet(tags: .constant([]), username: .constant("username")) AddTagSheet(tags: .constant([]), username: .constant("username"))
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -7,12 +7,17 @@
// //
import SwiftUI import SwiftUI
import ToastUI
struct TagList: View { struct TagList: View {
@EnvironmentObject var liveUser: LiveUser @EnvironmentObject var liveUser: LiveUser
@State private var showAdd = false // State for showing add modal view @State private var showAdd = false // State for showing add modal view
@State private var showingToast = false
@State private var toastText = ""
@State private var toastSuccess = true
var body: some View { var body: some View {
NavigationView { NavigationView {
List{ List{
@ -26,13 +31,11 @@ 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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode { }
case 200, 201: else {
break
case _:
break
} }
} }
} }
@ -44,7 +47,19 @@ struct TagList: View {
} }
} }
.refreshable { .refreshable {
self.liveUser.refreshTags() self.liveUser.refreshTags(onSuccess: {
toastText = "Refreshed!"
toastSuccess = true
showingToast = true
}, onFailure: {
toastText = "Refresh Failed"
toastSuccess = false
showingToast = true
})
} }
.navigationBarTitle(Text("Tags 🎷")) .navigationBarTitle(Text("Tags 🎷"))
.navigationBarItems( .navigationBarItems(
@ -59,6 +74,18 @@ struct TagList: View {
AddTagSheet(tags: self.$liveUser.tags, username: self.$liveUser.username) AddTagSheet(tags: self.$liveUser.tags, username: self.$liveUser.username)
} }
) )
.toast(isPresented: $showingToast, dismissAfter: 3.0){
if toastSuccess {
ToastView(toastText)
.toastViewStyle(.success)
}
else {
ToastView(toastText)
.toastViewStyle(.failure)
}
}
.toastDimmedBackground(false)
} }
} }
} }
@ -66,5 +93,6 @@ struct TagList: View {
struct TagList_Previews: PreviewProvider { struct TagList_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
TagList() TagList()
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -31,12 +31,9 @@ struct TagRow: View {
.validate() .validate()
.responseJSON{ response in .responseJSON{ response in
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode { } else {
case 200, 201:
break
case _:
self.showingNetworkError = true self.showingNetworkError = true
} }
} }
@ -69,5 +66,6 @@ struct TagRow_Previews: PreviewProvider {
last_updated: "10th Feb") last_updated: "10th Feb")
)) ))
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }

View File

@ -120,13 +120,10 @@ 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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode { } else {
case 200, 201:
break
case _:
break
} }
} }
//TODO: do better error checking //TODO: do better error checking
@ -136,13 +133,10 @@ 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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode { } else {
case 200, 201:
break
case _:
break
} }
} }
//TODO: do better error checking //TODO: do better error checking
@ -152,10 +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
self.liveUser.checkNetworkResponse(response: response) if self.liveUser.checkNetworkResponse(response: response) {
switch response.response?.statusCode {
case 200, 201:
guard let data = response.data else { guard let data = response.data else {
fatalError("error getting tag") fatalError("error getting tag")
@ -169,8 +160,8 @@ struct TagView: View {
self.tag = tag self.tag = tag
} }
case _: } else {
break
} }
self.isRefreshing = false self.isRefreshing = false
@ -196,5 +187,6 @@ struct TagView_Previews: PreviewProvider {
last_updated: "10th Feb") last_updated: "10th Feb")
)) ))
.environmentObject(LiveUser(playlists: [], tags: [], username: "user", loggedIn: false))
} }
} }