-
Notifications
You must be signed in to change notification settings - Fork 731
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 - Capability based personalisation flow #5375
FTUE - Capability based personalisation flow #5375
Conversation
Matrix SDKIntegration Tests Results:
|
c3c193d
to
147de6e
Compare
import androidx.appcompat.widget.AppCompatSpinner | ||
import im.vector.app.R | ||
|
||
class OverrideDropdownView @JvmOverloads constructor( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a reusable dropdown view for the Debug menu -> Private settings
(in the debug sourceset)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! 👍 I am a big fan of custom views!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I did not review the PR, just add 2 small comments 🙈 !)
|
||
<Button | ||
android:id="@+id/accountCreatedTakeMeHomeCta" | ||
style="@style/Widget.Vector.Button.Login" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a handy Widget.Vector.Button.CallToAction
but it's used much (it's quite new). I am wondering if we should at some point use this style a bit more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to use the CTA style, especially when it's defined in the components library https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1970%3A25160
I'm tempted to update all the usages in one go in a separate PR rather than just this layout, what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a plan! (that was my original intention to make you think that 👿 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I have only done static code review. Only small remarks and questions.
Is there a doc I can find somewhere to learn how to test account creation with a local homeserver?
import kotlinx.coroutines.flow.map | ||
import org.matrix.android.sdk.api.extensions.orFalse | ||
|
||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vector_overrides") | ||
private val keyForceDialPadDisplay = booleanPreferencesKey("force_dial_pad_display") | ||
private val keyForceLoginFallback = booleanPreferencesKey("force_login_fallback") | ||
private val forceCanChangeDisplayName = booleanPreferencesKey("force_can_change_display_name") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have never used DataStore
API but I see this so much cleaner than the SharedPreferences
API. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the main "downside" is that the api forces us to use suspend functions or flows (maybe a good thing from a performance standpoint 😄 )
@@ -51,4 +62,18 @@ class DebugVectorOverrides(private val context: Context) : VectorOverrides { | |||
settings[keyForceLoginFallback] = force | |||
} | |||
} | |||
|
|||
suspend fun updateHomeserverCapabilities(block: HomeserverCapabilitiesOverride.() -> HomeserverCapabilitiesOverride) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could rename it to setHomeserverCapabilities
to keep the same convention as the other methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, will do 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sealed interface DebugPrivateSettingsViewActions : VectorViewModelAction { | ||
data class SetDialPadVisibility(val force: Boolean) : DebugPrivateSettingsViewActions | ||
data class SetForceLoginFallbackEnabled(val force: Boolean) : DebugPrivateSettingsViewActions | ||
data class SetDisplayNameCapabilityOverride(val option: BooleanHomeserverCapabilitiesOverride?) : DebugPrivateSettingsViewActions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am just wondering why is the option BooleanHomeserverCapabilitiesOverride
a nullable? Is this a case we should handle? I thought it would be either enabled or disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay it makes sense. I forgot it is an override setting.
@@ -51,4 +62,18 @@ class DebugVectorOverrides(private val context: Context) : VectorOverrides { | |||
settings[keyForceLoginFallback] = force | |||
} | |||
} | |||
|
|||
suspend fun updateHomeserverCapabilities(block: HomeserverCapabilitiesOverride.() -> HomeserverCapabilitiesOverride) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if we should switch to Dispatchers.io
for write operations? Right now it uses implicitly the Dispatcher.main.immediate
from the viewModelScope
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah good point, I was under the assumption the datastore api would internally switch, I'll double check!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can confirm we're okay https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:datastore/datastore-core/src/main/java/androidx/datastore/core/SingleProcessDataStore.kt;l=88
under the hood there's an IO scope created
init { | ||
orientation = HORIZONTAL | ||
gravity = Gravity.CENTER_VERTICAL | ||
inflate(context, R.layout.view_boolean_dropdown, this) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want you could migrate to ViewBinding
like in this custom View.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do, thanks for the example 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val canChangeDisplayName: Boolean?, | ||
val canChangeAvatar: Boolean? | ||
) | ||
|
||
class DefaultVectorOverrides : VectorOverrides { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a little confused about the purpose of DefaultVectorOverrides
class. I see it is only used for testing purpose. Am I right? If it is the case, we may move it to the FakeVectorOverrides
which is currently not used. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the missing piece of the puzzle here is that the default implementations are injected by the release variant of the app release module whereas the debug module allows for providing the runtime overridable version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay thanks for explaining.
is OnboardingAction.UpdateDisplayName -> updateDisplayName(action.displayName) | ||
OnboardingAction.UpdateDisplayNameSkipped -> _viewEvents.post(OnboardingViewEvents.OnDisplayNameSkipped) | ||
OnboardingAction.UpdateDisplayNameSkipped -> handleDisplayNameStepComplete() | ||
OnboardingAction.UpdateProfilePictureSkipped -> _viewEvents.post(OnboardingViewEvents.OnPersonalizationComplete) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we could create a method onPersonalizationComplete
to be used each time we need to post the event OnboardingViewEvents.OnPersonalizationComplete
. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
happy to extract 👍 the reason I left it inlined was because there was no additional logic needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -42,8 +44,15 @@ class FtueAuthAccountCreatedFragment @Inject constructor( | |||
|
|||
private fun setupViews() { | |||
views.accountCreatedSubtitle.text = getString(R.string.ftue_account_created_subtitle, activeSessionHolder.getActiveSession().myUserId) | |||
views.accountCreatedPersonalize.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnPersonalizeProfile)) } | |||
views.accountCreatedPersonalize.debouncedClicks { viewModel.handle(OnboardingAction.PersonalizeProfile) } | |||
views.accountCreatedTakeMeHome.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome)) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are there 2 buttons accountCreatedTakeMeHome
and accountCreatedTakeMeHomeCta
? Is it to handle the 2 cases: can personalize and cannot?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I would vote to have separate views, it's easier to apply different style in the layout, and also the action are not the same so could be more confusing if we update the text and the action depending on the state)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay for me as well to have 2 different views.
import im.vector.app.features.DefaultVectorOverrides | ||
import im.vector.app.features.VectorOverrides | ||
|
||
class FakeVectorOverrides : VectorOverrides by DefaultVectorOverrides() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is not used, is it normal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦 nope, it's meant to be used here https://github.com/vector-im/element-android/pull/5375/files#diff-9bd5641dbabf55509a3dea8218d6d7d326781e4a23bf46867ec249eb640de34aR267
functionally the same but the abstraction enables reuse, will update 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is not used, is it normal?
good catch!
} else { | ||
navigateToHome(createdAccount = true) | ||
} | ||
activity.supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an idea, it would be cool if we could encapsulate all navigation code into a dedicated interface. It abstracts the navigation implementation and reduce the component responsability. Of course I don't suggest to change it in this PR 😄 But maybe later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have one for activities but not fragments, I think it's great idea and would help towards making this class testable
I've tracked it as an issue #5508
there's nothing specific needed for testing account creation from the synapse standpoint, the easiest way to get setup is to run the demo script https://github.com/matrix-org/synapse#quick-start |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't achieve to run the Synapse demo server on local (some strange config errors when starting server) to test the code. But I approve the PR since I don't see anything wrong in static.
@mnaturel thanks for the review 👍 (and trying to run synapse locally, I'm happy to help out if you want to give it another go) |
- cases for the personalisation and display name actions
- moves the personalisation state to a dedicated model to allow for back and forth state restoration
…erver supports personalisation
…ser home if the display name personalisation is not supported
…f the homeserver - when avatars can't be changed we complete the personlisation flow
…'t supported but pictures are when personalising the profile
… if personalization is supported - also removes a legacy loading workaround for the account creation step, we're navigating to a new screen AccountCreated so we have to stop the loading
Co-authored-by: Benoit Marty <[email protected]>
21222c1
to
c06d3ff
Compare
Type of change
Content
Draft PR, relies on #5361 #5323
Dynamically shows/hides the personalisation onboarding screen based on the homeservers capabilities.
when display name and profile picture are not supported
Take me home
buttonwhen only changing display name is supported
when only changing profile picture is supported
Also introduces debug overrides for the homeserver capabilities to make testing the different options easier.
Motivation and context
To only show personalisation options to users on homeservers which support them. Part of #5200
Screenshots / GIFs
Tests
Tested devices