mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-11 13:59:13 +00:00
Invite again user on direct chats (#1087)
* Add leaveRoom section for DMs * Add invite alert in RoomScreenViewModel * Show alert on composer focus * Add localisations * Refine invite alert logics * Amend tests * Update project * Fix local variable name * Refactor show invite alert logic
This commit is contained in:
parent
d2d626f724
commit
87b0d95e67
@ -22,6 +22,7 @@
|
||||
"action_edit" = "Edit";
|
||||
"action_enable" = "Enable";
|
||||
"action_forgot_password" = "Forgot password?";
|
||||
"action_forward" = "Forward";
|
||||
"action_invite" = "Invite";
|
||||
"action_invite_friends" = "Invite friends";
|
||||
"action_invite_friends_to_app" = "Invite friends to %1$@";
|
||||
@ -52,6 +53,7 @@
|
||||
"action_start" = "Start";
|
||||
"action_start_chat" = "Start chat";
|
||||
"action_start_verification" = "Start verification";
|
||||
"action_static_map_load" = "Tap to load map";
|
||||
"action_take_photo" = "Take photo";
|
||||
"action_view_source" = "View Source";
|
||||
"action_yes" = "Yes";
|
||||
@ -71,6 +73,7 @@
|
||||
"common_encryption_enabled" = "Encryption enabled";
|
||||
"common_error" = "Error";
|
||||
"common_file" = "File";
|
||||
"common_forward_message" = "Forward message";
|
||||
"common_gif" = "GIF";
|
||||
"common_image" = "Image";
|
||||
"common_invite_unknown_profile" = "We can’t validate this user’s Matrix ID. The invite might not be received.";
|
||||
@ -81,6 +84,7 @@
|
||||
"common_message_layout" = "Message layout";
|
||||
"common_message_removed" = "Message removed";
|
||||
"common_modern" = "Modern";
|
||||
"common_mute" = "Mute";
|
||||
"common_no_results" = "No results";
|
||||
"common_offline" = "Offline";
|
||||
"common_password" = "Password";
|
||||
@ -111,6 +115,7 @@
|
||||
"common_unable_to_decrypt" = "Unable to decrypt";
|
||||
"common_unable_to_invite_message" = "We were unable to successfully send invites to one or more users.";
|
||||
"common_unable_to_invite_title" = "Unable to send invite(s)";
|
||||
"common_unmute" = "Unmute";
|
||||
"common_unsupported_event" = "Unsupported event";
|
||||
"common_username" = "Username";
|
||||
"common_verification_cancelled" = "Verification cancelled";
|
||||
@ -146,9 +151,10 @@
|
||||
"notification_inline_reply_failed" = "** Failed to send - please open room";
|
||||
"notification_invitation_action_join" = "Join";
|
||||
"notification_invitation_action_reject" = "Reject";
|
||||
"notification_invite_body" = "invited you";
|
||||
"notification_invite_body" = "Invited you to chat";
|
||||
"notification_new_messages" = "New Messages";
|
||||
"notification_room_action_mark_as_read" = "Mark as read";
|
||||
"notification_room_invite_body" = "Invited you to join the room";
|
||||
"notification_sender_me" = "Me";
|
||||
"notification_test_push_notification_content" = "You are viewing the notification! Click me!";
|
||||
"notification_ticker_text_dm" = "%1$@: %2$@";
|
||||
@ -214,7 +220,6 @@
|
||||
"screen_change_server_form_notice" = "You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. %1$@";
|
||||
"screen_change_server_subtitle" = "What is the address of your server?";
|
||||
"screen_create_room_action_create_room" = "New room";
|
||||
"screen_create_room_action_invite_people" = "Invite friends to Element";
|
||||
"screen_create_room_add_people_title" = "Invite people";
|
||||
"screen_create_room_error_creating_room" = "An error occurred when creating the room";
|
||||
"screen_create_room_private_option_description" = "Messages in this room are encrypted. Encryption can’t be disabled afterwards.";
|
||||
@ -265,6 +270,8 @@
|
||||
"screen_room_details_share_room_title" = "Share room";
|
||||
"screen_room_details_updating_room" = "Updating room…";
|
||||
"screen_room_error_failed_retrieving_user_details" = "Could not retrieve user details";
|
||||
"screen_room_invite_again_alert_message" = "Would you like to invite them back?";
|
||||
"screen_room_invite_again_alert_title" = "You are alone in this chat";
|
||||
"screen_room_member_details_block_alert_action" = "Block";
|
||||
"screen_room_member_details_block_alert_description" = "Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime.";
|
||||
"screen_room_member_details_block_user" = "Block user";
|
||||
|
@ -290,7 +290,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
|
||||
private func dismissRoom(animated: Bool) {
|
||||
navigationStackCoordinator.popToRoot(animated: animated)
|
||||
// The room isn't in the same navigation stack of the home screen.
|
||||
// Animating the popToRoot causes weird animations on when the room is left from room's details
|
||||
navigationStackCoordinator.popToRoot(animated: false)
|
||||
navigationSplitCoordinator.setDetailCoordinator(nil, animated: animated)
|
||||
roomProxy = nil
|
||||
timelineController = nil
|
||||
|
@ -56,6 +56,8 @@ public enum L10n {
|
||||
public static var actionEnable: String { return L10n.tr("Localizable", "action_enable") }
|
||||
/// Forgot password?
|
||||
public static var actionForgotPassword: String { return L10n.tr("Localizable", "action_forgot_password") }
|
||||
/// Forward
|
||||
public static var actionForward: String { return L10n.tr("Localizable", "action_forward") }
|
||||
/// Invite
|
||||
public static var actionInvite: String { return L10n.tr("Localizable", "action_invite") }
|
||||
/// Invite friends
|
||||
@ -118,6 +120,8 @@ public enum L10n {
|
||||
public static var actionStartChat: String { return L10n.tr("Localizable", "action_start_chat") }
|
||||
/// Start verification
|
||||
public static var actionStartVerification: String { return L10n.tr("Localizable", "action_start_verification") }
|
||||
/// Tap to load map
|
||||
public static var actionStaticMapLoad: String { return L10n.tr("Localizable", "action_static_map_load") }
|
||||
/// Take photo
|
||||
public static var actionTakePhoto: String { return L10n.tr("Localizable", "action_take_photo") }
|
||||
/// View Source
|
||||
@ -158,6 +162,8 @@ public enum L10n {
|
||||
public static var commonError: String { return L10n.tr("Localizable", "common_error") }
|
||||
/// File
|
||||
public static var commonFile: String { return L10n.tr("Localizable", "common_file") }
|
||||
/// Forward message
|
||||
public static var commonForwardMessage: String { return L10n.tr("Localizable", "common_forward_message") }
|
||||
/// GIF
|
||||
public static var commonGif: String { return L10n.tr("Localizable", "common_gif") }
|
||||
/// Image
|
||||
@ -182,6 +188,8 @@ public enum L10n {
|
||||
public static var commonMessageRemoved: String { return L10n.tr("Localizable", "common_message_removed") }
|
||||
/// Modern
|
||||
public static var commonModern: String { return L10n.tr("Localizable", "common_modern") }
|
||||
/// Mute
|
||||
public static var commonMute: String { return L10n.tr("Localizable", "common_mute") }
|
||||
/// No results
|
||||
public static var commonNoResults: String { return L10n.tr("Localizable", "common_no_results") }
|
||||
/// Offline
|
||||
@ -244,6 +252,8 @@ public enum L10n {
|
||||
public static var commonUnableToInviteMessage: String { return L10n.tr("Localizable", "common_unable_to_invite_message") }
|
||||
/// Unable to send invite(s)
|
||||
public static var commonUnableToInviteTitle: String { return L10n.tr("Localizable", "common_unable_to_invite_title") }
|
||||
/// Unmute
|
||||
public static var commonUnmute: String { return L10n.tr("Localizable", "common_unmute") }
|
||||
/// Unsupported event
|
||||
public static var commonUnsupportedEvent: String { return L10n.tr("Localizable", "common_unsupported_event") }
|
||||
/// Username
|
||||
@ -340,7 +350,7 @@ public enum L10n {
|
||||
public static func notificationInvitations(_ p1: Int) -> String {
|
||||
return L10n.tr("Localizable", "notification_invitations", p1)
|
||||
}
|
||||
/// invited you
|
||||
/// Invited you to chat
|
||||
public static var notificationInviteBody: String { return L10n.tr("Localizable", "notification_invite_body") }
|
||||
/// New Messages
|
||||
public static var notificationNewMessages: String { return L10n.tr("Localizable", "notification_new_messages") }
|
||||
@ -352,6 +362,8 @@ public enum L10n {
|
||||
public static var notificationRoomActionMarkAsRead: String { return L10n.tr("Localizable", "notification_room_action_mark_as_read") }
|
||||
/// Quick reply
|
||||
public static var notificationRoomActionQuickReply: String { return L10n.tr("Localizable", "notification_room_action_quick_reply") }
|
||||
/// Invited you to join the room
|
||||
public static var notificationRoomInviteBody: String { return L10n.tr("Localizable", "notification_room_invite_body") }
|
||||
/// Me
|
||||
public static var notificationSenderMe: String { return L10n.tr("Localizable", "notification_sender_me") }
|
||||
/// You are viewing the notification! Click me!
|
||||
@ -538,8 +550,6 @@ public enum L10n {
|
||||
public static var screenChangeServerTitle: String { return L10n.tr("Localizable", "screen_change_server_title") }
|
||||
/// New room
|
||||
public static var screenCreateRoomActionCreateRoom: String { return L10n.tr("Localizable", "screen_create_room_action_create_room") }
|
||||
/// Invite friends to Element
|
||||
public static var screenCreateRoomActionInvitePeople: String { return L10n.tr("Localizable", "screen_create_room_action_invite_people") }
|
||||
/// Invite people
|
||||
public static var screenCreateRoomAddPeopleTitle: String { return L10n.tr("Localizable", "screen_create_room_add_people_title") }
|
||||
/// An error occurred when creating the room
|
||||
@ -682,6 +692,10 @@ public enum L10n {
|
||||
public static var screenRoomErrorFailedProcessingMedia: String { return L10n.tr("Localizable", "screen_room_error_failed_processing_media") }
|
||||
/// Could not retrieve user details
|
||||
public static var screenRoomErrorFailedRetrievingUserDetails: String { return L10n.tr("Localizable", "screen_room_error_failed_retrieving_user_details") }
|
||||
/// Would you like to invite them back?
|
||||
public static var screenRoomInviteAgainAlertMessage: String { return L10n.tr("Localizable", "screen_room_invite_again_alert_message") }
|
||||
/// You are alone in this chat
|
||||
public static var screenRoomInviteAgainAlertTitle: String { return L10n.tr("Localizable", "screen_room_invite_again_alert_title") }
|
||||
/// Block
|
||||
public static var screenRoomMemberDetailsBlockAlertAction: String { return L10n.tr("Localizable", "screen_room_member_details_block_alert_action") }
|
||||
/// Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime.
|
||||
|
@ -37,9 +37,9 @@ struct RoomDetailsScreen: View {
|
||||
|
||||
if let recipient = context.viewState.dmRecipient {
|
||||
ignoreUserSection(user: recipient)
|
||||
} else {
|
||||
leaveRoomSection
|
||||
}
|
||||
|
||||
leaveRoomSection
|
||||
}
|
||||
.elementFormStyle()
|
||||
.alert(item: $context.alertInfo)
|
||||
|
@ -117,6 +117,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
// swiftlint:disable:next function_body_length
|
||||
private func setupSubscriptions() {
|
||||
timelineController.callbacks
|
||||
.receive(on: DispatchQueue.main)
|
||||
@ -164,8 +165,33 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
.weakAssign(to: \.state.members, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
setupDirectRoomSubscriptionsIfNeeded()
|
||||
}
|
||||
|
||||
|
||||
private func setupDirectRoomSubscriptionsIfNeeded() {
|
||||
guard roomProxy.isDirect else {
|
||||
return
|
||||
}
|
||||
|
||||
let shouldShowInviteAlert = context.$viewState
|
||||
.map(\.bindings.composerFocused)
|
||||
.removeDuplicates()
|
||||
.map { [weak self] isFocused in
|
||||
guard let self else { return false }
|
||||
|
||||
return isFocused && self.roomProxy.isUserAloneInDirectRoom
|
||||
}
|
||||
// We want to show the alert just once, so we are taking the first "true" emitted
|
||||
.first { $0 }
|
||||
|
||||
shouldShowInviteAlert
|
||||
.sink { [weak self] _ in
|
||||
self?.showInviteAlert()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func paginateBackwards() async {
|
||||
switch await timelineController.paginateBackwards(requestSize: Constants.backPaginationEventLimit, untilNumberOfItems: Constants.backPaginationPageSize) {
|
||||
case .failure:
|
||||
@ -502,6 +528,57 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
private func hideLoadingIndicator() {
|
||||
userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
|
||||
// MARK: - Direct chats logics
|
||||
|
||||
private func showInviteAlert() {
|
||||
userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenRoomInviteAgainAlertTitle,
|
||||
message: L10n.screenRoomInviteAgainAlertMessage,
|
||||
primaryButton: .init(title: L10n.actionInvite, action: { [weak self] in self?.inviteOtherDMUserBack() }),
|
||||
secondaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil))
|
||||
}
|
||||
|
||||
private let inviteLoadingIndicatorID = UUID().uuidString
|
||||
|
||||
private func inviteOtherDMUserBack() {
|
||||
guard roomProxy.isUserAloneInDirectRoom else {
|
||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
userIndicatorController.submitIndicator(.init(id: inviteLoadingIndicatorID, type: .toast, title: L10n.commonLoading))
|
||||
defer {
|
||||
userIndicatorController.retractIndicatorWithId(inviteLoadingIndicatorID)
|
||||
}
|
||||
|
||||
guard
|
||||
let members = await roomProxy.members(),
|
||||
members.count == 2,
|
||||
let otherPerson = members.first(where: { !$0.isAccountOwner && $0.membership == .leave })
|
||||
else {
|
||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
|
||||
return
|
||||
}
|
||||
|
||||
switch await roomProxy.invite(userID: otherPerson.userID) {
|
||||
case .success:
|
||||
break
|
||||
case .failure:
|
||||
userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.commonUnableToInviteTitle,
|
||||
message: L10n.commonUnableToInviteMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension RoomProxyProtocol {
|
||||
/// Checks if the other person left the room in a direct chat
|
||||
var isUserAloneInDirectRoom: Bool {
|
||||
isDirect && activeMembersCount == 1
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Mocks
|
||||
|
@ -167,4 +167,9 @@ extension RoomProxyProtocol {
|
||||
var isEncryptedOneToOneRoom: Bool {
|
||||
isDirect && isEncrypted && activeMembersCount == 2
|
||||
}
|
||||
|
||||
func members() async -> [RoomMemberProxyProtocol]? {
|
||||
await updateMembers()
|
||||
return await membersPublisher.values.first()
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ class MockScreen: Identifiable {
|
||||
isEncrypted: true,
|
||||
members: members,
|
||||
memberForID: .mockOwner(allowedStateEvents: [], canInviteUsers: false),
|
||||
joinedMembersCount: members.count))
|
||||
activeMembersCount: members.count))
|
||||
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
roomProxy: roomProxy,
|
||||
@ -327,7 +327,7 @@ class MockScreen: Identifiable {
|
||||
canonicalAlias: "#mock:room.org",
|
||||
members: members,
|
||||
memberForID: .mockOwner(allowedStateEvents: [], canInviteUsers: false),
|
||||
joinedMembersCount: members.count))
|
||||
activeMembersCount: members.count))
|
||||
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
roomProxy: roomProxy,
|
||||
@ -348,7 +348,7 @@ class MockScreen: Identifiable {
|
||||
canonicalAlias: "#mock:room.org",
|
||||
members: members,
|
||||
memberForID: owner,
|
||||
joinedMembersCount: members.count))
|
||||
activeMembersCount: members.count))
|
||||
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
roomProxy: roomProxy,
|
||||
@ -365,7 +365,7 @@ class MockScreen: Identifiable {
|
||||
isEncrypted: true,
|
||||
members: members,
|
||||
memberForID: owner,
|
||||
joinedMembersCount: members.count))
|
||||
activeMembersCount: members.count))
|
||||
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
roomProxy: roomProxy,
|
||||
@ -383,7 +383,7 @@ class MockScreen: Identifiable {
|
||||
isEncrypted: true,
|
||||
members: members,
|
||||
memberForID: .mockOwner(allowedStateEvents: [], canInviteUsers: false),
|
||||
joinedMembersCount: members.count))
|
||||
activeMembersCount: members.count))
|
||||
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
roomProxy: roomProxy,
|
||||
|
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomDetailsScreenDmDetails.png
(Stored with Git LFS)
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user