From 1930739048a906774d67f31b97a0ab310088de8b Mon Sep 17 00:00:00 2001
From: Farhan Arshad <43750646+farhan-arshad-dev@users.noreply.github.com>
Date: Tue, 26 Mar 2024 14:50:34 +0500
Subject: [PATCH] style: Dates Tab Shift Due Dates Success SnackBar Design
Changes (#264)
style: Dates Tab Shift Due Dates Success SnackBar Design Changes
fixes: LEARNER-9868
---
.../main/java/org/openedx/core/UIMessage.kt | 4 +-
core/src/main/res/values/strings.xml | 3 +-
.../openedx/course/DatesShiftedSnackBar.kt | 5 ++
.../presentation/dates/CourseDatesFragment.kt | 26 +++++++
.../dates/CourseDatesViewModel.kt | 5 +-
.../outline/CourseOutlineFragment.kt | 63 +++++++++--------
.../outline/CourseOutlineViewModel.kt | 4 +-
.../course/presentation/ui/CourseUI.kt | 70 ++++++++++++++++---
course/src/main/res/values/strings.xml | 1 +
9 files changed, 140 insertions(+), 41 deletions(-)
create mode 100644 course/src/main/java/org/openedx/course/DatesShiftedSnackBar.kt
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 10ed72367..fdeefc411 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -106,9 +106,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 86fa2314c..c55d622cf 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 051466d25..e28b7b1cf 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
@@ -39,6 +39,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
@@ -110,7 +111,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(
@@ -138,8 +139,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,
@@ -447,9 +448,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 =
@@ -496,7 +497,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
)
@@ -663,7 +664,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
)
@@ -673,7 +674,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
)
@@ -1157,6 +1158,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()
+ })
+ }
+ }
+ }
+}
+
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
diff --git a/course/src/main/res/values/strings.xml b/course/src/main/res/values/strings.xml
index 63e1555de..d05d97808 100644
--- a/course/src/main/res/values/strings.xml
+++ b/course/src/main/res/values/strings.xml
@@ -51,6 +51,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.