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

FTUE - Server selection #5691

Merged
merged 20 commits into from
Apr 8, 2022
Merged

FTUE - Server selection #5691

merged 20 commits into from
Apr 8, 2022

Conversation

ouchadam
Copy link
Contributor

@ouchadam ouchadam commented Apr 4, 2022

Type of change

  • WIP Feature
  • Bugfix
  • Technical
  • Other :

Content

Fixes #2396 - Is behind the combined registration feature flag #5648

  • Adds a new server selection screen
  • Moves the homeserver selection related state to a dedicated state class
  • Extracts the authentication state logic to a use case with its own set of tests
  • URLs which use http:// do not have their prefix removed when editing the server url (as a way to remind the user they're insecure)

Motivation and context

Introduces a new server selection screen where the user can enter a homeserver url, the aim is to centralise the flow avoiding branching paths (simplifying the overall experience)

Screenshots / GIFs

Note - some screenshots are missing the EMS hyperlink

Interactions

DISMISS 3RD PARTY MATRIX.ORG HTTP CREATE ACCOUNT
after-dismiss after-3rd-party after-select-matrix-org after-http-selection after-create-account
EMS GET IN TOUCH EMS HYPERLINK
after-get-in-touch after-clicking-link

Theming

LIGHT DARK
Screenshot_20220404_162336 Screenshot_20220404_162345
Screenshot_20220404_162656 Screenshot_20220404_162649

Devices

SMALL Screenshot_20220404_163302 Screenshot_20220404_163306
PIXEL 4 XL Screenshot_20220404_162103 N/A
TABLET Screenshot_20220404_162201 Screenshot_20220404_162151

Tests

  • Enabled the combined registration flag
  • Create start the create new account flow
  • Tap edit in the register screen

Tested devices

  • Physical
  • Emulator
  • OS version(s): 29, 31Sv2

@ouchadam ouchadam changed the title Feature/adm/ftue server selection FTUE - Server selection Apr 4, 2022
@github-actions
Copy link

github-actions bot commented Apr 4, 2022

Unit Test Results

114 files  +  4  114 suites  +4   1m 29s ⏱️ +8s
201 tests +  6  201 ✔️ +  6  0 💤 ±0  0 ±0 
674 runs  +24  674 ✔️ +24  0 💤 ±0  0 ±0 

Results for commit 5f9d3e1. ± Comparison against base commit 15cf065.

♻️ This comment has been updated with latest results.

@ouchadam ouchadam force-pushed the feature/adm/ftue-server-selection branch 2 times, most recently from 9ed5786 to 3886c42 Compare April 5, 2022 11:58
@ouchadam ouchadam added the X-Needs-Design May require input from the design team label Apr 5, 2022
@ouchadam ouchadam requested a review from amshakal April 5, 2022 12:43
@ouchadam ouchadam marked this pull request as ready for review April 5, 2022 12:43
@ouchadam ouchadam added the PR-Large PR with more than 500 updated lines label Apr 6, 2022
@@ -139,7 +140,8 @@ class OnboardingViewModel @AssistedInject constructor(
is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
is OnboardingAction.InitWith -> handleInitWith(action)
is OnboardingAction.UpdateHomeServer -> handleUpdateHomeserver(action).also { lastAction = action }
is OnboardingAction.SelectHomeServer -> run { lastAction = action }.also { handleHomeserverChange(action.homeServerUrl) }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actions are set straight away as setting them afterwards implicitly relies on asynchronous coroutines (which isn't the case for tests!)

homeServerUrlFromUser = null,
homeServerUrl = null,
loginMode = LoginMode.Unknown,
serverType = ServerType.Unknown,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resetting the server url shouldn't also reset the server type as the server type is actually the user selection from the "Matrix, EMS, Other" page

@ouchadam ouchadam requested review from a team and ariskotsomitopoulos and removed request for a team April 6, 2022 08:12
@@ -70,6 +61,15 @@ enum class OnboardingFlow {
SignInSignUp
}

@Parcelize
data class SelectedHomeserverState(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the individual homeserver state has been bundled into a single SelectedHomeserverState, this object is created/updated when starting the authentication flow (by selecting or editing a homeserver url) through a single entry point StartAuthenticationFlowUseCase.kt

Copy link

@amshakal amshakal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! 💯

Copy link
Contributor

@ariskotsomitopoulos ariskotsomitopoulos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the large PR, good job for adding tests, I have some remarks!

@@ -18,3 +18,5 @@ package im.vector.app.core.extensions

inline fun <reified T> List<T>.nextOrNull(index: Int) = getOrNull(index + 1)
inline fun <reified T> List<T>.prevOrNull(index: Int) = getOrNull(index - 1)

fun <T> List<T>.containsAll(vararg items: T) = this.containsAll(items.toList())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid extension functions with the exact name as the original, I don't have a strong point though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was imagining this as an overload for containsAll but I agree it can be tricky to discover the extension, will rename

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good!

@@ -139,7 +140,8 @@ class OnboardingViewModel @AssistedInject constructor(
is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
is OnboardingAction.InitWith -> handleInitWith(action)
is OnboardingAction.UpdateHomeServer -> handleUpdateHomeserver(action).also { lastAction = action }
is OnboardingAction.SelectHomeServer -> run { lastAction = action }.also { handleHomeserverChange(action.homeServerUrl) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe its worth create a function to wrap duplicate lines:

 run { lastAction = action }.also { handleHomeserverChange(action.homeServerUrl) }
 run { lastAction = action }.also { handleHomeserverChange(action.homeServerUrl) }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great idea! I can extract the run { lastAction = action } but the handleHomeserverChange(action.homeServerUrl) is a bit trickier as we need to pull the server url from two different action types SelectHomeServer.homeServerUrl & EditHomeServer.homeServerUrl

could be a good case for a sealed interface with a common property, will have a play around!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice approach with the sealed interface, I like it

data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
else -> LoginMode.Unsupported
}
runCatching { startAuthenticationFlowUseCase.execute(homeServerConnectionConfig) }.fold(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using withContext(Dispatchers.IO) to startAuthenticationFlowUseCase will reduce the work on main thread. Or maybe using viewModelScope.launch(Dispatchers.IO) and exclude state updates to run on Main dispatcher

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about this, the authentication service has a bunch of internal non thread safe state, we can rely on the lower layers (retrofit/awaitTransaction) for doing the dispatcher switching for us, at least whilst the other parts aren't thread safe

but it does mean there's some thread jumping happening~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was talking mainly for the logic that is running on the main thread, except retrofit calls etc that always run on another thread. But if we have thread safety issues, there are not much we can do!

return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, I would have created some more methods instead of having all the login in onViewCreated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

views.loginSignupSigninText.text = getString(R.string.login_server_matrix_org_text)
}
ServerType.EMS -> {
views.loginSignupSigninServerIcon.setImageResource(R.drawable.ic_logo_element_matrix_services)
views.loginSignupSigninServerIcon.isVisible = true
views.loginSignupSigninTitle.text = getString(R.string.login_connect_to_modular)
views.loginSignupSigninText.text = state.homeServerUrlFromUser.toReducedUrl()
views.loginSignupSigninText.text = state.selectedHomeserver.userFacingUrl.toReducedUrl()
}
ServerType.Other -> {
views.loginSignupSigninServerIcon.isVisible = false
views.loginSignupSigninTitle.text = getString(R.string.login_server_other_title)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need setting the icon in ServerType.Other case?
views.loginSignupSigninServerIcon.setImageResource(R.drawable.ic_logo_element_matrix_services) or maybe set it to null?

Copy link
Contributor Author

@ouchadam ouchadam Apr 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the other case we're hiding the icon with views.loginSignupSigninServerIcon.isVisible = false, this icon is the one under the element logo

OTHER MATRIX.ORG
other-flow matrix-icon

also worth mentioning these screens are part of the legacy flow and will eventually be removed when the FTUE becomes the only flow 🤞

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, for explaining

@@ -60,27 +60,27 @@ class FtueAuthSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOF
ServerType.MatrixOrg -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extremely minor, maybe its worth creating a function like:

    private fun update(@DrawableRes drawableId: Int,
                       isServerIconVisible: Boolean,
                       signInTitle: String,
                       signInText: String
                       ){
        views.loginSignupSigninServerIcon.setImageResource(drawableId)
        views.loginSignupSigninServerIcon.isVisible = isServerIconVisible
        views.loginSignupSigninTitle.text = signInTitle
        views.loginSignupSigninText.text = signInText
    }

to reduce code duplication

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

<string name="ftue_auth_choose_server_entry_footer">You can only connect to a server that has already been set up</string>
<string name="ftue_auth_choose_server_ems_title">Want to host your own server?</string>

<string name="ftue_ems_url">https://element.io/ems</string>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add this to vector-config urls.xml ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@@ -58,6 +62,9 @@ private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplay
private val AN_IGNORED_FLOW_RESULT = FlowResult(missingStages = emptyList(), completedStages = emptyList())
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationResult.FlowResponse(AN_IGNORED_FLOW_RESULT)
private val A_LOGIN_OR_REGISTER_ACTION = OnboardingAction.LoginOrRegister("@a-user:id.org", "a-password", "a-device-name")
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
private val SELECTED_HOMESERVER_STATE = SelectedHomeserverState(preferredLoginMode = LoginMode.Password)

class OnboardingViewModelTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for adding Tests!

@ouchadam ouchadam force-pushed the feature/adm/ftue-server-selection branch from 05bc538 to 5f9d3e1 Compare April 8, 2022 12:18
@ouchadam ouchadam removed the X-Needs-Design May require input from the design team label Apr 8, 2022
@ouchadam ouchadam merged commit bd3e980 into develop Apr 8, 2022
@ouchadam ouchadam deleted the feature/adm/ftue-server-selection branch April 8, 2022 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR-Large PR with more than 500 updated lines
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mobile FTUE: Choose Server (Android) | "Hard to find where to enter custom server name"
3 participants