implementing a chat application interface in jetpack compose using paging3, the list contains message and date module, the issue is when i create a new message i get that message a response, but now i want to add that message at the starting of the list.
@HiltViewModel
class TasklogsViewModel @Inject constructor(
private val tasklogsRepository: TasklogsRepository,
savedStateHandle: SavedStateHandle
) : BaseViewModel() {
private val taskId: Int? = savedStateHandle[TASK_ID_KEY]
val tasklogsPager = Pager(
PagingConfig(pageSize = 10)
) {
TasklogsPagingSource(taskId = taskId, tasklogsRepository)
}.flow.cachedIn(viewModelScope)
private val _uiState = MutableStateFlow<TasklogsUiState>(TasklogsUiState())
val uiState: StateFlow<TasklogsUiState> = _uiState
fun createTasklogs(message: String) = launchIO({
}) {
try {
val request = CreateTasklogRequest(
content = message,
type = "TEXT_MESSAGE",
taskId = taskId!!,
dateCreated = System.currentTimeMillis()
)
when (val res = tasklogsRepository.createTasklog(request)) {
is NetworkResponse.Error -> {
_uiState.update { currentState ->
currentState.copy(errorMessage = res.body.message)
}
}
is NetworkResponse.Success -> {
}
}
} catch (e: Exception) {
_uiState.update { currentState ->
currentState.copy(errorMessage = e.message)
}
}
}
}
class TasklogsPagingSource(
private val taskId: Int?,
private val tasklogsRepo: TasklogsRepository
) : PagingSource<Int, TasklogsComponentViewData>() {
override fun getRefreshKey(state: PagingState<Int, TasklogsComponentViewData>): Int? {
return state.anchorPosition?.let { position ->
val page = state.closestPageToPosition(position)
page?.prevKey?.minus(1) ?: page?.nextKey?.plus(1)
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TasklogsComponentViewData> {
return try {
val page = params.key ?: 0
if (taskId == null) throw Exception("TaskId is invalid")
when (val response = tasklogsRepo.getTasklogs(taskId, page)) {
is NetworkResponse.Error -> {
LoadResult.Error(Exception(response.body.message))
}
is NetworkResponse.Success -> {
val tasklogsList = mutableListOf<TasklogsComponentViewData>()
response.successBody.data.forEach { moduleData ->
when (moduleData.moduleId) {
"task_logs" -> {
val taskLog =
(moduleData.data as LogEntity.TasklogEntity).toTasklogViewData()
tasklogsList.add(taskLog)
}
"log_date" -> {
val logDate =
(moduleData.data as LogEntity.LogDateEntity).toLogDateViewData()
tasklogsList.add(logDate)
}
}
}
LoadResult.Page(
data = tasklogsList,
prevKey = null,
nextKey = if (response.successBody.data.isNotEmpty()) response.successBody.paginationKey else null
)
}
}
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
couldn't actually found a solution, but thought of forcefetching the list again which i don't want to.
The problem you are facing is you are paging directly from the web, it only makes sense that if the page changes on the web you would have to pull the page again.
You should look into something like RemoteMediator
https://developer.android.com/reference/kotlin/androidx/paging/RemoteMediator
The solution is to add a local db as the PagingSource
instead of your web service directly.
The response from your web service will populate the database, and any change to the db will provoke the pager to run.
You would use the RemoteMediator
when the user first enters the page with all the messages, this will populate the db thus Paging the data to the screen.
Whenever you determine the user has a new message you would do the same, when you get the message you insert it into the database and the Pager does its thing.
This solution allows your Pager to just focus on what is in the db which makes everything a lot simpler down stream.