fully bound tag/playlist views to same environment object authoritative source, added pull to refresh on object views, added footer logo in settings
This commit is contained in:
parent
6318ab4f16
commit
a906b5396a
@ -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 = "<group>"; };
|
||||
E97AF45523FC4E7800635494 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
|
||||
E97AF45A23FC748D00635494 /* UserApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserApi.swift; sourceTree = "<group>"; };
|
||||
E97AF45F23FC85D600635494 /* PlaylistApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistApi.swift; sourceTree = "<group>"; };
|
||||
@ -192,6 +194,7 @@
|
||||
E9E30C2923FEAA3A00574EEF /* TagRow.swift */,
|
||||
E9E30C2C23FEAB0200574EEF /* TagView.swift */,
|
||||
E9E30C3023FEAF2B00574EEF /* TagObjList.swift */,
|
||||
E934AC98240DD0E4009869F4 /* AddTagSheet.swift */,
|
||||
);
|
||||
path = Tag;
|
||||
sourceTree = "<group>";
|
||||
@ -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 */,
|
||||
|
@ -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)
|
||||
|
21
Music Tools/Assets.xcassets/APFooter.imageset/Contents.json
vendored
Normal file
21
Music Tools/Assets.xcassets/APFooter.imageset/Contents.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
BIN
Music Tools/Assets.xcassets/APFooter.imageset/ap.png
vendored
Normal file
BIN
Music Tools/Assets.xcassets/APFooter.imageset/ap.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 273 KiB |
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ struct AddPlaylistSheet: View {
|
||||
|
||||
@Binding var state: Bool
|
||||
@Binding var playlists: Array<Playlist>
|
||||
@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"))
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -19,14 +19,8 @@ struct RootView: View {
|
||||
|
||||
@State private var showAdd = false // State for showing add modal view
|
||||
|
||||
@State private var justDeletedPlaylists: Array<Playlist> = [] // Cache of recently deleted playlists for removing from next net request
|
||||
@State private var justDeletedTags: Array<Tag> = []
|
||||
|
||||
@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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
90
Music Tools/Views/Tag/AddTagSheet.swift
Normal file
90
Music Tools/Views/Tag/AddTagSheet.swift
Normal file
@ -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<Tag>
|
||||
@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"))
|
||||
}
|
||||
}
|
@ -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")
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user