-
Notifications
You must be signed in to change notification settings - Fork 28
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
feat: pre-login mobile app exploration #86
feat: pre-login mobile app exploration #86
Conversation
6fb0f0c
to
a5a678f
Compare
config.yaml
Outdated
#feature flag for activating pre_login experience | ||
preLoginExperienceEnabled: true |
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.
Most of the configurations are in SCREAMING_SNAKE_CASE
better to adopt.
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 should decide on the standards first as the above mentioned flags are also written in CAMEL_CASE
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.
@volodymyr-chekyrta thoughts?
when { | ||
org.openedx.core.BuildConfig.PRE_LOGIN_EXPERIENCE_ENABLED -> | ||
addFragment(PreAuthFragment()) | ||
|
||
else -> addFragment(SignInFragment()) | ||
} | ||
} | ||
|
||
shouldShowWhatsNew() -> addFragment(WhatsNewFragment()) | ||
corePreferencesManager.user != null -> addFragment(MainFragment()) |
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.
IMO, better to wrap all the cases with {}
even for the single liners, to make the code format consistent.
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.
pending.
when { | ||
org.openedx.core.BuildConfig.PRE_LOGIN_EXPERIENCE_ENABLED -> | ||
addFragment(PreAuthFragment()) | ||
|
||
else -> addFragment(SignInFragment()) | ||
} |
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.
currently only have binary conditions so better to use if-else.
searchValue = textFieldValue, | ||
keyboardActions = { | ||
focusManager.clearFocus() | ||
onSearchClick(textFieldValue.text) |
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 learner should navigate to CourseSearchFragment
directly when the user clicks the search button.
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.
Also, there is the infinite search progress on CourseSearchFragment
.
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.
Let us decide as there is no decision on how we gonna handle the backstack.
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.
As decided in the meeting current flow is expected.
private fun PreLoginScreen( | ||
onSearchClick: (query: String) -> Unit, | ||
onRegisterClick: () -> Unit, | ||
onLoginClick: () -> Unit, |
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.
IMO, onSigninClick
is more appropriate here.
if (BuildConfig.PRE_LOGIN_EXPERIENCE_ENABLED.not()) { | ||
Text( | ||
modifier = Modifier.noRippleClickable { | ||
onRegisterClick() | ||
}, | ||
text = stringResource(id = R.string.auth_register), | ||
color = MaterialTheme.appColors.primary, | ||
style = MaterialTheme.appTypography.labelLarge | ||
) | ||
} |
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.
IMO, we can revert this to allow the learner to navigate to Registration screen from Signin screen.
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.
It was part of the ticket scope
auth/src/main/res/values/strings.xml
Outdated
@@ -1,5 +1,9 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<resources> | |||
<resources xmlns:tools="http://schemas.android.com/tools"> | |||
<string name="pre_auth_title" tools:ignore="ExtraTranslation">Courses and Programs from the world\'s best universities in your pocket.</string> |
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.
P
should be in the small letter to match with the production.
LaunchedEffect(key1 = uiState) { | ||
if (querySearch.isNotEmpty()) { | ||
router.navigateToCourseSearch( | ||
requireActivity().supportFragmentManager, querySearch | ||
) | ||
arguments?.let { | ||
it.putString(ARG_SEARCH_QUERY, "") | ||
} | ||
} | ||
} |
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.
no need once the learner directly navigates to search course screen.
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.
current flow is good for now.
LaunchedEffect(key1 = uiState) { | ||
viewModel.search(querySearch) | ||
} |
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 might be the cause of infinite progress.
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.
fixed
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.PaddingValues | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.navigationBarsPadding | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.width | ||
import androidx.compose.foundation.layout.widthIn |
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.
IMO, we should decide coding standards for the repo, to reduce such optimizations and code changes.
thoughts @volodymyr-chekyrta, @k1rill, @HamzaIsrar12
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.
IMO, better to go with the default studio Formatter | Linter. Open for suggestions.
Agree with the feedback provided by @farhan-arshad-dev. |
contentDescription = null, | ||
modifier = Modifier | ||
.padding(top = 32.dp, bottom = 32.dp) | ||
.width(170.dp) |
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.
batter to update with .wrapContentWidth()
.
painter = painterResource(id = org.openedx.core.R.drawable.core_ic_logo), | ||
contentDescription = null, | ||
modifier = Modifier | ||
.padding(top = 32.dp, bottom = 32.dp) |
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 top space should be 90dp
in total, to match with the production.
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 bottom space should be 20dp
in total, to match with the production.
text = stringResource(id = R.string.pre_auth_title), | ||
color = MaterialTheme.appColors.textPrimary, | ||
style = MaterialTheme.appTypography.headlineSmall, | ||
modifier = Modifier.padding(bottom = 32.dp) |
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 bottom space should be 40dp
in total, to match with the production.
style = MaterialTheme.appTypography.titleMedium, | ||
text = stringResource(id = R.string.pre_auth_search_title), | ||
) | ||
SearchBar( |
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.
bottom space should be 10dp
in total.
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) | ||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
@Composable | ||
private fun SignInScreenPreview() { |
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.
better to add the preview for Landscape|Tablet mode.
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.
Preview added for Tablet Mode, no need for landscape mode.
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.
Some concerns for @volodymyr-chekyrta, and some code-related nits, other than that LGTM.
@@ -183,6 +211,21 @@ internal fun DiscoveryScreen( | |||
|
|||
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState) | |||
|
|||
if (BuildConfig.PRE_LOGIN_EXPERIENCE_ENABLED) { |
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.
when { | ||
org.openedx.core.BuildConfig.PRE_LOGIN_EXPERIENCE_ENABLED -> | ||
addFragment(PreAuthFragment()) | ||
|
||
else -> addFragment(SignInFragment()) | ||
} | ||
} | ||
|
||
shouldShowWhatsNew() -> addFragment(WhatsNewFragment()) | ||
corePreferencesManager.user != null -> addFragment(MainFragment()) |
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.
pending.
@@ -379,6 +376,10 @@ private fun CourseSearchScreen( | |||
} | |||
} | |||
} | |||
var isPageLoaded = rememberSaveable { true } |
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.
replace with val
import org.openedx.core.ui.theme.appColors | ||
import org.openedx.core.ui.theme.appTypography | ||
|
||
class PreAuthFragment : Fragment() { |
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.
Previously we called this screen Logistration
, IMO, better to update the package, and class name accordingly.
auth/src/main/java/org/openedx/auth/presentation/preAuth/PreAuthFragment.kt
Outdated
Show resolved
Hide resolved
auth/src/main/java/org/openedx/auth/presentation/preAuth/PreAuthFragment.kt
Outdated
Show resolved
Hide resolved
discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryFragment.kt
Outdated
Show resolved
Hide resolved
@k1rill @farhan-arshad-dev @HamzaIsrar12, |
|
||
@Composable | ||
private fun PreLoginScreen( | ||
onSearchClick: (query: String) -> Unit, |
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.
it's better to use sealed interface here for example:
sealed interface OnClick {
data class Search(query: String): OnClick
object Registration: OnClick
....
}
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.
Currently, we are not following the mentioned approach.
@volodymyr-chekyrta your thoughts on 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.
will leave this as nit
auth/src/main/java/org/openedx/auth/presentation/logistration/Logistration.kt
Outdated
Show resolved
Hide resolved
@@ -197,7 +203,21 @@ private fun LoginScreen( | |||
uiMessage = uiMessage, | |||
scaffoldState = scaffoldState | |||
) | |||
|
|||
if (BuildConfig.PRE_LOGIN_EXPERIENCE_ENABLED) { |
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.
It's better to move config out of UI, for example in the UI state and init it in viewModel
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.
Hey @k1rill, we can do that but in my opinion, are there any benefits to taking such a long route for a single flag? is there inspiration or reference available?
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.
@omerhabib26 we are trying to build Clean architecture with SOLID principles. UI shouldn't work with logic directly. Config can't be changed runtime at this moment, so we should init it once on the ViewModel init. Also when the new configuration will be merged, we have to update this code, it's better to update it once inside viewModel, instead of searching all entries. Much easier to make this route long while development, than to change it a lot of times while maintaining the code
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.
updated
9d75c21
to
ed996b5
Compare
@Preview(name = "NEXUS_9_Light", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_NO) | ||
@Preview(name = "NEXUS_9_Night", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
@Composable | ||
private fun SignInScreenPreview() { |
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.
Please rename SignInScreen
. nit: Also there are 2 different names on this screen, fragment is called Logistration
, and main Composable is called PreLoginScreen
@k1rill ready for another pass |
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.
@omerhabib26 good job!
@volodymyr-chekyrta the PR is approved now you can merge it. 🚀 |
@@ -44,7 +45,11 @@ class SignInViewModel( | |||
val appUpgradeEvent: LiveData<AppUpgradeEvent> | |||
get() = _appUpgradeEvent | |||
|
|||
private val _isLogistrationEnabled = SingleEventLiveData<Boolean>() |
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.
SingleEventLiveData
leads to the app crash, better to update with MutableLiveData
.
@@ -110,10 +117,34 @@ class DiscoveryFragment : Fragment() { | |||
requireParentFragment().parentFragmentManager, |
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 following steps lead to the app crash.
- Open the app user redirect to the
Logistration
screen. - Click on the
Explore all courses
redirect to the Discover screen. - Click on the course card, 💥 app crashed.
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.
It was not the part of the ticket scope but I have fixed it
@@ -46,6 +47,9 @@ class DiscoveryViewModel( | |||
val appUpgradeEvent: LiveData<AppUpgradeEvent> | |||
get() = _appUpgradeEvent | |||
|
|||
private val _isLogistrationEnabled = SingleEventLiveData<Boolean>() |
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.
SingleEventLiveData
leads to the app crash at some point.
@omerhabib26, I found one problem, could you please check why it is crashing? |
Also, there is a crash when trying to open any course. |
4229af3
to
6a42e9c
Compare
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 improvement, other than that looks good to me.
val isLogistrationEnabled by viewModel.isLogistrationEnabled.observeAsState(false) | ||
val canShowBackButton = isLogistrationEnabled && corePreferencesManager.user == null |
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 is no other use of corePreferencesManager
so better to move inside the DiscoveryViewModel
.
import org.openedx.core.ui.theme.appColors | ||
import org.openedx.core.ui.theme.appTypography | ||
|
||
class Logistration : Fragment() { |
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.
To keep consistency with other fragments, it would be great to rename it to LogistrationFragment.
Same to the file name.
No other issues; we can merge it after renaming the Logistration file and resolving conflicts. |
- Feature is implemented behind a feature flag `preLoginExperienceEnabled` - User will be able to navigate to Signup, Signin, DiscoveryCourses & SearchCourses screen - Back button is added on required screens for back navigation - Navigation from SignIn to Signup is removed fix: LEARNER-9663
749464a
to
50f3a35
Compare
@volodymyr-chekyrta @k1rill @farhan-arshad-dev PR is updated according to the new Config architecture. please have a look |
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.
LGTM 🚀
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.
Some Nits
No need file config.yaml
, can be deleted.
After that, I'll approve the PR.
private val _isLogistrationEnabled = MutableLiveData<Boolean>() | ||
val isLogistrationEnabled: LiveData<Boolean> = _isLogistrationEnabled |
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.
better to replace this with the getter.
@@ -51,6 +50,10 @@ class Config(context: Context) { | |||
return getBoolean(WHATS_NEW_ENABLED, false) | |||
} | |||
|
|||
fun isPreLoginExperienceEnabled(): Boolean { | |||
return getBoolean(PRE_LOGIN_EXPERIENCE_ENABLED, true) |
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.
better to change the default value with false
.
* feat: pre-login mobile app exploration - Feature is implemented behind a feature flag `preLoginExperienceEnabled` - User will be able to navigate to Signup, Signin, DiscoveryCourses & SearchCourses screen - Back button is added on required screens for back navigation - Navigation from SignIn to Signup is removed fix: LEARNER-9663 * fix: Address PR comments * fix: Address PR comments - 2 * fix: Address PR comments * fix: resolve test cases * fix: Address PR comments + crash fix * fix: App crash on Discovery Fragment * fix: small code improvement * fix: Resolve conflicts + Update code according to new config architecture * fix: addressed minor nits
Description
This PR adds support for pre-login exploration.
preLoginExperienceEnabled
Here is the video of the implemented feature
prelogin-experience.mp4