Skip to content

Commit b5bfca3

Browse files
feat: pull to refresh
1 parent 17b1ed9 commit b5bfca3

File tree

3 files changed

+84
-50
lines changed

3 files changed

+84
-50
lines changed

dates/src/main/java/org/openedx/dates/presentation/dates/DatesFragment.kt

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ import androidx.compose.foundation.lazy.LazyColumn
2424
import androidx.compose.foundation.lazy.itemsIndexed
2525
import androidx.compose.foundation.shape.RoundedCornerShape
2626
import androidx.compose.material.CircularProgressIndicator
27+
import androidx.compose.material.ExperimentalMaterialApi
2728
import androidx.compose.material.Icon
2829
import androidx.compose.material.MaterialTheme
2930
import androidx.compose.material.Scaffold
3031
import androidx.compose.material.Text
3132
import androidx.compose.material.icons.Icons
3233
import androidx.compose.material.icons.automirrored.filled.ArrowForwardIos
34+
import androidx.compose.material.pullrefresh.PullRefreshIndicator
35+
import androidx.compose.material.pullrefresh.pullRefresh
36+
import androidx.compose.material.pullrefresh.rememberPullRefreshState
3337
import androidx.compose.material.rememberScaffoldState
3438
import androidx.compose.runtime.Composable
3539
import androidx.compose.runtime.collectAsState
@@ -92,8 +96,8 @@ class DatesFragment : Fragment() {
9296
viewModel.onSettingsClick(requireActivity().supportFragmentManager)
9397
}
9498

95-
DatesViewActions.ReloadData -> {
96-
99+
DatesViewActions.SwipeRefresh -> {
100+
viewModel.refreshData()
97101
}
98102

99103
is DatesViewActions.OpenEvent -> {
@@ -108,6 +112,7 @@ class DatesFragment : Fragment() {
108112

109113
}
110114

115+
@OptIn(ExperimentalMaterialApi::class)
111116
@Composable
112117
private fun DatesScreen(
113118
uiState: DatesUIState,
@@ -124,10 +129,15 @@ private fun DatesScreen(
124129
)
125130
)
126131
}
132+
val pullRefreshState = rememberPullRefreshState(
133+
refreshing = uiState.isRefreshing,
134+
onRefresh = { onAction(DatesViewActions.SwipeRefresh) }
135+
)
127136

128137
Scaffold(
129138
scaffoldState = scaffoldState,
130-
modifier = Modifier.fillMaxSize(),
139+
modifier = Modifier
140+
.fillMaxSize(),
131141
backgroundColor = MaterialTheme.appColors.background,
132142
topBar = {
133143
MainScreenTitle(
@@ -141,58 +151,71 @@ private fun DatesScreen(
141151
)
142152
},
143153
content = { paddingValues ->
144-
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
145-
146-
if (uiState.isLoading) {
147-
Box(
148-
modifier = Modifier.fillMaxSize(),
149-
contentAlignment = Alignment.Center
150-
) {
151-
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
152-
}
153-
} else if (uiState.dates.isEmpty()) {
154-
EmptyState()
155-
} else {
156-
Box(
157-
modifier = Modifier
158-
.fillMaxSize()
159-
.displayCutoutForLandscape()
160-
.padding(paddingValues)
161-
.padding(horizontal = 16.dp),
162-
contentAlignment = Alignment.TopCenter
163-
) {
164-
LazyColumn(
165-
modifier = contentWidth,
166-
contentPadding = PaddingValues(bottom = 20.dp)
154+
Box(
155+
modifier = Modifier
156+
.fillMaxSize()
157+
.pullRefresh(pullRefreshState)
158+
) {
159+
if (uiState.isLoading) {
160+
Box(
161+
modifier = Modifier.fillMaxSize(),
162+
contentAlignment = Alignment.Center
167163
) {
168-
uiState.dates.keys.forEach { dueDateCategory ->
169-
val dates = uiState.dates[dueDateCategory] ?: emptyList()
170-
if (dates.isNotEmpty()) {
171-
item {
172-
Text(
173-
modifier = Modifier
174-
.fillMaxWidth()
175-
.padding(bottom = 8.dp, top = 20.dp),
176-
text = stringResource(id = dueDateCategory.label),
177-
color = MaterialTheme.appColors.textDark,
178-
style = MaterialTheme.appTypography.titleMedium,
179-
)
180-
}
181-
itemsIndexed(dates) { index, date ->
182-
val itemPosition = ListItemPosition.detectPosition(index, dates)
183-
DateItem(
184-
date = date,
185-
lineColor = dueDateCategory.color,
186-
itemPosition = itemPosition,
187-
onClick = {
188-
onAction(DatesViewActions.OpenEvent())
189-
}
190-
)
164+
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
165+
}
166+
} else if (uiState.dates.isEmpty()) {
167+
EmptyState()
168+
} else {
169+
Box(
170+
modifier = Modifier
171+
.fillMaxSize()
172+
.displayCutoutForLandscape()
173+
.padding(paddingValues)
174+
.padding(horizontal = 16.dp),
175+
contentAlignment = Alignment.TopCenter
176+
) {
177+
LazyColumn(
178+
modifier = contentWidth,
179+
contentPadding = PaddingValues(bottom = 20.dp)
180+
) {
181+
uiState.dates.keys.forEach { dueDateCategory ->
182+
val dates = uiState.dates[dueDateCategory] ?: emptyList()
183+
if (dates.isNotEmpty()) {
184+
item {
185+
Text(
186+
modifier = Modifier
187+
.fillMaxWidth()
188+
.padding(bottom = 8.dp, top = 20.dp),
189+
text = stringResource(id = dueDateCategory.label),
190+
color = MaterialTheme.appColors.textDark,
191+
style = MaterialTheme.appTypography.titleMedium,
192+
)
193+
}
194+
itemsIndexed(dates) { index, date ->
195+
val itemPosition =
196+
ListItemPosition.detectPosition(index, dates)
197+
DateItem(
198+
date = date,
199+
lineColor = dueDateCategory.color,
200+
itemPosition = itemPosition,
201+
onClick = {
202+
onAction(DatesViewActions.OpenEvent())
203+
}
204+
)
205+
}
191206
}
192207
}
193208
}
194209
}
195210
}
211+
212+
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
213+
214+
PullRefreshIndicator(
215+
uiState.isRefreshing,
216+
pullRefreshState,
217+
Modifier.align(Alignment.TopCenter)
218+
)
196219
}
197220
}
198221
)

dates/src/main/java/org/openedx/dates/presentation/dates/DatesUIState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ package org.openedx.dates.presentation.dates
22

33
data class DatesUIState(
44
val isLoading: Boolean = true,
5+
val isRefreshing: Boolean = false,
56
val dates: Map<DueDateCategory, List<String>> = emptyMap()
67
)

dates/src/main/java/org/openedx/dates/presentation/dates/DatesViewModel.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class DatesViewModel(
3535
_uiState.update { state ->
3636
state.copy(
3737
isLoading = false,
38+
isRefreshing = false,
3839
dates = mapOf(
3940
DueDateCategory.PAST_DUE to listOf("Date1", "Date2", "Date3"),
4041
DueDateCategory.TODAY to listOf("Date1"),
@@ -46,6 +47,15 @@ class DatesViewModel(
4647
}
4748
}
4849

50+
fun refreshData() {
51+
_uiState.update { state ->
52+
state.copy(
53+
isRefreshing = true,
54+
)
55+
}
56+
fetchDates()
57+
}
58+
4959
fun onSettingsClick(fragmentManager: FragmentManager) {
5060
datesRouter.navigateToSettings(fragmentManager)
5161
}
@@ -54,5 +64,5 @@ class DatesViewModel(
5464
interface DatesViewActions {
5565
object OpenSettings : DatesViewActions
5666
class OpenEvent() : DatesViewActions
57-
object ReloadData : DatesViewActions
67+
object SwipeRefresh : DatesViewActions
5868
}

0 commit comments

Comments
 (0)