[iOS & tvOS] Fix Square Overlay Over Profiles (#1466)
* Fix Square over Circle * cleanup --------- Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
This commit is contained in:
parent
0de40a5788
commit
26ec19982e
|
@ -6,18 +6,19 @@
|
|||
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import Factory
|
||||
import JellyfinAPI
|
||||
import Nuke
|
||||
import SwiftUI
|
||||
|
||||
struct UserProfileImage<Placeholder: View>: View {
|
||||
|
||||
// MARK: - Inject Logger
|
||||
// MARK: - Environment Variables
|
||||
|
||||
@Injected(\.logService)
|
||||
private var logger
|
||||
@Environment(\.isEnabled)
|
||||
private var isEnabled
|
||||
@Environment(\.isEditing)
|
||||
private var isEditing
|
||||
@Environment(\.isSelected)
|
||||
private var isSelected
|
||||
|
||||
// MARK: - User Variables
|
||||
|
||||
|
@ -26,6 +27,17 @@ struct UserProfileImage<Placeholder: View>: View {
|
|||
private let pipeline: ImagePipeline
|
||||
private let placeholder: Placeholder
|
||||
|
||||
// MARK: - Overlay Opacity
|
||||
|
||||
private var overlayOpacity: Double {
|
||||
/// Dim the Profile Image if Editing & Unselected or if Disabled
|
||||
if (isEditing && !isSelected) || !isEnabled {
|
||||
return 0.5
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Body
|
||||
|
||||
var body: some View {
|
||||
|
@ -46,9 +58,12 @@ struct UserProfileImage<Placeholder: View>: View {
|
|||
.failure {
|
||||
placeholder
|
||||
}
|
||||
.posterShadow()
|
||||
.overlay {
|
||||
Color.black
|
||||
.opacity(overlayOpacity)
|
||||
}
|
||||
.aspectRatio(1, contentMode: .fill)
|
||||
.clipShape(Circle())
|
||||
.clipShape(.circle)
|
||||
.shadow(radius: 5)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,66 +52,51 @@ extension SelectUserView {
|
|||
return isSelected ? .primary : .secondary
|
||||
}
|
||||
|
||||
// MARK: - User Portrait
|
||||
|
||||
private var userPortrait: some View {
|
||||
UserProfileImage(
|
||||
userID: user.id,
|
||||
source: user.profileImageSource(
|
||||
client: server.client,
|
||||
maxWidth: 120
|
||||
),
|
||||
pipeline: .Swiftfin.local
|
||||
)
|
||||
.aspectRatio(1, contentMode: .fill)
|
||||
.clipShape(.circle)
|
||||
.overlay {
|
||||
if isEditing {
|
||||
ZStack(alignment: .bottom) {
|
||||
Color.black
|
||||
.opacity(isSelected ? 0 : 0.5)
|
||||
|
||||
if isSelected {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 75, height: 75)
|
||||
.symbolRenderingMode(.palette)
|
||||
.foregroundStyle(accentColor.overlayColor, accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Body
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Button {
|
||||
action()
|
||||
} label: {
|
||||
userPortrait
|
||||
.hoverEffect(.highlight)
|
||||
|
||||
Text(user.username)
|
||||
.font(.title3)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(labelForegroundStyle)
|
||||
.lineLimit(1)
|
||||
|
||||
if showServer {
|
||||
Text(server.name)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
Button(action: action) {
|
||||
UserProfileImage(
|
||||
userID: user.id,
|
||||
source: user.profileImageSource(
|
||||
client: server.client,
|
||||
maxWidth: 120
|
||||
),
|
||||
pipeline: .Swiftfin.local
|
||||
)
|
||||
.overlay(alignment: .bottom) {
|
||||
if isEditing && isSelected {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 75, height: 75)
|
||||
.symbolRenderingMode(.palette)
|
||||
.foregroundStyle(accentColor.overlayColor, accentColor)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.buttonBorderShape(.circle)
|
||||
.contextMenu {
|
||||
Button(L10n.delete, role: .destructive) {
|
||||
onDelete()
|
||||
}
|
||||
.hoverEffect(.highlight)
|
||||
|
||||
Text(user.username)
|
||||
.font(.title3)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(labelForegroundStyle)
|
||||
.lineLimit(1)
|
||||
|
||||
if showServer {
|
||||
Text(server.name)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.buttonBorderShape(.circle)
|
||||
.contextMenu {
|
||||
if !isEditing {
|
||||
Button(
|
||||
L10n.delete,
|
||||
role: .destructive,
|
||||
action: onDelete
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,12 +82,9 @@ extension ServerUsersView {
|
|||
maxWidth: 60
|
||||
)
|
||||
)
|
||||
.grayscale(userActive ? 0.0 : 1.0)
|
||||
|
||||
if isEditing {
|
||||
Color.black
|
||||
.opacity(isSelected ? 0 : 0.5)
|
||||
}
|
||||
.environment(\.isEnabled, userActive)
|
||||
.environment(\.isEditing, isEditing)
|
||||
.environment(\.isSelected, isSelected)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@ extension SelectUserView {
|
|||
@Default(.accentColor)
|
||||
private var accentColor
|
||||
|
||||
@Environment(\.colorScheme)
|
||||
private var colorScheme
|
||||
@Environment(\.isEditing)
|
||||
private var isEditing
|
||||
@Environment(\.isSelected)
|
||||
|
@ -51,40 +49,24 @@ extension SelectUserView {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
action()
|
||||
} label: {
|
||||
VStack(alignment: .center) {
|
||||
ZStack {
|
||||
Color.clear
|
||||
|
||||
UserProfileImage(
|
||||
userID: user.id,
|
||||
source: user.profileImageSource(
|
||||
client: server.client,
|
||||
maxWidth: 120
|
||||
),
|
||||
pipeline: .Swiftfin.local
|
||||
)
|
||||
}
|
||||
.aspectRatio(1, contentMode: .fill)
|
||||
.clipShape(.circle)
|
||||
.overlay {
|
||||
if isEditing {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
Color.black
|
||||
.opacity(isSelected ? 0 : 0.5)
|
||||
.clipShape(.circle)
|
||||
|
||||
if isSelected {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40, height: 40, alignment: .bottomTrailing)
|
||||
.symbolRenderingMode(.palette)
|
||||
.foregroundStyle(accentColor.overlayColor, accentColor)
|
||||
}
|
||||
}
|
||||
Button(action: action) {
|
||||
VStack {
|
||||
UserProfileImage(
|
||||
userID: user.id,
|
||||
source: user.profileImageSource(
|
||||
client: server.client,
|
||||
maxWidth: 120
|
||||
),
|
||||
pipeline: .Swiftfin.local
|
||||
)
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
if isEditing, isSelected {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40, height: 40, alignment: .bottomTrailing)
|
||||
.symbolRenderingMode(.palette)
|
||||
.foregroundStyle(accentColor.overlayColor, accentColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,8 +85,12 @@ extension SelectUserView {
|
|||
}
|
||||
.buttonStyle(.plain)
|
||||
.contextMenu {
|
||||
Button(L10n.delete, role: .destructive) {
|
||||
onDelete()
|
||||
if !isEditing {
|
||||
Button(
|
||||
L10n.delete,
|
||||
role: .destructive,
|
||||
action: onDelete
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,29 +69,6 @@ extension SelectUserView {
|
|||
.aspectRatio(1, contentMode: .fill)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var userImage: some View {
|
||||
ZStack {
|
||||
Color.clear
|
||||
|
||||
UserProfileImage(
|
||||
userID: user.id,
|
||||
source: user.profileImageSource(
|
||||
client: server.client,
|
||||
maxWidth: 120
|
||||
),
|
||||
pipeline: .Swiftfin.local
|
||||
)
|
||||
|
||||
if isEditing {
|
||||
Color.black
|
||||
.opacity(isSelected ? 0 : 0.5)
|
||||
}
|
||||
}
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.clipShape(.circle)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var rowContent: some View {
|
||||
HStack {
|
||||
|
@ -119,9 +96,16 @@ extension SelectUserView {
|
|||
|
||||
var body: some View {
|
||||
ListRow(insets: .init(horizontal: EdgeInsets.edgePadding)) {
|
||||
userImage
|
||||
.frame(width: 80)
|
||||
.padding(.vertical, 8)
|
||||
UserProfileImage(
|
||||
userID: user.id,
|
||||
source: user.profileImageSource(
|
||||
client: server.client,
|
||||
maxWidth: 120
|
||||
),
|
||||
pipeline: .Swiftfin.local
|
||||
)
|
||||
.frame(width: 80)
|
||||
.padding(.vertical, 8)
|
||||
} content: {
|
||||
rowContent
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue