mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Improvements to state items (#478)
- Remove context menu (and fix long press when there are no items). - Add membership change tests. - Fix new/old name incorrect ordering. - Fix message grouping when it comes before/after a state item from the same user. - Only show reactions picker for items that can be reacted to.
This commit is contained in:
@@ -32,11 +32,11 @@ enum RoomScreenComposerMode: Equatable {
|
||||
|
||||
enum RoomScreenViewAction {
|
||||
case displayRoomDetails
|
||||
case displayEmojiPicker(itemId: String)
|
||||
case paginateBackwards
|
||||
case itemAppeared(id: String)
|
||||
case itemDisappeared(id: String)
|
||||
case itemTapped(id: String)
|
||||
case itemDoubleTapped(id: String)
|
||||
case linkClicked(url: URL)
|
||||
case sendMessage
|
||||
case sendReaction(key: String, eventID: String)
|
||||
|
||||
@@ -73,7 +73,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
state.contextMenuBuilder = buildContexMenuForItemId(_:)
|
||||
state.contextMenuBuilder = buildContextMenuForItemId(_:)
|
||||
|
||||
buildTimelineViews()
|
||||
|
||||
@@ -104,14 +104,14 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
await timelineController.processItemDisappearance(id)
|
||||
case .itemTapped(let id):
|
||||
await itemTapped(with: id)
|
||||
case .itemDoubleTapped(let id):
|
||||
itemDoubleTapped(with: id)
|
||||
case .linkClicked(let url):
|
||||
MXLog.warning("Link clicked: \(url)")
|
||||
case .sendMessage:
|
||||
await sendCurrentMessage()
|
||||
case .sendReaction(let emoji, let itemId):
|
||||
await timelineController.sendReaction(emoji, to: itemId)
|
||||
case .displayEmojiPicker(let itemId):
|
||||
callback?(.displayEmojiPicker(itemId: itemId))
|
||||
case .cancelReply:
|
||||
state.composerMode = .default
|
||||
case .cancelEdit:
|
||||
@@ -157,6 +157,11 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
state.showLoading = false
|
||||
}
|
||||
|
||||
private func itemDoubleTapped(with itemId: String) {
|
||||
guard let item = state.items.first(where: { $0.id == itemId }), item.isReactable else { return }
|
||||
callback?(.displayEmojiPicker(itemId: itemId))
|
||||
}
|
||||
|
||||
private func buildTimelineViews() {
|
||||
let stateItems = timelineController.timelineItems.map { item in
|
||||
timelineViewFactory.buildTimelineViewFor(timelineItem: item)
|
||||
@@ -202,7 +207,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
// MARK: ContextMenus
|
||||
|
||||
private func buildContexMenuForItemId(_ itemId: String) -> TimelineItemContextMenu {
|
||||
private func buildContextMenuForItemId(_ itemId: String) -> TimelineItemContextMenu {
|
||||
TimelineItemContextMenu(contextMenuActions: contextMenuActionsForItemId(itemId)) { [weak self] action in
|
||||
self?.processContentMenuAction(action, itemId: itemId)
|
||||
}
|
||||
@@ -211,7 +216,13 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
private func contextMenuActionsForItemId(_ itemId: String) -> TimelineItemContextMenuActions {
|
||||
guard let timelineItem = timelineController.timelineItems.first(where: { $0.id == itemId }),
|
||||
let item = timelineItem as? EventBasedTimelineItemProtocol else {
|
||||
return .init(actions: [], debugActions: [])
|
||||
// Don't show a context menu for non-event based items.
|
||||
return .empty
|
||||
}
|
||||
|
||||
if timelineItem is StateRoomTimelineItem {
|
||||
// Don't show a context menu for state events.
|
||||
return .empty
|
||||
}
|
||||
|
||||
var actions: [TimelineItemContextMenuAction] = [
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
import SwiftUI
|
||||
|
||||
struct TimelineItemContextMenuActions {
|
||||
static var empty: TimelineItemContextMenuActions { .init(actions: [], debugActions: []) }
|
||||
|
||||
let actions: [TimelineItemContextMenuAction]
|
||||
let debugActions: [TimelineItemContextMenuAction]
|
||||
var isEmpty: Bool { actions.isEmpty && debugActions.isEmpty }
|
||||
}
|
||||
|
||||
enum TimelineItemContextMenuAction: Identifiable, Hashable {
|
||||
@@ -48,13 +51,17 @@ public struct TimelineItemContextMenu: View {
|
||||
let contextMenuActions: TimelineItemContextMenuActions
|
||||
let callback: (TimelineItemContextMenuAction) -> Void
|
||||
|
||||
@ViewBuilder
|
||||
public var body: some View {
|
||||
viewsForActions(contextMenuActions.actions)
|
||||
Menu {
|
||||
viewsForActions(contextMenuActions.debugActions)
|
||||
} label: {
|
||||
Label("Developer", systemImage: "hammer")
|
||||
if contextMenuActions.isEmpty {
|
||||
// When there are no actions make sure then menu isn't shown.
|
||||
EmptyView()
|
||||
} else {
|
||||
viewsForActions(contextMenuActions.actions)
|
||||
Menu {
|
||||
viewsForActions(contextMenuActions.debugActions)
|
||||
} label: {
|
||||
Label("Developer", systemImage: "hammer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ class TimelineTableViewController: UIViewController {
|
||||
return .systemAction
|
||||
})
|
||||
.onTapGesture(count: 2) {
|
||||
coordinator.send(viewAction: .displayEmojiPicker(itemId: timelineItem.id))
|
||||
coordinator.send(viewAction: .itemDoubleTapped(id: timelineItem.id))
|
||||
}
|
||||
.onTapGesture {
|
||||
coordinator.send(viewAction: .itemTapped(id: timelineItem.id))
|
||||
|
||||
Reference in New Issue
Block a user