InstantPage rich messages: index embedded media into shared-media tags

Add InstantPage.allMedia() (recursive gatherer over the page's blocks —
audio/collage/cover/details/image/list/slideshow/video — resolving each via
the page's [MediaId: Media] dict) and feed it into tagsForStoreMessage, so a
rich message's instant-page media is indexed into MessageTags
(photo/photoOrVideo/video/gif/voice/file). Rich messages now appear in the
per-peer shared-media tabs and tag-queried surfaces.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
isaac 2026-06-04 23:46:16 +02:00
parent 41aef1c928
commit c95e014681
2 changed files with 134 additions and 0 deletions

View file

@ -7,6 +7,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
var isSecret = false
var isUnconsumedPersonalMention = false
var hasUnseenReactions = false
var richText: RichTextMessageAttribute?
for attribute in attributes {
if let timerAttribute = attribute as? AutoclearTimeoutMessageAttribute {
if timerAttribute.timeout > 0 && (timerAttribute.timeout <= 60 || timerAttribute.timeout == viewOnceTimeout) {
@ -22,6 +23,8 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
}
} else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen {
hasUnseenReactions = true
} else if let attribute = attribute as? RichTextMessageAttribute {
richText = attribute
}
}
@ -134,6 +137,63 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
tags.insert(.unseenPollVote)
}
if let attribute = richText {
//TODO:rewrite to take all media
for media in attribute.instantPage.allMedia() {
switch media {
case _ as TelegramMediaImage:
tags.insert(.photo)
tags.insert(.photoOrVideo)
case let file as TelegramMediaFile:
var refinedTag: MessageTags? = .file
var isAnimated = false
inner: for attribute in file.attributes {
switch attribute {
case let .Video(_, _, flags, _, _, _):
if flags.contains(.instantRoundVideo) {
refinedTag = .voiceOrInstantVideo
} else {
if !isSecret {
refinedTag = [.photoOrVideo, .video]
} else {
refinedTag = nil
}
}
case let .Audio(isVoice, _, _, _, _):
if isVoice {
refinedTag = .voiceOrInstantVideo
} else {
if file.isInstantVideo {
refinedTag = .voiceOrInstantVideo
} else {
refinedTag = .music
}
}
break inner
case .Sticker:
refinedTag = nil
break inner
case .Animated:
isAnimated = true
default:
break
}
}
if isAnimated {
refinedTag = .gif
}
if file.isAnimatedSticker {
refinedTag = nil
}
if let refinedTag {
tags.insert(refinedTag)
}
default:
break
}
}
}
return (tags, globalTags)
}

View file

@ -1652,6 +1652,80 @@ public final class InstantPage: PostboxCoding, Equatable {
}
}
private extension InstantPageBlock {
func allMedia(mediaDict: [MediaId: Media]) -> [Media] {
switch self {
case let .audio(id, _):
if let file = mediaDict[id] {
return [file]
} else {
return []
}
case let .collage(items, _):
var result: [Media] = []
for item in items {
result.append(contentsOf: item.allMedia(mediaDict: mediaDict))
}
return result
case let .cover(block):
return block.allMedia(mediaDict: mediaDict)
case let .details(_, blocks, _):
var result: [Media] = []
for item in blocks {
result.append(contentsOf: item.allMedia(mediaDict: mediaDict))
}
return result
case let .image(id, _, _, _):
if let image = mediaDict[id] {
return [image]
} else {
return []
}
case let .list(items, _):
for item in items {
switch item {
case let .blocks(blocks, _, _):
var result: [Media] = []
for block in blocks {
result.append(contentsOf: block.allMedia(mediaDict: mediaDict))
}
return result
case .text, .unknown:
break
}
}
return []
case let .slideshow(items, _):
var result: [Media] = []
for item in items {
result.append(contentsOf: item.allMedia(mediaDict: mediaDict))
}
return result
case let .video(id, _, _, _):
if let video = mediaDict[id] {
return [video]
} else {
return []
}
default:
return []
}
}
}
public extension InstantPage {
func allMedia() -> [Media] {
if self.media.isEmpty {
return []
}
var result: [Media] = []
for block in self.blocks {
result.append(contentsOf: block.allMedia(mediaDict: self.media))
}
return result
}
}
public extension InstantPage {
struct Accessor: Equatable {
let _wrappedInstantPage: InstantPage?