Login UX flow: set avatar and display name after account creation
This commit is contained in:
parent
408a0fc010
commit
c141b26212
|
@ -76,6 +76,7 @@ import im.vector.app.features.login2.LoginFragment2SigninPassword
|
|||
import im.vector.app.features.login2.LoginFragment2SigninUsername
|
||||
import im.vector.app.features.login2.LoginFragment2SignupPassword
|
||||
import im.vector.app.features.login2.LoginFragment2SignupUsername
|
||||
import im.vector.app.features.login2.created.AccountCreatedFragment
|
||||
import im.vector.app.features.login2.LoginFragmentToAny2
|
||||
import im.vector.app.features.login2.LoginGenericTextInputFormFragment2
|
||||
import im.vector.app.features.login2.LoginResetPasswordFragment2
|
||||
|
@ -286,6 +287,11 @@ interface FragmentModule {
|
|||
@FragmentKey(LoginFragment2SigninUsername::class)
|
||||
fun bindLoginFragment2SigninUsername(fragment: LoginFragment2SigninUsername): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(AccountCreatedFragment::class)
|
||||
fun bindAccountCreatedFragment(fragment: AccountCreatedFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(LoginFragment2SignupUsername::class)
|
||||
|
|
|
@ -41,6 +41,7 @@ import im.vector.app.core.utils.DimensionConverter
|
|||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||
import jp.wasabeef.glide.transformations.ColorFilterTransformation
|
||||
import org.matrix.android.sdk.api.auth.login.LoginProfileInfo
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
@ -113,6 +114,23 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
|
|||
.into(imageView)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun render(profileInfo: LoginProfileInfo, imageView: ImageView) {
|
||||
// Create a Fake MatrixItem, for the placeholder
|
||||
val matrixItem = MatrixItem.UserItem(
|
||||
// Need an id starting with @
|
||||
id = profileInfo.matrixId,
|
||||
displayName = profileInfo.displayName
|
||||
)
|
||||
|
||||
val placeholder = getPlaceholderDrawable(matrixItem)
|
||||
GlideApp.with(imageView)
|
||||
.load(profileInfo.fullAvatarUrl)
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
.placeholder(placeholder)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun render(glideRequests: GlideRequests,
|
||||
matrixItem: MatrixItem,
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.matrix.android.sdk.api.failure.Failure
|
|||
/**
|
||||
* Parent Fragment for all the login/registration screens
|
||||
*/
|
||||
abstract class AbstractLoginFragment2<VB: ViewBinding> : VectorBaseFragment<VB>(), OnBackPressed {
|
||||
abstract class AbstractLoginFragment2<VB : ViewBinding> : VectorBaseFragment<VB>(), OnBackPressed {
|
||||
|
||||
protected val loginViewModel: LoginViewModel2 by activityViewModel()
|
||||
|
||||
|
@ -147,11 +147,19 @@ abstract class AbstractLoginFragment2<VB: ViewBinding> : VectorBaseFragment<VB>(
|
|||
}
|
||||
}
|
||||
|
||||
final override fun invalidate() = withState(loginViewModel) { state ->
|
||||
// True when email is sent with success to the homeserver
|
||||
isResetPasswordStarted = state.resetPasswordEmail.isNullOrBlank().not()
|
||||
final override fun invalidate() {
|
||||
withState(loginViewModel) { state ->
|
||||
// True when email is sent with success to the homeserver
|
||||
isResetPasswordStarted = state.resetPasswordEmail.isNullOrBlank().not()
|
||||
|
||||
updateWithState(state)
|
||||
updateWithState(state)
|
||||
}
|
||||
|
||||
invalidateMore()
|
||||
}
|
||||
|
||||
protected open fun invalidateMore() {
|
||||
// No op by default
|
||||
}
|
||||
|
||||
open fun updateWithState(state: LoginViewState2) {
|
||||
|
|
|
@ -41,6 +41,7 @@ sealed class LoginAction2 : VectorViewModelAction {
|
|||
|
||||
// Username to Login or Register, depending on the signMode
|
||||
data class SetUserName(val username: String) : LoginAction2()
|
||||
|
||||
// Password to Login or Register, depending on the signMode
|
||||
data class SetUserPassword(val password: String) : LoginAction2()
|
||||
|
||||
|
@ -82,4 +83,7 @@ sealed class LoginAction2 : VectorViewModelAction {
|
|||
data class PostViewEvent(val viewEvent: LoginViewEvents2) : LoginAction2()
|
||||
|
||||
data class UserAcceptCertificate(val fingerprint: Fingerprint) : LoginAction2()
|
||||
|
||||
// Account customization is over
|
||||
object Finish : LoginAction2()
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import im.vector.app.features.login.LoginWaitForEmailFragmentArgument
|
|||
import im.vector.app.features.login.isSupported
|
||||
import im.vector.app.features.login.terms.LoginTermsFragmentArgument
|
||||
import im.vector.app.features.login.terms.toLocalizedLoginTerms
|
||||
import im.vector.app.features.login2.created.AccountCreatedFragment
|
||||
import im.vector.app.features.login2.terms.LoginTermsFragment2
|
||||
import im.vector.app.features.pin.UnlockedActivity
|
||||
|
||||
|
@ -245,14 +246,26 @@ open class LoginActivity2 : VectorBaseActivity<ActivityLoginBinding>(), ToolbarC
|
|||
is LoginViewEvents2.OnLoginModeNotSupported ->
|
||||
onLoginModeNotSupported(event.supportedTypes)
|
||||
is LoginViewEvents2.OnSessionCreated -> handleOnSessionCreated(event)
|
||||
is LoginViewEvents2.Finish -> terminate(true)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleOnSessionCreated(event: LoginViewEvents2.OnSessionCreated) {
|
||||
// TODO Propose to set avatar and display name
|
||||
if (event.newAccount) {
|
||||
// Propose to set avatar and display name
|
||||
// Back on this Fragment will finish the Activity
|
||||
addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
AccountCreatedFragment::class.java,
|
||||
option = commonOption)
|
||||
} else {
|
||||
terminate(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun terminate(newAccount: Boolean) {
|
||||
val intent = HomeActivity.newIntent(
|
||||
this,
|
||||
accountCreation = event.newAccount
|
||||
accountCreation = newAccount
|
||||
)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
|
@ -260,7 +273,12 @@ open class LoginActivity2 : VectorBaseActivity<ActivityLoginBinding>(), ToolbarC
|
|||
|
||||
private fun updateWithState(LoginViewState2: LoginViewState2) {
|
||||
// Loading
|
||||
views.loginLoading.isVisible = LoginViewState2.isLoading
|
||||
setIsLoading(LoginViewState2.isLoading)
|
||||
}
|
||||
|
||||
// Hack for AccountCreatedFragment
|
||||
fun setIsLoading(isLoading: Boolean) {
|
||||
views.loginLoading.isVisible = isLoading
|
||||
}
|
||||
|
||||
private fun onWebLoginError(onWebLoginError: LoginViewEvents2.OnWebLoginError) {
|
||||
|
|
|
@ -23,16 +23,14 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.autofill.HintConstants
|
||||
import androidx.core.view.isVisible
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.jakewharton.rxbinding3.widget.textChanges
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.hideKeyboard
|
||||
import im.vector.app.core.extensions.setTextOrHide
|
||||
import im.vector.app.core.extensions.showPassword
|
||||
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.isInvalidPassword
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -42,7 +40,9 @@ import javax.inject.Inject
|
|||
* - the user is asked for password to sign in to a homeserver.
|
||||
* - He also can reset his password
|
||||
*/
|
||||
class LoginFragment2SigninPassword @Inject constructor() : AbstractSSOLoginFragment2<FragmentLogin2SigninPasswordBinding>() {
|
||||
class LoginFragment2SigninPassword @Inject constructor(
|
||||
private val avatarRenderer: AvatarRenderer
|
||||
) : AbstractSSOLoginFragment2<FragmentLogin2SigninPasswordBinding>() {
|
||||
|
||||
private var passwordShown = false
|
||||
|
||||
|
@ -106,15 +106,10 @@ class LoginFragment2SigninPassword @Inject constructor() : AbstractSSOLoginFragm
|
|||
state.loginProfileInfo?.displayName?.takeIf { it.isNotBlank() } ?: state.userIdentifier()
|
||||
)
|
||||
|
||||
if (state.loginProfileInfo != null) {
|
||||
views.loginUserIcon.isVisible = true
|
||||
Glide.with(requireContext())
|
||||
.load(state.loginProfileInfo.fullAvatarUrl)
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
.into(views.loginUserIcon)
|
||||
} else {
|
||||
views.loginUserIcon.isVisible = false
|
||||
}
|
||||
avatarRenderer.render(
|
||||
profileInfo = state.loginProfileInfo ?: LoginProfileInfo(state.userIdentifier(), null, null),
|
||||
imageView = views.loginUserIcon
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupSubmitButton() {
|
||||
|
|
|
@ -56,4 +56,6 @@ sealed class LoginViewEvents2 : VectorViewEvents {
|
|||
data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : LoginViewEvents2()
|
||||
|
||||
data class OnSessionCreated(val newAccount: Boolean): LoginViewEvents2()
|
||||
|
||||
object Finish : LoginViewEvents2()
|
||||
}
|
||||
|
|
|
@ -145,9 +145,15 @@ class LoginViewModel2 @AssistedInject constructor(
|
|||
is LoginAction2.UserAcceptCertificate -> handleUserAcceptCertificate(action)
|
||||
LoginAction2.ClearHomeServerHistory -> handleClearHomeServerHistory()
|
||||
is LoginAction2.PostViewEvent -> _viewEvents.post(action.viewEvent)
|
||||
is LoginAction2.Finish -> handleFinish()
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleFinish() {
|
||||
// Just post a view Event
|
||||
_viewEvents.post(LoginViewEvents2.Finish)
|
||||
}
|
||||
|
||||
private fun handleChooseAServerForSignin() {
|
||||
// Just post a view Event
|
||||
_viewEvents.post(LoginViewEvents2.OpenServerSelection)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.features.login2.created
|
||||
|
||||
import android.net.Uri
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class AccountCreatedAction : VectorViewModelAction {
|
||||
data class SetDisplayName(val displayName: String) : AccountCreatedAction()
|
||||
data class SetAvatar(val avatarUri: Uri, val filename: String) : AccountCreatedAction()
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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.features.login2.created
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
|
||||
import im.vector.app.core.intent.getFilenameFromUri
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.databinding.DialogBaseEditTextBinding
|
||||
import im.vector.app.databinding.FragmentLoginAccountCreatedBinding
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||
import im.vector.app.features.login2.AbstractLoginFragment2
|
||||
import im.vector.app.features.login2.LoginAction2
|
||||
import im.vector.app.features.login2.LoginActivity2
|
||||
import im.vector.app.features.login2.LoginViewState2
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* In this screen:
|
||||
* - the account has been created and we propose the user to set an avatar and a display name
|
||||
*/
|
||||
class AccountCreatedFragment @Inject constructor(
|
||||
val accountCreatedViewModelFactory: AccountCreatedViewModel.Factory,
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider,
|
||||
colorProvider: ColorProvider
|
||||
) : AbstractLoginFragment2<FragmentLoginAccountCreatedBinding>(),
|
||||
GalleryOrCameraDialogHelper.Listener {
|
||||
|
||||
private val viewModel: AccountCreatedViewModel by fragmentViewModel()
|
||||
|
||||
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginAccountCreatedBinding {
|
||||
return FragmentLoginAccountCreatedBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupClickListener()
|
||||
setupSubmitButton()
|
||||
observeViewEvents()
|
||||
}
|
||||
|
||||
private fun observeViewEvents() {
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is AccountCreatedViewEvents.Failure -> displayErrorDialog(it.throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupClickListener() {
|
||||
views.loginAccountCreatedMessage.setOnClickListener {
|
||||
// Update display name
|
||||
displayDialog()
|
||||
}
|
||||
views.loginAccountCreatedAvatar.setOnClickListener {
|
||||
galleryOrCameraDialogHelper.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayDialog() = withState(viewModel) { state ->
|
||||
val inflater = requireActivity().layoutInflater
|
||||
val layout = inflater.inflate(R.layout.dialog_base_edit_text, null)
|
||||
val views = DialogBaseEditTextBinding.bind(layout)
|
||||
views.editText.setText(state.currentUser()?.getBestName().orEmpty())
|
||||
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.settings_display_name)
|
||||
.setView(layout)
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
val newName = views.editText.text.toString()
|
||||
viewModel.handle(AccountCreatedAction.SetDisplayName(newName))
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onImageReady(uri: Uri?) {
|
||||
uri ?: return
|
||||
viewModel.handle(AccountCreatedAction.SetAvatar(
|
||||
avatarUri = uri,
|
||||
filename = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString())
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupSubmitButton() {
|
||||
views.loginAccountCreatedLater.setOnClickListener { terminate() }
|
||||
views.loginAccountCreatedDone.setOnClickListener { terminate() }
|
||||
}
|
||||
|
||||
private fun terminate() {
|
||||
loginViewModel.handle(LoginAction2.Finish)
|
||||
}
|
||||
|
||||
override fun invalidateMore() = withState(viewModel) { state ->
|
||||
// Ugly hack...
|
||||
(activity as? LoginActivity2)?.setIsLoading(state.isLoading)
|
||||
|
||||
views.loginAccountCreatedSubtitle.text = getString(R.string.login_account_created_subtitle, state.userId)
|
||||
|
||||
val user = state.currentUser()
|
||||
if (user != null) {
|
||||
avatarRenderer.render(user, views.loginAccountCreatedAvatar)
|
||||
views.loginAccountCreatedMemberName.text = user.getBestName()
|
||||
} else {
|
||||
// Should not happen
|
||||
views.loginAccountCreatedMemberName.text = state.userId
|
||||
}
|
||||
|
||||
// User color
|
||||
views.loginAccountCreatedMemberName
|
||||
.setTextColor(matrixItemColorProvider.getColor(MatrixItem.UserItem(state.userId)))
|
||||
|
||||
views.loginAccountCreatedLater.isVisible = state.hasBeenModified.not()
|
||||
views.loginAccountCreatedDone.isVisible = state.hasBeenModified
|
||||
}
|
||||
|
||||
override fun updateWithState(state: LoginViewState2) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun resetViewModel() {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||
// Just start the next Activity
|
||||
terminate()
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 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.features.login2.created
|
||||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
/**
|
||||
* Transient events for Account Created
|
||||
*/
|
||||
sealed class AccountCreatedViewEvents : VectorViewEvents {
|
||||
data class Failure(val throwable: Throwable) : AccountCreatedViewEvents()
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.features.login2.created
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
import org.matrix.android.sdk.rx.unwrap
|
||||
|
||||
class AccountCreatedViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: AccountCreatedViewState,
|
||||
private val session: Session
|
||||
) : VectorViewModel<AccountCreatedViewState, AccountCreatedAction, AccountCreatedViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(initialState: AccountCreatedViewState): AccountCreatedViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<AccountCreatedViewModel, AccountCreatedViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: AccountCreatedViewState): AccountCreatedViewModel? {
|
||||
val fragment: AccountCreatedFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.accountCreatedViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setState {
|
||||
copy(
|
||||
userId = session.myUserId
|
||||
)
|
||||
}
|
||||
observeUser()
|
||||
}
|
||||
|
||||
private fun observeUser() {
|
||||
session.rx()
|
||||
.liveUser(session.myUserId)
|
||||
.unwrap()
|
||||
.map { it.toMatrixItem() }
|
||||
.execute {
|
||||
copy(currentUser = it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: AccountCreatedAction) {
|
||||
when (action) {
|
||||
is AccountCreatedAction.SetAvatar -> handleSetAvatar(action)
|
||||
is AccountCreatedAction.SetDisplayName -> handleSetDisplayName(action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetAvatar(action: AccountCreatedAction.SetAvatar) {
|
||||
setState { copy(isLoading = true) }
|
||||
viewModelScope.launch {
|
||||
val result = runCatching { session.updateAvatar(session.myUserId, action.avatarUri, action.filename) }
|
||||
.onFailure { _viewEvents.post(AccountCreatedViewEvents.Failure(it)) }
|
||||
setState {
|
||||
copy(
|
||||
isLoading = false,
|
||||
hasBeenModified = hasBeenModified || result.isSuccess
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetDisplayName(action: AccountCreatedAction.SetDisplayName) {
|
||||
setState { copy(isLoading = true) }
|
||||
viewModelScope.launch {
|
||||
val result = runCatching { session.setDisplayName(session.myUserId, action.displayName) }
|
||||
.onFailure { _viewEvents.post(AccountCreatedViewEvents.Failure(it)) }
|
||||
setState {
|
||||
copy(
|
||||
isLoading = false,
|
||||
hasBeenModified = hasBeenModified || result.isSuccess
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.features.login2.created
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
data class AccountCreatedViewState(
|
||||
val userId: String = "",
|
||||
val isLoading: Boolean = false,
|
||||
val currentUser: Async<MatrixItem.UserItem> = Uninitialized,
|
||||
val hasBeenModified: Boolean = false
|
||||
) : MvRxState
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/login_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?riotx_background">
|
||||
|
||||
<androidx.core.widget.NestedScrollView style="@style/LoginFormScrollView">
|
||||
|
||||
<LinearLayout style="@style/LoginFormContainer">
|
||||
|
||||
<ImageView
|
||||
style="@style/LoginLogo"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/login_account_created_title"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginAccountCreatedSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Title.Small"
|
||||
tools:text="@string/login_account_created_subtitle" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/login_account_created_notice"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/login_account_created_notice_2"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/loginAccountCreatedMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/loginAccountCreatedAvatar"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:contentDescription="@string/avatar"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginAccountCreatedMemberName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_toEndOf="@+id/loginAccountCreatedAvatar"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="\@user:domain.org" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/loginAccountCreatedMemberName"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_toEndOf="@+id/loginAccountCreatedAvatar"
|
||||
android:text="@string/login_account_created_message"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/login_account_created_instruction"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginAccountCreatedLater"
|
||||
style="@style/Style.Vector.Login.Button.Text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/later" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginAccountCreatedDone"
|
||||
style="@style/Style.Vector.Login.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/done"
|
||||
android:visibility="gone"
|
||||
tools:layout_marginEnd="120dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</FrameLayout>
|
||||
|
|
@ -27,5 +27,11 @@
|
|||
|
||||
<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>
|
||||
<string name="login_account_created_subtitle">You account %s has been successfully created.</string>
|
||||
<string name="login_account_created_notice">To complete your profile, you can set a profile image and/or a display name. This can also be done later from the settings.</string>
|
||||
<string name="login_account_created_notice_2">This is how your messages will appear:</string>
|
||||
<string name="login_account_created_message">Hello Matrix world!</string>
|
||||
<string name="login_account_created_instruction">Click on the image and on your name to configure them.</string>
|
||||
|
||||
</resources>
|
Loading…
Reference in New Issue