Add known users to login screen
This commit is contained in:
parent
70b3dfd693
commit
9250dc650d
|
@ -138,6 +138,8 @@ internal enum L10n {
|
||||||
internal static func jumpLengthSeconds(_ p1: UnsafePointer<CChar>) -> String {
|
internal static func jumpLengthSeconds(_ p1: UnsafePointer<CChar>) -> String {
|
||||||
return L10n.tr("Localizable", "jumpLengthSeconds", p1)
|
return L10n.tr("Localizable", "jumpLengthSeconds", p1)
|
||||||
}
|
}
|
||||||
|
/// Known users
|
||||||
|
internal static var knownUsers: String { return L10n.tr("Localizable", "knownUsers") }
|
||||||
/// Larger
|
/// Larger
|
||||||
internal static var larger: String { return L10n.tr("Localizable", "larger") }
|
internal static var larger: String { return L10n.tr("Localizable", "larger") }
|
||||||
/// Largest
|
/// Largest
|
||||||
|
|
|
@ -9,12 +9,16 @@
|
||||||
import CoreStore
|
import CoreStore
|
||||||
import Foundation
|
import Foundation
|
||||||
import Stinsen
|
import Stinsen
|
||||||
|
import JellyfinAPI
|
||||||
|
|
||||||
final class UserSignInViewModel: ViewModel {
|
final class UserSignInViewModel: ViewModel {
|
||||||
|
|
||||||
@RouterObject
|
@RouterObject
|
||||||
var router: UserSignInCoordinator.Router?
|
var router: UserSignInCoordinator.Router?
|
||||||
let server: SwiftfinStore.State.Server
|
let server: SwiftfinStore.State.Server
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var users: [UserDto] = []
|
||||||
|
|
||||||
init(server: SwiftfinStore.State.Server) {
|
init(server: SwiftfinStore.State.Server) {
|
||||||
self.server = server
|
self.server = server
|
||||||
|
@ -48,4 +52,20 @@ final class UserSignInViewModel: ViewModel {
|
||||||
|
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
}
|
||||||
|
}, receiveValue: { response in
|
||||||
|
self.users = response
|
||||||
|
})
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// 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 SwiftUI
|
||||||
|
import JellyfinAPI
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -249,6 +249,8 @@
|
||||||
62EC353226766849000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
|
62EC353226766849000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
|
||||||
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
|
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
|
||||||
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; };
|
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; };
|
||||||
|
631759CF2879DB6A00A621AD /* UserLoginCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631759CE2879DB6A00A621AD /* UserLoginCellView.swift */; };
|
||||||
|
631759D02879DB6A00A621AD /* UserLoginCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631759CE2879DB6A00A621AD /* UserLoginCellView.swift */; };
|
||||||
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
||||||
C400DB6A27FE894F007B65FE /* LiveTVChannelsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */; };
|
C400DB6A27FE894F007B65FE /* LiveTVChannelsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */; };
|
||||||
C400DB6B27FE8C97007B65FE /* LiveTVChannelItemElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */; };
|
C400DB6B27FE8C97007B65FE /* LiveTVChannelItemElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */; };
|
||||||
|
@ -743,6 +745,7 @@
|
||||||
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
|
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
|
||||||
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
|
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
|
||||||
62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
||||||
|
631759CE2879DB6A00A621AD /* UserLoginCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLoginCellView.swift; sourceTree = "<group>"; };
|
||||||
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
|
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
|
||||||
C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelsView.swift; sourceTree = "<group>"; };
|
C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelsView.swift; sourceTree = "<group>"; };
|
||||||
C400DB6C27FE8E65007B65FE /* LiveTVChannelItemWideElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelItemWideElement.swift; sourceTree = "<group>"; };
|
C400DB6C27FE8E65007B65FE /* LiveTVChannelItemWideElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelItemWideElement.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1804,6 +1807,7 @@
|
||||||
E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */,
|
E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */,
|
||||||
624C21742685CF60007F1390 /* SearchablePickerView.swift */,
|
624C21742685CF60007F1390 /* SearchablePickerView.swift */,
|
||||||
53DE4BD1267098F300739748 /* SearchBarView.swift */,
|
53DE4BD1267098F300739748 /* SearchBarView.swift */,
|
||||||
|
631759CE2879DB6A00A621AD /* UserLoginCellView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2256,6 +2260,7 @@
|
||||||
E1FCD08926C35A0D007C8DCF /* NetworkError.swift in Sources */,
|
E1FCD08926C35A0D007C8DCF /* NetworkError.swift in Sources */,
|
||||||
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
||||||
E17885A4278105170094FBCF /* SFSymbolButton.swift in Sources */,
|
E17885A4278105170094FBCF /* SFSymbolButton.swift in Sources */,
|
||||||
|
631759D02879DB6A00A621AD /* UserLoginCellView.swift in Sources */,
|
||||||
E13DD3ED27178A54009D4DAF /* UserSignInViewModel.swift in Sources */,
|
E13DD3ED27178A54009D4DAF /* UserSignInViewModel.swift in Sources */,
|
||||||
E14B4142279354770016CBE5 /* LocalizedLookup.swift in Sources */,
|
E14B4142279354770016CBE5 /* LocalizedLookup.swift in Sources */,
|
||||||
E1B59FD92786AE4600A5287E /* NextUpCard.swift in Sources */,
|
E1B59FD92786AE4600A5287E /* NextUpCard.swift in Sources */,
|
||||||
|
@ -2485,6 +2490,7 @@
|
||||||
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */,
|
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */,
|
||||||
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
||||||
E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */,
|
E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */,
|
||||||
|
631759CF2879DB6A00A621AD /* UserLoginCellView.swift in Sources */,
|
||||||
E1AA33222782648000F6439C /* OverlaySliderColor.swift in Sources */,
|
E1AA33222782648000F6439C /* OverlaySliderColor.swift in Sources */,
|
||||||
E1D4BF842719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
E1D4BF842719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
||||||
E14F7D0726DB36EF007C3AE6 /* ItemPortraitMainView.swift in Sources */,
|
E14F7D0726DB36EF007C3AE6 /* ItemPortraitMainView.swift in Sources */,
|
||||||
|
|
|
@ -19,7 +19,19 @@ struct UserSignInView: View {
|
||||||
private var password: String = ""
|
private var password: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
List {
|
||||||
|
#if !os(tvOS)
|
||||||
|
// DisclosureGroup not available on tvOS
|
||||||
|
if (viewModel.users.count > 0) {
|
||||||
|
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 {
|
Section {
|
||||||
TextField(L10n.username, text: $username)
|
TextField(L10n.username, text: $username)
|
||||||
|
@ -55,5 +67,6 @@ struct UserSignInView: View {
|
||||||
}
|
}
|
||||||
.navigationTitle(L10n.signIn)
|
.navigationTitle(L10n.signIn)
|
||||||
.navigationBarBackButtonHidden(viewModel.isLoading)
|
.navigationBarBackButtonHidden(viewModel.isLoading)
|
||||||
|
.onAppear(perform: viewModel.loadUsers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue