Files
iOS/Sources/App/Servers/ServerSelectView.swift
Bruno Pantaleão Gonçalves 727db03a8d Fix: mTLS profile picture not loading (#4408)
<!-- Thank you for submitting a Pull Request and helping to improve Home
Assistant. Please complete the following sections to help the processing
and review of your changes. Please do not delete anything from this
template. -->

## Summary
<!-- Provide a brief summary of the changes you have made and most
importantly what they aim to achieve -->

## Screenshots
<!-- If this is a user-facing change not in the frontend, please include
screenshots in light and dark mode. -->

## Link to pull request in Documentation repository
<!-- Pull requests that add, change or remove functionality must have a
corresponding pull request in the Companion App Documentation repository
(https://github.com/home-assistant/companion.home-assistant). Please add
the number of this pull request after the "#" -->
Documentation: home-assistant/companion.home-assistant#

## Any other notes
<!-- If there is any other information of note, like if this Pull
Request is part of a bigger change, please include it here. -->
2026-03-04 16:49:23 +01:00

166 lines
5.1 KiB
Swift

import Shared
import SwiftUI
struct ServerSelectView: View {
@Environment(\.dismiss) private var dismiss
@State private var showSheet = true
let prompt: String?
let includeSettings: Bool
let selectAction: (Server) -> Void
var body: some View {
VStack {}
.background(Color.clear)
.sheet(isPresented: $showSheet) {
if #available(iOS 16.0, *) {
content
.presentationDetents([.medium, .large])
.presentationDragIndicator(.hidden)
} else {
content
}
}
.onChange(of: showSheet) { newValue in
if !newValue {
dismiss()
}
}
}
private var content: some View {
NavigationView {
List {
if let prompt {
Section {
Text(prompt)
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
.padding(.horizontal)
}
}
Section {
ForEach(Current.servers.all, id: \.identifier) { server in
ServerSelectViewRow(server: server) {
selectAction(server)
dismiss()
}
}
}
}
.navigationViewStyle(.stack)
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(L10n.ServersSelection.title)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
CloseButton {
showSheet = false
}
}
ToolbarItem(placement: .topBarLeading) {
if includeSettings {
SettingsButton(tint: Color.haPrimary) {
dismiss()
Current.sceneManager.webViewWindowControllerPromise.then(\.webViewControllerPromise)
.done { controller in
controller.showSettingsViewController()
}
}
}
}
}
}
}
}
struct ServerSelectViewRow: View {
@State private var userName: String = ""
@State private var profilePictureImage: UIImage?
@State private var selected = false
let server: Server
let action: () -> Void
var body: some View {
Button(action: {
action()
}, label: {
HStack(spacing: DesignSystem.Spaces.two) {
profilePicture
VStack {
Group {
Text(server.info.name)
.font(.headline)
.foregroundStyle(Color(uiColor: .label))
Text(userName)
.font(.caption)
.foregroundStyle(Color(uiColor: .secondaryLabel))
}
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
}
Image(systemSymbol: selected ? .checkmarkCircleFill : .circle)
}
})
.tint(Color.haPrimary)
.onAppear {
updateSelectionIndicator()
loadUserNameAndProfilePicture()
}
}
private var profilePicture: some View {
Group {
if let profilePictureImage {
Image(uiImage: profilePictureImage)
.resizable()
} else {
ZStack {
Image(systemSymbol: .circleFill)
.resizable()
Text(String(userName.first ?? Character(" ")))
.foregroundStyle(.white)
.font(.body.bold())
}
}
}
.frame(width: 40, height: 40)
.clipShape(Circle())
.overlay(
Circle()
.stroke(Color.haPrimary, lineWidth: 2)
)
}
private func updateSelectionIndicator() {
Current.sceneManager.webViewWindowControllerPromise.then(\.webViewControllerPromise).done { controller in
selected = controller.server == server
}
}
private func loadUserNameAndProfilePicture() {
Current.api(for: server)?.connection.caches.user.once { user in
userName = user.name.orEmpty
}
Current.api(for: server)?.profilePicture { image in
profilePictureImage = image
}
}
}
#Preview("Servers") {
ServerSelectView(prompt: nil, includeSettings: true) { _ in }
}
#Preview("Servers with prompt") {
ServerSelectView(prompt: "Are you sure?", includeSettings: false) { _ in }
}
#Preview("Rows") {
List {
ServerSelectViewRow(server: ServerFixture.standard) {}
}
}