From ea10acee2b4ed0ab5b6489d3435fb33570c42df9 Mon Sep 17 00:00:00 2001 From: omerhabib26 Date: Tue, 14 May 2024 03:20:34 +0500 Subject: [PATCH 1/2] fix: update config parsing structure - Update dictionary for ProgramConfig - Update UI related feature flags under a single Key - fix image load in course fix: LEARNER-9891 --- .../java/org/openedx/core/config/Config.kt | 45 ++++------ .../org/openedx/core/config/ProgramConfig.kt | 2 +- .../java/org/openedx/core/config/UIConfig.kt | 10 +++ .../dates/CourseDatesViewModel.kt | 2 +- .../outline/CourseOutlineViewModel.kt | 4 +- .../course/presentation/ui/CourseUI.kt | 83 +++++++++++++++++++ .../container/CourseUnitContainerViewModel.kt | 4 +- .../unit/html/HtmlUnitViewModel.kt | 2 +- .../videos/CourseVideoViewModel.kt | 2 +- .../outline/CourseOutlineViewModelTest.kt | 12 +-- .../videos/CourseVideoViewModelTest.kt | 14 ++-- .../presentation/DashboardListFragment.kt | 2 +- default_config/dev/config.yaml | 10 ++- default_config/prod/config.yaml | 9 +- default_config/stage/config.yaml | 9 +- .../discovery/presentation/ui/DiscoveryUI.kt | 4 +- 16 files changed, 151 insertions(+), 63 deletions(-) create mode 100644 core/src/main/java/org/openedx/core/config/UIConfig.kt diff --git a/core/src/main/java/org/openedx/core/config/Config.kt b/core/src/main/java/org/openedx/core/config/Config.kt index 4e39a0861..a8a5a1d3a 100644 --- a/core/src/main/java/org/openedx/core/config/Config.kt +++ b/core/src/main/java/org/openedx/core/config/Config.kt @@ -10,17 +10,13 @@ import java.io.InputStreamReader class Config(context: Context) { - private var configProperties: JsonObject - - init { - configProperties = try { - val inputStream = context.assets.open("config/config.json") - val parser = JsonParser() - val config = parser.parse(InputStreamReader(inputStream)) - config.asJsonObject - } catch (e: Exception) { - JsonObject() - } + private var configProperties: JsonObject = try { + val inputStream = context.assets.open("config/config.json") + val parser = JsonParser() + val config = parser.parse(InputStreamReader(inputStream)) + config.asJsonObject + } catch (e: Exception) { + JsonObject() } fun getAppId(): String { @@ -28,31 +24,31 @@ class Config(context: Context) { } fun getApiHostURL(): String { - return getString(API_HOST_URL, "") + return getString(API_HOST_URL) } fun getUriScheme(): String { - return getString(URI_SCHEME, "") + return getString(URI_SCHEME) } fun getOAuthClientId(): String { - return getString(OAUTH_CLIENT_ID, "") + return getString(OAUTH_CLIENT_ID) } fun getAccessTokenType(): String { - return getString(TOKEN_TYPE, "") + return getString(TOKEN_TYPE) } fun getFaqUrl(): String { - return getString(FAQ_URL, "") + return getString(FAQ_URL) } fun getFeedbackEmailAddress(): String { - return getString(FEEDBACK_EMAIL_ADDRESS, "") + return getString(FEEDBACK_EMAIL_ADDRESS) } fun getPlatformName(): String { - return getString(PLATFORM_NAME, "") + return getString(PLATFORM_NAME) } fun getAgreement(locale: String): AgreementUrls { @@ -111,15 +107,11 @@ class Config(context: Context) { return getBoolean(PRE_LOGIN_EXPERIENCE_ENABLED, true) } - fun isCourseNestedListEnabled(): Boolean { - return getBoolean(COURSE_NESTED_LIST_ENABLED, false) - } - - fun isCourseUnitProgressEnabled(): Boolean { - return getBoolean(COURSE_UNIT_PROGRESS_ENABLED, false) + fun getUIComponentConfig(): UIConfig { + return getObjectOrNewInstance(UI_COMPONENT, UIConfig::class.java) } - private fun getString(key: String, defaultValue: String): String { + private fun getString(key: String, defaultValue: String = ""): String { val element = getObject(key) return if (element != null) { element.asString @@ -175,8 +167,7 @@ class Config(context: Context) { private const val PROGRAM = "PROGRAM" private const val DASHBOARD = "DASHBOARD" private const val BRANCH = "BRANCH" - private const val COURSE_NESTED_LIST_ENABLED = "COURSE_NESTED_LIST_ENABLED" - private const val COURSE_UNIT_PROGRESS_ENABLED = "COURSE_UNIT_PROGRESS_ENABLED" + private const val UI_COMPONENT = "UI_COMPONENT" private const val PLATFORM_NAME = "PLATFORM_NAME" } diff --git a/core/src/main/java/org/openedx/core/config/ProgramConfig.kt b/core/src/main/java/org/openedx/core/config/ProgramConfig.kt index c553f8997..ce34365ec 100644 --- a/core/src/main/java/org/openedx/core/config/ProgramConfig.kt +++ b/core/src/main/java/org/openedx/core/config/ProgramConfig.kt @@ -16,6 +16,6 @@ data class ProgramConfig( data class ProgramWebViewConfig( @SerializedName("BASE_URL") val programUrl: String = "", - @SerializedName("PROGRAM_DETAIL_URL_TEMPLATE") + @SerializedName("PROGRAM_DETAIL_TEMPLATE") val programDetailUrlTemplate: String = "", ) diff --git a/core/src/main/java/org/openedx/core/config/UIConfig.kt b/core/src/main/java/org/openedx/core/config/UIConfig.kt new file mode 100644 index 000000000..1f8443d27 --- /dev/null +++ b/core/src/main/java/org/openedx/core/config/UIConfig.kt @@ -0,0 +1,10 @@ +package org.openedx.core.config + +import com.google.gson.annotations.SerializedName + +data class UIConfig( + @SerializedName("COURSE_NESTED_LIST_ENABLED") + val isCourseNestedListEnabled: Boolean = false, + @SerializedName("COURSE_UNIT_PROGRESS_ENABLED") + val isCourseUnitProgressEnabled: Boolean = false, +) diff --git a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt index 2591a8f3e..712ac3c01 100644 --- a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt @@ -77,7 +77,7 @@ class CourseDatesViewModel( private var courseBannerType: CourseBannerType = CourseBannerType.BLANK private var courseStructure: CourseStructure? = null - val isCourseExpandableSectionsEnabled get() = config.isCourseNestedListEnabled() + val isCourseExpandableSectionsEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled init { viewModelScope.launch { diff --git a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt index f533410b8..654d04f05 100644 --- a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt @@ -64,7 +64,7 @@ class CourseOutlineViewModel( workerController, coreAnalytics ) { - val isCourseNestedListEnabled get() = config.isCourseNestedListEnabled() + val isCourseNestedListEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled private val _uiState = MutableStateFlow(CourseOutlineUIState.Loading) val uiState: StateFlow @@ -81,7 +81,7 @@ class CourseOutlineViewModel( private var resumeSectionBlock: Block? = null private var resumeVerticalBlock: Block? = null - private val isCourseExpandableSectionsEnabled get() = config.isCourseNestedListEnabled() + private val isCourseExpandableSectionsEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled private val courseSubSections = mutableMapOf>() private val subSectionsDownloadsCount = mutableMapOf() diff --git a/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt index 10fa4ef84..a7eb5abae 100644 --- a/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt +++ b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt @@ -98,6 +98,89 @@ import subtitleFile.TimedTextObject import java.util.Date import org.openedx.core.R as coreR +@Composable +fun CourseImageHeader( + modifier: Modifier, + apiHostUrl: String, + courseImage: String?, + courseCertificate: Certificate?, + onCertificateClick: (String) -> Unit = {}, + courseName: String, +) { + val configuration = LocalConfiguration.current + val windowSize = rememberWindowSize() + val contentScale = + if (!windowSize.isTablet && configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { + ContentScale.Fit + } else { + ContentScale.Crop + } + val imageUrl = if (courseImage?.isLinkValid() == true) { + courseImage + } else { + apiHostUrl + courseImage + } + Box(modifier = modifier, contentAlignment = Alignment.Center) { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(imageUrl) + .error(coreR.drawable.core_no_image_course) + .placeholder(coreR.drawable.core_no_image_course) + .build(), + contentDescription = stringResource( + id = coreR.string.core_accessibility_header_image_for, + courseName + ), + contentScale = contentScale, + modifier = Modifier + .fillMaxSize() + .clip(MaterialTheme.appShapes.cardShape) + ) + if (courseCertificate?.isCertificateEarned() == true) { + Column( + Modifier + .fillMaxSize() + .clip(MaterialTheme.appShapes.cardShape) + .background(MaterialTheme.appColors.certificateForeground), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Icon( + modifier = Modifier.testTag("ic_congratulations"), + painter = painterResource(id = R.drawable.ic_course_completed_mark), + contentDescription = stringResource(id = R.string.course_congratulations), + tint = Color.White + ) + Spacer(Modifier.height(6.dp)) + Text( + modifier = Modifier.testTag("txt_congratulations"), + text = stringResource(id = R.string.course_congratulations), + style = MaterialTheme.appTypography.headlineMedium, + color = Color.White + ) + Spacer(Modifier.height(4.dp)) + Text( + modifier = Modifier.testTag("txt_course_passed"), + text = stringResource(id = R.string.course_passed), + style = MaterialTheme.appTypography.bodyMedium, + color = Color.White + ) + Spacer(Modifier.height(20.dp)) + OpenEdXOutlinedButton( + modifier = Modifier, + borderColor = Color.White, + textColor = MaterialTheme.appColors.buttonText, + text = stringResource(id = R.string.course_view_certificate), + onClick = { + courseCertificate.certificateURL?.let { + onCertificateClick(it) + } + }) + } + } + } +} + @Composable fun CourseSectionCard( block: Block, diff --git a/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt index f479f08c0..e02057ae3 100644 --- a/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt @@ -37,9 +37,9 @@ class CourseUnitContainerViewModel( private val blocks = ArrayList() - val isCourseExpandableSectionsEnabled get() = config.isCourseNestedListEnabled() + val isCourseExpandableSectionsEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled - val isCourseUnitProgressEnabled get() = config.isCourseUnitProgressEnabled() + val isCourseUnitProgressEnabled get() = config.getUIComponentConfig().isCourseUnitProgressEnabled private var currentIndex = 0 private var currentVerticalIndex = 0 diff --git a/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt b/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt index c65fcb33e..c89e70aa1 100644 --- a/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt @@ -24,7 +24,7 @@ class HtmlUnitViewModel( val injectJSList = _injectJSList.asStateFlow() val isOnline get() = networkConnection.isOnline() - val isCourseUnitProgressEnabled get() = config.isCourseUnitProgressEnabled() + val isCourseUnitProgressEnabled get() = config.getUIComponentConfig().isCourseUnitProgressEnabled val apiHostURL get() = config.getApiHostURL() val cookieManager get() = edxCookieManager diff --git a/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt index b8f2e8fb1..13945ac9b 100644 --- a/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt @@ -53,7 +53,7 @@ class CourseVideoViewModel( coreAnalytics ) { - val isCourseNestedListEnabled get() = config.isCourseNestedListEnabled() + val isCourseNestedListEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled private val _uiState = MutableStateFlow(CourseVideosUIState.Loading) val uiState: StateFlow diff --git a/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt index 45abb10ee..f7d007782 100644 --- a/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt @@ -303,7 +303,7 @@ class CourseOutlineViewModelTest { ) } coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -351,7 +351,7 @@ class CourseOutlineViewModelTest { ) } coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -398,7 +398,7 @@ class CourseOutlineViewModelTest { ) } coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -482,7 +482,7 @@ class CourseOutlineViewModelTest { coEvery { workerController.saveModels(any()) } returns Unit coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") coEvery { downloadDao.readAllData() } returns flow { emit(emptyList()) } - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -525,7 +525,7 @@ class CourseOutlineViewModelTest { every { networkConnection.isOnline() } returns true coEvery { workerController.saveModels(any()) } returns Unit coEvery { downloadDao.readAllData() } returns flow { emit(emptyList()) } - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false every { coreAnalytics.logEvent(any(), any()) } returns Unit val viewModel = CourseOutlineViewModel( @@ -562,7 +562,7 @@ class CourseOutlineViewModelTest { every { networkConnection.isOnline() } returns false coEvery { workerController.saveModels(any()) } returns Unit coEvery { downloadDao.readAllData() } returns flow { emit(emptyList()) } - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", diff --git a/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt index 7962011db..0bdf87b0b 100644 --- a/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt @@ -183,7 +183,7 @@ class CourseVideoViewModelTest { @Test fun `getVideos empty list`() = runTest { - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure.copy(blockData = emptyList()) every { downloadDao.readAllData() } returns flow { emit(emptyList()) } @@ -215,7 +215,7 @@ class CourseVideoViewModelTest { @Test fun `getVideos success`() = runTest { - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure every { downloadDao.readAllData() } returns flow { emit(emptyList()) } every { preferencesManager.videoSettings } returns VideoSettings.default @@ -248,7 +248,7 @@ class CourseVideoViewModelTest { @Test fun `updateVideos success`() = runTest { - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure coEvery { courseNotifier.notifier } returns flow { emit(CourseStructureUpdated("")) @@ -291,7 +291,7 @@ class CourseVideoViewModelTest { @Test fun `setIsUpdating success`() = runTest { - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure coEvery { downloadDao.readAllData() } returns flow { emit(listOf(downloadModelEntity)) } @@ -300,7 +300,7 @@ class CourseVideoViewModelTest { @Test fun `saveDownloadModels test`() = runTest(UnconfinedTestDispatcher()) { - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default val viewModel = CourseVideoViewModel( "", @@ -337,7 +337,7 @@ class CourseVideoViewModelTest { @Test fun `saveDownloadModels only wifi download, with connection`() = runTest(UnconfinedTestDispatcher()) { - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default val viewModel = CourseVideoViewModel( "", @@ -378,7 +378,7 @@ class CourseVideoViewModelTest { @Test fun `saveDownloadModels only wifi download, without connection`() = runTest(UnconfinedTestDispatcher()) { - every { config.isCourseNestedListEnabled() } returns false + every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default val viewModel = CourseVideoViewModel( "", diff --git a/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt index 597958e51..127164cc2 100644 --- a/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt +++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardListFragment.kt @@ -391,7 +391,7 @@ private fun CourseItem( ) ) } - val imageUrl = apiHostUrl.dropLast(1) + enrolledCourse.course.courseImage + val imageUrl = apiHostUrl + enrolledCourse.course.courseImage val context = LocalContext.current Surface( modifier = Modifier diff --git a/default_config/dev/config.yaml b/default_config/dev/config.yaml index 139652ca6..8c08df7f6 100644 --- a/default_config/dev/config.yaml +++ b/default_config/dev/config.yaml @@ -25,8 +25,8 @@ DISCOVERY: PROGRAM: TYPE: 'native' WEBVIEW: - PROGRAM_URL: '' - PROGRAM_DETAIL_URL_TEMPLATE: '' + BASE_URL: '' + PROGRAM_DETAIL_TEMPLATE: '' DASHBOARD: TYPE: 'gallery' @@ -79,5 +79,7 @@ WHATS_NEW_ENABLED: false #feature flag enable Social Login buttons SOCIAL_AUTH_ENABLED: false #Course navigation feature flags -COURSE_NESTED_LIST_ENABLED: false -COURSE_UNIT_PROGRESS_ENABLED: false +UI_COMPONENTS: + COURSE_NESTED_LIST_ENABLED: false + COURSE_UNIT_PROGRESS_ENABLED: false + diff --git a/default_config/prod/config.yaml b/default_config/prod/config.yaml index 139652ca6..8b1dc8f07 100644 --- a/default_config/prod/config.yaml +++ b/default_config/prod/config.yaml @@ -25,8 +25,8 @@ DISCOVERY: PROGRAM: TYPE: 'native' WEBVIEW: - PROGRAM_URL: '' - PROGRAM_DETAIL_URL_TEMPLATE: '' + BASE_URL: '' + PROGRAM_DETAIL_TEMPLATE: '' DASHBOARD: TYPE: 'gallery' @@ -79,5 +79,6 @@ WHATS_NEW_ENABLED: false #feature flag enable Social Login buttons SOCIAL_AUTH_ENABLED: false #Course navigation feature flags -COURSE_NESTED_LIST_ENABLED: false -COURSE_UNIT_PROGRESS_ENABLED: false +UI_COMPONENTS: + COURSE_NESTED_LIST_ENABLED: false + COURSE_UNIT_PROGRESS_ENABLED: false diff --git a/default_config/stage/config.yaml b/default_config/stage/config.yaml index 139652ca6..8b1dc8f07 100644 --- a/default_config/stage/config.yaml +++ b/default_config/stage/config.yaml @@ -25,8 +25,8 @@ DISCOVERY: PROGRAM: TYPE: 'native' WEBVIEW: - PROGRAM_URL: '' - PROGRAM_DETAIL_URL_TEMPLATE: '' + BASE_URL: '' + PROGRAM_DETAIL_TEMPLATE: '' DASHBOARD: TYPE: 'gallery' @@ -79,5 +79,6 @@ WHATS_NEW_ENABLED: false #feature flag enable Social Login buttons SOCIAL_AUTH_ENABLED: false #Course navigation feature flags -COURSE_NESTED_LIST_ENABLED: false -COURSE_UNIT_PROGRESS_ENABLED: false +UI_COMPONENTS: + COURSE_NESTED_LIST_ENABLED: false + COURSE_UNIT_PROGRESS_ENABLED: false diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/ui/DiscoveryUI.kt b/discovery/src/main/java/org/openedx/discovery/presentation/ui/DiscoveryUI.kt index e1b6645ea..30c2a63d2 100644 --- a/discovery/src/main/java/org/openedx/discovery/presentation/ui/DiscoveryUI.kt +++ b/discovery/src/main/java/org/openedx/discovery/presentation/ui/DiscoveryUI.kt @@ -70,7 +70,7 @@ fun ImageHeader( val imageUrl = if (courseImage?.isLinkValid() == true) { courseImage } else { - apiHostUrl.dropLast(1) + courseImage + apiHostUrl + courseImage } Box(modifier = modifier, contentAlignment = Alignment.Center) { AsyncImage( @@ -108,7 +108,7 @@ fun DiscoveryCourseItem( ) } - val imageUrl = apiHostUrl.dropLast(1) + course.media.courseImage?.uri + val imageUrl = apiHostUrl + course.media.courseImage?.uri Surface( modifier = Modifier .testTag("btn_course_card") From acd4a038c262712ab1c3e5f9078bb66fce88cd8c Mon Sep 17 00:00:00 2001 From: "omer.habib" Date: Thu, 30 May 2024 17:27:11 +0500 Subject: [PATCH 2/2] fix: Updated minor fix --- .../java/org/openedx/core/config/Config.kt | 6 +- .../dates/CourseDatesViewModel.kt | 2 +- .../outline/CourseOutlineViewModel.kt | 4 +- .../course/presentation/ui/CourseUI.kt | 109 +++--------------- .../container/CourseUnitContainerViewModel.kt | 4 +- .../unit/html/HtmlUnitViewModel.kt | 2 +- .../videos/CourseVideoViewModel.kt | 2 +- .../outline/CourseOutlineViewModelTest.kt | 12 +- .../videos/CourseVideoViewModelTest.kt | 14 +-- 9 files changed, 36 insertions(+), 119 deletions(-) diff --git a/core/src/main/java/org/openedx/core/config/Config.kt b/core/src/main/java/org/openedx/core/config/Config.kt index a8a5a1d3a..57f91ef88 100644 --- a/core/src/main/java/org/openedx/core/config/Config.kt +++ b/core/src/main/java/org/openedx/core/config/Config.kt @@ -107,8 +107,8 @@ class Config(context: Context) { return getBoolean(PRE_LOGIN_EXPERIENCE_ENABLED, true) } - fun getUIComponentConfig(): UIConfig { - return getObjectOrNewInstance(UI_COMPONENT, UIConfig::class.java) + fun getCourseUIConfig(): UIConfig { + return getObjectOrNewInstance(UI_COMPONENTS, UIConfig::class.java) } private fun getString(key: String, defaultValue: String = ""): String { @@ -167,7 +167,7 @@ class Config(context: Context) { private const val PROGRAM = "PROGRAM" private const val DASHBOARD = "DASHBOARD" private const val BRANCH = "BRANCH" - private const val UI_COMPONENT = "UI_COMPONENT" + private const val UI_COMPONENTS = "UI_COMPONENTS" private const val PLATFORM_NAME = "PLATFORM_NAME" } diff --git a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt index 712ac3c01..a6a78cb72 100644 --- a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesViewModel.kt @@ -77,7 +77,7 @@ class CourseDatesViewModel( private var courseBannerType: CourseBannerType = CourseBannerType.BLANK private var courseStructure: CourseStructure? = null - val isCourseExpandableSectionsEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled + val isCourseExpandableSectionsEnabled get() = config.getCourseUIConfig().isCourseNestedListEnabled init { viewModelScope.launch { diff --git a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt index 654d04f05..11ec94d95 100644 --- a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt @@ -64,7 +64,7 @@ class CourseOutlineViewModel( workerController, coreAnalytics ) { - val isCourseNestedListEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled + val isCourseNestedListEnabled get() = config.getCourseUIConfig().isCourseNestedListEnabled private val _uiState = MutableStateFlow(CourseOutlineUIState.Loading) val uiState: StateFlow @@ -81,7 +81,7 @@ class CourseOutlineViewModel( private var resumeSectionBlock: Block? = null private var resumeVerticalBlock: Block? = null - private val isCourseExpandableSectionsEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled + private val isCourseExpandableSectionsEnabled get() = config.getCourseUIConfig().isCourseNestedListEnabled private val courseSubSections = mutableMapOf>() private val subSectionsDownloadsCount = mutableMapOf() diff --git a/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt index a7eb5abae..6a89c1dc2 100644 --- a/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt +++ b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt @@ -98,95 +98,12 @@ import subtitleFile.TimedTextObject import java.util.Date import org.openedx.core.R as coreR -@Composable -fun CourseImageHeader( - modifier: Modifier, - apiHostUrl: String, - courseImage: String?, - courseCertificate: Certificate?, - onCertificateClick: (String) -> Unit = {}, - courseName: String, -) { - val configuration = LocalConfiguration.current - val windowSize = rememberWindowSize() - val contentScale = - if (!windowSize.isTablet && configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { - ContentScale.Fit - } else { - ContentScale.Crop - } - val imageUrl = if (courseImage?.isLinkValid() == true) { - courseImage - } else { - apiHostUrl + courseImage - } - Box(modifier = modifier, contentAlignment = Alignment.Center) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(imageUrl) - .error(coreR.drawable.core_no_image_course) - .placeholder(coreR.drawable.core_no_image_course) - .build(), - contentDescription = stringResource( - id = coreR.string.core_accessibility_header_image_for, - courseName - ), - contentScale = contentScale, - modifier = Modifier - .fillMaxSize() - .clip(MaterialTheme.appShapes.cardShape) - ) - if (courseCertificate?.isCertificateEarned() == true) { - Column( - Modifier - .fillMaxSize() - .clip(MaterialTheme.appShapes.cardShape) - .background(MaterialTheme.appColors.certificateForeground), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Icon( - modifier = Modifier.testTag("ic_congratulations"), - painter = painterResource(id = R.drawable.ic_course_completed_mark), - contentDescription = stringResource(id = R.string.course_congratulations), - tint = Color.White - ) - Spacer(Modifier.height(6.dp)) - Text( - modifier = Modifier.testTag("txt_congratulations"), - text = stringResource(id = R.string.course_congratulations), - style = MaterialTheme.appTypography.headlineMedium, - color = Color.White - ) - Spacer(Modifier.height(4.dp)) - Text( - modifier = Modifier.testTag("txt_course_passed"), - text = stringResource(id = R.string.course_passed), - style = MaterialTheme.appTypography.bodyMedium, - color = Color.White - ) - Spacer(Modifier.height(20.dp)) - OpenEdXOutlinedButton( - modifier = Modifier, - borderColor = Color.White, - textColor = MaterialTheme.appColors.buttonText, - text = stringResource(id = R.string.course_view_certificate), - onClick = { - courseCertificate.certificateURL?.let { - onCertificateClick(it) - } - }) - } - } - } -} - @Composable fun CourseSectionCard( block: Block, downloadedState: DownloadedState?, onItemClick: (Block) -> Unit, - onDownloadClick: (Block) -> Unit + onDownloadClick: (Block) -> Unit, ) { val iconModifier = Modifier.size(24.dp) @@ -287,7 +204,7 @@ fun OfflineQueueCard( downloadModel: DownloadModel, progressValue: Long, progressSize: Long, - onDownloadClick: (DownloadModel) -> Unit + onDownloadClick: (DownloadModel) -> Unit, ) { val iconModifier = Modifier.size(24.dp) @@ -355,7 +272,7 @@ fun OfflineQueueCard( @Composable fun CardArrow( - degrees: Float + degrees: Float, ) { Icon( imageVector = Icons.Filled.ChevronRight, @@ -387,7 +304,7 @@ fun NavigationUnitsButtons( hasNextBlock: Boolean, isVerticalNavigation: Boolean, onPrevClick: () -> Unit, - onNextClick: () -> Unit + onNextClick: () -> Unit, ) { val nextButtonIcon = if (hasNextBlock) { painterResource(id = coreR.drawable.core_ic_down) @@ -486,7 +403,7 @@ fun HorizontalPageIndicator( completedAndSelectedColor: Color = Color.Green, completedColor: Color = Color.Green, selectedColor: Color = Color.White, - defaultColor: Color = Color.Gray + defaultColor: Color = Color.Gray, ) { Row( horizontalArrangement = Arrangement.spacedBy(1.dp), @@ -551,7 +468,7 @@ fun Indicator( defaultColor: Color, defaultRadius: Dp, selectedSize: Dp, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val size by animateDpAsState( targetValue = if (isSelected) selectedSize else defaultRadius, @@ -580,7 +497,7 @@ fun VideoSubtitles( showSubtitleLanguage: Boolean, currentIndex: Int, onTranscriptClick: (Caption) -> Unit, - onSettingsClick: () -> Unit + onSettingsClick: () -> Unit, ) { timedTextObject?.let { val autoScrollDelay = 3000L @@ -660,7 +577,7 @@ fun CourseExpandableChapterCard( modifier: Modifier, block: Block, onItemClick: (Block) -> Unit, - arrowDegrees: Float = 0f + arrowDegrees: Float = 0f, ) { Column(modifier = Modifier .clickable { onItemClick(block) } @@ -710,7 +627,7 @@ fun CourseSubSectionItem( downloadedState: DownloadedState?, downloadsCount: Int, onClick: (Block) -> Unit, - onDownloadClick: (Block) -> Unit + onDownloadClick: (Block) -> Unit, ) { val icon = if (block.isCompleted()) painterResource(R.drawable.course_ic_task_alt) else painterResource( @@ -812,7 +729,7 @@ fun CourseSubSectionItem( @Composable fun CourseUnitToolbar( title: String, - onBackClick: () -> Unit + onBackClick: () -> Unit, ) { OpenEdXTheme { Box( @@ -842,7 +759,7 @@ fun SubSectionUnitsTitle( unitName: String, unitsCount: Int, unitsListShowed: Boolean, - onUnitsClick: () -> Unit + onUnitsClick: () -> Unit, ) { val textStyle = MaterialTheme.appTypography.titleMedium val hasUnits = unitsCount > 0 @@ -888,7 +805,7 @@ fun SubSectionUnitsTitle( fun SubSectionUnitsList( unitBlocks: List, selectedUnitIndex: Int = 0, - onUnitClick: (index: Int, unit: Block) -> Unit + onUnitClick: (index: Int, unit: Block) -> Unit, ) { Card( modifier = Modifier @@ -1133,7 +1050,7 @@ fun CourseMessage( icon: Painter, message: String, action: String? = null, - onActionClick: () -> Unit = {} + onActionClick: () -> Unit = {}, ) { Column { Row( diff --git a/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt index e02057ae3..61fc896bf 100644 --- a/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt @@ -37,9 +37,9 @@ class CourseUnitContainerViewModel( private val blocks = ArrayList() - val isCourseExpandableSectionsEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled + val isCourseExpandableSectionsEnabled get() = config.getCourseUIConfig().isCourseNestedListEnabled - val isCourseUnitProgressEnabled get() = config.getUIComponentConfig().isCourseUnitProgressEnabled + val isCourseUnitProgressEnabled get() = config.getCourseUIConfig().isCourseUnitProgressEnabled private var currentIndex = 0 private var currentVerticalIndex = 0 diff --git a/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt b/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt index c89e70aa1..9d52c979b 100644 --- a/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitViewModel.kt @@ -24,7 +24,7 @@ class HtmlUnitViewModel( val injectJSList = _injectJSList.asStateFlow() val isOnline get() = networkConnection.isOnline() - val isCourseUnitProgressEnabled get() = config.getUIComponentConfig().isCourseUnitProgressEnabled + val isCourseUnitProgressEnabled get() = config.getCourseUIConfig().isCourseUnitProgressEnabled val apiHostURL get() = config.getApiHostURL() val cookieManager get() = edxCookieManager diff --git a/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt index 13945ac9b..2cf3d8797 100644 --- a/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt @@ -53,7 +53,7 @@ class CourseVideoViewModel( coreAnalytics ) { - val isCourseNestedListEnabled get() = config.getUIComponentConfig().isCourseNestedListEnabled + val isCourseNestedListEnabled get() = config.getCourseUIConfig().isCourseNestedListEnabled private val _uiState = MutableStateFlow(CourseVideosUIState.Loading) val uiState: StateFlow diff --git a/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt index f7d007782..941fbfdac 100644 --- a/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt @@ -303,7 +303,7 @@ class CourseOutlineViewModelTest { ) } coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -351,7 +351,7 @@ class CourseOutlineViewModelTest { ) } coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -398,7 +398,7 @@ class CourseOutlineViewModelTest { ) } coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -482,7 +482,7 @@ class CourseOutlineViewModelTest { coEvery { workerController.saveModels(any()) } returns Unit coEvery { interactor.getCourseStatus(any()) } returns CourseComponentStatus("id") coEvery { downloadDao.readAllData() } returns flow { emit(emptyList()) } - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", @@ -525,7 +525,7 @@ class CourseOutlineViewModelTest { every { networkConnection.isOnline() } returns true coEvery { workerController.saveModels(any()) } returns Unit coEvery { downloadDao.readAllData() } returns flow { emit(emptyList()) } - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false every { coreAnalytics.logEvent(any(), any()) } returns Unit val viewModel = CourseOutlineViewModel( @@ -562,7 +562,7 @@ class CourseOutlineViewModelTest { every { networkConnection.isOnline() } returns false coEvery { workerController.saveModels(any()) } returns Unit coEvery { downloadDao.readAllData() } returns flow { emit(emptyList()) } - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false val viewModel = CourseOutlineViewModel( "", diff --git a/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt index 0bdf87b0b..a15de0583 100644 --- a/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt @@ -183,7 +183,7 @@ class CourseVideoViewModelTest { @Test fun `getVideos empty list`() = runTest { - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure.copy(blockData = emptyList()) every { downloadDao.readAllData() } returns flow { emit(emptyList()) } @@ -215,7 +215,7 @@ class CourseVideoViewModelTest { @Test fun `getVideos success`() = runTest { - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure every { downloadDao.readAllData() } returns flow { emit(emptyList()) } every { preferencesManager.videoSettings } returns VideoSettings.default @@ -248,7 +248,7 @@ class CourseVideoViewModelTest { @Test fun `updateVideos success`() = runTest { - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure coEvery { courseNotifier.notifier } returns flow { emit(CourseStructureUpdated("")) @@ -291,7 +291,7 @@ class CourseVideoViewModelTest { @Test fun `setIsUpdating success`() = runTest { - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default coEvery { interactor.getCourseStructureForVideos(any()) } returns courseStructure coEvery { downloadDao.readAllData() } returns flow { emit(listOf(downloadModelEntity)) } @@ -300,7 +300,7 @@ class CourseVideoViewModelTest { @Test fun `saveDownloadModels test`() = runTest(UnconfinedTestDispatcher()) { - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default val viewModel = CourseVideoViewModel( "", @@ -337,7 +337,7 @@ class CourseVideoViewModelTest { @Test fun `saveDownloadModels only wifi download, with connection`() = runTest(UnconfinedTestDispatcher()) { - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default val viewModel = CourseVideoViewModel( "", @@ -378,7 +378,7 @@ class CourseVideoViewModelTest { @Test fun `saveDownloadModels only wifi download, without connection`() = runTest(UnconfinedTestDispatcher()) { - every { config.getUIComponentConfig().isCourseNestedListEnabled } returns false + every { config.getCourseUIConfig().isCourseNestedListEnabled } returns false every { preferencesManager.videoSettings } returns VideoSettings.default val viewModel = CourseVideoViewModel( "",