mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Fix a bug where your own emotes showed as '* You emoted'. (#4038)
This commit is contained in:
@@ -17,9 +17,7 @@ struct RoomEventStringBuilder {
|
|||||||
func buildAttributedString(for eventItemProxy: EventTimelineItemProxy) -> AttributedString? {
|
func buildAttributedString(for eventItemProxy: EventTimelineItemProxy) -> AttributedString? {
|
||||||
let sender = eventItemProxy.sender
|
let sender = eventItemProxy.sender
|
||||||
let isOutgoing = eventItemProxy.isOwn
|
let isOutgoing = eventItemProxy.isOwn
|
||||||
let displayName = if isOutgoing {
|
let displayName = if shouldDisambiguateDisplayNames {
|
||||||
L10n.commonYou
|
|
||||||
} else if shouldDisambiguateDisplayNames {
|
|
||||||
sender.disambiguatedDisplayName ?? sender.id
|
sender.disambiguatedDisplayName ?? sender.id
|
||||||
} else {
|
} else {
|
||||||
sender.displayName ?? sender.id
|
sender.displayName ?? sender.id
|
||||||
@@ -29,14 +27,14 @@ struct RoomEventStringBuilder {
|
|||||||
case .msgLike(let messageLikeContent):
|
case .msgLike(let messageLikeContent):
|
||||||
switch messageLikeContent.kind {
|
switch messageLikeContent.kind {
|
||||||
case .message(let messageContent):
|
case .message(let messageContent):
|
||||||
return messageEventStringBuilder.buildAttributedString(for: messageContent.msgType, senderDisplayName: displayName)
|
return messageEventStringBuilder.buildAttributedString(for: messageContent.msgType, senderDisplayName: displayName, isOutgoing: isOutgoing)
|
||||||
case .sticker:
|
case .sticker:
|
||||||
if messageEventStringBuilder.destination == .pinnedEvent {
|
if messageEventStringBuilder.destination == .pinnedEvent {
|
||||||
var string = AttributedString(L10n.commonSticker)
|
var string = AttributedString(L10n.commonSticker)
|
||||||
string.bold()
|
string.bold()
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
return prefix(L10n.commonSticker, with: displayName)
|
return prefix(L10n.commonSticker, with: displayName, isOutgoing: isOutgoing)
|
||||||
case .poll(let question, _, _, _, _, _, _):
|
case .poll(let question, _, _, _, _, _, _):
|
||||||
if messageEventStringBuilder.destination == .pinnedEvent {
|
if messageEventStringBuilder.destination == .pinnedEvent {
|
||||||
let questionPlaceholder = "{question}"
|
let questionPlaceholder = "{question}"
|
||||||
@@ -46,9 +44,9 @@ struct RoomEventStringBuilder {
|
|||||||
finalString.replace(questionPlaceholder, with: normalString)
|
finalString.replace(questionPlaceholder, with: normalString)
|
||||||
return finalString
|
return finalString
|
||||||
}
|
}
|
||||||
return prefix(L10n.commonPollSummary(question), with: displayName)
|
return prefix(L10n.commonPollSummary(question), with: displayName, isOutgoing: isOutgoing)
|
||||||
case .redacted:
|
case .redacted:
|
||||||
return prefix(L10n.commonMessageRemoved, with: displayName)
|
return prefix(L10n.commonMessageRemoved, with: displayName, isOutgoing: isOutgoing)
|
||||||
case .unableToDecrypt(let encryptedMessage):
|
case .unableToDecrypt(let encryptedMessage):
|
||||||
let errorMessage = switch encryptedMessage {
|
let errorMessage = switch encryptedMessage {
|
||||||
case .megolmV1AesSha2(_, .sentBeforeWeJoined): L10n.commonUnableToDecryptNoAccess
|
case .megolmV1AesSha2(_, .sentBeforeWeJoined): L10n.commonUnableToDecryptNoAccess
|
||||||
@@ -56,10 +54,10 @@ struct RoomEventStringBuilder {
|
|||||||
case .megolmV1AesSha2(_, .unknownDevice), .megolmV1AesSha2(_, .unsignedDevice): L10n.commonUnableToDecryptInsecureDevice
|
case .megolmV1AesSha2(_, .unknownDevice), .megolmV1AesSha2(_, .unsignedDevice): L10n.commonUnableToDecryptInsecureDevice
|
||||||
default: L10n.commonWaitingForDecryptionKey
|
default: L10n.commonWaitingForDecryptionKey
|
||||||
}
|
}
|
||||||
return prefix(errorMessage, with: displayName)
|
return prefix(errorMessage, with: displayName, isOutgoing: isOutgoing)
|
||||||
}
|
}
|
||||||
case .failedToParseMessageLike, .failedToParseState:
|
case .failedToParseMessageLike, .failedToParseState:
|
||||||
return prefix(L10n.commonUnsupportedEvent, with: displayName)
|
return prefix(L10n.commonUnsupportedEvent, with: displayName, isOutgoing: isOutgoing)
|
||||||
case .state(_, let state):
|
case .state(_, let state):
|
||||||
return stateEventStringBuilder
|
return stateEventStringBuilder
|
||||||
.buildString(for: state, sender: sender, isOutgoing: isOutgoing)
|
.buildString(for: state, sender: sender, isOutgoing: isOutgoing)
|
||||||
@@ -78,19 +76,19 @@ struct RoomEventStringBuilder {
|
|||||||
memberIsYou: isOutgoing)
|
memberIsYou: isOutgoing)
|
||||||
.map(AttributedString.init)
|
.map(AttributedString.init)
|
||||||
case .callInvite:
|
case .callInvite:
|
||||||
return prefix(L10n.commonUnsupportedCall, with: displayName)
|
return prefix(L10n.commonUnsupportedCall, with: displayName, isOutgoing: isOutgoing)
|
||||||
case .callNotify:
|
case .callNotify:
|
||||||
return prefix(L10n.commonCallStarted, with: displayName)
|
return prefix(L10n.commonCallStarted, with: displayName, isOutgoing: isOutgoing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func prefix(_ eventSummary: String, with senderDisplayName: String) -> AttributedString {
|
private func prefix(_ eventSummary: String, with senderDisplayName: String, isOutgoing: Bool) -> AttributedString {
|
||||||
guard shouldPrefixSenderName else {
|
guard shouldPrefixSenderName else {
|
||||||
return AttributedString(eventSummary)
|
return AttributedString(eventSummary)
|
||||||
}
|
}
|
||||||
let attributedEventSummary = AttributedString(eventSummary.trimmingCharacters(in: .whitespacesAndNewlines))
|
let attributedEventSummary = AttributedString(eventSummary.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
|
|
||||||
var attributedSenderDisplayName = AttributedString(senderDisplayName)
|
var attributedSenderDisplayName = AttributedString(isOutgoing ? L10n.commonYou : senderDisplayName)
|
||||||
attributedSenderDisplayName.bold()
|
attributedSenderDisplayName.bold()
|
||||||
|
|
||||||
// Don't include the message body in the markdown otherwise it makes tappable links.
|
// Don't include the message body in the markdown otherwise it makes tappable links.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ struct RoomMessageEventStringBuilder {
|
|||||||
let attributedStringBuilder: AttributedStringBuilderProtocol
|
let attributedStringBuilder: AttributedStringBuilderProtocol
|
||||||
let destination: Destination
|
let destination: Destination
|
||||||
|
|
||||||
func buildAttributedString(for messageType: MessageType, senderDisplayName: String) -> AttributedString {
|
func buildAttributedString(for messageType: MessageType, senderDisplayName: String, isOutgoing: Bool) -> AttributedString {
|
||||||
let message: AttributedString
|
let message: AttributedString
|
||||||
switch messageType {
|
switch messageType {
|
||||||
case .emote(content: let content):
|
case .emote(content: let content):
|
||||||
@@ -69,7 +69,7 @@ struct RoomMessageEventStringBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if destination == .roomList {
|
if destination == .roomList {
|
||||||
return prefix(message, with: senderDisplayName)
|
return prefix(message, with: isOutgoing ? L10n.commonYou : senderDisplayName)
|
||||||
} else {
|
} else {
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ struct NotificationContentBuilder {
|
|||||||
var notification = try await processCommonRoomMessage(notificationItem: notificationItem, mediaProvider: mediaProvider)
|
var notification = try await processCommonRoomMessage(notificationItem: notificationItem, mediaProvider: mediaProvider)
|
||||||
|
|
||||||
let displayName = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName
|
let displayName = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName
|
||||||
notification.body = String(messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName).characters)
|
notification.body = String(messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName, isOutgoing: false).characters)
|
||||||
|
|
||||||
guard settings.timelineMediaVisibility == .always ||
|
guard settings.timelineMediaVisibility == .always ||
|
||||||
(settings.timelineMediaVisibility == .privateOnly && notificationItem.isRoomPrivate)
|
(settings.timelineMediaVisibility == .privateOnly && notificationItem.isRoomPrivate)
|
||||||
|
|||||||
@@ -26,43 +26,93 @@ class RoomEventStringBuilderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testSenderPrefix() {
|
func testSenderPrefix() {
|
||||||
let ownMessageString = stringBuilder.buildAttributedString(for: makeTextMessageItem(senderID: ownUserID, senderDisplayName: "Alice"))
|
let ownMessageString = stringBuilder.buildAttributedString(for: makeMessageItem(senderID: ownUserID, senderDisplayName: "Alice"))
|
||||||
XCTAssertEqual(ownMessageString?.string, "You: Hello, World!",
|
XCTAssertEqual(ownMessageString?.string, "You: Hello, World!", "Your own messages should be prefixed with 'You'")
|
||||||
"Your own messages should be prefixed with 'You'")
|
|
||||||
|
|
||||||
let otherMessageString = stringBuilder.buildAttributedString(for: makeTextMessageItem(senderID: "@bob:matrix.org", senderDisplayName: "Bob"))
|
let otherMessageString = stringBuilder.buildAttributedString(for: makeMessageItem(senderID: "@bob:matrix.org", senderDisplayName: "Bob"))
|
||||||
XCTAssertEqual(otherMessageString?.string, "Bob: Hello, World!",
|
XCTAssertEqual(otherMessageString?.string, "Bob: Hello, World!", "Everyone else's messages should be prefixed with their display name.")
|
||||||
"Everyone else's messages should be prefixed with their display name.")
|
|
||||||
|
|
||||||
let ambiguousMessageString = stringBuilder.buildAttributedString(for: makeTextMessageItem(senderID: "@charlie:matrix.org",
|
let ambiguousMessageString = stringBuilder.buildAttributedString(for: makeMessageItem(senderID: "@charlie:matrix.org",
|
||||||
senderDisplayName: "Charlie",
|
senderDisplayName: "Charlie",
|
||||||
senderDisplayNameAmbiguous: true))
|
senderDisplayNameAmbiguous: true))
|
||||||
XCTAssertEqual(ambiguousMessageString?.string, "Charlie (@charlie:matrix.org): Hello, World!",
|
XCTAssertEqual(ambiguousMessageString?.string, "Charlie (@charlie:matrix.org): Hello, World!",
|
||||||
"Messages from senders with ambiguous display names should include their user ID in the prefix.")
|
"Messages from senders with ambiguous display names should include their user ID in the prefix.")
|
||||||
|
|
||||||
|
let ownEmoteString = stringBuilder.buildAttributedString(for: makeMessageItem(senderID: ownUserID,
|
||||||
|
senderDisplayName: "Alice",
|
||||||
|
type: .emote,
|
||||||
|
message: "laughs"))
|
||||||
|
XCTAssertEqual(ownEmoteString?.string, "* Alice laughs", "Your own emotes shouldn't contain 'You'")
|
||||||
|
|
||||||
|
let otherEmoteString = stringBuilder.buildAttributedString(for: makeMessageItem(senderID: "@bob:matrix.org",
|
||||||
|
senderDisplayName: "Bob",
|
||||||
|
type: .emote,
|
||||||
|
message: "sighs"))
|
||||||
|
XCTAssertEqual(otherEmoteString?.string, "* Bob sighs", "Everyone else's emotes should contain their display name.")
|
||||||
|
|
||||||
|
let ownPollString = stringBuilder.buildAttributedString(for: makePollItem(senderID: ownUserID, senderDisplayName: "Alice"))
|
||||||
|
XCTAssertEqual(ownPollString?.string, "You: Poll: Which is better?", "Your own polls should be prefixed with 'You'")
|
||||||
|
|
||||||
|
let otherPollString = stringBuilder.buildAttributedString(for: makePollItem(senderID: "@bob:matrix.org", senderDisplayName: "Bob"))
|
||||||
|
XCTAssertEqual(otherPollString?.string, "Bob: Poll: Which is better?", "Everyone else's polls should be prefixed with their display name.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func makeTextMessageItem(senderID: String,
|
private enum MockMessageType { case textMessage, emote }
|
||||||
senderDisplayName: String? = nil,
|
|
||||||
senderDisplayNameAmbiguous: Bool = false,
|
private func makeMessageItem(senderID: String,
|
||||||
message: String = "Hello, World!") -> EventTimelineItemProxy {
|
senderDisplayName: String? = nil,
|
||||||
|
senderDisplayNameAmbiguous: Bool = false,
|
||||||
|
type: MockMessageType = .textMessage,
|
||||||
|
message: String = "Hello, World!") -> EventTimelineItemProxy {
|
||||||
|
let content = switch type {
|
||||||
|
case .textMessage: makeTextContent(message: message)
|
||||||
|
case .emote: makeEmoteContent(message: message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .init(item: .init(configuration: .init(eventID: "1234",
|
||||||
|
sender: senderID,
|
||||||
|
senderProfile: .ready(displayName: senderDisplayName, displayNameAmbiguous: senderDisplayNameAmbiguous, avatarUrl: nil),
|
||||||
|
isOwn: senderID == ownUserID,
|
||||||
|
content: .msgLike(content: .init(kind: .message(content: .init(msgType: content,
|
||||||
|
body: message,
|
||||||
|
isEdited: false,
|
||||||
|
mentions: nil)),
|
||||||
|
reactions: [],
|
||||||
|
inReplyTo: nil,
|
||||||
|
threadRoot: nil,
|
||||||
|
threadSummary: nil)))),
|
||||||
|
uniqueID: .init("0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeTextContent(message: String) -> MessageType {
|
||||||
|
.text(content: .init(body: message, formatted: nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeEmoteContent(message: String) -> MessageType {
|
||||||
|
.emote(content: .init(body: message, formatted: nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makePollItem(senderID: String,
|
||||||
|
senderDisplayName: String? = nil,
|
||||||
|
senderDisplayNameAmbiguous: Bool = false,
|
||||||
|
question: String = "Which is better?") -> EventTimelineItemProxy {
|
||||||
.init(item: .init(configuration: .init(eventID: "1234",
|
.init(item: .init(configuration: .init(eventID: "1234",
|
||||||
sender: senderID,
|
sender: senderID,
|
||||||
senderProfile: .ready(displayName: senderDisplayName, displayNameAmbiguous: senderDisplayNameAmbiguous, avatarUrl: nil),
|
senderProfile: .ready(displayName: senderDisplayName, displayNameAmbiguous: senderDisplayNameAmbiguous, avatarUrl: nil),
|
||||||
isOwn: senderID == ownUserID,
|
isOwn: senderID == ownUserID,
|
||||||
content: .msgLike(content: .init(kind: .message(content: .init(msgType: makeTextContent(message: message),
|
content: .msgLike(content: .init(kind: .poll(question: question,
|
||||||
body: message,
|
kind: .disclosed,
|
||||||
isEdited: false,
|
maxSelections: 1,
|
||||||
mentions: nil)),
|
answers: [],
|
||||||
|
votes: [:],
|
||||||
|
endTime: nil,
|
||||||
|
hasBeenEdited: false),
|
||||||
reactions: [],
|
reactions: [],
|
||||||
inReplyTo: nil,
|
inReplyTo: nil,
|
||||||
threadRoot: nil,
|
threadRoot: nil,
|
||||||
threadSummary: nil)))),
|
threadSummary: nil)))),
|
||||||
uniqueID: .init("0"))
|
uniqueID: .init("0"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeTextContent(message: String) -> MessageType {
|
|
||||||
.text(content: .init(body: message, formatted: nil))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user