diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/components/SearchComponents.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/components/SearchComponents.kt index 18fddf1..b9dbc8d 100644 --- a/app/src/main/java/ru/megboyzz/lks/profile/ui/components/SearchComponents.kt +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/components/SearchComponents.kt @@ -16,10 +16,12 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton @@ -31,10 +33,13 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -42,6 +47,8 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import ru.megboyzz.lks.profile.MainActivity import ru.megboyzz.lks.profile.domain.entities.LksResponse import ru.megboyzz.lks.profile.domain.entities.LksStudent @@ -182,10 +189,37 @@ private fun Main() { fun CardsColumn( modifier: Modifier = Modifier, lksStudents: List, - onLongPress: (LksStudent) -> Unit + onLongPress: (LksStudent) -> Unit, + onColumnOverflow: suspend () -> Unit, ) { + + val buffer = 1 + + var isLoading by remember { mutableStateOf(false) } + val listState = rememberLazyListState() + + // observe list scrolling + val reachedBottom: Boolean by remember { + derivedStateOf { + val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull() + lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - buffer + } + } + + // load more if scrolled to bottom + LaunchedEffect(reachedBottom) { + if (reachedBottom) { + isLoading = true + withContext(Dispatchers.IO) { + onColumnOverflow() + } + isLoading = false + } + } + Column(modifier) { LazyColumn( + state = listState, contentPadding = PaddingValues(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { @@ -195,6 +229,10 @@ fun CardsColumn( onLongPress = { onLongPress(student) } ) } + if(isLoading) + item { + CircularProgressIndicator() + } } } } \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SearchScreenModel.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SearchScreenModel.kt index a500b2d..95d3eba 100644 --- a/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SearchScreenModel.kt +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SearchScreenModel.kt @@ -2,9 +2,7 @@ package ru.megboyzz.lks.profile.ui.model import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.screenModelScope -import cafe.adriel.voyager.hilt.ScreenModelFactory -import cafe.adriel.voyager.navigator.Navigator -import dagger.assisted.AssistedFactory +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -15,7 +13,6 @@ import ru.megboyzz.lks.profile.domain.DefaultCookies import ru.megboyzz.lks.profile.domain.entities.LksRequest import ru.megboyzz.lks.profile.domain.entities.LksResponse import ru.megboyzz.lks.profile.domain.repositories.LksRepository -import ru.megboyzz.lks.profile.ui.HiltModule.MyFactory import javax.inject.Inject class SearchScreenModel @Inject constructor( @@ -43,30 +40,47 @@ class SearchScreenModel @Inject constructor( val state = MutableStateFlow(UiState.Idle) init { - /* _curPageNumber.onEach { - update() - }*/ - + _curPageNumber.onEach { + update(_request.value) + }.launchIn(CoroutineScope(Dispatchers.IO)) } fun nextPage() = screenModelScope.launch { _curPageNumber.emit(_curPageNumber.value++) } - fun newSearch(request: LksRequest) = screenModelScope.launch(Dispatchers.IO) { + fun resetStudents() = screenModelScope.launch { + _responseList.emit(LksResponse()) + } + + fun newSearch(request: LksRequest) { + screenModelScope.launch(Dispatchers.IO) { + resetStudents() + update(request) + } + } + + private suspend fun update(request: LksRequest) { state.emit(UiState.Loading) + _request.emit(request) + val repoCookies = lksRepository.restoreCooke() val result = lksRepository.getPage(request, repoCookies, curPageNumber.value) if(result.isFailure) { state.emit(UiState.Error("Ошибка :-(")) - return@launch + return } val list = result.getOrElse { LksResponse() } - _responseList.emit(list) + val updatedStudents = _responseList.value.copy( + students = _responseList.value.students + list.students, + availablePages = _responseList.value.availablePages + list.availablePages + ) + + _responseList.emit(updatedStudents) state.emit(UiState.Idle) } diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SearchScreen.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SearchScreen.kt index d8f78d6..858eb5a 100644 --- a/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SearchScreen.kt +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SearchScreen.kt @@ -1,5 +1,6 @@ package ru.megboyzz.lks.profile.ui.screens +import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,6 +17,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.PointerIcon.Companion.Text +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.hilt.getScreenModel @@ -41,6 +43,8 @@ class SearchScreen : Screen { val list by screenModel.responseList.collectAsState() + val context = LocalContext.current + MainScaffold( onFloatingButtonClick = { bottomSheetNavigator.show(SearchBottomScreen(screenModel)) } ) { @@ -52,7 +56,13 @@ class SearchScreen : Screen { CardsColumn( modifier = Modifier.padding(it), lksStudents = list.students, - onLongPress = { student -> bottomSheetNavigator.show(LongPressMenu(student)) } + onLongPress = { student -> bottomSheetNavigator.show(LongPressMenu(student)) }, + onColumnOverflow = { + //Toast.makeText(context, "Hello!", Toast.LENGTH_SHORT).show() + screenModel.nextPage() + screenModel.update(screenModel.request.value) + //screenModel.nextPage() + } ) } } diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/sub/SearchBottomScreen.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/sub/SearchBottomScreen.kt index 39fc0f6..fcbce1b 100644 --- a/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/sub/SearchBottomScreen.kt +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/sub/SearchBottomScreen.kt @@ -3,7 +3,6 @@ package ru.megboyzz.lks.profile.ui.screens.sub import androidx.compose.runtime.Composable import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.screen.Screen -import cafe.adriel.voyager.hilt.getScreenModel import ru.megboyzz.lks.profile.ui.components.SearchContextMenu import ru.megboyzz.lks.profile.ui.model.SearchScreenModel