mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Implemented message forwarding for media previews and media timelines (#4579)
* implemented message forwarding for media previews and media timelines * updated tests * pr suggestion * fix tests * fix tests
This commit is contained in:
@@ -10,6 +10,7 @@ import Foundation
|
|||||||
|
|
||||||
enum MediaEventsTimelineFlowCoordinatorAction {
|
enum MediaEventsTimelineFlowCoordinatorAction {
|
||||||
case viewInRoomTimeline(TimelineItemIdentifier)
|
case viewInRoomTimeline(TimelineItemIdentifier)
|
||||||
|
case displayMessageForwarding(MessageForwardingItem)
|
||||||
case finished
|
case finished
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,10 +92,13 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
coordinator.actions
|
coordinator.actions
|
||||||
.sink { [weak self] action in
|
.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
||||||
case .viewInRoomTimeline(let itemID):
|
case .viewInRoomTimeline(let itemID):
|
||||||
self?.navigationStackCoordinator.pop(animated: false)
|
navigationStackCoordinator.pop(animated: false)
|
||||||
self?.actionsSubject.send(.viewInRoomTimeline(itemID))
|
actionsSubject.send(.viewInRoomTimeline(itemID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -1592,12 +1592,15 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case .viewInRoomTimeline(let itemID):
|
case .viewInRoomTimeline(let itemID):
|
||||||
guard let eventID = itemID.eventID else {
|
guard let eventID = itemID.eventID else {
|
||||||
MXLog.error("Unable to present room timeline for event \(itemID)")
|
MXLog.error("Unable to present room timeline for event \(itemID)")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stateMachine.tryEvent(.presentRoom(presentationAction: .eventFocus(.init(eventID: eventID, shouldSetPin: false))),
|
stateMachine.tryEvent(.presentRoom(presentationAction: .eventFocus(.init(eventID: eventID, shouldSetPin: false))),
|
||||||
userInfo: EventUserInfo(animated: false)) // No animation so the timeline visible when the preview animates away.
|
userInfo: EventUserInfo(animated: false)) // No animation so the timeline visible when the preview animates away.
|
||||||
case .finished:
|
case .finished:
|
||||||
stateMachine.tryEvent(.dismissMediaEventsTimeline)
|
stateMachine.tryEvent(.dismissMediaEventsTimeline)
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
stateMachine.tryEvent(.presentMessageForwarding(forwardingItem: forwardingItem))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -198,6 +198,8 @@ extension RoomFlowCoordinator {
|
|||||||
|
|
||||||
case (.room, .presentMessageForwarding(let forwardingItem)):
|
case (.room, .presentMessageForwarding(let forwardingItem)):
|
||||||
return .messageForwarding(forwardingItem: forwardingItem, previousState: fromState)
|
return .messageForwarding(forwardingItem: forwardingItem, previousState: fromState)
|
||||||
|
case (.mediaEventsTimeline, .presentMessageForwarding(forwardingItem: let forwardingItem)):
|
||||||
|
return .messageForwarding(forwardingItem: forwardingItem, previousState: fromState)
|
||||||
|
|
||||||
case (.room, .presentMapNavigator(_)):
|
case (.room, .presentMapNavigator(_)):
|
||||||
return .mapNavigator(previousState: fromState)
|
return .mapNavigator(previousState: fromState)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import SwiftUI
|
|||||||
|
|
||||||
enum TimelineMediaPreviewViewModelAction: Equatable {
|
enum TimelineMediaPreviewViewModelAction: Equatable {
|
||||||
case viewInRoomTimeline(TimelineItemIdentifier)
|
case viewInRoomTimeline(TimelineItemIdentifier)
|
||||||
|
case displayMessageForwarding(MessageForwardingItem)
|
||||||
case dismiss
|
case dismiss
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import Foundation
|
|||||||
typealias TimelineMediaPreviewViewModelType = StateStoreViewModel<TimelineMediaPreviewViewState, TimelineMediaPreviewViewAction>
|
typealias TimelineMediaPreviewViewModelType = StateStoreViewModel<TimelineMediaPreviewViewState, TimelineMediaPreviewViewAction>
|
||||||
|
|
||||||
class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
|
class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
|
||||||
|
static let displayMessageForwardingDelay: TimeInterval = 1.0
|
||||||
|
|
||||||
let instanceID = UUID()
|
let instanceID = UUID()
|
||||||
|
|
||||||
private let timelineViewModel: TimelineViewModelProtocol
|
private let timelineViewModel: TimelineViewModelProtocol
|
||||||
@@ -86,6 +88,8 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
|
|||||||
Task { await saveCurrentItem() }
|
Task { await saveCurrentItem() }
|
||||||
case .redact:
|
case .redact:
|
||||||
state.bindings.redactConfirmationItem = item
|
state.bindings.redactConfirmationItem = item
|
||||||
|
case .forward(let itemID):
|
||||||
|
Task { await forwardItem(itemID: itemID) }
|
||||||
default:
|
default:
|
||||||
MXLog.error("Received unexpected action: \(action)")
|
MXLog.error("Received unexpected action: \(action)")
|
||||||
}
|
}
|
||||||
@@ -96,6 +100,12 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func forwardItem(itemID: TimelineItemIdentifier) async {
|
||||||
|
guard let forwardingItem = await timelineViewModel.makeForwardingItem(for: itemID) else { return }
|
||||||
|
state.previewControllerDriver.send(.dismissDetailsSheet)
|
||||||
|
actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
||||||
|
}
|
||||||
|
|
||||||
private func updateCurrentItem(_ previewItem: TimelineMediaPreviewItem) async {
|
private func updateCurrentItem(_ previewItem: TimelineMediaPreviewItem) async {
|
||||||
if case let .media(item) = previewItem {
|
if case let .media(item) = previewItem {
|
||||||
item.downloadError = nil // Clear any existing error.
|
item.downloadError = nil // Clear any existing error.
|
||||||
|
|||||||
+5
-1
@@ -25,6 +25,7 @@ struct MediaEventsTimelineScreenCoordinatorParameters {
|
|||||||
|
|
||||||
enum MediaEventsTimelineScreenCoordinatorAction {
|
enum MediaEventsTimelineScreenCoordinatorAction {
|
||||||
case viewInRoomTimeline(TimelineItemIdentifier)
|
case viewInRoomTimeline(TimelineItemIdentifier)
|
||||||
|
case displayMessageForwarding(MessageForwardingItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class MediaEventsTimelineScreenCoordinator: CoordinatorProtocol {
|
final class MediaEventsTimelineScreenCoordinator: CoordinatorProtocol {
|
||||||
@@ -73,9 +74,12 @@ final class MediaEventsTimelineScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
viewModel.actionsPublisher
|
viewModel.actionsPublisher
|
||||||
.sink { [weak self] action in
|
.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
||||||
case .viewInRoomTimeline(let itemID):
|
case .viewInRoomTimeline(let itemID):
|
||||||
self?.actionsSubject.send(.viewInRoomTimeline(itemID))
|
actionsSubject.send(.viewInRoomTimeline(itemID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
+1
@@ -8,6 +8,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
enum MediaEventsTimelineScreenViewModelAction {
|
enum MediaEventsTimelineScreenViewModelAction {
|
||||||
|
case displayMessageForwarding(MessageForwardingItem)
|
||||||
case viewInRoomTimeline(TimelineItemIdentifier)
|
case viewInRoomTimeline(TimelineItemIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
@@ -155,6 +155,8 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
sheetModel.actions.sink { [weak self] action in
|
sheetModel.actions.sink { [weak self] action in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
displayMessageForwarding(forwardingItem: forwardingItem)
|
||||||
case .viewInRoomTimeline(let itemID):
|
case .viewInRoomTimeline(let itemID):
|
||||||
actionsSubject.send(.viewInRoomTimeline(itemID))
|
actionsSubject.send(.viewInRoomTimeline(itemID))
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
@@ -222,6 +224,8 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
viewModel.actions.sink { [weak self] action in
|
viewModel.actions.sink { [weak self] action in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
displayMessageForwarding(forwardingItem: forwardingItem)
|
||||||
case .viewInRoomTimeline(let itemID):
|
case .viewInRoomTimeline(let itemID):
|
||||||
state.bindings.mediaPreviewViewModel = nil
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
actionsSubject.send(.viewInRoomTimeline(itemID))
|
actionsSubject.send(.viewInRoomTimeline(itemID))
|
||||||
@@ -241,4 +245,13 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
date.formatted(.dateTime.month(.wide).year())
|
date.formatted(.dateTime.month(.wide).year())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func displayMessageForwarding(forwardingItem: MessageForwardingItem) {
|
||||||
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
|
state.bindings.mediaPreviewSheetViewModel = nil
|
||||||
|
// We need a small delay because we need to wait for the presented sheet to be fully dismissed.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + TimelineMediaPreviewViewModel.displayMessageForwardingDelay) {
|
||||||
|
self.actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -65,6 +65,8 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
actionsSubject.send(.displayMessageForwarding(forwardingItem: forwardingItem))
|
||||||
case .viewInRoomTimeline(let itemID):
|
case .viewInRoomTimeline(let itemID):
|
||||||
guard let eventID = itemID.eventID else { fatalError("A pinned event must have an event ID.") }
|
guard let eventID = itemID.eventID else { fatalError("A pinned event must have an event ID.") }
|
||||||
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID))
|
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID))
|
||||||
|
|||||||
+1
@@ -9,6 +9,7 @@ import Foundation
|
|||||||
|
|
||||||
enum PinnedEventsTimelineScreenViewModelAction {
|
enum PinnedEventsTimelineScreenViewModelAction {
|
||||||
case viewInRoomTimeline(itemID: TimelineItemIdentifier)
|
case viewInRoomTimeline(itemID: TimelineItemIdentifier)
|
||||||
|
case displayMessageForwarding(MessageForwardingItem)
|
||||||
case dismiss
|
case dismiss
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -42,11 +42,18 @@ class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelTy
|
|||||||
|
|
||||||
func displayMediaPreview(_ mediaPreviewViewModel: TimelineMediaPreviewViewModel) {
|
func displayMediaPreview(_ mediaPreviewViewModel: TimelineMediaPreviewViewModel) {
|
||||||
mediaPreviewViewModel.actions.sink { [weak self] action in
|
mediaPreviewViewModel.actions.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
|
// We need a small delay because we need to wait for the media preview to be fully dismissed.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + TimelineMediaPreviewViewModel.displayMessageForwardingDelay) {
|
||||||
|
self.actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
||||||
|
}
|
||||||
case .viewInRoomTimeline(let itemID):
|
case .viewInRoomTimeline(let itemID):
|
||||||
self?.actionsSubject.send(.viewInRoomTimeline(itemID: itemID))
|
actionsSubject.send(.viewInRoomTimeline(itemID: itemID))
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
self?.state.bindings.mediaPreviewViewModel = nil
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
actionsSubject.send(.presentKnockRequestsList)
|
actionsSubject.send(.presentKnockRequestsList)
|
||||||
case .displayRoom(let roomID, let via):
|
case .displayRoom(let roomID, let via):
|
||||||
actionsSubject.send(.presentRoom(roomID: roomID, via: via))
|
actionsSubject.send(.presentRoom(roomID: roomID, via: via))
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ enum RoomScreenViewModelAction: Equatable {
|
|||||||
case removeComposerFocus
|
case removeComposerFocus
|
||||||
case displayKnockRequests
|
case displayKnockRequests
|
||||||
case displayRoom(roomID: String, via: [String])
|
case displayRoom(roomID: String, via: [String])
|
||||||
|
case displayMessageForwarding(MessageForwardingItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RoomScreenViewAction {
|
enum RoomScreenViewAction {
|
||||||
|
|||||||
@@ -136,11 +136,18 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
|
|
||||||
func displayMediaPreview(_ mediaPreviewViewModel: TimelineMediaPreviewViewModel) {
|
func displayMediaPreview(_ mediaPreviewViewModel: TimelineMediaPreviewViewModel) {
|
||||||
mediaPreviewViewModel.actions.sink { [weak self] action in
|
mediaPreviewViewModel.actions.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
case .viewInRoomTimeline:
|
|
||||||
fatalError("viewInRoomTimeline should not be visible on a room preview.")
|
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
self?.state.bindings.mediaPreviewViewModel = nil
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
|
// We need a small delay because we need to wait for the media preview to be fully dismissed.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + TimelineMediaPreviewViewModel.displayMessageForwardingDelay) {
|
||||||
|
self.actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
||||||
|
}
|
||||||
|
case .viewInRoomTimeline:
|
||||||
|
fatalError("\(action) should not be visible on a room preview.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -88,6 +88,16 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
viewModel.actionsPublisher
|
||||||
|
.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
|
switch action {
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
timelineViewModel.actions
|
timelineViewModel.actions
|
||||||
.sink { [weak self] action in
|
.sink { [weak self] action in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ThreadTimelineScreenViewModelAction { }
|
enum ThreadTimelineScreenViewModelAction {
|
||||||
|
case displayMessageForwarding(MessageForwardingItem)
|
||||||
|
}
|
||||||
|
|
||||||
struct ThreadTimelineScreenViewState: BindableState {
|
struct ThreadTimelineScreenViewState: BindableState {
|
||||||
var roomTitle: String
|
var roomTitle: String
|
||||||
|
|||||||
@@ -61,11 +61,18 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi
|
|||||||
|
|
||||||
func displayMediaPreview(_ mediaPreviewViewModel: TimelineMediaPreviewViewModel) {
|
func displayMediaPreview(_ mediaPreviewViewModel: TimelineMediaPreviewViewModel) {
|
||||||
mediaPreviewViewModel.actions.sink { [weak self] action in
|
mediaPreviewViewModel.actions.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
case .viewInRoomTimeline:
|
case .viewInRoomTimeline:
|
||||||
fatalError("viewInRoomTimeline should not be visible on a thread preview.")
|
fatalError("\(action) should not be visible on a thread preview.")
|
||||||
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
|
// We need a small delay because we need to wait for the media preview to be fully dismissed.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + TimelineMediaPreviewViewModel.displayMessageForwardingDelay) {
|
||||||
|
self.actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
||||||
|
}
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
self?.state.bindings.mediaPreviewViewModel = nil
|
state.bindings.mediaPreviewViewModel = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -270,6 +270,11 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeForwardingItem(for itemID: TimelineItemIdentifier) async -> MessageForwardingItem? {
|
||||||
|
guard let content = await timelineController.messageEventContent(for: itemID) else { return nil }
|
||||||
|
return .init(id: itemID, roomID: roomProxy.id, content: content)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func handleTappedOnSenderDetails(sender: TimelineItemSender) {
|
private func handleTappedOnSenderDetails(sender: TimelineItemSender) {
|
||||||
@@ -899,8 +904,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
// MARK: - Message forwarding
|
// MARK: - Message forwarding
|
||||||
|
|
||||||
private func forwardMessage(itemID: TimelineItemIdentifier) async {
|
private func forwardMessage(itemID: TimelineItemIdentifier) async {
|
||||||
guard let content = await timelineController.messageEventContent(for: itemID) else { return }
|
guard let forwardingItem = await makeForwardingItem(for: itemID) else { return }
|
||||||
actionsSubject.send(.displayMessageForwarding(forwardingItem: .init(id: itemID, roomID: roomProxy.id, content: content)))
|
actionsSubject.send(.displayMessageForwarding(forwardingItem: forwardingItem))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Pills
|
// MARK: Pills
|
||||||
|
|||||||
@@ -17,4 +17,7 @@ protocol TimelineViewModelProtocol {
|
|||||||
func process(composerAction: ComposerToolbarViewModelAction)
|
func process(composerAction: ComposerToolbarViewModelAction)
|
||||||
/// Updates the timeline to show and highlight the item with the corresponding event ID.
|
/// Updates the timeline to show and highlight the item with the corresponding event ID.
|
||||||
func focusOnEvent(eventID: String) async
|
func focusOnEvent(eventID: String) async
|
||||||
|
|
||||||
|
/// Handles getting the content to forward an item given its item ID.
|
||||||
|
func makeForwardingItem(for itemID: TimelineItemIdentifier) async -> MessageForwardingItem?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
|
|||||||
|
|
||||||
var canAppearInMediaDetails: Bool {
|
var canAppearInMediaDetails: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .viewInRoomTimeline, .share, .save, .redact:
|
case .viewInRoomTimeline, .share, .save, .redact, .forward:
|
||||||
true
|
true
|
||||||
default:
|
default:
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -323,12 +323,27 @@ private extension View {
|
|||||||
|
|
||||||
struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview {
|
struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel: TimelineViewModel = {
|
static let viewModel: TimelineViewModel = {
|
||||||
ServiceLocator.shared.settings.threadsEnabled = true
|
let appSettings = AppSettings()
|
||||||
return TimelineViewModel.mock
|
appSettings.threadsEnabled = true
|
||||||
|
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init())
|
||||||
|
return TimelineViewModel(roomProxy: roomProxy,
|
||||||
|
focussedEventID: nil,
|
||||||
|
timelineController: MockTimelineController(),
|
||||||
|
userSession: UserSessionMock(.init()),
|
||||||
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
|
appMediator: AppMediatorMock.default,
|
||||||
|
appSettings: appSettings,
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||||
|
linkMetadataProvider: LinkMetadataProvider(),
|
||||||
|
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
static let viewModelWithPins: TimelineViewModel = {
|
static let viewModelWithPins: TimelineViewModel = {
|
||||||
ServiceLocator.shared.settings.threadsEnabled = true
|
let appSettings = AppSettings()
|
||||||
|
appSettings.threadsEnabled = true
|
||||||
|
|
||||||
let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: ["pinned"]))
|
let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: ["pinned"]))
|
||||||
return TimelineViewModel(roomProxy: roomProxy,
|
return TimelineViewModel(roomProxy: roomProxy,
|
||||||
@@ -338,7 +353,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
appMediator: AppMediatorMock.default,
|
appMediator: AppMediatorMock.default,
|
||||||
appSettings: ServiceLocator.shared.settings,
|
appSettings: appSettings,
|
||||||
analyticsService: ServiceLocator.shared.analytics,
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||||
linkMetadataProvider: LinkMetadataProvider(),
|
linkMetadataProvider: LinkMetadataProvider(),
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:2cc67bdf871dd02120f5e380fdf1755d81332c35c904e2818bd27d5f9c8fb9b6
|
||||||
|
size 74274
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:33bb51b6d23a5bf42945c7d1b93257d7b729fe81b70f5cfbf6a2871c3e462122
|
||||||
|
size 75495
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:ad670b56be16888c137dc8060b34a7ff91b07490b4160a9c299af9406bbee477
|
||||||
|
size 33301
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:8a886c81562cf3a34b098389e8e5fc5e97e1c3a6d719c41006a28ee537d554fd
|
||||||
|
size 34819
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:aa6ed0d0629081afcc922b86d27b0d3547687dbd7a69b8b1be6bb55b47572ff9
|
oid sha256:4bdebb424a46a99ad6aedf1c477c236f8a02b35ce4ca21fd0a7b1682bddd0044
|
||||||
size 126115
|
size 130062
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6b90fa3b8e212590d32b5392bca159d6ab87aa68b6f5a41894b08df7d5a7e9b0
|
oid sha256:e5765f64f5dd6fcf108ed8d75832a1a82d1d246a50af0c957aaf77568fc96c14
|
||||||
size 135759
|
size 139865
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ceeacead9e4c3dafd66654b48465cc2a563ca103b6c49270dd14ef6d595b4194
|
oid sha256:f946edd1fc5bd9ad5172008ae0829dc71016239918dae4505312bb0d8691c1e9
|
||||||
size 78662
|
size 81809
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:459e2955bf05cb30da558b38d0973f147a517e817f41ed2a581c6f9827b3449e
|
oid sha256:eb35e096e50d72b505f17511cc10dbafbdb3d6bd75488d03541604a1cb9c54d4
|
||||||
size 88294
|
size 92716
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f08e6c7b2f5bca6355e25dc11d33a8af8712ac63ad905ff1c255354a1b185fb2
|
oid sha256:34326b90af3706b06a3db702bfc068419df5e313e9ea6e34871d39e573c6dfe0
|
||||||
size 108014
|
size 112687
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7abad7e0e48e480fb53f6f68bb35654da21cdb98c0f16b97ef9bb1be6a24b48e
|
oid sha256:9eda1c1794e8e4c26ad973e3181358295449bac44df3841abb6f5506eec7fbcb
|
||||||
size 114917
|
size 119856
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:16390a5a292262c44356cfba087399b08305878a19ac38994882269a0642e683
|
oid sha256:91b84d0fbaf7c8beb7b866f8f622c7d721bea7494a1a62121d2d131559428ef5
|
||||||
size 61793
|
size 66231
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:741cbf529043dd003a8180b94ebae71f91ee790f0808650afea1e52e9580a24e
|
oid sha256:55b819c3e149db5df36777dd9be8779e8d3644d3c92c34d55d2fdc8079bd060d
|
||||||
size 68207
|
size 74238
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:52d9f7aeec96fa06bd189bcb6a5387be38a82a8af0d1ddcb19a2710ba8144c56
|
oid sha256:38723c5a2d87690fb1f57ca66ecf696e7c91272a40979449aaa2b0996433a09a
|
||||||
size 120171
|
size 123617
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:dd84aacc5ad5459640ff0dd1ebf312d7b34cfd65b6fa3521bd2223584d81bb07
|
oid sha256:d3bbf56dc154230dec9e6a4e456a86e2f616870061ee24d3e090be785bb2375d
|
||||||
size 129280
|
size 132971
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:81a204268e94094c076727c8208aacdcc614ea309ec35e208de4405b31644984
|
oid sha256:c1ea2752b2825c440c2ce6a93c79505338ba34f597be350dcdfc0260d130302d
|
||||||
size 72990
|
size 76307
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3fd2f8dce85962a0b63ff6c8c34e09a67a174c7d141a76dc647f07b5d11b42ab
|
oid sha256:5cdbc286f8527cd584732a235058ff455ca93b883c904269540649e286b47580
|
||||||
size 81150
|
size 86008
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:69456a05b7a366553418e28ef88f2c0b510f1b5aac2982fad3e053fbdb3df017
|
oid sha256:527531157a910ae4f7c2e2ffef4a3e986482e1de2b9b366c153dceaf63652d2b
|
||||||
size 113743
|
size 117197
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b0e15faa23cab6f6b4f61ad5ecb91187eaffd100af6f6e0fa9c14ae5f51d2213
|
oid sha256:bc836edafebb818a804ed20932624fae810b2496daf0293553db448d0e0481a6
|
||||||
size 121471
|
size 125172
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4f73f08b7b07a057ad75f6e07401da24b3e0526c46b08fe7efd676b1c3d6162c
|
oid sha256:6eee65dc7f2dc39dca3e6bf868e6c040ddefa910065c5821bde34a0f30290e12
|
||||||
size 67534
|
size 70796
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2502e5b5bea2fb048fe9c121a9965e991246a81820e9deaf9d318928d743c013
|
oid sha256:96a69389b506efe0e1d9ce365bad1dafb346e287dbe9c5460b74917362ce218d
|
||||||
size 74710
|
size 79452
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ class AnalyticsSettingsScreenViewModelTests: XCTestCase {
|
|||||||
private var viewModel: AnalyticsSettingsScreenViewModelProtocol!
|
private var viewModel: AnalyticsSettingsScreenViewModelProtocol!
|
||||||
private var context: AnalyticsSettingsScreenViewModelType.Context!
|
private var context: AnalyticsSettingsScreenViewModelType.Context!
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
AppSettings.resetAllSettings()
|
||||||
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
AppSettings.resetAllSettings()
|
AppSettings.resetAllSettings()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user