From d25a9d8b889719bd21a215b5a9a86d18c2278f58 Mon Sep 17 00:00:00 2001 From: Andy Pack Date: Sat, 7 Jan 2023 10:54:13 +0000 Subject: [PATCH] decomposing playlist view --- Mixonomer.xcodeproj/project.pbxproj | 22 +- Mixonomer/Views/Playlist/PlaylistView.swift | 384 ------------------ .../Playlist/View/PlaylistInputSection.swift | 42 ++ .../View/PlaylistOptionsSection.swift | 89 ++++ .../Playlist/View/PlaylistStatsSection.swift | 148 +++++++ .../Views/Playlist/View/PlaylistView.swift | 185 +++++++++ 6 files changed, 485 insertions(+), 385 deletions(-) delete mode 100644 Mixonomer/Views/Playlist/PlaylistView.swift create mode 100644 Mixonomer/Views/Playlist/View/PlaylistInputSection.swift create mode 100644 Mixonomer/Views/Playlist/View/PlaylistOptionsSection.swift create mode 100644 Mixonomer/Views/Playlist/View/PlaylistStatsSection.swift create mode 100644 Mixonomer/Views/Playlist/View/PlaylistView.swift diff --git a/Mixonomer.xcodeproj/project.pbxproj b/Mixonomer.xcodeproj/project.pbxproj index bc05ef7..9efaf09 100644 --- a/Mixonomer.xcodeproj/project.pbxproj +++ b/Mixonomer.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ A10C8D29281302050018AE12 /* ToastUI in Frameworks */ = {isa = PBXBuildFile; productRef = A10C8D28281302050018AE12 /* ToastUI */; }; A11AC70628A188AE00645043 /* AuthApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11AC70528A188AE00645043 /* AuthApi.swift */; }; A13C54972928FD7C0034F233 /* ManagedInputList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A13C54962928FD7C0034F233 /* ManagedInputList.swift */; }; + A157584E29698317007B80AE /* PlaylistOptionsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A157584D29698317007B80AE /* PlaylistOptionsSection.swift */; }; + A157585029698455007B80AE /* PlaylistInputSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A157584F29698455007B80AE /* PlaylistInputSection.swift */; }; + A1575852296984A3007B80AE /* PlaylistStatsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1575851296984A3007B80AE /* PlaylistStatsSection.swift */; }; A15D257A293421350049055E /* StaticNotif.swift in Sources */ = {isa = PBXBuildFile; fileRef = A15D2579293421350049055E /* StaticNotif.swift */; }; A15D257C293425390049055E /* NotificationsControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = A15D257B293425390049055E /* NotificationsControls.swift */; }; A15D257E29342E4F0049055E /* APNSHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A15D257D29342E4F0049055E /* APNSHandler.swift */; }; @@ -76,6 +79,9 @@ A11AC70528A188AE00645043 /* AuthApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthApi.swift; sourceTree = ""; }; A13C54962928FD7C0034F233 /* ManagedInputList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedInputList.swift; sourceTree = ""; }; A146915A28118F940052999D /* Mixonomer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mixonomer.entitlements; sourceTree = ""; }; + A157584D29698317007B80AE /* PlaylistOptionsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistOptionsSection.swift; sourceTree = ""; }; + A157584F29698455007B80AE /* PlaylistInputSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistInputSection.swift; sourceTree = ""; }; + A1575851296984A3007B80AE /* PlaylistStatsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistStatsSection.swift; sourceTree = ""; }; A15D2579293421350049055E /* StaticNotif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticNotif.swift; sourceTree = ""; }; A15D257B293425390049055E /* NotificationsControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsControls.swift; sourceTree = ""; }; A15D257D29342E4F0049055E /* APNSHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APNSHandler.swift; sourceTree = ""; }; @@ -153,6 +159,17 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + A157584C296982F8007B80AE /* View */ = { + isa = PBXGroup; + children = ( + E98254C123F9FFF90056D9D3 /* PlaylistView.swift */, + A157584D29698317007B80AE /* PlaylistOptionsSection.swift */, + A157584F29698455007B80AE /* PlaylistInputSection.swift */, + A1575851296984A3007B80AE /* PlaylistStatsSection.swift */, + ); + path = View; + sourceTree = ""; + }; A15D2578293421250049055E /* Notifications */ = { isa = PBXGroup; children = ( @@ -225,7 +242,7 @@ E9E30C2E23FEACDE00574EEF /* Playlist */ = { isa = PBXGroup; children = ( - E98254C123F9FFF90056D9D3 /* PlaylistView.swift */, + A157584C296982F8007B80AE /* View */, E98254C923FA26600056D9D3 /* PlaylistRow.swift */, E97AF46623FD650800635494 /* AddPlaylistSheet.swift */, E97AF46823FD9E1B00635494 /* SpotInputList.swift */, @@ -478,10 +495,12 @@ E9EA690D23F9A5430012C3E8 /* SceneDelegate.swift in Sources */, E98254DB23FB64740056D9D3 /* Network.swift in Sources */, E9CCD5BD2454C64300B5CD6C /* TagList.swift in Sources */, + A157585029698455007B80AE /* PlaylistInputSection.swift in Sources */, E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */, E9CCD5BB2454C57300B5CD6C /* PlaylistList.swift in Sources */, A1DBCDA628A51869002CF730 /* AdminList.swift in Sources */, E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */, + A157584E29698317007B80AE /* PlaylistOptionsSection.swift in Sources */, A11AC70628A188AE00645043 /* AuthApi.swift in Sources */, A1AF726F28A84F7D00D317C9 /* AdminApi.swift in Sources */, A15D257E29342E4F0049055E /* APNSHandler.swift in Sources */, @@ -493,6 +512,7 @@ E97AF46723FD650800635494 /* AddPlaylistSheet.swift in Sources */, E971F8B9245462D500B543B6 /* Router.swift in Sources */, E98254C223F9FFF90056D9D3 /* PlaylistView.swift in Sources */, + A1575852296984A3007B80AE /* PlaylistStatsSection.swift in Sources */, E97AF46423FD4EEF00635494 /* LiveUser.swift in Sources */, E97AF45623FC4E7800635494 /* User.swift in Sources */, E98254D023FB00B60056D9D3 /* LoginScreen.swift in Sources */, diff --git a/Mixonomer/Views/Playlist/PlaylistView.swift b/Mixonomer/Views/Playlist/PlaylistView.swift deleted file mode 100644 index 26191c3..0000000 --- a/Mixonomer/Views/Playlist/PlaylistView.swift +++ /dev/null @@ -1,384 +0,0 @@ -// -// PlaylistView.swift -// Mixonomer -// -// Created by Andy Pack on 16/02/2020. -// Copyright © 2020 Sarsoo. All rights reserved. -// - -import SwiftUI -import ToastUI -import SwiftyJSON -import SwiftUICharts -import OSLog - -struct PlaylistView: View { - - @EnvironmentObject var liveUser: LiveUser - @Binding var playlist: Playlist - - @State private var showingSheet = false - @State private var isRefreshing = false - - // TOAST - @State private var showingToast = false - @State private var toastText = "" - @State private var toastSuccess = true - - var trackChartStyle: ChartStyle { - get { - let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.4765, green: 0.5976, blue: 0.7578), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray, dropShadowColor: .gray) - return _style - } - } - - var albumChartStyle: ChartStyle { - get { - let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.6367, green: 0.2968, blue: 0.4648), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray, dropShadowColor: .gray) - return _style - } - } - - var artistChartStyle: ChartStyle { - get { - let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.3476, green: 0.5195, blue: 0.3359), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray, dropShadowColor: .gray) - return _style - } - } - - var chartSize = CGSize(width:210, height:250); - - var body: some View { - Form { - - if liveUser.lastfm_connected() { - - Section(header: Text("Stats")){ - HStack { - Text("Track Count") - Spacer() - Text("\(self.playlist.lastfm_stat_count)") - .font(.title) - .foregroundColor(Color.gray) - Text("\(self.playlist.lastfm_stat_percent_str)") - .font(.body) - .foregroundColor(Color.gray) - } - HStack { - Text("Album Count") - Spacer() - Text("\(self.playlist.lastfm_stat_album_count)") - .font(.title) - .foregroundColor(Color.gray) - Text("\(self.playlist.lastfm_stat_album_percent_str)") - .font(.body) - .foregroundColor(Color.gray) - } - HStack { - Text("Artist Count") - Spacer() - Text("\(self.playlist.lastfm_stat_artist_count)") - .font(.title) - .foregroundColor(Color.gray) - Text("\(self.playlist.lastfm_stat_artist_percent_str)") - .font(.body) - .foregroundColor(Color.gray) - } - Button(action: { - self.refreshStats() - }){ - Text("Refresh") - } - } - - ScrollView(.horizontal){ - HStack { - Spacer() - PieChartView( - data: [Double(self.playlist.lastfm_stat_percent), Double(100 - self.playlist.lastfm_stat_percent)], - title: "Tracks", - legend:"Listening", - style: trackChartStyle, - form: chartSize) - Spacer(minLength: 20) - PieChartView( - data: [Double(self.playlist.lastfm_stat_album_percent), Double(100 - self.playlist.lastfm_stat_album_percent)], - title: "Albums", - legend:"Listening", - style: albumChartStyle, - form: chartSize) - Spacer(minLength: 20) - PieChartView( - data: [Double(self.playlist.lastfm_stat_artist_percent), Double(100 - self.playlist.lastfm_stat_artist_percent)], - title: "Artists", - legend:"Listening", - style: artistChartStyle, - form: chartSize) - Spacer() - } - .padding([.vertical], 20) - .padding([.horizontal], 10) - } - .listRowInsets(EdgeInsets()) - } - - Section(header: Text("Options")){ - Toggle(isOn: self.$playlist.include_recommendations) { - Text("Spotify Recommendations") - } - - if self.playlist.include_recommendations { - Stepper(onIncrement: { - self.$playlist.recommendation_sample.wrappedValue += 1 - }, - onDecrement: { - if self.playlist.recommendation_sample > 0 { - self.$playlist.recommendation_sample.wrappedValue -= 1 - - } - }){ - Text("#:") - .foregroundColor(Color.gray) - .multilineTextAlignment(.trailing) - Text("\(self.playlist.recommendation_sample)") - .multilineTextAlignment(.trailing) - - } - } - - Toggle(isOn: self.$playlist.include_library_tracks) { - Text("Library Tracks") - } - - Toggle(isOn: self.$playlist.shuffle) { - Text("Shuffle") - } - - if playlist.type == "recents" { - Toggle(isOn: self.$playlist.add_this_month) { - Text("This Month") - } - - Toggle(isOn: self.$playlist.add_last_month) { - Text("Last Month") - } - } - - if playlist.type == "fmchart" { - HStack { - Text("Chart Range") - Spacer() - Button(action: { - self.showingSheet = true - }) { - Text("\(self.playlist.chart_range.rawValue)") - .foregroundColor(Color.gray) - }.actionSheet(isPresented: $showingSheet) { - ActionSheet(title: Text("Chart range"), - message: Text("Select time range for Last.fm chart"), - buttons: [.default(Text("7 Days")), - .default(Text("1 Month")), - .default(Text("3 Months")), - .default(Text("6 Months")), - .default(Text("Year")), - .default(Text("Overall")), - .default(Text("Dismiss"))]) - } - } - } - } - Section(header: Text("Inputs")){ - NavigationLink(destination: ManagedInputList(names: self.$playlist.playlist_references, playlist: self.$playlist)) { - HStack { - Text("Managed Playlists") - Spacer() - Text("\(self.playlist.playlist_references.count)") - .foregroundColor(Color.gray) - } - } - - NavigationLink(destination: SpotInputList(names: self.$playlist.parts, playlist: self.$playlist)) { - HStack { - Text("Spotify Playlists") - Spacer() - Text("\(self.playlist.parts.count)") - .foregroundColor(Color.gray) - } - } - } - Section(header: Text("Actions"), - footer: VStack(alignment: .leading) { - Text("Last Updated \(self.playlist.last_updated ?? "never")") - Text("Stats Updated \(self.playlist.lastfm_stat_last_refresh ?? "never")") - }){ - Button(action: { self.runPlaylist() }) { - Text("Update") - } - - Button(action: { self.openPlaylist() }) { - Text("Open") - } - } - .toast(isPresented: $showingToast, dismissAfter: 1.0){ - - if toastSuccess { - ToastView(toastText) - .toastViewStyle(.success) - } - else { - ToastView(toastText) - .toastViewStyle(.failure) - } - } - .toastDimmedBackground(false) - - } - .navigationBarTitle(Text(playlist.name)) -#if os(iOS) - .refreshable { - self.refreshPlaylist() - } -#endif - } - - func runPlaylist() { - - Logger.net.debug("running playlist from view: \(self.playlist.name)") - - let api = PlaylistApi.runPlaylist(name: playlist.name) - RequestBuilder.buildRequest(apiRequest: api) - .validate() - .responseJSON{ response in - - if self.liveUser.check_network_response(response: response) { - - toastText = "Running!" - toastSuccess = true - showingToast = true - - Logger.net.debug("playlist run queued from view: \(self.playlist.name)") - - } else { - - toastText = "Run Request Failed" - toastSuccess = false - showingToast = true - - Logger.net.debug("playlist run request failed from view: \(self.playlist.name)") - } - } - } - - func refreshStats() { - - Logger.net.debug("refreshing playlist stats from view: \(self.playlist.name)") - - let api = PlaylistApi.refreshStats(name: playlist.name) - RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in - - if self.liveUser.check_network_response(response: response) { - - toastText = "Refreshing Stats!" - toastSuccess = true - showingToast = true - - Logger.net.debug("stat refresh queued from view: \(self.playlist.name)") - - } else { - - toastText = "Stat Refresh Failed" - toastSuccess = false - showingToast = true - - Logger.net.debug("stat refresh request failed from view: \(self.playlist.name)") - - } - } - } - - func openPlaylist() { - - Logger.sys.debug("attempting to open \(self.playlist.link)") - - if let url = URL(string: self.playlist.link) { - UIApplication.shared.open(url) - } - } - - func updatePlaylist(updates: JSON) { - - Logger.net.debug("updating playlist from view: \(self.playlist.name)") - - let api = PlaylistApi.updatePlaylist(name: playlist.name, updates: updates) - RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in - - if self.liveUser.check_network_response(response: response) { - Logger.net.debug("updated playlist from view") - } else { - Logger.net.error("failed to update playlist from view") - } - } - } - - func refreshPlaylist() { - - Logger.net.debug("Refreshing playlist: \(self.playlist.name)") - - let api = PlaylistApi.getPlaylist(name: self.playlist.name) - RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in - - if self.liveUser.check_network_response(response: response) { - - guard let data = response.data else { - Logger.net.error("failed to get playlist from net request") - return - } - - self.playlist = PlaylistApi.fromJSON(playlist: data)! - - toastText = "Refreshed!" - toastSuccess = true - showingToast = true - - Logger.net.debug("Successfully refreshed playlist: \(self.playlist.name)") - - } else { - - Logger.net.error("request failed for get playlist") - - toastText = "Refresh Failed" - toastSuccess = false - showingToast = true - - } - - self.isRefreshing = false - } - } -} - -struct PlaylistView_Previews: PreviewProvider { - static var previews: some View { - Group { - PlaylistView(playlist: .constant( - Playlist(name: "playlist name", - username: "username", - lastfm_stat_percent: 30, - lastfm_stat_album_percent: 40, - lastfm_stat_artist_percent: 80 - ) - )) - .environmentObject(LiveUser.get_preview_user()) - PlaylistView(playlist: .constant( - Playlist(name: "playlist name", - username: "username", - lastfm_stat_percent: 30, - lastfm_stat_album_percent: 40, - lastfm_stat_artist_percent: 80 - ) - )) - .environmentObject(LiveUser.get_preview_user_with_user()) - } - - } -} diff --git a/Mixonomer/Views/Playlist/View/PlaylistInputSection.swift b/Mixonomer/Views/Playlist/View/PlaylistInputSection.swift new file mode 100644 index 0000000..7faa227 --- /dev/null +++ b/Mixonomer/Views/Playlist/View/PlaylistInputSection.swift @@ -0,0 +1,42 @@ +// +// PlaylistInputSection.swift +// Mixonomer +// +// Created by Andy Pack on 07/01/2023. +// Copyright © 2023 Sarsoo. All rights reserved. +// + +import SwiftUI + +struct PlaylistInputSection: View { + + @Binding var playlist: Playlist + + var body: some View { + Section(header: Text("Inputs")){ + NavigationLink(destination: ManagedInputList(names: self.$playlist.playlist_references, playlist: self.$playlist)) { + HStack { + Text("Managed Playlists") + Spacer() + Text("\(self.playlist.playlist_references.count)") + .foregroundColor(Color.gray) + } + } + + NavigationLink(destination: SpotInputList(names: self.$playlist.parts, playlist: self.$playlist)) { + HStack { + Text("Spotify Playlists") + Spacer() + Text("\(self.playlist.parts.count)") + .foregroundColor(Color.gray) + } + } + } + } +} + +struct PlaylistInputSection_Previews: PreviewProvider { + static var previews: some View { + PlaylistInputSection(playlist: .constant(Playlist(name: "Test"))) + } +} diff --git a/Mixonomer/Views/Playlist/View/PlaylistOptionsSection.swift b/Mixonomer/Views/Playlist/View/PlaylistOptionsSection.swift new file mode 100644 index 0000000..5025379 --- /dev/null +++ b/Mixonomer/Views/Playlist/View/PlaylistOptionsSection.swift @@ -0,0 +1,89 @@ +// +// PlaylistOptionsSection.swift +// Mixonomer +// +// Created by Andy Pack on 07/01/2023. +// Copyright © 2023 Sarsoo. All rights reserved. +// + +import SwiftUI + +struct PlaylistOptionsSection: View { + + @Binding var playlist: Playlist + @Binding var showingSheet: Bool + + var body: some View { + Section(header: Text("Options")){ + Toggle(isOn: self.$playlist.include_recommendations) { + Text("Spotify Recommendations") + } + + if self.playlist.include_recommendations { + Stepper(onIncrement: { + self.$playlist.recommendation_sample.wrappedValue += 1 + }, + onDecrement: { + if self.playlist.recommendation_sample > 0 { + self.$playlist.recommendation_sample.wrappedValue -= 1 + + } + }){ + Text("#:") + .foregroundColor(Color.gray) + .multilineTextAlignment(.trailing) + Text("\(self.playlist.recommendation_sample)") + .multilineTextAlignment(.trailing) + + } + } + + Toggle(isOn: self.$playlist.include_library_tracks) { + Text("Library Tracks") + } + + Toggle(isOn: self.$playlist.shuffle) { + Text("Shuffle") + } + + if playlist.type == "recents" { + Toggle(isOn: self.$playlist.add_this_month) { + Text("This Month") + } + + Toggle(isOn: self.$playlist.add_last_month) { + Text("Last Month") + } + } + + if playlist.type == "fmchart" { + HStack { + Text("Chart Range") + Spacer() + Button(action: { + self.showingSheet = true + }) { + Text("\(self.playlist.chart_range.rawValue)") + .foregroundColor(Color.gray) + }.actionSheet(isPresented: $showingSheet) { + ActionSheet(title: Text("Chart range"), + message: Text("Select time range for Last.fm chart"), + buttons: [.default(Text("7 Days")), + .default(Text("1 Month")), + .default(Text("3 Months")), + .default(Text("6 Months")), + .default(Text("Year")), + .default(Text("Overall")), + .default(Text("Dismiss"))]) + } + } + } + } + } +} + +struct PlaylistOptionsSection_Previews: PreviewProvider { + static var previews: some View { + PlaylistOptionsSection(playlist: .constant(Playlist(name: "Test")), showingSheet: .constant(false)) + } +} diff --git a/Mixonomer/Views/Playlist/View/PlaylistStatsSection.swift b/Mixonomer/Views/Playlist/View/PlaylistStatsSection.swift new file mode 100644 index 0000000..8eac476 --- /dev/null +++ b/Mixonomer/Views/Playlist/View/PlaylistStatsSection.swift @@ -0,0 +1,148 @@ +// +// PlaylistStatsSection.swift +// Mixonomer +// +// Created by Andy Pack on 07/01/2023. +// Copyright © 2023 Sarsoo. All rights reserved. +// + +import SwiftUI +import SwiftUICharts +import OSLog + +struct PlaylistStatsSection: View { + + @EnvironmentObject var liveUser: LiveUser + + @Binding var playlist: Playlist + + @Binding var showingToast: Bool + @Binding var toastText: String + @Binding var toastSuccess: Bool + + var trackChartStyle: ChartStyle { + get { + let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.4765, green: 0.5976, blue: 0.7578), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray, dropShadowColor: .gray) + return _style + } + } + + var albumChartStyle: ChartStyle { + get { + let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.6367, green: 0.2968, blue: 0.4648), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray, dropShadowColor: .gray) + return _style + } + } + + var artistChartStyle: ChartStyle { + get { + let _style = ChartStyle(backgroundColor: .white, accentColor: Color(red: 0.3476, green: 0.5195, blue: 0.3359), gradientColor: GradientColors.bluPurpl, textColor: .black, legendTextColor: .gray, dropShadowColor: .gray) + return _style + } + } + + var chartSize = CGSize(width:210, height:250); + + var body: some View { + Section(header: Text("Stats")){ + HStack { + Text("Track Count") + Spacer() + Text("\(self.playlist.lastfm_stat_count)") + .font(.title) + .foregroundColor(Color.gray) + Text("\(self.playlist.lastfm_stat_percent_str)") + .font(.body) + .foregroundColor(Color.gray) + } + HStack { + Text("Album Count") + Spacer() + Text("\(self.playlist.lastfm_stat_album_count)") + .font(.title) + .foregroundColor(Color.gray) + Text("\(self.playlist.lastfm_stat_album_percent_str)") + .font(.body) + .foregroundColor(Color.gray) + } + HStack { + Text("Artist Count") + Spacer() + Text("\(self.playlist.lastfm_stat_artist_count)") + .font(.title) + .foregroundColor(Color.gray) + Text("\(self.playlist.lastfm_stat_artist_percent_str)") + .font(.body) + .foregroundColor(Color.gray) + } + Button(action: { + self.refreshStats() + }){ + Text("Refresh") + } + } + + ScrollView(.horizontal){ + HStack { + Spacer() + PieChartView( + data: [Double(self.playlist.lastfm_stat_percent), Double(100 - self.playlist.lastfm_stat_percent)], + title: "Tracks", + legend:"Listening", + style: trackChartStyle, + form: chartSize) + Spacer(minLength: 20) + PieChartView( + data: [Double(self.playlist.lastfm_stat_album_percent), Double(100 - self.playlist.lastfm_stat_album_percent)], + title: "Albums", + legend:"Listening", + style: albumChartStyle, + form: chartSize) + Spacer(minLength: 20) + PieChartView( + data: [Double(self.playlist.lastfm_stat_artist_percent), Double(100 - self.playlist.lastfm_stat_artist_percent)], + title: "Artists", + legend:"Listening", + style: artistChartStyle, + form: chartSize) + Spacer() + } + .padding([.vertical], 20) + .padding([.horizontal], 10) + } + .listRowInsets(EdgeInsets()) + } + + func refreshStats() { + + Logger.net.debug("refreshing playlist stats from view: \(self.playlist.name)") + + let api = PlaylistApi.refreshStats(name: playlist.name) + RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in + + if self.liveUser.check_network_response(response: response) { + + toastText = "Refreshing Stats!" + toastSuccess = true + showingToast = true + + Logger.net.debug("stat refresh queued from view: \(self.playlist.name)") + + } else { + + toastText = "Stat Refresh Failed" + toastSuccess = false + showingToast = true + + Logger.net.debug("stat refresh request failed from view: \(self.playlist.name)") + + } + } + } +} + +struct PlaylistStatsSection_Previews: PreviewProvider { + static var previews: some View { + PlaylistStatsSection(playlist: .constant(Playlist(name: "test")), showingToast: .constant(false), toastText: .constant(""), toastSuccess: .constant(false)) + } +} diff --git a/Mixonomer/Views/Playlist/View/PlaylistView.swift b/Mixonomer/Views/Playlist/View/PlaylistView.swift new file mode 100644 index 0000000..100fae0 --- /dev/null +++ b/Mixonomer/Views/Playlist/View/PlaylistView.swift @@ -0,0 +1,185 @@ +// +// PlaylistView.swift +// Mixonomer +// +// Created by Andy Pack on 16/02/2020. +// Copyright © 2020 Sarsoo. All rights reserved. +// + +import SwiftUI +import ToastUI +import SwiftyJSON +import OSLog + +struct PlaylistView: View { + + @EnvironmentObject var liveUser: LiveUser + @Binding var playlist: Playlist + + @State private var showingSheet = false + @State private var isRefreshing = false + + // TOAST + @State private var showingToast = false + @State private var toastText = "" + @State private var toastSuccess = true + + var body: some View { + Form { + + if liveUser.lastfm_connected() { + PlaylistStatsSection(playlist: $playlist, showingToast: $showingToast, toastText: $toastText, toastSuccess: $toastSuccess) + } + + PlaylistOptionsSection(playlist: $playlist, showingSheet: $showingSheet) + PlaylistInputSection(playlist: $playlist) + + Section(header: Text("Actions"), + footer: VStack(alignment: .leading) { + Text("Last Updated \(self.playlist.last_updated ?? "never")") + Text("Stats Updated \(self.playlist.lastfm_stat_last_refresh ?? "never")") + }){ + Button(action: { self.runPlaylist() }) { + Text("Update") + } + + Button(action: { self.openPlaylist() }) { + Text("Open") + } + } + .toast(isPresented: $showingToast, dismissAfter: 1.0){ + + if toastSuccess { + ToastView(toastText) + .toastViewStyle(.success) + } + else { + ToastView(toastText) + .toastViewStyle(.failure) + } + } + .toastDimmedBackground(false) + + } + .navigationBarTitle(Text(playlist.name)) +#if os(iOS) + .refreshable { + self.refreshPlaylist() + } +#endif + } + + func runPlaylist() { + + Logger.net.debug("running playlist from view: \(self.playlist.name)") + + let api = PlaylistApi.runPlaylist(name: playlist.name) + RequestBuilder.buildRequest(apiRequest: api) + .validate() + .responseJSON{ response in + + if self.liveUser.check_network_response(response: response) { + + toastText = "Running!" + toastSuccess = true + showingToast = true + + Logger.net.debug("playlist run queued from view: \(self.playlist.name)") + + } else { + + toastText = "Run Request Failed" + toastSuccess = false + showingToast = true + + Logger.net.debug("playlist run request failed from view: \(self.playlist.name)") + } + } + } + + func openPlaylist() { + + Logger.sys.debug("attempting to open \(self.playlist.link)") + + if let url = URL(string: self.playlist.link) { + UIApplication.shared.open(url) + } + } + + func updatePlaylist(updates: JSON) { + + Logger.net.debug("updating playlist from view: \(self.playlist.name)") + + let api = PlaylistApi.updatePlaylist(name: playlist.name, updates: updates) + RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in + + if self.liveUser.check_network_response(response: response) { + Logger.net.debug("updated playlist from view") + } else { + Logger.net.error("failed to update playlist from view") + } + } + } + + func refreshPlaylist() { + + Logger.net.debug("Refreshing playlist: \(self.playlist.name)") + + let api = PlaylistApi.getPlaylist(name: self.playlist.name) + RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in + + if self.liveUser.check_network_response(response: response) { + + guard let data = response.data else { + Logger.net.error("failed to get playlist from net request") + return + } + + self.playlist = PlaylistApi.fromJSON(playlist: data)! + + toastText = "Refreshed!" + toastSuccess = true + showingToast = true + + Logger.net.debug("Successfully refreshed playlist: \(self.playlist.name)") + + } else { + + Logger.net.error("request failed for get playlist") + + toastText = "Refresh Failed" + toastSuccess = false + showingToast = true + + } + + self.isRefreshing = false + } + } +} + +struct PlaylistView_Previews: PreviewProvider { + static var previews: some View { + Group { + PlaylistView(playlist: .constant( + Playlist(name: "playlist name", + username: "username", + lastfm_stat_percent: 30, + lastfm_stat_album_percent: 40, + lastfm_stat_artist_percent: 80 + ) + )) + .environmentObject(LiveUser.get_preview_user()) + PlaylistView(playlist: .constant( + Playlist(name: "playlist name", + username: "username", + lastfm_stat_percent: 30, + lastfm_stat_album_percent: 40, + lastfm_stat_artist_percent: 80 + ) + )) + .environmentObject(LiveUser.get_preview_user_with_user()) + } + + } +}