Merge pull request #78 from PangMo5/PangMo5/binding-side-effect
Remove @Published used for input(keyboard) binding (side effect)
This commit is contained in:
commit
31ba7a07d8
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<LibraryFilters>, enabledFilterType: [FilterType]) {
|
||||
_filters = filters
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<String, Never>("")
|
||||
var usernameSubject = CurrentValueSubject<String, Never>("")
|
||||
var passwordSubject = CurrentValueSubject<String, Never>("")
|
||||
|
||||
@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
|
||||
|
|
|
@ -15,15 +15,14 @@ final class LibrarySearchViewModel: ViewModel {
|
|||
@Published
|
||||
var items = [BaseItemDto]()
|
||||
|
||||
@Published
|
||||
var searchQuery = ""
|
||||
var searchQuerySubject = CurrentValueSubject<String, Never>("")
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue