restructured main views, added totals to rows

This commit is contained in:
aj 2020-04-26 00:11:07 +01:00
parent 6d3929a173
commit fced8c9fc3
12 changed files with 273 additions and 207 deletions

View File

@ -26,6 +26,8 @@
E98254D023FB00B60056D9D3 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254CF23FB00B60056D9D3 /* LoginScreen.swift */; }; E98254D023FB00B60056D9D3 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254CF23FB00B60056D9D3 /* LoginScreen.swift */; };
E98254D923FB53780056D9D3 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = E98254D823FB53780056D9D3 /* Alamofire */; }; E98254D923FB53780056D9D3 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = E98254D823FB53780056D9D3 /* Alamofire */; };
E98254DB23FB64740056D9D3 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254DA23FB64740056D9D3 /* Network.swift */; }; E98254DB23FB64740056D9D3 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254DA23FB64740056D9D3 /* Network.swift */; };
E9CCD5BB2454C57300B5CD6C /* PlaylistList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9CCD5BA2454C57300B5CD6C /* PlaylistList.swift */; };
E9CCD5BD2454C64300B5CD6C /* TagList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9CCD5BC2454C64300B5CD6C /* TagList.swift */; };
E9E30C2623FEA4F000574EEF /* TagApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E30C2523FEA4EF00574EEF /* TagApi.swift */; }; E9E30C2623FEA4F000574EEF /* TagApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E30C2523FEA4EF00574EEF /* TagApi.swift */; };
E9E30C2823FEA6BD00574EEF /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E30C2723FEA6BD00574EEF /* Tag.swift */; }; E9E30C2823FEA6BD00574EEF /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E30C2723FEA6BD00574EEF /* Tag.swift */; };
E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E30C2923FEAA3A00574EEF /* TagRow.swift */; }; E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E30C2923FEAA3A00574EEF /* TagRow.swift */; };
@ -74,6 +76,8 @@
E98254C923FA26600056D9D3 /* PlaylistRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistRow.swift; sourceTree = "<group>"; }; E98254C923FA26600056D9D3 /* PlaylistRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistRow.swift; sourceTree = "<group>"; };
E98254CF23FB00B60056D9D3 /* LoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = "<group>"; }; E98254CF23FB00B60056D9D3 /* LoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = "<group>"; };
E98254DA23FB64740056D9D3 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; }; E98254DA23FB64740056D9D3 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
E9CCD5BA2454C57300B5CD6C /* PlaylistList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistList.swift; sourceTree = "<group>"; };
E9CCD5BC2454C64300B5CD6C /* TagList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagList.swift; sourceTree = "<group>"; };
E9E30C2523FEA4EF00574EEF /* TagApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagApi.swift; sourceTree = "<group>"; }; E9E30C2523FEA4EF00574EEF /* TagApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagApi.swift; sourceTree = "<group>"; };
E9E30C2723FEA6BD00574EEF /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = "<group>"; }; E9E30C2723FEA6BD00574EEF /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = "<group>"; };
E9E30C2923FEAA3A00574EEF /* TagRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagRow.swift; sourceTree = "<group>"; }; E9E30C2923FEAA3A00574EEF /* TagRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagRow.swift; sourceTree = "<group>"; };
@ -178,6 +182,7 @@
E98254C923FA26600056D9D3 /* PlaylistRow.swift */, E98254C923FA26600056D9D3 /* PlaylistRow.swift */,
E97AF46623FD650800635494 /* AddPlaylistSheet.swift */, E97AF46623FD650800635494 /* AddPlaylistSheet.swift */,
E97AF46823FD9E1B00635494 /* PlaylistInputList.swift */, E97AF46823FD9E1B00635494 /* PlaylistInputList.swift */,
E9CCD5BA2454C57300B5CD6C /* PlaylistList.swift */,
); );
path = Playlist; path = Playlist;
sourceTree = "<group>"; sourceTree = "<group>";
@ -189,6 +194,7 @@
E9E30C2C23FEAB0200574EEF /* TagView.swift */, E9E30C2C23FEAB0200574EEF /* TagView.swift */,
E9E30C3023FEAF2B00574EEF /* TagObjList.swift */, E9E30C3023FEAF2B00574EEF /* TagObjList.swift */,
E934AC98240DD0E4009869F4 /* AddTagSheet.swift */, E934AC98240DD0E4009869F4 /* AddTagSheet.swift */,
E9CCD5BC2454C64300B5CD6C /* TagList.swift */,
); );
path = Tag; path = Tag;
sourceTree = "<group>"; sourceTree = "<group>";
@ -417,7 +423,9 @@
E9E30C3323FF255C00574EEF /* SettingsList.swift in Sources */, E9E30C3323FF255C00574EEF /* SettingsList.swift in Sources */,
E9EA690D23F9A5430012C3E8 /* SceneDelegate.swift in Sources */, E9EA690D23F9A5430012C3E8 /* SceneDelegate.swift in Sources */,
E98254DB23FB64740056D9D3 /* Network.swift in Sources */, E98254DB23FB64740056D9D3 /* Network.swift in Sources */,
E9CCD5BD2454C64300B5CD6C /* TagList.swift in Sources */,
E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */, E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */,
E9CCD5BB2454C57300B5CD6C /* PlaylistList.swift in Sources */,
E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */, E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */,
E9EA690F23F9A5430012C3E8 /* AppSkeleton.swift in Sources */, E9EA690F23F9A5430012C3E8 /* AppSkeleton.swift in Sources */,
E98254BD23F9B7A90056D9D3 /* Playlist.swift in Sources */, E98254BD23F9B7A90056D9D3 /* Playlist.swift in Sources */,

View File

@ -15,15 +15,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch. // Override point for customization after application launch.
let keychain = Keychain(service: "xyz.sarsoo.music.login")
// do {
// try keychain.remove("username")
// try keychain.remove("password")
// } catch let error {
// debugPrint("Could not clear keychain, \(error)")
// }
//
return true return true
} }

View File

@ -27,13 +27,9 @@ 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).loadUserDefaults()
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(liveUser)) window.rootViewController = UIHostingController(rootView: contentView.environmentObject(liveUser))
// window.rootViewController = UIHostingController(rootView: contentView.environmentObject(liveUser))
// window.rootViewController = LoginController()
self.window = window self.window = window
window.makeKeyAndVisible() window.makeKeyAndVisible()
} }
@ -66,7 +62,5 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// Use this method to save data, release shared resources, and store enough scene-specific state information // Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state. // to restore the scene back to its current state.
} }
} }

View File

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
// Root level view loaded by delegate to show either main app or login screen
struct Router: View { struct Router: View {
@EnvironmentObject var liveUser: LiveUser @EnvironmentObject var liveUser: LiveUser

View File

@ -16,52 +16,11 @@ struct AppSkeleton: View {
@EnvironmentObject var liveUser: LiveUser @EnvironmentObject var liveUser: LiveUser
@State private var selection = 0 // Tab view selection @State private var selection = 0 // Tab view selection
@State private var showAdd = false // State for showing add modal view
var body: some View { var body: some View {
TabView { TabView {
// PLAYLISTS PlaylistList()
NavigationView {
List{
if(liveUser.playlists.count > 0){
ForEach(liveUser.playlists.indices, id:\.self) { idx in
PlaylistRow(playlist: self.$liveUser.playlists[idx])
}
.onDelete { indexSet in
indexSet.forEach { index in
let api = PlaylistApi.deletePlaylist(name: self.liveUser.playlists[index].name)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
}
}
self.liveUser.playlists.remove(atOffsets: indexSet)
}
}else {
HStack {
Text("No Playlists")
.multilineTextAlignment(.center)
Spacer()
}
}
}
.pullToRefresh(isShowing: self.$liveUser.isRefreshingPlaylists) {
self.liveUser.refreshPlaylists()
}
.navigationBarTitle(Text("Playlists").font(.title))
// add playlist button
.navigationBarItems(trailing:
Button(
action: { self.showAdd = true },
label: { Text("Add") }
).sheet(isPresented: $showAdd) {
AddPlaylistSheet(playlists: self.$liveUser.playlists, username: self.$liveUser.username)
}
)
}
.tabItem { .tabItem {
VStack { VStack {
Image(systemName: "music.note.list") Image(systemName: "music.note.list")
@ -70,47 +29,7 @@ struct AppSkeleton: View {
} }
.tag(0) .tag(0)
// TAGS TagList()
NavigationView {
List{
if(liveUser.tags.count > 0) {
ForEach(liveUser.tags.indices, id:\.self) { idx in
TagRow(tag: self.$liveUser.tags[idx])
}
.onDelete { indexSet in
indexSet.forEach { index in
let api = TagApi.deleteTag(tag_id: self.liveUser.tags[index].tag_id)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
}
}
self.liveUser.tags.remove(atOffsets: indexSet)
}
} else {
HStack {
Text("No Tags")
.multilineTextAlignment(.center)
Spacer()
}
}
}
.pullToRefresh(isShowing: self.$liveUser.isRefreshingTags) {
self.liveUser.refreshTags()
}
.navigationBarTitle(Text("Tags").font(.title))
// add playlist button
.navigationBarItems(trailing:
Button(
action: { self.showAdd = true },
label: { Text("Add") }
).sheet(isPresented: $showAdd) {
AddTagSheet(tags: self.$liveUser.tags, username: self.$liveUser.username)
}
)
}
.tabItem { .tabItem {
VStack { VStack {
Image(systemName: "tag") Image(systemName: "tag")
@ -119,17 +38,15 @@ struct AppSkeleton: View {
} }
.tag(1) .tag(1)
// SETTINGS
NavigationView {
SettingsList() SettingsList()
.navigationBarTitle(Text("Settings").font(.title)) .tabItem {
}.tabItem {
VStack { VStack {
Image(systemName: "slider.horizontal.3") Image(systemName: "slider.horizontal.3")
Text("Settings") Text("Settings")
} }
} }
.tag(2) .tag(2)
}.onAppear { }.onAppear {
self.fetchAll() self.fetchAll()
} }

View File

@ -0,0 +1,58 @@
//
// PlaylistList.swift
// Music Tools
//
// Created by Andy Pack on 25/04/2020.
// Copyright © 2020 Sarsoo. All rights reserved.
//
import SwiftUI
struct PlaylistList: View {
@EnvironmentObject var liveUser: LiveUser
@State private var showAdd = false // State for showing add modal view
var body: some View {
NavigationView {
List{
if(liveUser.playlists.count > 0){
ForEach(liveUser.playlists.indices, id:\.self) { idx in
PlaylistRow(playlist: self.$liveUser.playlists[idx])
}
.onDelete { indexSet in
indexSet.forEach { index in
let api = PlaylistApi.deletePlaylist(name: self.liveUser.playlists[index].name)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
}
}
self.liveUser.playlists.remove(atOffsets: indexSet)
}
}else {
Text("No Playlists")
}
}
.pullToRefresh(isShowing: self.$liveUser.isRefreshingPlaylists) {
self.liveUser.refreshPlaylists()
}
.navigationBarTitle(Text("Playlists 📻"))
.navigationBarItems(trailing:
Button(
action: { self.showAdd = true },
label: { Text("Add") }
).sheet(isPresented: $showAdd) {
AddPlaylistSheet(playlists: self.$liveUser.playlists, username: self.$liveUser.username)
}
)
}
}
}
struct PlaylistList_Previews: PreviewProvider {
static var previews: some View {
PlaylistList()
}
}

View File

@ -10,26 +10,36 @@ import SwiftUI
import SwiftyJSON import SwiftyJSON
struct PlaylistRow: View { struct PlaylistRow: View {
@Binding var playlist: Playlist @Binding var playlist: Playlist
@State private var showingNetworkError = false
var body: some View { var body: some View {
NavigationLink(destination: PlaylistView(playlist: $playlist)){ NavigationLink(destination: PlaylistView(playlist: $playlist)){
HStack { HStack {
Text(playlist.name) Text(playlist.name)
.contextMenu { if playlist.lastfm_stat_count > 0 {
Spacer()
// run force touch Text("\(playlist.lastfm_stat_count)")
.foregroundColor(.gray)
}
}.contextMenu {
Button(action: { Button(action: {
let api = PlaylistApi.runPlaylist(name: self.playlist.name) let api = PlaylistApi.runPlaylist(name: self.playlist.name)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api)
.validate()
.responseJSON{ response in
switch response.result {
case .success:
break
case .failure:
self.showingNetworkError = true
}
} }
}) { }) {
Text("Refresh") Text("Refresh")
Image(systemName: "arrow.clockwise.circle") Image(systemName: "arrow.clockwise.circle")
} }
// open force touch
Button(action: { Button(action: {
if let url = URL(string: self.playlist.link) { if let url = URL(string: self.playlist.link) {
UIApplication.shared.open(url) UIApplication.shared.open(url)
@ -38,7 +48,9 @@ struct PlaylistRow: View {
Text("Open") Text("Open")
Image(systemName: "arrowshape.turn.up.right.circle") Image(systemName: "arrowshape.turn.up.right.circle")
} }
} }.alert(isPresented: $showingNetworkError) {
Alert(title: Text("Network Error"),
message: Text("Could not refresh playlist"))
} }
} }
} }

View File

@ -24,6 +24,7 @@ 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
var chartStyle: ChartStyle { var chartStyle: ChartStyle {
get { get {
@ -211,6 +212,10 @@ struct PlaylistView: View {
self.$chart_limit.wrappedValue = playlist.chart_limit self.$chart_limit.wrappedValue = playlist.chart_limit
} }
} }
.alert(isPresented: $showingNetworkError) {
Alert(title: Text("Network Error"),
message: Text("Could not refresh playlist"))
}
} }
func changeChartRange(newRange: LastFmRange) { func changeChartRange(newRange: LastFmRange) {
@ -222,8 +227,15 @@ struct PlaylistView: View {
func runPlaylist() { func runPlaylist() {
let api = PlaylistApi.runPlaylist(name: playlist.name) let api = PlaylistApi.runPlaylist(name: playlist.name)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api)
.validate()
.responseJSON{ response in
switch response.result {
case .success:
break
case .failure:
self.showingNetworkError = true
}
} }
//TODO: do better error checking //TODO: do better error checking
} }

View File

@ -13,12 +13,8 @@ struct SettingsList: View {
@EnvironmentObject var liveUser: LiveUser @EnvironmentObject var liveUser: LiveUser
init(){
UITableView.appearance().tableFooterView = UIView()
}
var body: some View { var body: some View {
VStack{ NavigationView {
List{ List{
Section { Section {
Button(action: { Button(action: {
@ -71,7 +67,9 @@ struct SettingsList: View {
Text("iOS Source") Text("iOS Source")
} }
} }
}.listStyle(GroupedListStyle()) }
.listStyle(GroupedListStyle())
.navigationBarTitle(Text("Settings ⚡️").font(.title))
} }
} }
} }

View File

@ -0,0 +1,62 @@
//
// TagList.swift
// Music Tools
//
// Created by Andy Pack on 25/04/2020.
// Copyright © 2020 Sarsoo. All rights reserved.
//
import SwiftUI
struct TagList: View {
@EnvironmentObject var liveUser: LiveUser
@State private var showAdd = false // State for showing add modal view
var body: some View {
NavigationView {
List{
if(liveUser.tags.count > 0) {
ForEach(liveUser.tags.indices, id:\.self) { idx in
TagRow(tag: self.$liveUser.tags[idx])
}
.onDelete { indexSet in
indexSet.forEach { index in
let api = TagApi.deleteTag(tag_id: self.liveUser.tags[index].tag_id)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
}
}
self.liveUser.tags.remove(atOffsets: indexSet)
}
} else {
Text("No Tags")
}
}
.pullToRefresh(isShowing: self.$liveUser.isRefreshingTags) {
self.liveUser.refreshTags()
}
.navigationBarTitle(Text("Tags 🎷"))
.navigationBarItems(
leading:
EditButton(),
trailing:
Button(
action: { self.showAdd = true },
label: { Text("Add") }
).sheet(isPresented: $showAdd) {
AddTagSheet(tags: self.$liveUser.tags, username: self.$liveUser.username)
}
)
}
}
}
struct TagList_Previews: PreviewProvider {
static var previews: some View {
TagList()
}
}

View File

@ -12,24 +12,37 @@ import SwiftyJSON
struct TagRow: View { struct TagRow: View {
@Binding var tag: Tag @Binding var tag: Tag
@State private var showingNetworkError = false
var body: some View { var body: some View {
NavigationLink(destination: TagView(tag: $tag)){ NavigationLink(destination: TagView(tag: $tag)){
HStack { HStack {
Text(tag.name) Text(tag.name)
.contextMenu { if tag.count > 0 {
Spacer()
// run force touch Text("\(tag.count)")
.foregroundColor(.gray)
}
}.contextMenu {
Button(action: { Button(action: {
let api = TagApi.runTag(tag_id: self.tag.tag_id) let api = TagApi.runTag(tag_id: self.tag.tag_id)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in RequestBuilder.buildRequest(apiRequest: api)
.validate()
.responseJSON{ response in
switch response.result {
case .success:
break
case .failure:
self.showingNetworkError = true
}
} }
}) { }) {
Text("Refresh") Text("Refresh")
Image(systemName: "arrow.clockwise.circle") Image(systemName: "arrow.clockwise.circle")
} }
} }.alert(isPresented: $showingNetworkError) {
Alert(title: Text("Network Error"),
message: Text("Could not refresh tag"))
} }
} }
} }

View File

@ -43,7 +43,7 @@ struct TagView: View {
.foregroundColor(Color.gray) .foregroundColor(Color.gray)
} }
HStack { HStack {
Text("User Total") Text("Total")
Spacer() Spacer()
Text("\(self.tag.total_user_scrobbles)") Text("\(self.tag.total_user_scrobbles)")
.font(.title) .font(.title)