diff --git a/core/src/main/java/org/openedx/core/UIMessage.kt b/core/src/main/java/org/openedx/core/UIMessage.kt
index 6f26d6f65..8a9267f36 100644
--- a/core/src/main/java/org/openedx/core/UIMessage.kt
+++ b/core/src/main/java/org/openedx/core/UIMessage.kt
@@ -2,11 +2,11 @@ package org.openedx.core
import androidx.compose.material.SnackbarDuration
-sealed class UIMessage {
+open class UIMessage {
class SnackBarMessage(
val message: String,
val duration: SnackbarDuration = SnackbarDuration.Long,
) : UIMessage()
class ToastMessage(val message: String) : UIMessage()
-}
\ No newline at end of file
+}
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 27c109b26..c2c9927c3 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -107,9 +107,10 @@
We built a suggested schedule to help you stay on track. But don’t worry – it’s flexible so you can learn at your own pace. If you happen to fall behind, you’ll be able to adjust the dates to keep yourself on track.
To complete graded assignments as part of this course, you can upgrade today.
You are auditing this course, which means that you are unable to participate in graded assignments. It looks like you missed some important deadlines based on our suggested schedule. To complete graded assignments as part of this course and shift the past due assignments into the future, you can upgrade today.
+ Due Dates Shifted
Your due dates have been successfully shifted to help you stay on track.
Your dates could not be shifted. Please try again.
- View all dates
+ View All Dates
Register
Sign in
diff --git a/course/src/main/java/org/openedx/course/DatesShiftedSnackBar.kt b/course/src/main/java/org/openedx/course/DatesShiftedSnackBar.kt
new file mode 100644
index 000000000..fd2a3ce6b
--- /dev/null
+++ b/course/src/main/java/org/openedx/course/DatesShiftedSnackBar.kt
@@ -0,0 +1,5 @@
+package org.openedx.course
+
+import org.openedx.core.UIMessage
+
+class DatesShiftedSnackBar : UIMessage()
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 4b0e175c1..e906ef6a2 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
@@ -37,6 +37,10 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
+import androidx.compose.material.SnackbarData
+import androidx.compose.material.SnackbarDuration
+import androidx.compose.material.SnackbarHost
+import androidx.compose.material.SnackbarHostState
import androidx.compose.material.Surface
import androidx.compose.material.Switch
import androidx.compose.material.SwitchDefaults
@@ -49,6 +53,7 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@@ -99,12 +104,14 @@ import org.openedx.core.ui.theme.appTypography
import org.openedx.core.ui.windowSizeValue
import org.openedx.core.utils.TimeUtils
import org.openedx.core.utils.clearTime
+import org.openedx.course.DatesShiftedSnackBar
import org.openedx.course.R
import org.openedx.course.presentation.CourseRouter
import org.openedx.course.presentation.calendarsync.CalendarSyncUIState
import org.openedx.course.presentation.container.CourseContainerFragment
import org.openedx.course.presentation.ui.CourseDatesBanner
import org.openedx.course.presentation.ui.CourseDatesBannerTablet
+import org.openedx.course.presentation.ui.DatesShiftedSnackBar
import java.util.concurrent.atomic.AtomicReference
import org.openedx.core.R as coreR
@@ -268,6 +275,16 @@ internal fun CourseDatesScreen(
)
}
+ val snackState = remember { SnackbarHostState() }
+ if (uiMessage is DatesShiftedSnackBar) {
+ val datesShiftedMessage = stringResource(id = R.string.course_dates_shifted_message)
+ LaunchedEffect(uiMessage) {
+ snackState.showSnackbar(
+ message = datesShiftedMessage,
+ duration = SnackbarDuration.Long
+ )
+ }
+ }
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
Box(
@@ -394,6 +411,15 @@ internal fun CourseDatesScreen(
})
}
}
+
+ SnackbarHost(
+ modifier = Modifier.align(Alignment.BottomStart),
+ hostState = snackState
+ ) { snackbarData: SnackbarData ->
+ DatesShiftedSnackBar(onClose = {
+ snackbarData.dismiss()
+ })
+ }
}
}
}
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 6313b599d..0c1754552 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
@@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.openedx.core.BaseViewModel
+import org.openedx.core.R
import org.openedx.core.SingleEventLiveData
import org.openedx.core.UIMessage
import org.openedx.core.config.Config
@@ -22,6 +23,7 @@ import org.openedx.core.system.connection.NetworkConnection
import org.openedx.core.system.notifier.CalendarSyncEvent.CheckCalendarSyncEvent
import org.openedx.core.system.notifier.CalendarSyncEvent.CreateCalendarSyncEvent
import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.course.DatesShiftedSnackBar
import org.openedx.course.domain.interactor.CourseInteractor
import org.openedx.course.presentation.calendarsync.CalendarManager
import org.openedx.course.presentation.calendarsync.CalendarSyncDialogType
@@ -115,6 +117,7 @@ class CourseDatesViewModel(
try {
interactor.resetCourseDates(courseId = courseId)
getCourseDates()
+ _uiMessage.value = DatesShiftedSnackBar()
onResetDates(true)
} catch (e: Exception) {
if (e.isInternetError()) {
@@ -122,7 +125,7 @@ class CourseDatesViewModel(
UIMessage.SnackBarMessage(resourceManager.getString(CoreR.string.core_error_no_connection))
} else {
_uiMessage.value =
- UIMessage.SnackBarMessage(resourceManager.getString(CoreR.string.core_error_unknown_error))
+ UIMessage.SnackBarMessage(resourceManager.getString(R.string.core_dates_shift_dates_unsuccessful_msg))
}
onResetDates(false)
}
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 c0f200b37..4d9dfc54b 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
@@ -28,6 +28,10 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
+import androidx.compose.material.SnackbarData
+import androidx.compose.material.SnackbarDuration
+import androidx.compose.material.SnackbarHost
+import androidx.compose.material.SnackbarHostState
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.pullrefresh.PullRefreshIndicator
@@ -35,6 +39,7 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
@@ -54,7 +59,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.google.android.material.snackbar.Snackbar
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -79,6 +83,7 @@ import org.openedx.core.ui.theme.OpenEdXTheme
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.appTypography
import org.openedx.core.ui.windowSizeValue
+import org.openedx.course.DatesShiftedSnackBar
import org.openedx.course.presentation.CourseRouter
import org.openedx.course.presentation.container.CourseContainerFragment
import org.openedx.course.presentation.container.CourseContainerTab
@@ -89,6 +94,7 @@ import org.openedx.course.presentation.ui.CourseExpandableChapterCard
import org.openedx.course.presentation.ui.CourseImageHeader
import org.openedx.course.presentation.ui.CourseSectionCard
import org.openedx.course.presentation.ui.CourseSubSectionItem
+import org.openedx.course.presentation.ui.DatesShiftedSnackBar
import java.io.File
import java.util.Date
@@ -99,8 +105,6 @@ class CourseOutlineFragment : Fragment() {
}
private val router by inject()
- private var snackBar: Snackbar? = null
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(viewModel)
@@ -210,38 +214,16 @@ class CourseOutlineFragment : Fragment() {
onResetDatesClick = {
viewModel.resetCourseDatesBanner(onResetDates = {
(parentFragment as CourseContainerFragment).updateCourseDates()
- showDatesUpdateSnackbar(it)
})
- }
- )
- }
- }
- }
-
- override fun onDestroyView() {
- snackBar?.dismiss()
- super.onDestroyView()
- }
-
- private fun showDatesUpdateSnackbar(isSuccess: Boolean) {
- val message = if (isSuccess) {
- getString(R.string.core_dates_shift_dates_successfully_msg)
- } else {
- getString(R.string.core_dates_shift_dates_unsuccessful_msg)
- }
- snackBar = view?.let {
- Snackbar.make(it, message, Snackbar.LENGTH_LONG).apply {
- if (isSuccess) {
- setAction(R.string.core_dates_view_all_dates) {
+ },
+ onViewDates = {
(parentFragment as CourseContainerFragment).navigateToTab(CourseContainerTab.DATES)
}
- }
+ )
}
}
- snackBar?.show()
}
-
companion object {
private const val ARG_COURSE_ID = "courseId"
private const val ARG_TITLE = "title"
@@ -288,6 +270,7 @@ internal fun CourseOutlineScreen(
onResumeClick: (String) -> Unit,
onDownloadClick: (Block) -> Unit,
onResetDatesClick: () -> Unit,
+ onViewDates: () -> Unit?,
) {
val scaffoldState = rememberScaffoldState()
val pullRefreshState =
@@ -331,6 +314,17 @@ internal fun CourseOutlineScreen(
)
}
+ val snackState = remember { SnackbarHostState() }
+ if (uiMessage is DatesShiftedSnackBar) {
+ val datesShiftedMessage =
+ stringResource(id = org.openedx.course.R.string.course_dates_shifted_message)
+ LaunchedEffect(uiMessage) {
+ snackState.showSnackbar(
+ message = datesShiftedMessage,
+ duration = SnackbarDuration.Long
+ )
+ }
+ }
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
Box(
@@ -512,6 +506,17 @@ internal fun CourseOutlineScreen(
)
}
}
+
+ SnackbarHost(
+ modifier = Modifier.align(Alignment.BottomStart),
+ hostState = snackState
+ ) { snackbarData: SnackbarData ->
+ DatesShiftedSnackBar(showAction = true,
+ onViewDates = onViewDates,
+ onClose = {
+ snackbarData.dismiss()
+ })
+ }
}
}
}
@@ -661,6 +666,7 @@ private fun CourseOutlineScreenPreview() {
onReloadClick = {},
onDownloadClick = {},
onResetDatesClick = {},
+ onViewDates = {},
)
}
}
@@ -701,6 +707,7 @@ private fun CourseOutlineScreenTabletPreview() {
onReloadClick = {},
onDownloadClick = {},
onResetDatesClick = {},
+ onViewDates = {},
)
}
}
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 0337f811b..b129fcd13 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
@@ -27,6 +27,7 @@ import org.openedx.core.system.connection.NetworkConnection
import org.openedx.core.system.notifier.CalendarSyncEvent.CreateCalendarSyncEvent
import org.openedx.core.system.notifier.CourseNotifier
import org.openedx.core.system.notifier.CourseStructureUpdated
+import org.openedx.course.DatesShiftedSnackBar
import org.openedx.course.domain.interactor.CourseInteractor
import org.openedx.course.presentation.CourseAnalytics
import org.openedx.course.presentation.calendarsync.CalendarSyncDialogType
@@ -270,6 +271,7 @@ class CourseOutlineViewModel(
try {
interactor.resetCourseDates(courseId = courseId)
updateCourseData(false)
+ _uiMessage.value = DatesShiftedSnackBar()
onResetDates(true)
} catch (e: Exception) {
if (e.isInternetError()) {
@@ -277,7 +279,7 @@ class CourseOutlineViewModel(
UIMessage.SnackBarMessage(resourceManager.getString(R.string.core_error_no_connection))
} else {
_uiMessage.value =
- UIMessage.SnackBarMessage(resourceManager.getString(R.string.core_error_unknown_error))
+ UIMessage.SnackBarMessage(resourceManager.getString(R.string.core_dates_shift_dates_unsuccessful_msg))
}
onResetDates(false)
}
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 e37c04175..23c31e843 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
@@ -40,6 +40,7 @@ import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Scaffold
+import androidx.compose.material.Snackbar
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
@@ -113,7 +114,7 @@ import org.openedx.course.presentation.outline.CourseOutlineFragment
import subtitleFile.Caption
import subtitleFile.TimedTextObject
import java.util.Date
-import org.openedx.course.R as courseR
+import org.openedx.core.R as coreR
@Composable
fun CourseImageHeader(
@@ -141,8 +142,8 @@ fun CourseImageHeader(
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(imageUrl)
- .error(org.openedx.core.R.drawable.core_no_image_course)
- .placeholder(org.openedx.core.R.drawable.core_no_image_course)
+ .error(coreR.drawable.core_no_image_course)
+ .placeholder(coreR.drawable.core_no_image_course)
.build(),
contentDescription = stringResource(
id = R.string.course_accessibility_header_image_for,
@@ -450,9 +451,9 @@ fun NavigationUnitsButtons(
onNextClick: () -> Unit
) {
val nextButtonIcon = if (hasNextBlock) {
- painterResource(id = org.openedx.core.R.drawable.core_ic_down)
+ painterResource(id = coreR.drawable.core_ic_down)
} else {
- painterResource(id = org.openedx.core.R.drawable.core_ic_check_in_box)
+ painterResource(id = coreR.drawable.core_ic_check_in_box)
}
val subModifier =
@@ -499,7 +500,7 @@ fun NavigationUnitsButtons(
Spacer(Modifier.width(8.dp))
Icon(
modifier = Modifier.rotate(if (isVerticalNavigation) 0f else -90f),
- painter = painterResource(id = org.openedx.core.R.drawable.core_ic_up),
+ painter = painterResource(id = coreR.drawable.core_ic_up),
contentDescription = null,
tint = MaterialTheme.appColors.primary
)
@@ -666,7 +667,7 @@ fun VideoSubtitles(
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
- text = stringResource(id = courseR.string.course_subtitles),
+ text = stringResource(id = R.string.course_subtitles),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.titleMedium
)
@@ -676,7 +677,7 @@ fun VideoSubtitles(
onSettingsClick()
},
text = subtitleLanguage,
- painter = painterResource(id = courseR.drawable.course_ic_cc),
+ painter = painterResource(id = R.drawable.course_ic_cc),
color = MaterialTheme.appColors.textAccent,
textStyle = MaterialTheme.appTypography.labelLarge
)
@@ -1160,6 +1161,59 @@ fun CourseDatesBannerTablet(
}
}
+@Composable
+fun DatesShiftedSnackBar(
+ showAction: Boolean = false,
+ onViewDates: () -> Unit? = {},
+ onClose: () -> Unit? = {},
+) {
+ Snackbar(
+ modifier = Modifier.padding(16.dp),
+ backgroundColor = MaterialTheme.appColors.background
+ ) {
+ Column(modifier = Modifier.padding(4.dp)) {
+ Box {
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .align(Alignment.CenterStart),
+ text = stringResource(id = coreR.string.core_dates_shift_dates_successfully_title),
+ color = MaterialTheme.appColors.textFieldText,
+ style = MaterialTheme.appTypography.titleMedium
+ )
+ IconButton(modifier = Modifier.align(Alignment.TopEnd), onClick = { onClose() }) {
+ Icon(
+ imageVector = Icons.Filled.Close,
+ contentDescription = "close",
+ tint = MaterialTheme.appColors.onBackground,
+ )
+ }
+ }
+ Text(
+ modifier = Modifier
+ .padding(top = 4.dp)
+ .fillMaxWidth(),
+ text = stringResource(id = coreR.string.core_dates_shift_dates_successfully_msg),
+ color = MaterialTheme.appColors.textFieldText,
+ style = MaterialTheme.appTypography.titleSmall,
+ )
+ if (showAction) {
+ OpenEdXOutlinedButton(
+ modifier = Modifier
+ .padding(top = 16.dp)
+ .fillMaxWidth(),
+ text = stringResource(id = coreR.string.core_dates_view_all_dates),
+ backgroundColor = MaterialTheme.appColors.background,
+ textColor = MaterialTheme.appColors.primary,
+ borderColor = MaterialTheme.appColors.primary,
+ onClick = {
+ onViewDates()
+ })
+ }
+ }
+ }
+}
+
@Composable
fun WarningLabel(
painter: Painter,
@@ -1219,7 +1273,7 @@ private fun WarningLabelPreview() {
OpenEdXTheme {
WarningLabel(
painter = painterResource(id = org.openedx.core.R.drawable.core_ic_offline),
- text = stringResource(id = courseR.string.course_no_internet_label)
+ text = stringResource(id = R.string.course_no_internet_label)
)
}
}
diff --git a/course/src/main/res/values/strings.xml b/course/src/main/res/values/strings.xml
index da5ebe36b..4a4ef80ed 100644
--- a/course/src/main/res/values/strings.xml
+++ b/course/src/main/res/values/strings.xml
@@ -52,6 +52,7 @@
You are already enrolled in this course.
Discover
You cannot change the download video quality when all videos are downloading
+ Dates Shifted
Course dates are not currently available.