mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
unified the filtering of the gallery items
This commit is contained in:
@@ -449,12 +449,8 @@ private extension RoomTimelineItemViewState {
|
||||
return [TimelineMediaPreviewItem.Media(roomTimelineItemViewState: self)].compactMap { $0 }
|
||||
}
|
||||
|
||||
return galleryItem.content.items.filter { item in
|
||||
guard let allowedItemType = item.allowedItemType else {
|
||||
return false // Nothing to preview, and unlike a tile there's no index to line up with.
|
||||
}
|
||||
|
||||
return allowedGalleryItemTypes?.contains(allowedItemType) ?? true
|
||||
}.map { TimelineMediaPreviewItem.Media(galleryParent: galleryItem, item: $0) }
|
||||
return galleryItem.content.attachments(allowedTypes: allowedGalleryItemTypes).map {
|
||||
TimelineMediaPreviewItem.Media(galleryParent: galleryItem, item: $0.item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ enum MediaEventsTimelineScreenViewModelAction {
|
||||
case viewInRoomTimeline(TimelineItemIdentifier)
|
||||
}
|
||||
|
||||
nonisolated enum MediaEventsTimelineScreenMode {
|
||||
enum MediaEventsTimelineScreenMode {
|
||||
case media
|
||||
case files
|
||||
}
|
||||
|
||||
+12
-13
@@ -204,7 +204,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
||||
return [itemViewState]
|
||||
case .gallery(let galleryItem):
|
||||
// A gallery's attachments are displayed flattened, listed individually.
|
||||
let flattenedItems = galleryItem.itemsAsIndividualMessages(for: state.screenMode)
|
||||
let flattenedItems = galleryItem.itemsAsIndividualMessages(allowedTypes: timelineViewState.allowedGalleryItemTypes)
|
||||
for (mediaIndex, item) in flattenedItems {
|
||||
newGalleryItemLookup[item.id.uniqueID] = .init(parentID: galleryItem.id, mediaIndex: mediaIndex)
|
||||
}
|
||||
@@ -285,32 +285,31 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
||||
}
|
||||
|
||||
private extension GalleryRoomTimelineItem {
|
||||
/// Represents the gallery's attachments as though each had been sent as an individual message, so
|
||||
/// that the screen can list them alongside the room's other media, keeping only those that belong
|
||||
/// in the given mode. Each one keeps the gallery's event ID but is given its own unique ID so that
|
||||
/// the attachments remain distinct within the list.
|
||||
func itemsAsIndividualMessages(for screenMode: MediaEventsTimelineScreenMode) -> [(mediaIndex: Int, item: EventBasedMessageTimelineItemProtocol)] {
|
||||
/// Represents the gallery's attachments of the given types as though each had been sent as an
|
||||
/// individual message, so that the screen can list them alongside the room's other media. Each one
|
||||
/// keeps the gallery's event ID but is given its own unique ID so that they remain distinct.
|
||||
func itemsAsIndividualMessages(allowedTypes: [TimelineAllowedGalleryItemType]?) -> [(mediaIndex: Int, item: EventBasedMessageTimelineItemProtocol)] {
|
||||
guard let eventOrTransactionID = id.eventOrTransactionID else { return [] }
|
||||
|
||||
return content.items.enumerated().compactMap { mediaIndex, galleryItem in
|
||||
return content.attachments(allowedTypes: allowedTypes).compactMap { mediaIndex, galleryItem in
|
||||
let itemID = TimelineItemIdentifier.event(uniqueID: .init("\(id.uniqueID.value)-\(mediaIndex)"),
|
||||
eventOrTransactionID: eventOrTransactionID)
|
||||
|
||||
let item: EventBasedMessageTimelineItemProtocol? = switch galleryItem {
|
||||
case .image(_, let content) where screenMode == .media:
|
||||
case .image(_, let content):
|
||||
ImageRoomTimelineItem(id: itemID, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isEditable,
|
||||
canBeRepliedTo: canBeRepliedTo, sender: sender, content: content, properties: properties)
|
||||
case .video(_, let content) where screenMode == .media:
|
||||
case .video(_, let content):
|
||||
VideoRoomTimelineItem(id: itemID, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isEditable,
|
||||
canBeRepliedTo: canBeRepliedTo, sender: sender, content: content, properties: properties)
|
||||
case .audio(_, let content) where screenMode == .files:
|
||||
case .audio(_, let content):
|
||||
AudioRoomTimelineItem(id: itemID, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isEditable,
|
||||
canBeRepliedTo: canBeRepliedTo, sender: sender, content: content, properties: properties)
|
||||
case .file(_, let content) where screenMode == .files:
|
||||
case .file(_, let content):
|
||||
FileRoomTimelineItem(id: itemID, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isEditable,
|
||||
canBeRepliedTo: canBeRepliedTo, sender: sender, content: content, properties: properties)
|
||||
default:
|
||||
nil // Attachments of an unknown type, or that belong in the other mode.
|
||||
case .other:
|
||||
nil // Filtered out above, as there's nothing to show for it.
|
||||
}
|
||||
|
||||
guard let item else { return nil }
|
||||
|
||||
+9
-1
@@ -361,7 +361,15 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
|
||||
timelineItems.append(contentsOf: [makeFileItem(url: .mockMXCScanning), makeFileItem(url: .mockMXCUnsafe)])
|
||||
}
|
||||
|
||||
return TimelineControllerMock(.init(timelineKind: .media(.mediaFilesScreen), timelineItems: timelineItems))
|
||||
// Mirrors the message types that the flow coordinator filters each timeline to.
|
||||
let allowedGalleryItemTypes: [TimelineAllowedGalleryItemType] = switch screenMode {
|
||||
case .media: [.image, .video]
|
||||
case .files: [.file, .audio]
|
||||
}
|
||||
|
||||
return TimelineControllerMock(.init(timelineKind: .media(.mediaFilesScreen),
|
||||
timelineItems: timelineItems,
|
||||
allowedGalleryItemTypes: allowedGalleryItemTypes))
|
||||
}
|
||||
|
||||
private static func makeImageItem(url: URL) -> ImageRoomTimelineItem {
|
||||
|
||||
+15
@@ -15,6 +15,21 @@ nonisolated struct GalleryRoomTimelineItemContent: Hashable {
|
||||
/// The original textual representation of the formatted caption directly from the event (usually HTML code)
|
||||
var formattedCaptionHTMLString: String?
|
||||
let items: [GalleryItem]
|
||||
|
||||
/// The attachments of the given types, paired with their position in the gallery so that they remain
|
||||
/// identifiable once separated from it. Attachments of an unknown type are never included as there's
|
||||
/// nothing to show for them.
|
||||
/// - Parameter allowedTypes: The types to include, or `nil` to include them all.
|
||||
func attachments(allowedTypes: [TimelineAllowedGalleryItemType]?) -> [(mediaIndex: Int, item: GalleryItem)] {
|
||||
items.enumerated().compactMap { mediaIndex, item in
|
||||
guard let allowedItemType = item.allowedItemType,
|
||||
allowedTypes?.contains(allowedItemType) ?? true else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (mediaIndex, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifies a single attachment within a gallery message.
|
||||
|
||||
@@ -12,8 +12,8 @@ import Testing
|
||||
struct MediaEventsTimelineScreenViewModelTests {
|
||||
@Test
|
||||
func changingScreenModeSwitchesTimelines() {
|
||||
let mediaTimelineViewModel = makeTimelineViewModel()
|
||||
let filesTimelineViewModel = makeTimelineViewModel()
|
||||
let mediaTimelineViewModel = makeTimelineViewModel(allowedGalleryItemTypes: [.image, .video])
|
||||
let filesTimelineViewModel = makeTimelineViewModel(allowedGalleryItemTypes: [.file, .audio])
|
||||
|
||||
let viewModel = MediaEventsTimelineScreenViewModel(mediaTimelineViewModel: mediaTimelineViewModel,
|
||||
filesTimelineViewModel: filesTimelineViewModel,
|
||||
@@ -45,10 +45,12 @@ struct MediaEventsTimelineScreenViewModelTests {
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
// Both timelines are given the same mixed chunk of items so that the mode based filtering is what's under test.
|
||||
private func makeTimelineViewModel() -> TimelineViewModel {
|
||||
// Both timelines are given the same mixed chunk of items, filtered as they are in the app, so that
|
||||
// the filtering of the items is what's under test.
|
||||
private func makeTimelineViewModel(allowedGalleryItemTypes: [TimelineAllowedGalleryItemType]) -> TimelineViewModel {
|
||||
let timelineController = TimelineControllerMock(.init(timelineKind: .media(.mediaFilesScreen),
|
||||
timelineItems: [TimelineFixtures.separator] + TimelineFixtures.mediaChunk))
|
||||
timelineItems: [TimelineFixtures.separator] + TimelineFixtures.mediaChunk,
|
||||
allowedGalleryItemTypes: allowedGalleryItemTypes))
|
||||
return TimelineViewModel.mock(timelineKind: .media(.mediaFilesScreen), timelineController: timelineController)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user