From fe76c3d9838a8e2f75ae00fd51bbbdcbcac9864b Mon Sep 17 00:00:00 2001 From: Igor Date: Thu, 21 Mar 2024 17:32:46 +0100 Subject: [PATCH 1/4] fix: bug when sequence units level appear after tapping on the Back button (#260) --- .../java/org/openedx/app/di/ScreenModule.kt | 1 + .../presentation/dates/CourseDatesFragment.kt | 30 ++++++++++++------- .../dates/CourseDatesViewModel.kt | 4 +++ .../outline/CourseOutlineFragment.kt | 26 +++++++++++----- .../outline/CourseOutlineViewModel.kt | 2 ++ .../dates/CourseDatesViewModelTest.kt | 14 ++++++--- 6 files changed, 55 insertions(+), 22 deletions(-) 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 c74d007a5..a32c35c22 100644 --- a/app/src/main/java/org/openedx/app/di/ScreenModule.kt +++ b/app/src/main/java/org/openedx/app/di/ScreenModule.kt @@ -246,6 +246,7 @@ val screenModule = module { get(), get(), get(), + get(), ) } viewModel { (courseId: String, handoutsType: String) -> diff --git a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt index 39a342634..4b0e175c1 100644 --- a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt @@ -156,16 +156,26 @@ class CourseDatesFragment : Fragment() { onItemClick = { blockId -> if (blockId.isNotEmpty()) { viewModel.getVerticalBlock(blockId)?.let { verticalBlock -> - viewModel.getSequentialBlock(verticalBlock.id) - ?.let { sequentialBlock -> - router.navigateToCourseSubsections( - fm = requireActivity().supportFragmentManager, - subSectionId = sequentialBlock.id, - courseId = viewModel.courseId, - unitId = verticalBlock.id, - mode = CourseViewMode.FULL - ) - } + if (viewModel.isCourseExpandableSectionsEnabled) { + router.navigateToCourseContainer( + fm = requireActivity().supportFragmentManager, + courseId = viewModel.courseId, + unitId = verticalBlock.id, + componentId = "", + mode = CourseViewMode.FULL + ) + } else { + viewModel.getSequentialBlock(verticalBlock.id) + ?.let { sequentialBlock -> + router.navigateToCourseSubsections( + fm = requireActivity().supportFragmentManager, + subSectionId = sequentialBlock.id, + courseId = viewModel.courseId, + unitId = verticalBlock.id, + mode = CourseViewMode.FULL + ) + } + } } } }, 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 2380dbab4..6313b599d 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 @@ -11,6 +11,7 @@ import kotlinx.coroutines.launch import org.openedx.core.BaseViewModel import org.openedx.core.SingleEventLiveData import org.openedx.core.UIMessage +import org.openedx.core.config.Config import org.openedx.core.data.storage.CorePreferences import org.openedx.core.domain.model.Block import org.openedx.core.extension.getSequentialBlocks @@ -37,6 +38,7 @@ class CourseDatesViewModel( private val networkConnection: NetworkConnection, private val resourceManager: ResourceManager, private val corePreferences: CorePreferences, + private val config: Config, ) : BaseViewModel() { private val _uiState = MutableLiveData(DatesUIState.Loading) @@ -64,6 +66,8 @@ class CourseDatesViewModel( val hasInternetConnection: Boolean get() = networkConnection.isOnline() + val isCourseExpandableSectionsEnabled get() = config.isCourseNestedListEnabled() + init { getCourseDates() viewModelScope.launch { diff --git a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineFragment.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineFragment.kt index f00055fa9..86fa2314c 100644 --- a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineFragment.kt @@ -168,14 +168,24 @@ class CourseOutlineFragment : Fragment() { viewModel.resumeSectionBlock?.let { subSection -> viewModel.resumeCourseTappedEvent(subSection.id) viewModel.resumeVerticalBlock?.let { unit -> - router.navigateToCourseSubsections( - requireActivity().supportFragmentManager, - courseId = viewModel.courseId, - subSectionId = subSection.id, - mode = CourseViewMode.FULL, - unitId = unit.id, - componentId = componentId - ) + if (viewModel.isCourseExpandableSectionsEnabled) { + router.navigateToCourseContainer( + fm = requireActivity().supportFragmentManager, + courseId = viewModel.courseId, + unitId = unit.id, + componentId = componentId, + mode = CourseViewMode.FULL + ) + } else { + router.navigateToCourseSubsections( + requireActivity().supportFragmentManager, + courseId = viewModel.courseId, + subSectionId = subSection.id, + mode = CourseViewMode.FULL, + unitId = unit.id, + componentId = componentId + ) + } } } }, 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 cf83fd041..0337f811b 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 @@ -73,6 +73,8 @@ class CourseOutlineViewModel( val hasInternetConnection: Boolean get() = networkConnection.isOnline() + val isCourseExpandableSectionsEnabled get() = config.isCourseNestedListEnabled() + private val courseSubSections = mutableMapOf>() private val subSectionsDownloadsCount = mutableMapOf() val courseSubSectionUnit = mutableMapOf() diff --git a/course/src/test/java/org/openedx/course/presentation/dates/CourseDatesViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/dates/CourseDatesViewModelTest.kt index df7becbc3..752ba30ba 100644 --- a/course/src/test/java/org/openedx/course/presentation/dates/CourseDatesViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/dates/CourseDatesViewModelTest.kt @@ -21,6 +21,7 @@ import org.junit.Test import org.junit.rules.TestRule import org.openedx.core.R import org.openedx.core.UIMessage +import org.openedx.core.config.Config import org.openedx.core.data.model.DateType import org.openedx.core.data.model.User import org.openedx.core.data.storage.CorePreferences @@ -54,6 +55,7 @@ class CourseDatesViewModelTest { private val calendarManager = mockk() private val networkConnection = mockk() private val corePreferences = mockk() + private val config = mockk() private val openEdx = "OpenEdx" private val calendarTitle = "OpenEdx - Abc" @@ -159,7 +161,8 @@ class CourseDatesViewModelTest { calendarManager, networkConnection, resourceManager, - corePreferences + corePreferences, + config ) every { networkConnection.isOnline() } returns true coEvery { interactor.getCourseDates(any()) } throws UnknownHostException() @@ -185,7 +188,8 @@ class CourseDatesViewModelTest { calendarManager, networkConnection, resourceManager, - corePreferences + corePreferences, + config ) every { networkConnection.isOnline() } returns true coEvery { interactor.getCourseDates(any()) } throws Exception() @@ -211,7 +215,8 @@ class CourseDatesViewModelTest { calendarManager, networkConnection, resourceManager, - corePreferences + corePreferences, + config ) every { networkConnection.isOnline() } returns true coEvery { interactor.getCourseDates(any()) } returns mockedCourseDatesResult @@ -236,7 +241,8 @@ class CourseDatesViewModelTest { calendarManager, networkConnection, resourceManager, - corePreferences + corePreferences, + config ) every { networkConnection.isOnline() } returns true coEvery { interactor.getCourseDates(any()) } returns CourseDatesResult( From cc295d9811d6b66ccf9ca66b0c7b84850db4dc60 Mon Sep 17 00:00:00 2001 From: Volodymyr Chekyrta <127732735+volodymyr-chekyrta@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:30:40 +0200 Subject: [PATCH 2/4] fix: Android Studio Iguana build issues (#265) --- .../signup/RegistrationScreenTest.kt | 165 ------------------ build.gradle | 4 +- buildSrc/build.gradle | 9 + .../detail/CourseDetailsScreenTest.kt | 134 -------------- .../outline/CourseOutlineScreenTest.kt | 104 ----------- .../presentation/DiscoveryScreenTest.kt | 152 ---------------- settings.gradle | 5 + 7 files changed, 16 insertions(+), 557 deletions(-) delete mode 100644 auth/src/androidTest/java/org/openedx/auth/presentation/signup/RegistrationScreenTest.kt delete mode 100644 course/src/androidTest/java/org/openedx/course/presentation/detail/CourseDetailsScreenTest.kt delete mode 100644 course/src/androidTest/java/org/openedx/course/presentation/outline/CourseOutlineScreenTest.kt delete mode 100644 discovery/src/androidTest/java/org/openedx/discovery/presentation/DiscoveryScreenTest.kt diff --git a/auth/src/androidTest/java/org/openedx/auth/presentation/signup/RegistrationScreenTest.kt b/auth/src/androidTest/java/org/openedx/auth/presentation/signup/RegistrationScreenTest.kt deleted file mode 100644 index 845e39034..000000000 --- a/auth/src/androidTest/java/org/openedx/auth/presentation/signup/RegistrationScreenTest.kt +++ /dev/null @@ -1,165 +0,0 @@ -package org.openedx.auth.presentation.signup - -import androidx.activity.ComponentActivity -import androidx.compose.ui.semantics.ProgressBarRangeInfo -import androidx.compose.ui.test.* -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import org.openedx.auth.R -import org.openedx.core.domain.model.RegistrationField -import org.openedx.core.domain.model.RegistrationFieldType -import org.openedx.core.ui.WindowSize -import org.openedx.core.ui.WindowType -import org.junit.Rule -import org.junit.Test - -class RegistrationScreenTest { - - @get:Rule - val composeTestRule = createAndroidComposeRule() - - //region mockField - private val option = RegistrationField.Option("def", "Bachelor", "Android") - - private val mockField = RegistrationField( - "Fullname", - "Fullname", - RegistrationFieldType.TEXT, - "Fullname", - instructions = "Enter your fullname", - exposed = false, - required = true, - restrictions = RegistrationField.Restrictions(), - options = listOf(option, option), - errorInstructions = "" - ) - //endregion - - - @Test - fun signUpLoadingFields() { - composeTestRule.setContent { - org.openedx.auth.presentation.signup.RegistrationScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - uiState = org.openedx.auth.presentation.signup.SignUpUIState.Loading, - uiMessage = null, - isButtonClicked = false, - validationError = false, - onBackClick = {}, - onRegisterClick = {} - ) - } - with(composeTestRule) { - onRoot().printToLog("ROOT_TAG") - - onNode(hasProgressBarRangeInfo(ProgressBarRangeInfo(0f, 0f..0f))).assertExists() - onNode(hasScrollAction().and(hasAnyChild(hasText(activity.getString(R.string.auth_sign_up))))).assertDoesNotExist() - } - } - - @Test - fun signUpNoOptionalFields() { - composeTestRule.setContent { - org.openedx.auth.presentation.signup.RegistrationScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields( - fields = listOf( - mockField, - mockField.copy(name = "Age", label = "Age", errorInstructions = "error") - ), - optionalFields = emptyList() - ), - uiMessage = null, - isButtonClicked = false, - validationError = false, - onBackClick = {}, - onRegisterClick = {} - ) - } - with(composeTestRule) { - onNode(hasText("Fullname").and(hasSetTextAction())).assertExists() - - onNode(hasText("Age").and(hasSetTextAction())).assertExists() - - onNodeWithText(activity.getString(R.string.auth_show_optional_fields)).assertDoesNotExist() - - onNode(hasText(activity.getString(R.string.auth_create_account)).and(hasClickAction())).assertExists() - } - } - - @Test - fun signUpHasOptionalFields() { - composeTestRule.setContent { - org.openedx.auth.presentation.signup.RegistrationScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields( - fields = listOf( - mockField, - mockField.copy(name = "Age", label = "Age", errorInstructions = "error") - ), - optionalFields = listOf(mockField) - ), - uiMessage = null, - isButtonClicked = false, - validationError = false, - onBackClick = {}, - onRegisterClick = {} - ) - } - with(composeTestRule) { - onNode(hasText("Age").and(hasSetTextAction())).assertExists() - - onNodeWithText(activity.getString(R.string.auth_show_optional_fields)).assertExists() - - onNode(hasText(activity.getString(R.string.auth_create_account)).and(hasClickAction())).assertExists() - } - } - - @Test - fun signUpFieldsWithError() { - composeTestRule.setContent { - org.openedx.auth.presentation.signup.RegistrationScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields( - fields = listOf( - mockField, - mockField.copy(name = "Age", label = "Age", errorInstructions = "error") - ), - optionalFields = emptyList() - ), - uiMessage = null, - isButtonClicked = false, - validationError = false, - onBackClick = {}, - onRegisterClick = {} - ) - } - with(composeTestRule) { - onNode(hasText("error")).assertExists() - } - } - - @Test - fun signUpCreateAccountClicked() { - composeTestRule.setContent { - org.openedx.auth.presentation.signup.RegistrationScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields( - fields = listOf( - mockField, - mockField.copy(name = "Age", label = "Age", errorInstructions = "error") - ), - optionalFields = listOf(mockField) - ), - uiMessage = null, - isButtonClicked = true, - validationError = false, - onBackClick = {}, - onRegisterClick = {} - ) - } - with(composeTestRule) { - onNode(hasProgressBarRangeInfo(ProgressBarRangeInfo(0f, 0f..0f))).assertExists() - onNode(hasText(activity.getString(R.string.auth_create_account)).and(hasClickAction())).assertDoesNotExist() - } - } -} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8e61cf92d..6ebb49a56 100644 --- a/build.gradle +++ b/build.gradle @@ -20,8 +20,8 @@ plugins { id "com.google.firebase.crashlytics" version "2.9.6" apply false } -task clean(type: Delete) { - delete rootProject.buildDir +tasks.register('clean', Delete) { + delete rootProject.layout.buildDirectory } ext { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 9cc60ef40..f1d8de5cb 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,7 +1,16 @@ +plugins { + id 'java-library' +} + repositories { mavenCentral() } +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + dependencies { implementation localGroovy() implementation gradleApi() diff --git a/course/src/androidTest/java/org/openedx/course/presentation/detail/CourseDetailsScreenTest.kt b/course/src/androidTest/java/org/openedx/course/presentation/detail/CourseDetailsScreenTest.kt deleted file mode 100644 index 5d79f2074..000000000 --- a/course/src/androidTest/java/org/openedx/course/presentation/detail/CourseDetailsScreenTest.kt +++ /dev/null @@ -1,134 +0,0 @@ -package org.openedx.course.presentation.detail - -import androidx.activity.ComponentActivity -import androidx.compose.ui.semantics.ProgressBarRangeInfo -import androidx.compose.ui.test.* -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import org.openedx.core.domain.model.* -import org.openedx.course.R -import org.junit.Rule -import org.junit.Test - -class CourseDetailsScreenTest { - - @get:Rule - val composeTestRule = createAndroidComposeRule() - - //region course - - private val course = Course( - id = "id", - blocksUrl = "blocksUrl", - courseId = "courseId", - effort = "effort", - enrollmentStart = "enrollmentStart", - enrollmentEnd = "enrollmentEnd", - hidden = false, - invitationOnly = false, - media = Media(), - mobileAvailable = true, - name = "Test course", - number = "number", - org = "EdX", - pacing = "pacing", - shortDescription = "shortDescription", - start = "start", - end = "end", - startDisplay = "startDisplay", - startType = "startType", - overview = "" - ) - - private val enrolledCourse = EnrolledCourse( - auditAccessExpires = "", - created = "created", - certificate = Certificate(""), - mode = "mode", - isActive = true, - course = EnrolledCourseData( - id = "id", - name = "name", - number = "", - org = "Org", - start = "", - startDisplay = "", - startType = "", - end = "Ending in 22 November", - dynamicUpgradeDeadline = "", - subscriptionId = "", - coursewareAccess = CoursewareAccess( - true, - "", - "", - "", - "", - "" - ), - media = null, - courseImage = "", - courseAbout = "", - courseSharingUtmParameters = CourseSharingUtmParameters("", ""), - courseUpdates = "", - courseHandouts = "", - discussionUrl = "", - videoOutline = "", - isSelfPaced = false - ) - ) - - //endregion - - @Test - fun courseDetailsScreenLoading() { - composeTestRule.setContent { - CourseDetailsScreen( - uiState = CourseDetailsUIState.Loading, - uiMessage = null, - htmlBody = "", - onBackClick = {}, - onButtonClick = {} - ) - } - - with(composeTestRule) { - onNode( - hasProgressBarRangeInfo( - ProgressBarRangeInfo( - current = 0f, - range = 0f..0f, - steps = 0 - ) - ) - ).assertExists() - - onNode( - hasClickAction() and hasTextExactly( - activity.getString(R.string.course_view_course), - activity.getString(R.string.course_register_now) - ) - ).assertDoesNotExist() - } - } - - @Test - fun courseDetailsScreenLoaded() { - composeTestRule.setContent { - CourseDetailsScreen( - uiState = CourseDetailsUIState.CourseData(course, enrolledCourse), - uiMessage = null, - htmlBody = "", - onBackClick = {}, - onButtonClick = {} - ) - } - - with(composeTestRule) { - onNode( - hasClickAction() and hasText( - activity.getString(R.string.course_view_course), - ignoreCase = true - ) - ).assertExists() - } - } -} \ No newline at end of file diff --git a/course/src/androidTest/java/org/openedx/course/presentation/outline/CourseOutlineScreenTest.kt b/course/src/androidTest/java/org/openedx/course/presentation/outline/CourseOutlineScreenTest.kt deleted file mode 100644 index dd1ecce8f..000000000 --- a/course/src/androidTest/java/org/openedx/course/presentation/outline/CourseOutlineScreenTest.kt +++ /dev/null @@ -1,104 +0,0 @@ -package org.openedx.course.presentation.outline - -import androidx.activity.ComponentActivity -import androidx.compose.ui.semantics.ProgressBarRangeInfo -import androidx.compose.ui.test.* -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import org.openedx.core.BlockType -import org.openedx.core.domain.model.Block -import org.openedx.core.domain.model.BlockCounts -import org.openedx.core.domain.model.Certificate -import org.openedx.course.R -import org.junit.Rule -import org.junit.Test - -class CourseOutlineScreenTest { - - @get:Rule - val composeTestRule = createAndroidComposeRule() - - //region block - private val mockBlock = Block( - id = "id", - blockId = "blockId", - lmsWebUrl = "lmsWebUrl", - legacyWebUrl = "legacyWebUrl", - studentViewUrl = "studentViewUrl", - type = BlockType.HTML, - displayName = "Block", - graded = false, - studentViewData = null, - studentViewMultiDevice = false, - blockCounts = BlockCounts(0), - descendants = emptyList(), - completion = 0.0 - ) - //endregion - - @Test - fun courseOutlineLoading() { - composeTestRule.setContent { - CourseOutlineScreen( - uiState = CourseOutlineUIState.Loading, - courseTitle = "Title", - courseImage = "", - courseCertificate = Certificate(""), - uiMessage = null, - refreshing = false, - onSwipeRefresh = {}, - onItemClick = {}, - onResumeClick = {}, - onBackClick = {} - ) - } - - with(composeTestRule) { - onNode( - hasProgressBarRangeInfo( - ProgressBarRangeInfo( - current = 0f, - range = 0f..0f, - steps = 0 - ) - ) - ).assertExists() - - onNode(hasText(activity.getString(R.string.course_content))).assertDoesNotExist() - } - } - - - @Test - fun courseOutlineLoaded() { - composeTestRule.setContent { - CourseOutlineScreen( - uiState = CourseOutlineUIState.CourseData(listOf(mockBlock, mockBlock), null), - courseTitle = "Title", - courseImage = "", - courseCertificate = Certificate(""), - uiMessage = null, - refreshing = false, - onSwipeRefresh = {}, - onItemClick = {}, - onResumeClick = {}, - onBackClick = {} - ) - } - - with(composeTestRule) { - onNode( - hasProgressBarRangeInfo( - ProgressBarRangeInfo( - current = 0f, - range = 0f..0f, - steps = 0 - ) - ) - ).assertDoesNotExist() - - onNode(hasScrollAction() and hasAnyChild(hasText(mockBlock.displayName))).assertExists() - onNode(hasScrollAction()).onChildren().assertCountEquals(3) - onNode(hasText(activity.getString(R.string.course_content))).assertExists() - } - } -} \ No newline at end of file diff --git a/discovery/src/androidTest/java/org/openedx/discovery/presentation/DiscoveryScreenTest.kt b/discovery/src/androidTest/java/org/openedx/discovery/presentation/DiscoveryScreenTest.kt deleted file mode 100644 index 6a70f334b..000000000 --- a/discovery/src/androidTest/java/org/openedx/discovery/presentation/DiscoveryScreenTest.kt +++ /dev/null @@ -1,152 +0,0 @@ -package org.openedx.discovery.presentation - -import androidx.activity.ComponentActivity -import androidx.compose.ui.semantics.ProgressBarRangeInfo -import androidx.compose.ui.test.assertAny -import androidx.compose.ui.test.hasAnyChild -import androidx.compose.ui.test.hasProgressBarRangeInfo -import androidx.compose.ui.test.hasScrollAction -import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onChildren -import org.junit.Rule -import org.junit.Test -import org.openedx.core.AppUpdateState -import org.openedx.core.domain.model.Course -import org.openedx.core.domain.model.Media -import org.openedx.core.ui.WindowSize -import org.openedx.core.ui.WindowType -import java.util.Date - -class DiscoveryScreenTest { - - @get:Rule - val composeTestRule = createAndroidComposeRule() - - //region mockCourse - private val course = Course( - id = "id", - blocksUrl = "blocksUrl", - courseId = "courseId", - effort = "effort", - enrollmentStart = Date(), - enrollmentEnd = null, - hidden = false, - invitationOnly = false, - media = Media(), - mobileAvailable = true, - name = "Test course", - number = "number", - org = "EdX", - pacing = "pacing", - shortDescription = "shortDescription", - start = "start", - end = "end", - startDisplay = "startDisplay", - startType = "startType", - overview = "", - isEnrolled = false - ) - //endregion - - @Test - fun discoveryScreenLoading() { - composeTestRule.setContent { - DiscoveryScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - state = DiscoveryUIState.Loading, - uiMessage = null, - canLoadMore = false, - refreshing = false, - hasInternetConnection = true, - canShowBackButton = false, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters(), - onSearchClick = {}, - onSwipeRefresh = {}, - paginationCallback = {}, - onItemClick = {}, - onReloadClick = {}, - onBackClick = {}, - ) - } - - with(composeTestRule) { - onNode( - hasProgressBarRangeInfo( - ProgressBarRangeInfo( - current = 0f, - range = 0f..0f, - steps = 0 - ) - ) - ) - } - - } - - @Test - fun discoveryScreenLoaded() { - composeTestRule.setContent { - DiscoveryScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - state = DiscoveryUIState.Courses(listOf(course)), - uiMessage = null, - canLoadMore = false, - refreshing = false, - hasInternetConnection = true, - canShowBackButton = false, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters(), - onSearchClick = {}, - onSwipeRefresh = {}, - paginationCallback = {}, - onItemClick = {}, - onReloadClick = {}, - onBackClick = {}, - ) - } - - with(composeTestRule) { - onNode(hasScrollAction()).onChildren().assertAny(hasText(course.name)) - } - } - - @Test - fun discoveryScreenPaginationAvailable() { - composeTestRule.setContent { - DiscoveryScreen( - windowSize = WindowSize(WindowType.Compact, WindowType.Compact), - state = DiscoveryUIState.Courses(listOf(course)), - uiMessage = null, - canLoadMore = true, - refreshing = false, - hasInternetConnection = true, - canShowBackButton = false, - appUpgradeParameters = AppUpdateState.AppUpgradeParameters(), - onSearchClick = {}, - onSwipeRefresh = {}, - paginationCallback = {}, - onItemClick = {}, - onReloadClick = {}, - onBackClick = {}, - ) - } - - with(composeTestRule) { - onNode(hasScrollAction()).onChildren().assertAny(hasText(course.name)) - onNode( - hasScrollAction().and( - hasAnyChild( - hasProgressBarRangeInfo( - ProgressBarRangeInfo( - current = 0f, - range = 0f..0f, - steps = 0 - ) - ) - ) - ) - ) - } - - } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 205f840ee..66cb04c11 100644 --- a/settings.gradle +++ b/settings.gradle @@ -32,6 +32,11 @@ dependencyResolutionManagement { maven { url "https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1" } } } +//Workaround for AS Iguana https://github.com/gradle/gradle/issues/28407 +//When Android Studio Iguana or later attempts to rebuild the project, the build fails with: +//Unable to make progress running work. There are items queued for execution but none of them can be started +gradle.startParameter.excludedTaskNames.addAll([":buildSrc:testClasses"]) + rootProject.name = "OpenEdX" include ':app' include ':core' From f49283d3d5ab4aa764ec39a96cc169b6e00f0a8b Mon Sep 17 00:00:00 2001 From: Igor Date: Mon, 25 Mar 2024 10:36:19 +0100 Subject: [PATCH 3/4] fix: bug when the name of subcategory is empty (#263) --- .../java/org/openedx/app/di/ScreenModule.kt | 2 +- .../data/repository/DiscussionRepository.kt | 20 ++++++++-- .../discussion/domain/model/TopicsData.kt | 1 + .../presentation/ui/DiscussionUI.kt | 40 +++++++++++++++---- discussion/src/main/res/values/strings.xml | 1 + 5 files changed, 53 insertions(+), 11 deletions(-) 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 a32c35c22..8c0add1c3 100644 --- a/app/src/main/java/org/openedx/app/di/ScreenModule.kt +++ b/app/src/main/java/org/openedx/app/di/ScreenModule.kt @@ -260,7 +260,7 @@ val screenModule = module { viewModel { CourseSearchViewModel(get(), get(), get(), get(), get()) } viewModel { SelectDialogViewModel(get()) } - single { DiscussionRepository(get(), get()) } + single { DiscussionRepository(get(), get(), get()) } factory { DiscussionInteractor(get()) } viewModel { (courseId: String) -> DiscussionTopicsViewModel(get(), get(), get(), courseId) } viewModel { (courseId: String, topicId: String, threadType: String) -> diff --git a/discussion/src/main/java/org/openedx/discussion/data/repository/DiscussionRepository.kt b/discussion/src/main/java/org/openedx/discussion/data/repository/DiscussionRepository.kt index b95b5447d..4ca6cde8d 100644 --- a/discussion/src/main/java/org/openedx/discussion/data/repository/DiscussionRepository.kt +++ b/discussion/src/main/java/org/openedx/discussion/data/repository/DiscussionRepository.kt @@ -2,22 +2,31 @@ package org.openedx.discussion.data.repository import org.openedx.core.data.model.BlocksCompletionBody import org.openedx.core.data.storage.CorePreferences +import org.openedx.core.system.ResourceManager +import org.openedx.discussion.R import org.openedx.discussion.data.api.DiscussionApi -import org.openedx.discussion.data.model.request.* +import org.openedx.discussion.data.model.request.CommentBody +import org.openedx.discussion.data.model.request.FollowBody +import org.openedx.discussion.data.model.request.ReadBody +import org.openedx.discussion.data.model.request.ReportBody +import org.openedx.discussion.data.model.request.ThreadBody +import org.openedx.discussion.data.model.request.VoteBody import org.openedx.discussion.domain.model.CommentsData import org.openedx.discussion.domain.model.ThreadsData import org.openedx.discussion.domain.model.Topic class DiscussionRepository( private val api: DiscussionApi, - private val preferencesManager: CorePreferences - ) { + private val preferencesManager: CorePreferences, + private val resourceManager: ResourceManager +) { private val topics = mutableListOf() private var currentCourseId = "" suspend fun getCourseTopics(courseId: String): List { val topicsData = api.getCourseTopics(courseId).mapToDomain() + val defaultTopicName = resourceManager.getString(R.string.discussion_unnamed_subcategory) currentCourseId = courseId topics.clear() topics.addAll(topicsData.nonCoursewareTopics) @@ -27,6 +36,11 @@ class DiscussionRepository( topics.addAll(it.children) } } + topics.forEachIndexed { index, topic -> + if (topic.name.isBlank()) { + topics[index] = topic.copy(name = defaultTopicName) + } + } return topics.toList() } diff --git a/discussion/src/main/java/org/openedx/discussion/domain/model/TopicsData.kt b/discussion/src/main/java/org/openedx/discussion/domain/model/TopicsData.kt index 6a3f5c5d5..12d9ad926 100644 --- a/discussion/src/main/java/org/openedx/discussion/domain/model/TopicsData.kt +++ b/discussion/src/main/java/org/openedx/discussion/domain/model/TopicsData.kt @@ -1,5 +1,6 @@ package org.openedx.discussion.domain.model + data class TopicsData( val coursewareTopics: List, val nonCoursewareTopics: List diff --git a/discussion/src/main/java/org/openedx/discussion/presentation/ui/DiscussionUI.kt b/discussion/src/main/java/org/openedx/discussion/presentation/ui/DiscussionUI.kt index 285b01d66..8d6323bfd 100644 --- a/discussion/src/main/java/org/openedx/discussion/presentation/ui/DiscussionUI.kt +++ b/discussion/src/main/java/org/openedx/discussion/presentation/ui/DiscussionUI.kt @@ -8,9 +8,24 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.* +import androidx.compose.material.Card +import androidx.compose.material.Divider +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ChevronRight import androidx.compose.material.icons.outlined.HelpOutline @@ -94,7 +109,10 @@ fun ThreadMainItem( .error(org.openedx.core.R.drawable.core_ic_default_profile_picture) .placeholder(org.openedx.core.R.drawable.core_ic_default_profile_picture) .build(), - contentDescription = stringResource(id = org.openedx.core.R.string.core_accessibility_user_profile_image, thread.author), + contentDescription = stringResource( + id = org.openedx.core.R.string.core_accessibility_user_profile_image, + thread.author + ), modifier = Modifier .size(48.dp) .clip(MaterialTheme.appShapes.material.medium) @@ -245,7 +263,10 @@ fun CommentItem( .error(org.openedx.core.R.drawable.core_ic_default_profile_picture) .placeholder(org.openedx.core.R.drawable.core_ic_default_profile_picture) .build(), - contentDescription = stringResource(id = org.openedx.core.R.string.core_accessibility_user_profile_image, comment.author), + contentDescription = stringResource( + id = org.openedx.core.R.string.core_accessibility_user_profile_image, + comment.author + ), modifier = Modifier .size(32.dp) .clip(CircleShape) @@ -395,7 +416,10 @@ fun CommentMainItem( .error(org.openedx.core.R.drawable.core_ic_default_profile_picture) .placeholder(org.openedx.core.R.drawable.core_ic_default_profile_picture) .build(), - contentDescription = stringResource(id = org.openedx.core.R.string.core_accessibility_user_profile_image, comment.author), + contentDescription = stringResource( + id = org.openedx.core.R.string.core_accessibility_user_profile_image, + comment.author + ), modifier = Modifier .size(32.dp) .clip(CircleShape) @@ -504,7 +528,8 @@ fun ThreadItem( text = textType, painter = icon, color = MaterialTheme.appColors.textPrimaryVariant, - textStyle = MaterialTheme.appTypography.labelSmall) + textStyle = MaterialTheme.appTypography.labelSmall + ) if (thread.unreadCommentCount > 0 && !thread.read) { Row( modifier = Modifier, @@ -562,7 +587,8 @@ fun ThreadItem( ), painter = painterResource(id = R.drawable.discussion_ic_responses), color = MaterialTheme.appColors.textAccent, - textStyle = MaterialTheme.appTypography.labelLarge) + textStyle = MaterialTheme.appTypography.labelLarge + ) } } diff --git a/discussion/src/main/res/values/strings.xml b/discussion/src/main/res/values/strings.xml index 8d66fc99a..cf50a9dbe 100644 --- a/discussion/src/main/res/values/strings.xml +++ b/discussion/src/main/res/values/strings.xml @@ -34,6 +34,7 @@ anonymous No discussions yet Click the button below to create your first discussion. + Unnamed subcategory From f13bd4749c2c029e1e1134500a1100a7115c2413 Mon Sep 17 00:00:00 2001 From: Volodymyr Chekyrta <127732735+volodymyr-chekyrta@users.noreply.github.com> Date: Tue, 26 Mar 2024 08:40:53 +0200 Subject: [PATCH 4/4] fix: Toolbar alignment (#268) --- core/src/main/java/org/openedx/core/ui/ComposeCommon.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 afa8d7b12..7114426c1 100644 --- a/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt +++ b/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt @@ -152,7 +152,7 @@ fun Toolbar( canShowBackBtn: Boolean = false, onBackClick: () -> Unit = {} ) { - Row( + Box( modifier = modifier .fillMaxWidth() .height(48.dp), @@ -163,9 +163,10 @@ fun Toolbar( Text( modifier = Modifier + .fillMaxWidth() .testTag("txt_toolbar_title") - .align(Alignment.CenterVertically) - .padding(end = 16.dp), + .align(Alignment.Center) + .padding(start = 48.dp, end = 48.dp), text = label, color = MaterialTheme.appColors.textPrimary, style = MaterialTheme.appTypography.titleMedium,