Files
element-x-ios/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemView.swift
T
Mauro Romito 4a9cd02fbc Add the gallery timeline item rendered as a placeholder
Introduces the GalleryRoomTimelineItem and its typed GalleryItem model (reusing the existing per-message content types), builds it in the RoomTimelineItemFactory and routes it through RoomTimelineItemView. For now it renders as a placeholder; the grid/list media views and the full-screen preview land in follow-up PRs.

Co-authored-by: svetoslav.mitov <svetoslav.mitov@sportinno.com>
2026-07-24 11:51:00 +02:00

97 lines
4.2 KiB
Swift

//
import OrderedCollections
// Copyright 2025 Element Creations Ltd.
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import SwiftUI
struct RoomTimelineItemView: View {
@Environment(\.timelineContext) var context
@ObservedObject var viewState: RoomTimelineItemViewState
var body: some View {
timelineView
.animation(.elementDefault, value: viewState.groupStyle)
.animation(.elementDefault, value: viewState.type)
.environment(\.timelineGroupStyle, viewState.groupStyle)
.onAppear {
context?.send(viewAction: .itemAppeared(itemID: viewState.identifier))
}
.onDisappear {
context?.send(viewAction: .itemDisappeared(itemID: viewState.identifier))
}
}
@ViewBuilder private var timelineView: some View {
switch viewState.type {
case .text(let item):
TextRoomTimelineView(timelineItem: item, linkMetadata: linkMetadataForItem(item))
case .separator(let item):
SeparatorRoomTimelineView(timelineItem: item)
case .image(let item):
ImageRoomTimelineView(timelineItem: item)
case .video(let item):
VideoRoomTimelineView(timelineItem: item)
case .audio(let item):
AudioRoomTimelineView(timelineItem: item)
case .file(let item):
FileRoomTimelineView(timelineItem: item)
case .gallery(let item):
GalleryRoomTimelineView(timelineItem: item)
case .emote(let item):
EmoteRoomTimelineView(timelineItem: item)
case .notice(let item):
NoticeRoomTimelineView(timelineItem: item)
case .redacted(let item):
RedactedRoomTimelineView(timelineItem: item)
case .encrypted(let item):
EncryptedRoomTimelineView(timelineItem: item)
case .readMarker:
ReadMarkerRoomTimelineView()
case .paginationIndicator:
PaginationIndicatorRoomTimelineView()
case .sticker(let item):
StickerRoomTimelineView(timelineItem: item)
case .unsupported(let item):
UnsupportedRoomTimelineView(timelineItem: item)
case .timelineStart(let item):
TimelineStartRoomTimelineView(timelineItem: item)
case .state(let item):
StateRoomTimelineView(timelineItem: item)
case .group(let item):
CollapsibleRoomTimelineView(timelineItem: item)
case .location(let item):
LocationRoomTimelineView(timelineItem: item)
case .poll(let item):
PollRoomTimelineView(timelineItem: item)
case .voice(let item):
let playerState = context?.viewState.audioPlayerStateProvider?(item.id) ?? AudioPlayerState(id: .timelineItemIdentifier(item.id),
title: L10n.commonVoiceMessage,
duration: 0)
VoiceMessageRoomTimelineView(timelineItem: item, playerState: playerState)
case .callInvite:
CallInviteRoomTimelineView()
case .callNotification(let item):
CallNotificationRoomTimelineView(timelineItem: item)
case .liveLocation(let item):
LiveLocationRoomTimelineView(timelineItem: item, isStopped: context?.viewState.stoppedLiveLocationIDs.contains(item.id) ?? false)
}
}
private func linkMetadataForItem(_ item: TextRoomTimelineItem) -> OrderedDictionary<URL, LinkMetadataProviderItem> {
var linkMetadata = OrderedDictionary<URL, LinkMetadataProviderItem>()
for url in item.links.prefix(TextRoomTimelineView.maxLinkPreviewsToRender) {
if let item = context?.viewState.linkMetadataProvider?.metadataItems[url] {
linkMetadata[url] = item
} else {
linkMetadata[url] = LinkMetadataProviderItem(metadata: nil)
}
}
return linkMetadata
}
}