diff --git a/Riot/Modules/Room/EventMenu/EventMenuBuilder.swift b/Riot/Modules/Room/EventMenu/EventMenuBuilder.swift new file mode 100644 index 000000000..a8d3caf66 --- /dev/null +++ b/Riot/Modules/Room/EventMenu/EventMenuBuilder.swift @@ -0,0 +1,50 @@ +// +// 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 + +/// Class to build an event menu. +@objcMembers +class EventMenuBuilder: NSObject { + + private var items: [EventMenuItemType: UIAlertAction] = [:] + + /// Returns true if no items or only one item with the type `EventMenuItemType.cancel`. + var isEmpty: Bool { + return items.isEmpty || (items.count == 1 && items.first?.key == .cancel) + } + + /// Add a menu item. + /// - Parameters: + /// - type: item type + /// - action: alert action + func addItem(withType type: EventMenuItemType, + action: UIAlertAction) { + items[type] = action + } + + /// Builds the action menu items. + /// - Returns: alert actions. Sorted by item types. + func build() -> [UIAlertAction] { + items.sorted(by: { $0.key < $1.key }).map { $1 } + } + + /// Reset the builder. Builder will be empty after this method call. + func reset() { + items.removeAll() + } + +} diff --git a/Riot/Modules/Room/EventMenu/EventMenuItemType.swift b/Riot/Modules/Room/EventMenu/EventMenuItemType.swift new file mode 100644 index 000000000..c15d2fbdb --- /dev/null +++ b/Riot/Modules/Room/EventMenu/EventMenuItemType.swift @@ -0,0 +1,49 @@ +// +// 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 + +/// Type of an event menu item. Ordering of the cases is important. See `EventMenuBuilder`. +@objc +enum EventMenuItemType: Int { + case viewInRoom + case copy + case retrySending + case cancelSending + case cancelDownloading + case saveMedia + case quote + case forward + case permalink + case share + case removePoll + case endPoll + case reactionHistory + case viewSource + case viewDecryptedSource + case viewEncryption + case report + case remove + case cancel +} + +extension EventMenuItemType: Comparable { + + static func < (lhs: EventMenuItemType, rhs: EventMenuItemType) -> Bool { + return lhs.rawValue < rhs.rawValue + } + +} diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 48c15e6d1..750fe737e 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -254,6 +254,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; @property (nonatomic, strong) SpaceDetailPresenter *spaceDetailPresenter; @property (nonatomic, strong) ShareManager *shareManager; +@property (nonatomic, strong) EventMenuBuilder *eventMenuBuilder; @property (nonatomic, strong) UserSuggestionCoordinatorBridge *userSuggestionCoordinator; @property (nonatomic, weak) IBOutlet UIView *userSuggestionContainerView; @@ -345,6 +346,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; self.enableBarTintColorStatusChange = NO; self.rageShakeManager = [RageShakeManager sharedManager]; formattedBodyParser = [FormattedBodyParser new]; + self.eventMenuBuilder = [EventMenuBuilder new]; _showMissedDiscussionsBadge = YES; _scrollToBottomHidden = YES; @@ -3249,17 +3251,19 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; currentAlert = nil; } + [self.eventMenuBuilder reset]; + MXWeakify(self); - UIAlertController *actionsMenu = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; BOOL showThreadOption = RiotSettings.shared.enableThreads - && !self.roomDataSource.threadId - && !selectedEvent.threadId; + && !self.roomDataSource.threadId + && !selectedEvent.threadId; if (showThreadOption && [self canCopyEvent:selectedEvent andCell:cell]) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCopy] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeCopy + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCopy] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3271,9 +3275,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; // Add actions for a failed event if (selectedEvent.sentState == MXEventSentStateFailed) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n retry] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeRetrySending + action:[UIAlertAction actionWithTitle:[VectorL10n retry] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3282,9 +3287,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; [self.roomDataSource resendEventWithEventId:selectedEvent.eventId success:nil failure:nil]; }]]; - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionDelete] - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeRemove + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionDelete] + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3298,9 +3304,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; { // if in the thread and selected event is the root event // add "View in room" action - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewInRoom] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewInRoom + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewInRoom] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self.delegate roomViewController:self showRoomWithId:self.roomDataSource.roomId @@ -3329,9 +3336,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; selectedEvent.sentState == MXEventSentStateEncrypting || selectedEvent.sentState == MXEventSentStateSending) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancelSending + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); self->currentAlert = nil; @@ -3346,9 +3354,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (!isJitsiCallEvent && selectedEvent.eventType != MXEventTypePollStart) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionQuote] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeQuote + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionQuote] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3363,9 +3372,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (selectedEvent.sentState == MXEventSentStateSent && selectedEvent.eventType != MXEventTypePollStart) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeForward + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self presentEventForwardingDialogForSelectedEvent:selectedEvent]; }]]; @@ -3373,9 +3383,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (!isJitsiCallEvent && BuildSettings.messageDetailsAllowShare && selectedEvent.eventType != MXEventTypePollStart) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeShare + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3402,9 +3413,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo || attachment.type == MXKAttachmentTypeVoiceMessage)) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeForward + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self presentEventForwardingDialogForSelectedEvent:selectedEvent]; }]]; @@ -3414,9 +3426,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; { if (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionSave] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeSaveMedia + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionSave] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3451,9 +3464,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; NSString *uploadId = roomBubbleTableViewCell.bubbleData.attachment.contentURL; if ([MXMediaManager existingUploaderWithId:uploadId]) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancelSending + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); @@ -3485,9 +3499,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; { if (BuildSettings.messageDetailsAllowShare) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeShare + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3532,9 +3547,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; NSString *downloadId = roomBubbleTableViewCell.bubbleData.attachment.downloadId; if ([MXMediaManager existingDownloaderWithIdentifier:downloadId]) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelDownload] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancelDownloading + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelDownload] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3553,9 +3569,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (BuildSettings.messageDetailsAllowPermalink) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionPermalink] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypePermalink + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionPermalink] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3581,9 +3598,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (BuildSettings.messageDetailsAllowViewSource) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewSource] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewSource + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewSource] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3596,9 +3614,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; // Add "View Decrypted Source" for e2ee event we can decrypt if (selectedEvent.isEncrypted && selectedEvent.clearEvent) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewDecryptedSource] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewDecryptedSource + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewDecryptedSource] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3608,26 +3627,31 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; }]]; } } - + // Do not allow to redact the event that enabled encryption (m.room.encryption) // because it breaks everything if (selectedEvent.eventType != MXEventTypeRoomEncryption) { NSString *title; - UIAlertActionStyle style = UIAlertActionStyleDefault; + UIAlertActionStyle style; + EventMenuItemType itemType; if (selectedEvent.eventType == MXEventTypePollStart) { title = [VectorL10n roomEventActionRemovePoll]; + style = UIAlertActionStyleDefault; + itemType = EventMenuItemTypeRemovePoll; } else { title = [VectorL10n roomEventActionRedact]; style = UIAlertActionStyleDestructive; + itemType = EventMenuItemTypeRemove; } - [actionsMenu addAction:[UIAlertAction actionWithTitle:title - style:style - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:itemType + action:[UIAlertAction actionWithTitle:title + style:style + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3649,11 +3673,14 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; }]]; } - if (selectedEvent.eventType == MXEventTypePollStart && [selectedEvent.sender isEqualToString:self.mainSession.myUserId]) { - if ([self.delegate roomViewController:self canEndPollWithEventIdentifier:selectedEvent.eventId]) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionEndPoll] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + if (selectedEvent.eventType == MXEventTypePollStart && [selectedEvent.sender isEqualToString:self.mainSession.myUserId]) + { + if ([self.delegate roomViewController:self canEndPollWithEventIdentifier:selectedEvent.eventId]) + { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeEndPoll + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionEndPoll] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self.delegate roomViewController:self endPollWithEventIdentifier:selectedEvent.eventId]; @@ -3666,9 +3693,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; // Add reaction history if event contains reactions if (roomBubbleTableViewCell.bubbleData.reactions[selectedEvent.eventId].aggregatedReactionsWithNonZeroCount) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReactionHistory] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeReactionHistory + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReactionHistory] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3680,9 +3708,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (![selectedEvent.sender isEqualToString:self.mainSession.myUserId] && RiotSettings.shared.roomContextualMenuShowReportContentOption) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReport] - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeReport + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReport] + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3772,9 +3801,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (!isJitsiCallEvent && self.roomDataSource.room.summary.isEncrypted) { - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewEncryption] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewEncryption + action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewEncryption] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self cancelEventSelection]; @@ -3784,9 +3814,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; }]]; } - [actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction * action) { + [self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancel + action:[UIAlertAction actionWithTitle:[VectorL10n cancel] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { MXStrongifyAndReturnIfNil(self); [self hideContextualMenuAnimated:YES]; @@ -3795,8 +3826,17 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } // Do not display empty action sheet - if (actionsMenu.actions.count > 1) + if (!self.eventMenuBuilder.isEmpty) { + UIAlertController *actionsMenu = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + // build actions and add them to the alert + NSArray *actions = [self.eventMenuBuilder build]; + for (UIAlertAction *action in actions) + { + [actionsMenu addAction:action]; + } + NSInteger bubbleComponentIndex = [roomBubbleTableViewCell.bubbleData bubbleComponentIndexForEventId:selectedEvent.eventId]; CGRect sourceRect = [roomBubbleTableViewCell componentFrameInContentViewForIndex:bubbleComponentIndex]; diff --git a/RiotTests/EventMenuBuilderTests.swift b/RiotTests/EventMenuBuilderTests.swift new file mode 100644 index 000000000..783f6ef87 --- /dev/null +++ b/RiotTests/EventMenuBuilderTests.swift @@ -0,0 +1,59 @@ +// +// 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 XCTest +import UIKit +@testable import Riot + +class EventMenuBuilderTests: XCTestCase { + + func testSorting() { + let builder = EventMenuBuilder() + + let title2 = "Title 2" + + builder.addItem(withType: .copy, + action: UIAlertAction(title: "Title 1", style: .default, handler: nil)) + + builder.addItem(withType: .viewInRoom, + action: UIAlertAction(title: title2, style: .default, handler: nil)) + + let actions = builder.build() + + XCTAssertEqual(actions.first?.title, title2, "Item with title 'title2' must come first in the result") + } + + func testEmptiness() { + let builder = EventMenuBuilder() + + XCTAssertTrue(builder.isEmpty, "Builder must be empty after initialization") + + builder.addItem(withType: .cancel, + action: UIAlertAction(title: "Cancel", style: .default, handler: nil)) + + XCTAssertTrue(builder.isEmpty, "Builder must still be empty after adding only a cancel action") + + builder.addItem(withType: .share, + action: UIAlertAction(title: "some_title", style: .default, handler: nil)) + + XCTAssertFalse(builder.isEmpty, "Builder must not be empty after adding an item") + + builder.reset() + + XCTAssertTrue(builder.isEmpty, "Builder must be empty again after reset") + } + +} diff --git a/RiotTests/target.yml b/RiotTests/target.yml index b07bd2911..a1b70659f 100644 --- a/RiotTests/target.yml +++ b/RiotTests/target.yml @@ -64,3 +64,5 @@ targets: - path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift - path: ../Riot/Managers/KeyValueStorage/ - path: ../Riot/PropertyWrappers/UserDefaultsBackedPropertyWrapper.swift + - path: ../Riot/Modules/Room/EventMenu/EventMenuBuilder.swift + - path: ../Riot/Modules/Room/EventMenu/EventMenuItemType.swift