From 43a5b9cb14246cec1e7fd51de9007fbc4f14eeb9 Mon Sep 17 00:00:00 2001 From: Farhan Arshad Date: Fri, 26 Jan 2024 10:53:25 +0500 Subject: [PATCH] feat: WebView discovery capabilities for Pre-Login Experience MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Integrate search to align with configured discovery settings. - “Explore All Courses" redirects based on the feature discovery flag. - Auth Panel (SignIn, Register) remains visible during pre-login webview discovery. - Ensure Sign In & Enroll mirror Native Discovery & Market App functionality. - Clear edit text after submit Logistration. - Fix test cases for View Models. LEARNER-9798 --- .../main/java/org/openedx/app/AppRouter.kt | 23 +++-- .../main/java/org/openedx/app/MainFragment.kt | 23 +++-- .../java/org/openedx/app/di/ScreenModule.kt | 40 ++++++-- .../openedx/auth/presentation/AuthRouter.kt | 12 ++- .../logistration/LogistrationFragment.kt | 20 +++- .../presentation/signin/SignInFragment.kt | 25 +++-- .../presentation/signin/SignInViewModel.kt | 1 + .../presentation/signup/SignUpFragment.kt | 17 +++- .../presentation/signup/SignUpViewModel.kt | 1 + .../signin/SignInViewModelTest.kt | 8 ++ .../signup/SignUpViewModelTest.kt | 7 ++ .../java/org/openedx/core/ui/ComposeCommon.kt | 7 +- .../java/org/openedx/core/utils/UrlUtils.kt | 41 ++++++++ .../course/presentation/CourseRouter.kt | 4 +- .../detail/CourseDetailsFragment.kt | 4 +- .../presentation/info/CourseInfoFragment.kt | 93 ++++++++++++++----- .../presentation/info/CourseInfoUIState.kt | 9 ++ .../presentation/info/CourseInfoViewModel.kt | 54 +++++++++-- .../presentation/DiscoveryNavigator.kt | 4 +- .../discovery/presentation/DiscoveryRouter.kt | 4 +- .../presentation/NativeDiscoveryFragment.kt | 6 +- .../presentation/WebViewDiscoveryFragment.kt | 63 ++++++++++++- .../presentation/WebViewDiscoveryViewModel.kt | 26 +++++- .../search/CourseSearchFragment.kt | 4 +- .../org/openedx/whatsnew/WhatsNewRouter.kt | 2 +- .../presentation/whatsnew/WhatsNewFragment.kt | 18 +++- .../whatsnew/WhatsNewViewModel.kt | 1 + .../openedx/whatsnew/WhatsNewViewModelTest.kt | 2 +- 28 files changed, 420 insertions(+), 99 deletions(-) create mode 100644 course/src/main/java/org/openedx/course/presentation/info/CourseInfoUIState.kt diff --git a/app/src/main/java/org/openedx/app/AppRouter.kt b/app/src/main/java/org/openedx/app/AppRouter.kt index 15f7be5a0..eff44c9a7 100644 --- a/app/src/main/java/org/openedx/app/AppRouter.kt +++ b/app/src/main/java/org/openedx/app/AppRouter.kt @@ -28,6 +28,7 @@ import org.openedx.dashboard.presentation.DashboardRouter import org.openedx.dashboard.presentation.program.ProgramFragment import org.openedx.discovery.presentation.DiscoveryRouter import org.openedx.discovery.presentation.NativeDiscoveryFragment +import org.openedx.discovery.presentation.WebViewDiscoveryFragment import org.openedx.discovery.presentation.search.CourseSearchFragment import org.openedx.discussion.domain.model.DiscussionComment import org.openedx.discussion.domain.model.Thread @@ -52,19 +53,19 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di ProfileRouter, AppUpgradeRouter, WhatsNewRouter { //region AuthRouter - override fun navigateToMain(fm: FragmentManager, courseId: String?) { + override fun navigateToMain(fm: FragmentManager, courseId: String?, infoType: String?) { fm.popBackStack() fm.beginTransaction() - .replace(R.id.container, MainFragment.newInstance(courseId)) + .replace(R.id.container, MainFragment.newInstance(courseId, infoType)) .commit() } - override fun navigateToSignIn(fm: FragmentManager, courseId: String?) { - replaceFragmentWithBackStack(fm, SignInFragment.newInstance(courseId)) + override fun navigateToSignIn(fm: FragmentManager, courseId: String?, infoType: String?) { + replaceFragmentWithBackStack(fm, SignInFragment.newInstance(courseId, infoType)) } - override fun navigateToSignUp(fm: FragmentManager, courseId: String?) { - replaceFragmentWithBackStack(fm, SignUpFragment.newInstance(courseId)) + override fun navigateToSignUp(fm: FragmentManager, courseId: String?, infoType: String?) { + replaceFragmentWithBackStack(fm, SignUpFragment.newInstance(courseId, infoType)) } override fun navigateToLogistration(fm: FragmentManager, courseId: String?) { @@ -75,14 +76,18 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di replaceFragmentWithBackStack(fm, RestorePasswordFragment()) } - override fun navigateToDiscoverCourses(fm: FragmentManager, querySearch: String) { + override fun navigateToNativeDiscoverCourses(fm: FragmentManager, querySearch: String) { replaceFragmentWithBackStack(fm, NativeDiscoveryFragment.newInstance(querySearch)) } - override fun navigateToWhatsNew(fm: FragmentManager, courseId: String?) { + override fun navigateToWebDiscoverCourses(fm: FragmentManager, querySearch: String) { + replaceFragmentWithBackStack(fm, WebViewDiscoveryFragment.newInstance(querySearch)) + } + + override fun navigateToWhatsNew(fm: FragmentManager, courseId: String?, infoType: String?) { fm.popBackStack() fm.beginTransaction() - .replace(R.id.container, WhatsNewFragment.newInstance(courseId)) + .replace(R.id.container, WhatsNewFragment.newInstance(courseId, infoType)) .commit() } diff --git a/app/src/main/java/org/openedx/app/MainFragment.kt b/app/src/main/java/org/openedx/app/MainFragment.kt index 2021e038f..8534eaffe 100644 --- a/app/src/main/java/org/openedx/app/MainFragment.kt +++ b/app/src/main/java/org/openedx/app/MainFragment.kt @@ -13,6 +13,7 @@ import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import org.openedx.app.adapter.MainNavigationFragmentAdapter import org.openedx.app.databinding.FragmentMainBinding +import org.openedx.core.config.Config import org.openedx.core.presentation.global.app_upgrade.UpgradeRequiredFragment import org.openedx.core.presentation.global.viewBinding import org.openedx.dashboard.presentation.dashboard.DashboardFragment @@ -27,6 +28,7 @@ class MainFragment : Fragment(R.layout.fragment_main) { private val analytics by inject() private val viewModel by viewModel() private val router by inject() + private val config by inject() private lateinit var adapter: MainNavigationFragmentAdapter @@ -82,12 +84,19 @@ class MainFragment : Fragment(R.layout.fragment_main) { } requireArguments().apply { - this.getString(ARG_COURSE_ID, null)?.let { - if (it.isNotBlank()) { - router.navigateToCourseDetail(parentFragmentManager, it) + getString(ARG_COURSE_ID).takeIf { it.isNullOrBlank().not() }?.let { courseId -> + val infoType = getString(ARG_INFO_TYPE) + + if (config.getDiscoveryConfig().isViewTypeWebView() && infoType != null) { + router.navigateToCourseInfo(parentFragmentManager, courseId, infoType) + } else { + router.navigateToCourseDetail(parentFragmentManager, courseId) } + + // Clear arguments after navigation + putString(ARG_COURSE_ID, "") + putString(ARG_INFO_TYPE, "") } - this.putString(ARG_COURSE_ID, null) } } @@ -121,10 +130,12 @@ class MainFragment : Fragment(R.layout.fragment_main) { companion object { private const val ARG_COURSE_ID = "courseId" - fun newInstance(courseId: String? = null): MainFragment { + private const val ARG_INFO_TYPE = "info_type" + fun newInstance(courseId: String? = null, infoType: String? = null): MainFragment { val fragment = MainFragment() fragment.arguments = bundleOf( - ARG_COURSE_ID to courseId + ARG_COURSE_ID to courseId, + ARG_INFO_TYPE to infoType ) return fragment } diff --git a/app/src/main/java/org/openedx/app/di/ScreenModule.kt b/app/src/main/java/org/openedx/app/di/ScreenModule.kt index 9ccac7be7..2f86dbb5a 100644 --- a/app/src/main/java/org/openedx/app/di/ScreenModule.kt +++ b/app/src/main/java/org/openedx/app/di/ScreenModule.kt @@ -64,7 +64,7 @@ val screenModule = module { factory { AuthRepository(get(), get(), get()) } factory { AuthInteractor(get()) } factory { Validator() } - viewModel { (courseId: String?) -> + viewModel { (courseId: String?, infoType: String?) -> SignInViewModel( get(), get(), @@ -75,10 +75,12 @@ val screenModule = module { get(), get(), courseId, + infoType, ) } - viewModel { (courseId: String?) -> - SignUpViewModel(get(), get(), get(), get(), get(), get(), get(), courseId) + + viewModel { (courseId: String?, infoType: String?) -> + SignUpViewModel(get(), get(), get(), get(), get(), get(), get(), courseId, infoType) } viewModel { RestorePasswordViewModel(get(), get(), get(), get()) } @@ -89,7 +91,15 @@ val screenModule = module { factory { DiscoveryRepository(get(), get()) } factory { DiscoveryInteractor(get()) } viewModel { NativeDiscoveryViewModel(get(), get(), get(), get(), get(), get(), get()) } - viewModel { WebViewDiscoveryViewModel(get(), get(), get()) } + viewModel { (querySearch: String) -> + WebViewDiscoveryViewModel( + get(), + get(), + get(), + get(), + querySearch + ) + } factory { ProfileRepository(get(), get(), get(), get(), get()) } factory { ProfileInteractor(get()) } @@ -116,7 +126,19 @@ val screenModule = module { single { CourseRepository(get(), get(), get(), get()) } factory { CourseInteractor(get()) } - viewModel { CourseInfoViewModel(get(), get(), get(), get(), get(), get()) } + viewModel { (pathId: String, infoType: String) -> + CourseInfoViewModel( + get(), + get(), + get(), + get(), + get(), + get(), + get(), + pathId, + infoType + ) + } viewModel { (courseId: String) -> CourseDetailsViewModel( courseId, @@ -264,7 +286,13 @@ val screenModule = module { ) } - viewModel { (courseId: String?) -> WhatsNewViewModel(courseId, get()) } + viewModel { (courseId: String?, infoType: String?) -> + WhatsNewViewModel( + courseId, + infoType, + get() + ) + } viewModel { HtmlUnitViewModel(get(), get(), get(), get()) } viewModel { ProgramViewModel(get(), get(), get(), get(), get(), get(), get()) } diff --git a/auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt b/auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt index ff73b7a44..a9a8357b7 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt @@ -4,19 +4,21 @@ import androidx.fragment.app.FragmentManager interface AuthRouter { - fun navigateToMain(fm: FragmentManager, courseId: String?) + fun navigateToMain(fm: FragmentManager, courseId: String?, infoType: String?) - fun navigateToSignIn(fm: FragmentManager, courseId: String?) + fun navigateToSignIn(fm: FragmentManager, courseId: String?, infoType: String?) fun navigateToLogistration(fm: FragmentManager, courseId: String?) - fun navigateToSignUp(fm: FragmentManager, courseId: String?) + fun navigateToSignUp(fm: FragmentManager, courseId: String?, infoType: String?) fun navigateToRestorePassword(fm: FragmentManager) - fun navigateToWhatsNew(fm: FragmentManager, courseId: String? = null) + fun navigateToWhatsNew(fm: FragmentManager, courseId: String? = null, infoType: String? = null) - fun navigateToDiscoverCourses(fm: FragmentManager, querySearch: String) + fun navigateToWebDiscoverCourses(fm: FragmentManager, querySearch: String) + + fun navigateToNativeDiscoverCourses(fm: FragmentManager, querySearch: String) fun clearBackStack(fm: FragmentManager) } diff --git a/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt index 6379b246c..eb63bbc19 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/logistration/LogistrationFragment.kt @@ -41,6 +41,7 @@ import androidx.fragment.app.Fragment import org.koin.android.ext.android.inject import org.openedx.auth.R import org.openedx.auth.presentation.AuthRouter +import org.openedx.core.config.Config import org.openedx.core.ui.AuthButtonsPanel import org.openedx.core.ui.SearchBar import org.openedx.core.ui.displayCutoutForLandscape @@ -53,6 +54,7 @@ import org.openedx.core.ui.theme.compose.LogistrationLogoView class LogistrationFragment : Fragment() { private val router: AuthRouter by inject() + private val config by inject() override fun onCreateView( inflater: LayoutInflater, @@ -63,15 +65,26 @@ class LogistrationFragment : Fragment() { setContent { OpenEdXTheme { val courseId = arguments?.getString(ARG_COURSE_ID, "") + val isDiscoveryTypeWebView = config.getDiscoveryConfig().isViewTypeWebView() LogistrationScreen( onSignInClick = { - router.navigateToSignIn(parentFragmentManager, courseId) + router.navigateToSignIn(parentFragmentManager, courseId, null) }, onRegisterClick = { - router.navigateToSignUp(parentFragmentManager, courseId) + router.navigateToSignUp(parentFragmentManager, courseId, null) }, onSearchClick = { querySearch -> - router.navigateToDiscoverCourses(parentFragmentManager, querySearch) + if (isDiscoveryTypeWebView) { + router.navigateToWebDiscoverCourses( + parentFragmentManager, + querySearch + ) + } else { + router.navigateToNativeDiscoverCourses( + parentFragmentManager, + querySearch + ) + } } ) } @@ -153,6 +166,7 @@ private fun LogistrationScreen( label = stringResource(id = R.string.pre_auth_search_hint), requestFocus = false, searchValue = textFieldValue, + clearOnSubmit = true, keyboardActions = { focusManager.clearFocus() onSearchClick(textFieldValue.text) diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt index 1adcaa3a1..fb613125f 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt @@ -26,7 +26,10 @@ import org.openedx.core.ui.theme.OpenEdXTheme class SignInFragment : Fragment() { private val viewModel: SignInViewModel by viewModel { - parametersOf(requireArguments().getString(ARG_COURSE_ID, null)) + parametersOf( + requireArguments().getString(ARG_COURSE_ID, ""), + requireArguments().getString(ARG_INFO_TYPE, "") + ) } private val router: AuthRouter by inject() private val whatsNewGlobalManager by inject() @@ -64,7 +67,7 @@ class SignInFragment : Fragment() { AuthEvent.RegisterClick -> { viewModel.signUpClickedEvent() - router.navigateToSignUp(parentFragmentManager, null) + router.navigateToSignUp(parentFragmentManager, null, null) } AuthEvent.BackClick -> { @@ -79,9 +82,17 @@ class SignInFragment : Fragment() { if (state.loginSuccess) { router.clearBackStack(parentFragmentManager) if (isNeedToShowWhatsNew) { - router.navigateToWhatsNew(parentFragmentManager, viewModel.courseId) + router.navigateToWhatsNew( + parentFragmentManager, + viewModel.courseId, + viewModel.infoType + ) } else { - router.navigateToMain(parentFragmentManager, viewModel.courseId) + router.navigateToMain( + parentFragmentManager, + viewModel.courseId, + viewModel.infoType + ) } } @@ -99,10 +110,12 @@ class SignInFragment : Fragment() { companion object { private const val ARG_COURSE_ID = "courseId" - fun newInstance(courseId: String?): SignInFragment { + private const val ARG_INFO_TYPE = "info_type" + fun newInstance(courseId: String?, infoType: String?): SignInFragment { val fragment = SignInFragment() fragment.arguments = bundleOf( - ARG_COURSE_ID to courseId + ARG_COURSE_ID to courseId, + ARG_INFO_TYPE to infoType ) return fragment } diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt index e5532429b..d47950341 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt @@ -40,6 +40,7 @@ class SignInViewModel( private val oAuthHelper: OAuthHelper, config: Config, val courseId: String?, + val infoType: String?, ) : BaseViewModel() { private val logger = Logger("SignInViewModel") diff --git a/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpFragment.kt index 97bfe45d9..2408202df 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpFragment.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpFragment.kt @@ -24,7 +24,10 @@ import org.openedx.core.ui.theme.OpenEdXTheme class SignUpFragment : Fragment() { private val viewModel by viewModel { - parametersOf(requireArguments().getString(ARG_COURSE_ID, "")) + parametersOf( + requireArguments().getString(ARG_COURSE_ID, ""), + requireArguments().getString(ARG_INFO_TYPE, "") + ) } private val router by inject() @@ -73,7 +76,11 @@ class SignUpFragment : Fragment() { LaunchedEffect(uiState.successLogin) { if (uiState.successLogin) { router.clearBackStack(requireActivity().supportFragmentManager) - router.navigateToMain(parentFragmentManager, viewModel.courseId) + router.navigateToMain( + parentFragmentManager, + viewModel.courseId, + viewModel.infoType + ) } } } else { @@ -89,10 +96,12 @@ class SignUpFragment : Fragment() { companion object { private const val ARG_COURSE_ID = "courseId" - fun newInstance(courseId: String?): SignUpFragment { + private const val ARG_INFO_TYPE = "info_type" + fun newInstance(courseId: String?, infoType: String?): SignUpFragment { val fragment = SignUpFragment() fragment.arguments = bundleOf( - ARG_COURSE_ID to courseId + ARG_COURSE_ID to courseId, + ARG_INFO_TYPE to infoType ) return fragment } diff --git a/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpViewModel.kt b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpViewModel.kt index af1b8e094..e8ca67e93 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpViewModel.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpViewModel.kt @@ -38,6 +38,7 @@ class SignUpViewModel( private val oAuthHelper: OAuthHelper, private val config: Config, val courseId: String?, + val infoType: String?, ) : BaseViewModel() { private val logger = Logger("SignUpViewModel") diff --git a/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt b/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt index 16b5032c4..6b04b596b 100644 --- a/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt +++ b/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt @@ -100,6 +100,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) viewModel.login("", "") coVerify(exactly = 0) { interactor.login(any(), any()) } @@ -127,6 +128,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) viewModel.login("acc@test.o", "") coVerify(exactly = 0) { interactor.login(any(), any()) } @@ -156,6 +158,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) viewModel.login("acc@test.org", "") @@ -184,6 +187,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) viewModel.login("acc@test.org", "ed") @@ -214,6 +218,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) coEvery { interactor.login("acc@test.org", "edx") } returns Unit viewModel.login("acc@test.org", "edx") @@ -245,6 +250,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) coEvery { interactor.login("acc@test.org", "edx") } throws UnknownHostException() viewModel.login("acc@test.org", "edx") @@ -277,6 +283,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) coEvery { interactor.login("acc@test.org", "edx") } throws EdxError.InvalidGrantException() viewModel.login("acc@test.org", "edx") @@ -309,6 +316,7 @@ class SignInViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) coEvery { interactor.login("acc@test.org", "edx") } throws IllegalStateException() viewModel.login("acc@test.org", "edx") diff --git a/auth/src/test/java/org/openedx/auth/presentation/signup/SignUpViewModelTest.kt b/auth/src/test/java/org/openedx/auth/presentation/signup/SignUpViewModelTest.kt index bd048902c..f93447bb8 100644 --- a/auth/src/test/java/org/openedx/auth/presentation/signup/SignUpViewModelTest.kt +++ b/auth/src/test/java/org/openedx/auth/presentation/signup/SignUpViewModelTest.kt @@ -129,6 +129,7 @@ class SignUpViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) coEvery { interactor.validateRegistrationFields(parametersMap) } returns ValidationFields( parametersMap @@ -169,6 +170,7 @@ class SignUpViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) val deferred = async { viewModel.uiMessage.first() } @@ -215,6 +217,7 @@ class SignUpViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) val deferred = async { viewModel.uiMessage.first() } @@ -251,6 +254,7 @@ class SignUpViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) coEvery { interactor.validateRegistrationFields(parametersMap) } returns ValidationFields( emptyMap() @@ -298,6 +302,7 @@ class SignUpViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) val deferred = async { viewModel.uiMessage.first() } @@ -322,6 +327,7 @@ class SignUpViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) val deferred = async { viewModel.uiMessage.first() } @@ -346,6 +352,7 @@ class SignUpViewModelTest { oAuthHelper = oAuthHelper, config = config, courseId = "", + infoType = "", ) coEvery { interactor.getRegistrationFields() } returns listOfFields viewModel.getRegistrationFields() diff --git a/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt b/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt index 80f61d75d..b5b4dca7c 100644 --- a/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt +++ b/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt @@ -1,8 +1,6 @@ package org.openedx.core.ui -import android.content.Context import android.os.Build.VERSION.SDK_INT -import android.widget.Toast import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -177,6 +175,7 @@ fun SearchBar( searchValue: TextFieldValue, requestFocus: Boolean = false, label: String = stringResource(id = R.string.core_search), + clearOnSubmit: Boolean = false, keyboardActions: () -> Unit, onValueChanged: (TextFieldValue) -> Unit = {}, onClearValue: () -> Unit, @@ -258,6 +257,10 @@ fun SearchBar( keyboardActions = KeyboardActions { keyboardController?.hide() keyboardActions() + if (clearOnSubmit) { + textFieldValue = TextFieldValue("") + onClearValue() + } }, textStyle = MaterialTheme.appTypography.bodyMedium, maxLines = 1 diff --git a/core/src/main/java/org/openedx/core/utils/UrlUtils.kt b/core/src/main/java/org/openedx/core/utils/UrlUtils.kt index e90f0dfae..191edd4da 100644 --- a/core/src/main/java/org/openedx/core/utils/UrlUtils.kt +++ b/core/src/main/java/org/openedx/core/utils/UrlUtils.kt @@ -5,6 +5,9 @@ import android.content.Intent import android.net.Uri object UrlUtils { + + const val QUERY_PARAM_SEARCH = "q" + fun openInBrowser(activity: Context, apiHostUrl: String, url: String) { if (url.isEmpty()) { return @@ -23,4 +26,42 @@ object UrlUtils { intent.setData(Uri.parse(url)) context.startActivity(intent) } + + /** + * Utility function to remove the given query parameter from the URL + * Ref: https://stackoverflow.com/a/56108097 + * + * @param url that needs to update + * @param queryParam that needs to remove from the URL + * @return The URL after removing the given params + */ + private fun removeQueryParameterFromURL(url: String, queryParam: String): String { + val uri = Uri.parse(url) + val params = uri.queryParameterNames + val newUri = uri.buildUpon().clearQuery() + for (param in params) { + if (queryParam != param) { + newUri.appendQueryParameter(param, uri.getQueryParameter(param)) + } + } + return newUri.build().toString() + } + + /** + * Builds a valid URL with the given query params. + * + * @param url The base URL. + * @param queryParams The query params to add in the URL. + * @return URL String with query params added to it. + */ + fun buildUrlWithQueryParams(url: String, queryParams: Map): String { + val uriBuilder = Uri.parse(url).buildUpon() + for ((key, value) in queryParams) { + if (url.contains(key)) { + removeQueryParameterFromURL(url, key) + } + uriBuilder.appendQueryParameter(key, value) + } + return uriBuilder.build().toString() + } } diff --git a/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt b/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt index 05a4f63a6..2eafb8d97 100644 --- a/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt +++ b/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt @@ -68,9 +68,9 @@ interface CourseRouter { infoType: String, ) - fun navigateToSignUp(fm: FragmentManager, courseId: String?) + fun navigateToSignUp(fm: FragmentManager, courseId: String?, infoType: String?) - fun navigateToSignIn(fm: FragmentManager, courseId: String?) + fun navigateToSignIn(fm: FragmentManager, courseId: String?, infoType: String?) fun navigateToLogistration(fm: FragmentManager, courseId: String?) } diff --git a/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsFragment.kt b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsFragment.kt index d7fe21eaa..cc7a500f4 100644 --- a/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsFragment.kt @@ -126,10 +126,10 @@ class CourseDetailsFragment : Fragment() { } }, onRegisterClick = { - router.navigateToSignUp(parentFragmentManager, viewModel.courseId) + router.navigateToSignUp(parentFragmentManager, viewModel.courseId, null) }, onSignInClick = { - router.navigateToSignIn(parentFragmentManager, viewModel.courseId) + router.navigateToSignIn(parentFragmentManager, viewModel.courseId, null) }, ) } diff --git a/course/src/main/java/org/openedx/course/presentation/info/CourseInfoFragment.kt b/course/src/main/java/org/openedx/course/presentation/info/CourseInfoFragment.kt index 852b3a5ca..a2f8ba0ec 100644 --- a/course/src/main/java/org/openedx/course/presentation/info/CourseInfoFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/info/CourseInfoFragment.kt @@ -40,10 +40,13 @@ import androidx.compose.ui.zIndex import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf import org.openedx.core.UIMessage import org.openedx.core.presentation.catalog.CatalogWebViewScreen +import org.openedx.core.presentation.catalog.WebViewLink import org.openedx.core.presentation.dialog.alert.ActionDialogFragment import org.openedx.core.presentation.dialog.alert.InfoDialogFragment +import org.openedx.core.ui.AuthButtonsPanel import org.openedx.core.ui.ConnectionErrorView import org.openedx.core.ui.HandleUIMessage import org.openedx.core.ui.Toolbar @@ -56,12 +59,18 @@ import org.openedx.core.ui.theme.OpenEdXTheme import org.openedx.core.ui.theme.appColors import org.openedx.core.ui.windowSizeValue import org.openedx.course.R +import java.util.concurrent.atomic.AtomicReference import org.openedx.core.R as CoreR import org.openedx.core.presentation.catalog.WebViewLink.Authority as linkAuthority class CourseInfoFragment : Fragment() { - private val viewModel by viewModel() + private val viewModel by viewModel { + parametersOf( + requireArguments().getString(ARG_PATH_ID, ""), + requireArguments().getString(ARG_INFO_TYPE, "") + ) + } override fun onCreateView( inflater: LayoutInflater, @@ -73,7 +82,7 @@ class CourseInfoFragment : Fragment() { OpenEdXTheme { val uiMessage by viewModel.uiMessage.collectAsState(initial = null) val showAlert by viewModel.showAlert.collectAsState(initial = false) - val enrollmentSuccess by viewModel.courseEnrollSuccess.collectAsState(initial = "") + val uiState by viewModel.uiState.collectAsState() val windowSize = rememberWindowSize() var hasInternetConnection by remember { mutableStateOf(viewModel.hasInternetConnection) @@ -94,24 +103,40 @@ class CourseInfoFragment : Fragment() { } } - LaunchedEffect(enrollmentSuccess) { - if (enrollmentSuccess.isNotEmpty()) { + LaunchedEffect(uiState.enrollmentSuccess.get()) { + if (uiState.enrollmentSuccess.get().isNotEmpty()) { viewModel.onSuccessfulCourseEnrollment( fragmentManager = requireActivity().supportFragmentManager, - courseId = enrollmentSuccess, + courseId = uiState.enrollmentSuccess.get(), ) + // Clear after navigation + uiState.enrollmentSuccess.set("") } } CourseInfoScreen( windowSize = windowSize, + uiState = uiState, uiMessage = uiMessage, - contentUrl = getInitialUrl(), uriScheme = viewModel.uriScheme, hasInternetConnection = hasInternetConnection, checkInternetConnection = { hasInternetConnection = viewModel.hasInternetConnection }, + onRegisterClick = { + viewModel.navigateToSignUp( + parentFragmentManager, + viewModel.pathId, + viewModel.infoType + ) + }, + onSignInClick = { + viewModel.navigateToSignIn( + parentFragmentManager, + viewModel.pathId, + viewModel.infoType + ) + }, onBackClick = { requireActivity().supportFragmentManager.popBackStackImmediate() }, @@ -141,7 +166,15 @@ class CourseInfoFragment : Fragment() { } linkAuthority.ENROLL -> { - viewModel.enrollInACourse(param) + if (uiState.isPreLogin) { + viewModel.navigateToSignUp( + fragmentManager = requireActivity().supportFragmentManager, + courseId = viewModel.pathId, + infoType = viewModel.infoType + ) + } else { + viewModel.enrollInACourse(courseId = param) + } } else -> {} @@ -152,18 +185,6 @@ class CourseInfoFragment : Fragment() { } } - private fun getInitialUrl(): String { - return arguments?.let { args -> - val pathId = args.getString(ARG_PATH_ID) ?: "" - val urlTemplate = if (args.getString(ARG_INFO_TYPE) == linkAuthority.COURSE_INFO.name) { - viewModel.webViewConfig.courseUrlTemplate - } else { - viewModel.webViewConfig.programUrlTemplate - } - urlTemplate.replace("{$ARG_PATH_ID}", pathId) - } ?: viewModel.webViewConfig.baseUrl - } - companion object { private const val ARG_PATH_ID = "path_id" private const val ARG_INFO_TYPE = "info_type" @@ -185,13 +206,15 @@ class CourseInfoFragment : Fragment() { @Composable private fun CourseInfoScreen( windowSize: WindowSize, + uiState: CourseInfoUIState, uiMessage: UIMessage?, - contentUrl: String, uriScheme: String, hasInternetConnection: Boolean, checkInternetConnection: () -> Unit, + onRegisterClick: () -> Unit, + onSignInClick: () -> Unit, onBackClick: () -> Unit, - onUriClick: (String, linkAuthority) -> Unit, + onUriClick: (String, WebViewLink.Authority) -> Unit, ) { val scaffoldState = rememberScaffoldState() val configuration = LocalConfiguration.current @@ -202,7 +225,23 @@ private fun CourseInfoScreen( Scaffold( scaffoldState = scaffoldState, modifier = Modifier.fillMaxSize(), - backgroundColor = MaterialTheme.appColors.background + backgroundColor = MaterialTheme.appColors.background, + bottomBar = { + if (uiState.isPreLogin) { + Box( + modifier = Modifier + .padding( + horizontal = 16.dp, + vertical = 32.dp, + ) + ) { + AuthButtonsPanel( + onRegisterClick = onRegisterClick, + onSignInClick = onSignInClick + ) + } + } + } ) { val modifierScreenWidth by remember(key1 = windowSize) { mutableStateOf( @@ -240,7 +279,7 @@ private fun CourseInfoScreen( ) { if (hasInternetConnection) { CourseInfoWebView( - contentUrl = contentUrl, + contentUrl = uiState.initialUrl, uriScheme = uriScheme, onWebPageLoaded = { isLoading = false }, onUriClick = onUriClick, @@ -307,11 +346,17 @@ fun CourseInfoScreenPreview() { OpenEdXTheme { CourseInfoScreen( windowSize = WindowSize(WindowType.Compact, WindowType.Compact), + uiState = CourseInfoUIState( + initialUrl = "https://www.example.com/", + isPreLogin = false, + enrollmentSuccess = AtomicReference("") + ), uiMessage = null, - contentUrl = "https://www.example.com/", uriScheme = "", hasInternetConnection = false, checkInternetConnection = {}, + onRegisterClick = {}, + onSignInClick = {}, onBackClick = {}, onUriClick = { _, _ -> }, ) diff --git a/course/src/main/java/org/openedx/course/presentation/info/CourseInfoUIState.kt b/course/src/main/java/org/openedx/course/presentation/info/CourseInfoUIState.kt new file mode 100644 index 000000000..e75a7873e --- /dev/null +++ b/course/src/main/java/org/openedx/course/presentation/info/CourseInfoUIState.kt @@ -0,0 +1,9 @@ +package org.openedx.course.presentation.info + +import java.util.concurrent.atomic.AtomicReference + +internal data class CourseInfoUIState( + val initialUrl: String = "", + val isPreLogin: Boolean = false, + val enrollmentSuccess: AtomicReference = AtomicReference("") +) diff --git a/course/src/main/java/org/openedx/course/presentation/info/CourseInfoViewModel.kt b/course/src/main/java/org/openedx/course/presentation/info/CourseInfoViewModel.kt index a70411049..f2da38168 100644 --- a/course/src/main/java/org/openedx/course/presentation/info/CourseInfoViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/info/CourseInfoViewModel.kt @@ -3,13 +3,18 @@ package org.openedx.course.presentation.info import androidx.fragment.app.FragmentManager import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.openedx.core.BaseViewModel import org.openedx.core.UIMessage import org.openedx.core.config.Config +import org.openedx.core.data.storage.CorePreferences import org.openedx.core.extension.isInternetError +import org.openedx.core.presentation.catalog.WebViewLink import org.openedx.core.system.ResourceManager import org.openedx.core.system.connection.NetworkConnection import org.openedx.core.system.notifier.CourseDashboardUpdate @@ -17,6 +22,7 @@ import org.openedx.core.system.notifier.CourseNotifier import org.openedx.course.R import org.openedx.course.domain.interactor.CourseInteractor import org.openedx.course.presentation.CourseRouter +import java.util.concurrent.atomic.AtomicReference import org.openedx.core.R as CoreR class CourseInfoViewModel( @@ -26,8 +32,20 @@ class CourseInfoViewModel( private val interactor: CourseInteractor, private val notifier: CourseNotifier, private val resourceManager: ResourceManager, + corePreferences: CorePreferences, + val pathId: String, + val infoType: String, ) : BaseViewModel() { + private val _uiState = + MutableStateFlow( + CourseInfoUIState( + initialUrl = getInitialUrl(), + isPreLogin = config.isPreLoginExperienceEnabled() && corePreferences.user == null + ) + ) + internal val uiState: StateFlow = _uiState + private val _uiMessage = MutableSharedFlow() val uiMessage: SharedFlow get() = _uiMessage.asSharedFlow() @@ -36,16 +54,25 @@ class CourseInfoViewModel( val showAlert: SharedFlow get() = _showAlert.asSharedFlow() - private val _courseEnrollSuccess = MutableSharedFlow() - val courseEnrollSuccess: SharedFlow - get() = _courseEnrollSuccess.asSharedFlow() - val hasInternetConnection: Boolean get() = networkConnection.isOnline() val uriScheme: String get() = config.getUriScheme() - val webViewConfig get() = config.getDiscoveryConfig().webViewConfig + private val webViewConfig get() = config.getDiscoveryConfig().webViewConfig + + private fun getInitialUrl(): String { + val urlTemplate = when (infoType) { + WebViewLink.Authority.COURSE_INFO.name -> webViewConfig.courseUrlTemplate + WebViewLink.Authority.PROGRAM_INFO.name -> webViewConfig.programUrlTemplate + else -> webViewConfig.baseUrl + } + return if (pathId.isEmpty() || infoType.isEmpty()) { + webViewConfig.baseUrl + } else { + urlTemplate.replace("{${ARG_PATH_ID}}", pathId) + } + } fun enrollInACourse(courseId: String) { viewModelScope.launch { @@ -57,14 +84,13 @@ class CourseInfoViewModel( _uiMessage.emit( UIMessage.ToastMessage(resourceManager.getString(R.string.course_you_are_already_enrolled)) ) - _courseEnrollSuccess.emit(courseId) + _uiState.update { it.copy(enrollmentSuccess = AtomicReference(courseId)) } return@launch } interactor.enrollInACourse(courseId) notifier.send(CourseDashboardUpdate()) - _courseEnrollSuccess.emit(courseId) - + _uiState.update { it.copy(enrollmentSuccess = AtomicReference(courseId)) } } catch (e: Exception) { if (e.isInternetError()) { _uiMessage.emit( @@ -96,4 +122,16 @@ class CourseInfoViewModel( ) } } + + fun navigateToSignUp(fragmentManager: FragmentManager, courseId: String?, infoType: String) { + router.navigateToSignUp(fragmentManager, courseId, infoType) + } + + fun navigateToSignIn(fragmentManager: FragmentManager, courseId: String, infoType: String) { + router.navigateToSignIn(fragmentManager, courseId, infoType) + } + + companion object { + private const val ARG_PATH_ID = "path_id" + } } diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryNavigator.kt b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryNavigator.kt index ff2e32178..2d49f7723 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryNavigator.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryNavigator.kt @@ -7,9 +7,9 @@ class DiscoveryNavigator( ) { fun getDiscoveryFragment(): Fragment { return if (isDiscoveryTypeWebView) { - WebViewDiscoveryFragment() + WebViewDiscoveryFragment.newInstance() } else { - NativeDiscoveryFragment() + NativeDiscoveryFragment.newInstance() } } } diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryRouter.kt b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryRouter.kt index fa66c542b..53a93f4c1 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryRouter.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryRouter.kt @@ -12,7 +12,7 @@ interface DiscoveryRouter { fun navigateToCourseInfo(fm: FragmentManager, courseId: String, infoType: String) - fun navigateToSignUp(fm: FragmentManager, courseId: String? = null) + fun navigateToSignUp(fm: FragmentManager, courseId: String? = null, infoType: String? = null) - fun navigateToSignIn(fm: FragmentManager, courseId: String?) + fun navigateToSignIn(fm: FragmentManager, courseId: String?, infoType: String?) } diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt b/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt index f68785720..b64e3b5b8 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/NativeDiscoveryFragment.kt @@ -153,10 +153,10 @@ class NativeDiscoveryFragment : Fragment() { ) }, onRegisterClick = { - router.navigateToSignUp(parentFragmentManager, null) + router.navigateToSignUp(parentFragmentManager, null, null) }, onSignInClick = { - router.navigateToSignIn(parentFragmentManager, null) + router.navigateToSignIn(parentFragmentManager, null, null) }, onBackClick = { requireActivity().supportFragmentManager.popBackStackImmediate() @@ -175,7 +175,7 @@ class NativeDiscoveryFragment : Fragment() { companion object { private const val ARG_SEARCH_QUERY = "query_search" - fun newInstance(querySearch: String): NativeDiscoveryFragment { + fun newInstance(querySearch: String = ""): NativeDiscoveryFragment { val fragment = NativeDiscoveryFragment() fragment.arguments = bundleOf( ARG_SEARCH_QUERY to querySearch diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryFragment.kt b/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryFragment.kt index a30d6075d..33a2bd24a 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryFragment.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryFragment.kt @@ -40,14 +40,17 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.zIndex +import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleOwner import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf import org.openedx.core.presentation.catalog.CatalogWebViewScreen import org.openedx.core.presentation.catalog.WebViewLink import org.openedx.core.presentation.dialog.alert.ActionDialogFragment +import org.openedx.core.ui.AuthButtonsPanel import org.openedx.core.ui.ConnectionErrorView import org.openedx.core.ui.Toolbar import org.openedx.core.ui.WindowSize @@ -63,7 +66,9 @@ import org.openedx.core.R as CoreR class WebViewDiscoveryFragment : Fragment() { - private val viewModel by viewModel() + private val viewModel by viewModel { + parametersOf(requireArguments().getString(ARG_SEARCH_QUERY, "")) + } override fun onCreateView( inflater: LayoutInflater, @@ -77,9 +82,9 @@ class WebViewDiscoveryFragment : Fragment() { var hasInternetConnection by remember { mutableStateOf(viewModel.hasInternetConnection) } - WebViewDiscoveryScreen( windowSize = windowSize, + isPreLogin = viewModel.isPreLogin, contentUrl = viewModel.discoveryUrl, uriScheme = viewModel.uriScheme, hasInternetConnection = hasInternetConnection, @@ -116,23 +121,47 @@ class WebViewDiscoveryFragment : Fragment() { else -> {} } + }, + onRegisterClick = { + viewModel.navigateToSignUp(parentFragmentManager) + }, + onSignInClick = { + viewModel.navigateToSignIn(parentFragmentManager) + }, + onBackClick = { + requireActivity().supportFragmentManager.popBackStackImmediate() } ) } } } + + companion object { + + private const val ARG_SEARCH_QUERY = "query_search" + + fun newInstance(querySearch: String = ""): WebViewDiscoveryFragment { + val fragment = WebViewDiscoveryFragment() + fragment.arguments = bundleOf(ARG_SEARCH_QUERY to querySearch) + return fragment + } + } } @Composable @SuppressLint("SetJavaScriptEnabled") private fun WebViewDiscoveryScreen( windowSize: WindowSize, + isPreLogin: Boolean, contentUrl: String, uriScheme: String, hasInternetConnection: Boolean, checkInternetConnection: () -> Unit, onWebPageUpdated: (String) -> Unit, - onUriClick: (String, WebViewLink.Authority) -> Unit + onUriClick: (String, WebViewLink.Authority) -> Unit, + onRegisterClick: () -> Unit, + onSignInClick: () -> Unit, + onBackClick: () -> Unit ) { val scaffoldState = rememberScaffoldState() val configuration = LocalConfiguration.current @@ -141,7 +170,23 @@ private fun WebViewDiscoveryScreen( Scaffold( scaffoldState = scaffoldState, modifier = Modifier.fillMaxSize(), - backgroundColor = MaterialTheme.appColors.background + backgroundColor = MaterialTheme.appColors.background, + bottomBar = { + if (isPreLogin) { + Box( + modifier = Modifier + .padding( + horizontal = 16.dp, + vertical = 32.dp, + ) + ) { + AuthButtonsPanel( + onRegisterClick = onRegisterClick, + onSignInClick = onSignInClick + ) + } + } + } ) { val modifierScreenWidth by remember(key1 = windowSize) { mutableStateOf( @@ -164,7 +209,11 @@ private fun WebViewDiscoveryScreen( .displayCutoutForLandscape(), horizontalAlignment = Alignment.CenterHorizontally, ) { - Toolbar(label = stringResource(id = R.string.discovery_explore_the_catalog)) + Toolbar( + label = stringResource(id = R.string.discovery_explore_the_catalog), + canShowBackBtn = isPreLogin, + onBackClick = onBackClick + ) Surface { Box( @@ -286,11 +335,15 @@ private fun WebViewDiscoveryScreenPreview() { WebViewDiscoveryScreen( windowSize = WindowSize(WindowType.Compact, WindowType.Compact), contentUrl = "https://www.example.com/", + isPreLogin = false, uriScheme = "", hasInternetConnection = false, checkInternetConnection = {}, onWebPageUpdated = {}, onUriClick = { _, _ -> }, + onRegisterClick = {}, + onSignInClick = {}, + onBackClick = {} ) } } diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryViewModel.kt b/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryViewModel.kt index a25bc801e..7fa72c67f 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryViewModel.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/WebViewDiscoveryViewModel.kt @@ -3,21 +3,35 @@ package org.openedx.discovery.presentation import androidx.fragment.app.FragmentManager import org.openedx.core.BaseViewModel import org.openedx.core.config.Config +import org.openedx.core.data.storage.CorePreferences import org.openedx.core.system.connection.NetworkConnection +import org.openedx.core.utils.UrlUtils class WebViewDiscoveryViewModel( private val config: Config, private val networkConnection: NetworkConnection, + private val corePreferences: CorePreferences, private val router: DiscoveryRouter, + private val querySearch: String, ) : BaseViewModel() { val uriScheme: String get() = config.getUriScheme() - val webViewConfig get() = config.getDiscoveryConfig().webViewConfig + private val webViewConfig get() = config.getDiscoveryConfig().webViewConfig + + val isPreLogin get() = config.isPreLoginExperienceEnabled() && corePreferences.user == null private var _discoveryUrl = webViewConfig.baseUrl val discoveryUrl: String - get() = _discoveryUrl + get() { + return if (querySearch.isNotBlank()) { + val queryParams: MutableMap = HashMap() + queryParams[UrlUtils.QUERY_PARAM_SEARCH] = querySearch + UrlUtils.buildUrlWithQueryParams(_discoveryUrl, queryParams) + } else { + _discoveryUrl + } + } val hasInternetConnection: Boolean get() = networkConnection.isOnline() @@ -37,4 +51,12 @@ class WebViewDiscoveryViewModel( ) } } + + fun navigateToSignUp(fragmentManager: FragmentManager) { + router.navigateToSignUp(fragmentManager, null) + } + + fun navigateToSignIn(fragmentManager: FragmentManager) { + router.navigateToSignIn(fragmentManager, null, null) + } } diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchFragment.kt b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchFragment.kt index 9329887fc..26af85022 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchFragment.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchFragment.kt @@ -134,10 +134,10 @@ class CourseSearchFragment : Fragment() { ) }, onRegisterClick = { - router.navigateToSignUp(parentFragmentManager, null) + router.navigateToSignUp(parentFragmentManager, null, null) }, onSignInClick = { - router.navigateToSignIn(parentFragmentManager, null) + router.navigateToSignIn(parentFragmentManager, null, null) }, ) } diff --git a/whatsnew/src/main/java/org/openedx/whatsnew/WhatsNewRouter.kt b/whatsnew/src/main/java/org/openedx/whatsnew/WhatsNewRouter.kt index 46bbe3d3d..a8d1cd463 100644 --- a/whatsnew/src/main/java/org/openedx/whatsnew/WhatsNewRouter.kt +++ b/whatsnew/src/main/java/org/openedx/whatsnew/WhatsNewRouter.kt @@ -3,5 +3,5 @@ package org.openedx.whatsnew import androidx.fragment.app.FragmentManager interface WhatsNewRouter { - fun navigateToMain(fm: FragmentManager, courseId: String? = null) + fun navigateToMain(fm: FragmentManager, courseId: String? = null, infoType: String? = null) } diff --git a/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewFragment.kt b/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewFragment.kt index d1d69b861..6ba02e558 100644 --- a/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewFragment.kt +++ b/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewFragment.kt @@ -76,7 +76,10 @@ import org.openedx.whatsnew.presentation.ui.PageIndicator class WhatsNewFragment : Fragment() { private val viewModel: WhatsNewViewModel by viewModel { - parametersOf(requireArguments().getString(ARG_COURSE_ID, null)) + parametersOf( + requireArguments().getString(ARG_COURSE_ID, null), + requireArguments().getString(ARG_INFO_TYPE, null) + ) } private val preferencesManager by inject() private val router by inject() @@ -98,7 +101,11 @@ class WhatsNewFragment : Fragment() { onCloseClick = { val versionName = appData.versionName preferencesManager.lastWhatsNewVersion = versionName - router.navigateToMain(parentFragmentManager, viewModel.courseId) + router.navigateToMain( + parentFragmentManager, + viewModel.courseId, + viewModel.infoType + ) } ) } @@ -107,10 +114,13 @@ class WhatsNewFragment : Fragment() { companion object { private const val ARG_COURSE_ID = "courseId" - fun newInstance(courseId: String? = null): WhatsNewFragment { + private const val ARG_INFO_TYPE = "info_type" + + fun newInstance(courseId: String? = null, infoType: String? = null): WhatsNewFragment { val fragment = WhatsNewFragment() fragment.arguments = bundleOf( - ARG_COURSE_ID to courseId + ARG_COURSE_ID to courseId, + ARG_INFO_TYPE to infoType ) return fragment } diff --git a/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewViewModel.kt b/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewViewModel.kt index c27ead37c..ba4ee3ee5 100644 --- a/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewViewModel.kt +++ b/whatsnew/src/main/java/org/openedx/whatsnew/presentation/whatsnew/WhatsNewViewModel.kt @@ -8,6 +8,7 @@ import org.openedx.whatsnew.domain.model.WhatsNewItem class WhatsNewViewModel( val courseId: String?, + val infoType: String?, private val whatsNewManager: WhatsNewManager ) : BaseViewModel() { diff --git a/whatsnew/src/test/java/org/openedx/whatsnew/WhatsNewViewModelTest.kt b/whatsnew/src/test/java/org/openedx/whatsnew/WhatsNewViewModelTest.kt index e187ffaa8..307946b19 100644 --- a/whatsnew/src/test/java/org/openedx/whatsnew/WhatsNewViewModelTest.kt +++ b/whatsnew/src/test/java/org/openedx/whatsnew/WhatsNewViewModelTest.kt @@ -21,7 +21,7 @@ class WhatsNewViewModelTest { fun `getNewestData success`() = runTest { every { whatsNewManager.getNewestData() } returns whatsNewItem - val viewModel = WhatsNewViewModel("", whatsNewManager) + val viewModel = WhatsNewViewModel("", "", whatsNewManager) verify(exactly = 1) { whatsNewManager.getNewestData() } assert(viewModel.whatsNewItem.value == whatsNewItem)