Skip to content

Commit 5fb01ff

Browse files
feat: Tablet UI
1 parent 3b4677d commit 5fb01ff

File tree

4 files changed

+235
-210
lines changed

4 files changed

+235
-210
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ interface CourseApi {
7272
suspend fun getUserCourses(
7373
@Path("username") username: String,
7474
@Query("page") page: Int = 1,
75-
@Query("page_size") pageSize: Int = 9,
75+
@Query("page_size") pageSize: Int = 20,
7676
@Query("status") status: String? = null,
7777
@Query("requested_fields") fields: List<String> = emptyList()
7878
): CourseEnrollments

dashboard/src/main/java/org/openedx/courses/presentation/AllEnrolledCoursesFragment.kt

Lines changed: 149 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,8 @@ import androidx.compose.ui.ExperimentalComposeUiApi
6262
import androidx.compose.ui.Modifier
6363
import androidx.compose.ui.graphics.Color
6464
import androidx.compose.ui.layout.ContentScale
65-
import androidx.compose.ui.layout.onSizeChanged
6665
import androidx.compose.ui.platform.ComposeView
6766
import androidx.compose.ui.platform.LocalContext
68-
import androidx.compose.ui.platform.LocalDensity
6967
import androidx.compose.ui.platform.LocalLayoutDirection
7068
import androidx.compose.ui.platform.ViewCompositionStrategy
7169
import androidx.compose.ui.platform.testTag
@@ -202,17 +200,20 @@ private fun AllEnrolledCoursesScreen(
202200
onAction: (AllEnrolledCoursesAction) -> Unit
203201
) {
204202
val windowSize = rememberWindowSize()
203+
val layoutDirection = LocalLayoutDirection.current
205204
val scaffoldState = rememberScaffoldState()
205+
val scrollState = rememberLazyGridState()
206+
val columns = if (windowSize.isTablet) 3 else 2
206207
val pullRefreshState = rememberPullRefreshState(
207208
refreshing = refreshing,
208209
onRefresh = { onAction(AllEnrolledCoursesAction.SwipeRefresh) }
209210
)
210-
val tabPagerState = rememberPagerState(pageCount = { CourseStatusFilter.entries.size })
211-
211+
val tabPagerState = rememberPagerState(pageCount = {
212+
CourseStatusFilter.entries.size
213+
})
212214
var isInternetConnectionShown by rememberSaveable {
213215
mutableStateOf(false)
214216
}
215-
val scrollState = rememberLazyGridState()
216217
val firstVisibleIndex = remember {
217218
mutableIntStateOf(scrollState.firstVisibleItemIndex)
218219
}
@@ -226,20 +227,28 @@ private fun AllEnrolledCoursesScreen(
226227
},
227228
backgroundColor = MaterialTheme.appColors.background
228229
) { paddingValues ->
229-
230-
val layoutDirection = LocalLayoutDirection.current
231230
val contentPaddings by remember(key1 = windowSize) {
232231
mutableStateOf(
233232
windowSize.windowSizeValue(
234233
expanded = PaddingValues(
235-
top = 32.dp,
236-
bottom = 40.dp
234+
top = 16.dp,
235+
bottom = 40.dp,
237236
),
238237
compact = PaddingValues(horizontal = 16.dp, vertical = 16.dp)
239238
)
240239
)
241240
}
242241

242+
val roundTapBarPaddings by remember(key1 = windowSize) {
243+
mutableStateOf(
244+
windowSize.windowSizeValue(
245+
expanded = PaddingValues(vertical = 6.dp),
246+
compact = PaddingValues(horizontal = 16.dp, vertical = 6.dp)
247+
)
248+
)
249+
}
250+
251+
243252
val emptyStatePaddings by remember(key1 = windowSize) {
244253
mutableStateOf(
245254
windowSize.windowSizeValue(
@@ -255,174 +264,167 @@ private fun AllEnrolledCoursesScreen(
255264
val contentWidth by remember(key1 = windowSize) {
256265
mutableStateOf(
257266
windowSize.windowSizeValue(
258-
expanded = Modifier.widthIn(Dp.Unspecified, 560.dp),
267+
expanded = Modifier.widthIn(Dp.Unspecified, 650.dp),
259268
compact = Modifier.fillMaxWidth(),
260269
)
261270
)
262271
}
263272

264273
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
265-
266-
Column(
274+
Box(
267275
modifier = Modifier
268-
.padding(paddingValues)
269-
.statusBarsInset()
270-
.displayCutoutForLandscape(),
271-
horizontalAlignment = Alignment.CenterHorizontally
276+
.fillMaxSize()
277+
.padding(paddingValues),
278+
contentAlignment = Alignment.TopCenter
272279
) {
273-
BackBtn(
274-
modifier = Modifier.align(Alignment.Start),
275-
tint = MaterialTheme.appColors.textDark
280+
Column(
281+
modifier = Modifier
282+
.statusBarsInset()
283+
.displayCutoutForLandscape()
284+
.then(contentWidth),
285+
horizontalAlignment = Alignment.CenterHorizontally
276286
) {
277-
onAction(AllEnrolledCoursesAction.Back)
278-
}
287+
BackBtn(
288+
modifier = Modifier.align(Alignment.Start),
289+
tint = MaterialTheme.appColors.textDark
290+
) {
291+
onAction(AllEnrolledCoursesAction.Back)
292+
}
279293

280-
Surface(
281-
color = MaterialTheme.appColors.background,
282-
shape = MaterialTheme.appShapes.screenBackgroundShape
283-
) {
284-
Box(
285-
modifier = Modifier
286-
.fillMaxWidth()
287-
.navigationBarsPadding()
288-
.pullRefresh(pullRefreshState),
294+
Surface(
295+
color = MaterialTheme.appColors.background,
296+
shape = MaterialTheme.appShapes.screenBackgroundShape
289297
) {
290-
Column(
298+
Box(
291299
modifier = Modifier
292-
.fillMaxWidth(),
293-
horizontalAlignment = Alignment.CenterHorizontally
300+
.fillMaxWidth()
301+
.navigationBarsPadding()
302+
.pullRefresh(pullRefreshState),
294303
) {
295-
Header(
304+
Column(
296305
modifier = Modifier
297-
.padding(
298-
start = contentPaddings.calculateStartPadding(layoutDirection),
299-
end = contentPaddings.calculateEndPadding(layoutDirection)
300-
),
301-
onSearchClick = {
302-
onAction(AllEnrolledCoursesAction.Search)
303-
}
304-
)
305-
RoundTabsBar(
306-
items = CourseStatusFilter.entries,
307-
contentPadding = PaddingValues(
308-
start = 16.dp,
309-
end = 16.dp,
310-
top = 8.dp,
311-
bottom = 4.dp
312-
),
313-
rowState = rememberLazyListState(),
314-
pagerState = tabPagerState,
315-
onTabClicked = {
316-
val newFilter = CourseStatusFilter.entries[it]
317-
onAction(AllEnrolledCoursesAction.FilterChange(newFilter))
318-
}
319-
)
320-
when (state) {
321-
is AllEnrolledCoursesUIState.Loading -> {
322-
Box(
323-
modifier = Modifier
324-
.fillMaxSize(),
325-
contentAlignment = Alignment.Center
326-
) {
327-
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
306+
.fillMaxWidth(),
307+
horizontalAlignment = Alignment.CenterHorizontally
308+
) {
309+
Header(
310+
modifier = Modifier
311+
.padding(
312+
start = contentPaddings.calculateStartPadding(layoutDirection),
313+
end = contentPaddings.calculateEndPadding(layoutDirection)
314+
),
315+
onSearchClick = {
316+
onAction(AllEnrolledCoursesAction.Search)
328317
}
329-
}
330-
331-
is AllEnrolledCoursesUIState.Courses -> {
332-
val density = LocalDensity.current
333-
var itemHeight by rememberSaveable {
334-
mutableStateOf(0f)
318+
)
319+
RoundTabsBar(
320+
modifier = Modifier.align(Alignment.Start),
321+
items = CourseStatusFilter.entries,
322+
contentPadding = roundTapBarPaddings,
323+
rowState = rememberLazyListState(),
324+
pagerState = tabPagerState,
325+
onTabClicked = {
326+
val newFilter = CourseStatusFilter.entries[it]
327+
onAction(AllEnrolledCoursesAction.FilterChange(newFilter))
335328
}
336-
Box(
337-
modifier = Modifier.fillMaxSize(),
338-
contentAlignment = Alignment.Center
339-
) {
340-
Column(
329+
)
330+
when (state) {
331+
is AllEnrolledCoursesUIState.Loading -> {
332+
Box(
341333
modifier = Modifier
342-
.fillMaxWidth()
343-
.padding(contentPaddings)
334+
.fillMaxSize(),
335+
contentAlignment = Alignment.Center
344336
) {
345-
LazyVerticalGrid(
346-
modifier = Modifier
347-
.fillMaxHeight()
348-
.then(contentWidth),
349-
state = scrollState,
350-
columns = GridCells.Fixed(2),
351-
verticalArrangement = Arrangement.spacedBy(12.dp),
352-
horizontalArrangement = Arrangement.spacedBy(12.dp),
353-
content = {
354-
items(state.courses) { course ->
355-
CourseItem(
356-
modifier = Modifier
357-
.onSizeChanged {
358-
itemHeight = it.height.toFloat()
359-
},
360-
course = course,
361-
apiHostUrl = apiHostUrl,
362-
onClick = {
363-
onAction(AllEnrolledCoursesAction.OpenCourse(it))
364-
}
365-
)
366-
}
367-
item {
368-
if (canLoadMore) {
369-
Box(
370-
modifier = Modifier
371-
.fillMaxWidth()
372-
.height(with(density) { itemHeight.toDp() }),
373-
contentAlignment = Alignment.Center
374-
) {
375-
CircularProgressIndicator(
376-
color = MaterialTheme.appColors.primary
377-
)
337+
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
338+
}
339+
}
340+
341+
is AllEnrolledCoursesUIState.Courses -> {
342+
Box(
343+
modifier = Modifier
344+
.fillMaxSize()
345+
.padding(contentPaddings),
346+
contentAlignment = Alignment.Center
347+
) {
348+
Column(
349+
modifier = Modifier.fillMaxWidth(),
350+
horizontalAlignment = Alignment.CenterHorizontally
351+
) {
352+
LazyVerticalGrid(
353+
modifier = Modifier
354+
.fillMaxHeight(),
355+
state = scrollState,
356+
columns = GridCells.Fixed(columns),
357+
verticalArrangement = Arrangement.spacedBy(12.dp),
358+
horizontalArrangement = Arrangement.spacedBy(12.dp),
359+
content = {
360+
items(state.courses) { course ->
361+
CourseItem(
362+
course = course,
363+
apiHostUrl = apiHostUrl,
364+
onClick = {
365+
onAction(AllEnrolledCoursesAction.OpenCourse(it))
366+
}
367+
)
368+
}
369+
item {
370+
if (canLoadMore) {
371+
Box(
372+
modifier = Modifier
373+
.fillMaxWidth()
374+
.height(180.dp),
375+
contentAlignment = Alignment.Center
376+
) {
377+
CircularProgressIndicator(
378+
color = MaterialTheme.appColors.primary
379+
)
380+
}
378381
}
379382
}
380383
}
381-
}
382-
)
383-
}
384-
if (scrollState.shouldLoadMore(firstVisibleIndex, 4)) {
385-
onAction(AllEnrolledCoursesAction.EndOfPage)
384+
)
385+
}
386+
if (scrollState.shouldLoadMore(firstVisibleIndex, 4)) {
387+
onAction(AllEnrolledCoursesAction.EndOfPage)
388+
}
386389
}
387390
}
388-
}
389391

390-
is AllEnrolledCoursesUIState.Empty -> {
391-
Box(
392-
modifier = Modifier.fillMaxSize(),
393-
contentAlignment = Alignment.Center
394-
) {
395-
Column(
396-
modifier = Modifier
397-
.fillMaxHeight()
398-
.then(contentWidth)
399-
.then(emptyStatePaddings)
392+
is AllEnrolledCoursesUIState.Empty -> {
393+
Box(
394+
modifier = Modifier.fillMaxSize(),
395+
contentAlignment = Alignment.Center
400396
) {
401-
EmptyState()
397+
Column(
398+
modifier = Modifier
399+
.fillMaxHeight()
400+
.then(emptyStatePaddings)
401+
) {
402+
EmptyState()
403+
}
402404
}
403405
}
404406
}
405407
}
406-
}
407-
PullRefreshIndicator(
408-
refreshing,
409-
pullRefreshState,
410-
Modifier.align(Alignment.TopCenter)
411-
)
412-
413-
if (!isInternetConnectionShown && !hasInternetConnection) {
414-
OfflineModeDialog(
415-
Modifier
416-
.fillMaxWidth()
417-
.align(Alignment.BottomCenter),
418-
onDismissCLick = {
419-
isInternetConnectionShown = true
420-
},
421-
onReloadClick = {
422-
isInternetConnectionShown = true
423-
onAction(AllEnrolledCoursesAction.Reload)
424-
}
408+
PullRefreshIndicator(
409+
refreshing,
410+
pullRefreshState,
411+
Modifier.align(Alignment.TopCenter)
425412
)
413+
414+
if (!isInternetConnectionShown && !hasInternetConnection) {
415+
OfflineModeDialog(
416+
Modifier
417+
.fillMaxWidth()
418+
.align(Alignment.BottomCenter),
419+
onDismissCLick = {
420+
isInternetConnectionShown = true
421+
},
422+
onReloadClick = {
423+
isInternetConnectionShown = true
424+
onAction(AllEnrolledCoursesAction.Reload)
425+
}
426+
)
427+
}
426428
}
427429
}
428430
}

0 commit comments

Comments
 (0)