mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
This commit is contained in:
@@ -111,6 +111,8 @@ struct TimelineViewState: BindableState {
|
||||
|
||||
/// A closure providing the associated audio player state for an item in the timeline.
|
||||
var audioPlayerStateProvider: (@MainActor (_ itemId: TimelineItemIdentifier) -> AudioPlayerState?)?
|
||||
|
||||
var emojiProvider: EmojiProviderProtocol
|
||||
}
|
||||
|
||||
struct TimelineViewStateBindings {
|
||||
|
||||
@@ -28,6 +28,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
private let appMediator: AppMediatorProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let analyticsService: AnalyticsService
|
||||
private let emojiProvider: EmojiProviderProtocol
|
||||
|
||||
private let timelineInteractionHandler: TimelineInteractionHandler
|
||||
|
||||
@@ -50,7 +51,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
appMediator: AppMediatorProtocol,
|
||||
appSettings: AppSettings,
|
||||
analyticsService: AnalyticsService) {
|
||||
analyticsService: AnalyticsService,
|
||||
emojiProvider: EmojiProviderProtocol) {
|
||||
self.timelineController = timelineController
|
||||
self.mediaPlayerProvider = mediaPlayerProvider
|
||||
self.roomProxy = roomProxy
|
||||
@@ -58,6 +60,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
self.analyticsService = analyticsService
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.appMediator = appMediator
|
||||
self.emojiProvider = emojiProvider
|
||||
|
||||
let voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider)
|
||||
|
||||
@@ -79,7 +82,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
ownUserID: roomProxy.ownUserID,
|
||||
isViewSourceEnabled: appSettings.viewSourceEnabled,
|
||||
hideTimelineMedia: appSettings.hideTimelineMedia,
|
||||
bindings: .init(reactionsCollapsed: [:])),
|
||||
bindings: .init(reactionsCollapsed: [:]),
|
||||
emojiProvider: emojiProvider),
|
||||
mediaProvider: mediaProvider)
|
||||
|
||||
if focussedEventID != nil {
|
||||
@@ -132,6 +136,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
case .itemSendInfoTapped(let itemID):
|
||||
handleItemSendInfoTapped(itemID: itemID)
|
||||
case .toggleReaction(let emoji, let itemID):
|
||||
emojiProvider.markEmojiAsFrequentlyUsed(emoji)
|
||||
|
||||
guard case let .event(_, eventOrTransactionID) = itemID else {
|
||||
fatalError()
|
||||
}
|
||||
@@ -861,7 +867,8 @@ extension TimelineViewModel {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static let pinnedEventsTimelineMock = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")),
|
||||
focussedEventID: nil,
|
||||
@@ -872,7 +879,8 @@ extension TimelineViewModel {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
|
||||
@@ -308,7 +308,8 @@ struct TimelineItemMenu_Previews: PreviewProvider, TestablePreview {
|
||||
guard var item = RoomTimelineItemFixtures.singleMessageChunk.first as? TextRoomTimelineItem,
|
||||
let actions = TimelineItemMenuActions(isReactable: true,
|
||||
actions: [.copy, .edit, .reply(isThread: false), .pin, .redact],
|
||||
debugActions: [.viewSource]) else {
|
||||
debugActions: [.viewSource],
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,26 +8,38 @@
|
||||
import SFSafeSymbols
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
struct TimelineItemMenuActions {
|
||||
let reactions: [TimelineItemMenuReaction]
|
||||
let actions: [TimelineItemMenuAction]
|
||||
let debugActions: [TimelineItemMenuAction]
|
||||
|
||||
init?(isReactable: Bool, actions: [TimelineItemMenuAction], debugActions: [TimelineItemMenuAction]) {
|
||||
init?(isReactable: Bool,
|
||||
actions: [TimelineItemMenuAction],
|
||||
debugActions: [TimelineItemMenuAction],
|
||||
emojiProvider: EmojiProviderProtocol) {
|
||||
if !isReactable, actions.isEmpty, debugActions.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.actions = actions
|
||||
self.debugActions = debugActions
|
||||
|
||||
// Only process 5 of the most frequently used emojis instead of all of them
|
||||
var frequentlyUsed = emojiProvider.frequentlyUsedSystemEmojis().prefix(5).map { TimelineItemMenuReaction(key: $0, symbol: .heart) }
|
||||
|
||||
frequentlyUsed += [
|
||||
.init(key: "👍️", symbol: .handThumbsup),
|
||||
.init(key: "👎️", symbol: .handThumbsdown),
|
||||
.init(key: "🔥", symbol: .flame),
|
||||
.init(key: "❤️", symbol: .heart),
|
||||
.init(key: "👏", symbol: .handsClap)
|
||||
]
|
||||
|
||||
frequentlyUsed = Array(frequentlyUsed.prefix(5))
|
||||
|
||||
reactions = if isReactable {
|
||||
[
|
||||
.init(key: "👍️", symbol: .handThumbsup),
|
||||
.init(key: "👎️", symbol: .handThumbsdown),
|
||||
.init(key: "🔥", symbol: .flame),
|
||||
.init(key: "❤️", symbol: .heart),
|
||||
.init(key: "👏", symbol: .handsClap)
|
||||
]
|
||||
frequentlyUsed
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
|
||||
+10
-2
@@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
struct TimelineItemMenuActionProvider {
|
||||
let timelineItem: RoomTimelineItemProtocol
|
||||
let canCurrentUserRedactSelf: Bool
|
||||
@@ -16,6 +17,7 @@ struct TimelineItemMenuActionProvider {
|
||||
let isDM: Bool
|
||||
let isViewSourceEnabled: Bool
|
||||
let isPinnedEventsTimeline: Bool
|
||||
let emojiProvider: EmojiProviderProtocol
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
func makeActions() -> TimelineItemMenuActions? {
|
||||
@@ -42,7 +44,10 @@ struct TimelineItemMenuActionProvider {
|
||||
break
|
||||
}
|
||||
|
||||
return .init(isReactable: false, actions: [.copyPermalink], debugActions: debugActions)
|
||||
return .init(isReactable: false,
|
||||
actions: [.copyPermalink],
|
||||
debugActions: debugActions,
|
||||
emojiProvider: emojiProvider)
|
||||
}
|
||||
|
||||
var actions: [TimelineItemMenuAction] = []
|
||||
@@ -100,7 +105,10 @@ struct TimelineItemMenuActionProvider {
|
||||
actions = actions.filter(\.canAppearInPinnedEventsTimeline)
|
||||
}
|
||||
|
||||
return .init(isReactable: isPinnedEventsTimeline ? false : item.isReactable, actions: actions, debugActions: debugActions)
|
||||
return .init(isReactable: isPinnedEventsTimeline ? false : item.isReactable,
|
||||
actions: actions,
|
||||
debugActions: debugActions,
|
||||
emojiProvider: emojiProvider)
|
||||
}
|
||||
|
||||
private func canRedactItem(_ item: EventBasedTimelineItemProtocol) -> Bool {
|
||||
|
||||
@@ -51,7 +51,8 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
return mock
|
||||
}()
|
||||
|
||||
|
||||
@@ -148,7 +148,8 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
pinnedEventIDs: context.viewState.pinnedEventIDs,
|
||||
isDM: context.viewState.isEncryptedOneToOneRoom,
|
||||
isViewSourceEnabled: context.viewState.isViewSourceEnabled,
|
||||
isPinnedEventsTimeline: context.viewState.isPinnedEventsTimeline)
|
||||
isPinnedEventsTimeline: context.viewState.isPinnedEventsTimeline,
|
||||
emojiProvider: context.viewState.emojiProvider)
|
||||
TimelineItemMacContextMenu(item: timelineItem, actionProvider: provider) { action in
|
||||
context.send(viewAction: .handleTimelineItemMenuAction(itemID: timelineItem.id, action: action))
|
||||
}
|
||||
@@ -364,7 +365,8 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
}()
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
@@ -89,7 +89,8 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static let singleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now")]
|
||||
static let doubleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now"),
|
||||
|
||||
+2
-1
@@ -96,7 +96,8 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
||||
@@ -89,7 +89,8 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
||||
Reference in New Issue
Block a user