mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 15:22:39 +00:00
Rename UserSuggestion
module as CompletionSuggestion
This commit is contained in:
parent
3cdbc26aed
commit
6d981004ed
24 changed files with 353 additions and 339 deletions
|
@ -61,7 +61,7 @@ extern NSTimeInterval const kResizeComposerAnimationDuration;
|
|||
// The preview header
|
||||
@property (weak, nonatomic, nullable) IBOutlet UIView *previewHeaderContainer;
|
||||
@property (weak, nonatomic, nullable) IBOutlet NSLayoutConstraint *previewHeaderContainerHeightConstraint;
|
||||
@property (weak, nonatomic, nullable) IBOutlet NSLayoutConstraint *userSuggestionContainerHeightConstraint;
|
||||
@property (weak, nonatomic, nullable) IBOutlet NSLayoutConstraint *completionSuggestionContainerHeightConstraint;
|
||||
|
||||
// The jump to last unread banner
|
||||
@property (weak, nonatomic, nullable) IBOutlet UIView *jumpToLastUnreadBannerContainer;
|
||||
|
|
|
@ -97,7 +97,7 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, UIScrollViewAccessibilityDelegate, RoomTitleViewTapGestureDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate,
|
||||
ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate, MXKDocumentPickerPresenterDelegate, EmojiPickerCoordinatorBridgePresenterDelegate,
|
||||
ReactionHistoryCoordinatorBridgePresenterDelegate, CameraPresenterDelegate, MediaPickerCoordinatorBridgePresenterDelegate,
|
||||
RoomDataSourceDelegate, RoomCreationModalCoordinatorBridgePresenterDelegate, RoomInfoCoordinatorBridgePresenterDelegate, DialpadViewControllerDelegate, RemoveJitsiWidgetViewDelegate, VoiceMessageControllerDelegate, SpaceDetailPresenterDelegate, UserSuggestionCoordinatorBridgeDelegate, ThreadsCoordinatorBridgePresenterDelegate, ThreadsBetaCoordinatorBridgePresenterDelegate, MXThreadingServiceDelegate, RoomParticipantsInviteCoordinatorBridgePresenterDelegate, RoomInputToolbarViewDelegate, ComposerCreateActionListBridgePresenterDelegate>
|
||||
RoomDataSourceDelegate, RoomCreationModalCoordinatorBridgePresenterDelegate, RoomInfoCoordinatorBridgePresenterDelegate, DialpadViewControllerDelegate, RemoveJitsiWidgetViewDelegate, VoiceMessageControllerDelegate, SpaceDetailPresenterDelegate, CompletionSuggestionCoordinatorBridgeDelegate, ThreadsCoordinatorBridgePresenterDelegate, ThreadsBetaCoordinatorBridgePresenterDelegate, MXThreadingServiceDelegate, RoomParticipantsInviteCoordinatorBridgePresenterDelegate, RoomInputToolbarViewDelegate, ComposerCreateActionListBridgePresenterDelegate>
|
||||
{
|
||||
|
||||
// The preview header
|
||||
|
@ -223,8 +223,8 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
@property (nonatomic, strong) ShareManager *shareManager;
|
||||
@property (nonatomic, strong) EventMenuBuilder *eventMenuBuilder;
|
||||
|
||||
@property (nonatomic, strong) UserSuggestionCoordinatorBridge *userSuggestionCoordinator;
|
||||
@property (nonatomic, weak) IBOutlet UIView *userSuggestionContainerView;
|
||||
@property (nonatomic, strong) CompletionSuggestionCoordinatorBridge *completionSuggestionCoordinator;
|
||||
@property (nonatomic, weak) IBOutlet UIView *completionSuggestionContainerView;
|
||||
|
||||
@property (nonatomic, readwrite) RoomDisplayConfiguration *displayConfiguration;
|
||||
|
||||
|
@ -416,7 +416,7 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
|
||||
[self setupActions];
|
||||
|
||||
[self setupUserSuggestionViewIfNeeded];
|
||||
[self setupCompletionSuggestionViewIfNeeded];
|
||||
|
||||
[self.topBannersStackView vc_removeAllSubviews];
|
||||
}
|
||||
|
@ -1088,12 +1088,12 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
[VoiceMessageMediaServiceProvider.sharedProvider setCurrentRoomSummary:dataSource.room.summary];
|
||||
_voiceMessageController.roomId = dataSource.roomId;
|
||||
|
||||
_userSuggestionCoordinator = [[UserSuggestionCoordinatorBridge alloc] initWithMediaManager:self.roomDataSource.mxSession.mediaManager
|
||||
_completionSuggestionCoordinator = [[CompletionSuggestionCoordinatorBridge alloc] initWithMediaManager:self.roomDataSource.mxSession.mediaManager
|
||||
room:dataSource.room
|
||||
userID:self.roomDataSource.mxSession.myUserId];
|
||||
_userSuggestionCoordinator.delegate = self;
|
||||
_completionSuggestionCoordinator.delegate = self;
|
||||
|
||||
[self setupUserSuggestionViewIfNeeded];
|
||||
[self setupCompletionSuggestionViewIfNeeded];
|
||||
|
||||
[self updateTopBanners];
|
||||
}
|
||||
|
@ -2726,13 +2726,13 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setupUserSuggestionViewIfNeeded
|
||||
- (void)setupCompletionSuggestionViewIfNeeded
|
||||
{
|
||||
if(!self.isViewLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIViewController *suggestionsViewController = self.userSuggestionCoordinator.toPresentable;
|
||||
UIViewController *suggestionsViewController = self.completionSuggestionCoordinator.toPresentable;
|
||||
|
||||
if (!suggestionsViewController)
|
||||
{
|
||||
|
@ -2742,12 +2742,12 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
[suggestionsViewController.view setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
[self addChildViewController:suggestionsViewController];
|
||||
[self.userSuggestionContainerView addSubview:suggestionsViewController.view];
|
||||
[self.completionSuggestionContainerView addSubview:suggestionsViewController.view];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[[suggestionsViewController.view.topAnchor constraintEqualToAnchor:self.userSuggestionContainerView.topAnchor],
|
||||
[suggestionsViewController.view.leadingAnchor constraintEqualToAnchor:self.userSuggestionContainerView.leadingAnchor],
|
||||
[suggestionsViewController.view.trailingAnchor constraintEqualToAnchor:self.userSuggestionContainerView.trailingAnchor],
|
||||
[suggestionsViewController.view.bottomAnchor constraintEqualToAnchor:self.userSuggestionContainerView.bottomAnchor],]];
|
||||
[NSLayoutConstraint activateConstraints:@[[suggestionsViewController.view.topAnchor constraintEqualToAnchor:self.completionSuggestionContainerView.topAnchor],
|
||||
[suggestionsViewController.view.leadingAnchor constraintEqualToAnchor:self.completionSuggestionContainerView.leadingAnchor],
|
||||
[suggestionsViewController.view.trailingAnchor constraintEqualToAnchor:self.completionSuggestionContainerView.trailingAnchor],
|
||||
[suggestionsViewController.view.bottomAnchor constraintEqualToAnchor:self.completionSuggestionContainerView.bottomAnchor],]];
|
||||
|
||||
[suggestionsViewController didMoveToParentViewController:self];
|
||||
}
|
||||
|
@ -5147,17 +5147,17 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
|
||||
- (void)roomInputToolbarViewDidChangeTextMessage:(RoomInputToolbarView *)toolbarView
|
||||
{
|
||||
[self.userSuggestionCoordinator processTextMessage:toolbarView.textMessage];
|
||||
[self.completionSuggestionCoordinator processTextMessage:toolbarView.textMessage];
|
||||
}
|
||||
|
||||
- (void)didDetectTextPattern:(SuggestionPatternWrapper *)suggestionPattern
|
||||
{
|
||||
[self.userSuggestionCoordinator processSuggestionPattern:suggestionPattern];
|
||||
[self.completionSuggestionCoordinator processSuggestionPattern:suggestionPattern];
|
||||
}
|
||||
|
||||
- (UserSuggestionViewModelContextWrapper *)userSuggestionContext
|
||||
- (CompletionSuggestionViewModelContextWrapper *)completionSuggestionContext
|
||||
{
|
||||
return [self.userSuggestionCoordinator sharedContext];
|
||||
return [self.completionSuggestionCoordinator sharedContext];
|
||||
}
|
||||
|
||||
- (MXMediaManager *)mediaManager
|
||||
|
@ -8059,9 +8059,9 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
[[LegacyAppDelegate theDelegate] openSpaceWithId:spaceId];
|
||||
}
|
||||
|
||||
#pragma mark - UserSuggestionCoordinatorBridgeDelegate
|
||||
#pragma mark - CompletionSuggestionCoordinatorBridgeDelegate
|
||||
|
||||
- (void)userSuggestionCoordinatorBridge:(UserSuggestionCoordinatorBridge *)coordinator
|
||||
- (void)completionSuggestionCoordinatorBridge:(CompletionSuggestionCoordinatorBridge *)coordinator
|
||||
didRequestMentionForMember:(MXRoomMember *)member
|
||||
textTrigger:(NSString *)textTrigger
|
||||
{
|
||||
|
@ -8069,14 +8069,14 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
[self mention:member];
|
||||
}
|
||||
|
||||
- (void)userSuggestionCoordinatorBridgeDidRequestMentionForRoom:(UserSuggestionCoordinatorBridge *)coordinator
|
||||
- (void)completionSuggestionCoordinatorBridgeDidRequestMentionForRoom:(CompletionSuggestionCoordinatorBridge *)coordinator
|
||||
textTrigger:(NSString *)textTrigger
|
||||
{
|
||||
[self removeTriggerTextFromComposer:textTrigger];
|
||||
[self.inputToolbarView pasteText:[UserSuggestionID.room stringByAppendingString:@" "]];
|
||||
[self.inputToolbarView pasteText:[CompletionSuggestionUserID.room stringByAppendingString:@" "]];
|
||||
}
|
||||
|
||||
- (void)userSuggestionCoordinatorBridge:(UserSuggestionCoordinatorBridge *)coordinator
|
||||
- (void)completionSuggestionCoordinatorBridge:(CompletionSuggestionCoordinatorBridge *)coordinator
|
||||
didRequestCommand:(NSString *)command
|
||||
textTrigger:(NSString *)textTrigger
|
||||
{
|
||||
|
@ -8097,11 +8097,11 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)userSuggestionCoordinatorBridge:(UserSuggestionCoordinatorBridge *)coordinator didUpdateViewHeight:(CGFloat)height
|
||||
- (void)completionSuggestionCoordinatorBridge:(CompletionSuggestionCoordinatorBridge *)coordinator didUpdateViewHeight:(CGFloat)height
|
||||
{
|
||||
if (self.userSuggestionContainerHeightConstraint.constant != height)
|
||||
if (self.completionSuggestionContainerHeightConstraint.constant != height)
|
||||
{
|
||||
self.userSuggestionContainerHeightConstraint.constant = height;
|
||||
self.completionSuggestionContainerHeightConstraint.constant = height;
|
||||
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -13,6 +12,8 @@
|
|||
<connections>
|
||||
<outlet property="bubblesTableView" destination="BGD-sd-SQR" id="OG4-Tw-Ovt"/>
|
||||
<outlet property="bubblesTableViewBottomConstraint" destination="1SD-y2-oTg" id="n8D-hT-eqt"/>
|
||||
<outlet property="completionSuggestionContainerHeightConstraint" destination="1Cd-cT-gOr" id="au5-3q-r54"/>
|
||||
<outlet property="completionSuggestionContainerView" destination="oni-F4-X1U" id="0js-Ji-8Mm"/>
|
||||
<outlet property="inputBackgroundView" destination="Xt7-83-dQh" id="xoG-eb-zFB"/>
|
||||
<outlet property="jumpToLastUnreadBanner" destination="S6r-bo-jxw" id="FSS-Be-E15"/>
|
||||
<outlet property="jumpToLastUnreadBannerContainer" destination="S6H-Az-RCM" id="YlI-fu-OpT"/>
|
||||
|
@ -32,8 +33,6 @@
|
|||
<outlet property="scrollToBottomBadgeLabel" destination="QHs-rM-UU8" id="wk7-PQ-9Jm"/>
|
||||
<outlet property="scrollToBottomButton" destination="Ih9-EU-BOU" id="Wwg-gS-Sfp"/>
|
||||
<outlet property="topBannersStackView" destination="3z2-8P-wlg" id="uf5-gw-zWi"/>
|
||||
<outlet property="userSuggestionContainerHeightConstraint" destination="1Cd-cT-gOr" id="au5-3q-r54"/>
|
||||
<outlet property="userSuggestionContainerView" destination="oni-F4-X1U" id="0js-Ji-8Mm"/>
|
||||
<outlet property="view" destination="iN0-l3-epB" id="ieV-u7-rXU"/>
|
||||
<outletCollection property="toolbarContainerConstraints" destination="T1Y-r9-bYV" id="wax-9P-KGn"/>
|
||||
<outletCollection property="toolbarContainerConstraints" destination="pRw-S0-6WL" id="q4S-0g-sqQ"/>
|
||||
|
@ -48,20 +47,20 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="3z2-8P-wlg" userLabel="Top Banners Stack View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="0.0"/>
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="0.0"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="250" id="Y9P-Ek-wjg"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<tableView contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="BGD-sd-SQR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="626"/>
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="606"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="RoomVCBubblesTableView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="54r-18-K1g">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="368"/>
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="368"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="RoomVCPreviewHeaderContainer"/>
|
||||
<constraints>
|
||||
|
@ -69,7 +68,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="fmF-ad-erE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="0.0"/>
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="0.0"/>
|
||||
<subviews>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hB3-nR-MVR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="54"/>
|
||||
|
@ -189,7 +188,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gt1-EO-UVY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</subviews>
|
||||
|
@ -237,11 +236,6 @@
|
|||
<point key="canvasLocation" x="136.80000000000001" y="152.47376311844079"/>
|
||||
</view>
|
||||
</objects>
|
||||
<designables>
|
||||
<designable name="QHs-rM-UU8">
|
||||
<size key="intrinsicContentSize" width="7.5" height="13.5"/>
|
||||
</designable>
|
||||
</designables>
|
||||
<resources>
|
||||
<image name="new_close" width="16" height="16"/>
|
||||
<image name="room_scroll_up" width="24" height="24"/>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
@class RoomInputToolbarView;
|
||||
@class LinkActionWrapper;
|
||||
@class SuggestionPatternWrapper;
|
||||
@class UserSuggestionViewModelContextWrapper;
|
||||
@class CompletionSuggestionViewModelContextWrapper;
|
||||
|
||||
/**
|
||||
Destination of the message in the composer
|
||||
|
@ -84,7 +84,7 @@ typedef NS_ENUM(NSUInteger, RoomInputToolbarViewSendMode)
|
|||
|
||||
- (void)didDetectTextPattern: (SuggestionPatternWrapper *)suggestionPattern;
|
||||
|
||||
- (UserSuggestionViewModelContextWrapper *)userSuggestionContext;
|
||||
- (CompletionSuggestionViewModelContextWrapper *)completionSuggestionContext;
|
||||
|
||||
- (MXMediaManager *)mediaManager;
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
let composer = Composer(
|
||||
viewModel: viewModel.context,
|
||||
wysiwygViewModel: wysiwygViewModel,
|
||||
userSuggestionSharedContext: toolbarViewDelegate.userSuggestionContext().context,
|
||||
completionSuggestionSharedContext: toolbarViewDelegate.completionSuggestionContext().context,
|
||||
resizeAnimationDuration: Double(kResizeComposerAnimationDuration),
|
||||
sendMessageAction: { [weak self] content in
|
||||
guard let self = self else { return }
|
||||
|
|
|
@ -51,7 +51,7 @@ enum MockAppScreens {
|
|||
MockStaticLocationViewingScreenState.self,
|
||||
MockLocationSharingScreenState.self,
|
||||
MockAnalyticsPromptScreenState.self,
|
||||
MockUserSuggestionScreenState.self,
|
||||
MockCompletionSuggestionScreenState.self,
|
||||
MockPollEditFormScreenState.self,
|
||||
MockSpaceCreationEmailInvitesScreenState.self,
|
||||
MockSpaceSettingsScreenState.self,
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum UserSuggestionViewAction {
|
||||
case selectedItem(UserSuggestionViewStateItem)
|
||||
enum CompletionSuggestionViewAction {
|
||||
case selectedItem(CompletionSuggestionViewStateItem)
|
||||
}
|
||||
|
||||
enum UserSuggestionViewModelResult {
|
||||
enum CompletionSuggestionViewModelResult {
|
||||
case selectedItemWithIdentifier(String)
|
||||
}
|
||||
|
||||
enum UserSuggestionViewStateItem: Identifiable {
|
||||
enum CompletionSuggestionViewStateItem: Identifiable {
|
||||
case command(name: String)
|
||||
case user(id: String, avatar: AvatarInputProtocol?, displayName: String?)
|
||||
|
||||
|
@ -38,6 +38,6 @@ enum UserSuggestionViewStateItem: Identifiable {
|
|||
}
|
||||
}
|
||||
|
||||
struct UserSuggestionViewState: BindableState {
|
||||
var items: [UserSuggestionViewStateItem]
|
||||
struct CompletionSuggestionViewState: BindableState {
|
||||
var items: [CompletionSuggestionViewStateItem]
|
||||
}
|
|
@ -17,32 +17,32 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
enum MockUserSuggestionScreenState: MockScreenState, CaseIterable {
|
||||
enum MockCompletionSuggestionScreenState: MockScreenState, CaseIterable {
|
||||
case multipleResults
|
||||
|
||||
private static var members: [RoomMembersProviderMember]!
|
||||
|
||||
var screenType: Any.Type {
|
||||
UserSuggestionList.self
|
||||
CompletionSuggestionList.self
|
||||
}
|
||||
|
||||
var screenView: ([Any], AnyView) {
|
||||
let service = UserSuggestionService(roomMemberProvider: self, commandProvider: self)
|
||||
let listViewModel = UserSuggestionViewModel(userSuggestionService: service)
|
||||
let service = CompletionSuggestionService(roomMemberProvider: self, commandProvider: self)
|
||||
let listViewModel = CompletionSuggestionViewModel(completionSuggestionService: service)
|
||||
|
||||
let viewModel = UserSuggestionListWithInputViewModel(listViewModel: listViewModel) { textMessage in
|
||||
let viewModel = CompletionSuggestionListWithInputViewModel(listViewModel: listViewModel) { textMessage in
|
||||
service.processTextMessage(textMessage)
|
||||
}
|
||||
|
||||
return (
|
||||
[service, listViewModel],
|
||||
AnyView(UserSuggestionListWithInput(viewModel: viewModel)
|
||||
AnyView(CompletionSuggestionListWithInput(viewModel: viewModel)
|
||||
.environmentObject(AvatarViewModel.withMockedServices()))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension MockUserSuggestionScreenState: RoomMembersProviderProtocol {
|
||||
extension MockCompletionSuggestionScreenState: RoomMembersProviderProtocol {
|
||||
var canMentionRoom: Bool { false }
|
||||
|
||||
func fetchMembers(_ members: ([RoomMembersProviderMember]) -> Void) {
|
||||
|
@ -61,7 +61,7 @@ extension MockUserSuggestionScreenState: RoomMembersProviderProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
extension MockUserSuggestionScreenState: CommandsProviderProtocol {
|
||||
extension MockCompletionSuggestionScreenState: CommandsProviderProtocol {
|
||||
func fetchCommands(_ commands: @escaping ([CommandsProviderCommand]) -> Void) {
|
||||
commands([
|
||||
CommandsProviderCommand(name: "/ban"),
|
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias CompletionSuggestionViewModelType = StateStoreViewModel<CompletionSuggestionViewState, CompletionSuggestionViewAction>
|
||||
|
||||
class CompletionSuggestionViewModel: CompletionSuggestionViewModelType, CompletionSuggestionViewModelProtocol {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let completionSuggestionService: CompletionSuggestionServiceProtocol
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var sharedContext: CompletionSuggestionViewModelType.Context {
|
||||
return self.context
|
||||
}
|
||||
|
||||
var completion: ((CompletionSuggestionViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(completionSuggestionService: CompletionSuggestionServiceProtocol) {
|
||||
self.completionSuggestionService = completionSuggestionService
|
||||
|
||||
let items = completionSuggestionService.items.value.map { suggestionItem in
|
||||
switch suggestionItem {
|
||||
case .command(let completionSuggestionCommandItem):
|
||||
return CompletionSuggestionViewStateItem.command(name: completionSuggestionCommandItem.name)
|
||||
case .user(let completionSuggestionUserItem):
|
||||
return CompletionSuggestionViewStateItem.user(id: completionSuggestionUserItem.userId,
|
||||
avatar: completionSuggestionUserItem,
|
||||
displayName: completionSuggestionUserItem.displayName)
|
||||
}
|
||||
}
|
||||
|
||||
super.init(initialViewState: CompletionSuggestionViewState(items: items))
|
||||
|
||||
completionSuggestionService.items.sink { [weak self] items in
|
||||
self?.state.items = items.map { item in
|
||||
switch item {
|
||||
case .command(let completionSuggestionCommandItem):
|
||||
return CompletionSuggestionViewStateItem.command(name: completionSuggestionCommandItem.name)
|
||||
case .user(let completionSuggestionUserItem):
|
||||
return CompletionSuggestionViewStateItem.user(id: completionSuggestionUserItem.userId,
|
||||
avatar: completionSuggestionUserItem,
|
||||
displayName: completionSuggestionUserItem.displayName)
|
||||
}
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: CompletionSuggestionViewAction) {
|
||||
switch viewAction {
|
||||
case .selectedItem(let item):
|
||||
completion?(.selectedItemWithIdentifier(item.id))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,10 +16,10 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
protocol UserSuggestionViewModelProtocol {
|
||||
/// Defines a shared context providing the ability to use a single `UserSuggestionViewModel` for multiple
|
||||
/// `UserSuggestionList` e.g. the list component can then be displayed seemlessly in both `RoomViewController`
|
||||
protocol CompletionSuggestionViewModelProtocol {
|
||||
/// Defines a shared context providing the ability to use a single `CompletionSuggestionViewModel` for multiple
|
||||
/// `CompletionSuggestionList` e.g. the list component can then be displayed seemlessly in both `RoomViewController`
|
||||
/// UIKit hosted context, and in Rich-Text-Editor's SwiftUI fullscreen mode, without need to reload the data.
|
||||
var sharedContext: UserSuggestionViewModelType.Context { get }
|
||||
var completion: ((UserSuggestionViewModelResult) -> Void)? { get set }
|
||||
var sharedContext: CompletionSuggestionViewModelType.Context { get }
|
||||
var completion: ((CompletionSuggestionViewModelResult) -> Void)? { get set }
|
||||
}
|
|
@ -20,40 +20,40 @@ import SwiftUI
|
|||
import UIKit
|
||||
import WysiwygComposer
|
||||
|
||||
protocol UserSuggestionCoordinatorDelegate: AnyObject {
|
||||
func userSuggestionCoordinator(_ coordinator: UserSuggestionCoordinator, didRequestMentionForMember member: MXRoomMember, textTrigger: String?)
|
||||
func userSuggestionCoordinatorDidRequestMentionForRoom(_ coordinator: UserSuggestionCoordinator, textTrigger: String?)
|
||||
func userSuggestionCoordinator(_ coordinator: UserSuggestionCoordinator, didRequestCommand command: String, textTrigger: String?)
|
||||
func userSuggestionCoordinator(_ coordinator: UserSuggestionCoordinator, didUpdateViewHeight height: CGFloat)
|
||||
protocol CompletionSuggestionCoordinatorDelegate: AnyObject {
|
||||
func completionSuggestionCoordinator(_ coordinator: CompletionSuggestionCoordinator, didRequestMentionForMember member: MXRoomMember, textTrigger: String?)
|
||||
func completionSuggestionCoordinatorDidRequestMentionForRoom(_ coordinator: CompletionSuggestionCoordinator, textTrigger: String?)
|
||||
func completionSuggestionCoordinator(_ coordinator: CompletionSuggestionCoordinator, didRequestCommand command: String, textTrigger: String?)
|
||||
func completionSuggestionCoordinator(_ coordinator: CompletionSuggestionCoordinator, didUpdateViewHeight height: CGFloat)
|
||||
}
|
||||
|
||||
struct UserSuggestionCoordinatorParameters {
|
||||
struct CompletionSuggestionCoordinatorParameters {
|
||||
let mediaManager: MXMediaManager
|
||||
let room: MXRoom
|
||||
let userID: String
|
||||
}
|
||||
|
||||
/// Wrapper around `UserSuggestionViewModelType.Context` to pass it through obj-c.
|
||||
final class UserSuggestionViewModelContextWrapper: NSObject {
|
||||
let context: UserSuggestionViewModelType.Context
|
||||
/// Wrapper around `CompletionSuggestionViewModelType.Context` to pass it through obj-c.
|
||||
final class CompletionSuggestionViewModelContextWrapper: NSObject {
|
||||
let context: CompletionSuggestionViewModelType.Context
|
||||
|
||||
init(context: UserSuggestionViewModelType.Context) {
|
||||
init(context: CompletionSuggestionViewModelType.Context) {
|
||||
self.context = context
|
||||
}
|
||||
}
|
||||
|
||||
final class UserSuggestionCoordinator: Coordinator, Presentable {
|
||||
final class CompletionSuggestionCoordinator: Coordinator, Presentable {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: UserSuggestionCoordinatorParameters
|
||||
private let parameters: CompletionSuggestionCoordinatorParameters
|
||||
|
||||
private var userSuggestionHostingController: UIHostingController<AnyView>
|
||||
private var userSuggestionService: UserSuggestionServiceProtocol
|
||||
private var userSuggestionViewModel: UserSuggestionViewModelProtocol
|
||||
private var roomMemberProvider: UserSuggestionCoordinatorRoomMemberProvider
|
||||
private var commandProvider: UserSuggestionCoordinatorCommandProvider
|
||||
private var completionSuggestionHostingController: UIHostingController<AnyView>
|
||||
private var completionSuggestionService: CompletionSuggestionServiceProtocol
|
||||
private var completionSuggestionViewModel: CompletionSuggestionViewModelProtocol
|
||||
private var roomMemberProvider: CompletionSuggestionCoordinatorRoomMemberProvider
|
||||
private var commandProvider: CompletionSuggestionCoordinatorCommandProvider
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
@ -63,57 +63,57 @@ final class UserSuggestionCoordinator: Coordinator, Presentable {
|
|||
var childCoordinators: [Coordinator] = []
|
||||
var completion: (() -> Void)?
|
||||
|
||||
weak var delegate: UserSuggestionCoordinatorDelegate?
|
||||
weak var delegate: CompletionSuggestionCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: UserSuggestionCoordinatorParameters) {
|
||||
init(parameters: CompletionSuggestionCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
roomMemberProvider = UserSuggestionCoordinatorRoomMemberProvider(room: parameters.room, userID: parameters.userID)
|
||||
commandProvider = UserSuggestionCoordinatorCommandProvider(room: parameters.room, userID: parameters.userID)
|
||||
userSuggestionService = UserSuggestionService(roomMemberProvider: roomMemberProvider, commandProvider: commandProvider)
|
||||
roomMemberProvider = CompletionSuggestionCoordinatorRoomMemberProvider(room: parameters.room, userID: parameters.userID)
|
||||
commandProvider = CompletionSuggestionCoordinatorCommandProvider(room: parameters.room, userID: parameters.userID)
|
||||
completionSuggestionService = CompletionSuggestionService(roomMemberProvider: roomMemberProvider, commandProvider: commandProvider)
|
||||
|
||||
let viewModel = UserSuggestionViewModel(userSuggestionService: userSuggestionService)
|
||||
let view = UserSuggestionList(viewModel: viewModel.context)
|
||||
let viewModel = CompletionSuggestionViewModel(completionSuggestionService: completionSuggestionService)
|
||||
let view = CompletionSuggestionList(viewModel: viewModel.context)
|
||||
.environmentObject(AvatarViewModel(avatarService: AvatarService(mediaManager: parameters.mediaManager)))
|
||||
|
||||
userSuggestionViewModel = viewModel
|
||||
userSuggestionHostingController = VectorHostingController(rootView: view)
|
||||
completionSuggestionViewModel = viewModel
|
||||
completionSuggestionHostingController = VectorHostingController(rootView: view)
|
||||
|
||||
userSuggestionViewModel.completion = { [weak self] result in
|
||||
completionSuggestionViewModel.completion = { [weak self] result in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .selectedItemWithIdentifier(let identifier):
|
||||
if identifier == UserSuggestionID.room {
|
||||
self.delegate?.userSuggestionCoordinatorDidRequestMentionForRoom(self, textTrigger: self.userSuggestionService.currentTextTrigger)
|
||||
if identifier == CompletionSuggestionUserID.room {
|
||||
self.delegate?.completionSuggestionCoordinatorDidRequestMentionForRoom(self, textTrigger: self.completionSuggestionService.currentTextTrigger)
|
||||
return
|
||||
}
|
||||
|
||||
if let member = self.roomMemberProvider.roomMembers.filter({ $0.userId == identifier }).first {
|
||||
self.delegate?.userSuggestionCoordinator(self, didRequestMentionForMember: member, textTrigger: self.userSuggestionService.currentTextTrigger)
|
||||
self.delegate?.completionSuggestionCoordinator(self, didRequestMentionForMember: member, textTrigger: self.completionSuggestionService.currentTextTrigger)
|
||||
} else if let command = self.commandProvider.commands.filter({ $0 == identifier }).first {
|
||||
self.delegate?.userSuggestionCoordinator(self, didRequestCommand: command, textTrigger: self.userSuggestionService.currentTextTrigger)
|
||||
self.delegate?.completionSuggestionCoordinator(self, didRequestCommand: command, textTrigger: self.completionSuggestionService.currentTextTrigger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userSuggestionService.items.sink { [weak self] _ in
|
||||
completionSuggestionService.items.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.delegate?.userSuggestionCoordinator(self,
|
||||
self.delegate?.completionSuggestionCoordinator(self,
|
||||
didUpdateViewHeight: self.calculateViewHeight())
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func processTextMessage(_ textMessage: String) {
|
||||
userSuggestionService.processTextMessage(textMessage)
|
||||
completionSuggestionService.processTextMessage(textMessage)
|
||||
}
|
||||
|
||||
func processSuggestionPattern(_ suggestionPattern: SuggestionPattern?) {
|
||||
userSuggestionService.processSuggestionPattern(suggestionPattern)
|
||||
completionSuggestionService.processSuggestionPattern(suggestionPattern)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
@ -121,18 +121,18 @@ final class UserSuggestionCoordinator: Coordinator, Presentable {
|
|||
func start() { }
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
userSuggestionHostingController
|
||||
completionSuggestionHostingController
|
||||
}
|
||||
|
||||
func sharedContext() -> UserSuggestionViewModelContextWrapper {
|
||||
UserSuggestionViewModelContextWrapper(context: userSuggestionViewModel.sharedContext)
|
||||
func sharedContext() -> CompletionSuggestionViewModelContextWrapper {
|
||||
CompletionSuggestionViewModelContextWrapper(context: completionSuggestionViewModel.sharedContext)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func calculateViewHeight() -> CGFloat {
|
||||
let viewModel = UserSuggestionViewModel(userSuggestionService: userSuggestionService)
|
||||
let view = UserSuggestionList(viewModel: viewModel.context)
|
||||
let viewModel = CompletionSuggestionViewModel(completionSuggestionService: completionSuggestionService)
|
||||
let view = CompletionSuggestionList(viewModel: viewModel.context)
|
||||
.environmentObject(AvatarViewModel(avatarService: AvatarService(mediaManager: parameters.mediaManager)))
|
||||
|
||||
let controller = VectorHostingController(rootView: view)
|
||||
|
@ -156,7 +156,7 @@ final class UserSuggestionCoordinator: Coordinator, Presentable {
|
|||
}
|
||||
}
|
||||
|
||||
private class UserSuggestionCoordinatorRoomMemberProvider: RoomMembersProviderProtocol {
|
||||
private class CompletionSuggestionCoordinatorRoomMemberProvider: RoomMembersProviderProtocol {
|
||||
private let room: MXRoom
|
||||
private let userID: String
|
||||
|
||||
|
@ -194,7 +194,7 @@ private class UserSuggestionCoordinatorRoomMemberProvider: RoomMembersProviderPr
|
|||
self.roomMembers = joinedMembers
|
||||
members(self.roomMembersToProviderMembers(joinedMembers))
|
||||
} failure: { error in
|
||||
MXLog.error("[UserSuggestionCoordinatorRoomMemberProvider] Failed loading room", context: error)
|
||||
MXLog.error("[CompletionSuggestionCoordinatorRoomMemberProvider] Failed loading room", context: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ private class UserSuggestionCoordinatorRoomMemberProvider: RoomMembersProviderPr
|
|||
}
|
||||
}
|
||||
|
||||
private class UserSuggestionCoordinatorCommandProvider: CommandsProviderProtocol {
|
||||
private class CompletionSuggestionCoordinatorCommandProvider: CommandsProviderProtocol {
|
||||
private let room: MXRoom
|
||||
private let userID: String
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
protocol CompletionSuggestionCoordinatorBridgeDelegate: AnyObject {
|
||||
func completionSuggestionCoordinatorBridge(_ coordinator: CompletionSuggestionCoordinatorBridge, didRequestMentionForMember member: MXRoomMember, textTrigger: String?)
|
||||
func completionSuggestionCoordinatorBridgeDidRequestMentionForRoom(_ coordinator: CompletionSuggestionCoordinatorBridge, textTrigger: String?)
|
||||
func completionSuggestionCoordinatorBridge(_ coordinator: CompletionSuggestionCoordinatorBridge, didRequestCommand command: String, textTrigger: String?)
|
||||
func completionSuggestionCoordinatorBridge(_ coordinator: CompletionSuggestionCoordinatorBridge, didUpdateViewHeight height: CGFloat)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class CompletionSuggestionCoordinatorBridge: NSObject {
|
||||
private var _completionSuggestionCoordinator: Any?
|
||||
fileprivate var completionSuggestionCoordinator: CompletionSuggestionCoordinator {
|
||||
_completionSuggestionCoordinator as! CompletionSuggestionCoordinator
|
||||
}
|
||||
|
||||
weak var delegate: CompletionSuggestionCoordinatorBridgeDelegate?
|
||||
|
||||
init(mediaManager: MXMediaManager, room: MXRoom, userID: String) {
|
||||
let parameters = CompletionSuggestionCoordinatorParameters(mediaManager: mediaManager, room: room, userID: userID)
|
||||
let completionSuggestionCoordinator = CompletionSuggestionCoordinator(parameters: parameters)
|
||||
_completionSuggestionCoordinator = completionSuggestionCoordinator
|
||||
|
||||
super.init()
|
||||
|
||||
completionSuggestionCoordinator.delegate = self
|
||||
}
|
||||
|
||||
func processTextMessage(_ textMessage: String) {
|
||||
completionSuggestionCoordinator.processTextMessage(textMessage)
|
||||
}
|
||||
|
||||
func processSuggestionPattern(_ suggestionPatternWrapper: SuggestionPatternWrapper) {
|
||||
completionSuggestionCoordinator.processSuggestionPattern(suggestionPatternWrapper.suggestionPattern)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController? {
|
||||
completionSuggestionCoordinator.toPresentable()
|
||||
}
|
||||
|
||||
func sharedContext() -> CompletionSuggestionViewModelContextWrapper {
|
||||
completionSuggestionCoordinator.sharedContext()
|
||||
}
|
||||
}
|
||||
|
||||
extension CompletionSuggestionCoordinatorBridge: CompletionSuggestionCoordinatorDelegate {
|
||||
func completionSuggestionCoordinator(_ coordinator: CompletionSuggestionCoordinator, didRequestMentionForMember member: MXRoomMember, textTrigger: String?) {
|
||||
delegate?.completionSuggestionCoordinatorBridge(self, didRequestMentionForMember: member, textTrigger: textTrigger)
|
||||
}
|
||||
|
||||
func completionSuggestionCoordinatorDidRequestMentionForRoom(_ coordinator: CompletionSuggestionCoordinator, textTrigger: String?) {
|
||||
delegate?.completionSuggestionCoordinatorBridgeDidRequestMentionForRoom(self, textTrigger: textTrigger)
|
||||
}
|
||||
|
||||
func completionSuggestionCoordinator(_ coordinator: CompletionSuggestionCoordinator, didRequestCommand command: String, textTrigger: String?) {
|
||||
delegate?.completionSuggestionCoordinatorBridge(self, didRequestCommand: command, textTrigger: textTrigger)
|
||||
}
|
||||
|
||||
func completionSuggestionCoordinator(_ coordinator: CompletionSuggestionCoordinator, didUpdateViewHeight height: CGFloat) {
|
||||
delegate?.completionSuggestionCoordinatorBridge(self, didUpdateViewHeight: height)
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ struct CommandsProviderCommand {
|
|||
var name: String
|
||||
}
|
||||
|
||||
class UserSuggestionID: NSObject {
|
||||
class CompletionSuggestionUserID: NSObject {
|
||||
/// A special case added for suggesting `@room` mentions.
|
||||
@objc static let room = "@room"
|
||||
}
|
||||
|
@ -42,17 +42,17 @@ protocol CommandsProviderProtocol {
|
|||
func fetchCommands(_ commands: @escaping ([CommandsProviderCommand]) -> Void)
|
||||
}
|
||||
|
||||
struct UserSuggestionServiceItem: UserSuggestionItemProtocol {
|
||||
struct CompletionSuggestionServiceUserItem: CompletionSuggestionUserItemProtocol {
|
||||
let userId: String
|
||||
let displayName: String?
|
||||
let avatarUrl: String?
|
||||
}
|
||||
|
||||
struct CommandSuggestionServiceItem: CommandSuggestionItemProtocol {
|
||||
struct CompletionSuggestionServiceCommandItem: CompletionSuggestionCommandItemProtocol {
|
||||
let name: String
|
||||
}
|
||||
|
||||
class UserSuggestionService: UserSuggestionServiceProtocol {
|
||||
class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
@ -60,13 +60,13 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
|
|||
private let roomMemberProvider: RoomMembersProviderProtocol
|
||||
private let commandProvider: CommandsProviderProtocol
|
||||
|
||||
private var suggestionItems: [SuggestionItem] = []
|
||||
private var suggestionItems: [CompletionSuggestionItem] = []
|
||||
private let currentTextTriggerSubject = CurrentValueSubject<String?, Never>(nil)
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var items = CurrentValueSubject<[SuggestionItem], Never>([])
|
||||
var items = CurrentValueSubject<[CompletionSuggestionItem], Never>([])
|
||||
|
||||
var currentTextTrigger: String? {
|
||||
currentTextTriggerSubject.value
|
||||
|
@ -93,7 +93,7 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - UserSuggestionServiceProtocol
|
||||
// MARK: - CompletionSuggestionServiceProtocol
|
||||
|
||||
func processTextMessage(_ textMessage: String?) {
|
||||
guard let textMessage = textMessage,
|
||||
|
@ -145,14 +145,14 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
|
|||
}
|
||||
|
||||
self.suggestionItems = members.withRoom(self.roomMemberProvider.canMentionRoom).map { member in
|
||||
SuggestionItem.user(value: UserSuggestionServiceItem(userId: member.userId, displayName: member.displayName, avatarUrl: member.avatarUrl))
|
||||
CompletionSuggestionItem.user(value: CompletionSuggestionServiceUserItem(userId: member.userId, displayName: member.displayName, avatarUrl: member.avatarUrl))
|
||||
}
|
||||
|
||||
self.items.send(self.suggestionItems.filter { item in
|
||||
guard case let .user(userSuggestion) = item else { return false }
|
||||
guard case let .user(completionSuggestionUserItem) = item else { return false }
|
||||
|
||||
let containedInUsername = userSuggestion.userId.lowercased().contains(partialName.lowercased())
|
||||
let containedInDisplayName = (userSuggestion.displayName ?? "").lowercased().contains(partialName.lowercased())
|
||||
let containedInUsername = completionSuggestionUserItem.userId.lowercased().contains(partialName.lowercased())
|
||||
let containedInDisplayName = (completionSuggestionUserItem.displayName ?? "").lowercased().contains(partialName.lowercased())
|
||||
|
||||
return (containedInUsername || containedInDisplayName)
|
||||
})
|
||||
|
@ -165,7 +165,7 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
|
|||
guard let self else { return }
|
||||
|
||||
self.suggestionItems = commands.map { command in
|
||||
SuggestionItem.command(value: CommandSuggestionServiceItem(name: command.name))
|
||||
CompletionSuggestionItem.command(value: CompletionSuggestionServiceCommandItem(name: command.name))
|
||||
}
|
||||
|
||||
self.items.send(self.suggestionItems.filter { item in
|
||||
|
@ -184,6 +184,6 @@ extension Array where Element == RoomMembersProviderMember {
|
|||
/// Returns the array with an additional member that represents an `@room` mention.
|
||||
func withRoom(_ canMentionRoom: Bool) -> Self {
|
||||
guard canMentionRoom else { return self }
|
||||
return self + [RoomMembersProviderMember(userId: UserSuggestionID.room, displayName: "Everyone", avatarUrl: "")]
|
||||
return self + [RoomMembersProviderMember(userId: CompletionSuggestionUserID.room, displayName: "Everyone", avatarUrl: "")]
|
||||
}
|
||||
}
|
|
@ -18,23 +18,23 @@ import Combine
|
|||
import Foundation
|
||||
import WysiwygComposer
|
||||
|
||||
protocol UserSuggestionItemProtocol: Avatarable {
|
||||
protocol CompletionSuggestionUserItemProtocol: Avatarable {
|
||||
var userId: String { get }
|
||||
var displayName: String? { get }
|
||||
var avatarUrl: String? { get }
|
||||
}
|
||||
|
||||
protocol CommandSuggestionItemProtocol {
|
||||
protocol CompletionSuggestionCommandItemProtocol {
|
||||
var name: String { get }
|
||||
}
|
||||
|
||||
enum SuggestionItem {
|
||||
case command(value: CommandSuggestionItemProtocol)
|
||||
case user(value: UserSuggestionItemProtocol)
|
||||
enum CompletionSuggestionItem {
|
||||
case command(value: CompletionSuggestionCommandItemProtocol)
|
||||
case user(value: CompletionSuggestionUserItemProtocol)
|
||||
}
|
||||
|
||||
protocol UserSuggestionServiceProtocol {
|
||||
var items: CurrentValueSubject<[SuggestionItem], Never> { get }
|
||||
protocol CompletionSuggestionServiceProtocol {
|
||||
var items: CurrentValueSubject<[CompletionSuggestionItem], Never> { get }
|
||||
|
||||
var currentTextTrigger: String? { get }
|
||||
|
||||
|
@ -44,7 +44,7 @@ protocol UserSuggestionServiceProtocol {
|
|||
|
||||
// MARK: Avatarable
|
||||
|
||||
extension UserSuggestionItemProtocol {
|
||||
extension CompletionSuggestionUserItemProtocol {
|
||||
var mxContentUri: String? {
|
||||
avatarUrl
|
||||
}
|
|
@ -17,9 +17,9 @@
|
|||
import RiotSwiftUI
|
||||
import XCTest
|
||||
|
||||
class UserSuggestionUITests: MockScreenTestCase {
|
||||
func testUserSuggestionScreen() throws {
|
||||
app.goToScreenWithIdentifier(MockUserSuggestionScreenState.multipleResults.title)
|
||||
class CompletionSuggestionUITests: MockScreenTestCase {
|
||||
func testCompletionSuggestionScreen() throws {
|
||||
app.goToScreenWithIdentifier(MockCompletionSuggestionScreenState.multipleResults.title)
|
||||
|
||||
let firstButton = app.buttons["displayNameText-userIdText"].firstMatch
|
||||
XCTAssert(firstButton.waitForExistence(timeout: 10))
|
|
@ -19,51 +19,53 @@ import XCTest
|
|||
|
||||
@testable import RiotSwiftUI
|
||||
|
||||
class UserSuggestionServiceTests: XCTestCase {
|
||||
var service: UserSuggestionService!
|
||||
class CompletionSuggestionServiceTests: XCTestCase {
|
||||
var service: CompletionSuggestionService!
|
||||
var canMentionRoom = false
|
||||
|
||||
override func setUp() {
|
||||
service = UserSuggestionService(roomMemberProvider: self, shouldDebounce: false)
|
||||
service = CompletionSuggestionService(roomMemberProvider: self,
|
||||
commandProvider: self,
|
||||
shouldDebounce: false)
|
||||
canMentionRoom = false
|
||||
}
|
||||
|
||||
func testAlice() {
|
||||
service.processTextMessage("@Al")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
|
||||
service.processTextMessage("@al")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
|
||||
service.processTextMessage("@ice")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
|
||||
service.processTextMessage("@Alice")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
|
||||
service.processTextMessage("@alice:matrix.org")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
}
|
||||
|
||||
func testBob() {
|
||||
service.processTextMessage("@ob")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Bob")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Bob")
|
||||
|
||||
service.processTextMessage("@ob:")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Bob")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Bob")
|
||||
|
||||
service.processTextMessage("@b:matrix")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Bob")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Bob")
|
||||
}
|
||||
|
||||
func testBoth() {
|
||||
service.processTextMessage("@:matrix")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.last?.displayName, "Bob")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.last?.asUser?.displayName, "Bob")
|
||||
|
||||
service.processTextMessage("@.org")
|
||||
XCTAssertEqual(service.items.value.first?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.last?.displayName, "Bob")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
XCTAssertEqual(service.items.value.last?.asUser?.displayName, "Bob")
|
||||
}
|
||||
|
||||
func testEmptyResult() {
|
||||
|
@ -117,18 +119,18 @@ class UserSuggestionServiceTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testRoomWithPower() {
|
||||
// Given a user without the power to mention a room.
|
||||
// Given a user with the power to mention a room.
|
||||
canMentionRoom = true
|
||||
|
||||
// Given a user without the power to mention a room.
|
||||
// Given a user with the power to mention a room.
|
||||
service.processTextMessage("@ro")
|
||||
|
||||
// Then the completion for a room mention should be shown.
|
||||
XCTAssertEqual(service.items.value.first?.userId, UserSuggestionID.room)
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.userId, CompletionSuggestionUserID.room)
|
||||
}
|
||||
}
|
||||
|
||||
extension UserSuggestionServiceTests: RoomMembersProviderProtocol {
|
||||
extension CompletionSuggestionServiceTests: RoomMembersProviderProtocol {
|
||||
func fetchMembers(_ members: @escaping ([RoomMembersProviderMember]) -> Void) {
|
||||
let users = [("Alice", "@alice:matrix.org"),
|
||||
("Bob", "@bob:matrix.org")]
|
||||
|
@ -138,3 +140,23 @@ extension UserSuggestionServiceTests: RoomMembersProviderProtocol {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension CompletionSuggestionServiceTests: CommandsProviderProtocol {
|
||||
func fetchCommands(_ commands: @escaping ([CommandsProviderCommand]) -> Void) {
|
||||
let commandList = ["/ban", "/invite", "/join", "/me"]
|
||||
|
||||
commands(commandList.map { command in
|
||||
CommandsProviderCommand(name: command)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension CompletionSuggestionItem {
|
||||
var asUser: CompletionSuggestionUserItemProtocol? {
|
||||
if case let .user(value) = self { return value } else { return nil }
|
||||
}
|
||||
|
||||
var asCommand: CompletionSuggestionCommandItemProtocol? {
|
||||
if case let .command(value) = self { return value } else { return nil }
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
struct UserSuggestionList: View {
|
||||
struct CompletionSuggestionList: View {
|
||||
private enum Constants {
|
||||
static let topPadding: CGFloat = 8.0
|
||||
static let listItemPadding: CGFloat = 4.0
|
||||
|
@ -43,7 +43,7 @@ struct UserSuggestionList: View {
|
|||
|
||||
// MARK: Public
|
||||
|
||||
@ObservedObject var viewModel: UserSuggestionViewModel.Context
|
||||
@ObservedObject var viewModel: CompletionSuggestionViewModel.Context
|
||||
var showBackgroundShadow: Bool = true
|
||||
|
||||
var body: some View {
|
||||
|
@ -51,7 +51,7 @@ struct UserSuggestionList: View {
|
|||
EmptyView()
|
||||
} else {
|
||||
ZStack {
|
||||
UserSuggestionListItem(content: UserSuggestionViewStateItem.user(
|
||||
CompletionSuggestionListItem(content: CompletionSuggestionViewStateItem.user(
|
||||
id: "Prototype",
|
||||
avatar: AvatarInput(mxContentUri: "",
|
||||
matrixItemId: "",
|
||||
|
@ -79,7 +79,7 @@ struct UserSuggestionList: View {
|
|||
Button {
|
||||
viewModel.send(viewAction: .selectedItem(item))
|
||||
} label: {
|
||||
UserSuggestionListItem(content: item)
|
||||
CompletionSuggestionListItem(content: item)
|
||||
.modifier(ListItemPaddingModifier(isFirst: viewModel.viewState.items.first?.id == item.id))
|
||||
}
|
||||
}
|
||||
|
@ -134,8 +134,8 @@ private struct BackgroundView<Content: View>: View {
|
|||
|
||||
// MARK: - Previews
|
||||
|
||||
struct UserSuggestion_Previews: PreviewProvider {
|
||||
static let stateRenderer = MockUserSuggestionScreenState.stateRenderer
|
||||
struct CompletionSuggestion_Previews: PreviewProvider {
|
||||
static let stateRenderer = MockCompletionSuggestionScreenState.stateRenderer
|
||||
static var previews: some View {
|
||||
stateRenderer.screenGroup()
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
struct UserSuggestionListItem: View {
|
||||
struct CompletionSuggestionListItem: View {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
@ -25,7 +25,7 @@ struct UserSuggestionListItem: View {
|
|||
|
||||
// MARK: Public
|
||||
|
||||
let content: UserSuggestionViewStateItem
|
||||
let content: CompletionSuggestionViewStateItem
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
|
@ -59,9 +59,9 @@ struct UserSuggestionListItem: View {
|
|||
|
||||
// MARK: - Previews
|
||||
|
||||
struct UserSuggestionHeader_Previews: PreviewProvider {
|
||||
struct CompletionSuggestionHeader_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
UserSuggestionListItem(content: UserSuggestionViewStateItem.user(
|
||||
CompletionSuggestionListItem(content: CompletionSuggestionViewStateItem.user(
|
||||
id: "@alice:matrix.org",
|
||||
avatar: MockAvatarInput.example,
|
||||
displayName: "Alice"
|
|
@ -16,24 +16,24 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
struct UserSuggestionListWithInputViewModel {
|
||||
let listViewModel: UserSuggestionViewModel
|
||||
struct CompletionSuggestionListWithInputViewModel {
|
||||
let listViewModel: CompletionSuggestionViewModel
|
||||
let callback: (String) -> Void
|
||||
}
|
||||
|
||||
struct UserSuggestionListWithInput: View {
|
||||
struct CompletionSuggestionListWithInput: View {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var viewModel: UserSuggestionListWithInputViewModel
|
||||
var viewModel: CompletionSuggestionListWithInputViewModel
|
||||
@State private var inputText = ""
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0.0) {
|
||||
UserSuggestionList(viewModel: viewModel.listViewModel.context)
|
||||
CompletionSuggestionList(viewModel: viewModel.listViewModel.context)
|
||||
TextField("Search for user", text: $inputText)
|
||||
.background(Color.white)
|
||||
.onChange(of: inputText, perform: viewModel.callback)
|
||||
|
@ -48,8 +48,8 @@ struct UserSuggestionListWithInput: View {
|
|||
|
||||
// MARK: - Previews
|
||||
|
||||
struct UserSuggestionListWithInput_Previews: PreviewProvider {
|
||||
static let stateRenderer = MockUserSuggestionScreenState.stateRenderer
|
||||
struct CompletionSuggestionListWithInput_Previews: PreviewProvider {
|
||||
static let stateRenderer = MockCompletionSuggestionScreenState.stateRenderer
|
||||
static var previews: some View {
|
||||
stateRenderer.screenGroup()
|
||||
}
|
|
@ -29,7 +29,7 @@ enum MockComposerScreenState: MockScreenState, CaseIterable {
|
|||
|
||||
var screenView: ([Any], AnyView) {
|
||||
let viewModel: ComposerViewModel
|
||||
let userSuggestionViewModel = MockUserSuggestionViewModel(initialViewState: UserSuggestionViewState(items: []))
|
||||
let completionSuggestionViewModel = MockCompletionSuggestionViewModel(initialViewState: CompletionSuggestionViewState(items: []))
|
||||
let bindings = ComposerBindings(focused: false)
|
||||
|
||||
switch self {
|
||||
|
@ -67,7 +67,7 @@ enum MockComposerScreenState: MockScreenState, CaseIterable {
|
|||
Spacer()
|
||||
Composer(viewModel: viewModel.context,
|
||||
wysiwygViewModel: wysiwygviewModel,
|
||||
userSuggestionSharedContext: userSuggestionViewModel.context,
|
||||
completionSuggestionSharedContext: completionSuggestionViewModel.context,
|
||||
resizeAnimationDuration: 0.1,
|
||||
sendMessageAction: { _ in },
|
||||
showSendMediaActions: { })
|
||||
|
@ -82,6 +82,4 @@ enum MockComposerScreenState: MockScreenState, CaseIterable {
|
|||
}
|
||||
}
|
||||
|
||||
private final class MockUserSuggestionViewModel: UserSuggestionViewModelType {
|
||||
|
||||
}
|
||||
private final class MockCompletionSuggestionViewModel: CompletionSuggestionViewModelType { }
|
||||
|
|
|
@ -257,11 +257,11 @@ final class SuggestionPatternWrapper: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
final class UserSuggestionViewModelWrapper: NSObject {
|
||||
let userSuggestionViewModel: UserSuggestionViewModel
|
||||
final class CompletionSuggestionViewModelWrapper: NSObject {
|
||||
let completionSuggestionViewModel: CompletionSuggestionViewModel
|
||||
|
||||
init(_ userSuggestionViewModel: UserSuggestionViewModel) {
|
||||
self.userSuggestionViewModel = userSuggestionViewModel
|
||||
init(_ completionSuggestionViewModel: CompletionSuggestionViewModel) {
|
||||
self.completionSuggestionViewModel = completionSuggestionViewModel
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ struct Composer: View {
|
|||
// MARK: Private
|
||||
@ObservedObject private var viewModel: ComposerViewModelType.Context
|
||||
@ObservedObject private var wysiwygViewModel: WysiwygComposerViewModel
|
||||
private let userSuggestionSharedContext: UserSuggestionViewModelType.Context
|
||||
private let completionSuggestionSharedContext: CompletionSuggestionViewModelType.Context
|
||||
private let resizeAnimationDuration: Double
|
||||
|
||||
private let sendMessageAction: (WysiwygComposerContent) -> Void
|
||||
|
@ -223,13 +223,13 @@ struct Composer: View {
|
|||
init(
|
||||
viewModel: ComposerViewModelType.Context,
|
||||
wysiwygViewModel: WysiwygComposerViewModel,
|
||||
userSuggestionSharedContext: UserSuggestionViewModelType.Context,
|
||||
completionSuggestionSharedContext: CompletionSuggestionViewModelType.Context,
|
||||
resizeAnimationDuration: Double,
|
||||
sendMessageAction: @escaping (WysiwygComposerContent) -> Void,
|
||||
showSendMediaActions: @escaping () -> Void) {
|
||||
self.viewModel = viewModel
|
||||
self.wysiwygViewModel = wysiwygViewModel
|
||||
self.userSuggestionSharedContext = userSuggestionSharedContext
|
||||
self.completionSuggestionSharedContext = completionSuggestionSharedContext
|
||||
self.resizeAnimationDuration = resizeAnimationDuration
|
||||
self.sendMessageAction = sendMessageAction
|
||||
self.showSendMediaActions = showSendMediaActions
|
||||
|
@ -256,7 +256,7 @@ struct Composer: View {
|
|||
}
|
||||
}
|
||||
if wysiwygViewModel.maximised {
|
||||
UserSuggestionList(viewModel: userSuggestionSharedContext, showBackgroundShadow: false)
|
||||
CompletionSuggestionList(viewModel: completionSuggestionSharedContext, showBackgroundShadow: false)
|
||||
}
|
||||
}
|
||||
.frame(height: composerHeight)
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
protocol UserSuggestionCoordinatorBridgeDelegate: AnyObject {
|
||||
func userSuggestionCoordinatorBridge(_ coordinator: UserSuggestionCoordinatorBridge, didRequestMentionForMember member: MXRoomMember, textTrigger: String?)
|
||||
func userSuggestionCoordinatorBridgeDidRequestMentionForRoom(_ coordinator: UserSuggestionCoordinatorBridge, textTrigger: String?)
|
||||
func userSuggestionCoordinatorBridge(_ coordinator: UserSuggestionCoordinatorBridge, didRequestCommand command: String, textTrigger: String?)
|
||||
func userSuggestionCoordinatorBridge(_ coordinator: UserSuggestionCoordinatorBridge, didUpdateViewHeight height: CGFloat)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class UserSuggestionCoordinatorBridge: NSObject {
|
||||
private var _userSuggestionCoordinator: Any?
|
||||
fileprivate var userSuggestionCoordinator: UserSuggestionCoordinator {
|
||||
_userSuggestionCoordinator as! UserSuggestionCoordinator
|
||||
}
|
||||
|
||||
weak var delegate: UserSuggestionCoordinatorBridgeDelegate?
|
||||
|
||||
init(mediaManager: MXMediaManager, room: MXRoom, userID: String) {
|
||||
let parameters = UserSuggestionCoordinatorParameters(mediaManager: mediaManager, room: room, userID: userID)
|
||||
let userSuggestionCoordinator = UserSuggestionCoordinator(parameters: parameters)
|
||||
_userSuggestionCoordinator = userSuggestionCoordinator
|
||||
|
||||
super.init()
|
||||
|
||||
userSuggestionCoordinator.delegate = self
|
||||
}
|
||||
|
||||
func processTextMessage(_ textMessage: String) {
|
||||
userSuggestionCoordinator.processTextMessage(textMessage)
|
||||
}
|
||||
|
||||
func processSuggestionPattern(_ suggestionPatternWrapper: SuggestionPatternWrapper) {
|
||||
userSuggestionCoordinator.processSuggestionPattern(suggestionPatternWrapper.suggestionPattern)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController? {
|
||||
userSuggestionCoordinator.toPresentable()
|
||||
}
|
||||
|
||||
func sharedContext() -> UserSuggestionViewModelContextWrapper {
|
||||
userSuggestionCoordinator.sharedContext()
|
||||
}
|
||||
}
|
||||
|
||||
extension UserSuggestionCoordinatorBridge: UserSuggestionCoordinatorDelegate {
|
||||
func userSuggestionCoordinator(_ coordinator: UserSuggestionCoordinator, didRequestMentionForMember member: MXRoomMember, textTrigger: String?) {
|
||||
delegate?.userSuggestionCoordinatorBridge(self, didRequestMentionForMember: member, textTrigger: textTrigger)
|
||||
}
|
||||
|
||||
func userSuggestionCoordinatorDidRequestMentionForRoom(_ coordinator: UserSuggestionCoordinator, textTrigger: String?) {
|
||||
delegate?.userSuggestionCoordinatorBridgeDidRequestMentionForRoom(self, textTrigger: textTrigger)
|
||||
}
|
||||
|
||||
func userSuggestionCoordinator(_ coordinator: UserSuggestionCoordinator, didRequestCommand command: String, textTrigger: String?) {
|
||||
delegate?.userSuggestionCoordinatorBridge(self, didRequestCommand: command, textTrigger: textTrigger)
|
||||
}
|
||||
|
||||
func userSuggestionCoordinator(_ coordinator: UserSuggestionCoordinator, didUpdateViewHeight height: CGFloat) {
|
||||
delegate?.userSuggestionCoordinatorBridge(self, didUpdateViewHeight: height)
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias UserSuggestionViewModelType = StateStoreViewModel<UserSuggestionViewState, UserSuggestionViewAction>
|
||||
|
||||
class UserSuggestionViewModel: UserSuggestionViewModelType, UserSuggestionViewModelProtocol {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let userSuggestionService: UserSuggestionServiceProtocol
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var sharedContext: UserSuggestionViewModelType.Context {
|
||||
return self.context
|
||||
}
|
||||
|
||||
var completion: ((UserSuggestionViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(userSuggestionService: UserSuggestionServiceProtocol) {
|
||||
self.userSuggestionService = userSuggestionService
|
||||
|
||||
let items = userSuggestionService.items.value.map { suggestionItem in
|
||||
switch suggestionItem {
|
||||
case .command(let commandSuggestionItem):
|
||||
return UserSuggestionViewStateItem.command(name: commandSuggestionItem.name)
|
||||
case .user(let userSuggestionItem):
|
||||
return UserSuggestionViewStateItem.user(id: userSuggestionItem.userId,
|
||||
avatar: userSuggestionItem,
|
||||
displayName: userSuggestionItem.displayName)
|
||||
}
|
||||
}
|
||||
|
||||
super.init(initialViewState: UserSuggestionViewState(items: items))
|
||||
|
||||
userSuggestionService.items.sink { [weak self] items in
|
||||
self?.state.items = items.map { item in
|
||||
switch item {
|
||||
case .command(let commandSuggestionItem):
|
||||
return UserSuggestionViewStateItem.command(name: commandSuggestionItem.name)
|
||||
case .user(let userSuggestionItem):
|
||||
return UserSuggestionViewStateItem.user(id: userSuggestionItem.userId,
|
||||
avatar: userSuggestionItem,
|
||||
displayName: userSuggestionItem.displayName)
|
||||
}
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: UserSuggestionViewAction) {
|
||||
switch viewAction {
|
||||
case .selectedItem(let item):
|
||||
completion?(.selectedItemWithIdentifier(item.id))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue