mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Render and play InstantPageBlock.audio in the InstantPage V2 renderer
(rich-data message bubbles + the rich send preview), where audio was
previously an inert grey placeholder.
- New InstantPageV2AudioContentNode replicates the standard music message
bubble (ChatMessageInteractiveFileNode's music layout): a SemanticStatusNode
control (album art via playerAlbumArt + play/pause) with a download/progress
overlay, title + "duration · performer" lines, exact fonts/colors from
theme.chat.message.{incoming|outgoing}. Tap is driven by a
UITapGestureRecognizer (ASControl .touchUpInside is cancelled by the chat
ListView's gesture system). V1's InstantPageAudioNode is unchanged.
- Playback runs on InstantPageMediaPlaylist with a discriminated, message-scoped
InstantPageMediaPlaylistId (.instantPage / .richMessage) so concurrent
rich-message audio bubbles don't collide; the big control's play/pause comes
from filteredPlaylistState, the overlay's download/progress from
messageMediaFileStatus, and fetch goes through the fetch manager.
- Rich-message audio fetches via a MessageReference (threaded through the V2
render context) instead of the synthesized webpage; FetchedMediaResource's
.message revalidation arm now also searches RichTextMessageAttribute instant
pages, so a stale instant-page audio/image reference can recover. Corrected a
dormant inverted InstantPagePlaylistLocation.isEqual.
- New .mediaAudio laid-out item + layout/reveal-cost arms; the audio block lays
out full-width at the file node's music normHeight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
61 lines
2.3 KiB
Swift
61 lines
2.3 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import TelegramCore
|
|
import AsyncDisplayKit
|
|
import TelegramPresentationData
|
|
import TelegramUIPreferences
|
|
import AccountContext
|
|
import ContextUI
|
|
|
|
public final class InstantPageAudioItem: InstantPageItem {
|
|
public var frame: CGRect
|
|
public let wantsNode: Bool = true
|
|
public let separatesTiles: Bool = false
|
|
public let medias: [InstantPageMedia]
|
|
|
|
let media: InstantPageMedia
|
|
let webpage: TelegramMediaWebpage
|
|
|
|
public init(frame: CGRect, media: InstantPageMedia, webpage: TelegramMediaWebpage) {
|
|
self.frame = frame
|
|
self.media = media
|
|
self.webpage = webpage
|
|
self.medias = [media]
|
|
}
|
|
|
|
public func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourceLocation: InstantPageSourceLocation, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?, getPreloadedResource: @escaping (String) -> Data?) -> InstantPageNode? {
|
|
return InstantPageAudioNode(context: context, strings: strings, theme: theme, webPage: self.webpage, media: self.media, playlistId: .instantPage(webpageId: self.webpage.webpageId), openMedia: openMedia)
|
|
}
|
|
|
|
public func matchesAnchor(_ anchor: String) -> Bool {
|
|
return false
|
|
}
|
|
|
|
public func matchesNode(_ node: InstantPageNode) -> Bool {
|
|
if let node = node as? InstantPageAudioNode {
|
|
return self.media == node.media
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
public func distanceThresholdGroup() -> Int? {
|
|
return 4
|
|
}
|
|
|
|
public func distanceThresholdWithGroupCount(_ count: Int) -> CGFloat {
|
|
if count > 3 {
|
|
return 1000.0
|
|
} else {
|
|
return CGFloat.greatestFiniteMagnitude
|
|
}
|
|
}
|
|
|
|
public func linkSelectionRects(at point: CGPoint) -> [CGRect] {
|
|
return []
|
|
}
|
|
|
|
public func drawInTile(context: CGContext) {
|
|
}
|
|
}
|
|
|