120 lines
4.7 KiB
Swift
120 lines
4.7 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
// Copyright 2023-2025 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import Foundation
|
|
import MatrixRustSDK
|
|
|
|
struct RoomMessageEventStringBuilder {
|
|
enum Style {
|
|
/// Plain: no prefix, no special text treatment
|
|
/// Shown in push notifications and thread lists
|
|
case plain
|
|
/// Strings show on the room list as the last message
|
|
/// The sender will be prefixed in bold
|
|
case senderPrefixed
|
|
/// Events pinned to the banner on the top of the timeline
|
|
/// The message type will be prefixed in bold
|
|
case typeBolded
|
|
}
|
|
|
|
let attributedStringBuilder: AttributedStringBuilderProtocol
|
|
let style: Style
|
|
|
|
func buildAttributedString(for messageType: MessageType, senderDisplayName: String, isOutgoing: Bool) -> AttributedString {
|
|
let message: AttributedString
|
|
switch messageType {
|
|
case .emote(content: let content):
|
|
if let attributedMessage = attributedMessageFrom(formattedBody: content.formatted) {
|
|
return AttributedString(L10n.commonEmote(senderDisplayName, String(attributedMessage.characters)))
|
|
} else {
|
|
return AttributedString(L10n.commonEmote(senderDisplayName, content.body))
|
|
}
|
|
case .audio(content: let content):
|
|
let isVoiceMessage = content.voice != nil
|
|
var content = AttributedString(isVoiceMessage ? L10n.commonVoiceMessage : L10n.commonAudio)
|
|
if style == .typeBolded {
|
|
content.bold()
|
|
}
|
|
message = content
|
|
case .image(let content):
|
|
message = buildMessage(for: style, caption: content.caption, type: L10n.commonImage)
|
|
case .video(let content):
|
|
message = buildMessage(for: style, caption: content.caption, type: L10n.commonVideo)
|
|
case .file(let content):
|
|
message = buildMessage(for: style, caption: content.caption, type: L10n.commonFile)
|
|
case .location:
|
|
var content = AttributedString(L10n.commonSharedLocation)
|
|
if style == .typeBolded {
|
|
content.bold()
|
|
}
|
|
message = content
|
|
case .notice(content: let content):
|
|
if let attributedMessage = attributedMessageFrom(formattedBody: content.formatted) {
|
|
message = attributedMessage
|
|
} else {
|
|
message = AttributedString(content.body)
|
|
}
|
|
case .text(content: let content):
|
|
if let attributedMessage = attributedMessageFrom(formattedBody: content.formatted) {
|
|
message = attributedMessage
|
|
} else {
|
|
message = AttributedString(content.body)
|
|
}
|
|
case .gallery(let content):
|
|
message = AttributedString(content.body)
|
|
case .other(_, let body):
|
|
message = AttributedString(body)
|
|
}
|
|
|
|
if style == .senderPrefixed {
|
|
return prefix(message, with: isOutgoing ? L10n.commonYou : senderDisplayName)
|
|
} else {
|
|
return message
|
|
}
|
|
}
|
|
|
|
func buildAttributedStringForLiveLocation(senderDisplayName: String, isOutgoing: Bool) -> AttributedString {
|
|
var message = AttributedString(L10n.commonSharedLiveLocation)
|
|
if style == .typeBolded {
|
|
message.bold()
|
|
}
|
|
|
|
if style == .senderPrefixed {
|
|
return prefix(message, with: isOutgoing ? L10n.commonYou : senderDisplayName)
|
|
} else {
|
|
return message
|
|
}
|
|
}
|
|
|
|
private func buildMessage(for style: Style, caption: String?, type: String) -> AttributedString {
|
|
guard let caption else {
|
|
return AttributedString(type)
|
|
}
|
|
|
|
if style == .typeBolded {
|
|
return prefix(AttributedString(caption), with: type)
|
|
} else {
|
|
return AttributedString("\(type) - \(caption)")
|
|
}
|
|
}
|
|
|
|
private func prefix(_ eventSummary: AttributedString, with textToBold: String) -> AttributedString {
|
|
let attributedEventSummary = AttributedString(eventSummary.string.trimmingCharacters(in: .whitespacesAndNewlines))
|
|
|
|
var attributedPrefix = AttributedString(textToBold + ":")
|
|
attributedPrefix.bold()
|
|
|
|
// Don't include the message body in the markdown otherwise it makes tappable links.
|
|
return attributedPrefix + " " + attributedEventSummary
|
|
}
|
|
|
|
private func attributedMessageFrom(formattedBody: FormattedBody?) -> AttributedString? {
|
|
formattedBody.flatMap { attributedStringBuilder.fromHTML($0.body) }
|
|
}
|
|
}
|