mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Fix the media gallery's empty state showing up at wrong times.
This commit is contained in:
parent
3a82b88859
commit
f9be39eb4f
@ -29,4 +29,9 @@ extension View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func mediaGalleryTimelineAspectRatio(imageInfo: ImageInfoProxy?) -> some View {
|
||||||
|
aspectRatio(imageInfo?.aspectRatio, contentMode: .fill)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ struct MediaEventsTimelineGroup: Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct MediaEventsTimelineScreenViewState: BindableState {
|
struct MediaEventsTimelineScreenViewState: BindableState {
|
||||||
|
var hasReachedTimelineStart = false
|
||||||
var isBackPaginating = false
|
var isBackPaginating = false
|
||||||
var groups = [MediaEventsTimelineGroup]()
|
var groups = [MediaEventsTimelineGroup]()
|
||||||
|
|
||||||
@ -31,6 +32,10 @@ struct MediaEventsTimelineScreenViewState: BindableState {
|
|||||||
var bindings: MediaEventsTimelineScreenViewStateBindings
|
var bindings: MediaEventsTimelineScreenViewStateBindings
|
||||||
|
|
||||||
var currentPreviewItemID: TimelineItemIdentifier?
|
var currentPreviewItemID: TimelineItemIdentifier?
|
||||||
|
|
||||||
|
var shouldShowEmptyState: Bool {
|
||||||
|
groups.isEmpty && hasReachedTimelineStart
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MediaEventsTimelineScreenViewStateBindings {
|
struct MediaEventsTimelineScreenViewStateBindings {
|
||||||
|
@ -36,13 +36,13 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
init(mediaTimelineViewModel: TimelineViewModelProtocol,
|
init(mediaTimelineViewModel: TimelineViewModelProtocol,
|
||||||
filesTimelineViewModel: TimelineViewModelProtocol,
|
filesTimelineViewModel: TimelineViewModelProtocol,
|
||||||
mediaProvider: MediaProviderProtocol,
|
mediaProvider: MediaProviderProtocol,
|
||||||
screenMode: MediaEventsTimelineScreenMode = .media,
|
initialViewState: MediaEventsTimelineScreenViewState = .init(bindings: .init(screenMode: .media)),
|
||||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||||
self.mediaTimelineViewModel = mediaTimelineViewModel
|
self.mediaTimelineViewModel = mediaTimelineViewModel
|
||||||
self.filesTimelineViewModel = filesTimelineViewModel
|
self.filesTimelineViewModel = filesTimelineViewModel
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
|
|
||||||
super.init(initialViewState: .init(bindings: .init(screenMode: screenMode)), mediaProvider: mediaProvider)
|
super.init(initialViewState: initialViewState, mediaProvider: mediaProvider)
|
||||||
|
|
||||||
state.activeTimelineContextProvider = { [weak self] in
|
state.activeTimelineContextProvider = { [weak self] in
|
||||||
guard let self else { fatalError() }
|
guard let self else { fatalError() }
|
||||||
@ -131,6 +131,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
state.groups = newGroups
|
state.groups = newGroups
|
||||||
|
|
||||||
state.isBackPaginating = (timelineViewState.timelineState.paginationState.backward == .paginating)
|
state.isBackPaginating = (timelineViewState.timelineState.paginationState.backward == .paginating)
|
||||||
|
// state.hasReachedTimelineStart = (timelineViewState.timelineState.paginationState.backward == .timelineEndReached)
|
||||||
backPaginateIfNecessary(paginationStatus: timelineViewState.timelineState.paginationState.backward)
|
backPaginateIfNecessary(paginationStatus: timelineViewState.timelineState.paginationState.backward)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,9 @@ struct MediaEventsTimelineScreen: View {
|
|||||||
}
|
}
|
||||||
.environmentObject(context.viewState.activeTimelineContextProvider())
|
.environmentObject(context.viewState.activeTimelineContextProvider())
|
||||||
.environment(\.timelineContext, context.viewState.activeTimelineContextProvider())
|
.environment(\.timelineContext, context.viewState.activeTimelineContextProvider())
|
||||||
|
.onChange(of: context.screenMode) { _, _ in
|
||||||
|
context.send(viewAction: .changedScreenMode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The scale effects do the following:
|
// The scale effects do the following:
|
||||||
@ -44,7 +47,7 @@ struct MediaEventsTimelineScreen: View {
|
|||||||
// * flip the items on both axes have them render correctly
|
// * flip the items on both axes have them render correctly
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var mainContent: some View {
|
private var mainContent: some View {
|
||||||
if context.viewState.groups.isEmpty, !context.viewState.isBackPaginating {
|
if context.viewState.shouldShowEmptyState {
|
||||||
emptyState
|
emptyState
|
||||||
} else {
|
} else {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@ -60,9 +63,6 @@ struct MediaEventsTimelineScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.scaleEffect(.init(width: 1, height: -1))
|
.scaleEffect(.init(width: 1, height: -1))
|
||||||
.onChange(of: context.screenMode) { _, _ in
|
|
||||||
context.send(viewAction: .changedScreenMode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,12 +76,7 @@ struct MediaEventsTimelineScreen: View {
|
|||||||
Button {
|
Button {
|
||||||
tappedItem(item)
|
tappedItem(item)
|
||||||
} label: {
|
} label: {
|
||||||
Color.clear // Let the image aspect fill in place
|
viewForTimelineItem(item)
|
||||||
.aspectRatio(1, contentMode: .fill)
|
|
||||||
.overlay {
|
|
||||||
viewForTimelineItem(item)
|
|
||||||
}
|
|
||||||
.clipped()
|
|
||||||
.scaleEffect(scale(for: item, isGridLayout: true))
|
.scaleEffect(scale(for: item, isGridLayout: true))
|
||||||
}
|
}
|
||||||
.zoomTransitionSource(id: item.identifier, in: zoomTransition)
|
.zoomTransitionSource(id: item.identifier, in: zoomTransition)
|
||||||
@ -260,7 +255,8 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
MediaEventsTimelineScreenViewModel(mediaTimelineViewModel: makeTimelineViewModel(timelineKind: timelineKind),
|
MediaEventsTimelineScreenViewModel(mediaTimelineViewModel: makeTimelineViewModel(timelineKind: timelineKind),
|
||||||
filesTimelineViewModel: makeTimelineViewModel(timelineKind: timelineKind),
|
filesTimelineViewModel: makeTimelineViewModel(timelineKind: timelineKind),
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
screenMode: screenMode,
|
initialViewState: .init(hasReachedTimelineStart: true,
|
||||||
|
bindings: .init(screenMode: screenMode)),
|
||||||
userIndicatorController: UserIndicatorControllerMock())
|
userIndicatorController: UserIndicatorControllerMock())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,12 @@ struct ImageMediaEventsTimelineView: View {
|
|||||||
let timelineItem: ImageRoomTimelineItem
|
let timelineItem: ImageRoomTimelineItem
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
loadableImage
|
Color.clear // Let the image aspect fill in place
|
||||||
|
.aspectRatio(1, contentMode: .fill)
|
||||||
|
.overlay {
|
||||||
|
loadableImage
|
||||||
|
}
|
||||||
|
.clipped()
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel(L10n.commonImage)
|
.accessibilityLabel(L10n.commonImage)
|
||||||
}
|
}
|
||||||
@ -48,13 +53,6 @@ struct ImageMediaEventsTimelineView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension View {
|
|
||||||
@ViewBuilder
|
|
||||||
func mediaGalleryTimelineAspectRatio(imageInfo: ImageInfoProxy?) -> some View {
|
|
||||||
aspectRatio(imageInfo?.aspectRatio, contentMode: .fill)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview {
|
struct ImageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = TimelineViewModel.mock
|
static let viewModel = TimelineViewModel.mock
|
||||||
|
|
||||||
|
@ -13,7 +13,13 @@ struct VideoMediaEventsTimelineView: View {
|
|||||||
let timelineItem: VideoRoomTimelineItem
|
let timelineItem: VideoRoomTimelineItem
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
thumbnail
|
Color.clear // Let the image aspect fill in place
|
||||||
|
.aspectRatio(1, contentMode: .fill)
|
||||||
|
.overlay {
|
||||||
|
thumbnail
|
||||||
|
}
|
||||||
|
.clipped()
|
||||||
|
.overlay(alignment: .bottom) { overlay }
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel(L10n.commonVideo)
|
.accessibilityLabel(L10n.commonVideo)
|
||||||
}
|
}
|
||||||
@ -25,23 +31,30 @@ struct VideoMediaEventsTimelineView: View {
|
|||||||
mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id),
|
mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id),
|
||||||
blurhash: timelineItem.content.blurhash,
|
blurhash: timelineItem.content.blurhash,
|
||||||
size: timelineItem.content.thumbnailInfo?.size,
|
size: timelineItem.content.thumbnailInfo?.size,
|
||||||
mediaProvider: context?.mediaProvider) { imageView in
|
mediaProvider: context?.mediaProvider) {
|
||||||
imageView
|
|
||||||
.overlay { playIcon }
|
|
||||||
} placeholder: {
|
|
||||||
placeholder
|
placeholder
|
||||||
}
|
}
|
||||||
|
.mediaGalleryTimelineAspectRatio(imageInfo: timelineItem.content.thumbnailInfo)
|
||||||
} else {
|
} else {
|
||||||
playIcon
|
overlay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var playIcon: some View {
|
var overlay: some View {
|
||||||
Image(systemName: "play.circle.fill")
|
HStack(spacing: 0) {
|
||||||
.resizable()
|
CompoundIcon(\.videoCallSolid)
|
||||||
.frame(width: 50, height: 50)
|
Spacer()
|
||||||
.background(.ultraThinMaterial, in: Circle())
|
Text(Date(timeIntervalSince1970: timelineItem.content.videoInfo.duration).formattedTime())
|
||||||
.foregroundColor(.white)
|
}
|
||||||
|
.padding(8)
|
||||||
|
.background {
|
||||||
|
LinearGradient(stops: [.init(color: .clear, location: 0.0),
|
||||||
|
.init(color: .compound.bgCanvasDefault, location: 1.0)],
|
||||||
|
startPoint: .top,
|
||||||
|
endPoint: .bottom)
|
||||||
|
}
|
||||||
|
.font(.compound.bodyXSSemibold)
|
||||||
|
.foregroundStyle(.compound.textPrimary)
|
||||||
}
|
}
|
||||||
|
|
||||||
var placeholder: some View {
|
var placeholder: some View {
|
||||||
|
@ -36,18 +36,20 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
|
|
||||||
init(timelineKind: TimelineKind = .live, listenForSignals: Bool = false) {
|
init(timelineKind: TimelineKind = .live, listenForSignals: Bool = false) {
|
||||||
self.timelineKind = timelineKind
|
self.timelineKind = timelineKind
|
||||||
paginationState = PaginationState(backward: .idle, forward: .timelineEndReached)
|
|
||||||
callbacks.send(.isLive(true))
|
|
||||||
|
|
||||||
switch timelineKind {
|
switch timelineKind {
|
||||||
case .media:
|
case .media:
|
||||||
|
paginationState = PaginationState(backward: .timelineEndReached, forward: .timelineEndReached)
|
||||||
timelineItems = (0..<5).reduce([]) { partialResult, _ in
|
timelineItems = (0..<5).reduce([]) { partialResult, _ in
|
||||||
partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk
|
partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
paginationState = PaginationState(backward: .idle, forward: .timelineEndReached)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callbacks.send(.paginationState(paginationState))
|
||||||
|
callbacks.send(.isLive(true))
|
||||||
|
|
||||||
guard listenForSignals else { return }
|
guard listenForSignals else { return }
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user