extracting inline fragment view logic to a controller
This commit is contained in:
parent
92c615af31
commit
d403d16731
@ -20,8 +20,11 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
import im.vector.app.core.extensions.associateContentStateWith
|
import im.vector.app.core.extensions.associateContentStateWith
|
||||||
import im.vector.app.core.extensions.clearErrorOnChange
|
import im.vector.app.core.extensions.clearErrorOnChange
|
||||||
import im.vector.app.core.extensions.content
|
import im.vector.app.core.extensions.content
|
||||||
@ -30,6 +33,7 @@ import im.vector.app.core.extensions.realignPercentagesToParent
|
|||||||
import im.vector.app.core.extensions.setOnImeDoneListener
|
import im.vector.app.core.extensions.setOnImeDoneListener
|
||||||
import im.vector.app.core.extensions.showKeyboard
|
import im.vector.app.core.extensions.showKeyboard
|
||||||
import im.vector.app.core.extensions.toReducedUrl
|
import im.vector.app.core.extensions.toReducedUrl
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.core.utils.ensureProtocol
|
import im.vector.app.core.utils.ensureProtocol
|
||||||
import im.vector.app.core.utils.ensureTrailingSlash
|
import im.vector.app.core.utils.ensureTrailingSlash
|
||||||
import im.vector.app.core.utils.openUrlInExternalBrowser
|
import im.vector.app.core.utils.openUrlInExternalBrowser
|
||||||
@ -37,42 +41,41 @@ import im.vector.app.databinding.FragmentFtueServerSelectionCombinedBinding
|
|||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
import im.vector.app.features.onboarding.OnboardingFlow
|
import im.vector.app.features.onboarding.OnboardingFlow
|
||||||
import im.vector.app.features.onboarding.OnboardingViewEvents
|
import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||||
|
import im.vector.app.features.onboarding.OnboardingViewModel
|
||||||
import im.vector.app.features.onboarding.OnboardingViewState
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
|
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
|
||||||
|
import reactivecircus.flowbinding.android.view.clicks
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class FtueAuthCombinedServerSelectionFragment :
|
class FtueAuthCombinedServerSelectionFragment : AbstractFtueAuthFragment<FragmentFtueServerSelectionCombinedBinding>() {
|
||||||
AbstractFtueAuthFragment<FragmentFtueServerSelectionCombinedBinding>() {
|
|
||||||
|
@Inject lateinit var stringProvider: StringProvider
|
||||||
|
private lateinit var controller: Controller
|
||||||
|
|
||||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueServerSelectionCombinedBinding {
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueServerSelectionCombinedBinding {
|
||||||
return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false)
|
return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false).also {
|
||||||
|
controller = Controller(viewLifecycleOwner, it, stringProvider, errorFormatter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setupViews()
|
controller.listener = object : Controller.Listener {
|
||||||
}
|
override fun onNavigationClicked() {
|
||||||
|
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnBack))
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupViews() {
|
override fun updateServerUrl() {
|
||||||
views.chooseServerRoot.realignPercentagesToParent()
|
viewModel.handle(OnboardingAction.HomeServerChange.EditHomeServer(views.chooseServerInput.content().ensureProtocol().ensureTrailingSlash()))
|
||||||
views.chooseServerToolbar.setNavigationOnClickListener {
|
}
|
||||||
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnBack))
|
|
||||||
}
|
override fun getInTouchClicked() {
|
||||||
views.chooseServerInput.associateContentStateWith(button = views.chooseServerSubmit, enabledPredicate = { canSubmit(it) })
|
openUrlInExternalBrowser(requireContext(), getString(R.string.ftue_ems_url))
|
||||||
views.chooseServerInput.setOnImeDoneListener {
|
|
||||||
if (canSubmit(views.chooseServerInput.content())) {
|
|
||||||
updateServerUrl()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
views.chooseServerGetInTouch.debouncedClicks { openUrlInExternalBrowser(requireContext(), getString(R.string.ftue_ems_url)) }
|
|
||||||
views.chooseServerSubmit.debouncedClicks { updateServerUrl() }
|
|
||||||
views.chooseServerInput.clearErrorOnChange(viewLifecycleOwner)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun canSubmit(url: String) = url.isNotEmpty()
|
|
||||||
|
|
||||||
private fun updateServerUrl() {
|
|
||||||
viewModel.handle(OnboardingAction.HomeServerChange.EditHomeServer(views.chooseServerInput.content().ensureProtocol().ensureTrailingSlash()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetViewModel() {
|
override fun resetViewModel() {
|
||||||
@ -80,13 +83,47 @@ class FtueAuthCombinedServerSelectionFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun updateWithState(state: OnboardingViewState) {
|
override fun updateWithState(state: OnboardingViewState) {
|
||||||
views.chooseServerHeaderSubtitle.setText(
|
controller.setData(state)
|
||||||
when (state.onboardingFlow) {
|
}
|
||||||
OnboardingFlow.SignIn -> R.string.ftue_auth_choose_server_sign_in_subtitle
|
|
||||||
OnboardingFlow.SignUp -> R.string.ftue_auth_choose_server_subtitle
|
override fun onError(throwable: Throwable) {
|
||||||
else -> throw IllegalStateException("Invalid flow state")
|
controller.setError(throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Controller(
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
|
private val views: FragmentFtueServerSelectionCombinedBinding,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val errorFormatter: ErrorFormatter,
|
||||||
|
private val viewModel: OnboardingViewModel,
|
||||||
|
) {
|
||||||
|
|
||||||
|
var listener: Listener? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
views.chooseServerRoot.realignPercentagesToParent()
|
||||||
|
views.chooseServerToolbar.setNavigationOnClickListener { listener?.onNavigationClicked() }
|
||||||
|
views.chooseServerInput.associateContentStateWith(button = views.chooseServerSubmit, enabledPredicate = { canSubmit(it) })
|
||||||
|
views.chooseServerInput.setOnImeDoneListener {
|
||||||
|
if (canSubmit(views.chooseServerInput.content())) {
|
||||||
|
listener?.updateServerUrl()
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
views.chooseServerGetInTouch.debouncedClicks(lifecycleOwner) { listener?.getInTouchClicked() }
|
||||||
|
views.chooseServerSubmit.debouncedClicks(lifecycleOwner) { listener?.updateServerUrl() }
|
||||||
|
views.chooseServerInput.clearErrorOnChange(lifecycleOwner)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canSubmit(url: String) = url.isNotEmpty()
|
||||||
|
|
||||||
|
fun setData(state: OnboardingViewState) {
|
||||||
|
views.chooseServerHeaderSubtitle.setText(
|
||||||
|
when (state.onboardingFlow) {
|
||||||
|
OnboardingFlow.SignIn -> R.string.ftue_auth_choose_server_sign_in_subtitle
|
||||||
|
OnboardingFlow.SignUp -> R.string.ftue_auth_choose_server_subtitle
|
||||||
|
else -> throw IllegalStateException("Invalid flow state")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (views.chooseServerInput.content().isEmpty()) {
|
if (views.chooseServerInput.content().isEmpty()) {
|
||||||
val userUrlInput = state.selectedHomeserver.userFacingUrl?.toReducedUrlKeepingSchemaIfInsecure() ?: viewModel.getDefaultHomeserverUrl()
|
val userUrlInput = state.selectedHomeserver.userFacingUrl?.toReducedUrlKeepingSchemaIfInsecure() ?: viewModel.getDefaultHomeserverUrl()
|
||||||
@ -97,12 +134,26 @@ class FtueAuthCombinedServerSelectionFragment :
|
|||||||
views.chooseServerInput.editText().showKeyboard(true)
|
views.chooseServerInput.editText().showKeyboard(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(throwable: Throwable) {
|
fun setError(throwable: Throwable) {
|
||||||
views.chooseServerInput.error = when {
|
views.chooseServerInput.error = when {
|
||||||
throwable.isHomeserverUnavailable() -> getString(R.string.login_error_homeserver_not_found)
|
throwable.isHomeserverUnavailable() -> stringProvider.getString(R.string.login_error_homeserver_not_found)
|
||||||
else -> errorFormatter.toHumanReadable(throwable)
|
else -> errorFormatter.toHumanReadable(throwable)
|
||||||
|
}
|
||||||
|
println(views.chooseServerInput.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.toReducedUrlKeepingSchemaIfInsecure() = toReducedUrl(keepSchema = this.startsWith("http://"))
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun onNavigationClicked()
|
||||||
|
fun updateServerUrl()
|
||||||
|
fun getInTouchClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private fun String.toReducedUrlKeepingSchemaIfInsecure() = toReducedUrl(keepSchema = this.startsWith("http://"))
|
|
||||||
|
private fun View.debouncedClicks(lifecycleOwner: LifecycleOwner, onClicked: () -> Unit) {
|
||||||
|
clicks()
|
||||||
|
.onEach { onClicked() }
|
||||||
|
.launchIn(lifecycleOwner.lifecycleScope)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user