diff --git a/Music Tools.xcodeproj/project.pbxproj b/Music Tools.xcodeproj/project.pbxproj index 5c4ece3..c6e31bb 100644 --- a/Music Tools.xcodeproj/project.pbxproj +++ b/Music Tools.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ E92F94822401412100B6B721 /* SwiftUIRefresh in Frameworks */ = {isa = PBXBuildFile; productRef = E92F94812401412100B6B721 /* SwiftUIRefresh */; }; + E934AC99240DD0E4009869F4 /* AddTagSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E934AC98240DD0E4009869F4 /* AddTagSheet.swift */; }; E97AF45623FC4E7800635494 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF45523FC4E7800635494 /* User.swift */; }; E97AF45923FC50EC00635494 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = E97AF45823FC50EC00635494 /* SwiftyJSON */; }; E97AF45B23FC748D00635494 /* UserApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF45A23FC748D00635494 /* UserApi.swift */; }; @@ -58,6 +59,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + E934AC98240DD0E4009869F4 /* AddTagSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTagSheet.swift; sourceTree = ""; }; E97AF45523FC4E7800635494 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; E97AF45A23FC748D00635494 /* UserApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserApi.swift; sourceTree = ""; }; E97AF45F23FC85D600635494 /* PlaylistApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistApi.swift; sourceTree = ""; }; @@ -192,6 +194,7 @@ E9E30C2923FEAA3A00574EEF /* TagRow.swift */, E9E30C2C23FEAB0200574EEF /* TagView.swift */, E9E30C3023FEAF2B00574EEF /* TagObjList.swift */, + E934AC98240DD0E4009869F4 /* AddTagSheet.swift */, ); path = Tag; sourceTree = ""; @@ -409,6 +412,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E934AC99240DD0E4009869F4 /* AddTagSheet.swift in Sources */, E9E30C2D23FEAB0200574EEF /* TagView.swift in Sources */, E9E30C2823FEA6BD00574EEF /* Tag.swift in Sources */, E9E30C3123FEAF2B00574EEF /* TagObjList.swift in Sources */, diff --git a/Music Tools/Application/SceneDelegate.swift b/Music Tools/Application/SceneDelegate.swift index 09adcf0..8f3a79f 100644 --- a/Music Tools/Application/SceneDelegate.swift +++ b/Music Tools/Application/SceneDelegate.swift @@ -22,12 +22,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Create the SwiftUI view that provides the window contents. let contentView = RootView() - let liveUser = LiveUser(playlists: [], tags: []) - let keychain = Keychain(service: "xyz.sarsoo.music.login") - -// debugPrint(keychain["username"] ?? "no username") -// debugPrint(keychain["password"] ?? "no password") // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { @@ -35,6 +30,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var controller: UIViewController if keychain["username"] != nil && keychain["password"] != nil { + let liveUser = LiveUser(playlists: [], tags: [], username: keychain["username"]!) controller = UIHostingController(rootView: contentView.environmentObject(liveUser)) } else { let storyboard = UIStoryboard(name: "Main", bundle: nil) diff --git a/Music Tools/Assets.xcassets/APFooter.imageset/Contents.json b/Music Tools/Assets.xcassets/APFooter.imageset/Contents.json new file mode 100644 index 0000000..743373b --- /dev/null +++ b/Music Tools/Assets.xcassets/APFooter.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ap.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Music Tools/Assets.xcassets/APFooter.imageset/ap.png b/Music Tools/Assets.xcassets/APFooter.imageset/ap.png new file mode 100644 index 0000000..4545ba1 Binary files /dev/null and b/Music Tools/Assets.xcassets/APFooter.imageset/ap.png differ diff --git a/Music Tools/Controller/LoginController.swift b/Music Tools/Controller/LoginController.swift index 5ffb433..38a0e3c 100644 --- a/Music Tools/Controller/LoginController.swift +++ b/Music Tools/Controller/LoginController.swift @@ -27,7 +27,8 @@ class LoginController: UIViewController, UITextFieldDelegate { // MARK: Actions @IBSegueAction func returnUIView(_ coder: NSCoder) -> UIViewController? { - let liveUser = LiveUser(playlists: [], tags: []) + // TODO add right username + let liveUser = LiveUser(playlists: [], tags: [], username: "") return UIHostingController(coder: coder, rootView: RootView().environmentObject(liveUser)) } diff --git a/Music Tools/Model/LiveUser.swift b/Music Tools/Model/LiveUser.swift index fe2dfb9..e212c75 100644 --- a/Music Tools/Model/LiveUser.swift +++ b/Music Tools/Model/LiveUser.swift @@ -12,10 +12,12 @@ class LiveUser: ObservableObject { @Published var playlists: [Playlist] @Published var tags: [Tag] + @Published var username: String - init(playlists: [Playlist], tags: [Tag]) { + init(playlists: [Playlist], tags: [Tag], username: String) { self.playlists = playlists self.tags = tags + self.username = username } func updatePlaylist(playlistIn: Playlist) { diff --git a/Music Tools/Network/TagApi.swift b/Music Tools/Network/TagApi.swift index 700a0d6..b848b64 100644 --- a/Music Tools/Network/TagApi.swift +++ b/Music Tools/Network/TagApi.swift @@ -16,6 +16,7 @@ public enum TagApi { case updateTag(tag_id: String, updates: JSON) case deleteTag(tag_id: String) case newTag(tag_id: String) + case getTag(tag_id: String) } extension TagApi: ApiRequest { @@ -35,6 +36,8 @@ extension TagApi: ApiRequest { return "api/tag/\(tag_id)" case .newTag(let tag_id): return "api/tag/\(tag_id)" + case .getTag(let tag_id): + return "api/tag/\(tag_id)" } } @@ -50,6 +53,8 @@ extension TagApi: ApiRequest { return .delete case .newTag: return .post + case .getTag: + return .get } } @@ -59,12 +64,14 @@ extension TagApi: ApiRequest { return nil case .runTag: return nil - case .updateTag(let tag_id, let updates): + case .updateTag(let _, let updates): return updates case .deleteTag: return nil case .newTag: return nil + case .getTag: + return nil } } @@ -80,6 +87,8 @@ extension TagApi: ApiRequest { return nil case .newTag: return nil + case .getTag: + return nil } } diff --git a/Music Tools/Views/Playlist/AddPlaylistSheet.swift b/Music Tools/Views/Playlist/AddPlaylistSheet.swift index ad55aa9..83fec22 100644 --- a/Music Tools/Views/Playlist/AddPlaylistSheet.swift +++ b/Music Tools/Views/Playlist/AddPlaylistSheet.swift @@ -18,6 +18,7 @@ struct AddPlaylistSheet: View { @Binding var state: Bool @Binding var playlists: Array + @Binding var username: String var body: some View { VStack { @@ -73,10 +74,26 @@ struct AddPlaylistSheet: View { return } + var playlist: Playlist? = nil + switch PlaylistType(rawValue: selectedType) ?? .defaultPlaylist { + case .defaultPlaylist: + playlist = Playlist(name: name, uri: "", username: username, include_recommendations: false, recommendation_sample: 10, include_library_tracks: false, parts: [], playlist_references: [], shuffle: false) + break + case .recents: + playlist = RecentsPlaylist(name: name, uri: "", username: username, include_recommendations: false, recommendation_sample: 10, include_library_tracks: false, parts: [], playlist_references: [], shuffle: false, add_last_month: false, add_this_month: false, day_boundary: 14) + break + case .fmchart: + playlist = LastFMChartPlaylist(name: name, uri: "", username: username, include_recommendations: false, recommendation_sample: 10, include_library_tracks: false, parts: [], playlist_references: [], shuffle: false, chart_range: .month, chart_limit: 10) + break + } + isLoading = true let api = PlaylistApi.newPlaylist(name: self.name, type: PlaylistType(rawValue: selectedType) ?? .defaultPlaylist) RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in + self.playlists.append(playlist!) + self.playlists = self.playlists.sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) + self.isLoading = false self.state = false } @@ -85,6 +102,6 @@ struct AddPlaylistSheet: View { struct AddPlaylistSheet_Previews: PreviewProvider { static var previews: some View { - AddPlaylistSheet(state: .constant(true), playlists: .constant([])) + AddPlaylistSheet(state: .constant(true), playlists: .constant([]), username: .constant("username")) } } diff --git a/Music Tools/Views/Playlist/PlaylistRow.swift b/Music Tools/Views/Playlist/PlaylistRow.swift index abe2b0e..0a7e068 100644 --- a/Music Tools/Views/Playlist/PlaylistRow.swift +++ b/Music Tools/Views/Playlist/PlaylistRow.swift @@ -10,13 +10,10 @@ import SwiftUI import SwiftyJSON struct PlaylistRow: View { - - @EnvironmentObject var liveUser: LiveUser - - var playlist: Playlist + @Binding var playlist: Playlist var body: some View { - NavigationLink(destination: PlaylistView(playlist: playlist)){ + NavigationLink(destination: PlaylistView(playlist: $playlist)){ HStack { Text(playlist.name) .contextMenu { @@ -50,18 +47,7 @@ struct PlaylistRow: View { struct PlaylistRow_Previews: PreviewProvider { static var previews: some View { PlaylistRow(playlist: - Playlist(name: "playlist name", - uri: "uri", - username: "username", - - include_recommendations: true, - recommendation_sample: 5, - include_library_tracks: true, - - parts: ["name"], - playlist_references: ["ref name"], - - shuffle: true) + .constant(Playlist(name: "", uri: "", username: "", include_recommendations: true, recommendation_sample: 1, include_library_tracks: true, parts: [], playlist_references: [], shuffle: true)) ) } } diff --git a/Music Tools/Views/Playlist/PlaylistView.swift b/Music Tools/Views/Playlist/PlaylistView.swift index a405f95..196a24e 100644 --- a/Music Tools/Views/Playlist/PlaylistView.swift +++ b/Music Tools/Views/Playlist/PlaylistView.swift @@ -7,41 +7,14 @@ // import SwiftUI -//import SwiftUIRefresh +import SwiftUIRefresh import SwiftyJSON -final class ChangeableBool: ObservableObject { - - var onClick: () -> () - - init(onClick: @escaping () -> ()) { - self.onClick = onClick - } - - @Published var state: Bool = false { - didSet { - self.onClick() - } - } -} - struct PlaylistView: View { @EnvironmentObject var liveUser: LiveUser - init(playlist: Playlist) { - self.playlist = playlist - - // hide empty items below list - UITableView.appearance().tableFooterView = UIView() - } - - var playlist: Playlist - @State private var recommendations: Bool = true - @State private var library_Tracks: Bool = false - @State private var shuffle: Bool = false - - @State private var rec_num: Int = 0 + @Binding var playlist: Playlist @State private var this_month: Bool = false @State private var last_month: Bool = false @@ -55,33 +28,33 @@ struct PlaylistView: View { var body: some View { List { Section(header: Text("Options")){ - Toggle(isOn: $recommendations) { + Toggle(isOn: self.$playlist.include_recommendations) { Text("Spotify Recommendations") } -// if recommendations { + if self.playlist.include_recommendations { Stepper(onIncrement: { - self.$rec_num.wrappedValue += 1 - self.updatePlaylist(updates: JSON(["recommendation_sample": self.$rec_num.wrappedValue])) + self.$playlist.recommendation_sample.wrappedValue += 1 + self.updatePlaylist(updates: JSON(["recommendation_sample": self.playlist.recommendation_sample])) }, onDecrement: { - self.$rec_num.wrappedValue -= 1 - self.updatePlaylist(updates: JSON(["recommendation_sample": self.$rec_num.wrappedValue])) + self.$playlist.recommendation_sample.wrappedValue -= 1 + self.updatePlaylist(updates: JSON(["recommendation_sample": self.playlist.recommendation_sample])) }){ Text("#:") .foregroundColor(Color.gray) .multilineTextAlignment(.trailing) - Text("\(rec_num)") + Text("\(self.playlist.recommendation_sample)") .multilineTextAlignment(.trailing) } -// } + } - Toggle(isOn: $library_Tracks) { + Toggle(isOn: self.$playlist.include_library_tracks) { Text("Library Tracks") } - Toggle(isOn: $shuffle) { + Toggle(isOn: self.$playlist.shuffle) { Text("Shuffle") } @@ -147,17 +120,13 @@ struct PlaylistView: View { } } } -// .pullToRefresh(isShowing: $isRefreshing) { -// self.refreshPlaylist() -// } + .pullToRefresh(isShowing: $isRefreshing) { + self.refreshPlaylist() + } .navigationBarTitle(Text(playlist.name)) .onAppear { - self.$recommendations.wrappedValue = self.playlist.include_recommendations - self.$library_Tracks.wrappedValue = self.playlist.include_library_tracks - self.$shuffle.wrappedValue = self.playlist.shuffle - - self.$rec_num.wrappedValue = self.playlist.recommendation_sample + // TODO are these binding properly? if let playlist = self.playlist as? RecentsPlaylist { self.$this_month.wrappedValue = playlist.add_this_month self.$last_month.wrappedValue = playlist.add_last_month @@ -204,7 +173,7 @@ struct PlaylistView: View { //TODO: do better error checking } - func refreshPlaylist(updates: JSON) { + func refreshPlaylist() { let api = PlaylistApi.getPlaylist(name: self.playlist.name) RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in guard let data = response.data else { @@ -214,11 +183,8 @@ struct PlaylistView: View { guard let json = try? JSON(data: data) else { fatalError("error parsing reponse") } - -// let playlist = Playlist.fromDict(json["playlist"]) -// -// self.playlist = playlist -// self.isRefreshing = false + self.playlist = Playlist.fromDict(dictionary: json)! + self.isRefreshing = false } //TODO: do better error checking } @@ -226,7 +192,7 @@ struct PlaylistView: View { struct PlaylistView_Previews: PreviewProvider { static var previews: some View { - PlaylistView(playlist: + PlaylistView(playlist: .constant( Playlist(name: "playlist name", uri: "uri", username: "username", @@ -239,6 +205,6 @@ struct PlaylistView_Previews: PreviewProvider { playlist_references: ["ref name"], shuffle: true) - ) + )) } } diff --git a/Music Tools/Views/RootView.swift b/Music Tools/Views/RootView.swift index 7e07a1b..adaad99 100644 --- a/Music Tools/Views/RootView.swift +++ b/Music Tools/Views/RootView.swift @@ -19,14 +19,8 @@ struct RootView: View { @State private var showAdd = false // State for showing add modal view - @State private var justDeletedPlaylists: Array = [] // Cache of recently deleted playlists for removing from next net request - @State private var justDeletedTags: Array = [] - @State private var isRefreshingPlaylists = false @State private var isRefreshingTags = false - - // refresh playlist list on interval -// let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() var body: some View { TabView { @@ -34,15 +28,12 @@ struct RootView: View { // PLAYLISTS NavigationView { List{ - ForEach(liveUser.playlists) { playlist in - PlaylistRow(playlist: playlist) + ForEach(liveUser.playlists.indices, id:\.self) { idx in + PlaylistRow(playlist: self.$liveUser.playlists[idx]) } .onDelete { indexSet in indexSet.forEach { index in - // add to recently deleted playlist cache - self.justDeletedPlaylists.append(self.liveUser.playlists[index]) - let api = PlaylistApi.deletePlaylist(name: self.liveUser.playlists[index].name) RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in @@ -63,7 +54,7 @@ struct RootView: View { action: { self.showAdd = true }, label: { Text("Add") } ).sheet(isPresented: $showAdd) { - AddPlaylistSheet(state: self.$showAdd, playlists: self.$liveUser.playlists) + AddPlaylistSheet(state: self.$showAdd, playlists: self.$liveUser.playlists, username: self.$liveUser.username) } ) } @@ -78,15 +69,12 @@ struct RootView: View { // TAGS NavigationView { List{ - ForEach(liveUser.tags) { tag in - TagRow(tag: tag) + ForEach(liveUser.tags.indices, id:\.self) { idx in + TagRow(tag: self.$liveUser.tags[idx]) } .onDelete { indexSet in indexSet.forEach { index in - // add to recently deleted playlist cache - self.justDeletedTags.append(self.liveUser.tags[index]) - let api = TagApi.deleteTag(tag_id: self.liveUser.tags[index].tag_id) RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in @@ -107,7 +95,7 @@ struct RootView: View { action: { self.showAdd = true }, label: { Text("Add") } ).sheet(isPresented: $showAdd) { - AddPlaylistSheet(state: self.$showAdd, playlists: self.$liveUser.playlists) + AddTagSheet(state: self.$showAdd, tags: self.$liveUser.tags, username: self.$liveUser.username) } ) } @@ -130,9 +118,6 @@ struct RootView: View { } } .tag(2) -// .onReceive(timer) { _ in -// self.fetch() -// } }.onAppear { self.fetchAll() } @@ -143,7 +128,7 @@ struct RootView: View { refreshTags() } - func refreshPlaylists() { + public func refreshPlaylists() { let api = PlaylistApi.getPlaylists RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in @@ -162,19 +147,6 @@ struct RootView: View { }) // sort .sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) - // filter playlists for those recently deleted - .filter { (rxPlaylist) -> Bool in - - var deleted = false - for playlist in self.justDeletedPlaylists { - if playlist == rxPlaylist { - deleted = true - } - } - return !deleted - } - // clear cache of recently deleted playlists - self.justDeletedPlaylists = [] // update state self.liveUser.playlists = playlists @@ -183,7 +155,7 @@ struct RootView: View { //TODO: do better error checking } - func refreshTags() { + public func refreshTags() { let tagApi = TagApi.getTags RequestBuilder.buildRequest(apiRequest: tagApi).responseJSON{ response in @@ -202,19 +174,6 @@ struct RootView: View { }) // sort .sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) - // filter playlists for those recently deleted - .filter { (rxTag) -> Bool in - - var deleted = false - for tag in self.justDeletedTags { - if tag == rxTag { - deleted = true - } - } - return !deleted - } - // clear cache of recently deleted playlists - self.justDeletedTags = [] // update state self.liveUser.tags = tags diff --git a/Music Tools/Views/Settings/SettingsList.swift b/Music Tools/Views/Settings/SettingsList.swift index 5f2406d..77d2c24 100644 --- a/Music Tools/Views/Settings/SettingsList.swift +++ b/Music Tools/Views/Settings/SettingsList.swift @@ -10,8 +10,21 @@ import SwiftUI import KeychainAccess struct SettingsList: View { + + init(){ + UITableView.appearance().tableFooterView = UIView() + } + var body: some View { + VStack{ List{ + Button(action: { + if let url = URL(string: "https://music.sarsoo.xyz") { + UIApplication.shared.open(url) + } + }) { + Text("Open Web") + } Button(action: { let keychain = Keychain(service: "xyz.sarsoo.music.login") do { @@ -24,6 +37,11 @@ struct SettingsList: View { Text("Log out") } } + Image("APFooter") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 100.0) + } } } diff --git a/Music Tools/Views/Tag/AddTagSheet.swift b/Music Tools/Views/Tag/AddTagSheet.swift new file mode 100644 index 0000000..a0f40f2 --- /dev/null +++ b/Music Tools/Views/Tag/AddTagSheet.swift @@ -0,0 +1,90 @@ +// +// AddTagSheet.swift +// Music Tools +// +// Created by Andy Pack on 02/03/2020. +// Copyright © 2020 Sarsoo. All rights reserved. +// + +import SwiftUI +import SwiftyJSON + +struct AddTagSheet: View { + + @State private var name = "" + @State private var errorMessage = "" + @State private var isLoading = false + + @Binding var state: Bool + @Binding var tags: Array + @Binding var username: String + + var body: some View { + VStack { + HStack(alignment: .center) { + Text("New Tag") + .font(.largeTitle) + .multilineTextAlignment(.center) + .padding([.top, .leading, .trailing], 20.0) + + } + TextField("Name", text: $name) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding([.bottom, .leading, .trailing], 20.0) + + + + Button(action: create){ + Text("Add") + .font(.title) + } + .disabled(isLoading) + .padding() + + Text(errorMessage) + .foregroundColor(Color.red) + .padding() + } + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) + } + + func create(){ + debugPrint(name) + let tag_id = self.$name.wrappedValue.replacingOccurrences(of: " ", with: "_") + + if tag_id.count == 0 { + errorMessage = "Enter Tag Name" + return + } + + var tagPresent = false + for tag in tags { + if tag.tag_id == tag_id { + tagPresent = true + break + } + } + if tagPresent == true { + errorMessage = "Tag already created" + return + } + + let tag = Tag(tag_id: tag_id, name: name, username: self.username, tracks: [], albums: [], artists: [], count: 0, proportion: 0.0, total_user_scrobbles: 0, last_updated: "Never") + + isLoading = true + let api = TagApi.newTag(tag_id: tag_id) + RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in + self.tags.append(tag) + self.tags = self.tags.sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) + + self.isLoading = false + self.state = false + } + } +} + +struct AddTagSheet_Previews: PreviewProvider { + static var previews: some View { + AddTagSheet(state: .constant(true), tags: .constant([]), username: .constant("username")) + } +} diff --git a/Music Tools/Views/Tag/TagRow.swift b/Music Tools/Views/Tag/TagRow.swift index e7786d9..b64ca25 100644 --- a/Music Tools/Views/Tag/TagRow.swift +++ b/Music Tools/Views/Tag/TagRow.swift @@ -11,12 +11,10 @@ import SwiftyJSON struct TagRow: View { - @EnvironmentObject var liveUser: LiveUser - - var tag: Tag + @Binding var tag: Tag var body: some View { - NavigationLink(destination: TagView(tag: tag)){ + NavigationLink(destination: TagView(tag: $tag)){ HStack { Text(tag.name) .contextMenu { @@ -39,7 +37,7 @@ struct TagRow: View { struct TagRow_Previews: PreviewProvider { static var previews: some View { - TagRow(tag: + TagRow(tag: .constant( Tag(tag_id: "tag_id", name: "tag name", username: "andy", @@ -53,6 +51,6 @@ struct TagRow_Previews: PreviewProvider { total_user_scrobbles: 2000, last_updated: "10th Feb") - ) + )) } } diff --git a/Music Tools/Views/Tag/TagView.swift b/Music Tools/Views/Tag/TagView.swift index 20588b1..3ed8c6c 100644 --- a/Music Tools/Views/Tag/TagView.swift +++ b/Music Tools/Views/Tag/TagView.swift @@ -7,18 +7,14 @@ // import SwiftUI +import SwiftUIRefresh import SwiftyJSON struct TagView: View { - init(tag: Tag) { - self.tag = tag - - // hide empty items below list - UITableView.appearance().tableFooterView = UIView() - } + @Binding var tag: Tag - var tag: Tag + @State private var isRefreshing = false var body: some View { List { @@ -82,6 +78,9 @@ struct TagView: View { } } } + .pullToRefresh(isShowing: $isRefreshing) { + self.refreshTag() + } .navigationBarTitle(Text(tag.name)) .onAppear { @@ -103,11 +102,27 @@ struct TagView: View { } //TODO: do better error checking } + + func refreshTag() { + let api = TagApi.getTag(tag_id: self.tag.tag_id) + RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in + guard let data = response.data else { + fatalError("error getting tag") + } + + guard let json = try? JSON(data: data) else { + fatalError("error parsing reponse") + } + self.tag = Tag.fromDict(dictionary: json["tag"]) + self.isRefreshing = false + } + //TODO: do better error checking + } } struct TagView_Previews: PreviewProvider { static var previews: some View { - TagView(tag: + TagView(tag: .constant( Tag(tag_id: "tag_id", name: "tag name", username: "andy", @@ -121,6 +136,6 @@ struct TagView_Previews: PreviewProvider { total_user_scrobbles: 2000, last_updated: "10th Feb") - ) + )) } }