mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
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:
@@ -85,7 +85,7 @@ enum TimelineComposerAction {
|
||||
}
|
||||
|
||||
struct TimelineViewState: BindableState {
|
||||
let isPinnedEventsTimeline: Bool
|
||||
let timelineKind: TimelineKind
|
||||
var roomID: String
|
||||
var members: [String: RoomMemberState] = [:]
|
||||
var typingMembers: [String] = []
|
||||
|
||||
@@ -316,7 +316,7 @@ class TimelineTableViewController: UIViewController {
|
||||
var snapshot = NSDiffableDataSourceSnapshot<TimelineSection, TimelineUniqueId>()
|
||||
|
||||
// We don't want to display the typing notification in this timeline
|
||||
if !coordinator.context.viewState.isPinnedEventsTimeline {
|
||||
if coordinator.context.viewState.timelineKind != .pinned {
|
||||
snapshot.appendSections([.typingIndicator])
|
||||
snapshot.appendItems([TimelineUniqueId(id: TimelineTypingIndicatorCell.reuseIdentifier)])
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
appSettings: appSettings,
|
||||
analyticsService: analyticsService)
|
||||
|
||||
super.init(initialViewState: TimelineViewState(isPinnedEventsTimeline: timelineController.timelineKind == .pinned,
|
||||
super.init(initialViewState: TimelineViewState(timelineKind: timelineController.timelineKind,
|
||||
roomID: roomProxy.id,
|
||||
isEncryptedOneToOneRoom: roomProxy.isEncryptedOneToOneRoom,
|
||||
timelineState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }),
|
||||
@@ -690,13 +690,13 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
} else {
|
||||
for (index, item) in itemGroup.enumerated() {
|
||||
if index == 0 {
|
||||
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .first),
|
||||
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.timelineKind == .pinned ? .single : .first),
|
||||
forKey: item.id.uniqueID)
|
||||
} else if index == itemGroup.count - 1 {
|
||||
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .last),
|
||||
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.timelineKind == .pinned ? .single : .last),
|
||||
forKey: item.id.uniqueID)
|
||||
} else {
|
||||
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .middle),
|
||||
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.timelineKind == .pinned ? .single : .middle),
|
||||
forKey: item.id.uniqueID)
|
||||
}
|
||||
}
|
||||
@@ -868,29 +868,21 @@ private extension RoomInfoProxy {
|
||||
// MARK: - Mocks
|
||||
|
||||
extension TimelineViewModel {
|
||||
static let mock = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
|
||||
focussedEventID: nil,
|
||||
timelineController: MockRoomTimelineController(),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
static let mock = mock(timelineKind: .live)
|
||||
|
||||
static let pinnedEventsTimelineMock = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
|
||||
focussedEventID: nil,
|
||||
timelineController: MockRoomTimelineController(timelineKind: .pinned),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
static func mock(timelineKind: TimelineKind = .live) -> TimelineViewModel {
|
||||
TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
|
||||
focussedEventID: nil,
|
||||
timelineController: MockRoomTimelineController(timelineKind: timelineKind),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
}
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
|
||||
@@ -345,7 +345,7 @@ struct TimelineItemMenu_Previews: PreviewProvider, TestablePreview {
|
||||
isDM: true,
|
||||
isViewSourceEnabled: true,
|
||||
isCreateMediaCaptionsEnabled: true,
|
||||
isPinnedEventsTimeline: false,
|
||||
timelineKind: .live,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
guard let actions = provider.makeActions() else { return nil }
|
||||
|
||||
|
||||
@@ -82,9 +82,9 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
|
||||
var switchToDefaultComposer: Bool {
|
||||
switch self {
|
||||
case .reply, .edit, .addCaption, .editCaption, .editPoll:
|
||||
return false
|
||||
false
|
||||
default:
|
||||
return true
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +92,9 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
|
||||
var canAppearInFailedEcho: Bool {
|
||||
switch self {
|
||||
case .copy, .edit, .redact, .viewSource, .editPoll:
|
||||
return true
|
||||
true
|
||||
default:
|
||||
return false
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,9 +102,9 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
|
||||
var canAppearInRedacted: Bool {
|
||||
switch self {
|
||||
case .viewSource, .unpin, .viewInRoomTimeline:
|
||||
return true
|
||||
true
|
||||
default:
|
||||
return false
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,18 +112,27 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
|
||||
var isDestructive: Bool {
|
||||
switch self {
|
||||
case .redact, .report, .removeCaption:
|
||||
return true
|
||||
true
|
||||
default:
|
||||
return false
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
var canAppearInPinnedEventsTimeline: Bool {
|
||||
switch self {
|
||||
case .viewInRoomTimeline, .pin, .unpin, .forward:
|
||||
return true
|
||||
true
|
||||
default:
|
||||
return false
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
var canAppearInMediaDetails: Bool {
|
||||
switch self {
|
||||
case .viewInRoomTimeline, .redact:
|
||||
true
|
||||
default:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-7
@@ -17,7 +17,7 @@ struct TimelineItemMenuActionProvider {
|
||||
let isDM: Bool
|
||||
let isViewSourceEnabled: Bool
|
||||
let isCreateMediaCaptionsEnabled: Bool
|
||||
let isPinnedEventsTimeline: Bool
|
||||
let timelineKind: TimelineKind
|
||||
let emojiProvider: EmojiProviderProtocol
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
@@ -38,6 +38,10 @@ struct TimelineItemMenuActionProvider {
|
||||
|
||||
var actions: [TimelineItemMenuAction] = []
|
||||
var secondaryActions: [TimelineItemMenuAction] = []
|
||||
|
||||
if timelineKind == .pinned || timelineKind == .media(.mediaFilesScreen) {
|
||||
actions.append(.viewInRoomTimeline)
|
||||
}
|
||||
|
||||
if item.canBeRepliedTo {
|
||||
if let messageItem = item as? EventBasedMessageTimelineItemProtocol {
|
||||
@@ -99,10 +103,15 @@ struct TimelineItemMenuActionProvider {
|
||||
secondaryActions.append(.redact)
|
||||
}
|
||||
|
||||
if isPinnedEventsTimeline {
|
||||
actions.insert(.viewInRoomTimeline, at: 0)
|
||||
switch timelineKind {
|
||||
case .pinned:
|
||||
actions = actions.filter(\.canAppearInPinnedEventsTimeline)
|
||||
secondaryActions = secondaryActions.filter(\.canAppearInPinnedEventsTimeline)
|
||||
case .media:
|
||||
actions = actions.filter(\.canAppearInMediaDetails)
|
||||
secondaryActions = secondaryActions.filter(\.canAppearInMediaDetails)
|
||||
case .live, .detached:
|
||||
break // viewInRoomTimeline is the only non-room item and was added conditionally.
|
||||
}
|
||||
|
||||
if item.hasFailedToSend {
|
||||
@@ -114,11 +123,10 @@ struct TimelineItemMenuActionProvider {
|
||||
actions = actions.filter(\.canAppearInRedacted)
|
||||
secondaryActions = secondaryActions.filter(\.canAppearInRedacted)
|
||||
}
|
||||
|
||||
let isReactable = timelineKind == .live || timelineKind == .detached ? item.isReactable : false
|
||||
|
||||
return .init(isReactable: isPinnedEventsTimeline ? false : item.isReactable,
|
||||
actions: actions,
|
||||
secondaryActions: secondaryActions,
|
||||
emojiProvider: emojiProvider)
|
||||
return .init(isReactable: isReactable, actions: actions, secondaryActions: secondaryActions, emojiProvider: emojiProvider)
|
||||
}
|
||||
|
||||
private func makeEncryptedItemActions(_ encryptedItem: EncryptedRoomTimelineItem) -> TimelineItemMenuActions? {
|
||||
|
||||
@@ -20,7 +20,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
private var isEncryptedOneToOneRoom: Bool { context.viewState.isEncryptedOneToOneRoom }
|
||||
private var isFocussed: Bool { focussedEventID != nil && timelineItem.id.eventID == focussedEventID }
|
||||
private var isPinned: Bool {
|
||||
guard !context.viewState.isPinnedEventsTimeline,
|
||||
guard context.viewState.timelineKind != .pinned,
|
||||
let eventID = timelineItem.id.eventID else {
|
||||
return false
|
||||
}
|
||||
@@ -110,7 +110,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
}
|
||||
|
||||
// Do not display reactions in the pinned events timeline
|
||||
if !context.viewState.isPinnedEventsTimeline,
|
||||
if context.viewState.timelineKind != .pinned,
|
||||
!timelineItem.properties.reactions.isEmpty {
|
||||
TimelineReactionsView(context: context,
|
||||
itemID: timelineItem.id,
|
||||
@@ -150,7 +150,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
isDM: context.viewState.isEncryptedOneToOneRoom,
|
||||
isViewSourceEnabled: context.viewState.isViewSourceEnabled,
|
||||
isCreateMediaCaptionsEnabled: context.viewState.isCreateMediaCaptionsEnabled,
|
||||
isPinnedEventsTimeline: context.viewState.isPinnedEventsTimeline,
|
||||
timelineKind: context.viewState.timelineKind,
|
||||
emojiProvider: context.viewState.emojiProvider)
|
||||
TimelineItemMacContextMenu(item: timelineItem, actionProvider: provider) { action in
|
||||
context.send(viewAction: .handleTimelineItemMenuAction(itemID: timelineItem.id, action: action))
|
||||
|
||||
@@ -23,7 +23,7 @@ struct TimelineItemStatusView: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var mainContent: some View {
|
||||
if context.viewState.isPinnedEventsTimeline {
|
||||
if context.viewState.timelineKind == .pinned {
|
||||
// Do not display any status when is a pinned events timeline
|
||||
EmptyView()
|
||||
} else if context.viewState.showReadReceipts, !timelineItem.properties.orderedReadReceipts.isEmpty {
|
||||
|
||||
@@ -12,7 +12,7 @@ struct PollRoomTimelineView: View {
|
||||
@EnvironmentObject private var context: TimelineViewModel.Context
|
||||
|
||||
private var state: PollViewState {
|
||||
if context.viewState.isPinnedEventsTimeline {
|
||||
if context.viewState.timelineKind == .pinned {
|
||||
return .preview
|
||||
} else {
|
||||
return .full(isEditable: timelineItem.isEditable)
|
||||
@@ -51,7 +51,7 @@ struct PollRoomTimelineView: View {
|
||||
|
||||
struct PollRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = TimelineViewModel.mock
|
||||
static let pinnedEventsTimelineViewModel = TimelineViewModel.pinnedEventsTimelineMock
|
||||
static let pinnedEventsTimelineViewModel = TimelineViewModel.mock(timelineKind: .pinned)
|
||||
|
||||
static var previews: some View {
|
||||
PollRoomTimelineView(timelineItem: .mock(poll: .disclosed(), isOutgoing: false))
|
||||
|
||||
Reference in New Issue
Block a user