From 368ac68005bc3c7246194bc4d23323894f5071ce Mon Sep 17 00:00:00 2001 From: PangMo5 Date: Sat, 19 Jun 2021 18:09:25 +0900 Subject: [PATCH] Remove @Published used for input(keyboard) binding (side effect) --- JellyfinPlayer tvOS/ConnectToServerView.swift | 37 +++++++++++++------ JellyfinPlayer/ConnectToServerView.swift | 34 ++++++++++++----- JellyfinPlayer/LibrarySearchView.swift | 8 +++- .../ViewModels/ConnectToServerViewModel.swift | 14 +++---- .../ViewModels/LibrarySearchViewModel.swift | 5 +-- 5 files changed, 65 insertions(+), 33 deletions(-) diff --git a/JellyfinPlayer tvOS/ConnectToServerView.swift b/JellyfinPlayer tvOS/ConnectToServerView.swift index 3c5383c5..8e5f3939 100644 --- a/JellyfinPlayer tvOS/ConnectToServerView.swift +++ b/JellyfinPlayer tvOS/ConnectToServerView.swift @@ -10,14 +10,20 @@ import SwiftUI struct ConnectToServerView: View { @StateObject var viewModel = ConnectToServerViewModel() + @State + var username = "" + @State + var password = "" + @State + var uri = "" var body: some View { VStack(alignment: .leading) { if viewModel.isConnectedServer { if viewModel.publicUsers.isEmpty { - Section(header: Text(viewModel.lastPublicUsers.isEmpty || viewModel.username == "" ? "Login to \(ServerEnvironment.current.server.name ?? "")": "")) { - if viewModel.lastPublicUsers.isEmpty || viewModel.username == "" { - TextField("Username", text: $viewModel.username) + Section(header: Text(viewModel.lastPublicUsers.isEmpty || username == "" ? "Login to \(ServerEnvironment.current.server.name ?? "")": "")) { + if viewModel.lastPublicUsers.isEmpty || username == "" { + TextField("Username", text: $username) .disableAutocorrection(true) .autocapitalization(.none) } else { @@ -30,7 +36,7 @@ struct ConnectToServerView: View { } } - SecureField("Password (optional)", text: $viewModel.password) + SecureField("Password (optional)", text: $password) .disableAutocorrection(true) .autocapitalization(.none) } @@ -39,7 +45,7 @@ struct ConnectToServerView: View { HStack { Button { if !viewModel.lastPublicUsers.isEmpty { - viewModel.username = "" + username = "" viewModel.showPublicUsers() } else { viewModel.isConnectedServer = false @@ -62,7 +68,7 @@ struct ConnectToServerView: View { Text("Login") } Spacer() - }.disabled(viewModel.isLoading || viewModel.username.isEmpty) + }.disabled(viewModel.isLoading || username.isEmpty) } } } else { @@ -74,11 +80,11 @@ struct ConnectToServerView: View { let user = SessionManager.current.getSavedSession(userID: publicUser.id!) SessionManager.current.loginWithSavedSession(user: user) } else { - viewModel.username = publicUser.name ?? "" + username = publicUser.name ?? "" viewModel.selectedPublicUser = publicUser viewModel.hidePublicUsers() if !(publicUser.hasPassword ?? true) { - viewModel.password = "" + password = "" viewModel.login() } } @@ -92,7 +98,7 @@ struct ConnectToServerView: View { Spacer() Button { viewModel.hidePublicUsers() - viewModel.username = "" + username = "" } label: { Text("Other User").font(.headline).fontWeight(.semibold) } @@ -103,7 +109,7 @@ struct ConnectToServerView: View { } else { Form { Section(header: Text("Server Information")) { - TextField("Jellyfin Server URL", text: $viewModel.uri) + TextField("Jellyfin Server URL", text: $uri) .disableAutocorrection(true) .autocapitalization(.none) Button { @@ -117,7 +123,7 @@ struct ConnectToServerView: View { ProgressView() } } - .disabled(viewModel.isLoading || viewModel.uri.isEmpty) + .disabled(viewModel.isLoading || uri.isEmpty) } } } @@ -127,6 +133,15 @@ struct ConnectToServerView: View { .alert(item: $viewModel.errorMessage) { _ in Alert(title: Text("Error"), message: Text(viewModel.errorMessage ?? ""), dismissButton: .default(Text("Ok"))) } + .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) + } .navigationTitle(viewModel.isConnectedServer ? "Who's watching?" : "Connect to Jellyfin") } } diff --git a/JellyfinPlayer/ConnectToServerView.swift b/JellyfinPlayer/ConnectToServerView.swift index c176d9f2..a99d6ef1 100644 --- a/JellyfinPlayer/ConnectToServerView.swift +++ b/JellyfinPlayer/ConnectToServerView.swift @@ -11,7 +11,14 @@ import KeychainSwift import SwiftUI struct ConnectToServerView: View { - @StateObject var viewModel = ConnectToServerViewModel() + @StateObject + var viewModel = ConnectToServerViewModel() + @State + var username = "" + @State + var password = "" + @State + var uri = "" var body: some View { ZStack { @@ -19,10 +26,10 @@ struct ConnectToServerView: View { if viewModel.isConnectedServer { if viewModel.publicUsers.isEmpty { Section(header: Text("Login to \(ServerEnvironment.current.server.name ?? "")")) { - TextField("Username", text: $viewModel.username) + TextField("Username", text: $username) .disableAutocorrection(true) .autocapitalization(.none) - SecureField("Password", text: $viewModel.password) + SecureField("Password", text: $password) .disableAutocorrection(true) .autocapitalization(.none) Button { @@ -35,7 +42,7 @@ struct ConnectToServerView: View { ProgressView() } } - }.disabled(viewModel.isLoading || viewModel.username.isEmpty) + }.disabled(viewModel.isLoading || username.isEmpty) } Section { @@ -56,10 +63,10 @@ struct ConnectToServerView: View { ForEach(viewModel.publicUsers, id: \.id) { publicUser in HStack { Button(action: { - viewModel.username = publicUser.name ?? "" + username = publicUser.name ?? "" viewModel.publicUsers.removeAll() if !(publicUser.hasPassword ?? true) { - viewModel.password = "" + password = "" viewModel.login() } }) { @@ -88,7 +95,7 @@ struct ConnectToServerView: View { Section { Button { viewModel.publicUsers.removeAll() - viewModel.username = "" + username = "" } label: { HStack { Text("Other User").font(.subheadline).fontWeight(.semibold) @@ -106,7 +113,7 @@ struct ConnectToServerView: View { } } else { Section(header: Text("Server Information")) { - TextField("Jellyfin Server URL", text: $viewModel.uri) + TextField("Jellyfin Server URL", text: $uri) .disableAutocorrection(true) .autocapitalization(.none) Button { @@ -120,11 +127,20 @@ struct ConnectToServerView: View { ProgressView() } } - .disabled(viewModel.isLoading || viewModel.uri.isEmpty) + .disabled(viewModel.isLoading || uri.isEmpty) } } } } + .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("Error"), message: Text("message"), dismissButton: .default(Text("Try again"))) } diff --git a/JellyfinPlayer/LibrarySearchView.swift b/JellyfinPlayer/LibrarySearchView.swift index 454c0cea..589f0281 100644 --- a/JellyfinPlayer/LibrarySearchView.swift +++ b/JellyfinPlayer/LibrarySearchView.swift @@ -12,7 +12,8 @@ import SwiftUI struct LibrarySearchView: View { @StateObject var viewModel: LibrarySearchViewModel - + @State + var searchQuery = "" // MARK: tracks for grid @State @@ -25,7 +26,7 @@ struct LibrarySearchView: View { var body: some View { VStack { Spacer().frame(height: 6) - SearchBar(text: $viewModel.searchQuery) + SearchBar(text: $searchQuery) ZStack { ScrollView(.vertical) { if !viewModel.items.isEmpty { @@ -67,6 +68,9 @@ struct LibrarySearchView: View { } } } + .onChange(of: searchQuery) { query in + viewModel.searchQuerySubject.send(query) + } .navigationBarTitle("Search", displayMode: .inline) } } diff --git a/Shared/ViewModels/ConnectToServerViewModel.swift b/Shared/ViewModels/ConnectToServerViewModel.swift index f81cd91f..d652ccec 100644 --- a/Shared/ViewModels/ConnectToServerViewModel.swift +++ b/Shared/ViewModels/ConnectToServerViewModel.swift @@ -14,12 +14,10 @@ import JellyfinAPI final class ConnectToServerViewModel: ViewModel { @Published var isConnectedServer = false - @Published - var uri = "" - @Published - var username = "" - @Published - var password = "" + + var uriSubject = CurrentValueSubject("") + var usernameSubject = CurrentValueSubject("") + var passwordSubject = CurrentValueSubject("") @Published var lastPublicUsers = [UserDto]() @@ -57,7 +55,7 @@ final class ConnectToServerViewModel: ViewModel { } func connectToServer() { - ServerEnvironment.current.create(with: uri) + ServerEnvironment.current.create(with: uriSubject.value) .sink(receiveCompletion: { result in switch result { case let .failure(error): @@ -72,7 +70,7 @@ final class ConnectToServerViewModel: ViewModel { } func login() { - SessionManager.current.login(username: username, password: password) + SessionManager.current.login(username: usernameSubject.value, password: passwordSubject.value) .sink(receiveCompletion: { completion in self.handleAPIRequestCompletion(completion: completion) }, receiveValue: { _ in diff --git a/Shared/ViewModels/LibrarySearchViewModel.swift b/Shared/ViewModels/LibrarySearchViewModel.swift index 065b0e47..75c4f86b 100644 --- a/Shared/ViewModels/LibrarySearchViewModel.swift +++ b/Shared/ViewModels/LibrarySearchViewModel.swift @@ -15,15 +15,14 @@ final class LibrarySearchViewModel: ViewModel { @Published var items = [BaseItemDto]() - @Published - var searchQuery = "" + var searchQuerySubject = CurrentValueSubject("") var parentID: String? init(parentID: String?) { self.parentID = parentID super.init() - $searchQuery + searchQuerySubject .debounce(for: 0.25, scheduler: DispatchQueue.main) .sink(receiveValue: search(with:)) .store(in: &cancellables)