Skip to content

Commit 23797f1

Browse files
committed
feat: locked content support
1 parent 66e9a79 commit 23797f1

File tree

14 files changed

+270
-0
lines changed

14 files changed

+270
-0
lines changed

app/src/main/java/org/openedx/app/di/ScreenModule.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ val screenModule = module {
293293
get(),
294294
get(),
295295
get(),
296+
get(),
296297
)
297298
}
298299
viewModel { (courseId: String, unitId: String) ->

core/src/main/java/org/openedx/core/data/api/CourseApi.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.openedx.core.data.model.CourseStructureModel
1212
import org.openedx.core.data.model.EnrollmentStatus
1313
import org.openedx.core.data.model.HandoutsModel
1414
import org.openedx.core.data.model.ResetCourseDates
15+
import org.openedx.core.data.model.SequenceModel
1516
import retrofit2.http.Body
1617
import retrofit2.http.GET
1718
import retrofit2.http.Header
@@ -100,4 +101,7 @@ interface CourseApi {
100101
suspend fun getEnrollmentDetails(
101102
@Path("course_id") courseId: String,
102103
): CourseEnrollmentDetails
104+
105+
@GET("api/courseware/sequence/{sequence_id}/")
106+
suspend fun getSequence(@Path("sequence_id") sequenceId: String): SequenceModel
103107
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.openedx.core.data.model
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
import org.openedx.core.domain.model.GatedContent
6+
7+
data class GatedContentModel(
8+
@SerializedName("prereq_id")
9+
val prereqId: String?,
10+
@SerializedName("prereq_url")
11+
val prereqUrl: String?,
12+
@SerializedName("prereq_section_name")
13+
val prereqSectionName: String?,
14+
@SerializedName("gated")
15+
val gated: Boolean,
16+
@SerializedName("gated_section_name")
17+
val gatedSectionName: String?,
18+
) {
19+
fun mapToDomain(): GatedContent {
20+
return GatedContent(
21+
prereqId = prereqId,
22+
prereqUrl = prereqUrl,
23+
prereqSubsectionName = prereqSectionName,
24+
gated = gated,
25+
gatedSubsectionName = gatedSectionName
26+
)
27+
}
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.openedx.core.data.model
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
import org.openedx.core.domain.model.Subsection
6+
7+
data class SequenceModel(
8+
@SerializedName("element_id")
9+
val elementId: String,
10+
@SerializedName("item_id")
11+
val itemId: String,
12+
@SerializedName("banner_text")
13+
val bannerText: String?,
14+
@SerializedName("gated_content")
15+
val gatedContentModel: GatedContentModel,
16+
@SerializedName("sequence_name")
17+
val sequenceName: String,
18+
@SerializedName("display_name")
19+
val displayName: String,
20+
) {
21+
fun mapToDomain(): Subsection {
22+
return Subsection(
23+
elementId = elementId,
24+
itemId = itemId,
25+
bannerText = bannerText,
26+
subsectionName = sequenceName,
27+
displayName = displayName,
28+
gatedContent = gatedContentModel.mapToDomain(),
29+
)
30+
}
31+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.openedx.core.domain.model;
2+
3+
import android.os.Parcelable;
4+
import kotlinx.parcelize.Parcelize;
5+
6+
@Parcelize
7+
data class GatedContent(
8+
val prereqId: String?,
9+
val prereqUrl: String?,
10+
val prereqSubsectionName: String?,
11+
val gated: Boolean,
12+
val gatedSubsectionName: String?
13+
) : Parcelable;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.openedx.core.domain.model;
2+
3+
import android.os.Parcelable;
4+
import kotlinx.parcelize.Parcelize;
5+
6+
@Parcelize
7+
data class Subsection(
8+
val elementId: String,
9+
val itemId: String,
10+
val bannerText: String?,
11+
val gatedContent: GatedContent,
12+
val subsectionName: String,
13+
val displayName: String
14+
) : Parcelable;

course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,6 @@ class CourseRepository(
139139
downloadDao.removeOfflineXBlockProgress(listOf(blockId))
140140
}
141141
}
142+
143+
suspend fun getSequence(sequenceId: String) = api.getSequence(sequenceId).mapToDomain()
142144
}

course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ class CourseInteractor(
8282

8383
suspend fun removeDownloadModel(id: String) = repository.removeDownloadModel(id)
8484

85+
suspend fun getSubsection(subsectionId: String) = repository.getSequence(subsectionId)
86+
8587
fun getDownloadModels() = repository.getDownloadModels()
8688

8789
suspend fun getAllDownloadModels() = repository.getAllDownloadModels()

course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ enum class CourseAnalyticsEvent(val eventName: String, val biValue: String) {
7878
"Course:Unit Detail",
7979
"edx.bi.app.course.unit_detail"
8080
),
81+
PREREQUISITE(
82+
"Course:Prerequisite",
83+
"edx.bi.app.course.prerequisite"
84+
),
8185
VIEW_CERTIFICATE(
8286
"Course:View Certificate Clicked",
8387
"edx.bi.app.course.view_certificate.clicked"

course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.content.res.Configuration
44
import android.os.Bundle
55
import android.view.LayoutInflater
66
import android.view.ViewGroup
7+
import androidx.compose.foundation.Image
78
import androidx.compose.foundation.clickable
89
import androidx.compose.foundation.layout.Arrangement
910
import androidx.compose.foundation.layout.Box
@@ -17,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
1718
import androidx.compose.foundation.layout.height
1819
import androidx.compose.foundation.layout.navigationBarsPadding
1920
import androidx.compose.foundation.layout.padding
21+
import androidx.compose.foundation.layout.size
2022
import androidx.compose.foundation.layout.width
2123
import androidx.compose.foundation.layout.widthIn
2224
import androidx.compose.foundation.lazy.LazyColumn
@@ -61,6 +63,7 @@ import org.openedx.core.domain.model.BlockCounts
6163
import org.openedx.core.presentation.course.CourseViewMode
6264
import org.openedx.core.ui.BackBtn
6365
import org.openedx.core.ui.HandleUIMessage
66+
import org.openedx.core.ui.OpenEdXButton
6467
import org.openedx.core.ui.displayCutoutForLandscape
6568
import org.openedx.core.ui.statusBarsInset
6669
import org.openedx.core.ui.theme.OpenEdXTheme
@@ -124,6 +127,15 @@ class CourseSectionFragment : Fragment() {
124127
)
125128
}
126129
},
130+
onGoToPrerequisiteClick = { subSectionId ->
131+
viewModel.goToPrerequisiteSectionClickedEvent(subSectionId)
132+
router.navigateToCourseSubsections(
133+
fm = requireActivity().supportFragmentManager,
134+
courseId = viewModel.courseId,
135+
subSectionId = subSectionId,
136+
mode = CourseViewMode.FULL
137+
)
138+
}
127139
)
128140

129141
LaunchedEffect(rememberSaveable { true }) {
@@ -176,6 +188,7 @@ private fun CourseSectionScreen(
176188
uiMessage: UIMessage?,
177189
onBackClick: () -> Unit,
178190
onItemClick: (Block) -> Unit,
191+
onGoToPrerequisiteClick: (String) -> Unit
179192
) {
180193
val scaffoldState = rememberScaffoldState()
181194
val title = when (uiState) {
@@ -256,6 +269,41 @@ private fun CourseSectionScreen(
256269
}
257270
}
258271

272+
is CourseSectionUIState.Gated -> {
273+
Column(
274+
modifier = Modifier.fillMaxSize(),
275+
verticalArrangement = Arrangement.Center,
276+
horizontalAlignment = Alignment.CenterHorizontally
277+
) {
278+
Image(
279+
painter = painterResource(id = R.drawable.ic_course_gated),
280+
contentDescription = "gated",
281+
modifier = Modifier.size(48.dp)
282+
)
283+
Spacer(modifier = Modifier.height(16.dp))
284+
Text(
285+
modifier = Modifier
286+
.padding(horizontal = 16.dp)
287+
.fillMaxWidth(),
288+
text = stringResource(
289+
id = R.string.course_gated_subsection,
290+
uiState.prereqSubsectionName ?: ""
291+
),
292+
textAlign = TextAlign.Center,
293+
style = MaterialTheme.appTypography.titleMedium,
294+
color = MaterialTheme.appColors.textPrimary,
295+
)
296+
Spacer(modifier = Modifier.height(16.dp))
297+
OpenEdXButton(
298+
text = stringResource(id = R.string.course_go_to_prerequisite_section),
299+
onClick = {
300+
onGoToPrerequisiteClick(uiState.prereqId ?: "")
301+
},
302+
modifier = Modifier.padding(top = 16.dp)
303+
)
304+
}
305+
}
306+
259307
is CourseSectionUIState.Blocks -> {
260308
Column(Modifier.fillMaxSize()) {
261309
LazyColumn(
@@ -361,6 +409,7 @@ private fun CourseSectionScreenPreview() {
361409
uiMessage = null,
362410
onBackClick = {},
363411
onItemClick = {},
412+
onGoToPrerequisiteClick = {}
364413
)
365414
}
366415
}
@@ -385,6 +434,29 @@ private fun CourseSectionScreenTabletPreview() {
385434
uiMessage = null,
386435
onBackClick = {},
387436
onItemClick = {},
437+
onGoToPrerequisiteClick = {}
438+
)
439+
}
440+
}
441+
442+
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
443+
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
444+
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, device = Devices.NEXUS_9)
445+
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
446+
@Composable
447+
private fun CourseSectionScreenGatedPreview() {
448+
OpenEdXTheme {
449+
CourseSectionScreen(
450+
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
451+
uiState = CourseSectionUIState.Gated(
452+
"Gated Subsection",
453+
"Prerequisite Subsection",
454+
"Prerequisite Id"
455+
),
456+
uiMessage = null,
457+
onBackClick = {},
458+
onItemClick = {},
459+
onGoToPrerequisiteClick = {}
388460
)
389461
}
390462
}

0 commit comments

Comments
 (0)