jellyflood/JellyfinPlayer/ConnectToServerView.swift
Aiden Vigue 961e5ff392 Fix background threading issues
Removes duplicate GlobalData structs
Fixes authHeader being passed between thread handlers
Moves async handlers in PlayerView to be user initiated
Fixes duplicate navigationviews to limit mem usage.
2021-05-22 18:58:14 -04:00

196 lines
9.5 KiB
Swift

//
// ConnectToServerView.swift
// JellyfinPlayer
//
// Created by Aiden Vigue on 4/29/21.
//
import SwiftUI
import HidingViews
import SwiftyRequest
import SwiftyJSON
import CoreData
import KeychainSwift
import Introspect
import Sentry
struct ConnectToServerView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var globalData: GlobalData
@EnvironmentObject var jsi: justSignedIn
@State private var uri = "";
@State private var isWorking = false;
@State private var isErrored = false;
@State private var isDone = false;
@State private var isSignInErrored = false;
@State private var isConnected = false;
@State private var serverName = "";
@Binding var rootIsActive : Bool
let userUUID = UUID();
@State private var username = "";
@State private var password = "";
@State private var server_id = "";
@State private var serverSkipped: Bool = false;
@State private var serverSkippedAlert: Bool = false;
private var reauthDeviceID: String = "";
private var skip_server_bool: Bool = false;
private var skip_server_obj: Server?
init(skip_server: Bool, skip_server_prefill: Server?, reauth_deviceId: String, isActive: Binding<Bool>) {
skip_server_bool = skip_server
skip_server_obj = skip_server_prefill
reauthDeviceID = reauth_deviceId
_rootIsActive = isActive
}
init(isActive: Binding<Bool>) {
_rootIsActive = isActive
}
func start() {
if(skip_server_bool) {
_serverSkipped.wrappedValue = true;
_serverSkippedAlert.wrappedValue = true;
_server_id.wrappedValue = skip_server_obj?.server_id ?? ""
_serverName.wrappedValue = skip_server_obj?.name ?? ""
_uri.wrappedValue = skip_server_obj?.baseURI ?? ""
_isConnected.wrappedValue = true;
}
}
var body: some View {
Form {
if(!isConnected) {
Section(header: Text("Server Information")) {
TextField("Server URL", text: $uri)
.disableAutocorrection(true)
.autocapitalization(.none)
Button {
_isWorking.wrappedValue = true;
if(!_uri.wrappedValue.contains("http")) {
_uri.wrappedValue = "http://" + _uri.wrappedValue;
}
if(_uri.wrappedValue.last == "/") {
_uri.wrappedValue = String(_uri.wrappedValue.dropLast())
}
let request = RestRequest(method: .get, url: uri + "/System/Info/Public")
request.responseObject() { (result: Result<RestResponse<ServerPublicInfoResponse>, RestError>) in
switch result {
case .success(let response):
let server = response.body
_serverName.wrappedValue = server.ServerName
_server_id.wrappedValue = server.Id
if(server.StartupWizardCompleted) {
_isConnected.wrappedValue = true;
}
case .failure(_):
_isErrored.wrappedValue = true;
}
_isWorking.wrappedValue = false;
}
} label: {
HStack {
Text("Connect")
Spacer()
ProgressView().isHidden(!isWorking)
}
}.disabled(isWorking || uri.isEmpty)
}.alert(isPresented: $isErrored) {
Alert(title: Text("Error"), message: Text("Couldn't connect to server"), dismissButton: .default(Text("Try again")))
}
} else {
Section(header: Text("\(serverSkipped ? "re" : "")Authenticate to \"\(serverName)\"")) {
TextField("Username", text: $username)
.disableAutocorrection(true)
.autocapitalization(.none)
SecureField("Password (optional)", text: $password)
.disableAutocorrection(true)
.autocapitalization(.none)
Button {
_isWorking.wrappedValue = true
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String;
let authHeader = "MediaBrowser Client=\"SwiftFin\", Device=\"\(UIDevice.current.name)\", DeviceId=\"\(serverSkipped ? reauthDeviceID : userUUID.uuidString)\", Version=\"\(appVersion ?? "0.0.1")\"";
let authJson: [String: Any] = ["Username": _username.wrappedValue, "Pw": _password.wrappedValue]
let request = RestRequest(method: .post, url: uri + "/Users/authenticatebyname")
request.headerParameters["X-Emby-Authorization"] = authHeader
request.contentType = "application/json"
request.acceptType = "application/json"
request.messageBodyDictionary = authJson
request.responseData() { (result: Result<RestResponse<Data>, RestError>) in
switch result {
case .success(let response):
do {
let json = try JSON(data: response.body)
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try viewContext.execute(deleteRequest)
} catch _ as NSError {
// TODO: handle the error
}
let fetchRequest2: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "SignedInUser")
let deleteRequest2 = NSBatchDeleteRequest(fetchRequest: fetchRequest2)
do {
try viewContext.execute(deleteRequest2)
} catch _ as NSError {
// TODO: handle the error
}
let newServer = Server(context: viewContext)
newServer.baseURI = _uri.wrappedValue
newServer.name = _serverName.wrappedValue
newServer.server_id = _server_id.wrappedValue
let newUser = SignedInUser(context: viewContext)
newUser.device_uuid = userUUID.uuidString
newUser.username = _username.wrappedValue
newUser.user_id = json["User"]["Id"].string ?? ""
let keychain = KeychainSwift()
keychain.set(json["AccessToken"].string ?? "", forKey: "AccessToken_\(json["User"]["Id"].string ?? "")")
do {
try viewContext.save()
DispatchQueue.main.async { [self] in
globalData.authHeader = authHeader
_rootIsActive.wrappedValue = false
jsi.did = true
}
} catch {
SentrySDK.capture(error: error)
}
} catch {
}
case .failure(let error):
SentrySDK.capture(error: error)
_isSignInErrored.wrappedValue = true;
}
_isWorking.wrappedValue = false;
}
} label: {
HStack {
Text("Login")
Spacer()
ProgressView().isHidden(!isWorking)
}
}.disabled(isWorking || username.isEmpty)
.alert(isPresented: $isSignInErrored) {
Alert(title: Text("Error"), message: Text("Invalid credentials"), dismissButton: .default(Text("Back")))
}
}
}
}.navigationTitle("Connect to Server")
.alert(isPresented: $serverSkippedAlert) {
Alert(title: Text("Error"), message: Text("Credentials have expired"), dismissButton: .default(Text("Sign in again")))
}
.onAppear(perform: start)
}
}