Prevent connecting/signing in if already exists
This commit is contained in:
parent
a5a842e815
commit
3599df56e9
|
@ -242,6 +242,9 @@
|
|||
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E508172703E8190045C9AB /* LibraryListView.swift */; };
|
||||
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */; };
|
||||
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; };
|
||||
E11B1B6C2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
|
||||
E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
|
||||
E11B1B6E2718CDBA006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
|
||||
E131691726C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
|
||||
E131691826C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
|
||||
E131691926C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
|
||||
|
@ -512,6 +515,7 @@
|
|||
D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = "<group>"; };
|
||||
DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayButtonRowView.swift; sourceTree = "<group>"; };
|
||||
E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinAPIError.swift; sourceTree = "<group>"; };
|
||||
E131691626C583BC0074BFEE /* LogConstructor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogConstructor.swift; sourceTree = "<group>"; };
|
||||
E13DD3BC27163C63009D4DAF /* EmailHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailHelper.swift; sourceTree = "<group>"; };
|
||||
E13DD3BE27163DD7009D4DAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -1164,9 +1168,10 @@
|
|||
E1AD105226D96D5F003E4A08 /* JellyfinAPIExtensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5364F454266CA0DC0026ECBA /* BaseItemPersonExtensions.swift */,
|
||||
E1AD104C26D96CE3003E4A08 /* BaseItemDtoExtensions.swift */,
|
||||
E18845F426DD631E00B0C5B7 /* BaseItemDto+Stackable.swift */,
|
||||
E1AD104C26D96CE3003E4A08 /* BaseItemDtoExtensions.swift */,
|
||||
5364F454266CA0DC0026ECBA /* BaseItemPersonExtensions.swift */,
|
||||
E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */,
|
||||
E1AD105E26D9ADDD003E4A08 /* NameGUIDPairExtensions.swift */,
|
||||
);
|
||||
path = JellyfinAPIExtensions;
|
||||
|
@ -1595,6 +1600,7 @@
|
|||
535870A52669D8AE00D05A09 /* ParallaxHeader.swift in Sources */,
|
||||
53272532268BF09D0035FBF1 /* MediaViewActionButton.swift in Sources */,
|
||||
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
||||
E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */,
|
||||
535870A72669D8AE00D05A09 /* MultiSelectorView.swift in Sources */,
|
||||
E1AD104E26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */,
|
||||
E13DD4032717EE79009D4DAF /* UserListCoordinator.swift in Sources */,
|
||||
|
@ -1716,6 +1722,7 @@
|
|||
E1AD106226D9B7CD003E4A08 /* ItemPortraitHeaderOverlayView.swift in Sources */,
|
||||
53E4E649263F725B00F67C6B /* MultiSelectorView.swift in Sources */,
|
||||
6220D0C626D62D8700B8E046 /* VideoPlayerCoordinator.swift in Sources */,
|
||||
E11B1B6C2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */,
|
||||
621338B32660A07800A81A2A /* LazyView.swift in Sources */,
|
||||
6220D0B126D5EC9900B8E046 /* SettingsCoordinator.swift in Sources */,
|
||||
62C29EA626D1036A00C1D2E7 /* HomeCoordinator.swift in Sources */,
|
||||
|
@ -1766,6 +1773,7 @@
|
|||
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */,
|
||||
6267B3DB2671139400A7371D /* ImageExtensions.swift in Sources */,
|
||||
E1AD105926D9A543003E4A08 /* LazyView.swift in Sources */,
|
||||
E11B1B6E2718CDBA006DA3E8 /* JellyfinAPIError.swift in Sources */,
|
||||
628B95372670CB800091AF3B /* JellyfinWidget.swift in Sources */,
|
||||
E1AD105426D97161003E4A08 /* BaseItemDtoExtensions.swift in Sources */,
|
||||
E1FCD09A26C4F35A007C8DCF /* ErrorMessage.swift in Sources */,
|
||||
|
|
|
@ -63,8 +63,8 @@ struct ConnectToServerView: View {
|
|||
.headerProminence(.increased)
|
||||
}
|
||||
.alert(item: $viewModel.errorMessage) { _ in
|
||||
Alert(title: Text("\(viewModel.errorMessage?.code ?? -1)\n\(viewModel.errorMessage?.title ?? "Error")"),
|
||||
message: Text(viewModel.errorMessage?.displayMessage ?? "Error"),
|
||||
Alert(title: Text(viewModel.alertTitle),
|
||||
message: Text(viewModel.errorMessage?.displayMessage ?? "Unknown Error"),
|
||||
dismissButton: .cancel())
|
||||
}
|
||||
.navigationTitle("Connect")
|
||||
|
|
|
@ -45,6 +45,11 @@ struct UserSignInView: View {
|
|||
Text("Sign In to \(viewModel.server.name)")
|
||||
}
|
||||
}
|
||||
.alert(item: $viewModel.errorMessage) { _ in
|
||||
Alert(title: Text(viewModel.alertTitle),
|
||||
message: Text(viewModel.errorMessage?.displayMessage ?? "Unknown Error"),
|
||||
dismissButton: .cancel())
|
||||
}
|
||||
.navigationTitle("Sign In")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ struct ErrorMessage: Identifiable {
|
|||
let title: String
|
||||
let displayMessage: String
|
||||
let logConstructor: LogConstructor
|
||||
|
||||
// Chosen value such that if an error has this code, don't show the code to the UI
|
||||
// This was chosen because of its unlikelyhood to ever be used
|
||||
static let noShowErrorCode = -69420
|
||||
|
||||
var id: String {
|
||||
return "\(code)\(title)\(logConstructor.message)"
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
struct JellyfinAPIError: Error {
|
||||
|
||||
private let message: String
|
||||
|
||||
init(_ message: String) {
|
||||
self.message = message
|
||||
}
|
||||
|
||||
var localizedDescription: String {
|
||||
return message
|
||||
}
|
||||
}
|
|
@ -75,16 +75,30 @@ final class SessionManager {
|
|||
JellyfinAPI.basePath = uri
|
||||
|
||||
return SystemAPI.getPublicSystemInfo()
|
||||
.map({ response -> (SwiftfinStore.Models.StoredServer, UnsafeDataTransaction) in
|
||||
.tryMap({ response -> (SwiftfinStore.Models.StoredServer, UnsafeDataTransaction) in
|
||||
|
||||
let transaction = SwiftfinStore.dataStack.beginUnsafe()
|
||||
let newServer = transaction.create(Into<SwiftfinStore.Models.StoredServer>())
|
||||
newServer.uri = response.localAddress ?? "SfUri"
|
||||
newServer.name = response.serverName ?? "SfServerName"
|
||||
newServer.id = response.id ?? ""
|
||||
newServer.os = response.operatingSystem ?? "SfOS"
|
||||
newServer.version = response.version ?? "SfVersion"
|
||||
|
||||
guard let uri = response.localAddress,
|
||||
let name = response.serverName,
|
||||
let id = response.id,
|
||||
let os = response.operatingSystem,
|
||||
let version = response.version else { throw JellyfinAPIError("Missing server data from network call") }
|
||||
|
||||
newServer.uri = uri
|
||||
newServer.name = name
|
||||
newServer.id = id
|
||||
newServer.os = os
|
||||
newServer.version = version
|
||||
newServer.users = []
|
||||
|
||||
// Check for existing server on device
|
||||
if let existingServer = try? SwiftfinStore.dataStack.fetchOne(From<SwiftfinStore.Models.StoredServer>(),
|
||||
[Where<SwiftfinStore.Models.StoredServer>("id == %@", newServer.id)]) {
|
||||
throw SwiftfinStore.Errors.existingServer(existingServer.state)
|
||||
}
|
||||
|
||||
return (newServer, transaction)
|
||||
})
|
||||
.handleEvents(receiveOutput: { (_, transaction) in
|
||||
|
@ -100,17 +114,29 @@ final class SessionManager {
|
|||
func loginUser(server: SwiftfinStore.State.Server, username: String, password: String) -> AnyPublisher<SwiftfinStore.State.User, Error> {
|
||||
setAuthHeader(with: "")
|
||||
|
||||
JellyfinAPI.basePath = server.uri
|
||||
|
||||
return UserAPI.authenticateUserByName(authenticateUserByName: AuthenticateUserByName(username: username, pw: password))
|
||||
.map({ response -> (SwiftfinStore.Models.StoredServer, SwiftfinStore.Models.StoredUser, UnsafeDataTransaction) in
|
||||
.tryMap({ response -> (SwiftfinStore.Models.StoredServer, SwiftfinStore.Models.StoredUser, UnsafeDataTransaction) in
|
||||
|
||||
guard let accessToken = response.accessToken else { fatalError("Received successful user with no access token") }
|
||||
guard let accessToken = response.accessToken else { throw JellyfinAPIError("Access token missing from network call") }
|
||||
|
||||
let transaction = SwiftfinStore.dataStack.beginUnsafe()
|
||||
let newUser = transaction.create(Into<SwiftfinStore.Models.StoredUser>())
|
||||
newUser.username = response.user?.name ?? "SfUsername"
|
||||
newUser.id = response.user?.id ?? "SfID"
|
||||
|
||||
guard let username = response.user?.name,
|
||||
let id = response.user?.id else { throw JellyfinAPIError("Missing user data from network call") }
|
||||
|
||||
newUser.username = username
|
||||
newUser.id = id
|
||||
newUser.appleTVID = ""
|
||||
|
||||
// Check for existing user on device
|
||||
if let existingUser = try? SwiftfinStore.dataStack.fetchOne(From<SwiftfinStore.Models.StoredUser>(),
|
||||
[Where<SwiftfinStore.Models.StoredUser>("id == %@", newUser.id)]) {
|
||||
throw SwiftfinStore.Errors.existingUser(existingUser.state)
|
||||
}
|
||||
|
||||
let newAccessToken = transaction.create(Into<SwiftfinStore.Models.StoredAccessToken>())
|
||||
newAccessToken.value = accessToken
|
||||
newUser.accessToken = newAccessToken
|
||||
|
@ -156,6 +182,14 @@ final class SessionManager {
|
|||
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil)
|
||||
}
|
||||
|
||||
func delete(user: SwiftfinStore.State.User) {
|
||||
|
||||
}
|
||||
|
||||
func delete(server: SwiftfinStore.State.Server) {
|
||||
|
||||
}
|
||||
|
||||
private func setAuthHeader(with accessToken: String) {
|
||||
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
var deviceName = UIDevice.current.name
|
||||
|
|
|
@ -119,6 +119,13 @@ enum SwiftfinStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Errors
|
||||
enum Errors {
|
||||
case existingServer(State.Server)
|
||||
case existingUser(State.User)
|
||||
}
|
||||
|
||||
// MARK: dataStack
|
||||
static let dataStack: DataStack = {
|
||||
let schema = CoreStoreSchema(modelVersion: "V1",
|
||||
entities: [
|
||||
|
@ -138,3 +145,24 @@ enum SwiftfinStore {
|
|||
return _dataStack
|
||||
}()
|
||||
}
|
||||
|
||||
extension SwiftfinStore.Errors: LocalizedError {
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .existingServer(_):
|
||||
return "Existing Server"
|
||||
case .existingUser(_):
|
||||
return "Existing User"
|
||||
}
|
||||
}
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .existingServer(let server):
|
||||
return "Server \(server.name) already exists with same server ID"
|
||||
case .existingUser(let user):
|
||||
return "User \(user.username) already exists with same user ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,15 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
@Published var discoveredServers: Set<ServerDiscovery.ServerLookupResponse> = []
|
||||
@Published var searching = false
|
||||
private let discovery = ServerDiscovery()
|
||||
|
||||
var alertTitle: String {
|
||||
var message: String = ""
|
||||
if errorMessage?.code != ErrorMessage.noShowErrorCode {
|
||||
message.append(contentsOf: "\(errorMessage?.code ?? ErrorMessage.noShowErrorCode)\n")
|
||||
}
|
||||
message.append(contentsOf: "\(errorMessage?.title ?? "Unkown Error")")
|
||||
return message
|
||||
}
|
||||
|
||||
func connectToServer(uri: String) {
|
||||
#if targetEnvironment(simulator)
|
||||
|
|
|
@ -20,9 +20,18 @@ final class UserSignInViewModel: ViewModel {
|
|||
self.server = server
|
||||
}
|
||||
|
||||
var alertTitle: String {
|
||||
var message: String = ""
|
||||
if errorMessage?.code != ErrorMessage.noShowErrorCode {
|
||||
message.append(contentsOf: "\(errorMessage?.code ?? ErrorMessage.noShowErrorCode)\n")
|
||||
}
|
||||
message.append(contentsOf: "\(errorMessage?.title ?? "Unkown Error")")
|
||||
return message
|
||||
}
|
||||
|
||||
func login(username: String, password: String) {
|
||||
LogManager.shared.log.debug("Attempting to login to server at \"\(server.uri)\"", tag: "login")
|
||||
LogManager.shared.log.debug("username == \"\": \(username), password == \"\": \(password)", tag: "login")
|
||||
LogManager.shared.log.debug("username: \(username), password: \(password)", tag: "login")
|
||||
|
||||
SessionManager.main.loginUser(server: server, username: username, password: password)
|
||||
.trackActivity(loading)
|
||||
|
|
|
@ -29,11 +29,12 @@ class ViewModel: ObservableObject {
|
|||
case .finished:
|
||||
break
|
||||
case .failure(let error):
|
||||
if let errorResponse = error as? ErrorResponse {
|
||||
|
||||
let logConstructor = LogConstructor(message: "__NOTHING__", tag: tag, level: logLevel, function: function, file: file, line: line)
|
||||
|
||||
switch error {
|
||||
case is ErrorResponse:
|
||||
let networkError: NetworkError
|
||||
let logConstructor = LogConstructor(message: "__NOTHING__", tag: tag, level: logLevel, function: function, file: file, line: line)
|
||||
|
||||
let errorResponse = error as! ErrorResponse
|
||||
switch errorResponse {
|
||||
case .error(-1, _, _, _):
|
||||
networkError = .URLError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
|
@ -51,7 +52,55 @@ class ViewModel: ObservableObject {
|
|||
self.errorMessage = networkError.errorMessage
|
||||
|
||||
networkError.logMessage()
|
||||
|
||||
case is SwiftfinStore.Errors:
|
||||
let swiftfinError = error as! SwiftfinStore.Errors
|
||||
let errorMessage = ErrorMessage(code: ErrorMessage.noShowErrorCode,
|
||||
title: swiftfinError.title,
|
||||
displayMessage: swiftfinError.errorDescription ?? "",
|
||||
logConstructor: logConstructor)
|
||||
self.errorMessage = errorMessage
|
||||
LogManager.shared.log.error("Request failed: \(swiftfinError.errorDescription ?? "")")
|
||||
|
||||
default:
|
||||
let genericErrorMessage = ErrorMessage(code: ErrorMessage.noShowErrorCode,
|
||||
title: "Generic Error",
|
||||
displayMessage: error.localizedDescription,
|
||||
logConstructor: logConstructor)
|
||||
self.errorMessage = genericErrorMessage
|
||||
LogManager.shared.log.error("Request failed: Generic error - \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
// if let errorResponse = error as? ErrorResponse {
|
||||
//
|
||||
// let networkError: NetworkError
|
||||
//
|
||||
// switch errorResponse {
|
||||
// case .error(-1, _, _, _):
|
||||
// networkError = .URLError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
// // Use the errorResponse description for debugging, rather than the user-facing friendly description which may not be implemented
|
||||
// LogManager.shared.log.error("Request failed: URL request failed with error \(networkError.errorMessage.code): \(errorResponse.localizedDescription)")
|
||||
// case .error(-2, _, _, _):
|
||||
// networkError = .HTTPURLError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
// LogManager.shared.log.error("Request failed: HTTP URL request failed with description: \(errorResponse.localizedDescription)")
|
||||
// default:
|
||||
// networkError = .JellyfinError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
// // Able to use user-facing friendly description here since just HTTP status codes
|
||||
// LogManager.shared.log.error("Request failed: \(networkError.errorMessage.code) - \(networkError.errorMessage.title): \(networkError.errorMessage.logConstructor.message)\n\(error.localizedDescription)")
|
||||
// }
|
||||
//
|
||||
// self.errorMessage = networkError.errorMessage
|
||||
//
|
||||
// networkError.logMessage()
|
||||
// } else {
|
||||
// let generalErrorMessage = ErrorMessage(code: 0,
|
||||
// title: "Error",
|
||||
// displayMessage: error.localizedDescription,
|
||||
// logConstructor: logConstructor)
|
||||
//
|
||||
// self.errorMessage = generalErrorMessage
|
||||
// LogManager.shared.log.error("Request failed: General error - \(error.localizedDescription)")
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue