Configure the media preview screen based on the event and presentation. (#3604)

* Configure media previews based on the presenting screen.

* Switch on TimelineKind instead of having an isPinnedEventsTimeline Bool.
This commit is contained in:
Doug
2024-12-11 15:40:31 +00:00
committed by GitHub
parent 9856e3e5b4
commit c827ab9165
35 changed files with 334 additions and 242 deletions
@@ -11,6 +11,7 @@ import Foundation
typealias TimelineMediaPreviewViewModelType = StateStoreViewModel<TimelineMediaPreviewViewState, TimelineMediaPreviewViewAction>
class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
private let timelineViewModel: TimelineViewModelProtocol
private let mediaProvider: MediaProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol
@@ -19,26 +20,51 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
actionsSubject.eraseToAnyPublisher()
}
init(previewItems: [EventBasedMessageTimelineItemProtocol], mediaProvider: MediaProviderProtocol, userIndicatorController: UserIndicatorControllerProtocol) {
init(initialItem: EventBasedMessageTimelineItemProtocol,
timelineViewModel: TimelineViewModelProtocol,
mediaProvider: MediaProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol) {
self.timelineViewModel = timelineViewModel
self.mediaProvider = mediaProvider
// We might not want to inject this, instead creating a new instance with a custom position and colour scheme 🤔
self.userIndicatorController = userIndicatorController
super.init(initialViewState: TimelineMediaPreviewViewState(previewItems: previewItems.map(TimelineMediaPreviewItem.init)), mediaProvider: mediaProvider)
let currentItem = TimelineMediaPreviewItem(timelineItem: initialItem)
super.init(initialViewState: TimelineMediaPreviewViewState(previewItems: [currentItem],
currentItem: currentItem),
mediaProvider: mediaProvider)
rebuildCurrentItemActions()
timelineViewModel.context.$viewState.map(\.canCurrentUserRedactSelf)
.merge(with: timelineViewModel.context.$viewState.map(\.canCurrentUserRedactOthers))
.sink { [weak self] _ in
self?.rebuildCurrentItemActions()
}
.store(in: &cancellables)
}
override func process(viewAction: TimelineMediaPreviewViewAction) {
switch viewAction {
case .viewInTimeline:
actionsSubject.send(.viewInTimeline)
case .redact:
case .menuAction(let action):
switch action {
case .viewInRoomTimeline:
actionsSubject.send(.viewInTimeline)
case .redact:
state.bindings.isPresentingRedactConfirmation = true
default:
MXLog.error("Received unexpected action: \(action)")
}
case .redactConfirmation:
break // Do it here??
}
}
func updateCurrentItem(_ previewItem: TimelineMediaPreviewItem) async {
state.currentItem = previewItem
rebuildCurrentItemActions()
if previewItem.fileHandle == nil, let source = previewItem.mediaSource {
showDownloadingIndicator(itemID: previewItem.id)
@@ -50,11 +76,26 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
actionsSubject.send(.loadedMediaFile)
case .failure(let error):
MXLog.error("Failed loading media: \(error)")
#warning("Show the error!")
showDownloadErrorIndicator()
}
}
}
func rebuildCurrentItemActions() {
let timelineContext = timelineViewModel.context
let provider = TimelineItemMenuActionProvider(timelineItem: state.currentItem.timelineItem,
canCurrentUserRedactSelf: timelineContext.viewState.canCurrentUserRedactSelf,
canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers,
canCurrentUserPin: timelineContext.viewState.canCurrentUserPin,
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
isDM: timelineContext.viewState.isEncryptedOneToOneRoom,
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
isCreateMediaCaptionsEnabled: timelineContext.viewState.isCreateMediaCaptionsEnabled,
timelineKind: timelineContext.viewState.timelineKind,
emojiProvider: timelineContext.viewState.emojiProvider)
state.currentItemActions = provider.makeActions()
}
private func showDownloadingIndicator(itemID: TimelineItemIdentifier) {
let indicatorID = makeDownloadIndicatorID(itemID: itemID)
userIndicatorController.submitIndicator(UserIndicator(id: indicatorID,
@@ -69,7 +110,16 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
userIndicatorController.retractIndicatorWithId(indicatorID)
}
private func showDownloadErrorIndicator() {
// FIXME: Add the correct string and indicator type??
userIndicatorController.submitIndicator(UserIndicator(id: downloadErrorIndicatorID,
type: .modal,
title: L10n.errorUnknown,
iconName: "exclamationmark.circle.fill"))
}
private var downloadErrorIndicatorID: String { "\(Self.self)-DownloadError" }
private func makeDownloadIndicatorID(itemID: TimelineItemIdentifier) -> String {
"\(TimelineMediaPreviewViewModel.self)-Download-\(itemID.uniqueID.id)"
"\(Self.self)-Download-\(itemID.uniqueID.id)"
}
}