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

Added an alert before creating a new DM #3730

Merged
merged 3 commits into from
Feb 3, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,9 @@
"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_alert_create_dm_confirmation_title" = "Send invite";
"screen_room_member_details_alert_create_dm_message" = "Would you like to start a chat with %1$@?";
"screen_room_member_details_alert_create_dm_title" = "Send invite?";
"screen_room_member_details_block_alert_action" = "Block";
"screen_room_member_details_block_alert_description" = "Blocked users won't be able to send you messages and all their messages will be hidden. You can unblock them anytime.";
"screen_room_member_details_block_user" = "Block user";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,9 @@
"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_alert_create_dm_confirmation_title" = "Send invite";
"screen_room_member_details_alert_create_dm_message" = "Would you like to start a chat with %1$@?";
"screen_room_member_details_alert_create_dm_title" = "Send invite?";
"screen_room_member_details_block_alert_action" = "Block";
"screen_room_member_details_block_alert_description" = "Blocked users won't be able to send you messages and all their messages will be hidden. You can unblock them anytime.";
"screen_room_member_details_block_user" = "Block user";
Expand Down
8 changes: 8 additions & 0 deletions ElementX/Sources/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,14 @@ internal enum L10n {
internal static var screenRoomInviteAgainAlertMessage: String { return L10n.tr("Localizable", "screen_room_invite_again_alert_message") }
/// You are alone in this chat
internal static var screenRoomInviteAgainAlertTitle: String { return L10n.tr("Localizable", "screen_room_invite_again_alert_title") }
/// Send invite
internal static var screenRoomMemberDetailsAlertCreateDmConfirmationTitle: String { return L10n.tr("Localizable", "screen_room_member_details_alert_create_dm_confirmation_title") }
/// Would you like to start a chat with %1$@?
internal static func screenRoomMemberDetailsAlertCreateDmMessage(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_room_member_details_alert_create_dm_message", String(describing: p1))
}
/// Send invite?
internal static var screenRoomMemberDetailsAlertCreateDmTitle: String { return L10n.tr("Localizable", "screen_room_member_details_alert_create_dm_title") }
/// Block
internal static var screenRoomMemberDetailsBlockAlertAction: String { return L10n.tr("Localizable", "screen_room_member_details_block_alert_action") }
/// Blocked users won't be able to send you messages and all their messages will be hidden. You can unblock them anytime.
Expand Down
70 changes: 0 additions & 70 deletions ElementX/Sources/Mocks/Generated/GeneratedMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2455,76 +2455,6 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
return accountURLActionReturnValue
}
}
//MARK: - createDirectRoomIfNeeded

var createDirectRoomIfNeededWithExpectedRoomNameUnderlyingCallsCount = 0
var createDirectRoomIfNeededWithExpectedRoomNameCallsCount: Int {
get {
if Thread.isMainThread {
return createDirectRoomIfNeededWithExpectedRoomNameUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = createDirectRoomIfNeededWithExpectedRoomNameUnderlyingCallsCount
}

return returnValue!
}
}
set {
if Thread.isMainThread {
createDirectRoomIfNeededWithExpectedRoomNameUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
createDirectRoomIfNeededWithExpectedRoomNameUnderlyingCallsCount = newValue
}
}
}
}
var createDirectRoomIfNeededWithExpectedRoomNameCalled: Bool {
return createDirectRoomIfNeededWithExpectedRoomNameCallsCount > 0
}
var createDirectRoomIfNeededWithExpectedRoomNameReceivedArguments: (userID: String, expectedRoomName: String?)?
var createDirectRoomIfNeededWithExpectedRoomNameReceivedInvocations: [(userID: String, expectedRoomName: String?)] = []

var createDirectRoomIfNeededWithExpectedRoomNameUnderlyingReturnValue: Result<(roomID: String, isNewRoom: Bool), ClientProxyError>!
var createDirectRoomIfNeededWithExpectedRoomNameReturnValue: Result<(roomID: String, isNewRoom: Bool), ClientProxyError>! {
get {
if Thread.isMainThread {
return createDirectRoomIfNeededWithExpectedRoomNameUnderlyingReturnValue
} else {
var returnValue: Result<(roomID: String, isNewRoom: Bool), ClientProxyError>? = nil
DispatchQueue.main.sync {
returnValue = createDirectRoomIfNeededWithExpectedRoomNameUnderlyingReturnValue
}

return returnValue!
}
}
set {
if Thread.isMainThread {
createDirectRoomIfNeededWithExpectedRoomNameUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
createDirectRoomIfNeededWithExpectedRoomNameUnderlyingReturnValue = newValue
}
}
}
}
var createDirectRoomIfNeededWithExpectedRoomNameClosure: ((String, String?) async -> Result<(roomID: String, isNewRoom: Bool), ClientProxyError>)?

func createDirectRoomIfNeeded(with userID: String, expectedRoomName: String?) async -> Result<(roomID: String, isNewRoom: Bool), ClientProxyError> {
createDirectRoomIfNeededWithExpectedRoomNameCallsCount += 1
createDirectRoomIfNeededWithExpectedRoomNameReceivedArguments = (userID: userID, expectedRoomName: expectedRoomName)
DispatchQueue.main.async {
self.createDirectRoomIfNeededWithExpectedRoomNameReceivedInvocations.append((userID: userID, expectedRoomName: expectedRoomName))
}
if let createDirectRoomIfNeededWithExpectedRoomNameClosure = createDirectRoomIfNeededWithExpectedRoomNameClosure {
return await createDirectRoomIfNeededWithExpectedRoomNameClosure(userID, expectedRoomName)
} else {
return createDirectRoomIfNeededWithExpectedRoomNameReturnValue
}
}
//MARK: - directRoomForUserID

var directRoomForUserIDUnderlyingCallsCount = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct RoomMemberDetailsScreenViewStateBindings {
}

var ignoreUserAlert: IgnoreUserAlertItem?
var alertInfo: AlertInfo<RoomMemberDetailsScreenError>?
var alertInfo: AlertInfo<RoomMemberDetailsScreenAlertType>?

/// A media item that will be previewed with QuickLook.
var mediaPreviewItem: MediaPreviewItem?
Expand All @@ -88,7 +88,8 @@ enum RoomMemberDetailsScreenViewAction {
case startCall(roomID: String)
}

enum RoomMemberDetailsScreenError: Hashable {
enum RoomMemberDetailsScreenAlertType: Hashable {
case failedOpeningDirectChat
case createDirectChatConfirmation
case unknown
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,41 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier,
type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false),
title: L10n.commonLoading,
persistent: true))
persistent: true),
delay: .milliseconds(200))
defer { userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier) }

switch await clientProxy.createDirectRoomIfNeeded(with: roomMemberProxy.userID, expectedRoomName: roomMemberProxy.displayName) {
case .success((let roomID, let isNewRoom)):
if isNewRoom {
analytics.trackCreatedRoom(isDM: true)
switch await clientProxy.directRoomForUserID(roomMemberProxy.userID) {
case .success(let roomID):
if let roomID {
actionsSubject.send(.openDirectChat(roomID: roomID))
} else {
let string = roomMemberProxy.displayName ?? roomMemberProxy.userID
state.bindings.alertInfo = .init(id: .createDirectChatConfirmation,
title: L10n.screenRoomMemberDetailsAlertCreateDmTitle,
message: L10n.screenRoomMemberDetailsAlertCreateDmMessage(string),
primaryButton: .init(title: L10n.screenRoomMemberDetailsAlertCreateDmConfirmationTitle) { [weak self] in Task { await self?.createDirectChat() }},
secondaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil))
}
case .failure:
state.bindings.alertInfo = .init(id: .failedOpeningDirectChat)
}
}

private func createDirectChat() async {
guard let roomMemberProxy else { fatalError() }

let loadingIndicatorIdentifier = "createDirectChatLoadingIndicator"
userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier,
type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false),
title: L10n.commonLoading,
persistent: true),
delay: .milliseconds(200))
defer { userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier) }

switch await clientProxy.createDirectRoom(with: roomMemberProxy.userID, expectedRoomName: roomMemberProxy.displayName) {
case .success(let roomID):
analytics.trackCreatedRoom(isDM: true)
actionsSubject.send(.openDirectChat(roomID: roomID))
case .failure:
state.bindings.alertInfo = .init(id: .failedOpeningDirectChat)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct UserProfileScreenViewState: BindableState {
}

struct UserProfileScreenViewStateBindings {
var alertInfo: AlertInfo<UserProfileScreenError>?
var alertInfo: AlertInfo<UserProfileScreenAlertType>?

/// A media item that will be previewed with QuickLook.
var mediaPreviewItem: MediaPreviewItem?
Expand All @@ -48,7 +48,8 @@ enum UserProfileScreenViewAction {
case dismiss
}

enum UserProfileScreenError: Hashable {
enum UserProfileScreenAlertType: Hashable {
case failedOpeningDirectChat
case unknown
case createDirectChatConfirmation
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,33 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr

showLoadingIndicator(allowsInteraction: false)
defer { hideLoadingIndicator() }

switch await clientProxy.createDirectRoomIfNeeded(with: userProfile.userID, expectedRoomName: userProfile.displayName) {
case .success((let roomID, let isNewRoom)):
if isNewRoom {
analytics.trackCreatedRoom(isDM: true)

switch await clientProxy.directRoomForUserID(userProfile.userID) {
case .success(let roomID):
if let roomID {
actionsSubject.send(.openDirectChat(roomID: roomID))
} else {
let string = userProfile.displayName ?? userProfile.userID
state.bindings.alertInfo = .init(id: .createDirectChatConfirmation,
title: L10n.screenRoomMemberDetailsAlertCreateDmTitle,
message: L10n.screenRoomMemberDetailsAlertCreateDmMessage(string),
primaryButton: .init(title: L10n.screenRoomMemberDetailsAlertCreateDmConfirmationTitle) { [weak self] in Task { await self?.createDirectChat() }},
secondaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil))
}
case .failure:
state.bindings.alertInfo = .init(id: .failedOpeningDirectChat)
}
}

private func createDirectChat() async {
guard let userProfile = state.userProfile else { fatalError() }

showLoadingIndicator(allowsInteraction: false)
defer { hideLoadingIndicator() }

switch await clientProxy.createDirectRoom(with: userProfile.userID, expectedRoomName: userProfile.displayName) {
case .success(let roomID):
analytics.trackCreatedRoom(isDM: true)
actionsSubject.send(.openDirectChat(roomID: roomID))
case .failure:
state.bindings.alertInfo = .init(id: .failedOpeningDirectChat)
Expand Down
17 changes: 0 additions & 17 deletions ElementX/Sources/Services/Client/ClientProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -341,23 +341,6 @@ class ClientProxy: ClientProxyProtocol {
try? await client.accountUrl(action: action).flatMap(URL.init(string:))
}

func createDirectRoomIfNeeded(with userID: String, expectedRoomName: String?) async -> Result<(roomID: String, isNewRoom: Bool), ClientProxyError> {
let currentDirectRoom = await directRoomForUserID(userID)
switch currentDirectRoom {
case .success(.some(let roomID)):
return .success((roomID: roomID, isNewRoom: false))
case .success(.none):
switch await createDirectRoom(with: userID, expectedRoomName: expectedRoomName) {
case .success(let roomID):
return .success((roomID: roomID, isNewRoom: true))
case .failure(let error):
return .failure(.sdkError(error))
}
case .failure(let error):
return .failure(.sdkError(error))
}
}

func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError> {
await Task.dispatch(on: clientQueue) {
do {
Expand Down
2 changes: 0 additions & 2 deletions ElementX/Sources/Services/Client/ClientProxyProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {

func accountURL(action: AccountManagementAction) async -> URL?

func createDirectRoomIfNeeded(with userID: String, expectedRoomName: String?) async -> Result<(roomID: String, isNewRoom: Bool), ClientProxyError>

func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError>

func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result<String, ClientProxyError>
Expand Down
Loading