Pinned items timeline implementation for the banner (#3099)

This commit is contained in:
Mauro
2024-08-05 14:09:05 +02:00
committed by GitHub
parent a11faeb131
commit ff2c42d53b
23 changed files with 336 additions and 83 deletions
@@ -759,10 +759,11 @@ class ClientProxy: ClientProxyProtocol {
let roomListService = syncService.roomListService()
let roomMessageEventStringBuilder = RoomMessageEventStringBuilder(attributedStringBuilder: AttributedStringBuilder(cacheKey: "roomList",
mentionBuilder: PlainMentionBuilder()))
mentionBuilder: PlainMentionBuilder()), prefix: .senderName)
let eventStringBuilder = RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID, shouldDisambiguateDisplayNames: false),
messageEventStringBuilder: roomMessageEventStringBuilder,
shouldDisambiguateDisplayNames: false)
shouldDisambiguateDisplayNames: false,
shouldPrefixSenderName: true)
roomSummaryProvider = RoomSummaryProvider(roomListService: roomListService,
eventStringBuilder: eventStringBuilder,
+23 -2
View File
@@ -23,6 +23,24 @@ class RoomProxy: RoomProxyProtocol {
private let roomListItem: RoomListItemProtocol
private let room: RoomProtocol
let timeline: TimelineProxyProtocol
private var innerPinnedEventsTimeline: TimelineProxyProtocol?
var pinnedEventsTimeline: TimelineProxyProtocol? {
get async {
if let innerPinnedEventsTimeline {
return innerPinnedEventsTimeline
} else {
do {
let timeline = try await TimelineProxy(timeline: room.pinnedEventsTimeline(internalIdPrefix: nil, maxEventsToLoad: 100), isLive: false)
await timeline.subscribeForUpdates()
innerPinnedEventsTimeline = timeline
return timeline
} catch {
MXLog.error("Failed creating pinned events timeline with error: \(error)")
return nil
}
}
}
}
// periphery:ignore - required for instance retention in the rust codebase
private var roomInfoObservationToken: TaskHandle?
@@ -92,9 +110,12 @@ class RoomProxy: RoomProxyProtocol {
}
}
var pinnedEventIDs: [String] {
var pinnedEventIDs: Set<String> {
get async {
await (try? room.roomInfo().pinnedEventIds) ?? []
guard let pinnedEventIDs = try? await room.roomInfo().pinnedEventIds else {
return []
}
return .init(pinnedEventIDs)
}
}
@@ -38,7 +38,7 @@ protocol RoomProxyProtocol {
var isSpace: Bool { get }
var isEncrypted: Bool { get }
var isFavourite: Bool { get async }
var pinnedEventIDs: [String] { get async }
var pinnedEventIDs: Set<String> { get async }
var membership: Membership { get }
var inviter: RoomMemberProxyProtocol? { get async }
var hasOngoingCall: Bool { get }
@@ -66,6 +66,8 @@ protocol RoomProxyProtocol {
var timeline: TimelineProxyProtocol { get }
var pinnedEventsTimeline: TimelineProxyProtocol? { get async }
func subscribeForUpdates() async
func subscribeToRoomInfoUpdates()
@@ -21,6 +21,7 @@ struct RoomEventStringBuilder {
let stateEventStringBuilder: RoomStateEventStringBuilder
let messageEventStringBuilder: RoomMessageEventStringBuilder
let shouldDisambiguateDisplayNames: Bool
let shouldPrefixSenderName: Bool
func buildAttributedString(for eventItemProxy: EventTimelineItemProxy) -> AttributedString? {
let sender = eventItemProxy.sender
@@ -50,7 +51,7 @@ struct RoomEventStringBuilder {
}
let messageType = messageContent.msgtype()
return messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName, prefixWithSenderName: true)
return messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName)
case .state(_, let state):
return stateEventStringBuilder
.buildString(for: state, sender: sender, isOutgoing: isOutgoing)
@@ -78,6 +79,9 @@ struct RoomEventStringBuilder {
}
private func prefix(_ eventSummary: String, with senderDisplayName: String) -> AttributedString {
guard shouldPrefixSenderName else {
return AttributedString(eventSummary)
}
let attributedEventSummary = AttributedString(eventSummary.trimmingCharacters(in: .whitespacesAndNewlines))
var attributedSenderDisplayName = AttributedString(senderDisplayName)
@@ -86,4 +90,13 @@ struct RoomEventStringBuilder {
// Don't include the message body in the markdown otherwise it makes tappable links.
return attributedSenderDisplayName + ": " + attributedEventSummary
}
static func pinnedEventStringBuilder(userID: String) -> Self {
RoomEventStringBuilder(stateEventStringBuilder: .init(userID: userID,
shouldDisambiguateDisplayNames: false),
messageEventStringBuilder: .init(attributedStringBuilder: AttributedStringBuilder(cacheKey: "pinnedEvents", mentionBuilder: PlainMentionBuilder()),
prefix: .mediaType),
shouldDisambiguateDisplayNames: false,
shouldPrefixSenderName: false)
}
}
@@ -18,10 +18,17 @@ import Foundation
import MatrixRustSDK
struct RoomMessageEventStringBuilder {
let attributedStringBuilder: AttributedStringBuilderProtocol
enum Prefix {
case senderName
case mediaType
case none
}
func buildAttributedString(for messageType: MessageType, senderDisplayName: String, prefixWithSenderName: Bool) -> AttributedString {
let message: String
let attributedStringBuilder: AttributedStringBuilderProtocol
let prefix: Prefix
func buildAttributedString(for messageType: MessageType, senderDisplayName: String) -> AttributedString {
let message: AttributedString
switch messageType {
// Message types that don't need a prefix.
case .emote(content: let content):
@@ -33,46 +40,54 @@ struct RoomMessageEventStringBuilder {
// Message types that should be prefixed with the sender's name.
case .audio(content: let content):
let isVoiceMessage = content.voice != nil
message = isVoiceMessage ? L10n.commonVoiceMessage : L10n.commonAudio
var content = AttributedString(isVoiceMessage ? L10n.commonVoiceMessage : L10n.commonAudio)
if prefix == .mediaType {
content.bold()
}
message = content
case .image(let content):
message = "\(L10n.commonImage) - \(content.body)"
message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonImage) : AttributedString("\(L10n.commonImage) - \(content.body)")
case .video(let content):
message = "\(L10n.commonVideo) - \(content.body)"
message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonVideo) : AttributedString("\(L10n.commonVideo) - \(content.body)")
case .file(let content):
message = "\(L10n.commonFile) - \(content.body)"
message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonFile) : AttributedString("\(L10n.commonFile) - \(content.body)")
case .location:
message = L10n.commonSharedLocation
var content = AttributedString(L10n.commonSharedLocation)
if prefix == .mediaType {
content.bold()
}
message = content
case .notice(content: let content):
if let attributedMessage = attributedMessageFrom(formattedBody: content.formatted) {
message = String(attributedMessage.characters)
message = attributedMessage
} else {
message = content.body
message = AttributedString(content.body)
}
case .text(content: let content):
if let attributedMessage = attributedMessageFrom(formattedBody: content.formatted) {
message = String(attributedMessage.characters)
message = attributedMessage
} else {
message = content.body
message = AttributedString(content.body)
}
case .other(_, let body):
message = body
message = AttributedString(body)
}
if prefixWithSenderName {
if prefix == .senderName {
return prefix(message, with: senderDisplayName)
} else {
return AttributedString(message)
return message
}
}
private func prefix(_ eventSummary: String, with senderDisplayName: String) -> AttributedString {
let attributedEventSummary = AttributedString(eventSummary.trimmingCharacters(in: .whitespacesAndNewlines))
private func prefix(_ eventSummary: AttributedString, with textToBold: String) -> AttributedString {
let attributedEventSummary = AttributedString(eventSummary.string.trimmingCharacters(in: .whitespacesAndNewlines))
var attributedSenderDisplayName = AttributedString(senderDisplayName)
attributedSenderDisplayName.bold()
var attributedPrefix = AttributedString(textToBold + ":")
attributedPrefix.bold()
// Don't include the message body in the markdown otherwise it makes tappable links.
return attributedSenderDisplayName + ": " + attributedEventSummary
return attributedPrefix + " " + attributedEventSummary
}
private func attributedMessageFrom(formattedBody: FormattedBody?) -> AttributedString? {
@@ -42,16 +42,17 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
init(roomProxy: RoomProxyProtocol,
timelineProxy: TimelineProxyProtocol,
initialFocussedEventID: String?,
timelineItemFactory: RoomTimelineItemFactoryProtocol,
appSettings: AppSettings) {
self.roomProxy = roomProxy
liveTimelineProvider = roomProxy.timeline.timelineProvider
liveTimelineProvider = timelineProxy.timelineProvider
self.timelineItemFactory = timelineItemFactory
self.appSettings = appSettings
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
activeTimeline = roomProxy.timeline
activeTimeline = timelineProxy
activeTimelineProvider = liveTimelineProvider
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
@@ -21,6 +21,7 @@ struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
initialFocussedEventID: String?,
timelineItemFactory: RoomTimelineItemFactoryProtocol) -> RoomTimelineControllerProtocol {
RoomTimelineController(roomProxy: roomProxy,
timelineProxy: roomProxy.timeline,
initialFocussedEventID: initialFocussedEventID,
timelineItemFactory: timelineItemFactory,
appSettings: ServiceLocator.shared.settings)