From 5f4c36f00ad4f70fab4b7ef3e0420b3a394d7a0e Mon Sep 17 00:00:00 2001 From: AnnZhimol Date: Thu, 23 Nov 2023 19:40:59 +0400 Subject: [PATCH] lab 4 complete(finally...) --- app/build.gradle.kts | 14 + .../pmulabs/designElem/items/ArticleItem.kt | 5 +- .../pmulabs/designElem/items/CommentItem.kt | 5 +- .../pmulabs/designElem/items/TagItem.kt | 5 +- .../example/pmulabs/graphs/AuthNavGraph.kt | 8 +- .../example/pmulabs/graphs/HomeNavGraph.kt | 21 +- .../example/pmulabs/graphs/RootNavGraph.kt | 13 +- .../example/pmulabs/room/dao/ArticleDao.kt | 3 + .../example/pmulabs/room/dao/CommentDao.kt | 3 + .../com/example/pmulabs/room/dao/TagDao.kt | 3 + .../room/pagingSource/ArticlePagingSource.kt | 38 ++ .../room/pagingSource/CommentPagingSource.kt | 38 ++ .../room/pagingSource/TagPagingSource.kt | 38 ++ .../room/repository/ArticleRepository.kt | 2 + .../room/repository/CommentRepository.kt | 2 + .../repository/OfflineArticleRepository.kt | 6 + .../repository/OfflineCommentRepository.kt | 6 + .../room/repository/OfflineTagRepository.kt | 6 + .../pmulabs/room/repository/TagRepository.kt | 2 + .../screensMobile/ArticlePageScreen.kt | 200 +++++---- .../pmulabs/screensMobile/MainScreen.kt | 65 +-- .../pmulabs/screensMobile/ProfileScreen.kt | 419 ++++++++++-------- .../pmulabs/screensMobile/TagsScreen.kt | 66 +-- .../screensMobile/authScreens/EntryScreen.kt | 29 +- .../authScreens/RegisterScreen.kt | 259 ++--------- .../filterScreens/CalendarScreen.kt | 74 ++-- .../filterScreens/SearchByTagScreen.kt | 57 ++- .../filterScreens/SearchScreen.kt | 56 ++- .../viewModels/AppViewModelProvider.kt | 12 + .../viewModels/ArticlePageScreenViewModel.kt | 74 ++++ .../viewModels/ArticleScreenViewModel.kt | 11 + .../viewModels/EntryScreenViewModel.kt | 21 + .../viewModels/RegisterScreenViewModel.kt | 26 ++ .../pmulabs/viewModels/TagItemViewModel.kt | 7 + 34 files changed, 936 insertions(+), 658 deletions(-) create mode 100644 app/src/main/java/com/example/pmulabs/room/pagingSource/ArticlePagingSource.kt create mode 100644 app/src/main/java/com/example/pmulabs/room/pagingSource/CommentPagingSource.kt create mode 100644 app/src/main/java/com/example/pmulabs/room/pagingSource/TagPagingSource.kt create mode 100644 app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt create mode 100644 app/src/main/java/com/example/pmulabs/viewModels/ArticleScreenViewModel.kt create mode 100644 app/src/main/java/com/example/pmulabs/viewModels/EntryScreenViewModel.kt create mode 100644 app/src/main/java/com/example/pmulabs/viewModels/RegisterScreenViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bd384f2..2ee0207 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,4 @@ + plugins { id("com.android.application") id("com.google.devtools.ksp") @@ -39,6 +40,7 @@ android { } buildFeatures { compose = true + viewBinding = true } composeOptions { kotlinCompilerExtensionVersion = "1.4.5" @@ -81,4 +83,16 @@ dependencies { ksp("androidx.room:room-compiler:$room_version") implementation("androidx.room:room-ktx:$room_version") implementation("androidx.room:room-paging:$room_version") + val paging_version = "3.1.1" + implementation ("androidx.paging:paging-runtime:$paging_version") +// alternatively - without Android dependencies for tests + testImplementation ("androidx.paging:paging-common:$paging_version") +// optional - RxJava2 support + implementation ("androidx.paging:paging-rxjava2:$paging_version") +// optional - RxJava3 support + implementation ("androidx.paging:paging-rxjava3:$paging_version") +// optional - Guava ListenableFuture support + implementation ("androidx.paging:paging-guava:$paging_version") +// optional - Jetpack Compose integration + implementation ("androidx.paging:paging-compose:1.0.0-alpha18") } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/designElem/items/ArticleItem.kt b/app/src/main/java/com/example/pmulabs/designElem/items/ArticleItem.kt index b329327..dfd6f66 100644 --- a/app/src/main/java/com/example/pmulabs/designElem/items/ArticleItem.kt +++ b/app/src/main/java/com/example/pmulabs/designElem/items/ArticleItem.kt @@ -60,7 +60,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.toSize import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController import com.example.pmulabs.R +import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.room.models.Article import com.example.pmulabs.room.models.Tag import com.example.pmulabs.viewModels.AppViewModelProvider @@ -72,7 +74,7 @@ import java.text.SimpleDateFormat @SuppressLint("UnrememberedMutableState", "CoroutineCreationDuringComposition") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ArticleItem(article: Article, modifier: Modifier = Modifier, onArticleClick: () -> Unit, articleItemViewModel: ArticleItemViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { +fun ArticleItem(navController: NavController,article: Article, modifier: Modifier = Modifier, onArticleClick: () -> Unit, articleItemViewModel: ArticleItemViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { val coroutineScope = rememberCoroutineScope() articleItemViewModel.setTagList() @@ -360,6 +362,7 @@ fun ArticleItem(article: Article, modifier: Modifier = Modifier, onArticleClick: coroutineScope.launch { articleItemViewModel.deleteArticle(article) } + navController.navigate(BottomBarScreen.Main.route) }, imageVector = Icons.Filled.Close, contentDescription = "Delete Icon", diff --git a/app/src/main/java/com/example/pmulabs/designElem/items/CommentItem.kt b/app/src/main/java/com/example/pmulabs/designElem/items/CommentItem.kt index 310d83b..b719db6 100644 --- a/app/src/main/java/com/example/pmulabs/designElem/items/CommentItem.kt +++ b/app/src/main/java/com/example/pmulabs/designElem/items/CommentItem.kt @@ -39,6 +39,8 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.User import com.example.pmulabs.viewModels.AppViewModelProvider @@ -49,7 +51,7 @@ import kotlinx.coroutines.launch @SuppressLint("CoroutineCreationDuringComposition", "UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun CommentItem(modifier: Modifier = Modifier, comm : Comment,commentItemViewModel: CommentItemViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { +fun CommentItem(navController: NavController,modifier: Modifier = Modifier, comm : Comment,commentItemViewModel: CommentItemViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { var getUser by remember { mutableStateOf(currentUserViewModel.user) } var openDialog by remember { mutableStateOf(false) } @@ -175,6 +177,7 @@ fun CommentItem(modifier: Modifier = Modifier, comm : Comment,commentItemViewMod coroutineScope.launch { commentItemViewModel.deleteComment(comm) } + navController.navigate(BottomBarScreen.ArticlePage.passId(comm.articleId.toString())) }, imageVector = Icons.Filled.Close, contentDescription = "Delete Icon", diff --git a/app/src/main/java/com/example/pmulabs/designElem/items/TagItem.kt b/app/src/main/java/com/example/pmulabs/designElem/items/TagItem.kt index 7c383ef..627f21a 100644 --- a/app/src/main/java/com/example/pmulabs/designElem/items/TagItem.kt +++ b/app/src/main/java/com/example/pmulabs/designElem/items/TagItem.kt @@ -41,7 +41,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController import com.example.pmulabs.R +import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.room.models.Tag import com.example.pmulabs.viewModels.AppViewModelProvider import com.example.pmulabs.viewModels.CurrentUserViewModel @@ -50,7 +52,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun TagItem(teg: Tag, modifier: Modifier = Modifier, onTagClick: () -> Unit, viewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), tagViewModel: TagItemViewModel =viewModel(factory = AppViewModelProvider.Factory)) { +fun TagItem(navController: NavController,teg: Tag, modifier: Modifier = Modifier, onTagClick: () -> Unit, viewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), tagViewModel: TagItemViewModel =viewModel(factory = AppViewModelProvider.Factory)) { var getUser by remember { mutableStateOf(viewModel.user) } var openDialog by remember { mutableStateOf(false) } @@ -197,6 +199,7 @@ fun TagItem(teg: Tag, modifier: Modifier = Modifier, onTagClick: () -> Unit, vie coroutineScope.launch { tagViewModel.deleteTag(teg) } + navController.navigate(BottomBarScreen.Categories.route) }, imageVector = Icons.Filled.Close, contentDescription = "Delete Icon", diff --git a/app/src/main/java/com/example/pmulabs/graphs/AuthNavGraph.kt b/app/src/main/java/com/example/pmulabs/graphs/AuthNavGraph.kt index a2d3bf2..0843b50 100644 --- a/app/src/main/java/com/example/pmulabs/graphs/AuthNavGraph.kt +++ b/app/src/main/java/com/example/pmulabs/graphs/AuthNavGraph.kt @@ -9,8 +9,10 @@ import com.example.pmulabs.screensMobile.authScreens.EntryScreen import com.example.pmulabs.screensMobile.authScreens.RegisterScreen import com.example.pmulabs.screensMobile.authScreens.SplashScreen import com.example.pmulabs.viewModels.CurrentUserViewModel +import com.example.pmulabs.viewModels.EntryScreenViewModel +import com.example.pmulabs.viewModels.RegisterScreenViewModel -fun NavGraphBuilder.authNavGraph(navController: NavHostController, currentUserViewModel: CurrentUserViewModel){ +fun NavGraphBuilder.authNavGraph(navController: NavHostController,registerScreenViewModel: RegisterScreenViewModel,entryScreenViewModel: EntryScreenViewModel, currentUserViewModel: CurrentUserViewModel){ navigation( route=Graph.AUTHENTICATION, startDestination = AuthScreen.Splash.route @@ -19,10 +21,10 @@ fun NavGraphBuilder.authNavGraph(navController: NavHostController, currentUserVi SplashScreen(navController = navController, Modifier) } composable(route=AuthScreen.Entry.route){ - EntryScreen(navController = navController, Modifier,currentUserViewModel) + EntryScreen(navController = navController, Modifier,entryScreenViewModel,currentUserViewModel) } composable(route=AuthScreen.Register.route){ - RegisterScreen(navController = navController, Modifier,currentUserViewModel) + RegisterScreen(navController = navController, Modifier,registerScreenViewModel,currentUserViewModel) } } } diff --git a/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt b/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt index e5375aa..1768ada 100644 --- a/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt +++ b/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt @@ -13,7 +13,6 @@ import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.basecomponents.navigate.CALENDAR_ARGUMENT_DATE import com.example.pmulabs.basecomponents.navigate.SEARCHBYTAG_ARGUMENT_KEY import com.example.pmulabs.basecomponents.navigate.SEARCH_ARGUMENT_TEXT -import com.example.pmulabs.viewModels.CurrentUserViewModel import com.example.pmulabs.screensMobile.ArticlePageScreen import com.example.pmulabs.screensMobile.CoopScreen import com.example.pmulabs.screensMobile.InfoScreen @@ -24,9 +23,13 @@ import com.example.pmulabs.screensMobile.filterScreens.CalendarScreen import com.example.pmulabs.screensMobile.filterScreens.SearchByTagScreen import com.example.pmulabs.screensMobile.filterScreens.SearchScreen import com.example.pmulabs.viewModels.AppViewModelProvider +import com.example.pmulabs.viewModels.ArticlePageScreenViewModel +import com.example.pmulabs.viewModels.ArticleScreenViewModel +import com.example.pmulabs.viewModels.CurrentUserViewModel +import com.example.pmulabs.viewModels.TagItemViewModel @Composable -fun HomeNavGraph(navController: NavHostController, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ +fun HomeNavGraph(navController: NavHostController,articlePageScreenViewModel: ArticlePageScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), tagItemViewModel: TagItemViewModel = viewModel(factory = AppViewModelProvider.Factory), articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ NavHost( navController = navController, route = Graph.MAIN, @@ -35,7 +38,7 @@ fun HomeNavGraph(navController: NavHostController, currentUserViewModel: Current composable( route=BottomBarScreen.Main.route ){ - MainScreen(navController,Modifier,currentUserViewModel) + MainScreen(navController,Modifier,articleScreenViewModel,currentUserViewModel) } composable( route=BottomBarScreen.SearchByTag.route, @@ -43,7 +46,7 @@ fun HomeNavGraph(navController: NavHostController, currentUserViewModel: Current type= NavType.StringType }) ){ - SearchByTagScreen(navController,Modifier,currentUserViewModel) + SearchByTagScreen(navController,Modifier,articleScreenViewModel,currentUserViewModel) } composable( route=BottomBarScreen.ArticlePage.route, @@ -51,7 +54,7 @@ fun HomeNavGraph(navController: NavHostController, currentUserViewModel: Current type= NavType.StringType }) ){ - ArticlePageScreen(navController,Modifier,currentUserViewModel) + ArticlePageScreen(navController,Modifier,articlePageScreenViewModel,currentUserViewModel) } composable( route=BottomBarScreen.Search.route, @@ -59,7 +62,7 @@ fun HomeNavGraph(navController: NavHostController, currentUserViewModel: Current type= NavType.StringType }) ){ - SearchScreen(navController,Modifier,currentUserViewModel) + SearchScreen(navController,Modifier,articleScreenViewModel,currentUserViewModel) } composable( route=BottomBarScreen.Calendar.route, @@ -67,10 +70,10 @@ fun HomeNavGraph(navController: NavHostController, currentUserViewModel: Current type= NavType.StringType }) ){ - CalendarScreen(navController,Modifier,currentUserViewModel) + CalendarScreen(navController,articleScreenViewModel,Modifier,currentUserViewModel) } composable(route=BottomBarScreen.Profile.route){ - ProfileScreen(navController,Modifier,currentUserViewModel) + ProfileScreen(navController,Modifier,articleScreenViewModel, tagItemViewModel, articlePageScreenViewModel,currentUserViewModel) } composable(route=BottomBarScreen.Info.route){ InfoScreen(navController,Modifier) @@ -79,7 +82,7 @@ fun HomeNavGraph(navController: NavHostController, currentUserViewModel: Current CoopScreen(navController,Modifier) } composable(route=BottomBarScreen.Categories.route){ - TagsScreen(navController,Modifier,currentUserViewModel) + TagsScreen(navController,Modifier, tagItemViewModel, currentUserViewModel) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt b/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt index bc79c50..29d6625 100644 --- a/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt +++ b/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt @@ -7,26 +7,31 @@ import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument -import com.example.pmulabs.viewModels.CurrentUserViewModel import com.example.pmulabs.screensMobile.LoadScreen import com.example.pmulabs.viewModels.AppViewModelProvider +import com.example.pmulabs.viewModels.ArticlePageScreenViewModel +import com.example.pmulabs.viewModels.ArticleScreenViewModel +import com.example.pmulabs.viewModels.CurrentUserViewModel +import com.example.pmulabs.viewModels.EntryScreenViewModel +import com.example.pmulabs.viewModels.RegisterScreenViewModel import com.example.pmulabs.viewModels.SearchViewModel +import com.example.pmulabs.viewModels.TagItemViewModel const val USERID_ARGUMENT="userId" @Composable -fun RootNavigationGraph(navController: NavHostController, searchViewModel: SearchViewModel, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ +fun RootNavigationGraph(navController: NavHostController, searchViewModel: SearchViewModel,articlePageScreenViewModel: ArticlePageScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), tagItemViewModel: TagItemViewModel = viewModel(factory = AppViewModelProvider.Factory), articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), registerScreenViewModel: RegisterScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), entryScreenViewModel: EntryScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ NavHost( navController=navController, route = Graph.ROOT, startDestination = Graph.AUTHENTICATION ){ - authNavGraph(navController=navController,currentUserViewModel) + authNavGraph(navController=navController,registerScreenViewModel,entryScreenViewModel,currentUserViewModel) composable(route=Graph.MAIN, arguments = listOf(navArgument(USERID_ARGUMENT){ type= NavType.StringType })){ - LoadScreen(searchViewModel = searchViewModel, currentUserViewModel = currentUserViewModel) + LoadScreen(searchViewModel = searchViewModel, articleScreenViewModel = articleScreenViewModel, articlePageScreenViewModel = articlePageScreenViewModel, tagItemViewModel = tagItemViewModel,currentUserViewModel = currentUserViewModel) } } } diff --git a/app/src/main/java/com/example/pmulabs/room/dao/ArticleDao.kt b/app/src/main/java/com/example/pmulabs/room/dao/ArticleDao.kt index cb4e423..962c36c 100644 --- a/app/src/main/java/com/example/pmulabs/room/dao/ArticleDao.kt +++ b/app/src/main/java/com/example/pmulabs/room/dao/ArticleDao.kt @@ -16,6 +16,9 @@ interface ArticleDao { @Query("select * from article where article.id = :idArticle") fun getArticleById(idArticle: Int): Flow
+ @Query("SELECT * FROM article ORDER BY id DESC LIMIT :limit OFFSET :offset") + suspend fun getArticles(limit: Int, offset: Int): List
+ @Insert suspend fun insert(article: Article) diff --git a/app/src/main/java/com/example/pmulabs/room/dao/CommentDao.kt b/app/src/main/java/com/example/pmulabs/room/dao/CommentDao.kt index cec1325..90f2d5a 100644 --- a/app/src/main/java/com/example/pmulabs/room/dao/CommentDao.kt +++ b/app/src/main/java/com/example/pmulabs/room/dao/CommentDao.kt @@ -16,6 +16,9 @@ interface CommentDao { @Query("select COUNT(*) from comment WHERE comment.text!='' AND comment.article_id= :idArticle") fun getCountComment(idArticle : Int?) : Int + @Query("SELECT * FROM comment ORDER BY id DESC LIMIT :limit OFFSET :offset") + suspend fun getComments(limit: Int, offset: Int): List + @Insert suspend fun insert(comment: Comment) diff --git a/app/src/main/java/com/example/pmulabs/room/dao/TagDao.kt b/app/src/main/java/com/example/pmulabs/room/dao/TagDao.kt index a80a8ac..39aeb88 100644 --- a/app/src/main/java/com/example/pmulabs/room/dao/TagDao.kt +++ b/app/src/main/java/com/example/pmulabs/room/dao/TagDao.kt @@ -19,6 +19,9 @@ interface TagDao { @Query("select * from tag where tag.title = :nameTag") fun getTagByName(nameTag: String): Flow + @Query("SELECT * FROM tag ORDER BY id ASC LIMIT :limit OFFSET :offset") + suspend fun getTags(limit: Int, offset: Int): List + @Insert suspend fun insert(tag: Tag) diff --git a/app/src/main/java/com/example/pmulabs/room/pagingSource/ArticlePagingSource.kt b/app/src/main/java/com/example/pmulabs/room/pagingSource/ArticlePagingSource.kt new file mode 100644 index 0000000..d1ad4fa --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/room/pagingSource/ArticlePagingSource.kt @@ -0,0 +1,38 @@ +package com.example.pmulabs.room.pagingSource + +import android.util.Log +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.example.pmulabs.room.dao.ArticleDao +import com.example.pmulabs.room.models.Article +import kotlinx.coroutines.delay + +class ArticlePagingSource( + private val dao: ArticleDao, +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + val page = params.key ?: 0 + + return try { + Log.d("MainPagingSource", "load: $page") + val entities = dao.getArticles(params.loadSize, page * params.loadSize) + if (page != 0) delay(1000) + LoadResult.Page( + data = entities, + prevKey = if (page == 0) null else page - 1, + nextKey = if (entities.isEmpty()) null else page + 1 + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } + + override val jumpingSupported: Boolean = true + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/pagingSource/CommentPagingSource.kt b/app/src/main/java/com/example/pmulabs/room/pagingSource/CommentPagingSource.kt new file mode 100644 index 0000000..d32dadd --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/room/pagingSource/CommentPagingSource.kt @@ -0,0 +1,38 @@ +package com.example.pmulabs.room.pagingSource + +import android.util.Log +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.example.pmulabs.room.dao.CommentDao +import com.example.pmulabs.room.models.Comment +import kotlinx.coroutines.delay + +class CommentPagingSource( + private val dao: CommentDao, +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + val page = params.key ?: 0 + + return try { + Log.d("MainPagingSource", "load: $page") + val entities = dao.getComments(params.loadSize, page * params.loadSize) + if (page != 0) delay(1000) + LoadResult.Page( + data = entities, + prevKey = if (page == 0) null else page - 1, + nextKey = if (entities.isEmpty()) null else page + 1 + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } + + override val jumpingSupported: Boolean = true + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/pagingSource/TagPagingSource.kt b/app/src/main/java/com/example/pmulabs/room/pagingSource/TagPagingSource.kt new file mode 100644 index 0000000..d50e430 --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/room/pagingSource/TagPagingSource.kt @@ -0,0 +1,38 @@ +package com.example.pmulabs.room.pagingSource + +import android.util.Log +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.example.pmulabs.room.dao.TagDao +import com.example.pmulabs.room.models.Tag +import kotlinx.coroutines.delay + +class TagPagingSource( + private val dao: TagDao, +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + val page = params.key ?: 0 + + return try { + Log.d("MainPagingSource", "load: $page") + val entities = dao.getTags(params.loadSize, page * params.loadSize) + if (page != 0) delay(1000) + LoadResult.Page( + data = entities, + prevKey = if (page == 0) null else page - 1, + nextKey = if (entities.isEmpty()) null else page + 1 + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } + + override val jumpingSupported: Boolean = true + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/repository/ArticleRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/ArticleRepository.kt index 069b149..18d6258 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/ArticleRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/ArticleRepository.kt @@ -1,5 +1,6 @@ package com.example.pmulabs.room.repository +import androidx.paging.PagingData import com.example.pmulabs.room.models.Article import kotlinx.coroutines.flow.Flow @@ -9,4 +10,5 @@ interface ArticleRepository { suspend fun deleteArticle(article: Article) fun getAllArticles(): Flow> fun getArticleById(idArticle: Int): Flow
+ fun getArticles(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/repository/CommentRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/CommentRepository.kt index 1d64968..ee74477 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/CommentRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/CommentRepository.kt @@ -1,5 +1,6 @@ package com.example.pmulabs.room.repository +import androidx.paging.PagingData import com.example.pmulabs.room.models.Comment import kotlinx.coroutines.flow.Flow @@ -9,4 +10,5 @@ interface CommentRepository { suspend fun deleteComment(comment: Comment) fun getAllComments(): Flow> fun getCountComment(idArticle : Int?) : Int + fun getComments(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/repository/OfflineArticleRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/OfflineArticleRepository.kt index f31837b..e37cc26 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/OfflineArticleRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/OfflineArticleRepository.kt @@ -1,7 +1,11 @@ package com.example.pmulabs.room.repository +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import com.example.pmulabs.room.dao.ArticleDao import com.example.pmulabs.room.models.Article +import com.example.pmulabs.room.pagingSource.ArticlePagingSource import kotlinx.coroutines.flow.Flow class OfflineArticleRepository(private val articleDao: ArticleDao) : ArticleRepository { @@ -10,4 +14,6 @@ class OfflineArticleRepository(private val articleDao: ArticleDao) : ArticleRepo override suspend fun deleteArticle(article: Article) = articleDao.delete(article) override fun getAllArticles(): Flow> = articleDao.getAll() override fun getArticleById(idArticle: Int): Flow
= articleDao.getArticleById(idArticle) + override fun getArticles(): Flow> = Pager(config = PagingConfig(pageSize = 4, jumpThreshold = 4, initialLoadSize = 4) ){ ArticlePagingSource(articleDao) }.flow + } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/repository/OfflineCommentRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/OfflineCommentRepository.kt index 0da976c..c3cd1d7 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/OfflineCommentRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/OfflineCommentRepository.kt @@ -1,7 +1,11 @@ package com.example.pmulabs.room.repository +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import com.example.pmulabs.room.dao.CommentDao import com.example.pmulabs.room.models.Comment +import com.example.pmulabs.room.pagingSource.CommentPagingSource import kotlinx.coroutines.flow.Flow class OfflineCommentRepository(private val commentDao: CommentDao) : CommentRepository { @@ -10,4 +14,6 @@ class OfflineCommentRepository(private val commentDao: CommentDao) : CommentRepo override suspend fun deleteComment(comment: Comment) = commentDao.delete(comment) override fun getAllComments(): Flow> = commentDao.getAll() override fun getCountComment(idArticle : Int?) : Int = commentDao.getCountComment(idArticle) + override fun getComments(): Flow> = Pager(config = PagingConfig(pageSize = 4, jumpThreshold = 4, initialLoadSize = 4) ){ CommentPagingSource(commentDao) }.flow + } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/repository/OfflineTagRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/OfflineTagRepository.kt index c8dbc12..16d73a4 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/OfflineTagRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/OfflineTagRepository.kt @@ -1,7 +1,11 @@ package com.example.pmulabs.room.repository +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import com.example.pmulabs.room.dao.TagDao import com.example.pmulabs.room.models.Tag +import com.example.pmulabs.room.pagingSource.TagPagingSource import kotlinx.coroutines.flow.Flow class OfflineTagRepository(private val tagDao: TagDao) : TagRepository { @@ -11,4 +15,6 @@ class OfflineTagRepository(private val tagDao: TagDao) : TagRepository { override fun getAllTags(): Flow> = tagDao.getAll() override fun getTagById(idTag: Int): Flow = tagDao.getTagById(idTag) override fun getTagByName(nameTag: String): Flow = tagDao.getTagByName(nameTag) + override fun getTags(): Flow> = Pager(config = PagingConfig(pageSize = 4, jumpThreshold = 4, initialLoadSize = 4) ){ TagPagingSource(tagDao) }.flow + } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/repository/TagRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/TagRepository.kt index a98af68..762d470 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/TagRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/TagRepository.kt @@ -1,5 +1,6 @@ package com.example.pmulabs.room.repository +import androidx.paging.PagingData import com.example.pmulabs.room.models.Tag import kotlinx.coroutines.flow.Flow @@ -10,4 +11,5 @@ interface TagRepository { fun getAllTags(): Flow> fun getTagById(idTag: Int): Flow fun getTagByName(nameTag: String): Flow + fun getTags(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/ArticlePageScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/ArticlePageScreen.kt index 7ac2833..070c0b3 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/ArticlePageScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/ArticlePageScreen.kt @@ -1,5 +1,7 @@ package com.example.pmulabs.screensMobile +import android.annotation.SuppressLint +import android.util.Log import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -20,6 +22,7 @@ import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme @@ -27,102 +30,83 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.example.pmulabs.basecomponents.navigate.ARTICLE_ARGUMENT_KEY +import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.designElem.elem.BackButton import com.example.pmulabs.designElem.items.CommentItem -import com.example.pmulabs.room.database.NewsPortalDatabase import com.example.pmulabs.room.models.Article import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Tag -import com.example.pmulabs.room.models.User import com.example.pmulabs.viewModels.AppViewModelProvider +import com.example.pmulabs.viewModels.ArticlePageScreenViewModel import com.example.pmulabs.viewModels.CurrentUserViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.text.SimpleDateFormat import java.util.Date +@SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { - val argument = currentUserViewModel.argument.value - val context = LocalContext.current +fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifier,articlePageScreenViewModel: ArticlePageScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { - var id=navController.currentBackStackEntry?.arguments?.getString(ARTICLE_ARGUMENT_KEY).toString() - var textComm by rememberSaveable { mutableStateOf("") } - var article by remember { mutableStateOf(null) } - var tag by remember { mutableStateOf(null) } - var tagId = 0 - if (id.toString() != "null") { - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).articleDao().getArticleById(id.toInt()).collect { data -> - article=data - tagId=data.tagId - NewsPortalDatabase.getInstance(context).tagDao().getTagById(tagId).collect { data -> - tag=data - } - } - } - } + var id = + navController.currentBackStackEntry?.arguments?.getString(ARTICLE_ARGUMENT_KEY).toString() + try { + articlePageScreenViewModel.getArticleById(id.toInt()) + } catch (e: NumberFormatException) { + e.printStackTrace() } + var textComm by rememberSaveable { mutableStateOf("") } + var article by mutableStateOf(articlePageScreenViewModel.article) + + try { + article?.tagId?.let { articlePageScreenViewModel.getTagById(it) } + } catch (e: Exception) { + e.printStackTrace() + } + var tag by mutableStateOf(articlePageScreenViewModel.tag) + Log.d("Tag:", tag.toString()) + var tagId = articlePageScreenViewModel.tag?.id val formatter = SimpleDateFormat("dd.MM.YY") - val publishDate=formatter.format(Date(article?.publishDate ?: 0)) + val publishDate = formatter.format(Date(article?.publishDate ?: 0)) - val comms = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).commentDao().getAll().collect { data -> - comms.clear() - comms.addAll(data) - } - } - } + val comms = articlePageScreenViewModel.comments.collectAsLazyPagingItems() - var getUser by remember { mutableStateOf(null) } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).userDao() - .getUserById(argument.toString().toInt()).collect { data -> - getUser=data - } - } - } + var getUser by remember { mutableStateOf(currentUserViewModel.user) } + val coroutineScope = rememberCoroutineScope() LazyColumn( - contentPadding= PaddingValues(top=105.dp, bottom = 50.dp,start=15.dp,end=15.dp), + contentPadding = PaddingValues(top = 105.dp, bottom = 50.dp, start = 15.dp, end = 15.dp), modifier = modifier .requiredWidth(width = 412.dp) .requiredHeight(height = 915.dp) .background(color = Color.White) .fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(10.dp) + verticalArrangement = Arrangement.spacedBy(1.dp) ) { - item{ - BackButton(modifier=Modifier - .clickable { navController.popBackStack()} + item { + BackButton(modifier = Modifier + .clickable { navController.navigate(BottomBarScreen.Main.route) } .offset( - y=0.dp + y = 0.dp )) } item { @@ -131,20 +115,24 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie .requiredWidth(width = 350.dp) .requiredHeight(height = 33.dp) .offset( - y=0.dp + y = 0.dp ) ) { Text( text = "${publishDate}", color = Color(0xff423a99), - style = MaterialTheme.typography.headlineMedium) + style = MaterialTheme.typography.headlineMedium + ) Text( text = "#${tag?.title}", color = Color(0xff423a99), style = MaterialTheme.typography.headlineMedium, modifier = Modifier - .offset(x = 20.dp, - y = 0.dp)) + .offset( + x = 20.dp, + y = 0.dp + ) + ) } } item { @@ -153,11 +141,15 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie color = Color(0xff423a99), style = TextStyle( fontSize = 32.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier - .offset(x = 10.dp, - y = 0.dp) - .requiredWidth(width = 395.dp)) + .offset( + x = 10.dp, + y = 0.dp + ) + .requiredWidth(width = 395.dp) + ) } item { @@ -166,9 +158,12 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie color = Color(0xff423a99), style = MaterialTheme.typography.headlineMedium, modifier = Modifier - .offset(x = 5.dp, - y = 0.dp) - .requiredWidth(width = 375.dp)) + .offset( + x = 5.dp, + y = 0.dp + ) + .requiredWidth(width = 375.dp) + ) } item { Box( @@ -176,7 +171,7 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie .requiredWidth(width = 412.dp) .requiredHeight(height = 53.dp) .offset( - y=0.dp + y = 0.dp ) ) { Text( @@ -184,33 +179,38 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie color = Color(0xff423a99), style = TextStyle( fontSize = 32.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .align(alignment = Alignment.TopStart) - .offset(x = 15.dp, - y = 6.dp) + .offset( + x = 15.dp, + y = 6.dp + ) .requiredWidth(width = 395.dp) - .requiredHeight(height = 43.dp)) + .requiredHeight(height = 43.dp) + ) HorizontalDivider( - thickness=5.dp, + thickness = 5.dp, modifier = Modifier .border(BorderStroke(3.dp, Color(0xff423a99))), color = Color(0xff423a99) ) HorizontalDivider( - thickness=5.dp, + thickness = 5.dp, modifier = Modifier .align(alignment = Alignment.TopStart) .border(BorderStroke(3.dp, Color(0xff423a99))) .offset( - y = 53.dp), color = Color(0xff423a99) + y = 53.dp + ), color = Color(0xff423a99) ) } } - item{ + item { OutlinedTextField( value = textComm, onValueChange = { - textComm=it + textComm = it }, colors = TextFieldDefaults.textFieldColors( containerColor = Color.White, @@ -236,13 +236,14 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie disabledContentColor = Color.White ), onClick = { - if(textComm.isNotEmpty()) { + if (textComm.isNotEmpty()) { var newComment = Comment(null, textComm, getUser?.id.toString().toInt(), id.toInt()) - CoroutineScope(Dispatchers.IO).launch { - NewsPortalDatabase.getInstance(context).commentDao().insert(newComment) + coroutineScope.launch { + articlePageScreenViewModel.insertComment(newComment) } textComm = "" + navController.navigate(BottomBarScreen.ArticlePage.passId(article?.id.toString())) } }, modifier = Modifier @@ -253,19 +254,52 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie Text(text = "Send", fontSize = 20.sp) } } - item{ - comms.forEach{ comm -> - if(comm.articleId==article?.id && comm.text!="") { + + items(count = comms.itemCount) { index -> + val comm = comms[index] + if (comm != null) { + if (comm.articleId == article?.id && comm.text != "") { Spacer(modifier = Modifier.padding(0.dp)) HorizontalDivider( - thickness=3.dp, + thickness = 3.dp, modifier = Modifier .border(BorderStroke(3.dp, Color(0xff423a99))), color = Color(0xff423a99) ) - CommentItem(comm = comm, currentUserViewModel = currentUserViewModel) + CommentItem(navController,comm = comm, currentUserViewModel = currentUserViewModel) } } - } + comms.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { + CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(), + color = Color(0xff423a99) + ) + } + } + + loadState.append is LoadState.Loading -> { + item { + CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(), + color = Color(0xff423a99) + ) + } + } + + loadState.refresh is LoadState.Error -> { + val err = comms.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + + loadState.append is LoadState.Error -> { + val err = comms.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } + } + } + } } diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt index b24bf7e..e964675 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt @@ -8,9 +8,9 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ButtonColors +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DatePicker import androidx.compose.material3.DatePickerColors import androidx.compose.material3.DatePickerDialog @@ -21,61 +21,70 @@ import androidx.compose.material3.TextButton import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.example.pmulabs.basecomponents.navigate.BottomBar import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.basecomponents.navigate.SearchAppBar import com.example.pmulabs.basecomponents.navigate.TopBar import com.example.pmulabs.designElem.items.ArticleItem -import com.example.pmulabs.viewModels.CurrentUserViewModel import com.example.pmulabs.graphs.HomeNavGraph -import com.example.pmulabs.room.database.NewsPortalDatabase -import com.example.pmulabs.room.models.Article import com.example.pmulabs.viewModels.AppViewModelProvider +import com.example.pmulabs.viewModels.ArticlePageScreenViewModel +import com.example.pmulabs.viewModels.ArticleScreenViewModel +import com.example.pmulabs.viewModels.CurrentUserViewModel import com.example.pmulabs.viewModels.SearchViewModel import com.example.pmulabs.viewModels.SearchWidget -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import com.example.pmulabs.viewModels.TagItemViewModel import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date @Composable -fun MainScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ - - val context = LocalContext.current - val articles = remember { mutableStateListOf
() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).articleDao().getAll().collect { data -> - articles.clear() - articles.addAll(data) - } - } - } +fun MainScreen(navController: NavController, modifier: Modifier = Modifier, articleScreenViewModel: ArticleScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ + val articles = articleScreenViewModel.articles.collectAsLazyPagingItems() LazyColumn( modifier=Modifier .background(Color.White) .fillMaxSize(), contentPadding = PaddingValues(top=75.dp, bottom = 70.dp, start = 10.dp,end=10.dp), verticalArrangement = Arrangement.spacedBy(15.dp)){ - items(items = articles){ article -> - ArticleItem(article = article, currentUserViewModel = currentUserViewModel, onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))}) + items(count = articles.itemCount) { index -> + val article = articles[index] + if (article != null) { + ArticleItem(navController=navController,article = article, currentUserViewModel = currentUserViewModel, onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))}) + } + } + articles.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.append is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.refresh is LoadState.Error -> { + val err = articles.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + loadState.append is LoadState.Error -> { + val err = articles.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } + } } } } @@ -83,14 +92,14 @@ fun MainScreen(navController: NavController, modifier: Modifier = Modifier, curr @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LoadScreen(navController: NavHostController=rememberNavController(), searchViewModel: SearchViewModel, currentUserViewModel: CurrentUserViewModel){ +fun LoadScreen(navController: NavHostController=rememberNavController(),articlePageScreenViewModel: ArticlePageScreenViewModel, tagItemViewModel: TagItemViewModel, articleScreenViewModel: ArticleScreenViewModel, searchViewModel: SearchViewModel, currentUserViewModel: CurrentUserViewModel){ val searchWidgetState by searchViewModel.searchWidgetState val searchTextState by searchViewModel.searchTextState val formatter = SimpleDateFormat("dd-MMMM-YY") val calendar = Calendar.getInstance() - calendar.set(2023, 0, 1) // add year, month (Jan), date + calendar.set(2023,10,23) // set the initial date val datePickerState = rememberDatePickerState(initialSelectedDateMillis = calendar.timeInMillis) @@ -133,7 +142,7 @@ fun LoadScreen(navController: NavHostController=rememberNavController(), searchV selectedYearContentColor=Color.White, subheadContentColor=Color(0xff423a99), titleContentColor=Color(0xff423a99), - todayContentColor=Color.White, + todayContentColor=Color(0xff423a99), todayDateBorderColor=Color(0xff423a99), weekdayContentColor=Color(0xff423a99), yearContentColor=Color(0xff423a99) @@ -212,7 +221,7 @@ fun LoadScreen(navController: NavHostController=rememberNavController(), searchV selectedYearContentColor=Color.White, subheadContentColor=Color(0xff423a99), titleContentColor=Color(0xff423a99), - todayContentColor=Color.White, + todayContentColor=Color(0xff423a99), todayDateBorderColor=Color(0xff423a99), weekdayContentColor=Color(0xff423a99), yearContentColor=Color(0xff423a99) @@ -246,7 +255,7 @@ fun LoadScreen(navController: NavHostController=rememberNavController(), searchV ) { Modifier .padding(it) - HomeNavGraph(navController = navController,currentUserViewModel) + HomeNavGraph(navController = navController,articlePageScreenViewModel, tagItemViewModel,articleScreenViewModel,currentUserViewModel) } } diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt index dd02f90..0f2ca93 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt @@ -1,5 +1,6 @@ package com.example.pmulabs.screensMobile +import android.annotation.SuppressLint import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -21,7 +22,6 @@ import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -30,6 +30,7 @@ import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api @@ -39,11 +40,11 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -62,80 +63,45 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.toSize import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.example.pmulabs.R import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.designElem.elem.ValidateEmail import com.example.pmulabs.designElem.elem.isValidEmail -import com.example.pmulabs.room.database.NewsPortalDatabase import com.example.pmulabs.room.models.Article -import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Tag import com.example.pmulabs.viewModels.AppViewModelProvider +import com.example.pmulabs.viewModels.ArticlePageScreenViewModel +import com.example.pmulabs.viewModels.ArticleScreenViewModel import com.example.pmulabs.viewModels.CurrentUserViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import com.example.pmulabs.viewModels.TagItemViewModel import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.util.Date +@SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { +fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory),tagItemViewModel: TagItemViewModel= viewModel(factory = AppViewModelProvider.Factory),articlePageScreenViewModel: ArticlePageScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { val context = LocalContext.current var getUser by remember { mutableStateOf(currentUserViewModel.user) } var openDialogUser by remember { mutableStateOf(false) } var email by remember { mutableStateOf(getUser?.email) } var password by remember { mutableStateOf(getUser?.password) } var nickname by remember { mutableStateOf(getUser?.nickname) } + val getArticles = articleScreenViewModel.articles.collectAsLazyPagingItems() + val getComms = articlePageScreenViewModel.comments.collectAsLazyPagingItems() + val getTags = tagItemViewModel.tags.collectAsLazyPagingItems() + val coroutineScope = rememberCoroutineScope() + articlePageScreenViewModel.setTagList() - val getComms = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).userDao() - .getUserComms(getUser?.id.toString().toInt()).collect { data -> - getComms.clear() - getComms.addAll(data) - } - } - } + val tags = mutableStateOf>(articlePageScreenViewModel.tagList) - val getArticles = remember { mutableStateListOf
() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).userDao() - .getUserArticles(getUser?.id.toString().toInt()).collect { data -> - getArticles.clear() - getArticles.addAll(data) - } - } - } - - val getTags = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).userDao() - .getUserTags(getUser?.id.toString().toInt()).collect { data -> - getTags.clear() - getTags.addAll(data) - } - } - } - - //список тэгов - val tags = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).tagDao().getAll().collect { data -> - tags.clear() - tags.addAll(data) - } - } - } //список названий тэгов - var tagsNames= remember { mutableStateListOf() } - tags.forEach{teg -> - if(!tagsNames.contains(teg.title)) { + var tagsNames = remember { mutableStateListOf() } + tags.value.forEach { teg -> + if (!tagsNames.contains(teg.title)) { tagsNames.add(teg.title) } } @@ -149,7 +115,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c var expanded by remember { mutableStateOf(false) } val suggestions = tagsNames var selectedText by remember { mutableStateOf("") } - var textfieldSize by remember { mutableStateOf(Size.Zero)} + var textfieldSize by remember { mutableStateOf(Size.Zero) } val icon = if (expanded) Icons.Filled.ArrowDropUp else @@ -159,8 +125,8 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c AlertDialog( onDismissRequest = { openDialog = false - text="" - title="" + text = "" + title = "" }, content = { Column( @@ -186,7 +152,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .onGloballyPositioned { coordinates -> textfieldSize = coordinates.size.toSize() }, - label = {Text("Select Tag")}, + label = { Text("Select Tag") }, colors = TextFieldDefaults.textFieldColors( containerColor = Color.White, focusedIndicatorColor = Color(0xff423a99), @@ -195,7 +161,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c unfocusedLabelColor = Color(0xff423a99) ), trailingIcon = { - Icon(icon,"List of tags", + Icon(icon, "List of tags", Modifier.clickable { expanded = !expanded }) } ) @@ -203,17 +169,18 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c expanded = expanded, onDismissRequest = { expanded = false }, modifier = Modifier - .width(with(LocalDensity.current){textfieldSize.width.toDp()}) + .width(with(LocalDensity.current) { textfieldSize.width.toDp() }) ) { suggestions.forEach { label -> DropdownMenuItem( - text= {Text(text=label)}, - onClick = { selectedText = label - CoroutineScope(Dispatchers.IO).launch { - NewsPortalDatabase.getInstance(context).tagDao().getTagByName(selectedText).collect{ tag -> - tagId=tag?.id.toString().toInt() - } - }} + text = { Text(text = label) }, + onClick = { + selectedText = label + coroutineScope.launch { + tagId = + articlePageScreenViewModel.getTagByName(selectedText).id!! + } + } ) } } @@ -221,7 +188,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c OutlinedTextField( value = title, onValueChange = { - title=it + title = it }, colors = TextFieldDefaults.textFieldColors( containerColor = Color.White, @@ -242,7 +209,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c OutlinedTextField( value = text, onValueChange = { - text=it + text = it }, colors = TextFieldDefaults.textFieldColors( containerColor = Color.White, @@ -273,14 +240,13 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ), onClick = { openDialog = false - if(title.isNotEmpty() && text.isNotEmpty() && tagId!=null) { + if (title.isNotEmpty() && text.isNotEmpty() && tagId != null) { val newArticle = Article( null, title, text, Date().time, getUser?.id.toString().toInt(), tagId ) - CoroutineScope(Dispatchers.IO).launch { - NewsPortalDatabase.getInstance(context).articleDao() - .insert(newArticle) + coroutineScope.launch { + articlePageScreenViewModel.insertArticle(newArticle) } } }, @@ -301,9 +267,9 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c AlertDialog( onDismissRequest = { openDialogUser = false - email=getUser?.email.toString() - password=getUser?.password.toString() - nickname=getUser?.nickname.toString() + email = getUser?.email.toString() + password = getUser?.password.toString() + nickname = getUser?.nickname.toString() }, content = { Column( @@ -319,12 +285,12 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c fontSize = 20.sp, ), ) - email?.let { ValidateEmail(it, {email=it} ) } + email?.let { ValidateEmail(it, { email = it }) } password?.let { OutlinedTextField( value = it, onValueChange = { - password=it + password = it }, colors = TextFieldDefaults.textFieldColors( containerColor = Color.White, @@ -347,7 +313,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c OutlinedTextField( value = it, onValueChange = { - nickname=it + nickname = it }, colors = TextFieldDefaults.textFieldColors( containerColor = Color.White, @@ -379,17 +345,16 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ), onClick = { openDialogUser = false - if(password?.isNotEmpty() == true && email?.isNotEmpty() == true && isValidEmail( + if (password?.isNotEmpty() == true && email?.isNotEmpty() == true && isValidEmail( email!! ) && nickname?.isNotEmpty() == true ) { - getUser?.password= password as String - getUser?.email= email as String - getUser?.nickname= nickname as String - CoroutineScope(Dispatchers.IO).launch { + getUser?.password = password as String + getUser?.email = email as String + getUser?.nickname = nickname as String + coroutineScope.launch { getUser?.let { - NewsPortalDatabase.getInstance(context).userDao() - .update(it) + articlePageScreenViewModel.updateUser(it) } } } @@ -409,7 +374,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c LazyColumn( - contentPadding= PaddingValues(top=5.dp, bottom = 320.dp), + contentPadding = PaddingValues(top = 5.dp, bottom = 320.dp), modifier = modifier .requiredWidth(width = 412.dp) .requiredHeight(height = 915.dp) @@ -430,7 +395,8 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c contentDescription = "Ellipse 8", modifier = Modifier .requiredSize(size = 200.dp) - .clip(shape = CircleShape)) + .clip(shape = CircleShape) + ) Box( modifier = Modifier .align(alignment = Alignment.TopStart) @@ -446,18 +412,21 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) .clip(shape = RoundedCornerShape(15.dp)) - .background(color = Color(0xffdbdbf1))) + .background(color = Color(0xffdbdbf1)) + ) Text( text = "${getUser?.nickname}", color = Color(0xff423a99), textAlign = TextAlign.Center, style = TextStyle( fontSize = 17.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } Box( modifier = Modifier @@ -474,18 +443,21 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) .clip(shape = RoundedCornerShape(15.dp)) - .background(color = Color(0xffdbdbf1))) + .background(color = Color(0xffdbdbf1)) + ) Text( text = "${getUser?.email}", color = Color(0xff423a99), textAlign = TextAlign.Center, style = TextStyle( fontSize = 17.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } Box( modifier = Modifier @@ -502,14 +474,16 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) .clip(shape = RoundedCornerShape(15.dp)) - .background(color = Color(0xff423a99))) + .background(color = Color(0xff423a99)) + ) Text( text = "Add Article!", color = Color(0xffdbdbf1), textAlign = TextAlign.Center, style = TextStyle( fontSize = 17.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) @@ -531,14 +505,16 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) .clip(shape = RoundedCornerShape(15.dp)) - .background(color = Color(0xff423a99))) + .background(color = Color(0xff423a99)) + ) Text( text = "Edit my Data", color = Color(0xffdbdbf1), textAlign = TextAlign.Center, style = TextStyle( fontSize = 17.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .requiredWidth(width = 200.dp) .requiredHeight(height = 44.dp) @@ -588,35 +564,69 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ), verticalArrangement = Arrangement.spacedBy(1.dp) ) { - if (!getComms.isEmpty()) { - items(items = getComms) { comment -> - Spacer(modifier = Modifier.padding(5.dp)) - Text( - text = "${comment.text}", - color = Color(0xff423a99), - style = TextStyle( - fontSize = 15.sp, - fontWeight = FontWeight.Bold - ), - modifier = Modifier - .align(alignment = Alignment.TopStart) - .clickable { - navController.navigate( - BottomBarScreen.ArticlePage.passId( - comment.articleId.toString() + if (getComms.itemCount != 0) { + items(count = getComms.itemCount) { index -> + val comment = getComms[index] + if (comment?.userId == getUser?.id) { + Spacer(modifier = Modifier.padding(5.dp)) + Text( + text = "${comment?.text}", + color = Color(0xff423a99), + style = TextStyle( + fontSize = 15.sp, + fontWeight = FontWeight.Bold + ), + modifier = Modifier + .align(alignment = Alignment.TopStart) + .clickable { + navController.navigate( + BottomBarScreen.ArticlePage.passId( + comment?.articleId.toString() + ) ) + } + ) + Spacer(modifier = Modifier.padding(5.dp)) + HorizontalDivider( + thickness = 3.dp, + modifier = Modifier + .border(BorderStroke(3.dp, Color(0xffdbdbf1))), + color = Color(0xffdbdbf1) + ) + } + } + getComms.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { + CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(), + color = Color(0xff423a99) ) } - ) - Spacer(modifier = Modifier.padding(5.dp)) - HorizontalDivider( - thickness=3.dp, - modifier = Modifier - .border(BorderStroke(3.dp, Color(0xffdbdbf1))), color = Color(0xffdbdbf1) - ) + } + + loadState.append is LoadState.Loading -> { + item { + CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(), + color = Color(0xff423a99) + ) + } + } + + loadState.refresh is LoadState.Error -> { + val err = getComms.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + + loadState.append is LoadState.Error -> { + val err = getComms.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } + } } - } - else{ + } else { item { Text( text = "Комментариев пока нет!", @@ -644,13 +654,15 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .requiredWidth(width = 393.dp) .requiredHeight(height = 54.dp) .clip(shape = RoundedCornerShape(15.dp)) - .background(color = Color(0xff423a99))) + .background(color = Color(0xff423a99)) + ) Text( text = "Comments:", color = Color.White, style = TextStyle( fontSize = 17.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .align(alignment = Alignment.TopStart) .offset( @@ -659,7 +671,8 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ) .requiredWidth(width = 210.dp) .requiredHeight(height = 54.dp) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } } } @@ -705,35 +718,69 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ), verticalArrangement = Arrangement.spacedBy(1.dp) ) { - if (!getArticles.isEmpty()) { - items(items = getArticles) { article -> - Spacer(modifier = Modifier.padding(5.dp)) - Text( - text = "${article.title}", - color = Color(0xff423a99), - style = TextStyle( - fontSize = 15.sp, - fontWeight = FontWeight.Bold - ), - modifier = Modifier - .align(alignment = Alignment.TopStart) - .clickable { - navController.navigate( - BottomBarScreen.ArticlePage.passId( - article.id.toString() + if (getArticles.itemCount != 0) { + items(count = getArticles.itemCount) { index -> + val article = getArticles[index] + if (article?.userId == getUser?.id) { + Spacer(modifier = Modifier.padding(5.dp)) + Text( + text = "${article?.title}", + color = Color(0xff423a99), + style = TextStyle( + fontSize = 15.sp, + fontWeight = FontWeight.Bold + ), + modifier = Modifier + .align(alignment = Alignment.TopStart) + .clickable { + navController.navigate( + BottomBarScreen.ArticlePage.passId( + article?.id.toString() + ) ) + } + ) + Spacer(modifier = Modifier.padding(5.dp)) + HorizontalDivider( + thickness = 3.dp, + modifier = Modifier + .border(BorderStroke(3.dp, Color(0xffdbdbf1))), + color = Color(0xffdbdbf1) + ) + } + } + getArticles.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { + CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(), + color = Color(0xff423a99) ) } - ) - Spacer(modifier = Modifier.padding(5.dp)) - HorizontalDivider( - thickness=3.dp, - modifier = Modifier - .border(BorderStroke(3.dp, Color(0xffdbdbf1))), color = Color(0xffdbdbf1) - ) + } + + loadState.append is LoadState.Loading -> { + item { + CircularProgressIndicator( + modifier = Modifier.fillParentMaxSize(), + color = Color(0xff423a99) + ) + } + } + + loadState.refresh is LoadState.Error -> { + val err = getArticles.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + + loadState.append is LoadState.Error -> { + val err = getArticles.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } + } } - } - else{ + } else { item { Text( text = "Статей пока нет!", @@ -761,13 +808,15 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .requiredWidth(width = 393.dp) .requiredHeight(height = 54.dp) .clip(shape = RoundedCornerShape(15.dp)) - .background(color = Color(0xff423a99))) + .background(color = Color(0xff423a99)) + ) Text( text = "Articles:", color = Color.White, style = TextStyle( fontSize = 17.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .align(alignment = Alignment.TopStart) .offset( @@ -776,7 +825,8 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ) .requiredWidth(width = 210.dp) .requiredHeight(height = 54.dp) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } } } @@ -822,29 +872,38 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ), verticalArrangement = Arrangement.spacedBy(1.dp) ) { - if (!getTags.isEmpty()) { - items(items = getTags) { tag -> - Spacer(modifier = Modifier.padding(5.dp)) - Text( - text = "${tag.title}", - color = Color(0xff423a99), - style = TextStyle( - fontSize = 15.sp, - fontWeight = FontWeight.Bold - ), - modifier = Modifier - .align(alignment = Alignment.TopStart) - .clickable {navController.navigate(BottomBarScreen.SearchByTag.passId(tag.id.toString()))} - ) - Spacer(modifier = Modifier.padding(5.dp)) - HorizontalDivider( - thickness=3.dp, - modifier = Modifier - .border(BorderStroke(3.dp, Color(0xffdbdbf1))), color = Color(0xffdbdbf1) - ) + if (getTags.itemCount != 0) { + items(count = getTags.itemCount) { index -> + val tag = getTags[index] + if (tag?.userId == getUser?.id) { + Spacer(modifier = Modifier.padding(5.dp)) + Text( + text = "${tag?.title}", + color = Color(0xff423a99), + style = TextStyle( + fontSize = 15.sp, + fontWeight = FontWeight.Bold + ), + modifier = Modifier + .align(alignment = Alignment.TopStart) + .clickable { + navController.navigate( + BottomBarScreen.SearchByTag.passId( + tag?.id.toString() + ) + ) + } + ) + Spacer(modifier = Modifier.padding(5.dp)) + HorizontalDivider( + thickness = 3.dp, + modifier = Modifier + .border(BorderStroke(3.dp, Color(0xffdbdbf1))), + color = Color(0xffdbdbf1) + ) + } } - } - else{ + } else { item { Text( text = "Тэгов пока нет!", @@ -872,13 +931,15 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c .requiredWidth(width = 393.dp) .requiredHeight(height = 54.dp) .clip(shape = RoundedCornerShape(15.dp)) - .background(color = Color(0xff423a99))) + .background(color = Color(0xff423a99)) + ) Text( text = "Tags:", color = Color.White, style = TextStyle( fontSize = 17.sp, - fontWeight = FontWeight.Bold), + fontWeight = FontWeight.Bold + ), modifier = Modifier .align(alignment = Alignment.TopStart) .offset( @@ -887,9 +948,11 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier, c ) .requiredWidth(width = 210.dp) .requiredHeight(height = 54.dp) - .wrapContentHeight(align = Alignment.CenterVertically)) + .wrapContentHeight(align = Alignment.CenterVertically) + ) } } } } } + diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/TagsScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/TagsScreen.kt index aff38e3..c4d0eeb 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/TagsScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/TagsScreen.kt @@ -16,26 +16,24 @@ import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -43,34 +41,24 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.designElem.items.TagItem -import com.example.pmulabs.room.database.NewsPortalDatabase import com.example.pmulabs.room.models.Tag import com.example.pmulabs.viewModels.AppViewModelProvider import com.example.pmulabs.viewModels.CurrentUserViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import com.example.pmulabs.viewModels.TagItemViewModel import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext @OptIn(ExperimentalMaterial3Api::class) @Composable -fun TagsScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ +fun TagsScreen(navController: NavController, modifier: Modifier = Modifier, tagItemViewModel: TagItemViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ - val context = LocalContext.current - val tags = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).tagDao().getAll().collect { data -> - tags.clear() - tags.addAll(data) - } - } - } + var tags = tagItemViewModel.tags.collectAsLazyPagingItems() var getUser by remember { mutableStateOf(currentUserViewModel.user) } - + val coroutineScope = rememberCoroutineScope() var openDialog by remember { mutableStateOf(false) } var text by remember { mutableStateOf("") } @@ -130,10 +118,10 @@ fun TagsScreen(navController: NavController, modifier: Modifier = Modifier, curr openDialog = false if(text.isNotEmpty()) { val newTag = Tag(null, text, getUser?.id.toString().toInt()) - CoroutineScope(Dispatchers.IO).launch { - NewsPortalDatabase.getInstance(context).tagDao() - .insert(newTag) + coroutineScope.launch { + tagItemViewModel.insertTag(newTag) } + navController.navigate(BottomBarScreen.Categories.route) } }, modifier = Modifier @@ -186,12 +174,34 @@ fun TagsScreen(navController: NavController, modifier: Modifier = Modifier, curr ) } } - items(items = tags){ tag -> - TagItem( - teg = tag, - viewModel = currentUserViewModel, - onTagClick = {navController.navigate(BottomBarScreen.SearchByTag.passId(tag.id.toString()))} + items(count = tags.itemCount) { index -> + val tag = tags[index] + if (tag != null) { + TagItem( + navController=navController, + teg = tag, + viewModel = currentUserViewModel, + onTagClick = {navController.navigate(BottomBarScreen.SearchByTag.passId(tag?.id.toString()))} ) + } + } + tags.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.append is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.refresh is LoadState.Error -> { + val err = tags.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + loadState.append is LoadState.Error -> { + val err = tags.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/EntryScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/EntryScreen.kt index 08b8815..e0587fe 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/EntryScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/EntryScreen.kt @@ -1,5 +1,6 @@ package com.example.pmulabs.screensMobile.authScreens +import android.annotation.SuppressLint import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -28,11 +29,9 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -41,7 +40,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation @@ -58,33 +56,24 @@ import com.example.pmulabs.designElem.statDesign.LogoMobile import com.example.pmulabs.designElem.statDesign.RightCircles import com.example.pmulabs.graphs.AuthScreen import com.example.pmulabs.graphs.Graph -import com.example.pmulabs.room.database.NewsPortalDatabase import com.example.pmulabs.room.models.User import com.example.pmulabs.viewModels.AppViewModelProvider import com.example.pmulabs.viewModels.CurrentUserViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import com.example.pmulabs.viewModels.EntryScreenViewModel +@SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun EntryScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { +fun EntryScreen(navController: NavController, modifier: Modifier = Modifier,entryScreenViewModel: EntryScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { var emailValue by rememberSaveable { mutableStateOf("") } var passwordValue by rememberSaveable { mutableStateOf("") } - val context = LocalContext.current - val users = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).userDao().getAll().collect { data -> - users.clear() - users.addAll(data) - } - } - } + entryScreenViewModel.setUserList() + val users = mutableStateOf>(entryScreenViewModel.userList) val argument = currentUserViewModel.argument.value - var passwordVisibility by rememberSaveable { mutableStateOf(false) } val emailRegex = "[a-zA-Z0-9._-]+@[a-z]+\\.+[a-z]+".toRegex() + val coroutineScope = rememberCoroutineScope() Box( modifier = modifier @@ -205,7 +194,7 @@ fun EntryScreen(navController: NavController, modifier: Modifier = Modifier, cur ), onClick = { if (passwordValue.isNotEmpty() && isValidEmail(emailValue)) { - users.forEach { user -> + users.value.forEach { user -> if (user.password == passwordValue && user.email == emailValue) { currentUserViewModel.setArgument(user.id.toString()) navController.navigate(route = Graph.passUserId(user.id.toString())) { diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/RegisterScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/RegisterScreen.kt index 7001bdb..290094a 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/RegisterScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/authScreens/RegisterScreen.kt @@ -1,5 +1,6 @@ package com.example.pmulabs.screensMobile.authScreens +import android.annotation.SuppressLint import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -29,11 +30,9 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -42,7 +41,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation @@ -54,36 +52,31 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.example.pmulabs.designElem.elem.ValidateEmail import com.example.pmulabs.designElem.elem.isValidEmail -import com.example.pmulabs.viewModels.CurrentUserViewModel import com.example.pmulabs.designElem.statDesign.LeftCircles import com.example.pmulabs.designElem.statDesign.LogoMobile import com.example.pmulabs.designElem.statDesign.RightCircles import com.example.pmulabs.graphs.AuthScreen import com.example.pmulabs.graphs.Graph -import com.example.pmulabs.room.database.NewsPortalDatabase import com.example.pmulabs.room.models.User import com.example.pmulabs.viewModels.AppViewModelProvider -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import com.example.pmulabs.viewModels.CurrentUserViewModel +import com.example.pmulabs.viewModels.RegisterScreenViewModel +import kotlinx.coroutines.async import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +@SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable -fun RegisterScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { +fun RegisterScreen(navController: NavController, modifier: Modifier = Modifier,registerScreenViewModel: RegisterScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { var emailValue by rememberSaveable { mutableStateOf("") } var passwordValue by rememberSaveable { mutableStateOf("") } var nicknameValue by rememberSaveable { mutableStateOf("") } - val context = LocalContext.current - val users = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).userDao().getAll().collect { data -> - users.clear() - users.addAll(data) - } - } + val coroutineScope = rememberCoroutineScope() + registerScreenViewModel.setUserList() + var users = mutableStateOf>(emptyList()) + registerScreenViewModel.users.observeForever { userList -> + users.value = userList } var passwordVisibility by rememberSaveable { mutableStateOf(false) } @@ -169,7 +162,7 @@ fun RegisterScreen(navController: NavController, modifier: Modifier = Modifier, TextField( value = nicknameValue, onValueChange = { - nicknameValue=it + nicknameValue = it }, colors = TextFieldDefaults.textFieldColors( containerColor = Color.White @@ -185,7 +178,7 @@ fun RegisterScreen(navController: NavController, modifier: Modifier = Modifier, singleLine = true, ) Spacer(modifier = Modifier.padding(3.dp)) - ValidateEmail(emailValue, {emailValue=it} ) + ValidateEmail(emailValue, { emailValue = it }) Spacer(modifier = Modifier.padding(3.dp)) TextField( value = passwordValue, @@ -228,32 +221,32 @@ fun RegisterScreen(navController: NavController, modifier: Modifier = Modifier, onClick = { var isExist = false; if (passwordValue.isNotEmpty() && isValidEmail(emailValue) && nicknameValue.isNotEmpty()) { - users.forEach { user -> + users.value.forEach { user -> if (user.email == emailValue || user.nickname == nicknameValue) { Log.d("User already exist. User id: ", user.id.toString()) isExist = true } } - if (!isExist) { - var newUser = User(null, nicknameValue, emailValue, passwordValue, "user") - CoroutineScope(Dispatchers.IO).launch { - NewsPortalDatabase.getInstance(context).userDao().insert(newUser) - NewsPortalDatabase.getInstance(context).userDao().getAll() - .collect { data -> - data.forEach { user -> - if (user.password == passwordValue && user.email == emailValue) { - withContext(Dispatchers.Main) { - currentUserViewModel.setArgument(user.id.toString()) - navController.navigate( - route = Graph.passUserId( - user.id.toString() - ) - ) - } - } + val newUser = User(null, nicknameValue, emailValue, passwordValue, "user") + coroutineScope.launch { + val insertResult = async { + registerScreenViewModel.insertUser(newUser) + } + + insertResult.await() + + registerScreenViewModel.setUserList() + registerScreenViewModel.users.observeForever { userList -> + users.value = userList + Log.println(Log.ASSERT, "UsersList", users.value.toString()) + users.value?.forEach { user -> + if (user.password == passwordValue && user.email == emailValue) { + currentUserViewModel.setArgument(user.id.toString()) + navController.navigate(route = Graph.passUserId(user.id.toString())) } } + } } } } @@ -288,195 +281,9 @@ fun RegisterScreen(navController: NavController, modifier: Modifier = Modifier, } ) Spacer(modifier = Modifier.padding(20.dp)) + } } } } } - - -/* -@Composable -fun RegisterScreen(navController: NavController,modifier: Modifier = Modifier) { - Box( - modifier = modifier - .requiredWidth(width = 412.dp) - .requiredHeight(height = 915.dp) - .background(color = Color.White) - ) { - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = (-17).dp, - y = 329.dp - ) - .requiredWidth(width = 436.dp) - .requiredHeight(height = 325.dp) - ) { - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 69.1444091796875.dp, - y = 122.9124755859375.dp - ) - .requiredWidth(width = 308.dp) - .requiredHeight(height = 61.dp) - ) { - - } - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 69.1444091796875.dp, - y = 193.824951171875.dp - ) - .requiredWidth(width = 308.dp) - .requiredHeight(height = 61.dp) - ) { - - } - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 68.dp, - y = 52.dp - ) - .requiredWidth(width = 308.dp) - .requiredHeight(height = 61.dp) - ) { - - } - Text( - textAlign = TextAlign.Center, - text = buildAnnotatedString { - withStyle(style = SpanStyle( - color = Color(0xff7d7dbd), - fontSize = 20.sp)) {append("Please, ")} - withStyle(style = SpanStyle( - color = Color(0xff7d7dbd), - fontSize = 20.sp, - fontWeight = FontWeight.Bold)) {append("Sign Up")}}, - modifier = Modifier - .fillMaxSize()) - } - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 128.dp, - y = 730.dp - ) - .requiredWidth(width = 381.dp) - .requiredHeight(height = 268.dp) - .rotate(degrees = 8.33f) - ) { - RightCircles() - } - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = (-111.28436279296875).dp, - y = (-96.86965942382812).dp - ) - .requiredWidth(width = 250.dp) - .requiredHeight(height = 290.dp) - ) { - Box( - modifier = Modifier - .rotate(degrees = 12.96f) - ) { - LeftCircles() - } - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 140.35101318359375.dp, - y = 152.06341552734375.dp - ) - .requiredWidth(width = 66.dp) - .requiredHeight(height = 27.dp) - ) { - BackButton(modifier=Modifier - .clickable { navController.navigate(route = AuthScreen.Entry.route){ - popUpTo(AuthScreen.Splash.route) - } })} - } - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 50.dp, - y = 681.dp - ) - .requiredWidth(width = 308.dp) - .requiredHeight(height = 127.dp) - ) { - Text( - text = "Do you have an account? ", - color = Color(0xff7d7dbd), - textAlign = TextAlign.Center, - style = TextStyle( - fontSize = 20.sp), - modifier = Modifier - .fillMaxSize() - .offset( - x = 0.dp, - y = 75.dp - )) - Text( - text = "Sign In", - color = Color(0xff7d7dbd), - textAlign = TextAlign.Center, - style = TextStyle( - fontSize = 20.sp, - fontWeight = FontWeight.Bold), - modifier = Modifier - .fillMaxSize() - .offset( - x = 0.dp, - y = 95.dp - ) - .clickable { navController.navigate(route = AuthScreen.Entry.route) - { - popUpTo(AuthScreen.Splash.route) - }}) - Box( - modifier = Modifier - .requiredWidth(width = 308.dp) - .requiredHeight(height = 82.dp) - ) { - ButtonCustom(name = "Sign Up",modifier = Modifier - .fillMaxSize() - .clickable { navController.navigate(route = Graph.MAIN){ - popUpTo(Graph.MAIN) - } } - ) - } - } - Box( - modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 57.dp, - y = 165.dp - ) - .requiredWidth(width = 302.dp) - .requiredHeight(height = 140.dp) - ) { - LogoMobile(modifier = Modifier - .align(alignment = Alignment.TopStart) - .offset( - x = 0.dp, - y = 9.dp - ) - .fillMaxWidth() - .requiredHeight(height = 131.dp)) - } - } -}*/ diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/CalendarScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/CalendarScreen.kt index ba3f514..376ae94 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/CalendarScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/CalendarScreen.kt @@ -1,5 +1,6 @@ package com.example.pmulabs.screensMobile.filterScreens +import android.annotation.SuppressLint import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -7,41 +8,29 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.basecomponents.navigate.CALENDAR_ARGUMENT_DATE import com.example.pmulabs.designElem.elem.BackButton import com.example.pmulabs.designElem.items.ArticleItem -import com.example.pmulabs.viewModels.CurrentUserViewModel -import com.example.pmulabs.room.database.NewsPortalDatabase -import com.example.pmulabs.room.models.Article import com.example.pmulabs.viewModels.AppViewModelProvider -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import com.example.pmulabs.viewModels.ArticleScreenViewModel +import com.example.pmulabs.viewModels.CurrentUserViewModel import java.text.SimpleDateFormat +@SuppressLint("UnrememberedMutableState") @Composable -fun CalendarScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ - val context = LocalContext.current - val articles = remember { mutableStateListOf
() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).articleDao().getAll().collect { data -> - articles.clear() - articles.addAll(data) - } - } - } +fun CalendarScreen(navController: NavController, articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ + val articles = articleScreenViewModel.articles.collectAsLazyPagingItems() var date=navController.currentBackStackEntry?.arguments?.getString(CALENDAR_ARGUMENT_DATE).toString() Log.d("date: ",date) val formatter = SimpleDateFormat("dd-MMMM-YY") @@ -56,14 +45,43 @@ fun CalendarScreen(navController: NavController, modifier: Modifier = Modifier, BackButton(modifier=Modifier .clickable { navController.popBackStack()}) } - items(items = articles){ article -> - val publishDate=formatter.format(article.publishDate) + items(count = articles.itemCount) { index -> + val article = articles[index] + val publishDate=formatter.format(article?.publishDate) if(publishDate==date) { - ArticleItem( - article = article, - currentUserViewModel=currentUserViewModel, - onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))} - ) + if (article != null) { + ArticleItem( + navController=navController, + article = article, + currentUserViewModel = currentUserViewModel, + onArticleClick = { + navController.navigate( + BottomBarScreen.ArticlePage.passId( + article?.id.toString() + ) + ) + } + ) + } + } + } + + articles.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.append is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.refresh is LoadState.Error -> { + val err = articles.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + loadState.append is LoadState.Error -> { + val err = articles.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } } } } diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchByTagScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchByTagScreen.kt index 2f9cd4c..e80a4d1 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchByTagScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchByTagScreen.kt @@ -7,40 +7,28 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.basecomponents.navigate.SEARCHBYTAG_ARGUMENT_KEY import com.example.pmulabs.designElem.elem.BackButton import com.example.pmulabs.designElem.items.ArticleItem -import com.example.pmulabs.viewModels.CurrentUserViewModel -import com.example.pmulabs.room.database.NewsPortalDatabase -import com.example.pmulabs.room.models.Article import com.example.pmulabs.viewModels.AppViewModelProvider -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import com.example.pmulabs.viewModels.ArticleScreenViewModel +import com.example.pmulabs.viewModels.CurrentUserViewModel @Composable -fun SearchByTagScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ - val context = LocalContext.current - val articles = remember { mutableStateListOf
() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).articleDao().getAll().collect { data -> - articles.clear() - articles.addAll(data) - } - } - } +fun SearchByTagScreen(navController: NavController, modifier: Modifier = Modifier, articleScreenViewModel: ArticleScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ + val articles = articleScreenViewModel.articles.collectAsLazyPagingItems() + var id=navController.currentBackStackEntry?.arguments?.getString(SEARCHBYTAG_ARGUMENT_KEY).toString() Log.d("id: ",id) @@ -53,9 +41,30 @@ fun SearchByTagScreen(navController: NavController, modifier: Modifier = Modifie BackButton(modifier=Modifier .clickable { navController.popBackStack()}) } - items(items = articles){ article -> - if(article.tagId.toString()==id) { - ArticleItem(article = article, currentUserViewModel = currentUserViewModel, onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))}) + items(count = articles.itemCount) { index -> + val article = articles[index] + if(article?.tagId.toString()==id) { + if (article != null) { + ArticleItem(navController=navController,article = article, currentUserViewModel = currentUserViewModel, onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))}) + } + } + } + articles.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.append is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.refresh is LoadState.Error -> { + val err = articles.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + loadState.append is LoadState.Error -> { + val err = articles.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } } } } diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchScreen.kt index 5d6e47f..db6f78f 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/filterScreens/SearchScreen.kt @@ -7,40 +7,27 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.example.pmulabs.basecomponents.navigate.BottomBarScreen import com.example.pmulabs.basecomponents.navigate.SEARCH_ARGUMENT_TEXT import com.example.pmulabs.designElem.elem.BackButton import com.example.pmulabs.designElem.items.ArticleItem -import com.example.pmulabs.viewModels.CurrentUserViewModel -import com.example.pmulabs.room.database.NewsPortalDatabase -import com.example.pmulabs.room.models.Article import com.example.pmulabs.viewModels.AppViewModelProvider -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import com.example.pmulabs.viewModels.ArticleScreenViewModel +import com.example.pmulabs.viewModels.CurrentUserViewModel @Composable -fun SearchScreen(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ - val context = LocalContext.current - val articles = remember { mutableStateListOf
() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - NewsPortalDatabase.getInstance(context).articleDao().getAll().collect { data -> - articles.clear() - articles.addAll(data) - } - } - } +fun SearchScreen(navController: NavController, modifier: Modifier = Modifier, articleScreenViewModel: ArticleScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){ + val articles = articleScreenViewModel.articles.collectAsLazyPagingItems() var text=navController.currentBackStackEntry?.arguments?.getString(SEARCH_ARGUMENT_TEXT).toString() Log.d("text: ",text) @@ -54,9 +41,30 @@ fun SearchScreen(navController: NavController, modifier: Modifier = Modifier, cu BackButton(modifier=Modifier .clickable { navController.popBackStack()}) } - items(items = articles){ article -> - if(article.title.contains(text)) { - ArticleItem(article = article,currentUserViewModel=currentUserViewModel,onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))}) + items(count = articles.itemCount) { index -> + val article = articles[index] + if(article?.title?.contains(text)==true) { + if (article != null) { + ArticleItem(navController=navController,article = article,currentUserViewModel=currentUserViewModel,onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))}) + } + } + } + articles.apply { + when { + loadState.refresh is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.append is LoadState.Loading -> { + item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = Color(0xff423a99)) } + } + loadState.refresh is LoadState.Error -> { + val err = articles.loadState.refresh as LoadState.Error + item { Text(err.error.localizedMessage) } + } + loadState.append is LoadState.Error -> { + val err = articles.loadState.append as LoadState.Error + item { Text(err.error.localizedMessage) } + } } } } diff --git a/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt b/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt index a70ce10..01df35a 100644 --- a/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt +++ b/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt @@ -20,6 +20,18 @@ object AppViewModelProvider { initializer { ArticleItemViewModel(newsPortalApplication().container.tagRepository,newsPortalApplication().container.commentRepository,newsPortalApplication().container.articleRepository) } + initializer { + EntryScreenViewModel(newsPortalApplication().container.userRepository) + } + initializer { + RegisterScreenViewModel(newsPortalApplication().container.userRepository) + } + initializer { + ArticleScreenViewModel(newsPortalApplication().container.articleRepository) + } + initializer { + ArticlePageScreenViewModel(newsPortalApplication().container.tagRepository,newsPortalApplication().container.commentRepository,newsPortalApplication().container.articleRepository,newsPortalApplication().container.userRepository) + } } } diff --git a/app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt b/app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt new file mode 100644 index 0000000..2763324 --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt @@ -0,0 +1,74 @@ +package com.example.pmulabs.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import com.example.pmulabs.room.models.Article +import com.example.pmulabs.room.models.Comment +import com.example.pmulabs.room.models.Tag +import com.example.pmulabs.room.models.User +import com.example.pmulabs.room.repository.ArticleRepository +import com.example.pmulabs.room.repository.CommentRepository +import com.example.pmulabs.room.repository.TagRepository +import com.example.pmulabs.room.repository.UserRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +class ArticlePageScreenViewModel( + private val tagRepository: TagRepository, + private val commentRepository: CommentRepository, + private val articleRepository: ArticleRepository, + private val userRepository: UserRepository +) : ViewModel() { + private val articleid = mutableStateOf(null) + private val tagid = mutableStateOf(null) + var article by mutableStateOf(null) + var tag by mutableStateOf(null) + val comments: Flow> = commentRepository.getComments() + + fun getArticleById(articleId:Int) { + articleid.value = articleId + viewModelScope.launch { + article = articleRepository.getArticleById(articleid.value!!) + .filterNotNull() + .first() + } + } + + var tagList by mutableStateOf>(emptyList()) + fun setTagList() { + viewModelScope.launch { + tagList=tagRepository.getAllTags().first() + } + } + + suspend fun insertComment(comment: Comment) { + commentRepository.insertComment(comment) + } + + suspend fun updateUser(user: User) { + userRepository.updateUser(user) + } + + suspend fun insertArticle(article: Article) { + articleRepository.insertArticle(article) + } + + suspend fun getTagByName(name: String) : Tag { + return tagRepository.getTagByName(name).first() + } + + fun getTagById(tagId:Int) { + tagid.value = tagId + viewModelScope.launch { + tag = tagRepository.getTagById(tagid.value!!) + .filterNotNull() + .first() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/viewModels/ArticleScreenViewModel.kt b/app/src/main/java/com/example/pmulabs/viewModels/ArticleScreenViewModel.kt new file mode 100644 index 0000000..6dbc8c4 --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/viewModels/ArticleScreenViewModel.kt @@ -0,0 +1,11 @@ +package com.example.pmulabs.viewModels + +import androidx.lifecycle.ViewModel +import androidx.paging.PagingData +import com.example.pmulabs.room.models.Article +import com.example.pmulabs.room.repository.ArticleRepository +import kotlinx.coroutines.flow.Flow + +class ArticleScreenViewModel(private val articleRepository: ArticleRepository) : ViewModel() { + val articles: Flow> = articleRepository.getArticles() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/viewModels/EntryScreenViewModel.kt b/app/src/main/java/com/example/pmulabs/viewModels/EntryScreenViewModel.kt new file mode 100644 index 0000000..9f33c6a --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/viewModels/EntryScreenViewModel.kt @@ -0,0 +1,21 @@ +package com.example.pmulabs.viewModels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.pmulabs.room.models.User +import com.example.pmulabs.room.repository.UserRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +class EntryScreenViewModel(private val userRepository: UserRepository) : ViewModel() { + + var userList by mutableStateOf>(emptyList()) + fun setUserList() { + viewModelScope.launch { + userList=userRepository.getAllUsers().first() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/viewModels/RegisterScreenViewModel.kt b/app/src/main/java/com/example/pmulabs/viewModels/RegisterScreenViewModel.kt new file mode 100644 index 0000000..830eb7f --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/viewModels/RegisterScreenViewModel.kt @@ -0,0 +1,26 @@ +package com.example.pmulabs.viewModels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.pmulabs.room.models.User +import com.example.pmulabs.room.repository.UserRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +class RegisterScreenViewModel(private val userRepository: UserRepository) : ViewModel() { + + private val _users = MutableLiveData>() + val users: LiveData> get() = _users + + fun setUserList() { + viewModelScope.launch { + _users.value = userRepository.getAllUsers().first() + } + } + + suspend fun insertUser(user: User) { + userRepository.insertUser(user) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/viewModels/TagItemViewModel.kt b/app/src/main/java/com/example/pmulabs/viewModels/TagItemViewModel.kt index 2c7fcd5..57abfad 100644 --- a/app/src/main/java/com/example/pmulabs/viewModels/TagItemViewModel.kt +++ b/app/src/main/java/com/example/pmulabs/viewModels/TagItemViewModel.kt @@ -1,12 +1,19 @@ package com.example.pmulabs.viewModels import androidx.lifecycle.ViewModel +import androidx.paging.PagingData import com.example.pmulabs.room.models.Tag import com.example.pmulabs.room.repository.TagRepository +import kotlinx.coroutines.flow.Flow class TagItemViewModel( private val tagRepository: TagRepository ) : ViewModel() { + + var tags: Flow> = tagRepository.getTags() + suspend fun insertTag(tag: Tag) { + tagRepository.insertTag(tag) + } suspend fun updateTag(tag: Tag) { tagRepository.updateTag(tag) }