diff --git a/JellyfinPlayer tvOS/ConnectToServerView.swift b/JellyfinPlayer tvOS/ConnectToServerView.swift index 3c5383c5..8f0e0214 100644 --- a/JellyfinPlayer tvOS/ConnectToServerView.swift +++ b/JellyfinPlayer tvOS/ConnectToServerView.swift @@ -10,14 +10,17 @@ 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 +33,7 @@ struct ConnectToServerView: View { } } - SecureField("Password (optional)", text: $viewModel.password) + SecureField("Password (optional)", text: $password) .disableAutocorrection(true) .autocapitalization(.none) } @@ -39,7 +42,7 @@ struct ConnectToServerView: View { HStack { Button { if !viewModel.lastPublicUsers.isEmpty { - viewModel.username = "" + username = "" viewModel.showPublicUsers() } else { viewModel.isConnectedServer = false @@ -62,7 +65,7 @@ struct ConnectToServerView: View { Text("Login") } Spacer() - }.disabled(viewModel.isLoading || viewModel.username.isEmpty) + }.disabled(viewModel.isLoading || username.isEmpty) } } } else { @@ -74,11 +77,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 +95,7 @@ struct ConnectToServerView: View { Spacer() Button { viewModel.hidePublicUsers() - viewModel.username = "" + username = "" } label: { Text("Other User").font(.headline).fontWeight(.semibold) } @@ -103,7 +106,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 +120,7 @@ struct ConnectToServerView: View { ProgressView() } } - .disabled(viewModel.isLoading || viewModel.uri.isEmpty) + .disabled(viewModel.isLoading || uri.isEmpty) } } } @@ -127,6 +130,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 tvOS/LatestMediaView.swift b/JellyfinPlayer tvOS/LatestMediaView.swift index 7b661f87..ad58190c 100644 --- a/JellyfinPlayer tvOS/LatestMediaView.swift +++ b/JellyfinPlayer tvOS/LatestMediaView.swift @@ -11,8 +11,7 @@ import Combine struct LatestMediaView: View { - @StateObject - var tempViewModel = ViewModel() + @StateObject var tempViewModel = ViewModel() @State var items: [BaseItemDto] = [] private var library_id: String = "" @State private var viewDidLoad: Bool = false diff --git a/JellyfinPlayer/ConnectToServerView.swift b/JellyfinPlayer/ConnectToServerView.swift index c176d9f2..4abc06b2 100644 --- a/JellyfinPlayer/ConnectToServerView.swift +++ b/JellyfinPlayer/ConnectToServerView.swift @@ -12,6 +12,9 @@ import SwiftUI struct ConnectToServerView: View { @StateObject var viewModel = ConnectToServerViewModel() + @State var username = "" + @State var password = "" + @State var uri = "" var body: some View { ZStack { @@ -19,10 +22,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 +38,7 @@ struct ConnectToServerView: View { ProgressView() } } - }.disabled(viewModel.isLoading || viewModel.username.isEmpty) + }.disabled(viewModel.isLoading || username.isEmpty) } Section { @@ -56,10 +59,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 +91,7 @@ struct ConnectToServerView: View { Section { Button { viewModel.publicUsers.removeAll() - viewModel.username = "" + username = "" } label: { HStack { Text("Other User").font(.subheadline).fontWeight(.semibold) @@ -106,7 +109,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 +123,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/EpisodeItemView.swift b/JellyfinPlayer/EpisodeItemView.swift index 413058e5..38fc44bc 100644 --- a/JellyfinPlayer/EpisodeItemView.swift +++ b/JellyfinPlayer/EpisodeItemView.swift @@ -10,8 +10,7 @@ import JellyfinAPI import Combine struct EpisodeItemView: View { - @StateObject - var viewModel: EpisodeItemViewModel + @StateObject var viewModel: EpisodeItemViewModel @State private var orientation = UIDeviceOrientation.unknown @Environment(\.horizontalSizeClass) var hSizeClass @Environment(\.verticalSizeClass) var vSizeClass diff --git a/JellyfinPlayer/HomeView.swift b/JellyfinPlayer/HomeView.swift index 779a7389..824c97b1 100644 --- a/JellyfinPlayer/HomeView.swift +++ b/JellyfinPlayer/HomeView.swift @@ -11,16 +11,11 @@ import Foundation import SwiftUI struct HomeView: View { - @StateObject - var viewModel = HomeViewModel() - @State - private var orientation = UIDevice.current.orientation - @Environment(\.horizontalSizeClass) - var hSizeClass - @Environment(\.verticalSizeClass) - var vSizeClass - @State - var showingSettings = false + @StateObject var viewModel = HomeViewModel() + @State private var orientation = UIDevice.current.orientation + @Environment(\.horizontalSizeClass) var hSizeClass + @Environment(\.verticalSizeClass) var vSizeClass + @State var showingSettings = false var body: some View { ZStack { diff --git a/JellyfinPlayer/LatestMediaView.swift b/JellyfinPlayer/LatestMediaView.swift index b4760031..740524ce 100644 --- a/JellyfinPlayer/LatestMediaView.swift +++ b/JellyfinPlayer/LatestMediaView.swift @@ -10,8 +10,7 @@ import JellyfinAPI import SwiftUI struct LatestMediaView: View { - @StateObject - var viewModel: LatestMediaViewModel + @StateObject var viewModel: LatestMediaViewModel var body: some View { ScrollView(.horizontal, showsIndicators: false) { diff --git a/JellyfinPlayer/LibraryFilterView.swift b/JellyfinPlayer/LibraryFilterView.swift index 2720692d..4a9db41f 100644 --- a/JellyfinPlayer/LibraryFilterView.swift +++ b/JellyfinPlayer/LibraryFilterView.swift @@ -9,13 +9,10 @@ import JellyfinAPI import SwiftUI struct LibraryFilterView: View { - @Environment(\.presentationMode) - var presentationMode - @Binding - var filters: LibraryFilters + @Environment(\.presentationMode) var presentationMode + @Binding var filters: LibraryFilters - @StateObject - var viewModel: LibraryFilterViewModel + @StateObject var viewModel: LibraryFilterViewModel init(filters: Binding, enabledFilterType: [FilterType]) { _filters = filters diff --git a/JellyfinPlayer/LibraryListView.swift b/JellyfinPlayer/LibraryListView.swift index 0fecac3e..a4afa775 100644 --- a/JellyfinPlayer/LibraryListView.swift +++ b/JellyfinPlayer/LibraryListView.swift @@ -9,8 +9,7 @@ import Foundation import SwiftUI struct LibraryListView: View { - @StateObject - var viewModel = LibraryListViewModel() + @StateObject var viewModel = LibraryListViewModel() var body: some View { List(viewModel.libraries, id: \.self) { library in diff --git a/JellyfinPlayer/LibrarySearchView.swift b/JellyfinPlayer/LibrarySearchView.swift index 454c0cea..465c48f0 100644 --- a/JellyfinPlayer/LibrarySearchView.swift +++ b/JellyfinPlayer/LibrarySearchView.swift @@ -10,13 +10,11 @@ import JellyfinAPI import SwiftUI struct LibrarySearchView: View { - @StateObject - var viewModel: LibrarySearchViewModel - + @StateObject var viewModel: LibrarySearchViewModel + @State var searchQuery = "" // MARK: tracks for grid - @State - private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) + @State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) func recalcTracks() { tracks = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) @@ -25,7 +23,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 +65,9 @@ struct LibrarySearchView: View { } } } + .onChange(of: searchQuery) { query in + viewModel.searchQuerySubject.send(query) + } .navigationBarTitle("Search", displayMode: .inline) } } diff --git a/JellyfinPlayer/LibraryView.swift b/JellyfinPlayer/LibraryView.swift index e6ffec52..c9673c46 100644 --- a/JellyfinPlayer/LibraryView.swift +++ b/JellyfinPlayer/LibraryView.swift @@ -12,19 +12,15 @@ import NukeUI import SwiftUI struct LibraryView: View { - @StateObject - var viewModel: LibraryViewModel + @StateObject var viewModel: LibraryViewModel var title: String // MARK: tracks for grid - @State - var isShowingSearchView = false - @State - var isShowingFilterView = false + @State var isShowingSearchView = false + @State var isShowingFilterView = false - @State - private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) + @State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) func recalcTracks() { tracks = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) diff --git a/JellyfinPlayer/MovieItemView.swift b/JellyfinPlayer/MovieItemView.swift index 98e387cc..f9e917f2 100644 --- a/JellyfinPlayer/MovieItemView.swift +++ b/JellyfinPlayer/MovieItemView.swift @@ -10,10 +10,8 @@ import JellyfinAPI import SwiftUI struct MovieItemView: View { - @StateObject - var viewModel: MovieItemViewModel - @State - private var orientation = UIDeviceOrientation.unknown + @StateObject var viewModel: MovieItemViewModel + @State private var orientation = UIDeviceOrientation.unknown @Environment(\.horizontalSizeClass) var hSizeClass @Environment(\.verticalSizeClass) diff --git a/JellyfinPlayer/SeasonItemView.swift b/JellyfinPlayer/SeasonItemView.swift index d69c5fcf..d436f603 100644 --- a/JellyfinPlayer/SeasonItemView.swift +++ b/JellyfinPlayer/SeasonItemView.swift @@ -10,8 +10,7 @@ import Combine import JellyfinAPI struct SeasonItemView: View { - @StateObject - var viewModel: SeasonItemViewModel + @StateObject var viewModel: SeasonItemViewModel @State private var orientation = UIDeviceOrientation.unknown @Environment(\.horizontalSizeClass) var hSizeClass @Environment(\.verticalSizeClass) var vSizeClass diff --git a/JellyfinPlayer/SeriesItemView.swift b/JellyfinPlayer/SeriesItemView.swift index f7276db8..d7dbd0e6 100644 --- a/JellyfinPlayer/SeriesItemView.swift +++ b/JellyfinPlayer/SeriesItemView.swift @@ -10,11 +10,9 @@ import JellyfinAPI import Combine struct SeriesItemView: View { - @StateObject - var viewModel: SeriesItemViewModel + @StateObject var viewModel: SeriesItemViewModel - @State - private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) + @State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) func recalcTracks() { tracks = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125) 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)