Implement suggestions

This commit is contained in:
David Ullmer 2022-07-11 14:29:45 +02:00
parent e94d2795c7
commit 51d30cf60a
7 changed files with 70 additions and 96 deletions

View File

@ -138,8 +138,6 @@ 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
@ -260,6 +258,8 @@ internal enum L10n {
internal static var previousItem: String { return L10n.tr("Localizable", "previousItem") } internal static var previousItem: String { return L10n.tr("Localizable", "previousItem") }
/// Programs /// Programs
internal static var programs: String { return L10n.tr("Localizable", "programs") } internal static var programs: String { return L10n.tr("Localizable", "programs") }
/// Public users
internal static var publicUsers: String { return L10n.tr("Localizable", "publicUsers") }
/// Rated /// Rated
internal static var rated: String { return L10n.tr("Localizable", "rated") } internal static var rated: String { return L10n.tr("Localizable", "rated") }
/// Recently Added /// Recently Added

View File

@ -18,7 +18,7 @@ final class UserSignInViewModel: ViewModel {
let server: SwiftfinStore.State.Server let server: SwiftfinStore.State.Server
@Published @Published
var users: [UserDto] = [] var publicUsers: [UserDto] = []
init(server: SwiftfinStore.State.Server) { init(server: SwiftfinStore.State.Server) {
self.server = server self.server = server
@ -54,18 +54,21 @@ final class UserSignInViewModel: ViewModel {
} }
func loadUsers() { func loadUsers() {
// TODO: this is a hack
JellyfinAPIAPI.basePath = server.currentURI JellyfinAPIAPI.basePath = server.currentURI
UserAPI.getPublicUsers() UserAPI.getPublicUsers()
.sink(receiveCompletion: { completion in .sink(receiveCompletion: { completion in
switch completion { self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion)
case .finished: ()
case .failure:
self.users = []
}
}, receiveValue: { response in }, receiveValue: { response in
self.users = response self.publicUsers = response
}) })
.store(in: &cancellables) .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)
}
} }

View File

@ -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
}
}

View File

@ -247,7 +247,7 @@
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 */; }; 631759CF2879DB6A00A621AD /* PublicUserSignInCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */; };
637FCAF4287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; 637FCAF4287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; };
637FCAF5287B5B2600C0A353 /* 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 */; }; 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 = "<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>"; }; 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicUserSignInCellView.swift; sourceTree = "<group>"; };
637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = UDPBroadcast.xcframework; path = Carthage/Build/UDPBroadcast.xcframework; sourceTree = "<group>"; }; 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = UDPBroadcast.xcframework; path = Carthage/Build/UDPBroadcast.xcframework; 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>"; };
@ -1669,6 +1669,7 @@
E1E5D54A2783E26100692DFE /* SettingsView */, E1E5D54A2783E26100692DFE /* SettingsView */,
E13DD3FB2717EAE8009D4DAF /* UserListView.swift */, E13DD3FB2717EAE8009D4DAF /* UserListView.swift */,
E13DD3F4271793BB009D4DAF /* UserSignInView.swift */, E13DD3F4271793BB009D4DAF /* UserSignInView.swift */,
631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */,
E193D5452719418B00900D82 /* VideoPlayer */, E193D5452719418B00900D82 /* VideoPlayer */,
); );
path = Views; path = Views;
@ -1808,7 +1809,6 @@
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>";
@ -2488,7 +2488,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 */, 631759CF2879DB6A00A621AD /* PublicUserSignInCellView.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 */,

View File

@ -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()
}
}
}
}

View File

@ -20,19 +20,6 @@ struct UserSignInView: View {
var body: some View { var body: some View {
List { 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 { Section {
TextField(L10n.username, text: $username) TextField(L10n.username, text: $username)
.disableAutocorrection(true) .disableAutocorrection(true)
@ -59,6 +46,15 @@ struct UserSignInView: View {
} header: { } header: {
L10n.signInToServer(viewModel.server.name).text 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(item: $viewModel.errorMessage) { _ in
Alert(title: Text(viewModel.alertTitle), Alert(title: Text(viewModel.alertTitle),