From e81b593fa11197a92d539258d5fc090ad4d0fe12 Mon Sep 17 00:00:00 2001 From: Ethan Pippin Date: Wed, 13 Oct 2021 19:24:50 -0600 Subject: [PATCH] Add UserLoginView --- JellyfinPlayer tvOS/SettingsView.swift | 7 +- JellyfinPlayer.xcodeproj/project.pbxproj | 18 ++ .../AppURLHandler/AppURLHandler.swift | 2 +- .../ConnectToServerCoodinator.swift | 5 + .../Coordinators/MainCoordinator.swift | 8 +- .../Coordinators/ServerListCoordinator.swift | 3 - .../Coordinators/UserLoginCoordinator.swift | 28 +++ JellyfinPlayer/VideoPlayer.swift | 2 +- .../Views/ConnectToServerView.swift | 177 ------------------ JellyfinPlayer/Views/HomeView.swift | 1 + JellyfinPlayer/Views/ServerListView.swift | 34 +++- JellyfinPlayer/Views/SettingsView.swift | 5 +- JellyfinPlayer/Views/SplashView.swift | 2 +- JellyfinPlayer/Views/UserLoginView.swift | 51 +++++ Shared/Singleton/NotificationCenter.swift | 23 +++ Shared/Singleton/SessionManager.swift | 27 ++- .../ViewModels/ConnectToServerViewModel.swift | 5 +- Shared/ViewModels/SplashViewModel.swift | 3 +- Shared/ViewModels/UserLoginViewModel.swift | 18 +- 19 files changed, 197 insertions(+), 222 deletions(-) create mode 100644 JellyfinPlayer/Coordinators/UserLoginCoordinator.swift create mode 100644 JellyfinPlayer/Views/UserLoginView.swift create mode 100644 Shared/Singleton/NotificationCenter.swift diff --git a/JellyfinPlayer tvOS/SettingsView.swift b/JellyfinPlayer tvOS/SettingsView.swift index 97916b7c..e2e3ebea 100644 --- a/JellyfinPlayer tvOS/SettingsView.swift +++ b/JellyfinPlayer tvOS/SettingsView.swift @@ -67,18 +67,17 @@ struct SettingsView: View { Spacer() Button { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - let nc = NotificationCenter.default - nc.post(name: Notification.Name("didSignOut"), object: nil) + SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil) } } label: { Text("Switch user").font(.callout) } } Button { + // TODO: remove delay DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { SessionManager.current.logout() - let nc = NotificationCenter.default - nc.post(name: Notification.Name("didSignOut"), object: nil) + SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil) } } label: { Text("Sign out").font(.callout) diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index e4b17dd3..512282c8 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -276,6 +276,12 @@ E13DD3EA27177ED6009D4DAF /* ServerListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3E827177ED6009D4DAF /* ServerListCoordinator.swift */; }; E13DD3EC27178A54009D4DAF /* UserLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3EB27178A54009D4DAF /* UserLoginViewModel.swift */; }; E13DD3ED27178A54009D4DAF /* UserLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3EB27178A54009D4DAF /* UserLoginViewModel.swift */; }; + E13DD3EF27178F87009D4DAF /* NotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3EE27178F87009D4DAF /* NotificationCenter.swift */; }; + E13DD3F027178F87009D4DAF /* NotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3EE27178F87009D4DAF /* NotificationCenter.swift */; }; + E13DD3F227179378009D4DAF /* UserLoginCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3F127179378009D4DAF /* UserLoginCoordinator.swift */; }; + E13DD3F327179378009D4DAF /* UserLoginCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3F127179378009D4DAF /* UserLoginCoordinator.swift */; }; + E13DD3F5271793BB009D4DAF /* UserLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3F4271793BB009D4DAF /* UserLoginView.swift */; }; + E13DD3F6271793BB009D4DAF /* UserLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3F4271793BB009D4DAF /* UserLoginView.swift */; }; E14F7D0726DB36EF007C3AE6 /* ItemPortraitMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14F7D0626DB36EF007C3AE6 /* ItemPortraitMainView.swift */; }; E14F7D0926DB36F7007C3AE6 /* ItemLandscapeMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14F7D0826DB36F7007C3AE6 /* ItemLandscapeMainView.swift */; }; E173DA5026D048D600CC4EB7 /* ServerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */; }; @@ -517,6 +523,9 @@ E13DD3E427177D15009D4DAF /* ServerListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListView.swift; sourceTree = ""; }; E13DD3E827177ED6009D4DAF /* ServerListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListCoordinator.swift; sourceTree = ""; }; E13DD3EB27178A54009D4DAF /* UserLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLoginViewModel.swift; sourceTree = ""; }; + E13DD3EE27178F87009D4DAF /* NotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenter.swift; sourceTree = ""; }; + E13DD3F127179378009D4DAF /* UserLoginCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLoginCoordinator.swift; sourceTree = ""; }; + E13DD3F4271793BB009D4DAF /* UserLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLoginView.swift; sourceTree = ""; }; E14F7D0626DB36EF007C3AE6 /* ItemPortraitMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemPortraitMainView.swift; sourceTree = ""; }; E14F7D0826DB36F7007C3AE6 /* ItemLandscapeMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemLandscapeMainView.swift; sourceTree = ""; }; E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailView.swift; sourceTree = ""; }; @@ -1029,6 +1038,7 @@ 62C29EA026D102A500C1D2E7 /* MainTabCoordinator.swift */, 6220D0B626D5EE1100B8E046 /* SearchCoordinator.swift */, E13DD3E827177ED6009D4DAF /* ServerListCoordinator.swift */, + E13DD3F127179378009D4DAF /* UserLoginCoordinator.swift */, 6220D0B026D5EC9900B8E046 /* SettingsCoordinator.swift */, 6220D0C526D62D8700B8E046 /* VideoPlayerCoordinator.swift */, ); @@ -1039,6 +1049,7 @@ isa = PBXGroup; children = ( 536D3D73267BA8170004248C /* BackgroundManager.swift */, + E13DD3EE27178F87009D4DAF /* NotificationCenter.swift */, 53649AB0269CFB1900A2D8B7 /* LogManager.swift */, 62EC352E267666A5000E9F2D /* SessionManager.swift */, ); @@ -1112,6 +1123,7 @@ E13DD3E427177D15009D4DAF /* ServerListView.swift */, 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */, 625CB5672678B6FB00530A6E /* SplashView.swift */, + E13DD3F4271793BB009D4DAF /* UserLoginView.swift */, 53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */, ); path = Views; @@ -1541,11 +1553,13 @@ C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */, 53ABFDE9267974EF00886593 /* HomeViewModel.swift in Sources */, 53116A17268B919A003024C9 /* SeriesItemView.swift in Sources */, + E13DD3F027178F87009D4DAF /* NotificationCenter.swift in Sources */, E13DD3DA27169406009D4DAF /* SwiftfinStoreKeychain.swift in Sources */, 531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */, 53ABFDDE267974E300886593 /* SplashView.swift in Sources */, 53ABFDE8267974EF00886593 /* SplashViewModel.swift in Sources */, 62E632DE267D2E170063E547 /* LatestMediaViewModel.swift in Sources */, + E13DD3F327179378009D4DAF /* UserLoginCoordinator.swift in Sources */, E1FCD09726C47118007C8DCF /* ErrorMessage.swift in Sources */, E13DD3EA27177ED6009D4DAF /* ServerListCoordinator.swift in Sources */, 53116A19268B947A003024C9 /* PlainLinkButton.swift in Sources */, @@ -1609,6 +1623,7 @@ E1F0204F26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */, 53ABFDEB2679753200886593 /* ConnectToServerView.swift in Sources */, 536D3D76267BA9BB0004248C /* MainTabViewModel.swift in Sources */, + E13DD3F6271793BB009D4DAF /* UserLoginView.swift in Sources */, 5310695C2684E7EE00CFFDBA /* VideoPlayerViewController.swift in Sources */, C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */, 536D3D74267BA8170004248C /* BackgroundManager.swift in Sources */, @@ -1635,6 +1650,7 @@ 5364F455266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */, 6220D0B426D5ED8000B8E046 /* LibraryCoordinator.swift in Sources */, 6220D0C026D61C5000B8E046 /* ItemCoordinator.swift in Sources */, + E13DD3F227179378009D4DAF /* UserLoginCoordinator.swift in Sources */, 621338932660107500A81A2A /* StringExtensions.swift in Sources */, 53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */, 62E632EC267D410B0063E547 /* SeriesItemViewModel.swift in Sources */, @@ -1682,6 +1698,7 @@ E188460026DECB9E00B0C5B7 /* ItemLandscapeTopBarView.swift in Sources */, 091B5A8B2683142E00D78B61 /* UDPBroadCastConnection.swift in Sources */, 6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */, + E13DD3F5271793BB009D4DAF /* UserLoginView.swift in Sources */, E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */, 53649AB1269CFB1900A2D8B7 /* LogManager.swift in Sources */, E13DD3E127176BD3009D4DAF /* ServerListViewModel.swift in Sources */, @@ -1725,6 +1742,7 @@ 5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */, 09389CC726819B4600AE350E /* VideoPlayerModel.swift in Sources */, 6220D0B726D5EE1100B8E046 /* SearchCoordinator.swift in Sources */, + E13DD3EF27178F87009D4DAF /* NotificationCenter.swift in Sources */, 5377CBF5263B596A003A4E83 /* JellyfinPlayerApp.swift in Sources */, E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */, 53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */, diff --git a/JellyfinPlayer/AppURLHandler/AppURLHandler.swift b/JellyfinPlayer/AppURLHandler/AppURLHandler.swift index 9dcb5eaf..4c64b916 100644 --- a/JellyfinPlayer/AppURLHandler/AppURLHandler.swift +++ b/JellyfinPlayer/AppURLHandler/AppURLHandler.swift @@ -82,7 +82,7 @@ extension AppURLHandler { // It would be nice if the ItemViewModel could be initialized to id later. getItem(userID: userID, itemID: itemID) { item in guard let item = item else { return } - NotificationCenter.default.post(name: Notification.Name("processDeepLink"), object: DeepLink.item(item)) + SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.processDeepLink, object: DeepLink.item(item)) } return true diff --git a/JellyfinPlayer/Coordinators/ConnectToServerCoodinator.swift b/JellyfinPlayer/Coordinators/ConnectToServerCoodinator.swift index 5f81bd85..4d3c49e0 100644 --- a/JellyfinPlayer/Coordinators/ConnectToServerCoodinator.swift +++ b/JellyfinPlayer/Coordinators/ConnectToServerCoodinator.swift @@ -15,6 +15,11 @@ final class ConnectToServerCoodinator: NavigationCoordinatable { let stack = NavigationStack(initial: \ConnectToServerCoodinator.start) @Root var start = makeStart + @Route(.push) var userLogin = makeUserLogin + + func makeUserLogin(server: SwiftfinStore.Models.Server) -> UserLoginCoordinator { + return UserLoginCoordinator(viewModel: .init(server: server)) + } @ViewBuilder func makeStart() -> some View { ConnectToServerView() diff --git a/JellyfinPlayer/Coordinators/MainCoordinator.swift b/JellyfinPlayer/Coordinators/MainCoordinator.swift index 7b723617..4f50e0cf 100644 --- a/JellyfinPlayer/Coordinators/MainCoordinator.swift +++ b/JellyfinPlayer/Coordinators/MainCoordinator.swift @@ -37,10 +37,10 @@ import SwiftUI UIScrollView.appearance().keyboardDismissMode = .onDrag #endif - let nc = NotificationCenter.default - nc.addObserver(self, selector: #selector(didLogIn), name: Notification.Name("didSignIn"), object: nil) - nc.addObserver(self, selector: #selector(didLogOut), name: Notification.Name("didSignOut"), object: nil) - nc.addObserver(self, selector: #selector(processDeepLink), name: Notification.Name("processDeepLink"), object: nil) + let nc = SwiftfinNotificationCenter.main + nc.addObserver(self, selector: #selector(didLogIn), name: SwiftfinNotificationCenter.Keys.didSignIn, object: nil) + nc.addObserver(self, selector: #selector(didLogOut), name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil) + nc.addObserver(self, selector: #selector(processDeepLink), name: SwiftfinNotificationCenter.Keys.processDeepLink, object: nil) } @objc func didLogIn() { diff --git a/JellyfinPlayer/Coordinators/ServerListCoordinator.swift b/JellyfinPlayer/Coordinators/ServerListCoordinator.swift index 0dbdb704..20ea321a 100644 --- a/JellyfinPlayer/Coordinators/ServerListCoordinator.swift +++ b/JellyfinPlayer/Coordinators/ServerListCoordinator.swift @@ -16,14 +16,11 @@ final class ServerListCoordinator: NavigationCoordinatable { @Root var start = makeStart @Route(.push) var connectToServer = makeConnectToServer -// @Route(.push) var loginUser = makeLoginuser func makeConnectToServer() -> ConnectToServerCoodinator { ConnectToServerCoodinator() } -// func makeLoginUser -> - @ViewBuilder func makeStart() -> some View { ServerListView(viewModel: ServerListViewModel()) } diff --git a/JellyfinPlayer/Coordinators/UserLoginCoordinator.swift b/JellyfinPlayer/Coordinators/UserLoginCoordinator.swift new file mode 100644 index 00000000..f40522a0 --- /dev/null +++ b/JellyfinPlayer/Coordinators/UserLoginCoordinator.swift @@ -0,0 +1,28 @@ +// + /* + * 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 +import Stinsen +import SwiftUI + +final class UserLoginCoordinator: NavigationCoordinatable { + let stack = NavigationStack(initial: \UserLoginCoordinator.start) + + @Root var start = makeStart + + let viewModel: UserLoginViewModel + + init(viewModel: UserLoginViewModel) { + self.viewModel = viewModel + } + + @ViewBuilder func makeStart() -> some View { + UserLoginView(viewModel: viewModel) + } +} diff --git a/JellyfinPlayer/VideoPlayer.swift b/JellyfinPlayer/VideoPlayer.swift index 35c5f0c8..3585c916 100644 --- a/JellyfinPlayer/VideoPlayer.swift +++ b/JellyfinPlayer/VideoPlayer.swift @@ -539,7 +539,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe self.delegate?.exitPlayer(self) // TODO: todo // SessionManager.current.logout() - main?.root(\.connectToServer) + main?.root(\.serverList) case .error: self.delegate?.exitPlayer(self) } diff --git a/JellyfinPlayer/Views/ConnectToServerView.swift b/JellyfinPlayer/Views/ConnectToServerView.swift index fc923d2c..6792f388 100644 --- a/JellyfinPlayer/Views/ConnectToServerView.swift +++ b/JellyfinPlayer/Views/ConnectToServerView.swift @@ -72,180 +72,3 @@ struct ConnectToServerView: View { } } } - -//struct ConnectToServerView: View { -// @EnvironmentObject var mainRouter: MainCoordinator.Router -// @StateObject var viewModel = ConnectToServerViewModel() -// @State var username = "" -// @State var password = "" -// @State var uri = "" -// -// var body: some View { -// ZStack { -// Form { -// if viewModel.isConnectedServer { -// if viewModel.publicUsers.isEmpty { -// Section(header: Text("Login to \(SessionManager.main.currentLogin.server.name)")) { -// TextField(NSLocalizedString("Username", comment: ""), text: $username) -// .disableAutocorrection(true) -// .autocapitalization(.none) -// SecureField(NSLocalizedString("Password", comment: ""), text: $password) -// .disableAutocorrection(true) -// .autocapitalization(.none) -// Button { -// viewModel.login() -// } label: { -// HStack { -// Text("Login") -// Spacer() -// if viewModel.isLoading { -// ProgressView() -// } -// } -// }.disabled(viewModel.isLoading || username.isEmpty) -// } -// -// Section { -// Button { -// viewModel.isConnectedServer = false -// } label: { -// HStack { -// HStack { -// Image(systemName: "chevron.left") -// Text("Change Server") -// } -// Spacer() -// } -// } -// } -// } else { -// Section(header: Text("Login to \(SessionManager.main.currentLogin.server.name)")) { -// ForEach(viewModel.publicUsers, id: \.id) { publicUser in -// HStack { -// Button(action: { -// // TODO: todo -// print("TODO") -//// if SessionManager.current.doesUserHaveSavedSession(userID: publicUser.id!) { -//// let user = SessionManager.current.getSavedSession(userID: publicUser.id!) -//// SessionManager.current.loginWithSavedSession(user: user) -//// mainRouter.root(\.mainTab) -//// } else { -//// username = publicUser.name ?? "" -//// viewModel.selectedPublicUser = publicUser -//// viewModel.hidePublicUsers() -//// if !(publicUser.hasPassword ?? true) { -//// password = "" -//// DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { -//// viewModel.login() -//// } -//// } -//// } -// }) { -// HStack { -// Text(publicUser.name ?? "").font(.subheadline).fontWeight(.semibold) -// Spacer() -// if publicUser.primaryImageTag != nil { -// ImageView(src: URL(string: "\(SessionManager.main.currentLogin.server.uri)/Users/\(publicUser.id ?? "")/Images/Primary?width=60&quality=80&tag=\(publicUser.primaryImageTag!)")!) -// .frame(width: 60, height: 60) -// .cornerRadius(30.0) -// } else { -// Image(systemName: "person.fill") -// .foregroundColor(Color(red: 1, green: 1, blue: 1).opacity(0.8)) -// .font(.system(size: 35)) -// .frame(width: 60, height: 60) -// .background(Color(red: 98 / 255, green: 121 / 255, blue: 205 / 255)) -// .cornerRadius(30.0) -// .shadow(radius: 6) -// } -// } -// } -// } -// } -// } -// -// Section { -// Button { -// viewModel.publicUsers.removeAll() -// username = "" -// } label: { -// HStack { -// Text("Other User").font(.subheadline).fontWeight(.semibold) -// Spacer() -// Image(systemName: "person.fill.questionmark") -// .foregroundColor(Color(red: 1, green: 1, blue: 1).opacity(0.8)) -// .font(.system(size: 35)) -// .frame(width: 60, height: 60) -// .background(Color(red: 98 / 255, green: 121 / 255, blue: 205 / 255)) -// .cornerRadius(30.0) -// .shadow(radius: 6) -// } -// } -// } -// } -// } else { -// Section(header: Text("Connect Manually")) { -// TextField(NSLocalizedString("Server URL", comment: ""), text: $uri) -// .disableAutocorrection(true) -// .autocapitalization(.none) -// .keyboardType(.URL) -// Button { -// viewModel.connectToServer() -// } label: { -// HStack { -// Text("Connect") -// Spacer() -// if viewModel.isLoading { -// ProgressView() -// } -// } -// } -// .disabled(viewModel.isLoading || uri.isEmpty) -// } -// -// Section(header: Text("Discovered Servers")) { -// if self.viewModel.searching { -// ProgressView() -// } -// ForEach(self.viewModel.servers, id: \.id) { server in -// Button(action: { -// viewModel.connectToServer(at: server.url) -// }, label: { -// HStack { -// Text(server.name) -// .font(.headline) -// Text("• \(server.host)") -// .font(.subheadline) -// .foregroundColor(.secondary) -// Spacer() -// if viewModel.isLoading { -// ProgressView() -// } -// } -// -// }) -// } -// } -// .onAppear(perform: self.viewModel.discoverServers) -// } -// } -// } -// .onChange(of: uri) { uri in -// viewModel.uriSubject.send(uri) -// } -// .onChange(of: username) { username in -// viewModel.usernameSubject.send(username) -// } -// .onChange(of: password) { password in -// viewModel.passwordSubject.send(password) -// } -// .alert(item: $viewModel.errorMessage) { _ in -// Alert(title: Text("\(viewModel.errorMessage?.code ?? -1)\n\(viewModel.errorMessage?.title ?? "Error")"), -// message: Text(viewModel.errorMessage?.displayMessage ?? "Error"), -// dismissButton: .cancel()) -// } -// .navigationTitle(NSLocalizedString("Connect to Server", comment: "")) -// .onAppear { -// AppURLHandler.shared.appURLState = .allowedInLogin -// } -// } -//} diff --git a/JellyfinPlayer/Views/HomeView.swift b/JellyfinPlayer/Views/HomeView.swift index 05687d0e..d534f1af 100644 --- a/JellyfinPlayer/Views/HomeView.swift +++ b/JellyfinPlayer/Views/HomeView.swift @@ -14,6 +14,7 @@ struct HomeView: View { @EnvironmentObject var homeRouter: HomeCoordinator.Router @StateObject var viewModel = HomeViewModel() + // TODO: Move so that setup has same button init() { let backButtonBackgroundImage = UIImage(systemName: "chevron.backward.circle.fill") let barAppearance = UINavigationBar.appearance() diff --git a/JellyfinPlayer/Views/ServerListView.swift b/JellyfinPlayer/Views/ServerListView.swift index 9c208724..3fa614f9 100644 --- a/JellyfinPlayer/Views/ServerListView.swift +++ b/JellyfinPlayer/Views/ServerListView.swift @@ -7,6 +7,7 @@ * Copyright 2021 Aiden Vigue & Jellyfin Contributors */ +import CoreStore import SwiftUI struct ServerListView: View { @@ -22,13 +23,36 @@ struct ServerListView: View { } .navigationTitle("Servers") .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button { - serverListRouter.route(to: \.connectToServer) - } label: { - Text("Connect") + + ToolbarItemGroup(placement: .navigation) { + HStack { + Button { + serverListRouter.route(to: \.connectToServer) + } label: { + Text("Connect") + } + + Button { + SwiftfinStore.dataStack.perform(asynchronous: { transaction in + try! transaction.deleteAll(From()) + try! transaction.deleteAll(From()) + try! transaction.deleteAll(From()) + }) { _ in + viewModel.servers = [] + } + } label: { + Text("Purge") + } } } + +// ToolbarItem(placement: .navigationBarTrailing) { +// Button { +// serverListRouter.route(to: \.connectToServer) +// } label: { +// Text("Connect") +// } +// } } } } diff --git a/JellyfinPlayer/Views/SettingsView.swift b/JellyfinPlayer/Views/SettingsView.swift index 707b89e2..197bbe61 100644 --- a/JellyfinPlayer/Views/SettingsView.swift +++ b/JellyfinPlayer/Views/SettingsView.swift @@ -51,10 +51,9 @@ struct SettingsView: View { Button { settingsRouter.dismissCoordinator() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - // TODO: todo + // TODO: todo and move notification somewhere else // SessionManager.current.logout() - let nc = NotificationCenter.default - nc.post(name: Notification.Name("didSignOut"), object: nil) + SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil) } } label: { Text("Sign out") diff --git a/JellyfinPlayer/Views/SplashView.swift b/JellyfinPlayer/Views/SplashView.swift index 1235572e..fd099537 100644 --- a/JellyfinPlayer/Views/SplashView.swift +++ b/JellyfinPlayer/Views/SplashView.swift @@ -20,7 +20,7 @@ struct SplashView: View { if flag { mainRouter.root(\.mainTab) } else { - mainRouter.root(\.connectToServer) + mainRouter.root(\.serverList) } } } diff --git a/JellyfinPlayer/Views/UserLoginView.swift b/JellyfinPlayer/Views/UserLoginView.swift new file mode 100644 index 00000000..b2810ba0 --- /dev/null +++ b/JellyfinPlayer/Views/UserLoginView.swift @@ -0,0 +1,51 @@ +// + /* + * 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 SwiftUI +import Stinsen + +struct UserLoginView: View { + + @ObservedObject var viewModel: UserLoginViewModel + @State private var username: String = "" + @State private var password: String = "" + + var body: some View { + Form { + + Section { + TextField("Username", text: $username) + .disableAutocorrection(true) + .autocapitalization(.none) + + SecureField("Password", text: $password) + .disableAutocorrection(true) + .autocapitalization(.none) + + Button { + viewModel.login(username: username, password: password) + } label: { + HStack { + Text("Connect") + Spacer() + if viewModel.isLoading { + ProgressView() + } + } + } + .disabled(viewModel.isLoading || username.isEmpty) + + } header: { + // TODO: Server + Text("Login to \(viewModel.server.name)") + } + } + .navigationTitle("Login") + } +} diff --git a/Shared/Singleton/NotificationCenter.swift b/Shared/Singleton/NotificationCenter.swift new file mode 100644 index 00000000..6717d878 --- /dev/null +++ b/Shared/Singleton/NotificationCenter.swift @@ -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 + +enum SwiftfinNotificationCenter { + + static let main: NotificationCenter = { + return NotificationCenter() + }() + + enum Keys { + static let didSignIn = Notification.Name("didSignIn") + static let didSignOut = Notification.Name("didSignOut") + static let processDeepLink = Notification.Name("processDeepLink") + } +} diff --git a/Shared/Singleton/SessionManager.swift b/Shared/Singleton/SessionManager.swift index 13096968..19732556 100644 --- a/Shared/Singleton/SessionManager.swift +++ b/Shared/Singleton/SessionManager.swift @@ -10,6 +10,7 @@ import Combine import CoreData import CoreStore +import Defaults import Foundation import JellyfinAPI import KeychainSwift @@ -17,6 +18,7 @@ import UIKit #if os(tvOS) import TVServices +import SwiftUIFocusGuide #endif typealias CurrentLogin = (server: SwiftfinStore.Models.Server, user: SwiftfinStore.Models.User) @@ -30,18 +32,18 @@ final class SessionManager { // MARK: main static let main = SessionManager() - private let JellyfinDefaults = UserDefaults(suiteName: "jellyfin-defaults")! - private init() { - if let lastServerUserID = SwiftfinStore.Defaults.suite[.lastServerUserID], - let userID = lastServerUserID.split(separator: "-")[safe: 1], + if let lastUserID = SwiftfinStore.Defaults.suite[.lastServerUserID], let user = try? SwiftfinStore.dataStack.fetchOne(From(), - [Where("id == %@", userID)]) { + [Where("id == %@", lastUserID)]) { + + // TODO: Fetch for right queue // Strongly assuming that we didn't delete the server associate with the user guard let server = user.server, let accessToken = user.accessToken else { return } + guard let existingServer = SwiftfinStore.dataStack.fetchExisting(server) else { return } setAuthHeader(with: accessToken.value) - currentLogin = (server: server, user: user) + currentLogin = (server: existingServer, user: user) } } @@ -115,7 +117,14 @@ final class SessionManager { .handleEvents(receiveOutput: { [unowned self] (user, transaction) in setAuthHeader(with: user.accessToken?.value ?? "") try? transaction.commitAndWait() - currentLogin = (server: server, user: user) + + // Fetch for the right queue + let currentServer = SwiftfinStore.dataStack.fetchExisting(server)! + let currentUser = SwiftfinStore.dataStack.fetchExisting(user)! + + SwiftfinStore.Defaults.suite[.lastServerUserID] = user.id + + currentLogin = (server: currentServer, user: currentUser) }) .map({ (user, _) in return user @@ -123,6 +132,10 @@ final class SessionManager { .eraseToAnyPublisher() } + func logout() { + // TODO: todo + } + private func setAuthHeader(with accessToken: String) { let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String var deviceName = UIDevice.current.name diff --git a/Shared/ViewModels/ConnectToServerViewModel.swift b/Shared/ViewModels/ConnectToServerViewModel.swift index d0479cbe..474018fe 100644 --- a/Shared/ViewModels/ConnectToServerViewModel.swift +++ b/Shared/ViewModels/ConnectToServerViewModel.swift @@ -14,7 +14,7 @@ import Stinsen final class ConnectToServerViewModel: ViewModel { - @RouterObject var main: MainCoordinator.Router? + @RouterObject var router: ConnectToServerCoodinator.Router? @Published var discoveredServers: Set = [] @Published var searching = false private let discovery = ServerDiscovery() @@ -33,8 +33,9 @@ final class ConnectToServerViewModel: ViewModel { .sink(receiveCompletion: { completion in self.handleAPIRequestError(displayMessage: "Unable to connect to server.", logLevel: .critical, tag: "connectToServer", completion: completion) - }, receiveValue: { _ in + }, receiveValue: { server in LogManager.shared.log.debug("Connected to server at \"\(uri)\"", tag: "connectToServer") + self.router?.route(to: \.userLogin, server) }) .store(in: &cancellables) } diff --git a/Shared/ViewModels/SplashViewModel.swift b/Shared/ViewModels/SplashViewModel.swift index dbbe9a56..22d2c308 100644 --- a/Shared/ViewModels/SplashViewModel.swift +++ b/Shared/ViewModels/SplashViewModel.swift @@ -16,12 +16,13 @@ import UIKit import WidgetKit #endif +// TODO: Remove SplashViewModel + final class SplashViewModel: ViewModel { @Published var isLoggedIn: Bool = false override init() { - // TODO: Remove SplashViewModel isLoggedIn = SessionManager.main.currentLogin != nil super.init() diff --git a/Shared/ViewModels/UserLoginViewModel.swift b/Shared/ViewModels/UserLoginViewModel.swift index 103d884c..c177bcfe 100644 --- a/Shared/ViewModels/UserLoginViewModel.swift +++ b/Shared/ViewModels/UserLoginViewModel.swift @@ -7,16 +7,19 @@ * Copyright 2021 Aiden Vigue & Jellyfin Contributors */ +import CoreStore import Foundation import JellyfinAPI import Stinsen final class UserLoginViewModel: ViewModel { + @RouterObject var router: UserLoginCoordinator.Router? let server: SwiftfinStore.Models.Server init(server: SwiftfinStore.Models.Server) { - self.server = server + // Need to fetch for this context + self.server = SwiftfinStore.dataStack.fetchExisting(server)! } func login(username: String, password: String) { @@ -29,19 +32,8 @@ final class UserLoginViewModel: ViewModel { self.handleAPIRequestError(displayMessage: "Unable to connect to server.", logLevel: .critical, tag: "login", completion: completion) } receiveValue: { user in - print(user) + SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignIn, object: nil) } .store(in: &cancellables) -// -// -// SessionManager.current.login(username: username, password: password) -// .trackActivity(loading) -// .sink(receiveCompletion: { completion in -// self.handleAPIRequestError(displayMessage: "Unable to connect to server.", logLevel: .critical, tag: "login", -// completion: completion) -// }, receiveValue: { [weak self] _ in -// self?.main?.root(\.mainTab) -// }) -// .store(in: &cancellables) } }