diff --git a/Shared/Generated/Strings.swift b/Shared/Generated/Strings.swift index 62f1ced4..301d74ef 100644 --- a/Shared/Generated/Strings.swift +++ b/Shared/Generated/Strings.swift @@ -138,8 +138,6 @@ internal enum L10n { internal static func jumpLengthSeconds(_ p1: UnsafePointer) -> String { return L10n.tr("Localizable", "jumpLengthSeconds", p1) } - /// Known users - internal static var knownUsers: String { return L10n.tr("Localizable", "knownUsers") } /// Larger internal static var larger: String { return L10n.tr("Localizable", "larger") } /// Largest @@ -260,6 +258,8 @@ internal enum L10n { internal static var previousItem: String { return L10n.tr("Localizable", "previousItem") } /// Programs internal static var programs: String { return L10n.tr("Localizable", "programs") } + /// Public users + internal static var publicUsers: String { return L10n.tr("Localizable", "publicUsers") } /// Rated internal static var rated: String { return L10n.tr("Localizable", "rated") } /// Recently Added diff --git a/Shared/ViewModels/UserSignInViewModel.swift b/Shared/ViewModels/UserSignInViewModel.swift index 9b8c1560..5bc4ca56 100644 --- a/Shared/ViewModels/UserSignInViewModel.swift +++ b/Shared/ViewModels/UserSignInViewModel.swift @@ -18,7 +18,7 @@ final class UserSignInViewModel: ViewModel { let server: SwiftfinStore.State.Server @Published - var users: [UserDto] = [] + var publicUsers: [UserDto] = [] init(server: SwiftfinStore.State.Server) { self.server = server @@ -54,18 +54,21 @@ final class UserSignInViewModel: ViewModel { } func loadUsers() { - // TODO: this is a hack JellyfinAPIAPI.basePath = server.currentURI UserAPI.getPublicUsers() .sink(receiveCompletion: { completion in - switch completion { - case .finished: () - case .failure: - self.users = [] - } + self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion) }, receiveValue: { response in - self.users = response + self.publicUsers = response }) .store(in: &cancellables) } + + func getProfileImageUrl(user: UserDto) -> URL? { + let urlString = ImageAPI.getUserImageWithRequestBuilder(userId: user.id ?? "--", + imageType: .primary, + width: 200, + quality: 90).URLString + return URL(string: urlString) + } } diff --git a/Shared/Views/UserLoginCellView.swift b/Shared/Views/UserLoginCellView.swift deleted file mode 100644 index 4441250f..00000000 --- a/Shared/Views/UserLoginCellView.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// 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 (c) 2022 Jellyfin & Jellyfin Contributors -// - -import JellyfinAPI -import SwiftUI - -struct UserLoginCellView: View { - - @State - private var expanded = false - - @State - private var enteredPassword: String = "" - - var user: UserDto - var baseURL: String? - var loginTapped: (String, String) -> Void - var cancelTapped: () -> Void - - var body: some View { - DisclosureGroup { - VStack(alignment: .leading, spacing: 16) { - SecureField(L10n.password, text: $enteredPassword) - Button { - loginTapped(user.name ?? "", enteredPassword) - } label: { - L10n.signIn.text - } - } - .padding(.leading, -16) - } label: { - HStack(spacing: 4.0) { - AsyncImage(url: getProfileImageUrl(), - content: { image in - image.resizable() - .aspectRatio(contentMode: .fit) - .frame(maxWidth: 50, maxHeight: 50) - .clipShape(Circle()) - }, - placeholder: { - Image(systemName: "person.circle") - .resizable() - .font(.system(size: 40)) - .scaledToFit() - .frame(maxWidth: 50, maxHeight: 50) - }) - .padding(.vertical, 4.0) - - Text(user.name ?? "") - .padding(.leading, 4.0) - Spacer() - } - } - } - - func getProfileImageUrl() -> URL? { - if let userId = user.id, let imageTag = user.primaryImageTag, let server = baseURL { - let url = URL(string: "\(server)/Users/\(userId)/Images/Primary?width=200&tag=\(imageTag)&quality=90") - LogManager.log.debug(url?.absoluteString ?? "") - return url - } - return nil - } -} diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index a9883555..1162e948 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -247,7 +247,7 @@ 62EC353226766849000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; }; 62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; }; 62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; }; - 631759CF2879DB6A00A621AD /* UserLoginCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631759CE2879DB6A00A621AD /* UserLoginCellView.swift */; }; + 631759CF2879DB6A00A621AD /* PublicUserSignInCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */; }; 637FCAF4287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; 637FCAF5287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; }; @@ -743,7 +743,7 @@ 62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = ""; }; 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = ""; }; 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = ""; }; - 631759CE2879DB6A00A621AD /* UserLoginCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLoginCellView.swift; sourceTree = ""; }; + 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicUserSignInCellView.swift; sourceTree = ""; }; 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = UDPBroadcast.xcframework; path = Carthage/Build/UDPBroadcast.xcframework; sourceTree = ""; }; AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = ""; }; C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelsView.swift; sourceTree = ""; }; @@ -1669,6 +1669,7 @@ E1E5D54A2783E26100692DFE /* SettingsView */, E13DD3FB2717EAE8009D4DAF /* UserListView.swift */, E13DD3F4271793BB009D4DAF /* UserSignInView.swift */, + 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */, E193D5452719418B00900D82 /* VideoPlayer */, ); path = Views; @@ -1808,7 +1809,6 @@ E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */, 624C21742685CF60007F1390 /* SearchablePickerView.swift */, 53DE4BD1267098F300739748 /* SearchBarView.swift */, - 631759CE2879DB6A00A621AD /* UserLoginCellView.swift */, ); path = Views; sourceTree = ""; @@ -2488,7 +2488,7 @@ 6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */, 53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */, E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */, - 631759CF2879DB6A00A621AD /* UserLoginCellView.swift in Sources */, + 631759CF2879DB6A00A621AD /* PublicUserSignInCellView.swift in Sources */, E1AA33222782648000F6439C /* OverlaySliderColor.swift in Sources */, E1D4BF842719D25A00A11E64 /* TrackLanguage.swift in Sources */, E14F7D0726DB36EF007C3AE6 /* ItemPortraitMainView.swift in Sources */, diff --git a/Swiftfin/Views/PublicUserSignInCellView.swift b/Swiftfin/Views/PublicUserSignInCellView.swift new file mode 100644 index 00000000..5771ed24 --- /dev/null +++ b/Swiftfin/Views/PublicUserSignInCellView.swift @@ -0,0 +1,44 @@ +// +// 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 (c) 2022 Jellyfin & Jellyfin Contributors +// + +import JellyfinAPI +import SwiftUI + +struct UserLoginCellView: View { + + @ObservedObject + var viewModel: UserSignInViewModel + + @State + private var enteredPassword: String = "" + + var user: UserDto + + var body: some View { + DisclosureGroup { + SecureField(L10n.password, text: $enteredPassword) + Button { + viewModel.login(username: user.name ?? "--", password: enteredPassword) + } label: { + L10n.signIn.text + } + } label: { + HStack { + ImageView(viewModel.getProfileImageUrl(user: user)) { + Image(systemName: "person.circle") + .frame(width: 50, height: 50) + } + .frame(width: 50, height: 50) + .clipShape(Circle()) + + Text(user.name ?? "") + Spacer() + } + } + } +} diff --git a/Swiftfin/Views/UserSignInView.swift b/Swiftfin/Views/UserSignInView.swift index d5174f50..b31669b4 100644 --- a/Swiftfin/Views/UserSignInView.swift +++ b/Swiftfin/Views/UserSignInView.swift @@ -20,19 +20,6 @@ struct UserSignInView: View { var body: some View { List { - #if !os(tvOS) - // DisclosureGroup not available on tvOS - if !viewModel.users.isEmpty { - Section(header: L10n.knownUsers.text) { - ForEach(viewModel.users, id: \.id) { user in - UserLoginCellView(user: user, baseURL: viewModel.server.currentURI, loginTapped: viewModel.login, - cancelTapped: viewModel.cancelSignIn) - .disabled(viewModel.isLoading) - } - } - } - #endif - Section { TextField(L10n.username, text: $username) .disableAutocorrection(true) @@ -59,6 +46,15 @@ struct UserSignInView: View { } header: { L10n.signInToServer(viewModel.server.name).text } + + if !viewModel.publicUsers.isEmpty { + Section(header: L10n.publicUsers.text) { + ForEach(viewModel.publicUsers, id: \.id) { user in + UserLoginCellView(viewModel: viewModel, user: user) + .disabled(viewModel.isLoading) + } + } + } } .alert(item: $viewModel.errorMessage) { _ in Alert(title: Text(viewModel.alertTitle), diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index ad118a59..a23e6b50 100644 Binary files a/Translations/en.lproj/Localizable.strings and b/Translations/en.lproj/Localizable.strings differ