Skip to content

Commit aaff6be

Browse files
committed
[messages] dynamic messages list loading
1 parent bd1a1c9 commit aaff6be

File tree

4 files changed

+58
-14
lines changed

4 files changed

+58
-14
lines changed

app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ interface MessagesDao {
3838
)
3939
fun getMessagesStats(): LiveData<MessagesTotals>
4040

41-
@Query("SELECT * FROM message ORDER BY createdAt DESC LIMIT 50")
42-
fun selectLast(): LiveData<List<Message>>
41+
@Query("SELECT * FROM message ORDER BY createdAt DESC LIMIT :limit")
42+
fun selectLast(limit: Int): LiveData<List<Message>>
4343

4444
/**
4545
* FIFO: oldest pending first (priority DESC, createdAt ASC)

app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import java.util.Date
1919
class MessagesRepository(private val dao: MessagesDao) {
2020
private val gson = GsonBuilder().serializeNulls().create()
2121

22-
val lastMessages = dao.selectLast().distinctUntilChanged()
22+
fun selectLast(limit: Int) = dao.selectLast(limit).distinctUntilChanged()
2323

2424
val messagesTotals: LiveData<MessagesTotals> = dao.getMessagesStats().distinctUntilChanged()
2525

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,45 @@
11
package me.capcom.smsgateway.modules.messages.vm
22

33
import androidx.lifecycle.LiveData
4+
import androidx.lifecycle.MediatorLiveData
5+
import androidx.lifecycle.MutableLiveData
46
import androidx.lifecycle.ViewModel
7+
import androidx.lifecycle.switchMap
58
import me.capcom.smsgateway.data.entities.Message
69
import me.capcom.smsgateway.data.entities.MessagesTotals
710
import me.capcom.smsgateway.modules.messages.MessagesRepository
811

912
class MessagesListViewModel(
10-
messagesRepo: MessagesRepository
13+
private val messagesRepo: MessagesRepository
1114
) : ViewModel() {
12-
val messages: LiveData<List<Message>> =
13-
messagesRepo.lastMessages
14-
1515
val totals: LiveData<MessagesTotals> =
1616
messagesRepo.messagesTotals
17+
18+
private val _limit = MutableLiveData<Int>(chunkSize)
19+
private val _messages = MediatorLiveData<List<Message>>()
20+
val messages: LiveData<List<Message>> = _messages
21+
22+
init {
23+
_messages.addSource(_limit.switchMap { messagesRepo.selectLast(it) }) {
24+
_messages.value = it
25+
hasMore = it.size >= (_limit.value ?: chunkSize)
26+
isLoading = false
27+
}
28+
loadMore()
29+
}
30+
31+
private var isLoading = false
32+
private var hasMore = true
33+
34+
fun loadMore(index: Int = 0) {
35+
val currentLimit = _limit.value ?: 0
36+
if (currentLimit >= index + chunkSize || isLoading || !hasMore) return
37+
38+
isLoading = true
39+
_limit.value = currentLimit + chunkSize
40+
}
41+
42+
companion object {
43+
private const val chunkSize = 50
44+
}
1745
}

app/src/main/java/me/capcom/smsgateway/ui/MessagesListFragment.kt

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import android.view.ViewGroup
77
import androidx.fragment.app.Fragment
88
import androidx.fragment.app.commit
99
import androidx.recyclerview.widget.DividerItemDecoration
10+
import androidx.recyclerview.widget.LinearLayoutManager
11+
import androidx.recyclerview.widget.RecyclerView
1012
import me.capcom.smsgateway.R
1113
import me.capcom.smsgateway.data.entities.Message
1214
import me.capcom.smsgateway.databinding.FragmentMessagesListBinding
1315
import me.capcom.smsgateway.modules.messages.vm.MessagesListViewModel
1416
import me.capcom.smsgateway.ui.adapters.MessagesAdapter
1517
import org.koin.androidx.viewmodel.ext.android.viewModel
1618

19+
1720
class MessagesListFragment : Fragment(), MessagesAdapter.OnItemClickListener<Message> {
1821

1922
private val viewModel: MessagesListViewModel by viewModel()
@@ -34,6 +37,7 @@ class MessagesListFragment : Fragment(), MessagesAdapter.OnItemClickListener<Mes
3437
super.onViewCreated(view, savedInstanceState)
3538

3639
binding.recyclerView.adapter = messagesAdapter
40+
binding.recyclerView.addOnScrollListener(scrollListener)
3741
binding.recyclerView.addItemDecoration(
3842
DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)
3943
)
@@ -58,20 +62,32 @@ class MessagesListFragment : Fragment(), MessagesAdapter.OnItemClickListener<Mes
5862
}
5963
}
6064

65+
override fun onItemClick(item: Message) {
66+
parentFragmentManager.commit {
67+
replace(R.id.rootLayout, MessageDetailsFragment.newInstance(item.id))
68+
addToBackStack(null)
69+
}
70+
}
71+
6172
override fun onDestroyView() {
73+
binding.recyclerView.removeOnScrollListener(scrollListener)
6274
super.onDestroyView()
6375
_binding = null
6476
}
6577

78+
private val scrollListener = object : RecyclerView.OnScrollListener() {
79+
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
80+
super.onScrolled(recyclerView, dx, dy)
81+
82+
val linearLayoutManager = recyclerView.layoutManager as? LinearLayoutManager
83+
linearLayoutManager?.findLastVisibleItemPosition()?.let {
84+
if (it == messagesAdapter.itemCount - 1) viewModel.loadMore(messagesAdapter.itemCount)
85+
}
86+
}
87+
}
88+
6689
companion object {
6790
fun newInstance() =
6891
MessagesListFragment()
6992
}
70-
71-
override fun onItemClick(item: Message) {
72-
parentFragmentManager.commit {
73-
replace(R.id.rootLayout, MessageDetailsFragment.newInstance(item.id))
74-
addToBackStack(null)
75-
}
76-
}
7793
}

0 commit comments

Comments
 (0)