Files
element-x-ios/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreenUserStatusRow.swift
T
Doug 0d222e8ac0 More tweaks for User Status. (#6051)
* Make sure user profiles are up to date when mentioning.

* Automatically focus the custom status text field.

* Make sure to clear the previous custom status text before input.

* Attempt to fix the user status sheet sometimes disappearing.
2026-08-21 11:23:33 +01:00

147 lines
5.6 KiB
Swift

//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Compound
import SwiftUI
struct SettingsScreenUserStatusRow: View {
enum Mode: Equatable {
case pickStatusButton, customStatusInput(emoji: Character), showingStatus(UserStatus.Displayed)
var isCustomStatusInput: Bool {
switch self {
case .customStatusInput: true
default: false
}
}
}
let mode: Mode
let action: (SettingsScreenViewAction.UserStatusAction) -> Void
@State private var customText = ""
@FocusState private var isCustomFieldFocused: Bool
var body: some View {
rowContent
.onChange(of: mode.isCustomStatusInput) { _, newValue in
guard newValue else { return }
customText = ""
}
}
@ViewBuilder
var rowContent: some View {
switch mode {
case .pickStatusButton:
ListRow(label: .default(title: L10n.screenSettingsUserStatusPlaceholder, icon: \.reaction),
kind: .button { action(.pickStatus) })
case .customStatusInput(let emoji):
ListRow(kind: .custom {
HStack(spacing: ListRowPadding.labelIconSpacing) {
Button { action(.pickCustomEmoji) } label: {
Text(String(emoji))
.font(.compound.headingSM)
.foregroundStyle(.compound.textPrimary)
.accessibilityLabel(L10n.a11yCustomEmoji)
.accessibilityValue(String(emoji))
}
.buttonStyle(EditEmojiButtonStyle())
TextField(L10n.screenSettingsUserStatusCustomHint, text: $customText)
.textFieldStyle(.compound(.raised))
.focused($isCustomFieldFocused)
.onAppear { isCustomFieldFocused = true }
.accessibilityLabel(L10n.screenSettingsUserStatusCustomHint)
.padding(.vertical, 3)
ZStack { // ZStack to stop the text field from resizing.
Button(L10n.actionCancel) { action(.cancel) }
.opacity(customText.isEmpty ? 1 : 0)
Button(L10n.actionSave, action: saveCustomStatus)
.opacity(customText.isEmpty ? 0 : 1)
}
.buttonStyle(.compound(.textLink))
}
.listRowBackground(Color.clear)
})
case .showingStatus(let status):
ListRow(kind: .custom {
HStack(spacing: ListRowPadding.labelIconSpacing) {
Button { action(.pickStatus) } label: {
HStack(spacing: ListRowPadding.labelIconSpacing) {
CompoundIcon(\.reaction)
.hidden()
.overlay {
Text(String(status.emoji))
.font(.compound.bodyLG)
.foregroundStyle(.compound.textPrimary)
}
Text(status.text)
.font(.compound.bodyLG)
.foregroundStyle(.compound.textPrimary)
.frame(maxWidth: .infinity, alignment: .leading)
}
.contentShape(Rectangle())
}
.accessibilityLabel(L10n.a11yStatus)
.accessibilityValue(Text(String(status.emoji)) + Text(status.text))
.buttonStyle(.plain)
Button(L10n.actionClear) { action(.clear) }
.buttonStyle(.compound(.textLink))
}
.padding(ListRowPadding.insets)
})
}
}
private func saveCustomStatus() {
guard case let .customStatusInput(emoji) = mode else { return }
action(.set(.init(text: customText, emoji: emoji)))
}
struct EditEmojiButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(12)
.background {
ZStack {
Circle().fill(.compound.bgCanvasDefaultLevel1)
Circle().inset(by: 0.5).stroke(.compound.borderInteractiveSecondary)
}
}
.drawingGroup()
.opacity(configuration.isPressed ? 0.6 : 1)
}
}
}
struct SettingsScreenUserStatusRow_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
Form {
Section {
SettingsScreenUserStatusRow(mode: .pickStatusButton) { _ in }
}
Section {
SettingsScreenUserStatusRow(mode: .customStatusInput(emoji: "😄")) { _ in }
}
Section {
SettingsScreenUserStatusRow(mode: .showingStatus(.userSet(.init(text: "Away", emoji: "🌴")))) { _ in }
}
Section {
SettingsScreenUserStatusRow(mode: .showingStatus(.inCall(.init(joinedDate: nil)))) { _ in }
}
}
.compoundList()
}
}