From 3163bc8b8056d68ced9517db227ac79696e0fe59 Mon Sep 17 00:00:00 2001
From: onurays <onurays@matrix.org>
Date: Tue, 21 Apr 2020 15:25:48 +0300
Subject: [PATCH 1/3] Add user to direct chat by user id.

Fixes #1065
---
 CHANGES.md                                      |  1 +
 .../createdirect/DirectoryUsersController.kt    | 17 ++++++++++++++---
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 0c1d209f61..91ebeecaa7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -35,6 +35,7 @@ Bugfix 🐛:
  - Local echo are not updated in timeline (for failed & encrypted states)
  - Render image event even if thumbnail_info does not have mimetype defined (#1209)
  - Fix issue with media path (#1227)
+ - Add user to direct chat by user id (#1065)
 
 Translations 🗣:
  -
diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt
index 016806f319..fd943b6447 100644
--- a/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt
@@ -23,6 +23,7 @@ import com.airbnb.mvrx.Fail
 import com.airbnb.mvrx.Loading
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.Uninitialized
+import im.vector.matrix.android.api.MatrixPatterns
 import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.user.model.User
 import im.vector.matrix.android.api.util.toMatrixItem
@@ -56,15 +57,25 @@ class DirectoryUsersController @Inject constructor(private val session: Session,
     override fun buildModels() {
         val currentState = state ?: return
         val hasSearch = currentState.directorySearchTerm.isNotBlank()
-        val asyncUsers = currentState.directoryUsers
-        when (asyncUsers) {
+        when (val asyncUsers = currentState.directoryUsers) {
             is Uninitialized -> renderEmptyState(false)
             is Loading       -> renderLoading()
-            is Success       -> renderSuccess(asyncUsers(), currentState.selectedUsers.map { it.userId }, hasSearch)
+            is Success       -> renderSuccess(getAsyncUsers(currentState), currentState.selectedUsers.map { it.userId }, hasSearch)
             is Fail          -> renderFailure(asyncUsers.error)
         }
     }
 
+    private fun getAsyncUsers(currentState: CreateDirectRoomViewState): List<User> {
+        return currentState
+                .directoryUsers()
+                ?.toMutableList()
+                ?.apply {
+                    currentState.directorySearchTerm
+                            .takeIf { MatrixPatterns.isUserId(it) }
+                            ?.let { add(User(it)) }
+                } ?: emptyList()
+    }
+
     private fun renderLoading() {
         loadingItem {
             id("loading")

From 06cf59bca71f6d146cdc8544e0e67adcb8f1572b Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Wed, 22 Apr 2020 19:20:13 +0200
Subject: [PATCH 2/3] Even if it's not happening, do not add the search term if
 already present in the results.

---
 .../createdirect/DirectoryUsersController.kt  | 24 +++++++++++--------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt
index fd943b6447..1c38e6f723 100644
--- a/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/createdirect/DirectoryUsersController.kt
@@ -60,20 +60,24 @@ class DirectoryUsersController @Inject constructor(private val session: Session,
         when (val asyncUsers = currentState.directoryUsers) {
             is Uninitialized -> renderEmptyState(false)
             is Loading       -> renderLoading()
-            is Success       -> renderSuccess(getAsyncUsers(currentState), currentState.selectedUsers.map { it.userId }, hasSearch)
+            is Success       -> renderSuccess(
+                    computeUsersList(asyncUsers(), currentState.directorySearchTerm),
+                    currentState.selectedUsers.map { it.userId },
+                    hasSearch
+            )
             is Fail          -> renderFailure(asyncUsers.error)
         }
     }
 
-    private fun getAsyncUsers(currentState: CreateDirectRoomViewState): List<User> {
-        return currentState
-                .directoryUsers()
-                ?.toMutableList()
-                ?.apply {
-                    currentState.directorySearchTerm
-                            .takeIf { MatrixPatterns.isUserId(it) }
-                            ?.let { add(User(it)) }
-                } ?: emptyList()
+    /**
+     * Eventually add the searched terms, if it is a userId, and if not already present in the result
+     */
+    private fun computeUsersList(directoryUsers: List<User>, searchTerms: String): List<User> {
+        return directoryUsers +
+                searchTerms
+                        .takeIf { terms -> MatrixPatterns.isUserId(terms) && !directoryUsers.any { it.userId == terms } }
+                        ?.let { listOf(User(it)) }
+                        .orEmpty()
     }
 
     private fun renderLoading() {

From 1363100f944c4ae1b001e66bbb5c9d8085ef7d73 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Wed, 22 Apr 2020 23:03:04 +0200
Subject: [PATCH 3/3] Create DM: now any userId can be entered, so deal with
 the case of the userId does not exists. Use same string resource value than
 Riot-Web

---
 .../features/createdirect/CreateDirectRoomActivity.kt  | 10 +++++++++-
 vector/src/main/res/values/strings_riotX.xml           |  1 +
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomActivity.kt
index 12674e5cd2..3ae206cd21 100644
--- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomActivity.kt
@@ -28,6 +28,7 @@ import com.airbnb.mvrx.Fail
 import com.airbnb.mvrx.Loading
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.viewModel
+import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure
 import im.vector.riotx.R
 import im.vector.riotx.core.di.ScreenComponent
@@ -37,6 +38,7 @@ import im.vector.riotx.core.extensions.addFragmentToBackstack
 import im.vector.riotx.core.platform.SimpleFragmentActivity
 import im.vector.riotx.core.platform.WaitingViewData
 import kotlinx.android.synthetic.main.activity.*
+import java.net.HttpURLConnection
 import javax.inject.Inject
 
 class CreateDirectRoomActivity : SimpleFragmentActivity() {
@@ -91,8 +93,14 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
         if (error is CreateRoomFailure.CreatedWithTimeout) {
             finish()
         } else {
+            val message = if (error is Failure.ServerError && error.httpCode == HttpURLConnection.HTTP_INTERNAL_ERROR /*500*/) {
+                // This error happen if the invited userId does not exist.
+                getString(R.string.create_room_dm_failure)
+            } else {
+                errorFormatter.toHumanReadable(error)
+            }
             AlertDialog.Builder(this)
-                    .setMessage(errorFormatter.toHumanReadable(error))
+                    .setMessage(message)
                     .setPositiveButton(R.string.ok, null)
                     .show()
         }
diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
index 3e23f61acf..8ffea96205 100644
--- a/vector/src/main/res/values/strings_riotX.xml
+++ b/vector/src/main/res/values/strings_riotX.xml
@@ -29,4 +29,5 @@
 
     <!-- END Strings added by Others -->
 
+    <string name="create_room_dm_failure">"We couldn't create your DM. Please check the users you want to invite and try again."</string>
 </resources>