2020-02-19 23:00:23 +00:00
|
|
|
//
|
|
|
|
// LiveUser.swift
|
2022-08-07 13:31:15 +01:00
|
|
|
// Mixonomer
|
2020-02-19 23:00:23 +00:00
|
|
|
//
|
|
|
|
// Created by Andy Pack on 19/02/2020.
|
|
|
|
// Copyright © 2020 Sarsoo. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
2020-03-06 23:36:51 +00:00
|
|
|
import Alamofire
|
|
|
|
import SwiftyJSON
|
2022-08-11 20:53:09 +01:00
|
|
|
import KeychainAccess
|
2022-11-23 23:39:58 +00:00
|
|
|
import OSLog
|
2022-12-09 08:51:42 +00:00
|
|
|
import UserNotifications
|
2020-02-19 23:00:23 +00:00
|
|
|
|
|
|
|
class LiveUser: ObservableObject {
|
2020-02-22 13:28:15 +00:00
|
|
|
|
|
|
|
@Published var playlists: [Playlist]
|
|
|
|
@Published var tags: [Tag]
|
2020-03-03 00:04:20 +00:00
|
|
|
@Published var username: String
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var user: User
|
2020-02-19 23:00:23 +00:00
|
|
|
|
2020-04-25 15:44:54 +01:00
|
|
|
@Published var loggedIn: Bool {
|
|
|
|
didSet {
|
|
|
|
UserDefaults.standard.set(loggedIn, forKey: "loggedIn")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-09 08:51:42 +00:00
|
|
|
func requestAPNSPerms(){
|
|
|
|
let center = UNUserNotificationCenter.current()
|
|
|
|
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
|
|
|
|
|
|
|
|
if error != nil {
|
|
|
|
self.handleAPNSFailure()
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// load token from static var and pass to backend server
|
|
|
|
APNSHandler.pass_token_to_backend(onFailure: {
|
|
|
|
self.handleAPNSFailure()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleAPNSFailure(){
|
|
|
|
Logger.sys.debug("failed to get APNS token")
|
|
|
|
}
|
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
@Published var isFullRefreshingUser = true
|
|
|
|
@Published var isFullRefreshingPlaylists = true
|
|
|
|
@Published var isFullRefreshingTags = true
|
2022-12-10 11:52:03 +00:00
|
|
|
|
2022-08-11 20:15:21 +01:00
|
|
|
@Published var isRefreshingUser = false
|
2020-03-07 18:51:52 +00:00
|
|
|
@Published var isRefreshingPlaylists = false
|
|
|
|
@Published var isRefreshingTags = false
|
|
|
|
|
2020-04-25 15:44:54 +01:00
|
|
|
init(playlists: [Playlist], tags: [Tag], username: String, loggedIn: Bool) {
|
2020-02-19 23:00:23 +00:00
|
|
|
self.playlists = playlists
|
2020-02-20 18:25:28 +00:00
|
|
|
self.tags = tags
|
2020-03-03 00:04:20 +00:00
|
|
|
self.username = username
|
2020-04-25 15:44:54 +01:00
|
|
|
self.loggedIn = loggedIn
|
2022-12-09 08:51:42 +00:00
|
|
|
self.user = User.get_null_user()
|
2020-02-19 23:00:23 +00:00
|
|
|
}
|
2020-02-22 13:28:15 +00:00
|
|
|
|
2022-08-11 20:53:09 +01:00
|
|
|
init(playlists: [Playlist], tags: [Tag], username: String, loggedIn: Bool, user: User) {
|
|
|
|
self.playlists = playlists
|
|
|
|
self.tags = tags
|
|
|
|
self.username = username
|
|
|
|
self.loggedIn = loggedIn
|
|
|
|
self.user = user
|
|
|
|
}
|
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
var lastfm_connected: Bool {
|
|
|
|
get {
|
|
|
|
return user.lastfm_username.count > 0
|
|
|
|
}
|
2022-08-13 22:07:59 +01:00
|
|
|
}
|
|
|
|
|
2022-08-11 20:53:09 +01:00
|
|
|
func logout() {
|
|
|
|
let keychain = Keychain(service: "xyz.sarsoo.music.login")
|
|
|
|
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.sys.info("logging user out")
|
|
|
|
|
2022-08-11 20:53:09 +01:00
|
|
|
do {
|
|
|
|
try keychain.remove("username")
|
|
|
|
try keychain.remove("jwt")
|
|
|
|
|
2022-08-11 23:13:51 +01:00
|
|
|
playlists.removeAll()
|
|
|
|
tags.removeAll()
|
|
|
|
username = ""
|
2022-12-09 08:51:42 +00:00
|
|
|
user = User.get_null_user()
|
2022-08-11 23:13:51 +01:00
|
|
|
|
2022-08-11 20:53:09 +01:00
|
|
|
UserDefaults.standard.removeObject(forKey: "playlists")
|
|
|
|
UserDefaults.standard.removeObject(forKey: "tags")
|
|
|
|
|
|
|
|
self.loggedIn = false
|
|
|
|
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.sys.debug("successfully logged user out")
|
|
|
|
|
2022-08-11 20:53:09 +01:00
|
|
|
} catch let error {
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.sys.error("could not clear keychain, \(error)")
|
2022-08-11 20:53:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-13 22:07:59 +01:00
|
|
|
func update_playlist(playlistIn: Playlist) {
|
2020-02-22 13:28:15 +00:00
|
|
|
guard let index = self.playlists.firstIndex(of: playlistIn) else {
|
|
|
|
fatalError("\(playlistIn) not found")
|
|
|
|
}
|
|
|
|
self.playlists[index] = playlistIn
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2022-08-13 22:07:59 +01:00
|
|
|
func refresh_user(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) {
|
2022-11-23 23:39:58 +00:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
if !isRefreshingUser {
|
|
|
|
self.isRefreshingUser = true
|
2022-08-11 20:15:21 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
Logger.sys.info("refreshing user")
|
|
|
|
|
|
|
|
let api = UserApi.getUser
|
|
|
|
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
2022-08-11 20:15:21 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
if self.check_network_response(response: response) {
|
|
|
|
|
|
|
|
guard let data = response.data else {
|
|
|
|
Logger.net.error("error getting user")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let json = try? JSON(data: data) else {
|
|
|
|
Logger.parse.error("error parsing user")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// update state
|
|
|
|
self.user = UserApi.fromJSON(user: json)
|
|
|
|
|
|
|
|
self.isRefreshingUser = false
|
|
|
|
self.isFullRefreshingUser = false
|
|
|
|
|
|
|
|
if let success = onSuccess {
|
|
|
|
Logger.sys.debug("successfully refreshed user")
|
|
|
|
success()
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if let failure = onFailure {
|
|
|
|
Logger.net.error("failed to refresh user")
|
|
|
|
failure()
|
|
|
|
}
|
2022-08-11 20:15:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-08 22:22:57 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger.sys.info("skipping refreshing user, already refreshing")
|
|
|
|
}
|
2022-08-11 20:15:21 +01:00
|
|
|
}
|
|
|
|
|
2022-08-13 22:07:59 +01:00
|
|
|
func refresh_playlists(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) {
|
2020-03-07 18:51:52 +00:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
if !isRefreshingPlaylists {
|
|
|
|
self.isRefreshingPlaylists = true
|
2022-08-08 21:59:28 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
Logger.sys.info("refreshing playlists")
|
|
|
|
|
|
|
|
let api = PlaylistApi.getPlaylists
|
|
|
|
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
2022-08-11 20:15:21 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
if self.check_network_response(response: response) {
|
2022-08-08 21:59:28 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
guard let data = response.data else {
|
|
|
|
Logger.net.error("error getting playlists from net request")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let json = try? JSON(data: data) else {
|
|
|
|
Logger.parse.error("error parsing playlists reponse")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let playlists = json["playlists"].arrayValue
|
|
|
|
|
|
|
|
// update state
|
|
|
|
self.playlists = PlaylistApi.fromJSON(playlist: playlists).sorted(by: { $0.name.lowercased() < $1.name.lowercased() })
|
|
|
|
|
|
|
|
self.isRefreshingPlaylists = false
|
|
|
|
self.isFullRefreshingPlaylists = false
|
|
|
|
|
|
|
|
if let success = onSuccess {
|
|
|
|
Logger.sys.debug("successfully refreshed playlists")
|
|
|
|
success()
|
|
|
|
}
|
|
|
|
|
|
|
|
let encoder = JSONEncoder()
|
|
|
|
do {
|
|
|
|
UserDefaults.standard.set(String(data: try encoder.encode(playlists), encoding: .utf8), forKey: "playlists")
|
|
|
|
} catch {
|
|
|
|
Logger.parse.error("error encoding playlists: \(error)")
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if let failure = onFailure {
|
|
|
|
Logger.net.error("failed to refresh playlists")
|
|
|
|
failure()
|
|
|
|
}
|
2022-08-11 20:15:21 +01:00
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-08 22:22:57 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger.sys.info("skipping refreshing playlists, already refreshing")
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
|
2022-08-13 22:07:59 +01:00
|
|
|
func refresh_tags(onSuccess: (() -> Void)? = nil, onFailure: (() -> Void)? = nil) {
|
2020-03-07 18:51:52 +00:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
if !isRefreshingTags {
|
|
|
|
self.isRefreshingTags = true
|
|
|
|
|
|
|
|
Logger.sys.info("refreshing tags")
|
2022-08-09 17:42:01 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
let api = TagApi.getTags
|
|
|
|
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
2022-08-11 20:15:21 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
if self.check_network_response(response: response) {
|
2022-08-09 17:42:01 +01:00
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
guard let data = response.data else {
|
|
|
|
Logger.net.error("error getting tags")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let json = try? JSON(data: data) else {
|
|
|
|
Logger.parse.error("error parsing tags response")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let tags = json["tags"].arrayValue
|
|
|
|
|
|
|
|
// update state
|
|
|
|
self.tags = TagApi.fromJSON(tag: tags).sorted(by: { $0.name.lowercased() < $1.name.lowercased() })
|
|
|
|
|
|
|
|
self.isRefreshingTags = false
|
|
|
|
self.isFullRefreshingTags = false
|
|
|
|
|
|
|
|
if let success = onSuccess {
|
|
|
|
Logger.sys.debug("successfully refreshed tags")
|
|
|
|
success()
|
|
|
|
}
|
|
|
|
|
|
|
|
let encoder = JSONEncoder()
|
|
|
|
do {
|
|
|
|
UserDefaults.standard.set(String(data: try encoder.encode(tags), encoding: .utf8), forKey: "tags")
|
|
|
|
} catch {
|
|
|
|
Logger.parse.error("error encoding tags: \(error)")
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if let failure = onFailure {
|
|
|
|
Logger.net.error("failed to refresh tags")
|
|
|
|
failure()
|
|
|
|
}
|
2022-08-11 20:15:21 +01:00
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-08 22:22:57 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger.sys.info("skipping refreshing tags, already refreshing")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var isFullRefreshing: Bool {
|
|
|
|
get {
|
|
|
|
return isFullRefreshingTags || isFullRefreshingPlaylists || isFullRefreshingUser
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 10:36:45 +00:00
|
|
|
func full_refresh() {
|
|
|
|
|
2023-01-08 22:22:57 +00:00
|
|
|
if !isFullRefreshing {
|
|
|
|
self.isFullRefreshingUser = true
|
|
|
|
self.isFullRefreshingPlaylists = true
|
|
|
|
self.isFullRefreshingTags = true
|
|
|
|
|
|
|
|
self.refresh_user()
|
|
|
|
self.refresh_playlists()
|
|
|
|
self.refresh_tags()
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger.sys.info("skipping full refresh, already refreshing")
|
|
|
|
}
|
2023-01-07 10:36:45 +00:00
|
|
|
}
|
|
|
|
|
2022-08-13 22:07:59 +01:00
|
|
|
func check_network_response(response: AFDataResponse<Any>) -> Bool {
|
2022-12-09 08:51:42 +00:00
|
|
|
return NetworkHelper.check_network_response(response: response, onTokenFail: {
|
|
|
|
self.logout()
|
|
|
|
})
|
2022-08-09 17:42:01 +01:00
|
|
|
}
|
|
|
|
|
2022-08-13 22:07:59 +01:00
|
|
|
func load_user_defaults() -> LiveUser {
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.sys.debug("loading user defaults")
|
|
|
|
|
2020-03-06 23:36:51 +00:00
|
|
|
let defaults = UserDefaults.standard
|
|
|
|
let decoder = JSONDecoder()
|
|
|
|
|
|
|
|
let _strPlaylists = defaults.string(forKey: "playlists")
|
|
|
|
let _strTags = defaults.string(forKey: "tags")
|
2020-04-25 15:44:54 +01:00
|
|
|
loggedIn = defaults.bool(forKey: "loggedIn")
|
2020-03-06 23:36:51 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
if let _strPlaylists = _strPlaylists {
|
2020-03-20 00:08:18 +00:00
|
|
|
if _strPlaylists.count > 0 {
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.sys.debug("reading \(_strPlaylists.count) playlists")
|
2020-03-20 00:08:18 +00:00
|
|
|
self.playlists = (try decoder.decode([Playlist].self, from: _strPlaylists.data(using: .utf8)!)).sorted(by: { $0.name.lowercased() < $1.name.lowercased() })
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if let _strTags = _strTags {
|
2020-03-20 00:08:18 +00:00
|
|
|
if _strTags.count > 0 {
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.sys.debug("reading \(_strTags.count) tags")
|
2020-03-20 00:08:18 +00:00
|
|
|
self.tags = (try decoder.decode([Tag].self, from: _strTags.data(using: .utf8)!)).sorted(by: { $0.name.lowercased() < $1.name.lowercased() })
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
} catch {
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.parse.error("error parsing user defaults: \(error)")
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return self
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
|
|
|
|
static func get_preview_user() -> LiveUser {
|
|
|
|
return LiveUser(playlists: [], tags: [], username: "user", loggedIn: false)
|
|
|
|
}
|
|
|
|
|
|
|
|
static func get_preview_user_with_user() -> LiveUser {
|
|
|
|
return LiveUser(playlists: [], tags: [], username: "user", loggedIn: false, user: User())
|
|
|
|
}
|
2020-02-19 23:00:23 +00:00
|
|
|
}
|