Skip to content

Commit 08b6c69

Browse files
author
alllexey
committed
Load next/previous weeks on scroll
1 parent 468e184 commit 08b6c69

File tree

2 files changed

+120
-5
lines changed

2 files changed

+120
-5
lines changed

app/src/main/java/me/alllexey123/itmowidgets/ui/schedule/ScheduleActivity.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ class ScheduleActivity : AppCompatActivity() {
4646

4747
observeUiState()
4848

49-
scheduleViewModel.fetchScheduleData(forceRefresh = false)
5049

5150
swipeRefreshLayout.setOnRefreshListener {
5251
scheduleViewModel.fetchScheduleData(forceRefresh = true)
5352
}
53+
54+
scheduleViewModel.fetchScheduleData(forceRefresh = false)
5455
}
5556

5657
private fun setupButtons() {
@@ -75,6 +76,25 @@ class ScheduleActivity : AppCompatActivity() {
7576
outerRecyclerView.adapter = dayScheduleAdapter
7677

7778
snapHelper.attachToRecyclerView(outerRecyclerView)
79+
80+
outerRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
81+
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
82+
super.onScrolled(recyclerView, dx, dy)
83+
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
84+
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
85+
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
86+
val totalItemCount = dayScheduleAdapter.itemCount
87+
88+
if (totalItemCount > 0) {
89+
if (firstVisibleItemPosition < 2) {
90+
scheduleViewModel.fetchPreviousDays()
91+
}
92+
if (lastVisibleItemPosition > totalItemCount - 3) {
93+
scheduleViewModel.fetchNextDays()
94+
}
95+
}
96+
}
97+
})
7898
}
7999

80100
private var isInitialLoad = true

app/src/main/java/me/alllexey123/itmowidgets/ui/schedule/ScheduleViewModel.kt

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ class ScheduleViewModel(
2323
private val _uiState = MutableLiveData<ScheduleUiState>()
2424
val uiState: LiveData<ScheduleUiState> = _uiState
2525

26+
private var isLoading = false
27+
private var dateRange: ClosedRange<LocalDate>? = null
28+
2629
fun fetchScheduleData(forceRefresh: Boolean) {
30+
if (isLoading) return
31+
isLoading = true
32+
2733
viewModelScope.launch {
2834
try {
29-
val startDate = LocalDate.now().minusDays(7)
30-
val endDate = LocalDate.now().plusDays(7)
35+
val startDate = dateRange?.start ?: LocalDate.now().minusDays(7)
36+
val endDate = dateRange?.endInclusive ?: LocalDate.now().plusDays(7)
3137

32-
val cachedSchedule =
33-
scheduleRepository.getCachedScheduleForRange(startDate, endDate)
38+
val cachedSchedule = scheduleRepository.getCachedScheduleForRange(startDate, endDate)
3439

3540
if (cachedSchedule.isNotEmpty() && !forceRefresh) {
3641

@@ -48,10 +53,100 @@ class ScheduleViewModel(
4853
val remoteSchedule = scheduleRepository.getScheduleForRange(startDate, endDate)
4954
_uiState.postValue(ScheduleUiState.Success(remoteSchedule, false))
5055
}
56+
dateRange = startDate..endDate
57+
} catch (e: Exception) {
58+
val errorMessage = "Failed to update schedule: ${e.message}"
59+
_uiState.postValue(ScheduleUiState.Error(errorMessage))
60+
e.printStackTrace()
61+
} finally {
62+
isLoading = false
63+
}
64+
}
65+
}
66+
67+
fun fetchPreviousDays() {
68+
if (isLoading) return
69+
isLoading = true
70+
71+
viewModelScope.launch {
72+
val currentSchedule = (_uiState.value as? ScheduleUiState.Success)?.schedule ?: emptyList()
73+
try {
74+
val currentStartDate = dateRange?.start ?: LocalDate.now()
75+
val newStartDate = currentStartDate.minusDays(7)
76+
val newEndDate = currentStartDate.minusDays(1)
77+
78+
val cachedSchedule = scheduleRepository.getCachedScheduleForRange(newStartDate, newEndDate)
79+
val remoteSchedule = if (cachedSchedule.isNotEmpty()) {
80+
val totalDays = Duration.between(newStartDate.atStartOfDay(), newEndDate.atStartOfDay()).toDays() + 1
81+
82+
if (cachedSchedule.size < totalDays) {
83+
_uiState.postValue(ScheduleUiState.Loading)
84+
scheduleRepository.getScheduleForRange(newStartDate, newEndDate)
85+
} else {
86+
cachedSchedule
87+
}
88+
} else {
89+
_uiState.value = ScheduleUiState.Loading
90+
scheduleRepository.getScheduleForRange(newStartDate, newEndDate)
91+
}
92+
93+
val updatedSchedule = (remoteSchedule + currentSchedule).distinctBy { it.date }.sortedBy { it.date }
94+
_uiState.postValue(ScheduleUiState.Success(updatedSchedule, false))
95+
dateRange = newStartDate..(dateRange?.endInclusive ?: LocalDate.now())
96+
} catch (e: Exception) {
97+
val errorMessage = "Failed to update schedule: ${e.message}"
98+
_uiState.postValue(ScheduleUiState.Error(errorMessage))
99+
_uiState.postValue(ScheduleUiState.Success(currentSchedule, false))
100+
e.printStackTrace()
101+
} finally {
102+
isLoading = false
103+
}
104+
}
105+
}
106+
107+
fun fetchNextDays() {
108+
if (isLoading) return
109+
isLoading = true
110+
111+
viewModelScope.launch {
112+
val currentSchedule =
113+
(_uiState.value as? ScheduleUiState.Success)?.schedule ?: emptyList()
114+
try {
115+
116+
val currentEndDate = dateRange?.endInclusive ?: LocalDate.now()
117+
val newStartDate = currentEndDate.plusDays(1)
118+
val newEndDate = currentEndDate.plusDays(7)
119+
120+
val cachedSchedule =
121+
scheduleRepository.getCachedScheduleForRange(newStartDate, newEndDate)
122+
val remoteSchedule = if (cachedSchedule.isNotEmpty()) {
123+
val totalDays =
124+
Duration.between(newStartDate.atStartOfDay(), newEndDate.atStartOfDay())
125+
.toDays() + 1
126+
127+
if (cachedSchedule.size < totalDays) {
128+
_uiState.postValue(ScheduleUiState.Loading)
129+
scheduleRepository.getScheduleForRange(newStartDate, newEndDate)
130+
} else {
131+
cachedSchedule
132+
}
133+
} else {
134+
_uiState.value = ScheduleUiState.Loading
135+
scheduleRepository.getScheduleForRange(newStartDate, newEndDate)
136+
}
137+
138+
val updatedSchedule =
139+
(currentSchedule + remoteSchedule).distinctBy { it.date }.sortedBy { it.date }
140+
_uiState.postValue(ScheduleUiState.Success(updatedSchedule, false))
141+
142+
dateRange = (dateRange?.start ?: LocalDate.now())..newEndDate
51143
} catch (e: Exception) {
52144
val errorMessage = "Failed to update schedule: ${e.message}"
53145
_uiState.postValue(ScheduleUiState.Error(errorMessage))
146+
_uiState.postValue(ScheduleUiState.Success(currentSchedule, false))
54147
e.printStackTrace()
148+
} finally {
149+
isLoading = false
55150
}
56151
}
57152
}

0 commit comments

Comments
 (0)