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