mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Add quote and copy context menu actions.
This commit is contained in:
parent
7f1c24059f
commit
1e76faa1c8
@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
01CB8ACFA5E143E89C168CA8 /* TimelineItemContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */; };
|
||||||
01F4A40C1EDCEC8DC4EC9CFA /* WeakDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 109C0201D8CB3F947340DC80 /* WeakDictionary.swift */; };
|
01F4A40C1EDCEC8DC4EC9CFA /* WeakDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 109C0201D8CB3F947340DC80 /* WeakDictionary.swift */; };
|
||||||
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; };
|
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; };
|
||||||
03B8FEA668A5B76A93113BB1 /* MemberDetailProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2ABC1A9B62BDB3D216E7FD /* MemberDetailProviderManager.swift */; };
|
03B8FEA668A5B76A93113BB1 /* MemberDetailProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2ABC1A9B62BDB3D216E7FD /* MemberDetailProviderManager.swift */; };
|
||||||
@ -254,6 +255,7 @@
|
|||||||
AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderTests.swift; sourceTree = "<group>"; };
|
AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderTests.swift; sourceTree = "<group>"; };
|
||||||
B12969CEC0051BC750DA5068 /* WeakKeyDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakKeyDictionary.swift; sourceTree = "<group>"; };
|
B12969CEC0051BC750DA5068 /* WeakKeyDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakKeyDictionary.swift; sourceTree = "<group>"; };
|
||||||
B4173A48FD8542CD4AD3645C /* NavigationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = "<group>"; };
|
B4173A48FD8542CD4AD3645C /* NavigationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = "<group>"; };
|
||||||
|
B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemContextMenu.swift; sourceTree = "<group>"; };
|
||||||
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
B6BDAC8895AB2B77B47703AE /* MockRoomSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomSummary.swift; sourceTree = "<group>"; };
|
B6BDAC8895AB2B77B47703AE /* MockRoomSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomSummary.swift; sourceTree = "<group>"; };
|
||||||
B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenCoordinator.swift; sourceTree = "<group>"; };
|
B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@ -541,6 +543,7 @@
|
|||||||
E18CF12478983A5EB390FB26 /* MessageComposer.swift */,
|
E18CF12478983A5EB390FB26 /* MessageComposer.swift */,
|
||||||
BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */,
|
BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */,
|
||||||
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */,
|
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */,
|
||||||
|
B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */,
|
||||||
804F9B0FABE093C7284CD09B /* TimelineItemList.swift */,
|
804F9B0FABE093C7284CD09B /* TimelineItemList.swift */,
|
||||||
874A1842477895F199567BD7 /* TimelineView.swift */,
|
874A1842477895F199567BD7 /* TimelineView.swift */,
|
||||||
B7D3886505ECC85A06DA8258 /* Timeline */,
|
B7D3886505ECC85A06DA8258 /* Timeline */,
|
||||||
@ -1062,6 +1065,7 @@
|
|||||||
D013E70C8E28E43497820444 /* TextRoomMessage.swift in Sources */,
|
D013E70C8E28E43497820444 /* TextRoomMessage.swift in Sources */,
|
||||||
7963F98CDFDEAC75E072BD81 /* TextRoomTimelineItem.swift in Sources */,
|
7963F98CDFDEAC75E072BD81 /* TextRoomTimelineItem.swift in Sources */,
|
||||||
5E0F2E612718BB4397A6D40A /* TextRoomTimelineView.swift in Sources */,
|
5E0F2E612718BB4397A6D40A /* TextRoomTimelineView.swift in Sources */,
|
||||||
|
01CB8ACFA5E143E89C168CA8 /* TimelineItemContextMenu.swift in Sources */,
|
||||||
4D970CB606276717B43E2332 /* TimelineItemList.swift in Sources */,
|
4D970CB606276717B43E2332 /* TimelineItemList.swift in Sources */,
|
||||||
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */,
|
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */,
|
||||||
15FEC447B7FEA62F418732AC /* ToastActivityPresenter.swift in Sources */,
|
15FEC447B7FEA62F418732AC /* ToastActivityPresenter.swift in Sources */,
|
||||||
|
@ -20,6 +20,11 @@ enum RoomScreenViewModelResult {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum TimelineItemContextMenuAction: Hashable {
|
||||||
|
case copy
|
||||||
|
case quote
|
||||||
|
}
|
||||||
|
|
||||||
enum RoomScreenViewAction {
|
enum RoomScreenViewAction {
|
||||||
case loadPreviousPage
|
case loadPreviousPage
|
||||||
case itemAppeared(id: String)
|
case itemAppeared(id: String)
|
||||||
@ -34,6 +39,8 @@ struct RoomScreenViewState: BindableState {
|
|||||||
var isBackPaginating = false
|
var isBackPaginating = false
|
||||||
var bindings: RoomScreenViewStateBindings
|
var bindings: RoomScreenViewStateBindings
|
||||||
|
|
||||||
|
var contextMenuBuilder: ((_ itemId: String) -> TimelineItemContextMenu)?
|
||||||
|
|
||||||
var sendButtonDisabled: Bool {
|
var sendButtonDisabled: Bool {
|
||||||
bindings.composerText.count == 0
|
bindings.composerText.count == 0
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
}.store(in: &cancellables)
|
}.store(in: &cancellables)
|
||||||
|
|
||||||
|
state.contextMenuBuilder = buildContexMenuForItemId(_:)
|
||||||
|
|
||||||
buildTimelineViews()
|
buildTimelineViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,4 +95,35 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
|
|
||||||
state.items = stateItems
|
state.items = stateItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: ContextMenus
|
||||||
|
|
||||||
|
private func buildContexMenuForItemId(_ itemId: String) -> TimelineItemContextMenu {
|
||||||
|
TimelineItemContextMenu(contextMenuActions: self.contextMenuActionsForItemId(itemId)) { [weak self] action in
|
||||||
|
self?.processContentMenuAction(action, itemId: itemId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func contextMenuActionsForItemId(_ itemId: String) -> [TimelineItemContextMenuAction] {
|
||||||
|
guard let timelineItem = self.timelineController.timelineItems.first(where: { $0.id == itemId }),
|
||||||
|
timelineItem is EventBasedTimelineItemProtocol else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.copy, .quote]
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processContentMenuAction(_ action: TimelineItemContextMenuAction, itemId: String) {
|
||||||
|
guard let timelineItem = self.timelineController.timelineItems.first(where: { $0.id == itemId }),
|
||||||
|
let item = timelineItem as? EventBasedTimelineItemProtocol else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case .copy:
|
||||||
|
UIPasteboard.general.string = item.text
|
||||||
|
case .quote:
|
||||||
|
state.bindings.composerText = "> \(item.text)"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,9 +129,9 @@ private struct UITextViewWrapper: UIViewRepresentable {
|
|||||||
self.maxHeight = maxHeight
|
self.maxHeight = maxHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
func textViewDidChange(_ uiView: UITextView) {
|
func textViewDidChange(_ textView: UITextView) {
|
||||||
text.wrappedValue = uiView.text
|
text.wrappedValue = textView.text
|
||||||
UITextViewWrapper.recalculateHeight(view: uiView,
|
UITextViewWrapper.recalculateHeight(view: textView,
|
||||||
result: calculatedHeight,
|
result: calculatedHeight,
|
||||||
maxHeight: maxHeight)
|
maxHeight: maxHeight)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// TimelineItemContextMenu.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 18/04/2022.
|
||||||
|
// Copyright © 2022 element.io. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct TimelineItemContextMenu: View {
|
||||||
|
|
||||||
|
let contextMenuActions: [TimelineItemContextMenuAction]
|
||||||
|
let callback: (TimelineItemContextMenuAction) -> Void
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
public var body: some View {
|
||||||
|
ForEach(contextMenuActions, id: \.self) { item in
|
||||||
|
switch item {
|
||||||
|
case .copy:
|
||||||
|
Button("Copy") {
|
||||||
|
callback(item)
|
||||||
|
}
|
||||||
|
case .quote:
|
||||||
|
Button("Quote") {
|
||||||
|
callback(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,9 @@ struct TimelineItemList: View {
|
|||||||
// No idea why previews don't work otherwise
|
// No idea why previews don't work otherwise
|
||||||
ForEach(isPreview ? context.viewState.items : timelineItems) { timelineItem in
|
ForEach(isPreview ? context.viewState.items : timelineItems) { timelineItem in
|
||||||
timelineItem
|
timelineItem
|
||||||
|
.contextMenu(menuItems: {
|
||||||
|
context.viewState.contextMenuBuilder?(timelineItem.id)
|
||||||
|
})
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
context.send(viewAction: .itemAppeared(id: timelineItem.id))
|
context.send(viewAction: .itemAppeared(id: timelineItem.id))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user