Skip to content

Commit

Permalink
feat: [FC-0047] Course progress and collapsing sections (#323)
Browse files Browse the repository at this point in the history
* feat: Course Home progress bar

* feat: Collapsing course sections

* feat: New download icons

* feat: show CourseContainerFragment if COURSE_NESTED_LIST_ENABLED false

* fix: course progress bar updating

* feat: Renamed COURSE_NESTED_LIST_ENABLE feature flag

* feat: Course home. Moved certificate access.

* chore: enhance app theme capability for prod edX theme/branding (#262)

chore: enhance app theme capability for prod edX theme/branding

- Integrate Program config updates
- theming/branding code improvements for light and dark modes
- Force dark mode for the WebView (beta version)
- No major change in the Open edX theme

fixes: LEARNER-9783

* feat: [FC-0047] Calendar main screen and dialogs (#322)

* feat: Created calendar setting screen

* feat: CalendarAccessDialog

* feat: NewCalendarDialog

* fix: Fixes according to PR feedback

* fix: DiscussionTopicsViewModelTest.kt jUnit test

* fix: assignment dates

* feat: [FC-0047] Improved Dashboard Level Navigation (#308)

* feat: Created Learn screen. Added course/program navigation. Added endpoint for UserCourses screen.

* feat: Added primary course card

* feat: Added start/resume course button

* feat: Added alignment items

* feat: Fix future assignment date, add courses list, add onSearch and onCourse clicks

* feat: Add feature flag for enabling new/old dashboard screen, add UserCoursesScreen onClick methods

* feat: Create AllEnrolledCoursesFragment. Add endpoint parameters

* feat: AllEnrolledCoursesFragment UI

* feat: Minor code refactoring, show cached data if no internet connection

* feat: UserCourses screen data caching

* feat: Dashboard

* refactor: Dashboard type flag change, start course button change

* feat: Added programs fragment to LearnFragment viewPager

* feat: Empty states and settings button

* fix: Number of courses

* fix: Minor UI changes

* fix: Fixes according to designer feedback

* fix: Fixes after demo

* refactor: Move CourseContainerTab

* fix: Fixes according to PR feedback

* fix: Fixes according to PR feedback

* feat: added a patch from Omer Habib

* fix: Fixes according to PR feedback

* fix: Assignment date string

* fix: Lint error

* fix: Assignment date string

* fix: Fixes according to PR feedback

* fix: Fixes according to designer feedback

* fix: Fixes according to PR feedback

---------

Co-authored-by: Volodymyr Chekyrta <volodymyr.chekyrta@raccoongang.com>
Co-authored-by: Farhan Arshad <43750646+farhan-arshad-dev@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 6, 2024
1 parent 22ee176 commit 1bc1d4c
Show file tree
Hide file tree
Showing 40 changed files with 770 additions and 487 deletions.
2 changes: 1 addition & 1 deletion Documentation/ConfigurationManagement.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ android:
- **PRE_LOGIN_EXPERIENCE_ENABLED:** Enables the pre login courses discovery experience.
- **WHATS_NEW_ENABLED:** Enables the "What's New" feature to present the latest changes to the user.
- **SOCIAL_AUTH_ENABLED:** Enables SSO buttons on the SignIn and SignUp screens.
- **COURSE_NESTED_LIST_ENABLED:** Enables an alternative visual representation for the course structure.
- **COURSE_DROPDOWN_NAVIGATION_ENABLED:** Enables an alternative navigation through units.
- **COURSE_UNIT_PROGRESS_ENABLED:** Enables the display of the unit progress within the courseware.

## Future Support
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/openedx/core/config/UIConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ 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_DROPDOWN_NAVIGATION_ENABLED")
val isCourseDropdownNavigationEnabled: Boolean = false,
@SerializedName("COURSE_UNIT_PROGRESS_ENABLED")
val isCourseUnitProgressEnabled: Boolean = false,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.openedx.core.data.model

import com.google.gson.annotations.SerializedName
import org.openedx.core.data.model.room.AssignmentProgressDb
import org.openedx.core.domain.model.AssignmentProgress

data class AssignmentProgress(
@SerializedName("assignment_type")
val assignmentType: String?,
@SerializedName("num_points_earned")
val numPointsEarned: Float?,
@SerializedName("num_points_possible")
val numPointsPossible: Float?,
) {
fun mapToDomain() = AssignmentProgress(
assignmentType = assignmentType ?: "",
numPointsEarned = numPointsEarned ?: 0f,
numPointsPossible = numPointsPossible ?: 0f
)

fun mapToRoomEntity() = AssignmentProgressDb(
assignmentType = assignmentType,
numPointsEarned = numPointsEarned,
numPointsPossible = numPointsPossible
)
}
35 changes: 23 additions & 12 deletions core/src/main/java/org/openedx/core/data/model/Block.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ package org.openedx.core.data.model

import com.google.gson.annotations.SerializedName
import org.openedx.core.BlockType
import org.openedx.core.domain.model.Block
import org.openedx.core.utils.TimeUtils
import org.openedx.core.domain.model.Block as DomainBlock
import org.openedx.core.domain.model.BlockCounts as DomainBlockCounts
import org.openedx.core.domain.model.EncodedVideos as DomainEncodedVideos
import org.openedx.core.domain.model.StudentViewData as DomainStudentViewData
import org.openedx.core.domain.model.VideoInfo as DomainVideoInfo

data class Block(
@SerializedName("id")
Expand Down Expand Up @@ -33,8 +38,12 @@ data class Block(
val completion: Double?,
@SerializedName("contains_gated_content")
val containsGatedContent: Boolean?,
@SerializedName("assignment_progress")
val assignmentProgress: AssignmentProgress?,
@SerializedName("due")
val due: String?
) {
fun mapToDomain(blockData: Map<String, org.openedx.core.data.model.Block>): Block {
fun mapToDomain(blockData: Map<String, Block>): DomainBlock {
val blockType = BlockType.getBlockType(type ?: "")
val descendantsType = if (blockType == BlockType.VERTICAL) {
val types = descendants?.map { descendant ->
Expand All @@ -46,7 +55,7 @@ data class Block(
blockType
}

return org.openedx.core.domain.model.Block(
return DomainBlock(
id = id ?: "",
blockId = blockId ?: "",
lmsWebUrl = lmsWebUrl ?: "",
Expand All @@ -61,7 +70,9 @@ data class Block(
studentViewMultiDevice = studentViewMultiDevice ?: false,
blockCounts = blockCounts?.mapToDomain()!!,
completion = completion ?: 0.0,
containsGatedContent = containsGatedContent ?: false
containsGatedContent = containsGatedContent ?: false,
assignmentProgress = assignmentProgress?.mapToDomain(),
due = TimeUtils.iso8601ToDate(due ?: ""),
)
}
}
Expand All @@ -80,8 +91,8 @@ data class StudentViewData(
@SerializedName("topic_id")
val topicId: String?
) {
fun mapToDomain(): org.openedx.core.domain.model.StudentViewData {
return org.openedx.core.domain.model.StudentViewData(
fun mapToDomain(): DomainStudentViewData {
return DomainStudentViewData(
onlyOnWeb = onlyOnWeb ?: false,
duration = duration ?: "",
transcripts = transcripts,
Expand All @@ -106,8 +117,8 @@ data class EncodedVideos(
var mobileLow: VideoInfo?
) {

fun mapToDomain(): org.openedx.core.domain.model.EncodedVideos {
return org.openedx.core.domain.model.EncodedVideos(
fun mapToDomain(): DomainEncodedVideos {
return DomainEncodedVideos(
youtube = videoInfo?.mapToDomain(),
hls = hls?.mapToDomain(),
fallback = fallback?.mapToDomain(),
Expand All @@ -124,8 +135,8 @@ data class VideoInfo(
@SerializedName("file_size")
var fileSize: Int?
) {
fun mapToDomain(): org.openedx.core.domain.model.VideoInfo {
return org.openedx.core.domain.model.VideoInfo(
fun mapToDomain(): DomainVideoInfo {
return DomainVideoInfo(
url = url ?: "",
fileSize = fileSize ?: 0
)
Expand All @@ -136,8 +147,8 @@ data class BlockCounts(
@SerializedName("video")
var video: Int?
) {
fun mapToDomain(): org.openedx.core.domain.model.BlockCounts {
return org.openedx.core.domain.model.BlockCounts(
fun mapToDomain(): DomainBlockCounts {
return DomainBlockCounts(
video = video ?: 0
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.google.gson.annotations.SerializedName
import org.openedx.core.data.model.room.BlockDb
import org.openedx.core.data.model.room.CourseStructureEntity
import org.openedx.core.data.model.room.MediaDb
import org.openedx.core.data.model.room.discovery.ProgressDb
import org.openedx.core.domain.model.CourseStructure
import org.openedx.core.utils.TimeUtils

Expand Down Expand Up @@ -35,7 +36,9 @@ data class CourseStructureModel(
@SerializedName("certificate")
val certificate: Certificate?,
@SerializedName("is_self_paced")
var isSelfPaced: Boolean?
var isSelfPaced: Boolean?,
@SerializedName("course_progress")
val progress: Progress?,
) {
fun mapToDomain(): CourseStructure {
return CourseStructure(
Expand All @@ -54,7 +57,8 @@ data class CourseStructureModel(
coursewareAccess = coursewareAccess?.mapToDomain(),
media = media?.mapToDomain(),
certificate = certificate?.mapToDomain(),
isSelfPaced = isSelfPaced ?: false
isSelfPaced = isSelfPaced ?: false,
progress = progress?.mapToDomain()
)
}

Expand All @@ -73,7 +77,8 @@ data class CourseStructureModel(
coursewareAccess = coursewareAccess?.mapToRoomEntity(),
media = MediaDb.createFrom(media),
certificate = certificate?.mapToRoomEntity(),
isSelfPaced = isSelfPaced ?: false
isSelfPaced = isSelfPaced ?: false,
progress = progress?.mapToRoomEntity() ?: ProgressDb.DEFAULT_PROGRESS
)
}
}
70 changes: 52 additions & 18 deletions core/src/main/java/org/openedx/core/data/model/room/BlockDb.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ package org.openedx.core.data.model.room
import androidx.room.ColumnInfo
import androidx.room.Embedded
import org.openedx.core.BlockType
import org.openedx.core.domain.model.*
import org.openedx.core.data.model.Block
import org.openedx.core.data.model.BlockCounts
import org.openedx.core.data.model.EncodedVideos
import org.openedx.core.data.model.StudentViewData
import org.openedx.core.data.model.VideoInfo
import org.openedx.core.utils.TimeUtils
import org.openedx.core.domain.model.AssignmentProgress as DomainAssignmentProgress
import org.openedx.core.domain.model.Block as DomainBlock
import org.openedx.core.domain.model.BlockCounts as DomainBlockCounts
import org.openedx.core.domain.model.EncodedVideos as DomainEncodedVideos
import org.openedx.core.domain.model.StudentViewData as DomainStudentViewData
import org.openedx.core.domain.model.VideoInfo as DomainVideoInfo

data class BlockDb(
@ColumnInfo("id")
Expand Down Expand Up @@ -33,9 +44,13 @@ data class BlockDb(
@ColumnInfo("completion")
val completion: Double,
@ColumnInfo("contains_gated_content")
val containsGatedContent: Boolean
val containsGatedContent: Boolean,
@Embedded
val assignmentProgress: AssignmentProgressDb?,
@ColumnInfo("due")
val due: String?
) {
fun mapToDomain(blocks: List<BlockDb>): Block {
fun mapToDomain(blocks: List<BlockDb>): DomainBlock {
val blockType = BlockType.getBlockType(type)
val descendantsType = if (blockType == BlockType.VERTICAL) {
val types = descendants.map { descendant ->
Expand All @@ -47,7 +62,7 @@ data class BlockDb(
blockType
}

return Block(
return DomainBlock(
id = id,
blockId = blockId,
lmsWebUrl = lmsWebUrl,
Expand All @@ -62,14 +77,16 @@ data class BlockDb(
descendants = descendants,
descendantsType = descendantsType,
completion = completion,
containsGatedContent = containsGatedContent
containsGatedContent = containsGatedContent,
assignmentProgress = assignmentProgress?.mapToDomain(),
due = TimeUtils.iso8601ToDate(due ?: ""),
)
}

companion object {

fun createFrom(
block: org.openedx.core.data.model.Block
block: Block
): BlockDb {
with(block) {
return BlockDb(
Expand All @@ -86,7 +103,9 @@ data class BlockDb(
studentViewMultiDevice = studentViewMultiDevice ?: false,
blockCounts = BlockCountsDb.createFrom(blockCounts),
completion = completion ?: 0.0,
containsGatedContent = containsGatedContent ?: false
containsGatedContent = containsGatedContent ?: false,
assignmentProgress = assignmentProgress?.mapToRoomEntity(),
due = due
)
}
}
Expand All @@ -105,8 +124,8 @@ data class StudentViewDataDb(
@Embedded
val encodedVideos: EncodedVideosDb?
) {
fun mapToDomain(): StudentViewData {
return StudentViewData(
fun mapToDomain(): DomainStudentViewData {
return DomainStudentViewData(
onlyOnWeb,
duration,
transcripts,
Expand All @@ -117,7 +136,7 @@ data class StudentViewDataDb(

companion object {

fun createFrom(studentViewData: org.openedx.core.data.model.StudentViewData?): StudentViewDataDb {
fun createFrom(studentViewData: StudentViewData?): StudentViewDataDb {
return StudentViewDataDb(
onlyOnWeb = studentViewData?.onlyOnWeb ?: false,
duration = studentViewData?.duration.toString(),
Expand All @@ -144,9 +163,9 @@ data class EncodedVideosDb(
@ColumnInfo("mobileLow")
var mobileLow: VideoInfoDb?
) {
fun mapToDomain(): EncodedVideos {
return EncodedVideos(
youtube?.mapToDomain(),
fun mapToDomain(): DomainEncodedVideos {
return DomainEncodedVideos(
youtube = youtube?.mapToDomain(),
hls = hls?.mapToDomain(),
fallback = fallback?.mapToDomain(),
desktopMp4 = desktopMp4?.mapToDomain(),
Expand All @@ -156,7 +175,7 @@ data class EncodedVideosDb(
}

companion object {
fun createFrom(encodedVideos: org.openedx.core.data.model.EncodedVideos?): EncodedVideosDb {
fun createFrom(encodedVideos: EncodedVideos?): EncodedVideosDb {
return EncodedVideosDb(
youtube = VideoInfoDb.createFrom(encodedVideos?.videoInfo),
hls = VideoInfoDb.createFrom(encodedVideos?.hls),
Expand All @@ -176,10 +195,10 @@ data class VideoInfoDb(
@ColumnInfo("fileSize")
val fileSize: Int
) {
fun mapToDomain() = VideoInfo(url, fileSize)
fun mapToDomain() = DomainVideoInfo(url, fileSize)

companion object {
fun createFrom(videoInfo: org.openedx.core.data.model.VideoInfo?): VideoInfoDb? {
fun createFrom(videoInfo: VideoInfo?): VideoInfoDb? {
if (videoInfo == null) return null
return VideoInfoDb(
videoInfo.url ?: "",
Expand All @@ -193,11 +212,26 @@ data class BlockCountsDb(
@ColumnInfo("video")
val video: Int
) {
fun mapToDomain() = BlockCounts(video)
fun mapToDomain() = DomainBlockCounts(video)

companion object {
fun createFrom(blocksCounts: org.openedx.core.data.model.BlockCounts?): BlockCountsDb {
fun createFrom(blocksCounts: BlockCounts?): BlockCountsDb {
return BlockCountsDb(blocksCounts?.video ?: 0)
}
}
}

data class AssignmentProgressDb(
@ColumnInfo("assignment_type")
val assignmentType: String?,
@ColumnInfo("num_points_earned")
val numPointsEarned: Float?,
@ColumnInfo("num_points_possible")
val numPointsPossible: Float?,
) {
fun mapToDomain() = DomainAssignmentProgress(
assignmentType = assignmentType ?: "",
numPointsEarned = numPointsEarned ?: 0f,
numPointsPossible = numPointsPossible ?: 0f
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
import org.openedx.core.data.model.room.discovery.CertificateDb
import org.openedx.core.data.model.room.discovery.CoursewareAccessDb
import org.openedx.core.data.model.room.discovery.ProgressDb
import org.openedx.core.domain.model.CourseStructure
import org.openedx.core.utils.TimeUtils

Expand Down Expand Up @@ -39,7 +40,9 @@ data class CourseStructureEntity(
@Embedded
val certificate: CertificateDb?,
@ColumnInfo("isSelfPaced")
val isSelfPaced: Boolean
val isSelfPaced: Boolean,
@Embedded
val progress: ProgressDb,
) {

fun mapToDomain(): CourseStructure {
Expand All @@ -57,7 +60,8 @@ data class CourseStructureEntity(
coursewareAccess?.mapToDomain(),
media?.mapToDomain(),
certificate?.mapToDomain(),
isSelfPaced
isSelfPaced,
progress.mapToDomain()
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.openedx.core.domain.model

data class AssignmentProgress(
val assignmentType: String,
val numPointsEarned: Float,
val numPointsPossible: Float
)
5 changes: 4 additions & 1 deletion core/src/main/java/org/openedx/core/domain/model/Block.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.openedx.core.module.db.DownloadModel
import org.openedx.core.module.db.DownloadedState
import org.openedx.core.module.db.FileType
import org.openedx.core.utils.VideoUtil
import java.util.Date


data class Block(
Expand All @@ -25,7 +26,9 @@ data class Block(
val descendantsType: BlockType,
val completion: Double,
val containsGatedContent: Boolean = false,
val downloadModel: DownloadModel? = null
val downloadModel: DownloadModel? = null,
val assignmentProgress: AssignmentProgress?,
val due: Date?
) {
val isDownloadable: Boolean
get() {
Expand Down
Loading

0 comments on commit 1bc1d4c

Please sign in to comment.