Content scanning view for reply preview (#5842)

* implement a MediaView to delegate at the view level the usage of the content scanner service, to determine how to render the media.

* fix for a small design mismatch

* fix for greedy error state

* updated naming

* just making some screenshot that have been affected by a string change on Localazy

* pr suggestions + updated strings

* implemented content scanning in the timeline reply view

* pr suggestions
This commit is contained in:
Mauro
2026-07-07 17:49:16 +02:00
committed by GitHub
parent 834938f585
commit 9e1d95db1c
11 changed files with 152 additions and 21 deletions
@@ -43,6 +43,7 @@ struct ContentScanningView<SafeContent: View, ScanningContent: View, UnsafeConte
private let contentScannerService: ContentScannerServiceProtocol?
private let mediaSource: MediaSourceProxy?
private let containerShowsFailure: Bool
private let safeContent: () -> SafeContent
private let scanningContent: () -> ScanningContent
private let unsafeContent: (ContentScanningFailure) -> UnsafeContent
@@ -54,16 +55,22 @@ struct ContentScanningView<SafeContent: View, ScanningContent: View, UnsafeConte
/// scanning and the content is always rendered.
/// - mediaSource: The media source to scan. Passing `nil` renders the content, this
/// makes it convenient to wrap items whose media source is optional.
/// - containerShowsFailure: Whether a failure is reported up the view hierarchy through
/// ``ContentScanningFailurePreferenceKey`` so that the surrounding container (such as the
/// message bubble) adopts the critical styling. Pass `false` when the failure should only
/// affect this view, e.g. inside a reply preview where the bubble must stay regular.
/// - safeContent: The regular presentation of the media, including any interactions with it.
/// - scanningContent: The presentation of the media whilst it's being scanned.
/// - unsafeContent: The presentation of the media when it can't be shown.
init(contentScannerService: ContentScannerServiceProtocol?,
mediaSource: MediaSourceProxy?,
containerShowsFailure: Bool = true,
@ViewBuilder safeContent: @escaping () -> SafeContent,
@ViewBuilder scanningContent: @escaping () -> ScanningContent,
@ViewBuilder unsafeContent: @escaping (ContentScanningFailure) -> UnsafeContent) {
self.contentScannerService = contentScannerService
self.mediaSource = mediaSource
self.containerShowsFailure = containerShowsFailure
self.safeContent = safeContent
self.scanningContent = scanningContent
self.unsafeContent = unsafeContent
@@ -71,7 +78,7 @@ struct ContentScanningView<SafeContent: View, ScanningContent: View, UnsafeConte
var body: some View {
content
.preference(key: ContentScanningFailurePreferenceKey.self, value: scanFailure)
.preference(key: ContentScanningFailurePreferenceKey.self, value: containerShowsFailure ? scanFailure : nil)
.task(id: mediaSource?.url.absoluteString) {
await scan()
}