Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Direct Message: manage encrypted DM in case of invite by email #7396

Merged
merged 6 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"room_creation_invite_another_user" = "User ID, name or email";
"room_creation_error_invite_user_by_email_without_identity_server" = "No identity server is configured so you cannot add a participant with an email.";
"room_creation_dm_error" = "We couldn't create your DM. Please check the users you want to invite and try again.";
"room_creation_only_one_email_invite" = "You can only invite one email at a time";

// Room recents
"room_recents_directory_section" = "ROOM DIRECTORY";
Expand Down Expand Up @@ -2295,6 +2296,9 @@ Tap the + to start adding people.";
"room_invites_empty_view_title" = "Nothing new.";
"room_invites_empty_view_information" = "This is where your invites appear.";

"room_waiting_other_participants_title" = "Waiting for users to join %@";
"room_waiting_other_participants_message" = "Once invited users have joined %@, you will be able to chat and the room will be end-to-end encrypted";

// MARK: - Space Selector

"space_selector_title" = "My spaces";
Expand Down
12 changes: 12 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5267,6 +5267,10 @@ public class VectorL10n: NSObject {
public static var roomCreationNameTitle: String {
return VectorL10n.tr("Vector", "room_creation_name_title")
}
/// You can only invite one email at a time
public static var roomCreationOnlyOneEmailInvite: String {
return VectorL10n.tr("Vector", "room_creation_only_one_email_invite")
}
/// (e.g. @bob:homeserver1; @john:homeserver2...)
public static var roomCreationParticipantsPlaceholder: String {
return VectorL10n.tr("Vector", "room_creation_participants_placeholder")
Expand Down Expand Up @@ -6623,6 +6627,14 @@ public class VectorL10n: NSObject {
public static var roomUnsentMessagesUnknownDevicesNotification: String {
return VectorL10n.tr("Vector", "room_unsent_messages_unknown_devices_notification")
}
/// Once invited users have joined %@, you will be able to chat and the room will be end-to-end encrypted
public static func roomWaitingOtherParticipantsMessage(_ p1: String) -> String {
return VectorL10n.tr("Vector", "room_waiting_other_participants_message", p1)
}
/// Waiting for users to join %@
public static func roomWaitingOtherParticipantsTitle(_ p1: String) -> String {
return VectorL10n.tr("Vector", "room_waiting_other_participants_title", p1)
}
/// End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption.
public static var roomWarningAboutEncryption: String {
return VectorL10n.tr("Vector", "room_warning_about_encryption")
Expand Down
4 changes: 4 additions & 0 deletions Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
private let parentSpaceId: String?
private let initialSection: RoomInfoSection
private let dismissOnCancel: Bool
private let canAddParticipants: Bool
private weak var roomSettingsViewController: RoomSettingsViewController?

private lazy var segmentedViewController: SegmentedViewController = {
Expand All @@ -43,6 +44,8 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
participants.parentSpaceId = self.parentSpaceId
participants.delegate = self
participants.screenTracker = AnalyticsScreenTracker(screen: .roomMembers)
participants.showInviteUserFab = self.canAddParticipants


let files = RoomFilesViewController()
files.finalizeInit()
Expand Down Expand Up @@ -105,6 +108,7 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
self.room = parameters.room
self.parentSpaceId = parameters.parentSpaceId
self.initialSection = parameters.initialSection
self.canAddParticipants = parameters.canAddParticipants
self.dismissOnCancel = parameters.dismissOnCancel
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ class RoomInfoCoordinatorParameters: NSObject {
let parentSpaceId: String?
let initialSection: RoomInfoSection
let dismissOnCancel: Bool
let canAddParticipants: Bool

init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection, dismissOnCancel: Bool) {
init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection, canAddParticipants: Bool = true, dismissOnCancel: Bool) {
self.session = session
self.room = room
self.parentSpaceId = parentSpaceId
self.initialSection = initialSection
self.canAddParticipants = canAddParticipants
self.dismissOnCancel = dismissOnCancel
super.init()
}
Expand All @@ -50,4 +52,8 @@ class RoomInfoCoordinatorParameters: NSObject {
convenience init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection) {
self.init(session: session, room: room, parentSpaceId: parentSpaceId, initialSection: initialSection, dismissOnCancel: false)
}

convenience init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection, canAddParticipants: Bool) {
self.init(session: session, room: room, parentSpaceId: parentSpaceId, initialSection: initialSection, canAddParticipants: canAddParticipants, dismissOnCancel: false)
}
}
2 changes: 2 additions & 0 deletions Riot/Modules/Room/RoomViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ extern NSTimeInterval const kResizeComposerAnimationDuration;

@property (nonatomic, strong, nullable) ComposerLinkActionBridgePresenter *composerLinkActionBridgePresenter;

@property (weak, nonatomic, nullable) UIViewController *waitingOtherParticipantViewController;
@property (nonatomic) BOOL isWaitingForOtherParticipants;

/**
Retrieve the live data source in cases where the timeline is not live.
Expand Down
77 changes: 70 additions & 7 deletions Riot/Modules/Room/RoomViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ @interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelega

// Time to display notification content in the timeline
MXTaskProfile *notificationTaskProfile;

// Observe kMXEventTypeStringRoomMember events
__weak id roomMemberEventListener;
}

@property (nonatomic, strong) RemoveJitsiWidgetView *removeJitsiWidgetView;
Expand Down Expand Up @@ -233,6 +236,9 @@ @interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelega
// scroll state just before the layout change, and restore it after the layout.
@property (nonatomic) BOOL wasScrollAtBottomBeforeLayout;

// Check if we should wait for other participants
@property (nonatomic, readonly) BOOL shouldWaitForOtherParticipants;

@end

@implementation RoomViewController
Expand Down Expand Up @@ -329,6 +335,7 @@ - (void)finalizeInit

_showMissedDiscussionsBadge = YES;
_scrollToBottomHidden = YES;
_isWaitingForOtherParticipants = NO;

// Listen to the event sent state changes
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeSentState:) name:kMXEventDidChangeSentStateNotification object:nil];
Expand Down Expand Up @@ -372,7 +379,10 @@ - (void)viewDidLoad

// Prepare missed dicussion badge (if any)
self.showMissedDiscussionsBadge = _showMissedDiscussionsBadge;


// Refresh the waiting for other participants state
[self refreshWaitForOtherParticipantsState];

// Set up the room title view according to the data source (if any)
[self refreshRoomTitle];

Expand Down Expand Up @@ -1194,9 +1204,9 @@ - (void)updateRoomInputToolbarViewClassIfNeeded

BOOL canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomMessage]);
BOOL isRoomObsolete = self.roomDataSource.roomState.isObsolete;
BOOL isResourceLimitExceeded = [self.roomDataSource.mxSession.syncError.errcode isEqualToString:kMXErrCodeStringResourceLimitExceeded];
BOOL isResourceLimitExceeded = [self.roomDataSource.mxSession.syncError.errcode isEqualToString:kMXErrCodeStringResourceLimitExceeded];

if (isRoomObsolete || isResourceLimitExceeded)
if (isRoomObsolete || isResourceLimitExceeded || _isWaitingForOtherParticipants)
{
roomInputToolbarViewClass = nil;
shouldDismissContextualMenu = YES;
Expand Down Expand Up @@ -1532,6 +1542,8 @@ - (void)destroy
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeSentStateNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeIdentifierNotification object:nil];

[self waitForOtherParticipant:NO];

[super destroy];
}

Expand Down Expand Up @@ -1638,6 +1650,57 @@ - (BOOL)shouldShowLiveLocationSharingBannerView
return self.customizedRoomDataSource.isCurrentUserSharingActiveLocation;
}

#pragma mark - Wait for 3rd party invitee

- (void)setIsWaitingForOtherParticipants:(BOOL)isWaitingForOtherParticipants
{
if (_isWaitingForOtherParticipants == isWaitingForOtherParticipants)
{
return;
}

_isWaitingForOtherParticipants = isWaitingForOtherParticipants;
[self updateRoomInputToolbarViewClassIfNeeded];

if (_isWaitingForOtherParticipants)
{
if (self->roomMemberEventListener == nil)
{
MXWeakify(self);
self->roomMemberEventListener = [self.roomDataSource.room listenToEventsOfTypes:@[kMXEventTypeStringRoomMember] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
MXStrongifyAndReturnIfNil(self);
if (direction != MXTimelineDirectionForwards)
{
return;
}
[self refreshWaitForOtherParticipantsState];
}];
}
}
else
{
if (self->roomMemberEventListener != nil)
{
[self.roomDataSource.room removeListener:self->roomMemberEventListener];
self->roomMemberEventListener = nil;
}
}
}

- (BOOL)shouldWaitForOtherParticipants
{
MXRoomState *roomState = self.roomDataSource.roomState;
BOOL isDirect = self.roomDataSource.room.isDirect;

// Wait for the other participant only if it is a direct encrypted room with only one member waiting for a third party guest.
return (isDirect && roomState.isEncrypted && roomState.membersCount.members == 1 && roomState.thirdPartyInvites.count > 0);
}

- (void)refreshWaitForOtherParticipantsState
{
[self waitForOtherParticipant:self.shouldWaitForOtherParticipants];
}

#pragma mark - Internals

- (UIBarButtonItem *)videoCallBarButtonItem
Expand Down Expand Up @@ -1948,7 +2011,7 @@ - (void)refreshRoomTitle

[self refreshMissedDiscussionsCount:YES];

if (RiotSettings.shared.enableThreads)
if (RiotSettings.shared.enableThreads && !_isWaitingForOtherParticipants)
{
if (self.roomDataSource.threadId)
{
Expand Down Expand Up @@ -2260,8 +2323,8 @@ - (void)showRoomInfo

- (void)showRoomInfoWithInitialSection:(RoomInfoSection)roomInfoSection animated:(BOOL)animated
{
RoomInfoCoordinatorParameters *parameters = [[RoomInfoCoordinatorParameters alloc] initWithSession:self.roomDataSource.mxSession room:self.roomDataSource.room parentSpaceId:self.parentSpaceId initialSection:roomInfoSection];
RoomInfoCoordinatorParameters *parameters = [[RoomInfoCoordinatorParameters alloc] initWithSession:self.roomDataSource.mxSession room:self.roomDataSource.room parentSpaceId:self.parentSpaceId initialSection:roomInfoSection canAddParticipants: !self.isWaitingForOtherParticipants];

self.roomInfoCoordinatorBridgePresenter = [[RoomInfoCoordinatorBridgePresenter alloc] initWithParameters:parameters];

self.roomInfoCoordinatorBridgePresenter.delegate = self;
Expand Down Expand Up @@ -7450,7 +7513,7 @@ - (void)updateThreadListBarButtonBadgeWith:(MXThreadingService *)service

- (void)updateThreadListBarButtonItem:(UIBarButtonItem *)barButtonItem with:(MXThreadingService *)service
{
if (!service)
if (!service || _isWaitingForOtherParticipants)
{
return;
}
Expand Down
44 changes: 44 additions & 0 deletions Riot/Modules/Room/RoomViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,50 @@ extension RoomViewController {
composerLinkActionBridgePresenter = presenter
presenter.present(from: self, animated: true)
}

@objc func showWaitingOtherParticipantHeader() {
let controller = VectorHostingController(rootView: RoomWaitingForMembers())
guard let headerView = controller.view else {
return
}
self.waitingOtherParticipantViewController = controller
self.addChild(controller)

let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
headerView.translatesAutoresizingMaskIntoConstraints = false
containerView.vc_addSubViewMatchingParent(headerView, withInsets: UIEdgeInsets(top: 9, left: 9, bottom: -9, right: -9))

self.bubblesTableView.tableHeaderView = containerView
NSLayoutConstraint.activate([
containerView.centerXAnchor.constraint(equalTo: self.bubblesTableView.centerXAnchor),
containerView.widthAnchor.constraint(equalTo: self.bubblesTableView.widthAnchor),
containerView.topAnchor.constraint(equalTo: self.bubblesTableView.topAnchor)
])
controller.didMove(toParent: self)

self.bubblesTableView.tableHeaderView?.layoutIfNeeded()
}

@objc func hideWaitingOtherParticipantHeader() {
guard let waitingOtherParticipantViewController else {
return
}
waitingOtherParticipantViewController.removeFromParent()
self.bubblesTableView.tableHeaderView = nil
waitingOtherParticipantViewController.didMove(toParent: nil)
self.waitingOtherParticipantViewController = nil
}

@objc func waitForOtherParticipant(_ wait: Bool) {
self.isWaitingForOtherParticipants = wait
if wait {
showWaitingOtherParticipantHeader()
} else {
hideWaitingOtherParticipantHeader()
}
}

}

// MARK: - Private Helpers
Expand Down
Loading