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.
196 lines
9.5 KiB
Swift
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)
|
|
}
|
|
}
|