added pull to refresh, moved playlist and tag reference to environment object, added get playlist
This commit is contained in:
parent
54a3806908
commit
fea9e3f84b
@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
E92F94822401412100B6B721 /* SwiftUIRefresh in Frameworks */ = {isa = PBXBuildFile; productRef = E92F94812401412100B6B721 /* SwiftUIRefresh */; };
|
||||
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 */; };
|
||||
@ -97,6 +98,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E92F94822401412100B6B721 /* SwiftUIRefresh in Frameworks */,
|
||||
E97AF45923FC50EC00635494 /* SwiftyJSON in Frameworks */,
|
||||
E98254D923FB53780056D9D3 /* Alamofire in Frameworks */,
|
||||
E97AF45E23FC83AF00635494 /* KeychainAccess in Frameworks */,
|
||||
@ -225,14 +227,14 @@
|
||||
E9EA690923F9A5430012C3E8 /* Music Tools */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E97AF46A23FDA8ED00635494 /* Controller */,
|
||||
E98254C623FA25280056D9D3 /* Application */,
|
||||
E98254C023F9FFDD0056D9D3 /* Views */,
|
||||
E98254BF23F9BE040056D9D3 /* Network */,
|
||||
E98254BE23F9BD540056D9D3 /* Model */,
|
||||
E9EA691023F9A54A0012C3E8 /* Assets.xcassets */,
|
||||
E97AF46A23FDA8ED00635494 /* Controller */,
|
||||
E9EA691823F9A54B0012C3E8 /* Info.plist */,
|
||||
E98254BE23F9BD540056D9D3 /* Model */,
|
||||
E98254BF23F9BE040056D9D3 /* Network */,
|
||||
E9EA691223F9A54B0012C3E8 /* Preview Content */,
|
||||
E98254C023F9FFDD0056D9D3 /* Views */,
|
||||
);
|
||||
path = "Music Tools";
|
||||
sourceTree = "<group>";
|
||||
@ -283,6 +285,7 @@
|
||||
E98254D823FB53780056D9D3 /* Alamofire */,
|
||||
E97AF45823FC50EC00635494 /* SwiftyJSON */,
|
||||
E97AF45D23FC83AF00635494 /* KeychainAccess */,
|
||||
E92F94812401412100B6B721 /* SwiftUIRefresh */,
|
||||
);
|
||||
productName = "Music Tools";
|
||||
productReference = E9EA690723F9A5430012C3E8 /* Music Tools.app */;
|
||||
@ -360,6 +363,7 @@
|
||||
E98254D723FB53770056D9D3 /* XCRemoteSwiftPackageReference "alamofire" */,
|
||||
E97AF45723FC50EC00635494 /* XCRemoteSwiftPackageReference "swiftyjson" */,
|
||||
E97AF45C23FC83AF00635494 /* XCRemoteSwiftPackageReference "keychainaccess" */,
|
||||
E92F94802401412100B6B721 /* XCRemoteSwiftPackageReference "swiftuirefresh" */,
|
||||
);
|
||||
productRefGroup = E9EA690823F9A5430012C3E8 /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -752,6 +756,14 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
E92F94802401412100B6B721 /* XCRemoteSwiftPackageReference "swiftuirefresh" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/siteline/swiftuirefresh";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.0.2;
|
||||
};
|
||||
};
|
||||
E97AF45723FC50EC00635494 /* XCRemoteSwiftPackageReference "swiftyjson" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/swiftyjson/swiftyjson";
|
||||
@ -779,6 +791,11 @@
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
E92F94812401412100B6B721 /* SwiftUIRefresh */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E92F94802401412100B6B721 /* XCRemoteSwiftPackageReference "swiftuirefresh" */;
|
||||
productName = SwiftUIRefresh;
|
||||
};
|
||||
E97AF45823FC50EC00635494 /* SwiftyJSON */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E97AF45723FC50EC00635494 /* XCRemoteSwiftPackageReference "swiftyjson" */;
|
||||
|
@ -19,6 +19,24 @@
|
||||
"version": "4.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Introspect",
|
||||
"repositoryURL": "https://github.com/siteline/SwiftUI-Introspect.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "de5c32c15ae169cfcb27397ffb2734dcd0e1e6d5",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftUIRefresh",
|
||||
"repositoryURL": "https://github.com/siteline/swiftuirefresh",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "6939ec13efa866eb9f556f5de7f0e06f5d7f2761",
|
||||
"version": "0.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftyJSON",
|
||||
"repositoryURL": "https://github.com/swiftyjson/swiftyjson",
|
||||
|
@ -33,6 +33,9 @@
|
||||
<rect key="frame" x="196" y="633" width="120" height="53"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
|
||||
<state key="normal" title="Register"/>
|
||||
<connections>
|
||||
<segue destination="TND-IP-OdM" kind="show" id="96d-mV-baq"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
@ -57,11 +60,12 @@
|
||||
<objects>
|
||||
<viewController id="TND-IP-OdM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="MhD-yZ-pD8">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="VLy-9d-bfF"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="ols-n0-cLe"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Dgg-BJ-VHU" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
@ -102,7 +106,7 @@
|
||||
<constraint firstAttribute="height" constant="34" id="ynd-9a-coG"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="go" textContentType="password"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="go" secureTextEntry="YES" textContentType="password"/>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6YH-OC-12B">
|
||||
<rect key="frame" x="185.5" y="712" width="43" height="53"/>
|
||||
|
@ -9,11 +9,19 @@
|
||||
import Foundation
|
||||
|
||||
class LiveUser: ObservableObject {
|
||||
var playlists: [Playlist]
|
||||
var tags: [Tag]
|
||||
|
||||
@Published var playlists: [Playlist]
|
||||
@Published var tags: [Tag]
|
||||
|
||||
init(playlists: [Playlist], tags: [Tag]) {
|
||||
self.playlists = playlists
|
||||
self.tags = tags
|
||||
}
|
||||
|
||||
func updatePlaylist(playlistIn: Playlist) {
|
||||
guard let index = self.playlists.firstIndex(of: playlistIn) else {
|
||||
fatalError("\(playlistIn) not found")
|
||||
}
|
||||
self.playlists[index] = playlistIn
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ public enum PlaylistApi {
|
||||
case updatePlaylist(name: String, updates: JSON)
|
||||
case deletePlaylist(name: String)
|
||||
case newPlaylist(name: String, type: PlaylistType)
|
||||
case getPlaylist(name: String)
|
||||
}
|
||||
|
||||
extension PlaylistApi: ApiRequest {
|
||||
@ -43,6 +44,8 @@ extension PlaylistApi: ApiRequest {
|
||||
return "api/playlist"
|
||||
case .newPlaylist:
|
||||
return "api/playlist"
|
||||
case .getPlaylist:
|
||||
return "api/playlist"
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +61,8 @@ extension PlaylistApi: ApiRequest {
|
||||
return .delete
|
||||
case .newPlaylist:
|
||||
return .put
|
||||
case .getPlaylist:
|
||||
return .get
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +80,8 @@ extension PlaylistApi: ApiRequest {
|
||||
return JSON(["name": name])
|
||||
case .newPlaylist(let name, let type):
|
||||
return JSON(["name": name, "type": txTypeHeaders[type.rawValue]])
|
||||
case .getPlaylist(let name):
|
||||
return JSON(["name": name])
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,6 +97,8 @@ extension PlaylistApi: ApiRequest {
|
||||
return URLEncodedFormParameterEncoder()
|
||||
case .newPlaylist:
|
||||
return JSONParameterEncoder.default
|
||||
case .getPlaylist:
|
||||
return URLEncodedFormParameterEncoder()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,28 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
//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
|
||||
|
||||
@ -25,6 +43,8 @@ struct PlaylistView: View {
|
||||
|
||||
@State private var rec_num: Int = 0
|
||||
|
||||
@State private var isRefreshing = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(header: Text("Options")){
|
||||
@ -87,6 +107,9 @@ struct PlaylistView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
// .pullToRefresh(isShowing: $isRefreshing) {
|
||||
// self.refreshPlaylist()
|
||||
// }
|
||||
.navigationBarTitle(Text(playlist.name))
|
||||
.onAppear {
|
||||
self.$recommendations.wrappedValue = self.playlist.include_recommendations
|
||||
@ -114,7 +137,31 @@ struct PlaylistView: View {
|
||||
func updatePlaylist(updates: JSON) {
|
||||
let api = PlaylistApi.updatePlaylist(name: playlist.name, updates: updates)
|
||||
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
||||
switch response.result {
|
||||
case .success:
|
||||
debugPrint("success")
|
||||
case .failure:
|
||||
debugPrint("error")
|
||||
}
|
||||
}
|
||||
//TODO: do better error checking
|
||||
}
|
||||
|
||||
func refreshPlaylist(updates: JSON) {
|
||||
let api = PlaylistApi.getPlaylist(name: self.playlist.name)
|
||||
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
||||
guard let data = response.data else {
|
||||
fatalError("error getting playlist")
|
||||
}
|
||||
|
||||
guard let json = try? JSON(data: data) else {
|
||||
fatalError("error parsing reponse")
|
||||
}
|
||||
|
||||
// let playlist = Playlist.fromDict(json["playlist"])
|
||||
//
|
||||
// self.playlist = playlist
|
||||
// self.isRefreshing = false
|
||||
}
|
||||
//TODO: do better error checking
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SwiftUIRefresh
|
||||
import Alamofire
|
||||
import SwiftyJSON
|
||||
|
||||
@ -15,16 +16,17 @@ struct RootView: View {
|
||||
@EnvironmentObject var liveUser: LiveUser
|
||||
|
||||
@State private var selection = 0 // Tab view selection
|
||||
@State private var playlists: Array<Playlist> = [] // Network pulled playlists
|
||||
@State private var tags: Array<Tag> = [] // Network pulled tags
|
||||
|
||||
@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()
|
||||
// let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
@ -32,24 +34,27 @@ struct RootView: View {
|
||||
// PLAYLISTS
|
||||
NavigationView {
|
||||
List{
|
||||
ForEach(playlists) { playlist in
|
||||
ForEach(liveUser.playlists) { playlist in
|
||||
PlaylistRow(playlist: playlist)
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
|
||||
indexSet.forEach { index in
|
||||
// add to recently deleted playlist cache
|
||||
self.justDeletedPlaylists.append(self.playlists[index])
|
||||
self.justDeletedPlaylists.append(self.liveUser.playlists[index])
|
||||
|
||||
let api = PlaylistApi.deletePlaylist(name: self.playlists[index].name)
|
||||
let api = PlaylistApi.deletePlaylist(name: self.liveUser.playlists[index].name)
|
||||
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
self.playlists.remove(atOffsets: indexSet)
|
||||
self.liveUser.playlists.remove(atOffsets: indexSet)
|
||||
}
|
||||
}
|
||||
.pullToRefresh(isShowing: $isRefreshingPlaylists) {
|
||||
self.refreshPlaylists()
|
||||
}
|
||||
.navigationBarTitle(Text("Playlists").font(.title))
|
||||
|
||||
// add playlist button
|
||||
@ -58,7 +63,7 @@ struct RootView: View {
|
||||
action: { self.showAdd = true },
|
||||
label: { Text("Add") }
|
||||
).sheet(isPresented: $showAdd) {
|
||||
AddPlaylistSheet(state: self.$showAdd, playlists: self.$playlists)
|
||||
AddPlaylistSheet(state: self.$showAdd, playlists: self.$liveUser.playlists)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -73,24 +78,27 @@ struct RootView: View {
|
||||
// TAGS
|
||||
NavigationView {
|
||||
List{
|
||||
ForEach(tags) { tag in
|
||||
ForEach(liveUser.tags) { tag in
|
||||
TagRow(tag: tag)
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
|
||||
indexSet.forEach { index in
|
||||
// add to recently deleted playlist cache
|
||||
self.justDeletedTags.append(self.tags[index])
|
||||
self.justDeletedTags.append(self.liveUser.tags[index])
|
||||
|
||||
let api = TagApi.deleteTag(tag_id: self.tags[index].tag_id)
|
||||
let api = TagApi.deleteTag(tag_id: self.liveUser.tags[index].tag_id)
|
||||
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
self.tags.remove(atOffsets: indexSet)
|
||||
self.liveUser.tags.remove(atOffsets: indexSet)
|
||||
}
|
||||
}
|
||||
.pullToRefresh(isShowing: $isRefreshingTags) {
|
||||
self.refreshTags()
|
||||
}
|
||||
.navigationBarTitle(Text("Tags").font(.title))
|
||||
|
||||
// add playlist button
|
||||
@ -99,7 +107,7 @@ struct RootView: View {
|
||||
action: { self.showAdd = true },
|
||||
label: { Text("Add") }
|
||||
).sheet(isPresented: $showAdd) {
|
||||
AddPlaylistSheet(state: self.$showAdd, playlists: self.$playlists)
|
||||
AddPlaylistSheet(state: self.$showAdd, playlists: self.$liveUser.playlists)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -122,15 +130,20 @@ struct RootView: View {
|
||||
}
|
||||
}
|
||||
.tag(2)
|
||||
.onReceive(timer) { _ in
|
||||
self.fetch()
|
||||
}
|
||||
// .onReceive(timer) { _ in
|
||||
// self.fetch()
|
||||
// }
|
||||
}.onAppear {
|
||||
self.fetch()
|
||||
self.fetchAll()
|
||||
}
|
||||
}
|
||||
|
||||
private func fetch() {
|
||||
private func fetchAll() {
|
||||
refreshPlaylists()
|
||||
refreshTags()
|
||||
}
|
||||
|
||||
func refreshPlaylists() {
|
||||
let api = PlaylistApi.getPlaylists
|
||||
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
||||
|
||||
@ -165,10 +178,12 @@ struct RootView: View {
|
||||
|
||||
// update state
|
||||
self.liveUser.playlists = playlists
|
||||
self.playlists = self.liveUser.playlists
|
||||
self.isRefreshingPlaylists = false
|
||||
}
|
||||
//TODO: do better error checking
|
||||
|
||||
}
|
||||
|
||||
func refreshTags() {
|
||||
let tagApi = TagApi.getTags
|
||||
RequestBuilder.buildRequest(apiRequest: tagApi).responseJSON{ response in
|
||||
|
||||
@ -203,7 +218,7 @@ struct RootView: View {
|
||||
|
||||
// update state
|
||||
self.liveUser.tags = tags
|
||||
self.tags = self.liveUser.tags
|
||||
self.isRefreshingTags = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user