From 51a39909dcf783fc5b787ee98381f90d3eaccbff Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 14 Apr 2021 18:20:01 +0200
Subject: [PATCH] Login UX flow: warning if no profile can ba found

---
 .../app/core/extensions/MvRxExtension.kt      | 32 +++++++++++++++++++
 .../login2/LoginFragment2SigninPassword.kt    | 12 +++++--
 .../app/features/login2/LoginViewModel2.kt    | 16 ++++------
 .../app/features/login2/LoginViewState2.kt    |  4 ++-
 .../fragment_login_2_signin_password.xml      | 11 +++++++
 vector/src/main/res/values/colors.xml         |  1 +
 .../src/main/res/values/strings_login_v2.xml  |  2 ++
 7 files changed, 65 insertions(+), 13 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/core/extensions/MvRxExtension.kt

diff --git a/vector/src/main/java/im/vector/app/core/extensions/MvRxExtension.kt b/vector/src/main/java/im/vector/app/core/extensions/MvRxExtension.kt
new file mode 100644
index 0000000000..9daf16a589
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/extensions/MvRxExtension.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 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.
+ */
+
+package im.vector.app.core.extensions
+
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Success
+
+/**
+ * It maybe already exist somewhere but I cannot find it
+ */
+suspend fun <T> tryAsync(block: suspend () -> T): Async<T> {
+    return try {
+        Success(block.invoke())
+    } catch (failure: Throwable) {
+        Fail(failure)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginFragment2SigninPassword.kt b/vector/src/main/java/im/vector/app/features/login2/LoginFragment2SigninPassword.kt
index 9517685a9e..39701cb01b 100644
--- a/vector/src/main/java/im/vector/app/features/login2/LoginFragment2SigninPassword.kt
+++ b/vector/src/main/java/im/vector/app/features/login2/LoginFragment2SigninPassword.kt
@@ -23,6 +23,8 @@ import android.view.View
 import android.view.ViewGroup
 import android.view.inputmethod.EditorInfo
 import androidx.autofill.HintConstants
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.Fail
 import com.jakewharton.rxbinding3.widget.textChanges
 import im.vector.app.R
 import im.vector.app.core.extensions.hideKeyboard
@@ -31,8 +33,10 @@ import im.vector.app.databinding.FragmentLogin2SigninPasswordBinding
 import im.vector.app.features.home.AvatarRenderer
 import io.reactivex.rxkotlin.subscribeBy
 import org.matrix.android.sdk.api.auth.login.LoginProfileInfo
+import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.isInvalidPassword
 import javax.inject.Inject
+import javax.net.ssl.HttpsURLConnection
 
 /**
  * In this screen:
@@ -103,13 +107,17 @@ class LoginFragment2SigninPassword @Inject constructor(
         // Name and avatar
         views.loginWelcomeBack.text = getString(
                 R.string.login_welcome_back,
-                state.loginProfileInfo?.displayName?.takeIf { it.isNotBlank() } ?: state.userIdentifier()
+                state.loginProfileInfo()?.displayName?.takeIf { it.isNotBlank() } ?: state.userIdentifier()
         )
 
         avatarRenderer.render(
-                profileInfo = state.loginProfileInfo ?: LoginProfileInfo(state.userIdentifier(), null, null),
+                profileInfo = state.loginProfileInfo() ?: LoginProfileInfo(state.userIdentifier(), null, null),
                 imageView = views.loginUserIcon
         )
+
+        views.loginWelcomeBackWarning.isVisible = ((state.loginProfileInfo as? Fail)
+                ?.error as? Failure.ServerError)
+                ?.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */
     }
 
     private fun setupSubmitButton() {
diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt
index b09fc4ac8a..a6f87f74c7 100644
--- a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt
+++ b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt
@@ -21,6 +21,7 @@ import android.net.Uri
 import androidx.fragment.app.FragmentActivity
 import androidx.lifecycle.viewModelScope
 import com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.Loading
 import com.airbnb.mvrx.MvRxViewModelFactory
 import com.airbnb.mvrx.ViewModelContext
 import dagger.assisted.Assisted
@@ -30,6 +31,7 @@ import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.extensions.exhaustive
+import im.vector.app.core.extensions.tryAsync
 import im.vector.app.core.platform.VectorViewModel
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.core.utils.ensureTrailingSlash
@@ -665,17 +667,11 @@ class LoginViewModel2 @AssistedInject constructor(
         val safeLoginWizard = loginWizard
 
         if (safeLoginWizard != null) {
-            try {
-                val info = safeLoginWizard.getProfileInfo(username)
-                setState {
-                    copy(
-                            loginProfileInfo = info
-                    )
-                }
-            } catch (failure: Throwable) {
-                // Ignore error
-                // TODO 404 may indicates that the user does not exist, so there is a mistake in the id
+            setState { copy(loginProfileInfo = Loading()) }
+            val result = tryAsync {
+               safeLoginWizard.getProfileInfo(username)
             }
+            setState { copy(loginProfileInfo = result) }
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt
index 3c631af18c..261fd417ee 100644
--- a/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt
+++ b/vector/src/main/java/im/vector/app/features/login2/LoginViewState2.kt
@@ -16,8 +16,10 @@
 
 package im.vector.app.features.login2
 
+import com.airbnb.mvrx.Async
 import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.PersistState
+import com.airbnb.mvrx.Uninitialized
 import im.vector.app.core.extensions.toReducedUrl
 import im.vector.app.features.login.LoginMode
 import org.matrix.android.sdk.api.MatrixPatterns
@@ -45,7 +47,7 @@ data class LoginViewState2(
         val deviceId: String? = null,
 
         // Network result
-        val loginProfileInfo: LoginProfileInfo? = null,
+        val loginProfileInfo: Async<LoginProfileInfo> = Uninitialized,
 
         // True on Matrix.org
         val isNumericOnlyUserIdForbidden: Boolean = false,
diff --git a/vector/src/main/res/layout/fragment_login_2_signin_password.xml b/vector/src/main/res/layout/fragment_login_2_signin_password.xml
index 638314cf3f..bc23420312 100644
--- a/vector/src/main/res/layout/fragment_login_2_signin_password.xml
+++ b/vector/src/main/res/layout/fragment_login_2_signin_password.xml
@@ -31,6 +31,17 @@
                 android:textAppearance="@style/TextAppearance.Vector.Login.Text"
                 tools:text="Welcome back user!" />
 
+            <TextView
+                android:id="@+id/loginWelcomeBackWarning"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/layout_vertical_margin"
+                android:text="@string/login_unknown_user_warning"
+                android:textAppearance="@style/TextAppearance.Vector.Login.Text"
+                android:textColor="@color/vector_warning_color_2"
+                android:visibility="gone"
+                tools:visibility="visible" />
+
             <TextView
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/vector/src/main/res/values/colors.xml b/vector/src/main/res/values/colors.xml
index f4a026bea3..55cd232edf 100644
--- a/vector/src/main/res/values/colors.xml
+++ b/vector/src/main/res/values/colors.xml
@@ -4,6 +4,7 @@
     <!-- Error colors -->
     <color name="vector_success_color">#70BF56</color>
     <color name="vector_warning_color">#ff4b55</color>
+    <color name="vector_warning_color_2">#ff812d</color>
     <color name="vector_error_color">#ff4b55</color>
     <color name="vector_info_color">#2f9edb</color>
 
diff --git a/vector/src/main/res/values/strings_login_v2.xml b/vector/src/main/res/values/strings_login_v2.xml
index a32912cc00..73f5ba03b4 100644
--- a/vector/src/main/res/values/strings_login_v2.xml
+++ b/vector/src/main/res/values/strings_login_v2.xml
@@ -25,6 +25,8 @@
     <string name="login_create_a_new_account">Create a new account</string>
     <string name="login_i_already_have_an_account">I already have an account</string>
 
+    <string name="login_unknown_user_warning">Warning: no profile information can be retrieved with this Matrix identifier. Please check that there is no mistake.</string>
+
     <string name="login_wait_for_email_notice_2">We just sent an email to %1$s.</string>
     <string name="login_wait_for_email_help">Click on the link it contains to continue the account creation.</string>
     <string name="login_account_created_title">Congratulations!</string>