mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
Use the new preview screen when tapping media on the room and pinned events screens. (#3736)
* Use the new TimelineMediaPreview modifier on the room and pinned timeline screens. * Use the same presentation logic for all timeline media previews. * Fix a bug with the detection of the timeline end. * Send pagination requests from the media preview screen. * Add SwiftLint to the Danger workflow (it is no longer installed on the runner). * Put SwiftLint back on all of the GitHub runners too. * Set the function_parameter_count lint rule to 10. * Make sure to clean-up any previews when the coordinator is done. * Handle the viewInRoomTimeline action more appropriately.
This commit is contained in:
@@ -14,7 +14,6 @@ import XCTest
|
||||
class HomeScreenRoomTests: XCTestCase {
|
||||
var roomSummary: RoomSummary!
|
||||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func setupRoomSummary(isMarkedUnread: Bool,
|
||||
unreadMessagesCount: UInt,
|
||||
unreadMentionsCount: UInt,
|
||||
|
||||
@@ -23,7 +23,7 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
initialMediaViewStates = initialMediaItems.map { RoomTimelineItemViewState(item: $0, groupStyle: .single) }
|
||||
}
|
||||
|
||||
func testInitialItems() -> TimelineMediaPreviewDataSource {
|
||||
func testInitialItems() throws -> TimelineMediaPreviewDataSource {
|
||||
// Given a data source built with the initial items.
|
||||
let dataSource = TimelineMediaPreviewDataSource(itemViewStates: initialMediaViewStates,
|
||||
initialItem: initialMediaItems[initialItemIndex],
|
||||
@@ -32,12 +32,13 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
|
||||
// When the preview controller displays the data.
|
||||
let previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
let displayedItem = dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media
|
||||
let displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media,
|
||||
"A preview item should be found.")
|
||||
|
||||
// Then the preview controller should be showing the initial item and the data source should reflect this.
|
||||
XCTAssertEqual(dataSource.initialItemIndex, initialItemIndex + initialPadding, "The initial item index should be padded for the preview controller.")
|
||||
XCTAssertEqual(displayedItem?.id, initialMediaItems[initialItemIndex].id, "The displayed item should be the initial item.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id, "The current item should also be the initial item.")
|
||||
XCTAssertEqual(displayedItem.id, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The displayed item should be the initial item.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The current item should also be the initial item.")
|
||||
|
||||
XCTAssertEqual(dataSource.previewItems.count, initialMediaViewStates.count, "The initial count of preview items should be correct.")
|
||||
XCTAssertEqual(previewItemCount, initialMediaViewStates.count + (2 * initialPadding), "The initial item count should be padded for the preview controller.")
|
||||
@@ -45,17 +46,15 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func testCurrentUpdateItem() {
|
||||
func testCurrentUpdateItem() throws {
|
||||
// Given a data source built with the initial items.
|
||||
let dataSource = TimelineMediaPreviewDataSource(itemViewStates: initialMediaViewStates,
|
||||
initialItem: initialMediaItems[initialItemIndex],
|
||||
paginationState: .initial)
|
||||
|
||||
// When a different item is displayed.
|
||||
guard let previewItem = dataSource.previewController(previewController, previewItemAt: 1 + initialPadding) as? TimelineMediaPreviewItem.Media else {
|
||||
XCTFail("A preview item should be found.")
|
||||
return
|
||||
}
|
||||
let previewItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: 1 + initialPadding) as? TimelineMediaPreviewItem.Media,
|
||||
"A preview item should be found.")
|
||||
dataSource.updateCurrentItem(.media(previewItem))
|
||||
|
||||
// Then the data source should reflect the change of item.
|
||||
@@ -74,7 +73,7 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
|
||||
func testUpdatedItems() async throws {
|
||||
// Given a data source built with the initial items.
|
||||
let dataSource = testInitialItems()
|
||||
let dataSource = try testInitialItems()
|
||||
|
||||
// When one of the items changes but no pagination has occurred.
|
||||
let deferred = deferFailure(dataSource.previewItemsPaginationPublisher, timeout: 1) { _ in true }
|
||||
@@ -84,9 +83,9 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
try await deferred.fulfill()
|
||||
|
||||
let previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
let displayedItem = dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media
|
||||
XCTAssertEqual(displayedItem?.id, initialMediaItems[initialItemIndex].id, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id, "The current item should not change.")
|
||||
let displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media)
|
||||
XCTAssertEqual(displayedItem.id, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The current item should not change.")
|
||||
|
||||
XCTAssertEqual(dataSource.previewItems.count, initialMediaViewStates.count, "The number of items should not change.")
|
||||
XCTAssertEqual(previewItemCount, initialMediaViewStates.count + (2 * initialPadding), "The padded number of items should not change.")
|
||||
@@ -94,7 +93,7 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
|
||||
func testPagination() async throws {
|
||||
// Given a data source built with the initial items.
|
||||
let dataSource = testInitialItems()
|
||||
let dataSource = try testInitialItems()
|
||||
|
||||
// When more items are loaded in a back pagination.
|
||||
var deferred = deferFulfillment(dataSource.previewItemsPaginationPublisher) { _ in true }
|
||||
@@ -107,9 +106,9 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
XCTAssertEqual(dataSource.previewItems.count, newViewStates.count, "The new items should be added.")
|
||||
|
||||
var previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
var displayedItem = dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media
|
||||
XCTAssertEqual(displayedItem?.id, initialMediaItems[initialItemIndex].id, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id, "The current item should not change.")
|
||||
var displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media)
|
||||
XCTAssertEqual(displayedItem.id, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The current item should not change.")
|
||||
XCTAssertEqual(previewItemCount, initialMediaViewStates.count + (2 * initialPadding), "The number of items should not change")
|
||||
|
||||
// When more items are loaded in a forward pagination or sync.
|
||||
@@ -123,16 +122,16 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
XCTAssertEqual(dataSource.previewItems.count, newViewStates.count, "The new items should be added.")
|
||||
|
||||
previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
displayedItem = dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media
|
||||
XCTAssertEqual(displayedItem?.id, initialMediaItems[initialItemIndex].id, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id, "The current item should not change.")
|
||||
displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media)
|
||||
XCTAssertEqual(displayedItem.id, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The current item should not change.")
|
||||
XCTAssertEqual(previewItemCount, initialMediaViewStates.count + (2 * initialPadding), "The number of items should not change")
|
||||
}
|
||||
|
||||
func testPaginationLimits() async throws {
|
||||
// Given a data source with a small amount of padding remaining.
|
||||
initialPadding = 2
|
||||
let dataSource = testInitialItems()
|
||||
let dataSource = try testInitialItems()
|
||||
|
||||
// When paginating backwards by more than the available padding.
|
||||
var deferred = deferFulfillment(dataSource.previewItemsPaginationPublisher) { _ in true }
|
||||
@@ -146,9 +145,9 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
XCTAssertEqual(dataSource.previewItems.count, newViewStates.count, "The new items should be added.")
|
||||
|
||||
var previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
var displayedItem = dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media
|
||||
XCTAssertEqual(displayedItem?.id, initialMediaItems[initialItemIndex].id, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id, "The current item should not change.")
|
||||
var displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media)
|
||||
XCTAssertEqual(displayedItem.id, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The current item should not change.")
|
||||
XCTAssertEqual(previewItemCount, initialMediaViewStates.count + (2 * initialPadding), "The number of items should not change")
|
||||
|
||||
// When paginating forwards by more than the available padding.
|
||||
@@ -162,12 +161,92 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
XCTAssertEqual(dataSource.previewItems.count, newViewStates.count, "The new items should be added.")
|
||||
|
||||
previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
displayedItem = dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media
|
||||
XCTAssertEqual(displayedItem?.id, initialMediaItems[initialItemIndex].id, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id, "The current item should not change.")
|
||||
displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media)
|
||||
XCTAssertEqual(displayedItem.id, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The current item should not change.")
|
||||
XCTAssertEqual(previewItemCount, initialMediaViewStates.count + (2 * initialPadding), "The number of items should not change")
|
||||
}
|
||||
|
||||
func testEmptyTimeline() async throws {
|
||||
// Given a data source built with no timeline items loaded.
|
||||
let initialItem = initialMediaItems[initialItemIndex]
|
||||
let dataSource = TimelineMediaPreviewDataSource(itemViewStates: [],
|
||||
initialItem: initialItem,
|
||||
initialPadding: initialPadding,
|
||||
paginationState: .initial)
|
||||
|
||||
// When the preview controller displays the data.
|
||||
var previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
var displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media,
|
||||
"A preview item should be found.")
|
||||
|
||||
// Then the preview controller should always show the initial item.
|
||||
XCTAssertEqual(dataSource.previewItems.count, 1, "The initial item should be in the preview items array.")
|
||||
XCTAssertEqual(previewItemCount, 1 + (2 * initialPadding), "The initial item count should be padded for the preview controller.")
|
||||
XCTAssertEqual(dataSource.initialItemIndex, initialPadding, "The initial item index should be padded for the preview controller.")
|
||||
|
||||
XCTAssertEqual(displayedItem.id, initialItem.id.eventOrTransactionID, "The displayed item should be the initial item.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialItem.id.eventOrTransactionID, "The current item should also be the initial item.")
|
||||
|
||||
// When the timeline loads the initial items.
|
||||
let deferred = deferFulfillment(dataSource.previewItemsPaginationPublisher) { _ in true }
|
||||
let loadedItems = initialMediaItems.map { RoomTimelineItemViewState(item: $0, groupStyle: .single) }
|
||||
dataSource.updatePreviewItems(itemViewStates: loadedItems)
|
||||
try await deferred.fulfill()
|
||||
|
||||
// Then the preview controller should still show the initial item with the other items loaded around it.
|
||||
previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media,
|
||||
"A preview item should be found.")
|
||||
|
||||
XCTAssertEqual(dataSource.previewItems.count, initialMediaViewStates.count, "The preview items should now be loaded.")
|
||||
XCTAssertEqual(previewItemCount, 1 + (2 * initialPadding), "The item count should not change as the padding will be reduced.")
|
||||
XCTAssertEqual(dataSource.initialItemIndex, initialPadding, "The item index should not change.")
|
||||
|
||||
XCTAssertEqual(displayedItem.id, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialMediaItems[initialItemIndex].id.eventOrTransactionID, "The current item should not change.")
|
||||
}
|
||||
|
||||
func testTimelineUpdateWithoutInitialItem() async throws {
|
||||
// Given a data source built with no timeline items loaded.
|
||||
let initialItem = initialMediaItems[initialItemIndex]
|
||||
let dataSource = TimelineMediaPreviewDataSource(itemViewStates: [],
|
||||
initialItem: initialItem,
|
||||
initialPadding: initialPadding,
|
||||
paginationState: .initial)
|
||||
|
||||
// When the preview controller displays the data.
|
||||
var previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
var displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media,
|
||||
"A preview item should be found.")
|
||||
|
||||
// Then the preview controller should always show the initial item.
|
||||
XCTAssertEqual(dataSource.previewItems.count, 1, "The initial item should be in the preview items array.")
|
||||
XCTAssertEqual(previewItemCount, 1 + (2 * initialPadding), "The initial item count should be padded for the preview controller.")
|
||||
XCTAssertEqual(dataSource.initialItemIndex, initialPadding, "The initial item index should be padded for the preview controller.")
|
||||
|
||||
XCTAssertEqual(displayedItem.id, initialItem.id.eventOrTransactionID, "The displayed item should be the initial item.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialItem.id.eventOrTransactionID, "The current item should also be the initial item.")
|
||||
|
||||
// When the timeline loads more items but still doesn't include the initial item.
|
||||
let failure = deferFailure(dataSource.previewItemsPaginationPublisher, timeout: 1) { _ in true }
|
||||
let loadedItems = newChunk().map { RoomTimelineItemViewState(item: $0, groupStyle: .single) }
|
||||
dataSource.updatePreviewItems(itemViewStates: loadedItems)
|
||||
try await failure.fulfill()
|
||||
|
||||
// Then the preview controller shouldn't update the available preview items.
|
||||
previewItemCount = dataSource.numberOfPreviewItems(in: previewController)
|
||||
displayedItem = try XCTUnwrap(dataSource.previewController(previewController, previewItemAt: dataSource.initialItemIndex) as? TimelineMediaPreviewItem.Media,
|
||||
"A preview item should be found.")
|
||||
|
||||
XCTAssertEqual(dataSource.previewItems.count, 1, "No new items should have been added to the array.")
|
||||
XCTAssertEqual(previewItemCount, 1 + (2 * initialPadding), "The initial item count should not change.")
|
||||
XCTAssertEqual(dataSource.initialItemIndex, initialPadding, "The initial item index should not change.")
|
||||
|
||||
XCTAssertEqual(displayedItem.id, initialItem.id.eventOrTransactionID, "The displayed item should not change.")
|
||||
XCTAssertEqual(dataSource.currentMediaItemID, initialItem.id.eventOrTransactionID, "The current item not change.")
|
||||
}
|
||||
|
||||
// MARK: Helpers
|
||||
|
||||
func newChunk() -> [EventBasedMessageTimelineItemProtocol] {
|
||||
@@ -178,7 +257,7 @@ class TimelineMediaPreviewDataSourceTests: XCTestCase {
|
||||
}
|
||||
|
||||
private extension TimelineMediaPreviewDataSource {
|
||||
var currentMediaItemID: TimelineItemIdentifier? {
|
||||
var currentMediaItemID: TimelineItemIdentifier.EventOrTransactionID? {
|
||||
switch currentItem {
|
||||
case .media(let mediaItem): mediaItem.id
|
||||
case .loading: nil
|
||||
|
||||
@@ -84,18 +84,22 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
XCTAssertEqual(context.viewState.currentItem, .media(context.viewState.dataSource.previewItems[0]))
|
||||
}
|
||||
|
||||
func testLoadingMoreItem() async throws {
|
||||
func testLoadingMoreItems() async throws {
|
||||
// Given a view model with a loaded item.
|
||||
try await testLoadingItem()
|
||||
XCTAssertEqual(timelineController.paginateBackwardsCallCount, 0)
|
||||
|
||||
// When swiping to a "loading more" item.
|
||||
// When swiping to a "loading more" item and there are more media items to load.
|
||||
timelineController.paginationState = .init(backward: .idle, forward: .timelineEndReached)
|
||||
timelineController.backPaginationResponses.append(RoomTimelineItemFixtures.mediaChunk)
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: 1) { $0.isItemLoaded }
|
||||
context.send(viewAction: .updateCurrentItem(.loading(.paginating)))
|
||||
context.send(viewAction: .updateCurrentItem(.loading(.paginatingBackwards)))
|
||||
try await failure.fulfill()
|
||||
|
||||
// Then there should no longer be a media preview and no attempt should be made to load one.
|
||||
// Then there should no longer be a media preview and instead of loading any media, a pagination request should be made.
|
||||
XCTAssertEqual(mediaProvider.loadFileFromSourceFilenameCallsCount, 1)
|
||||
XCTAssertEqual(context.viewState.currentItem, .loading(.paginating))
|
||||
XCTAssertEqual(context.viewState.currentItem, .loading(.paginatingBackwards)) // Note: This item only changes when the preview controller handles the new items.
|
||||
XCTAssertEqual(timelineController.paginateBackwardsCallCount, 1)
|
||||
}
|
||||
|
||||
func testPagination() async throws {
|
||||
@@ -130,7 +134,7 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
return
|
||||
}
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .viewInRoomTimeline(mediaItem.id) }
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .viewInRoomTimeline(mediaItem.timelineItem.id) }
|
||||
context.send(viewAction: .menuAction(.viewInRoomTimeline, item: mediaItem))
|
||||
|
||||
// Then the action should be sent upwards to make this happen.
|
||||
|
||||
Reference in New Issue
Block a user