mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Implement thread list long press actions, fix #5310
This commit is contained in:
parent
a69738266c
commit
36d1873c98
9 changed files with 131 additions and 5 deletions
|
@ -69,6 +69,10 @@ extension ThreadListCoordinator: ThreadListViewModelCoordinatorDelegate {
|
|||
self.delegate?.threadListCoordinatorDidSelectThread(self, thread: thread)
|
||||
}
|
||||
|
||||
func threadListViewModelDidSelectThreadViewInRoom(_ viewModel: ThreadListViewModelProtocol, thread: MXThread) {
|
||||
self.delegate?.threadListCoordinatorDidSelectRoom(self, roomId: thread.roomId, eventId: thread.id)
|
||||
}
|
||||
|
||||
func threadListViewModelDidCancel(_ viewModel: ThreadListViewModelProtocol) {
|
||||
self.delegate?.threadListCoordinatorDidCancel(self)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import Foundation
|
|||
protocol ThreadListCoordinatorDelegate: AnyObject {
|
||||
func threadListCoordinatorDidLoadThreads(_ coordinator: ThreadListCoordinatorProtocol)
|
||||
func threadListCoordinatorDidSelectThread(_ coordinator: ThreadListCoordinatorProtocol, thread: MXThread)
|
||||
func threadListCoordinatorDidSelectRoom(_ coordinator: ThreadListCoordinatorProtocol, roomId: String, eventId: String)
|
||||
func threadListCoordinatorDidCancel(_ coordinator: ThreadListCoordinatorProtocol)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,5 +25,9 @@ enum ThreadListViewAction {
|
|||
case showFilterTypes
|
||||
case selectFilterType(_ type: ThreadListFilterType)
|
||||
case selectThread(_ index: Int)
|
||||
case longPressThread(_ index: Int)
|
||||
case actionViewInRoom
|
||||
case actionCopyLinkToThread
|
||||
case actionShare
|
||||
case cancel
|
||||
}
|
||||
|
|
|
@ -20,9 +20,11 @@
|
|||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="X8K-NO-SQ3">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<gestureRecognizers/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="V8j-Lb-PgC" id="FCQ-5E-AuZ"/>
|
||||
<outlet property="delegate" destination="V8j-Lb-PgC" id="Kxs-vj-1RW"/>
|
||||
<outletCollection property="gestureRecognizers" destination="OxP-Mp-c6Z" appends="YES" id="371-L6-Skg"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<view contentMode="scaleToFill" placeholderIntrinsicWidth="414" placeholderIntrinsicHeight="818" translatesAutoresizingMaskIntoConstraints="NO" id="7VY-m9-wCS" customClass="ThreadListEmptyView" customModule="Riot" customModuleProvider="target">
|
||||
|
@ -52,6 +54,11 @@
|
|||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="OxP-Mp-c6Z">
|
||||
<connections>
|
||||
<action selector="longPressed:" destination="V8j-Lb-PgC" id="rvv-ml-CSq"/>
|
||||
</connections>
|
||||
</pongPressGestureRecognizer>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3198.5507246376815" y="-647.54464285714278"/>
|
||||
</scene>
|
||||
|
|
|
@ -147,15 +147,19 @@ final class ThreadListViewController: UIViewController {
|
|||
case .idle:
|
||||
break
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
renderLoading()
|
||||
case .loaded:
|
||||
self.renderLoaded()
|
||||
renderLoaded()
|
||||
case .empty(let viewModel):
|
||||
self.renderEmptyView(withViewModel: viewModel)
|
||||
renderEmptyView(withViewModel: viewModel)
|
||||
case .showingFilterTypes:
|
||||
self.renderShowingFilterTypes()
|
||||
renderShowingFilterTypes()
|
||||
case .showingLongPressActions:
|
||||
renderShowingLongPressActions()
|
||||
case .share(let string):
|
||||
renderShare(string)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,6 +218,44 @@ final class ThreadListViewController: UIViewController {
|
|||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func renderShowingLongPressActions() {
|
||||
let controller = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
controller.addAction(UIAlertAction(title: VectorL10n.roomEventActionViewInRoom,
|
||||
style: .default,
|
||||
handler: { [weak self] action in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.process(viewAction: .actionViewInRoom)
|
||||
}))
|
||||
|
||||
controller.addAction(UIAlertAction(title: VectorL10n.threadCopyLinkToThread,
|
||||
style: .default,
|
||||
handler: { [weak self] action in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.process(viewAction: .actionCopyLinkToThread)
|
||||
}))
|
||||
|
||||
controller.addAction(UIAlertAction(title: VectorL10n.roomEventActionShare,
|
||||
style: .default,
|
||||
handler: { [weak self] action in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.process(viewAction: .actionShare)
|
||||
}))
|
||||
|
||||
controller.addAction(UIAlertAction(title: VectorL10n.cancel,
|
||||
style: .cancel,
|
||||
handler: nil))
|
||||
|
||||
self.present(controller, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func renderShare(_ string: String) {
|
||||
let activityVC = UIActivityViewController(activityItems: [string],
|
||||
applicationActivities: nil)
|
||||
activityVC.modalTransitionStyle = .coverVertical
|
||||
present(activityVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
|
@ -225,6 +267,22 @@ final class ThreadListViewController: UIViewController {
|
|||
private func filterButtonTapped(_ sender: UIBarButtonItem) {
|
||||
self.viewModel.process(viewAction: .showFilterTypes)
|
||||
}
|
||||
|
||||
@IBAction private func longPressed(_ sender: UILongPressGestureRecognizer) {
|
||||
guard sender.state == .began else {
|
||||
return
|
||||
}
|
||||
let point = sender.location(in: threadsTableView)
|
||||
guard let indexPath = threadsTableView.indexPathForRow(at: point) else {
|
||||
return
|
||||
}
|
||||
guard let cell = threadsTableView.cellForRow(at: indexPath) else {
|
||||
return
|
||||
}
|
||||
if cell.isHighlighted {
|
||||
viewModel.process(viewAction: .longPressThread(indexPath.row))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ final class ThreadListViewModel: ThreadListViewModelProtocol {
|
|||
private var roomState: MXRoomState?
|
||||
|
||||
private var currentOperation: MXHTTPOperation?
|
||||
private var longPressedThread: MXThread?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
@ -73,6 +74,14 @@ final class ThreadListViewModel: ThreadListViewModelProtocol {
|
|||
loadData()
|
||||
case .selectThread(let index):
|
||||
selectThread(index)
|
||||
case .longPressThread(let index):
|
||||
longPressThread(index)
|
||||
case .actionViewInRoom:
|
||||
actionViewInRoom()
|
||||
case .actionCopyLinkToThread:
|
||||
actionCopyLinkToThread()
|
||||
case .actionShare:
|
||||
actionShare()
|
||||
case .cancel:
|
||||
cancelOperations()
|
||||
coordinatorDelegate?.threadListViewModelDidCancel(self)
|
||||
|
@ -264,6 +273,42 @@ final class ThreadListViewModel: ThreadListViewModelProtocol {
|
|||
coordinatorDelegate?.threadListViewModelDidSelectThread(self, thread: thread)
|
||||
}
|
||||
|
||||
private func longPressThread(_ index: Int) {
|
||||
guard index < threads.count else {
|
||||
return
|
||||
}
|
||||
longPressedThread = threads[index]
|
||||
viewState = .showingLongPressActions
|
||||
}
|
||||
|
||||
private func actionViewInRoom() {
|
||||
guard let thread = longPressedThread else {
|
||||
return
|
||||
}
|
||||
coordinatorDelegate?.threadListViewModelDidSelectThreadViewInRoom(self, thread: thread)
|
||||
longPressedThread = nil
|
||||
}
|
||||
|
||||
private func actionCopyLinkToThread() {
|
||||
guard let thread = longPressedThread else {
|
||||
return
|
||||
}
|
||||
if let permalink = MXTools.permalink(toEvent: thread.id, inRoom: thread.roomId) {
|
||||
MXKPasteboardManager.shared.pasteboard.string = permalink
|
||||
}
|
||||
longPressedThread = nil
|
||||
}
|
||||
|
||||
private func actionShare() {
|
||||
guard let thread = longPressedThread else {
|
||||
return
|
||||
}
|
||||
if let permalink = MXTools.permalink(toEvent: thread.id, inRoom: thread.roomId) {
|
||||
viewState = .share(permalink)
|
||||
}
|
||||
longPressedThread = nil
|
||||
}
|
||||
|
||||
private func cancelOperations() {
|
||||
self.currentOperation?.cancel()
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ protocol ThreadListViewModelViewDelegate: AnyObject {
|
|||
protocol ThreadListViewModelCoordinatorDelegate: AnyObject {
|
||||
func threadListViewModelDidLoadThreads(_ viewModel: ThreadListViewModelProtocol)
|
||||
func threadListViewModelDidSelectThread(_ viewModel: ThreadListViewModelProtocol, thread: MXThread)
|
||||
func threadListViewModelDidSelectThreadViewInRoom(_ viewModel: ThreadListViewModelProtocol, thread: MXThread)
|
||||
func threadListViewModelDidCancel(_ viewModel: ThreadListViewModelProtocol)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,5 +25,7 @@ enum ThreadListViewState {
|
|||
case loaded
|
||||
case empty(_ viewModel: ThreadListEmptyViewModel)
|
||||
case showingFilterTypes
|
||||
case showingLongPressActions
|
||||
case share(_ string: String)
|
||||
case error(Error)
|
||||
}
|
||||
|
|
|
@ -151,6 +151,10 @@ extension ThreadsCoordinator: ThreadListCoordinatorDelegate {
|
|||
self.add(childCoordinator: roomCoordinator)
|
||||
}
|
||||
|
||||
func threadListCoordinatorDidSelectRoom(_ coordinator: ThreadListCoordinatorProtocol, roomId: String, eventId: String) {
|
||||
self.delegate?.threadsCoordinatorDidSelect(self, roomId: roomId, eventId: eventId)
|
||||
}
|
||||
|
||||
func threadListCoordinatorDidCancel(_ coordinator: ThreadListCoordinatorProtocol) {
|
||||
self.delegate?.threadsCoordinatorDidComplete(self)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue