Skip to content

Commit 4915d18

Browse files
authored
[CLX-3127][Horizon] Learner dashboard landscape mode (#3364)
refs: CLX-3127 affects: Horizon release note: none
1 parent 6f4add1 commit 4915d18

20 files changed

+396
-394
lines changed

libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/DashboardScreen.kt

Lines changed: 93 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,19 @@ import android.content.pm.PackageManager
2222
import android.os.Build
2323
import androidx.activity.compose.rememberLauncherForActivityResult
2424
import androidx.activity.result.contract.ActivityResultContracts
25+
import androidx.compose.foundation.horizontalScroll
26+
import androidx.compose.foundation.layout.Arrangement
27+
import androidx.compose.foundation.layout.BoxWithConstraints
2528
import androidx.compose.foundation.layout.Column
29+
import androidx.compose.foundation.layout.IntrinsicSize
2630
import androidx.compose.foundation.layout.PaddingValues
2731
import androidx.compose.foundation.layout.Row
2832
import androidx.compose.foundation.layout.Spacer
33+
import androidx.compose.foundation.layout.fillMaxWidth
2934
import androidx.compose.foundation.layout.height
3035
import androidx.compose.foundation.layout.heightIn
3136
import androidx.compose.foundation.layout.padding
37+
import androidx.compose.foundation.layout.width
3238
import androidx.compose.foundation.pager.rememberPagerState
3339
import androidx.compose.foundation.rememberScrollState
3440
import androidx.compose.foundation.verticalScroll
@@ -76,6 +82,7 @@ import com.instructure.horizon.horizonui.foundation.HorizonColors
7682
import com.instructure.horizon.horizonui.foundation.HorizonElevation
7783
import com.instructure.horizon.horizonui.foundation.HorizonSpace
7884
import com.instructure.horizon.horizonui.foundation.SpaceSize
85+
import com.instructure.horizon.horizonui.isWideLayout
7986
import com.instructure.horizon.horizonui.molecules.Badge
8087
import com.instructure.horizon.horizonui.molecules.BadgeContent
8188
import com.instructure.horizon.horizonui.molecules.BadgeType
@@ -191,42 +198,7 @@ fun DashboardScreen(uiState: DashboardUiState, mainNavController: NavHostControl
191198
refreshStateFlow
192199
)
193200
HorizonSpace(SpaceSize.SPACE_16)
194-
val pagerState = rememberPagerState{ 3 }
195-
AnimatedHorizontalPager(
196-
pagerState,
197-
sizeAnimationRange = 0f,
198-
contentPadding = PaddingValues(horizontal = 24.dp),
199-
pageSpacing = 12.dp,
200-
verticalAlignment = Alignment.CenterVertically,
201-
) { index, modifier ->
202-
when (index) {
203-
0 -> {
204-
DashboardMyProgressWidget(
205-
shouldRefresh,
206-
refreshStateFlow,
207-
modifier.padding(bottom = 16.dp)
208-
)
209-
}
210-
1 -> {
211-
DashboardTimeSpentWidget(
212-
shouldRefresh,
213-
refreshStateFlow,
214-
modifier.padding(bottom = 16.dp)
215-
)
216-
}
217-
2 -> {
218-
DashboardSkillOverviewWidget(
219-
homeNavController,
220-
shouldRefresh,
221-
refreshStateFlow,
222-
modifier.padding(bottom = 16.dp)
223-
)
224-
}
225-
else -> {
226-
227-
}
228-
}
229-
}
201+
NumericWidgetRow(shouldRefresh, refreshStateFlow, homeNavController)
230202
DashboardSkillHighlightsWidget(
231203
homeNavController,
232204
shouldRefresh,
@@ -303,6 +275,91 @@ private fun HomeScreenTopBar(uiState: DashboardUiState, mainNavController: NavCo
303275
}
304276
}
305277

278+
@Composable
279+
private fun NumericWidgetRow(
280+
shouldRefresh: Boolean,
281+
refreshStateFlow: MutableStateFlow<List<Boolean>>,
282+
homeNavController: NavHostController
283+
) {
284+
BoxWithConstraints {
285+
val pagerState = rememberPagerState { 3 }
286+
if (this.isWideLayout) {
287+
Row(
288+
verticalAlignment = Alignment.CenterVertically,
289+
horizontalArrangement = Arrangement.spacedBy(12.dp),
290+
modifier = Modifier
291+
.horizontalScroll(rememberScrollState())
292+
.fillMaxWidth()
293+
.padding(horizontal = 24.dp)
294+
.padding(bottom = 12.dp)
295+
) {
296+
DashboardMyProgressWidget(
297+
shouldRefresh,
298+
refreshStateFlow,
299+
Modifier.width(IntrinsicSize.Max)
300+
)
301+
DashboardTimeSpentWidget(
302+
shouldRefresh,
303+
refreshStateFlow,
304+
Modifier.width(IntrinsicSize.Max)
305+
)
306+
DashboardSkillOverviewWidget(
307+
homeNavController,
308+
shouldRefresh,
309+
refreshStateFlow,
310+
Modifier.width(IntrinsicSize.Max)
311+
)
312+
}
313+
} else {
314+
AnimatedHorizontalPager(
315+
pagerState,
316+
sizeAnimationRange = 0f,
317+
beyondViewportPageCount = 3,
318+
contentPadding = PaddingValues(horizontal = 24.dp),
319+
pageSpacing = 12.dp,
320+
verticalAlignment = Alignment.CenterVertically,
321+
) { index, modifier ->
322+
when (index) {
323+
0 -> {
324+
DashboardMyProgressWidget(
325+
shouldRefresh,
326+
refreshStateFlow,
327+
modifier
328+
.fillMaxWidth()
329+
.padding(bottom = 16.dp)
330+
)
331+
}
332+
333+
1 -> {
334+
DashboardTimeSpentWidget(
335+
shouldRefresh,
336+
refreshStateFlow,
337+
modifier
338+
.fillMaxWidth()
339+
.padding(bottom = 16.dp)
340+
)
341+
}
342+
343+
2 -> {
344+
DashboardSkillOverviewWidget(
345+
homeNavController,
346+
shouldRefresh,
347+
refreshStateFlow,
348+
modifier
349+
.fillMaxWidth()
350+
.padding(bottom = 16.dp)
351+
)
352+
}
353+
354+
else -> {
355+
356+
}
357+
}
358+
}
359+
}
360+
}
361+
}
362+
306363
@Composable
307364
private fun NotificationPermissionRequest() {
308365
val context = LocalContext.current

libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCardState.kt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ import java.util.Date
66
data class DashboardPaginatedWidgetCardState(
77
val items: List<DashboardPaginatedWidgetCardItemState> = emptyList(),
88
val isLoading: Boolean = false,
9-
)
9+
) {
10+
companion object {
11+
val Loading = DashboardPaginatedWidgetCardState(
12+
items = listOf(DashboardPaginatedWidgetCardItemState.Loading),
13+
isLoading = true
14+
)
15+
}
16+
}
1017

1118
data class DashboardPaginatedWidgetCardItemState(
1219
val chipState: DashboardPaginatedWidgetCardChipState? = null,
@@ -15,7 +22,20 @@ data class DashboardPaginatedWidgetCardItemState(
1522
val date: Date? = null,
1623
val title: String? = null,
1724
val route: DashboardPaginatedWidgetCardButtonRoute? = null
18-
)
25+
) {
26+
companion object {
27+
val Loading = DashboardPaginatedWidgetCardItemState(
28+
chipState = DashboardPaginatedWidgetCardChipState(
29+
label = "Announcement",
30+
color = StatusChipColor.Sky
31+
),
32+
title = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Announcement title shown here.",
33+
source = "Institution or Course Name Here",
34+
date = Date(),
35+
route = DashboardPaginatedWidgetCardButtonRoute.MainRoute("")
36+
)
37+
}
38+
}
1939

2040
data class DashboardPaginatedWidgetCardChipState(
2141
val label: String,

libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidget.kt

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,15 @@ import androidx.compose.runtime.LaunchedEffect
2222
import androidx.compose.runtime.collectAsState
2323
import androidx.compose.runtime.getValue
2424
import androidx.compose.ui.Modifier
25-
import androidx.compose.ui.res.stringResource
2625
import androidx.compose.ui.unit.dp
2726
import androidx.hilt.navigation.compose.hiltViewModel
2827
import androidx.navigation.NavHostController
29-
import com.instructure.horizon.R
3028
import com.instructure.horizon.features.dashboard.DashboardItemState
3129
import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCard
32-
import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardButtonRoute
33-
import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardChipState
34-
import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardItemState
30+
import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardState
3531
import com.instructure.horizon.features.dashboard.widget.announcement.card.DashboardAnnouncementBannerCardError
36-
import com.instructure.horizon.horizonui.molecules.StatusChipColor
3732
import kotlinx.coroutines.flow.MutableStateFlow
3833
import kotlinx.coroutines.flow.update
39-
import java.util.Date
4034

4135
@Composable
4236
fun DashboardAnnouncementBannerWidget(
@@ -71,21 +65,7 @@ fun DashboardAnnouncementBannerSection(
7165
when (state.state) {
7266
DashboardItemState.LOADING -> {
7367
DashboardPaginatedWidgetCard(
74-
state.cardState.copy(
75-
items = listOf(
76-
DashboardPaginatedWidgetCardItemState(
77-
chipState = DashboardPaginatedWidgetCardChipState(
78-
label = stringResource(R.string.notificationsAnnouncementCategoryLabel),
79-
color = StatusChipColor.Sky
80-
),
81-
title = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Announcement title shown here.",
82-
source = "Institution or Course Name Here",
83-
date = Date(),
84-
route = DashboardPaginatedWidgetCardButtonRoute.MainRoute("")
85-
)
86-
),
87-
isLoading = true
88-
),
68+
DashboardPaginatedWidgetCardState.Loading,
8969
mainNavController,
9070
homeNavController,
9171
)

libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardCourseSection.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidge
4141
import com.instructure.horizon.features.dashboard.widget.course.card.CardClickAction
4242
import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardContent
4343
import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardError
44-
import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardLoading
4544
import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardState
4645
import com.instructure.horizon.features.home.HomeNavigationRoute
4746
import com.instructure.horizon.horizonui.foundation.HorizonColors
@@ -82,7 +81,12 @@ fun DashboardCourseSection(
8281
) {
8382
when(state.state) {
8483
DashboardItemState.LOADING -> {
85-
DashboardCourseCardLoading(Modifier.padding(horizontal = 24.dp))
84+
DashboardCourseCardContent(
85+
DashboardCourseCardState.Loading,
86+
{ handleClickAction(it, mainNavController, homeNavController) },
87+
true,
88+
modifier = Modifier.padding(horizontal = 24.dp)
89+
)
8690
}
8791
DashboardItemState.ERROR -> {
8892
DashboardCourseCardError({state.onRefresh {} }, Modifier.padding(horizontal = 24.dp))
@@ -160,7 +164,9 @@ private fun DashboardCourseItem(
160164
modifier = modifier.fillMaxWidth()
161165
){
162166
DashboardCourseCardContent(
163-
cardState, { handleClickAction(it, mainNavController, homeNavController) }
167+
cardState,
168+
{ handleClickAction(it, mainNavController, homeNavController) },
169+
false
164170
)
165171
}
166172
}

libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardCourseViewModel.kt

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ import com.instructure.canvasapi2.utils.weave.tryLaunch
2525
import com.instructure.horizon.features.dashboard.DashboardEvent
2626
import com.instructure.horizon.features.dashboard.DashboardEventHandler
2727
import com.instructure.horizon.features.dashboard.DashboardItemState
28+
import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardState
2829
import com.instructure.horizon.features.dashboard.widget.course.card.CardClickAction
2930
import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardModuleItemState
30-
import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardState
31-
import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardState
3231
import com.instructure.horizon.model.LearningObjectType
3332
import com.instructure.journey.type.ProgramProgressCourseEnrollmentStatus
3433
import com.instructure.pandautils.utils.formatIsoDuration
@@ -110,22 +109,7 @@ class DashboardCourseViewModel @Inject constructor(
110109
nextModuleForCourse = { courseId ->
111110
fetchNextModuleState(courseId, forceNetwork)
112111
},
113-
).map { state ->
114-
if (state.buttonState?.onClickAction is CardClickAction.Action) {
115-
state.copy(buttonState = state.buttonState.copy(
116-
onClickAction = CardClickAction.Action {
117-
viewModelScope.tryLaunch {
118-
updateCourseButtonState(state, isLoading = true)
119-
state.buttonState.action()
120-
onRefresh()
121-
updateCourseButtonState(state, isLoading = false)
122-
} catch {
123-
updateCourseButtonState(state, isLoading = false)
124-
}
125-
},
126-
))
127-
} else state
128-
}
112+
)
129113

130114
val programCardStates = programs
131115
.filter { program -> program.sortedRequirements.none { it.enrollmentStatus == ProgramProgressCourseEnrollmentStatus.ENROLLED } }
@@ -156,22 +140,4 @@ class DashboardCourseViewModel @Inject constructor(
156140
onClickAction = CardClickAction.NavigateToModuleItem(courseId, nextModuleItem.id)
157141
)
158142
}
159-
160-
private fun updateCourseButtonState(state: DashboardCourseCardState, isLoading: Boolean) {
161-
_uiState.update {
162-
it.copy(
163-
courses = it.courses.map { originalState ->
164-
if (originalState.title == state.title && originalState.parentPrograms == state.parentPrograms) {
165-
originalState.copy(
166-
buttonState = originalState.buttonState?.copy(
167-
isLoading = isLoading
168-
)
169-
)
170-
} else {
171-
originalState
172-
}
173-
}
174-
)
175-
}
176-
}
177143
}

libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardMapper.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ private fun GetCoursesQuery.Enrollment.mapCompleted(context: Context, programs:
9797
description = context.getString(R.string.dashboardCompletedCourseDetails),
9898
progress = this.course?.usersConnection?.nodes?.firstOrNull()?.courseProgression?.requirements?.completionPercentage ?: 0.0,
9999
moduleItem = null,
100-
buttonState = null,
101100
onClickAction = CardClickAction.NavigateToCourse(this.course?.id?.toLongOrNull() ?: -1L)
102101
)
103102
}
@@ -124,8 +123,6 @@ private suspend fun GetCoursesQuery.Enrollment.mapActive(
124123
description = null,
125124
progress = this.course?.usersConnection?.nodes?.firstOrNull()?.courseProgression?.requirements?.completionPercentage ?: 0.0,
126125
moduleItem = nextModuleForCourse(this.course?.id?.toLongOrNull()),
127-
buttonState = null,
128126
onClickAction = CardClickAction.NavigateToCourse(this.course?.id?.toLongOrNull() ?: -1L),
129-
lastAccessed = this.lastActivityAt
130127
)
131128
}

0 commit comments

Comments
 (0)