Add support for showing the user's own status over their avatar on the app's Settings button. (#5912)

* Add a common .inverseMask view modifier and use a blend mode for efficiency.

This also fixes the snapshot rendering 🎉

* Add support for the status emoji on the app's Setting button.

* Align the room header decoration icons with the status emoji.

* Add missing snapshots.
This commit is contained in:
Doug
2026-07-22 14:53:32 +01:00
committed by GitHub
parent e4f919e58f
commit b46189d05f
103 changed files with 376 additions and 268 deletions
@@ -0,0 +1,86 @@
//
// 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 AvatarSettingsButtonLabel: View {
let userProfile: UserProfile
var requiresExtraAccountSetup = false
let mediaProvider: MediaProviderProtocol?
var body: some View {
LoadableAvatarImage(url: userProfile.avatarURL,
name: userProfile.displayName,
contentID: userProfile.id,
avatarSize: .user(on: .chats),
mediaProvider: mediaProvider)
.accessibilityIdentifier(A11yIdentifiers.homeScreen.userAvatar)
.overlayBadge(10, isBadged: requiresExtraAccountSetup)
.modifier(StatusEmojiModifier(statusEmoji: userProfile.status.displayed?.emoji))
.geometryGroup()
}
}
private struct StatusEmojiModifier: ViewModifier {
let statusEmoji: Character?
func body(content: Content) -> some View {
if let emojiText {
content
.inverseMask(alignment: .bottomTrailing) {
emojiText
.hidden()
.overlay { Circle().inset(by: -3) }
}
.overlay(alignment: .bottomTrailing) {
emojiText
}
} else {
content
}
}
var emojiText: (some View)? {
statusEmoji.map {
Text(String($0))
// All values (inc font) scaled by 0.8 as our Liquid Glass avatar is 32pt and not 40pt.
.font(.compound.bodyXS)
.padding(.vertical, 1) // Match Figma's line height.
.alignmentGuide(.trailing) { $0.width - 6.5 }
.alignmentGuide(.bottom) { $0.height + 1.5 }
}
}
}
struct AvatarSettingsButtonLabel_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
HStack(spacing: 16) {
AvatarSettingsButtonLabel(userProfile: .mockAlice,
requiresExtraAccountSetup: false,
mediaProvider: MediaProviderMock(.init()))
AvatarSettingsButtonLabel(userProfile: .mockAlice,
requiresExtraAccountSetup: true,
mediaProvider: MediaProviderMock(.init()))
AvatarSettingsButtonLabel(userProfile: .init(userID: "",
avatarURL: .mockMXCUserAvatar,
status: .mock(text: "", emoji: "🌴")),
requiresExtraAccountSetup: false,
mediaProvider: MediaProviderMock(.init()))
AvatarSettingsButtonLabel(userProfile: .init(userID: "",
avatarURL: .mockMXCUserAvatar,
status: .mock(text: "", emoji: "🌴")),
requiresExtraAccountSetup: true,
mediaProvider: MediaProviderMock(.init()))
}
.padding(16)
.previewLayout(.sizeThatFits)
}
}