iOS/iPadOS - User List Images (#586)
This commit is contained in:
parent
93c19dbe54
commit
fb38394a43
|
@ -154,9 +154,3 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
SettingsView(viewModel: SettingsViewModel(server: .sample, user: .sample))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -409,6 +409,7 @@
|
||||||
E18E023C288749540022598C /* UIScrollViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18E0239288749540022598C /* UIScrollViewExtensions.swift */; };
|
E18E023C288749540022598C /* UIScrollViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18E0239288749540022598C /* UIScrollViewExtensions.swift */; };
|
||||||
E19169CE272514760085832A /* HTTPScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19169CD272514760085832A /* HTTPScheme.swift */; };
|
E19169CE272514760085832A /* HTTPScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19169CD272514760085832A /* HTTPScheme.swift */; };
|
||||||
E19169CF272514760085832A /* HTTPScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19169CD272514760085832A /* HTTPScheme.swift */; };
|
E19169CF272514760085832A /* HTTPScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19169CD272514760085832A /* HTTPScheme.swift */; };
|
||||||
|
E192608028D28AAD002314B4 /* UserProfileButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E192607F28D28AAD002314B4 /* UserProfileButton.swift */; };
|
||||||
E1937A3B288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */; };
|
E1937A3B288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */; };
|
||||||
E1937A3C288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */; };
|
E1937A3C288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */; };
|
||||||
E1937A3E288F0D3D00CB80AA /* UIScreenExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3D288F0D3D00CB80AA /* UIScreenExtensions.swift */; };
|
E1937A3E288F0D3D00CB80AA /* UIScreenExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3D288F0D3D00CB80AA /* UIScreenExtensions.swift */; };
|
||||||
|
@ -893,6 +894,7 @@
|
||||||
E18E0203288749200022598C /* BlurView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurView.swift; sourceTree = "<group>"; };
|
E18E0203288749200022598C /* BlurView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurView.swift; sourceTree = "<group>"; };
|
||||||
E18E0239288749540022598C /* UIScrollViewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollViewExtensions.swift; sourceTree = "<group>"; };
|
E18E0239288749540022598C /* UIScrollViewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollViewExtensions.swift; sourceTree = "<group>"; };
|
||||||
E19169CD272514760085832A /* HTTPScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPScheme.swift; sourceTree = "<group>"; };
|
E19169CD272514760085832A /* HTTPScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPScheme.swift; sourceTree = "<group>"; };
|
||||||
|
E192607F28D28AAD002314B4 /* UserProfileButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileButton.swift; sourceTree = "<group>"; };
|
||||||
E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseItemDto+Images.swift"; sourceTree = "<group>"; };
|
E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseItemDto+Images.swift"; sourceTree = "<group>"; };
|
||||||
E1937A3D288F0D3D00CB80AA /* UIScreenExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScreenExtensions.swift; sourceTree = "<group>"; };
|
E1937A3D288F0D3D00CB80AA /* UIScreenExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScreenExtensions.swift; sourceTree = "<group>"; };
|
||||||
E1937A60288F32DB00CB80AA /* Poster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Poster.swift; sourceTree = "<group>"; };
|
E1937A60288F32DB00CB80AA /* Poster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Poster.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1500,6 +1502,7 @@
|
||||||
E1AA331C2782541500F6439C /* PrimaryButton.swift */,
|
E1AA331C2782541500F6439C /* PrimaryButton.swift */,
|
||||||
E18E01A4288746AF0022598C /* RefreshableScrollView.swift */,
|
E18E01A4288746AF0022598C /* RefreshableScrollView.swift */,
|
||||||
E1D3043428D1763100587289 /* SeeAllButton.swift */,
|
E1D3043428D1763100587289 /* SeeAllButton.swift */,
|
||||||
|
E192607F28D28AAD002314B4 /* UserProfileButton.swift */,
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2920,6 +2923,7 @@
|
||||||
E13DD4022717EE79009D4DAF /* UserListCoordinator.swift in Sources */,
|
E13DD4022717EE79009D4DAF /* UserListCoordinator.swift in Sources */,
|
||||||
E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */,
|
E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */,
|
||||||
53EE24E6265060780068F029 /* SearchView.swift in Sources */,
|
53EE24E6265060780068F029 /* SearchView.swift in Sources */,
|
||||||
|
E192608028D28AAD002314B4 /* UserProfileButton.swift in Sources */,
|
||||||
625CB5752678C33500530A6E /* MediaViewModel.swift in Sources */,
|
625CB5752678C33500530A6E /* MediaViewModel.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
"location" : "https://github.com/LePips/CollectionView",
|
"location" : "https://github.com/LePips/CollectionView",
|
||||||
"state" : {
|
"state" : {
|
||||||
"branch" : "main",
|
"branch" : "main",
|
||||||
"revision" : "1dbf31d860626f8debdbb08201517a4684d226c6"
|
"revision" : "b05ad718700cc99a4b88009ede6cf04c7326cd99"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// 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 UserProfileButton: View {
|
||||||
|
|
||||||
|
let user: UserDto
|
||||||
|
private var action: () -> Void
|
||||||
|
|
||||||
|
init(user: UserDto) {
|
||||||
|
self.user = user
|
||||||
|
self.action = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(user: SwiftfinStore.State.User) {
|
||||||
|
self.init(user: .init(name: user.username, id: user.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .center) {
|
||||||
|
Button {
|
||||||
|
action()
|
||||||
|
} label: {
|
||||||
|
ImageView(user.profileImageSource(maxWidth: 120, maxHeight: 120))
|
||||||
|
.failure {
|
||||||
|
ZStack {
|
||||||
|
Color.secondarySystemFill
|
||||||
|
.opacity(0.5)
|
||||||
|
|
||||||
|
Image(systemName: "person.fill")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 60, height: 60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
.frame(width: 120, height: 120)
|
||||||
|
|
||||||
|
Text(user.name ?? .emptyDash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UserProfileButton {
|
||||||
|
func onSelect(_ action: @escaping () -> Void) -> Self {
|
||||||
|
var copy = self
|
||||||
|
copy.action = action
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CollectionView
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct UserListView: View {
|
struct UserListView: View {
|
||||||
|
@ -15,44 +16,6 @@ struct UserListView: View {
|
||||||
@ObservedObject
|
@ObservedObject
|
||||||
var viewModel: UserListViewModel
|
var viewModel: UserListViewModel
|
||||||
|
|
||||||
private var listView: some View {
|
|
||||||
ScrollView {
|
|
||||||
LazyVStack {
|
|
||||||
ForEach(viewModel.users, id: \.id) { user in
|
|
||||||
Button {
|
|
||||||
viewModel.signIn(user: user)
|
|
||||||
} label: {
|
|
||||||
ZStack(alignment: Alignment.leading) {
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(Color(UIColor.secondarySystemFill))
|
|
||||||
.frame(height: 50)
|
|
||||||
.cornerRadius(10)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Text(user.username)
|
|
||||||
.font(.title2)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if viewModel.isLoading {
|
|
||||||
ProgressView()
|
|
||||||
}
|
|
||||||
}.padding(.leading)
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
.contextMenu {
|
|
||||||
Button(role: .destructive) {
|
|
||||||
viewModel.remove(user: user)
|
|
||||||
} label: {
|
|
||||||
Label(L10n.remove, systemImage: "trash")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var noUserView: some View {
|
private var noUserView: some View {
|
||||||
VStack {
|
VStack {
|
||||||
L10n.signInGetStarted.text
|
L10n.signInGetStarted.text
|
||||||
|
@ -68,44 +31,59 @@ struct UserListView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var innerBody: some View {
|
private var gridView: some View {
|
||||||
if viewModel.users.isEmpty {
|
CollectionView(items: viewModel.users) { _, user, _ in
|
||||||
noUserView
|
UserProfileButton(user: user)
|
||||||
.offset(y: -50)
|
.onSelect {
|
||||||
} else {
|
viewModel.signIn(user: user)
|
||||||
listView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var toolbarContent: some View {
|
|
||||||
HStack {
|
|
||||||
Button {
|
|
||||||
userListRouter.route(to: \.serverDetail, viewModel.server)
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "info.circle.fill")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !viewModel.users.isEmpty {
|
|
||||||
Button {
|
|
||||||
userListRouter.route(to: \.userSignIn, viewModel.server)
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "person.crop.circle.fill.badge.plus")
|
|
||||||
}
|
}
|
||||||
}
|
.contextMenu {
|
||||||
|
Button(role: .destructive) {
|
||||||
|
viewModel.remove(user: user)
|
||||||
|
} label: {
|
||||||
|
Label(L10n.remove, systemImage: "trash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.layout { _, layoutEnvironment in
|
||||||
|
.grid(
|
||||||
|
layoutEnvironment: layoutEnvironment,
|
||||||
|
layoutMode: .adaptive(withMinItemSize: 120),
|
||||||
|
itemSpacing: 30,
|
||||||
|
lineSpacing: 30
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
innerBody
|
Group {
|
||||||
.navigationTitle(viewModel.server.name)
|
if viewModel.users.isEmpty {
|
||||||
.toolbar {
|
noUserView
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
.offset(y: -50)
|
||||||
toolbarContent
|
} else {
|
||||||
|
gridView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(viewModel.server.name)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||||
|
if !viewModel.users.isEmpty {
|
||||||
|
Button {
|
||||||
|
userListRouter.route(to: \.userSignIn, viewModel.server)
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "person.crop.circle.fill.badge.plus")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
userListRouter.route(to: \.serverDetail, viewModel.server)
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "info.circle.fill")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
}
|
||||||
viewModel.fetchUsers()
|
.onAppear {
|
||||||
}
|
viewModel.fetchUsers()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue