add ServerEnvironment.setUp, reset func
add SessionManager.updateHeader, login, logout
This commit is contained in:
parent
bc03de481d
commit
40127b0710
|
@ -97,6 +97,8 @@
|
||||||
62EC3530267666A5000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
|
62EC3530267666A5000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
|
||||||
62EC353126766848000E9F2D /* ServerEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352B26766675000E9F2D /* ServerEnvironment.swift */; };
|
62EC353126766848000E9F2D /* ServerEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352B26766675000E9F2D /* ServerEnvironment.swift */; };
|
||||||
62EC353226766849000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
|
62EC353226766849000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
|
||||||
|
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
|
||||||
|
62EC353526766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
|
||||||
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
@ -219,6 +221,7 @@
|
||||||
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
|
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||||
62EC352B26766675000E9F2D /* ServerEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerEnvironment.swift; sourceTree = "<group>"; };
|
62EC352B26766675000E9F2D /* ServerEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerEnvironment.swift; sourceTree = "<group>"; };
|
||||||
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
|
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
|
||||||
|
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
|
||||||
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
|
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
@ -402,6 +405,7 @@
|
||||||
621338922660107500A81A2A /* StringExtensions.swift */,
|
621338922660107500A81A2A /* StringExtensions.swift */,
|
||||||
6267B3D526710B8900A7371D /* CollectionExtensions.swift */,
|
6267B3D526710B8900A7371D /* CollectionExtensions.swift */,
|
||||||
6267B3D92671138200A7371D /* ImageExtensions.swift */,
|
6267B3D92671138200A7371D /* ImageExtensions.swift */,
|
||||||
|
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -609,6 +613,7 @@
|
||||||
5321753F2671DEA6005491E6 /* SettingsViewModel.swift in Sources */,
|
5321753F2671DEA6005491E6 /* SettingsViewModel.swift in Sources */,
|
||||||
535870AA2669D8AE00D05A09 /* BlurHashDecode.swift in Sources */,
|
535870AA2669D8AE00D05A09 /* BlurHashDecode.swift in Sources */,
|
||||||
535870652669D21600D05A09 /* ContentView.swift in Sources */,
|
535870652669D21600D05A09 /* ContentView.swift in Sources */,
|
||||||
|
62EC353526766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
|
||||||
535870A62669D8AE00D05A09 /* LazyView.swift in Sources */,
|
535870A62669D8AE00D05A09 /* LazyView.swift in Sources */,
|
||||||
53C4404F266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */,
|
53C4404F266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */,
|
||||||
5358706F2669D21700D05A09 /* JellyfinPlayer_tvOS.xcdatamodeld in Sources */,
|
5358706F2669D21700D05A09 /* JellyfinPlayer_tvOS.xcdatamodeld in Sources */,
|
||||||
|
@ -653,6 +658,7 @@
|
||||||
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */,
|
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */,
|
||||||
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
|
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
|
||||||
5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */,
|
5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */,
|
||||||
|
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
|
||||||
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
|
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
|
||||||
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
|
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
|
||||||
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */,
|
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */,
|
||||||
|
|
|
@ -12,7 +12,6 @@ import JellyfinAPI
|
||||||
|
|
||||||
struct ConnectToServerView: View {
|
struct ConnectToServerView: View {
|
||||||
@Environment(\.managedObjectContext) private var viewContext
|
@Environment(\.managedObjectContext) private var viewContext
|
||||||
@EnvironmentObject var globalData: GlobalData
|
|
||||||
@EnvironmentObject var jsi: justSignedIn
|
@EnvironmentObject var jsi: justSignedIn
|
||||||
|
|
||||||
@State private var uri = ""
|
@State private var uri = ""
|
||||||
|
|
|
@ -17,8 +17,6 @@ struct ContentView: View {
|
||||||
@EnvironmentObject var orientationInfo: OrientationInfo
|
@EnvironmentObject var orientationInfo: OrientationInfo
|
||||||
@EnvironmentObject var jsi: justSignedIn
|
@EnvironmentObject var jsi: justSignedIn
|
||||||
|
|
||||||
@StateObject private var globalData = GlobalData()
|
|
||||||
|
|
||||||
@State private var needsToSelectServer = false
|
@State private var needsToSelectServer = false
|
||||||
@State private var isLoading = false
|
@State private var isLoading = false
|
||||||
@State private var tabSelection: String = "Home"
|
@State private var tabSelection: String = "Home"
|
||||||
|
|
|
@ -9,7 +9,6 @@ import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct LibraryListView: View {
|
struct LibraryListView: View {
|
||||||
@EnvironmentObject var globalData: GlobalData
|
|
||||||
|
|
||||||
@State var library_ids: [String] = ["favorites", "genres"]
|
@State var library_ids: [String] = ["favorites", "genres"]
|
||||||
@State var library_names: [String: String] = ["favorites": "Favorites", "genres": "Genres"]
|
@State var library_names: [String: String] = ["favorites": "Favorites", "genres": "Genres"]
|
||||||
|
|
|
@ -9,7 +9,6 @@ import SwiftUI
|
||||||
import JellyfinAPI
|
import JellyfinAPI
|
||||||
|
|
||||||
struct NextUpView: View {
|
struct NextUpView: View {
|
||||||
@EnvironmentObject var globalData: GlobalData
|
|
||||||
|
|
||||||
@State private var items: [BaseItemDto] = []
|
@State private var items: [BaseItemDto] = []
|
||||||
@State private var viewDidLoad: Bool = false
|
@State private var viewDidLoad: Bool = false
|
||||||
|
|
|
@ -9,7 +9,6 @@ import SwiftUI
|
||||||
import JellyfinAPI
|
import JellyfinAPI
|
||||||
|
|
||||||
struct SeriesItemView: View {
|
struct SeriesItemView: View {
|
||||||
@EnvironmentObject private var globalData: GlobalData
|
|
||||||
@EnvironmentObject private var orientationInfo: OrientationInfo
|
@EnvironmentObject private var orientationInfo: OrientationInfo
|
||||||
|
|
||||||
var item: BaseItemDto
|
var item: BaseItemDto
|
||||||
|
|
|
@ -11,7 +11,6 @@ import SwiftUI
|
||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
@Environment(\.managedObjectContext) private var viewContext
|
@Environment(\.managedObjectContext) private var viewContext
|
||||||
|
|
||||||
@EnvironmentObject var globalData: GlobalData
|
|
||||||
@EnvironmentObject var jsi: justSignedIn
|
@EnvironmentObject var jsi: justSignedIn
|
||||||
|
|
||||||
@ObservedObject var viewModel: SettingsViewModel
|
@ObservedObject var viewModel: SettingsViewModel
|
||||||
|
|
|
@ -39,7 +39,6 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
weak var delegate: PlayerViewControllerDelegate?
|
weak var delegate: PlayerViewControllerDelegate?
|
||||||
|
|
||||||
var mediaPlayer = VLCMediaPlayer()
|
var mediaPlayer = VLCMediaPlayer()
|
||||||
var globalData = GlobalData()
|
|
||||||
|
|
||||||
@IBOutlet weak var timeText: UILabel!
|
@IBOutlet weak var timeText: UILabel!
|
||||||
@IBOutlet weak var videoContentView: UIView!
|
@IBOutlet weak var videoContentView: UIView!
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
// https://www.hackingwithswift.com/quick-start/swiftui/how-to-detect-device-rotation
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// Our custom view modifier to track rotation and
|
||||||
|
// call our action
|
||||||
|
struct DeviceRotationViewModifier: ViewModifier {
|
||||||
|
let action: (UIDeviceOrientation) -> Void
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.onAppear()
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
||||||
|
action(UIDevice.current.orientation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A View wrapper to make the modifier easier to use
|
||||||
|
extension View {
|
||||||
|
func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View {
|
||||||
|
self.modifier(DeviceRotationViewModifier(action: action))
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,17 +7,53 @@
|
||||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
import Combine
|
||||||
import CoreData
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
import JellyfinAPI
|
||||||
|
|
||||||
final class ServerEnvironment {
|
final class ServerEnvironment {
|
||||||
|
|
||||||
static let shared = ServerEnvironment()
|
static let shared = ServerEnvironment()
|
||||||
var server: Server?
|
fileprivate(set) var server: Server!
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
let serverRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Server")
|
let serverRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Server")
|
||||||
let servers = try? PersistenceController.shared.container.viewContext.fetch(serverRequest) as? [Server]
|
let servers = try? PersistenceController.shared.container.viewContext.fetch(serverRequest) as? [Server]
|
||||||
server = servers?.first
|
server = servers?.first
|
||||||
|
guard let baseURI = server?.baseURI else { return }
|
||||||
|
JellyfinAPI.basePath = baseURI
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUp(with uri: String) -> AnyPublisher<Server, Error> {
|
||||||
|
var uri = uri
|
||||||
|
if !uri.contains("http") {
|
||||||
|
uri = "https://" + uri
|
||||||
|
}
|
||||||
|
if uri.last == "/" {
|
||||||
|
uri = String(uri.dropLast())
|
||||||
|
}
|
||||||
|
JellyfinAPI.basePath = uri
|
||||||
|
return SystemAPI.getPublicSystemInfo()
|
||||||
|
.map { response in
|
||||||
|
let server = Server(context: PersistenceController.shared.container.viewContext)
|
||||||
|
server.baseURI = uri
|
||||||
|
server.name = response.serverName
|
||||||
|
server.server_id = response.id
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
.handleEvents(receiveOutput: { [unowned self] response in
|
||||||
|
server = response
|
||||||
|
_ = try? PersistenceController.shared.container.viewContext.save()
|
||||||
|
}).eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() throws {
|
||||||
|
JellyfinAPI.basePath = ""
|
||||||
|
server = nil
|
||||||
|
|
||||||
|
let serverRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
|
||||||
|
let deleteRequest = NSBatchDeleteRequest(fetchRequest: serverRequest)
|
||||||
|
|
||||||
|
try PersistenceController.shared.container.viewContext.execute(deleteRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,18 @@
|
||||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
import Combine
|
||||||
import CoreData
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
import JellyfinAPI
|
||||||
import KeychainSwift
|
import KeychainSwift
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
final class SessionManager {
|
final class SessionManager {
|
||||||
|
|
||||||
static let shared = SessionManager()
|
static let shared = SessionManager()
|
||||||
var user: SignedInUser?
|
fileprivate(set) var user: SignedInUser!
|
||||||
var authHeader: String?
|
fileprivate(set) var authHeader: String!
|
||||||
|
fileprivate(set) var deviceIDString: String
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
let savedUserRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "SignedInUser")
|
let savedUserRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "SignedInUser")
|
||||||
|
@ -24,12 +26,22 @@ final class SessionManager {
|
||||||
user = savedUsers?.first
|
user = savedUsers?.first
|
||||||
|
|
||||||
let keychain = KeychainSwift()
|
let keychain = KeychainSwift()
|
||||||
// need prefix
|
|
||||||
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
||||||
|
if let deviceID = keychain.get("DeviceIDString") {
|
||||||
|
self.deviceIDString = deviceID
|
||||||
|
} else {
|
||||||
|
self.deviceIDString = UUID().uuidString
|
||||||
|
keychain.set(deviceIDString, forKey: "DeviceIDString")
|
||||||
|
}
|
||||||
|
|
||||||
guard let authToken = keychain.get("AccessToken_\(user?.user_id ?? "")") else {
|
guard let authToken = keychain.get("AccessToken_\(user?.user_id ?? "")") else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateHeader(with: authToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateHeader(with authToken: String?) {
|
||||||
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||||
var deviceName = UIDevice.current.name
|
var deviceName = UIDevice.current.name
|
||||||
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
|
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
|
||||||
|
@ -38,10 +50,54 @@ final class SessionManager {
|
||||||
var header = "MediaBrowser "
|
var header = "MediaBrowser "
|
||||||
header.append("Client=\"SwiftFin\", ")
|
header.append("Client=\"SwiftFin\", ")
|
||||||
header.append("Device=\"\(deviceName)\", ")
|
header.append("Device=\"\(deviceName)\", ")
|
||||||
header.append("DeviceId=\"\(user?.device_uuid ?? "")\", ")
|
header.append("DeviceId=\"\(deviceIDString)\", ")
|
||||||
header.append("Version=\"\(appVersion ?? "0.0.1")\", ")
|
header.append("Version=\"\(appVersion ?? "0.0.1")\", ")
|
||||||
header.append("Token=\"\(authToken)\"")
|
if let token = authToken {
|
||||||
|
header.append("Token=\"\(token)\"")
|
||||||
|
}
|
||||||
|
|
||||||
self.authHeader = header
|
authHeader = header
|
||||||
|
JellyfinAPI.customHeaders["X-Emby-Authorization"] = authHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
func login(username: String, password: String) -> AnyPublisher<SignedInUser, Error> {
|
||||||
|
updateHeader(with: nil)
|
||||||
|
|
||||||
|
return UserAPI.authenticateUserByName(authenticateUserByName: AuthenticateUserByName(username: username, pw: password))
|
||||||
|
.map { [unowned self] response -> (SignedInUser, String?) in
|
||||||
|
let user = SignedInUser(context: PersistenceController.shared.container.viewContext)
|
||||||
|
user.device_uuid = deviceIDString
|
||||||
|
user.username = response.user?.name
|
||||||
|
user.user_id = response.user?.id
|
||||||
|
return (user, response.accessToken)
|
||||||
|
}
|
||||||
|
.handleEvents(receiveOutput: { [unowned self] response, accessToken in
|
||||||
|
user = response
|
||||||
|
_ = try? PersistenceController.shared.container.viewContext.save()
|
||||||
|
if let userID = user.user_id,
|
||||||
|
let token = accessToken
|
||||||
|
{
|
||||||
|
let keychain = KeychainSwift()
|
||||||
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
||||||
|
keychain.set(token, forKey: "AccessToken_\(userID)")
|
||||||
|
}
|
||||||
|
updateHeader(with: accessToken)
|
||||||
|
})
|
||||||
|
.map(\.0)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
func logout() throws {
|
||||||
|
let keychain = KeychainSwift()
|
||||||
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
||||||
|
keychain.delete("AccessToken_\(user.user_id ?? "")")
|
||||||
|
JellyfinAPI.customHeaders["X-Emby-Authorization"] = nil
|
||||||
|
user = nil
|
||||||
|
authHeader = nil
|
||||||
|
|
||||||
|
let userRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "SignedInUser")
|
||||||
|
let deleteRequest = NSBatchDeleteRequest(fetchRequest: userRequest)
|
||||||
|
|
||||||
|
try PersistenceController.shared.container.viewContext.execute(deleteRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,24 +26,3 @@ public enum SortBy: String, Codable, CaseIterable {
|
||||||
class justSignedIn: ObservableObject {
|
class justSignedIn: ObservableObject {
|
||||||
@Published var did: Bool = false
|
@Published var did: Bool = false
|
||||||
}
|
}
|
||||||
|
|
||||||
class GlobalData: ObservableObject {
|
|
||||||
@Published var user: SignedInUser!
|
|
||||||
@Published var authToken: String = ""
|
|
||||||
@Published var server: Server!
|
|
||||||
@Published var authHeader: String = ""
|
|
||||||
@Published var isInNetwork: Bool = true
|
|
||||||
@Published var networkError: Bool = false
|
|
||||||
@Published var expiredCredentials: Bool = false
|
|
||||||
var pendingAPIRequests = Set<AnyCancellable>()
|
|
||||||
}
|
|
||||||
|
|
||||||
extension GlobalData: Equatable {
|
|
||||||
|
|
||||||
static func == (lhs: GlobalData, rhs: GlobalData) -> Bool {
|
|
||||||
lhs.user == rhs.user
|
|
||||||
&& lhs.authToken == rhs.authToken
|
|
||||||
&& lhs.server == rhs.server
|
|
||||||
&& lhs.authHeader == rhs.authHeader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue