From d7127562d7efb50095c775477c10b1cfea3db46b Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 30 Jul 2026 16:13:03 +0200 Subject: [PATCH] added the strings for the pin banner, the room list and the notification content builder --- .../RoomMessageEventStringBuilder.swift | 17 ++++++++++- NSE/Sources/NotificationContentBuilder.swift | 28 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift index 62aac98de..98eaa7ff5 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift @@ -66,7 +66,7 @@ nonisolated struct RoomMessageEventStringBuilder { message = AttributedString(content.body) } case .gallery(let content): - message = AttributedString(content.body) + message = buildGalleryMessage(for: style, content: content) case .other(_, let body): message = AttributedString(body) } @@ -91,6 +91,21 @@ nonisolated struct RoomMessageEventStringBuilder { } } + /// A gallery's body is its caption. Notifications fall back to counting the attachments when + /// there isn't one, whilst elsewhere it is treated as any other media's caption. + private func buildGalleryMessage(for style: Style, content: GalleryMessageContent) -> AttributedString { + switch style { + case .plain: + if content.body.isBlank { + AttributedString(L10n.notificationGalleryBody(content.itemtypes.count)) + } else { + AttributedString(content.body) + } + case .senderPrefixed, .typeBolded: + buildMessage(for: style, caption: content.body, type: L10n.commonGallery) + } + } + private func buildMessage(for style: Style, caption: String?, type: String) -> AttributedString { guard let caption else { return AttributedString(type) diff --git a/NSE/Sources/NotificationContentBuilder.swift b/NSE/Sources/NotificationContentBuilder.swift index 93d543932..c4ed82f6b 100644 --- a/NSE/Sources/NotificationContentBuilder.swift +++ b/NSE/Sources/NotificationContentBuilder.swift @@ -207,6 +207,13 @@ nonisolated struct NotificationContentBuilder { using: mediaProvider, mediaSource: .init(source: content.source, mimeType: content.info?.mimetype)) + case .gallery(content: let content): + // A notification can only show one attachment, so the gallery is represented by its first. + if let mediaSource = content.itemtypes.firstPreviewableMediaSource { + await addMediaAttachment(notificationContent: ¬ificationContent, + using: mediaProvider, + mediaSource: mediaSource) + } default: break } @@ -362,3 +369,24 @@ private nonisolated struct NotificationIcon { groupInfo != nil } } + +private nonisolated extension [GalleryItemType] { + /// The media source of the first attachment that a notification is able to show, skipping the + /// types that it can't such as documents. + var firstPreviewableMediaSource: MediaSourceProxy? { + for itemType in self { + switch itemType { + case .image(let content): + return .init(source: content.source, mimeType: content.info?.mimetype) + case .video(let content): + return .init(source: content.source, mimeType: content.info?.mimetype) + case .audio(let content): + return .init(source: content.source, mimeType: content.info?.mimetype) + case .file, .other: + continue + } + } + + return nil + } +}