Update Send button bg color (#5170)

* Update Send button bg color

Change the gradient bg to accent/rest.

* Tidy-up ComposerToolbar to match iOS 18 Figma.

Also simplifies the tests a bit.

* Add a .glassEffect to Compound's SendButton.

* Add a border to TimelineReplyView.

Also use the same sizes in both the message bubbles and the composer.

* Change icon size and container in message bubbles

- Container size = 36x36px
- Icon size = 24x24px

* Update icon of reply contents to be 24x24

* Update the VoiceMessageButton to match the designs.

* Adopt Liquid Glass in the ComposerToolbar.

* Generate and fix snapshots.

---------

Co-authored-by: Doug <douglase@element.io>
This commit is contained in:
Aaron Thornburgh
2026-03-25 18:42:10 +01:00
committed by GitHub
parent a2481b59ba
commit 96b6d69d5a
181 changed files with 790 additions and 719 deletions
@@ -797,6 +797,69 @@ private final class ComposerMentionReplacer: MentionReplacer {
}
}
// MARK: - Mocks
extension ComposerToolbarViewModel {
enum MockMode { case editing, recordVoiceMessage, previewVoiceMessage(isUploading: Bool), reply(isLoading: Bool) }
static func mock(focused: Bool = false,
message: String = "",
mockMode: MockMode? = nil,
hasSuggestions: Bool = false,
canSend: Bool = true) -> ComposerToolbarViewModel {
let suggestions: [SuggestionItem] = if hasSuggestions {
[.init(suggestionType: .user(.init(id: "@user_mention_1:matrix.org", displayName: "User 1", avatarURL: nil)), range: .init(), rawSuggestionText: ""),
.init(suggestionType: .user(.init(id: "@user_mention_2:matrix.org", displayName: "User 2", avatarURL: .mockMXCUserAvatar)), range: .init(), rawSuggestionText: "")]
} else {
[]
}
let roomProxy = JoinedRoomProxyMock(.init())
if !canSend {
roomProxy.identityStatusChangesPublisher = .init([.init(userId: RoomMemberProxyMock.mockAlice.userID, changedTo: .verificationViolation)])
}
let wysiwygViewModel = WysiwygComposerViewModel()
let viewModel = ComposerToolbarViewModel(roomProxy: roomProxy,
wysiwygViewModel: wysiwygViewModel,
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)),
mediaProvider: MediaProviderMock(configuration: .init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
composerDraftService: ComposerDraftServiceMock(.init()))
viewModel.state.bindings.composerFocused = focused
viewModel.state.bindings.plainComposerText = NSAttributedString(string: message)
switch mockMode {
case .editing:
viewModel.state.composerMode = .edit(originalEventOrTransactionID: .eventID(""), type: .default)
case .recordVoiceMessage:
viewModel.state.composerMode = .recordVoiceMessage(state: AudioRecorderState())
case .previewVoiceMessage(let isUploading):
viewModel.state.composerMode = .previewVoiceMessage(state: AudioPlayerState(id: .recorderPreview,
title: L10n.commonVoiceMessage,
duration: 10.0),
waveform: .data(Array(repeating: 1.0, count: 1000)),
isUploading: isUploading)
case .reply(let isLoading):
let replyDetails: TimelineItemReplyDetails = if isLoading {
.loading(eventID: "")
} else {
.loaded(sender: .init(id: "", displayName: "Test"),
eventID: "",
eventContent: .message(.text(.init(body: "Hello World!"))))
}
viewModel.state.composerMode = .reply(eventID: UUID().uuidString, replyDetails: replyDetails, isThread: false)
case nil:
break
}
return viewModel
}
}
private struct PlainComposerContent {
let text: String
let mentionedUserIDs: Set<String>