diff --git a/Shared/Generated/Strings.swift b/Shared/Generated/Strings.swift index a9fe5576..8b668a97 100644 --- a/Shared/Generated/Strings.swift +++ b/Shared/Generated/Strings.swift @@ -198,6 +198,8 @@ internal enum L10n { internal static var `none`: String { return L10n.tr("Localizable", "none") } /// No overview available internal static var noOverviewAvailable: String { return L10n.tr("Localizable", "noOverviewAvailable") } + /// No public Users + internal static var noPublicUsers: String { return L10n.tr("Localizable", "noPublicUsers") } /// No results. internal static var noResults: String { return L10n.tr("Localizable", "noResults") } /// Normal @@ -258,6 +260,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 a9b154dc..412ff59c 100644 --- a/Shared/ViewModels/UserSignInViewModel.swift +++ b/Shared/ViewModels/UserSignInViewModel.swift @@ -8,6 +8,7 @@ import CoreStore import Foundation +import JellyfinAPI import Stinsen final class UserSignInViewModel: ViewModel { @@ -16,6 +17,12 @@ final class UserSignInViewModel: ViewModel { var router: UserSignInCoordinator.Router? let server: SwiftfinStore.State.Server + @Published + var isLoadingUsers = false + + @Published + var publicUsers: [UserDto] = [] + init(server: SwiftfinStore.State.Server) { self.server = server } @@ -48,4 +55,25 @@ final class UserSignInViewModel: ViewModel { self.isLoading = false } + + func loadUsers() { + self.isLoadingUsers = true + JellyfinAPIAPI.basePath = server.currentURI + UserAPI.getPublicUsers() + .sink(receiveCompletion: { completion in + self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion) + }, receiveValue: { response in + self.publicUsers = response + self.isLoadingUsers = false + }) + .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/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index f9215cf2..1162e948 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -247,6 +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 /* 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 */; }; @@ -742,6 +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 /* 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 = ""; }; @@ -1667,6 +1669,7 @@ E1E5D54A2783E26100692DFE /* SettingsView */, E13DD3FB2717EAE8009D4DAF /* UserListView.swift */, E13DD3F4271793BB009D4DAF /* UserSignInView.swift */, + 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */, E193D5452719418B00900D82 /* VideoPlayer */, ); path = Views; @@ -2485,6 +2488,7 @@ 6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */, 53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */, E1E48CC9271E6D410021A2F9 /* RefreshHelper.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..184b270c --- /dev/null +++ b/Swiftfin/Views/PublicUserSignInCellView.swift @@ -0,0 +1,45 @@ +// +// 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") + .resizable() + .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 eec3f212..6c7dc759 100644 --- a/Swiftfin/Views/UserSignInView.swift +++ b/Swiftfin/Views/UserSignInView.swift @@ -19,8 +19,7 @@ struct UserSignInView: View { private var password: String = "" var body: some View { - Form { - + List { Section { TextField(L10n.username, text: $username) .disableAutocorrection(true) @@ -47,6 +46,35 @@ struct UserSignInView: View { } header: { L10n.signInToServer(viewModel.server.name).text } + + Section { + if !viewModel.publicUsers.isEmpty { + ForEach(viewModel.publicUsers, id: \.id) { user in + UserLoginCellView(viewModel: viewModel, user: user) + .disabled(viewModel.isLoading) + } + } else { + HStack(alignment: .center) { + Spacer() + L10n.noPublicUsers.text + .font(.callout) + .foregroundColor(.secondary) + Spacer() + } + } + } header: { + HStack { + L10n.publicUsers.text + Spacer() + Button { + viewModel.loadUsers() + } label: { + Image(systemName: "arrow.clockwise.circle.fill") + } + .disabled(viewModel.isLoadingUsers || viewModel.isLoading) + } + } + .headerProminence(.increased) } .alert(item: $viewModel.errorMessage) { _ in Alert(title: Text(viewModel.alertTitle), @@ -55,5 +83,6 @@ struct UserSignInView: View { } .navigationTitle(L10n.signIn) .navigationBarBackButtonHidden(viewModel.isLoading) + .onAppear(perform: viewModel.loadUsers) } } diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index c5e7a518..4471fd68 100644 Binary files a/Translations/en.lproj/Localizable.strings and b/Translations/en.lproj/Localizable.strings differ