tweaking settings, splitting spotify and managed input lists

This commit is contained in:
Andy Pack 2022-11-19 12:28:33 +00:00
parent 761a755973
commit d73ec449cf
Signed by: sarsoo
GPG Key ID: A55BA3536A5E0ED7
10 changed files with 216 additions and 97 deletions

View File

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
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 */; };
A1AF726F28A84F7D00D317C9 /* AdminApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1AF726E28A84F7D00D317C9 /* AdminApi.swift */; };
A1AF727128A850AE00D317C9 /* UsersList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1AF727028A850AE00D317C9 /* UsersList.swift */; };
A1AF727328A9062600D317C9 /* UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1AF727228A9062600D317C9 /* UserView.swift */; };
@ -24,7 +25,7 @@
E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF45F23FC85D600635494 /* PlaylistApi.swift */; };
E97AF46423FD4EEF00635494 /* LiveUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46323FD4EEF00635494 /* LiveUser.swift */; };
E97AF46723FD650800635494 /* AddPlaylistSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46623FD650800635494 /* AddPlaylistSheet.swift */; };
E97AF46923FD9E1B00635494 /* PlaylistInputList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46823FD9E1B00635494 /* PlaylistInputList.swift */; };
E97AF46923FD9E1B00635494 /* SpotInputList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46823FD9E1B00635494 /* SpotInputList.swift */; };
E98254BD23F9B7A90056D9D3 /* Playlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254BC23F9B7A90056D9D3 /* Playlist.swift */; };
E98254C223F9FFF90056D9D3 /* PlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254C123F9FFF90056D9D3 /* PlaylistView.swift */; };
E98254CA23FA26600056D9D3 /* PlaylistRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254C923FA26600056D9D3 /* PlaylistRow.swift */; };
@ -68,6 +69,7 @@
/* Begin PBXFileReference section */
A11AC70528A188AE00645043 /* AuthApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthApi.swift; sourceTree = "<group>"; };
A13C54962928FD7C0034F233 /* ManagedInputList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedInputList.swift; sourceTree = "<group>"; };
A146915A28118F940052999D /* Mixonomer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mixonomer.entitlements; sourceTree = "<group>"; };
A1AF726E28A84F7D00D317C9 /* AdminApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminApi.swift; sourceTree = "<group>"; };
A1AF727028A850AE00D317C9 /* UsersList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersList.swift; sourceTree = "<group>"; };
@ -81,7 +83,7 @@
E97AF45F23FC85D600635494 /* PlaylistApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistApi.swift; sourceTree = "<group>"; };
E97AF46323FD4EEF00635494 /* LiveUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveUser.swift; sourceTree = "<group>"; };
E97AF46623FD650800635494 /* AddPlaylistSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPlaylistSheet.swift; sourceTree = "<group>"; };
E97AF46823FD9E1B00635494 /* PlaylistInputList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistInputList.swift; sourceTree = "<group>"; };
E97AF46823FD9E1B00635494 /* SpotInputList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotInputList.swift; sourceTree = "<group>"; };
E98254BC23F9B7A90056D9D3 /* Playlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playlist.swift; sourceTree = "<group>"; };
E98254C123F9FFF90056D9D3 /* PlaylistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistView.swift; sourceTree = "<group>"; };
E98254C923FA26600056D9D3 /* PlaylistRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistRow.swift; sourceTree = "<group>"; };
@ -205,8 +207,9 @@
E98254C123F9FFF90056D9D3 /* PlaylistView.swift */,
E98254C923FA26600056D9D3 /* PlaylistRow.swift */,
E97AF46623FD650800635494 /* AddPlaylistSheet.swift */,
E97AF46823FD9E1B00635494 /* PlaylistInputList.swift */,
E97AF46823FD9E1B00635494 /* SpotInputList.swift */,
E9CCD5BA2454C57300B5CD6C /* PlaylistList.swift */,
A13C54962928FD7C0034F233 /* ManagedInputList.swift */,
);
path = Playlist;
sourceTree = "<group>";
@ -459,6 +462,7 @@
A1AF727128A850AE00D317C9 /* UsersList.swift in Sources */,
E9EA690F23F9A5430012C3E8 /* AppSkeleton.swift in Sources */,
E98254BD23F9B7A90056D9D3 /* Playlist.swift in Sources */,
A13C54972928FD7C0034F233 /* ManagedInputList.swift in Sources */,
E97AF46723FD650800635494 /* AddPlaylistSheet.swift in Sources */,
E971F8B9245462D500B543B6 /* Router.swift in Sources */,
E98254C223F9FFF90056D9D3 /* PlaylistView.swift in Sources */,
@ -466,7 +470,7 @@
E97AF45623FC4E7800635494 /* User.swift in Sources */,
E98254D023FB00B60056D9D3 /* LoginScreen.swift in Sources */,
E9E30C2623FEA4F000574EEF /* TagApi.swift in Sources */,
E97AF46923FD9E1B00635494 /* PlaylistInputList.swift in Sources */,
E97AF46923FD9E1B00635494 /* SpotInputList.swift in Sources */,
E97AF45B23FC748D00635494 /* UserApi.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -45,9 +45,7 @@ class LiveUser: ObservableObject {
func lastfm_connected() -> Bool {
if let username = user?.lastfm_username {
if username.count > 0 {
return true
}
return username.count > 0
}
return false

View File

@ -26,6 +26,11 @@ public enum PlaylistApi {
case newPlaylist(name: String, type: PlaylistType)
case getPlaylist(name: String)
case refreshStats(name: String)
case addPart(name: String, subject: String)
case removePart(name: String, subject: String)
case addRef(name: String, subject: String)
case removeRef(name: String, subject: String)
}
extension PlaylistApi: ApiRequest {
@ -49,6 +54,8 @@ extension PlaylistApi: ApiRequest {
return "api/playlist"
case .refreshStats:
return "api/spotfm/playlist/refresh"
case .addPart, .removePart, .addRef, .removeRef:
return "api/playlist"
}
}
@ -68,6 +75,8 @@ extension PlaylistApi: ApiRequest {
return .get
case .refreshStats:
return .get
case .addPart, .removePart, .addRef, .removeRef:
return .post
}
}
@ -89,6 +98,14 @@ extension PlaylistApi: ApiRequest {
return JSON(["name": name])
case .refreshStats(let name):
return JSON(["name": name])
case .addPart(let name, let subject):
return JSON(["name": name, "subject": subject])
case .removePart(let name, let subject):
return JSON(["name": name, "subject": subject])
case .addRef(let name, let subject):
return JSON(["name": name, "subject": subject])
case .removeRef(let name, let subject):
return JSON(["name": name, "subject": subject])
}
}
@ -108,6 +125,8 @@ extension PlaylistApi: ApiRequest {
return URLEncodedFormParameterEncoder()
case .refreshStats:
return URLEncodedFormParameterEncoder()
case .addPart, .removePart, .addRef, .removeRef:
return JSONParameterEncoder.default
}
}

View File

@ -0,0 +1,53 @@
//
// ManagedInputList.swift
// Mixonomer
//
// Created by Andy Pack on 19/11/2022.
// Copyright © 2022 Sarsoo. All rights reserved.
//
import SwiftUI
struct ManagedInputList: View {
@EnvironmentObject var liveUser: LiveUser
@Binding var playlist: Playlist
@Binding var names: [String]
@State var addName: String = ""
@State private var addAlertShowing = false
init(names: Binding<[String]>, playlist: Binding<Playlist>){
self._names = names
self._playlist = playlist
}
var body: some View {
List{
Section(header: Image(systemName: "music.note")){ // Weird? added empty header as list renders with space for header then jumps up, not nice
if self.names.count > 0 {
ForEach(self.names, id: \.self){ name in
Text(name)
}
}else {
HStack {
Text("No Playlists")
.multilineTextAlignment(.center)
Spacer()
}
}
}
}
// .id(UUID())
.navigationBarTitle("Managed Playlists")
}
}
struct ManagedInputList_Previews: PreviewProvider {
static var previews: some View {
ManagedInputList(names: .constant([
"name"
]), playlist: .constant(Playlist(name: "Name")))
}
}

View File

@ -1,68 +0,0 @@
//
// PlaylistInputList.swift
// Mixonomer
//
// Created by Andy Pack on 19/02/2020.
// Copyright © 2020 Sarsoo. All rights reserved.
//
import SwiftUI
struct Name: Identifiable, Hashable {
var id = UUID()
var name: String
}
enum PlaylistInputType {
case MixonomerPlaylists
case SpotifyPlaylists
}
struct PlaylistInputList: View {
@Binding var names: [String]
var nameType: String
var type: PlaylistInputType
init(names: Binding<[String]>, nameType: String, type: PlaylistInputType){
self.nameType = nameType
self._names = names
self.type = type
}
var body: some View {
List{
Section(header: Image(systemName: "music.note")){ // Weird? added empty header as list renders with space for header then jumps up, not nice
if self.names.count > 0 {
ForEach(self.names, id: \.self){ name in
Text(name)
}
}else {
HStack {
Text("No Playlists")
.multilineTextAlignment(.center)
Spacer()
}
}
}
}
// .id(UUID())
.navigationBarTitle(nameType)
// .navigationBarItems(trailing:
// Button(
// action: {
//
// },
// label: { Image(systemName: "plus.circle") }
// )
// )
}
}
struct PlaylistInputList_Previews: PreviewProvider {
static var previews: some View {
PlaylistInputList(names: .constant([
"name"
]), nameType: "Spotify Playlists", type: .MixonomerPlaylists)
}
}

View File

@ -187,7 +187,7 @@ struct PlaylistView: View {
}
}
Section(header: Text("Inputs")){
NavigationLink(destination: PlaylistInputList(names: self.$playlist.playlist_references, nameType: "Managed Playlists", type: .MixonomerPlaylists)) {
NavigationLink(destination: ManagedInputList(names: self.$playlist.playlist_references, playlist: self.$playlist)) {
HStack {
Text("Managed Playlists")
Spacer()
@ -196,7 +196,7 @@ struct PlaylistView: View {
}
}
NavigationLink(destination: PlaylistInputList(names: self.$playlist.parts, nameType: "Spotify Playlists", type: .SpotifyPlaylists)) {
NavigationLink(destination: SpotInputList(names: self.$playlist.parts, playlist: self.$playlist)) {
HStack {
Text("Spotify Playlists")
Spacer()

View File

@ -0,0 +1,114 @@
//
// PlaylistInputList.swift
// Mixonomer
//
// Created by Andy Pack on 19/02/2020.
// Copyright © 2020 Sarsoo. All rights reserved.
//
import SwiftUI
import ToastUI
struct Name: Identifiable, Hashable {
var id = UUID()
var name: String
}
struct SpotInputList: View {
@EnvironmentObject var liveUser: LiveUser
@Binding var playlist: Playlist
@Binding var names: [String]
@State var addName: String = ""
@State private var addAlertShowing = false
@State private var showingToast = false
@State private var toastText = ""
@State private var toastSuccess = true
init(names: Binding<[String]>, playlist: Binding<Playlist>){
self._names = names
self._playlist = playlist
}
var body: some View {
List{
Section(header: Image(systemName: "music.note")){ // Weird? added empty header as list renders with space for header then jumps up, not nice
if self.names.count > 0 {
ForEach(self.names, id: \.self){ name in
Text(name)
}
}else {
HStack {
Text("No Playlists")
.multilineTextAlignment(.center)
Spacer()
}
}
}
}
// .id(UUID())
.navigationBarTitle("Spotify Playlists")
.navigationBarItems(trailing:
Button(
action: {
addAlertShowing = true
},
label: { Image(systemName: "plus.circle") }
)
.alert("Add", isPresented: $addAlertShowing, actions: {
TextField("Playlist Name", text: $addName)
Button("Add") {
let api = PlaylistApi.addPart(name: playlist.name, subject: addName)
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.check_network_response(response: response) {
playlist.parts.append(addName)
toastText = "Playlist Added"
toastSuccess = true
showingToast = true
}
else {
print("Failed to add playlist: \(response.response?.statusCode ?? 0)")
toastText = "Failed to add playlist"
toastSuccess = false
showingToast = true
}
}
}
Button("Cancel", role: .cancel) {
addAlertShowing = false
}
}, message: {
Text("Enter a playlist name")
})
)
.toast(isPresented: $showingToast, dismissAfter: 1.0){
if toastSuccess {
ToastView(toastText)
.toastViewStyle(.success)
}
else {
ToastView(toastText)
.toastViewStyle(.failure)
}
}
.toastDimmedBackground(false)
}
}
struct PlaylistInputList_Previews: PreviewProvider {
static var previews: some View {
SpotInputList(names: .constant([
"name"
]), playlist: .constant(Playlist(name: "Name")))
}
}

View File

@ -19,8 +19,8 @@ struct SettingsList: View {
let spotify_link_bind = Binding<Bool>(get: { liveUser.user?.spotify_linked ?? false},
set: { newVal in liveUser.user?.spotify_linked = newVal })
let lastfm_bind = Binding<String>(get: { liveUser.user?.lastfm_username ?? ""},
set: { newVal in liveUser.user?.lastfm_username = newVal })
// let lastfm_bind = Binding<String>(get: { liveUser.user?.lastfm_username ?? ""},
// set: { newVal in liveUser.user?.lastfm_username = newVal })
return NavigationView {
List{
@ -46,16 +46,22 @@ struct SettingsList: View {
}
}
Section(header: Text("Spotify")) {
Section(header: Text("Integrations")) {
Toggle(isOn: spotify_link_bind) {
Text("Account Link")
Text("Spotify Link")
}
.disabled(true)
// NavigationLink("Last.fm Username") {
// List{
// TextField("Username", text: lastfm_bind)
// }
// }
}
Section(header: Text("Last.fm")) {
TextField("Last.fm Username", text: lastfm_bind)
}
// Section(header: Text("Last.fm")) {
// TextField("Last.fm Username", text: lastfm_bind)
// }
Section {
Button(action: {
@ -65,28 +71,23 @@ struct SettingsList: View {
.foregroundColor(.red)
}
.alert("Delete Account", isPresented: $deleteAlertShowing, actions: {
Text("This will irreversibly delete all of your data, are you sure?")
Button("Delete", role: .destructive) {
let api = UserApi.deleteUser
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
if self.liveUser.check_network_response(response: response) {
self.liveUser.logout()
}
else {
}
}
}
Button("Cancel", role: .cancel) {
deleteAlertShowing = false
}
}, message: {
Text("This is irreversible, are you sure you want to delete your account?")
})
}
Section(

View File

@ -1,8 +1,6 @@
# Mixonomer iOS
iOS client for [Mixonomer](https://mixonomer.sarsoo.xyz) ([Source](https://github.com/sarsoo/Mixonomer)), using it to learn swift.
Using SwiftUI for the main app UI. Hanging SwiftUI on a hosting controller for log in/register.
iOS client for [Mixonomer](https://mixonomer.sarsoo.xyz) ([Source](https://github.com/sarsoo/Mixonomer)), using it to learn swift and SwiftUI.
Using Alamofire and SwiftyJSON for network requests.

BIN
res/Splashscreen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB