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

Storing and tracking the onboarding messaging use case #5009

Merged
merged 10 commits into from
Jan 31, 2022

Conversation

ouchadam
Copy link
Contributor

Part of #4585

  • Adds storing and tracking of the selected messaging use case as part of the onboarding journey (this will eventually be used to better tailor the first time experience)

  • Adds VectorAnalytics.updateUserProperties to allow for updating the identity/user properties outside of the analytics id/consent flow, eg locally capturing the onboarding selection and only sending after the user has consented.Posthog.Identify() accumulates a local cache which is only sent once a user has consented Posthog.OptOut(false)

@ouchadam ouchadam added Z-FTUE Issue is relevant to the first time use project or experience Z-Tracking labels Jan 20, 2022
@ouchadam ouchadam mentioned this pull request Jan 20, 2022
6 tasks
@ouchadam
Copy link
Contributor Author

marked as draft whilst confirming if the identity payload is coming through

@github-actions
Copy link

github-actions bot commented Jan 20, 2022

Unit Test Results

  72 files  ±0    72 suites  ±0   1m 1s ⏱️ ±0s
141 tests ±0  141 ✔️ ±0  0 💤 ±0  0 ±0 
440 runs  ±0  440 ✔️ ±0  0 💤 ±0  0 ±0 

Results for commit cbdeb54. ± Comparison against base commit 38a6c3e.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Jan 20, 2022

Matrix SDK

Integration Tests Results:

  • [org.matrix.android.sdk.session]
    passed="21" failures="0" errors="0" skipped="2"
  • [org.matrix.android.sdk.account]
    passed="5" failures="0" errors="0" skipped="2"
  • [org.matrix.android.sdk.internal]
    passed="158" failures="1" errors="0" skipped="38"
  • [org.matrix.android.sdk.ordering]
    passed="16" failures="0" errors="0" skipped="0"
  • [org.matrix.android.sdk.PermalinkParserTest]
    passed="2" failures="0" errors="0" skipped="0"

Copy link
Member

@bmarty bmarty left a comment

Choose a reason for hiding this comment

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

LGTM, some remarks

}
}

fun FtueUseCase.toTrackingValue(): Identity.FtueUseCaseSelection {
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do the same (in a separate FtueUseCaseExt.kt file) 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

import kotlinx.coroutines.flow.first
import javax.inject.Inject

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vector_onboarding")
Copy link
Member

Choose a reason for hiding this comment

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

I wonder how we will manage mutli accounts, if we manage it one day.
We should probably have scoped pref: global / per session.
Just thinking out loud.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that's a good point, delaying the storing until after sign up makes sense to me, then we can associate to a session/account

I'm happy to try converting this to a realm entity in the global database (by user)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

assuming we're happy to avoid using a database for the time being~ I've updated the onboarding store to be by user id efbdd70

/**
* Update user specific properties
*/
fun updateUserProperties(identity: Identity)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bmarty I'll also move this to the AnalyticsTracker

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ouchadam ouchadam force-pushed the feature/adm/storing-use-case branch from 0222a33 to efbdd70 Compare January 21, 2022 13:50
@ouchadam ouchadam marked this pull request as ready for review January 21, 2022 14:25
class OnboardingStore @Inject constructor(
private val context: Context
) {
suspend fun readUseCase(userId: String) = context.dataStore.data.first().let { preferences ->
Copy link
Member

Choose a reason for hiding this comment

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

Q: is it possible to have a OnBoardingStore scoped to the session to avoid passing the userId to each of its fun?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we could do it for the MainActivity injection site (for cleaning up the local storage) but not the OnboardingViewModel as the session doesn't exist yet

alternatively we could have a provider/factory as a session extension instead of injecting the dependency, what do you think?

Session.onboardingStore(context: Context) = OnboardingStore(context, myUserId)

Copy link
Member

Choose a reason for hiding this comment

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

this looks neat!

@ouchadam ouchadam force-pushed the feature/adm/storing-use-case branch from ff109c0 to bcb065a Compare January 25, 2022 17:54
}

private fun String.toUseCaseKey() = stringPreferencesKey("$this-use_case")
}
Copy link
Member

Choose a reason for hiding this comment

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

This class could be simplified with:

class OnboardingStore(
        private val context: Context,
        myUserId: String
) {
    private val useCasePreferences = stringPreferencesKey("$myUserId-use_case")

    suspend fun readUseCase() = context.dataStore.data.first().let { preferences ->
        preferences[useCasePreferences]?.let { FtueUseCase.from(it) }
    }

    suspend fun setUseCase(useCase: FtueUseCase) {
        context.dataStore.edit { settings ->
            settings[useCasePreferences] = useCase.persistableValue
        }
    }

    suspend fun resetUseCase() {
        context.dataStore.edit { settings ->
            settings.remove(useCasePreferences)
        }
    }

    suspend fun clear() {
        resetUseCase()
    }
}

and removeKeysWithPrefix is not necessary since we know have a new per userId store object.

I am also wondering if we could make it more generic, so its goal will be to store any data app side related to the session. I have in mind the storage of a client secret for matrix-org/sygnal#70.

The class name could be something like SessionDataStore (open for better ideas!)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sounds good to me 👍

Copy link
Contributor Author

@ouchadam ouchadam Jan 26, 2022

Choose a reason for hiding this comment

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

went with VectorSessionStore 64c18ac

@ouchadam ouchadam force-pushed the feature/adm/storing-use-case branch from 42f5ba8 to 64c18ac Compare January 26, 2022 09:18
@ouchadam ouchadam force-pushed the feature/adm/storing-use-case branch from 64c18ac to bc37391 Compare January 26, 2022 09:35
) {

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vector_session_store_$myUserId")
Copy link
Member

Choose a reason for hiding this comment

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

I am wondering if directly using the userId may lead to some trouble: special chars - low risk, length - higher risk. Maybe using a hash of it would be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

very good point, I keep forgetting that user-id is actually a matrix id rather than a uuid, will change

Copy link
Member

Choose a reason for hiding this comment

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

Yes. "Matrix Id" is not really a specific concept, but it is sometimes used for user ids. https://spec.matrix.org/v1.1/appendices/#identifier-grammar is an interesting reading on the subject

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ouchadam ouchadam requested a review from bmarty January 27, 2022 12:14
Copy link
Member

@bmarty bmarty left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for all the updates!

@ouchadam ouchadam merged commit 986d9f9 into develop Jan 31, 2022
@ouchadam ouchadam deleted the feature/adm/storing-use-case branch January 31, 2022 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Z-FTUE Issue is relevant to the first time use project or experience Z-Tracking
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants