lab 5 complete

This commit is contained in:
AnnZhimol 2023-12-13 14:49:20 +04:00
parent 5f4c36f00a
commit 9b7acce5b6
53 changed files with 1347 additions and 609 deletions

View File

@ -3,6 +3,7 @@ plugins {
id("com.android.application") id("com.android.application")
id("com.google.devtools.ksp") id("com.google.devtools.ksp")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.serialization")
} }
android { android {
@ -95,4 +96,12 @@ dependencies {
implementation ("androidx.paging:paging-guava:$paging_version") implementation ("androidx.paging:paging-guava:$paging_version")
// optional - Jetpack Compose integration // optional - Jetpack Compose integration
implementation ("androidx.paging:paging-compose:1.0.0-alpha18") implementation ("androidx.paging:paging-compose:1.0.0-alpha18")
// retrofit
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
implementation("androidx.paging:paging-compose:3.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
} }

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
@ -12,6 +12,8 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.PMULabs" android:theme="@style/Theme.PMULabs"
android:name=".NewsPortalApplication" android:name=".NewsPortalApplication"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View File

@ -0,0 +1,159 @@
package com.example.pmulabs.api
import com.example.pmulabs.api.model.ArticleRemote
import com.example.pmulabs.api.model.CommentRemote
import com.example.pmulabs.api.model.TagRemote
import com.example.pmulabs.api.model.UserRemote
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
interface NewsPortalService {
@GET("users")
suspend fun getUsers(): List<UserRemote>
@GET("tags")
suspend fun getTags(
@Query("_page") page: Int,
@Query("_limit") limit: Int,
): List<TagRemote>
@GET("tags")
suspend fun getAllTags(): List<TagRemote>
@GET("articles")
suspend fun getAllArticles(): List<ArticleRemote>
@GET("comments")
suspend fun getAllComments(): List<CommentRemote>
@GET("articles")
suspend fun getArticles(
@Query("_page") page: Int,
@Query("_limit") limit: Int,
): List<ArticleRemote>
@GET("comments")
suspend fun getComments(
@Query("_page") page: Int,
@Query("_limit") limit: Int,
): List<CommentRemote>
@GET("comments")
suspend fun getCountComment(
@Query("articleId") articleId: Int
): List<CommentRemote>
@GET("comments")
suspend fun getUserComments(@Query("userId") userId: Int): List<CommentRemote>
@GET("articles")
suspend fun getUserArticles(@Query("userId") userId: Int): List<ArticleRemote>
@GET("tags")
suspend fun getUserTags(@Query("userId") userId: Int): List<TagRemote>
@GET("users/{id}")
suspend fun getUser(
@Path("id") id: Int,
): UserRemote
@POST("users")
suspend fun createUser(
@Body user: UserRemote,
): UserRemote
@PUT("users/{id}")
suspend fun updateUser(
@Path("id") id: Int,
@Body user: UserRemote,
): UserRemote
@DELETE("users/{id}")
suspend fun deleteUser(
@Path("id") id: Int,
): UserRemote
@GET("tags")
suspend fun getTagByName(
@Query("title") title: String,
): List<TagRemote>
@GET("tags/{id}")
suspend fun getTag(
@Path("id") id: Int,
): TagRemote
@POST("tags")
suspend fun createTag(
@Body tag: TagRemote,
): TagRemote
@PUT("tags/{id}")
suspend fun updateTag(
@Path("id") id: Int,
@Body tag: TagRemote,
): TagRemote
@DELETE("tags/{id}")
suspend fun deleteTag(
@Path("id") id: Int,
): TagRemote
@GET("articles/{id}")
suspend fun getArticle(
@Path("id") id: Int,
): ArticleRemote
@POST("articles")
suspend fun createArticle(
@Body article: ArticleRemote,
): ArticleRemote
@PUT("articles/{id}")
suspend fun updateArticle(
@Path("id") id: Int,
@Body article: ArticleRemote,
): ArticleRemote
@DELETE("articles/{id}")
suspend fun deleteArticle(
@Path("id") id: Int,
): ArticleRemote
@GET("comments/{id}")
suspend fun getComment(
@Path("id") id: Int,
): CommentRemote
@POST("comments")
suspend fun createComment(
@Body comment: CommentRemote,
): CommentRemote
@PUT("comments/{id}")
suspend fun updateComment(
@Path("id") id: Int,
@Body comment: CommentRemote,
): CommentRemote
@DELETE("comments/{id}")
suspend fun deleteComment(
@Path("id") id: Int,
): CommentRemote
companion object {
private const val BASE_URL = "http://10.0.2.2:8079/"
@Volatile
private var INSTANCE: NewsPortalService? = null
fun getInstance(): NewsPortalService {
return INSTANCE ?: synchronized(this) {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
.create(NewsPortalService::class.java)
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,112 @@
package com.example.pmulabs.api.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.model.toArticle
import com.example.pmulabs.api.repository.RestTagRepository
import com.example.pmulabs.room.database.NewsPortalDatabase
import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.models.RemoteKeyType
import com.example.pmulabs.room.models.RemoteKeys
import com.example.pmulabs.room.repository.OfflineArticleRepository
import com.example.pmulabs.room.repository.OfflineRemoteKeyRepository
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class ArticleRemoteMediator(
private val service: NewsPortalService,
private val dbArticleRepository: OfflineArticleRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val tagRestRepository: RestTagRepository,
private val database: NewsPortalDatabase
) : RemoteMediator<Int, Article>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Article>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val articles = service.getArticles(page, state.config.pageSize).map { it.toArticle() }
val endOfPaginationReached = articles.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.ARTICLE)
dbArticleRepository.clearArticles()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = articles.map {
it.id?.let { it1 ->
RemoteKeys(
entityId = it1,
type = RemoteKeyType.ARTICLE,
prevKey = prevKey,
nextKey = nextKey
)
}
}
dbRemoteKeyRepository.createRemoteKeys(keys)
tagRestRepository.getAllTags()
dbArticleRepository.insertArticles(articles)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Article>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { art ->
art.id?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.ARTICLE) }
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Article>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { art ->
art.id?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.ARTICLE) }
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Article>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.id?.let { artid ->
dbRemoteKeyRepository.getAllRemoteKeys(artid, RemoteKeyType.ARTICLE)
}
}
}
}

View File

@ -0,0 +1,111 @@
package com.example.pmulabs.api.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.model.toComment
import com.example.pmulabs.api.repository.RestArticleRepository
import com.example.pmulabs.room.database.NewsPortalDatabase
import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.RemoteKeyType
import com.example.pmulabs.room.models.RemoteKeys
import com.example.pmulabs.room.repository.OfflineCommentRepository
import com.example.pmulabs.room.repository.OfflineRemoteKeyRepository
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class CommentRemoteMediator(
private val service: NewsPortalService,
private val dbCommentRepository: OfflineCommentRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val articleRestRepository: RestArticleRepository,
private val database: NewsPortalDatabase
) : RemoteMediator<Int, Comment>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Comment>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val comms = service.getComments(page, state.config.pageSize).map { it.toComment() }
val endOfPaginationReached = comms.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.COMMENT)
dbCommentRepository.clearComments()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = comms.map {
it.id?.let { it1 ->
RemoteKeys(
entityId = it1,
type = RemoteKeyType.COMMENT,
prevKey = prevKey,
nextKey = nextKey
)
}
}
dbRemoteKeyRepository.createRemoteKeys(keys)
articleRestRepository.getAllArticles()
dbCommentRepository.insertComments(comms)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Comment>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { comm ->
comm.id?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.COMMENT) }
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Comment>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { comm ->
comm.id?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.COMMENT) }
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Comment>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.id?.let { commid ->
dbRemoteKeyRepository.getAllRemoteKeys(commid, RemoteKeyType.COMMENT)
}
}
}
}

View File

@ -0,0 +1,109 @@
package com.example.pmulabs.api.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.model.toTag
import com.example.pmulabs.room.database.NewsPortalDatabase
import com.example.pmulabs.room.models.RemoteKeyType
import com.example.pmulabs.room.models.RemoteKeys
import com.example.pmulabs.room.models.Tag
import com.example.pmulabs.room.repository.OfflineRemoteKeyRepository
import com.example.pmulabs.room.repository.OfflineTagRepository
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class TagRemoteMediator(
private val service: NewsPortalService,
private val dbTagRepository: OfflineTagRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: NewsPortalDatabase
) : RemoteMediator<Int, Tag>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Tag>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val tags = service.getTags(page, state.config.pageSize).map { it.toTag() }
val endOfPaginationReached = tags.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.TAG)
dbTagRepository.clearTags()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = tags.map {
it.id?.let { it1 ->
RemoteKeys(
entityId = it1,
type = RemoteKeyType.TAG,
prevKey = prevKey,
nextKey = nextKey
)
}
}
dbRemoteKeyRepository.createRemoteKeys(keys)
dbTagRepository.insertTags(tags)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Tag>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { tag ->
tag.id?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.TAG) }
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Tag>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { tag ->
tag.id?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.TAG) }
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Tag>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.id?.let { tagid ->
dbRemoteKeyRepository.getAllRemoteKeys(tagid, RemoteKeyType.TAG)
}
}
}
}

View File

@ -0,0 +1,32 @@
package com.example.pmulabs.api.model
import com.example.pmulabs.room.models.Article
import kotlinx.serialization.Serializable
@Serializable
data class ArticleRemote(
val id: Int?=0,
var title: String="",
var text: String="",
val publishDate: Long=0,
val userId: Int=0,
var tagId: Int=0
)
fun ArticleRemote.toArticle(): Article = Article(
id,
title,
text,
publishDate,
userId,
tagId
)
fun Article.toArticleRemote(): ArticleRemote = ArticleRemote(
id,
title,
text,
publishDate,
userId,
tagId
)

View File

@ -0,0 +1,26 @@
package com.example.pmulabs.api.model
import com.example.pmulabs.room.models.Comment
import kotlinx.serialization.Serializable
@Serializable
data class CommentRemote(
val id: Int?=0,
var text: String="",
val userId: Int=0,
val articleId: Int=0
)
fun CommentRemote.toComment(): Comment = Comment(
id,
text,
userId,
articleId
)
fun Comment.toCommentRemote(): CommentRemote = CommentRemote(
id,
text,
userId,
articleId
)

View File

@ -0,0 +1,23 @@
package com.example.pmulabs.api.model
import com.example.pmulabs.room.models.Tag
import kotlinx.serialization.Serializable
@Serializable
data class TagRemote(
var title: String="",
val id: Int?=0,
val userId: Int=0
)
fun TagRemote.toTag(): Tag = Tag(
title,
id,
userId
)
fun Tag.toTagRemote(): TagRemote = TagRemote(
title,
id,
userId
)

View File

@ -0,0 +1,29 @@
package com.example.pmulabs.api.model
import com.example.pmulabs.room.models.User
import kotlinx.serialization.Serializable
@Serializable
data class UserRemote(
val id: Int?=0,
var nickname: String="",
var email: String="",
var password: String="",
val role: String=""
)
fun UserRemote.toUser(): User = User(
id,
nickname,
email,
password,
role
)
fun User.toUserRemote(): UserRemote = UserRemote(
id,
nickname,
email,
password,
role
)

View File

@ -0,0 +1,79 @@
package com.example.pmulabs.api.repository
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.mediator.ArticleRemoteMediator
import com.example.pmulabs.api.model.toArticle
import com.example.pmulabs.api.model.toArticleRemote
import com.example.pmulabs.room.AppContainer
import com.example.pmulabs.room.database.NewsPortalDatabase
import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.repository.ArticleRepository
import com.example.pmulabs.room.repository.OfflineArticleRepository
import com.example.pmulabs.room.repository.OfflineRemoteKeyRepository
import kotlinx.coroutines.flow.Flow
class RestArticleRepository(
private val service: NewsPortalService,
private val dbArticleRepository: OfflineArticleRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val dbTagRepository: RestTagRepository,
private val database: NewsPortalDatabase
) : ArticleRepository {
override suspend fun insertArticle(article: Article) {
service.createArticle(article.toArticleRemote()).toArticle()
}
override suspend fun updateArticle(article: Article) {
article.id?.let { service.updateArticle(it, article.toArticleRemote()).toArticle() }
}
override suspend fun deleteArticle(article: Article) {
article.id?.let { service.deleteArticle(it).toArticle() }
}
override suspend fun getAllArticles(): List<Article> {
dbTagRepository.getAllTags()
val existArticles = dbArticleRepository.getAllArticles().associateBy { it.id }.toMutableMap()
service.getAllArticles()
.map { it.toArticle() }
.forEach { art ->
val existArt = existArticles[art.id]
if (existArt == null) {
dbArticleRepository.insertArticle(art)
} else if (existArt != art) {
dbArticleRepository.updateArticle(art)
}
existArticles[art.id] = art
}
return existArticles.map { it.value }.sortedBy { it.id }
}
override suspend fun getArticleById(idArticle: Int): Article? =
idArticle?.let { service.getArticle(it).toArticle() }
override fun getArticles(): Flow<PagingData<Article>> {
val pagingSourceFactory = { dbArticleRepository.getAllArticlesPagingSource() }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = ArticleRemoteMediator(
service,
dbArticleRepository,
dbRemoteKeyRepository,
dbTagRepository,
database,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
}

View File

@ -0,0 +1,85 @@
package com.example.pmulabs.api.repository
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.mediator.CommentRemoteMediator
import com.example.pmulabs.api.model.toComment
import com.example.pmulabs.api.model.toCommentRemote
import com.example.pmulabs.api.model.toUser
import com.example.pmulabs.room.AppContainer
import com.example.pmulabs.room.database.NewsPortalDatabase
import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.User
import com.example.pmulabs.room.repository.CommentRepository
import com.example.pmulabs.room.repository.OfflineCommentRepository
import com.example.pmulabs.room.repository.OfflineRemoteKeyRepository
import kotlinx.coroutines.flow.Flow
class RestCommentRepository(
private val service: NewsPortalService,
private val dbCommentRepository: OfflineCommentRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val dbArticleRepository: RestArticleRepository,
private val database: NewsPortalDatabase
) : CommentRepository {
override suspend fun insertComment(comment: Comment) {
service.createComment(comment.toCommentRemote()).toComment()
}
override suspend fun updateComment(comment: Comment) {
comment.id?.let { service.updateComment(it, comment.toCommentRemote()).toComment() }
}
override suspend fun deleteComment(comment: Comment) {
comment.id?.let { service.deleteComment(it).toComment() }
}
override suspend fun getAllComments(): List<Comment> {
val existComms = dbCommentRepository.getAllComments().associateBy { it.id }.toMutableMap()
service.getAllComments()
.map { it.toComment() }
.forEach { comm ->
val existComm = existComms[comm.id]
if (existComm == null) {
dbCommentRepository.insertComment(comm)
} else if (existComm != comm) {
dbCommentRepository.updateComment(comm)
}
existComms[comm.id] = comm
}
return existComms.map { it.value }.sortedBy { it.id }
}
override suspend fun getCountComment(idArticle: Int?): Int? {
return idArticle?.let { service.getCountComment(it).size }
}
override suspend fun getUser(commId : Int) : User? =
commId?.let { service.getUser(it).toUser() }
override fun getComments(): Flow<PagingData<Comment>> {
val pagingSourceFactory = { dbCommentRepository.getAllCommentsPagingSource() }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = CommentRemoteMediator(
service,
dbCommentRepository,
dbRemoteKeyRepository,
dbArticleRepository,
database,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
}

View File

@ -0,0 +1,80 @@
package com.example.pmulabs.api.repository
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.mediator.TagRemoteMediator
import com.example.pmulabs.api.model.toTag
import com.example.pmulabs.api.model.toTagRemote
import com.example.pmulabs.room.AppContainer
import com.example.pmulabs.room.database.NewsPortalDatabase
import com.example.pmulabs.room.models.Tag
import com.example.pmulabs.room.repository.OfflineRemoteKeyRepository
import com.example.pmulabs.room.repository.OfflineTagRepository
import com.example.pmulabs.room.repository.TagRepository
import kotlinx.coroutines.flow.Flow
class RestTagRepository(
private val service: NewsPortalService,
private val dbTagRepository: OfflineTagRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: NewsPortalDatabase
) : TagRepository {
override suspend fun insertTag(tag: Tag) {
service.createTag(tag.toTagRemote()).toTag()
}
override suspend fun updateTag(tag: Tag) {
tag.id?.let { service.updateTag(it, tag.toTagRemote()).toTag() }
}
override suspend fun deleteTag(tag: Tag) {
tag.id?.let { service.deleteTag(it).toTag() }
}
override suspend fun getAllTags(): List<Tag> {
val existTags = dbTagRepository.getAllTags().associateBy { it.id }.toMutableMap()
service.getAllTags()
.map { it.toTag() }
.forEach { tag ->
val existTag = existTags[tag.id]
if (existTag == null) {
dbTagRepository.insertTag(tag)
} else if (existTag != tag) {
dbTagRepository.updateTag(tag)
}
existTags[tag.id] = tag
}
return existTags.map { it.value }.sortedBy { it.id }
}
override suspend fun getTagById(idTag: Int): Tag? =
idTag?.let { service.getTag(it).toTag() }
override suspend fun getTagByName(nameTag: String): Tag? =
nameTag?.let { service.getTagByName(it)[0].toTag() }
override fun getTags(): Flow<PagingData<Tag>> {
val pagingSourceFactory = { dbTagRepository.getAllTagsPagingSource() }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = TagRemoteMediator(
service,
dbTagRepository,
dbRemoteKeyRepository,
database,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
}

View File

@ -0,0 +1,119 @@
package com.example.pmulabs.api.repository
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.model.toArticle
import com.example.pmulabs.api.model.toComment
import com.example.pmulabs.api.model.toTag
import com.example.pmulabs.api.model.toUser
import com.example.pmulabs.api.model.toUserRemote
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.OfflineArticleRepository
import com.example.pmulabs.room.repository.OfflineCommentRepository
import com.example.pmulabs.room.repository.OfflineTagRepository
import com.example.pmulabs.room.repository.OfflineUserRepository
import com.example.pmulabs.room.repository.UserRepository
class RestUserRepository(
private val service: NewsPortalService,
private val dbUserRepository: OfflineUserRepository,
private val dbCommentRepository: OfflineCommentRepository,
private val dbTagRepository: OfflineTagRepository,
private val dbArticleRepository: OfflineArticleRepository
) : UserRepository {
override suspend fun insertUser(user: User) {
service.createUser(user.toUserRemote()).toUser()
}
override suspend fun updateUser(user: User) {
user.id?.let { service.updateUser(it, user.toUserRemote()).toUser() }
}
override suspend fun deleteUser(user: User) {
user.id?.let { service.deleteUser(it).toUser() }
}
override suspend fun getAllUsers(): List<User> {
val existUsers = dbUserRepository.getAllUsers().associateBy { it.id }.toMutableMap()
service.getUsers()
.map { it.toUser() }
.forEach { user ->
val existUser = existUsers[user.id]
if (existUser == null) {
dbUserRepository.insertUser(user)
} else if (existUser != user) {
dbUserRepository.updateUser(user)
}
existUsers[user.id] = user
}
return existUsers.map { it.value }.sortedBy { it.id }
}
override suspend fun getUserById(idUser: Int?): User? =
idUser?.let { service.getUser(it).toUser() }
override suspend fun getUserComms(idUser: Int): List<Comment> {
val existComments = dbUserRepository.getUserComms(idUser).associateBy { it.id }.toMutableMap()
service.getUserComments(idUser)
.map { it.toComment() }
.forEach { comm ->
if(comm.userId==idUser) {
val existComm = existComments[comm.id]
if (existComm == null) {
dbCommentRepository.insertComment(comm)
} else if (existComm != comm) {
dbCommentRepository.updateComment(comm)
}
existComments[comm.id] = comm
}
}
return existComments.map { it.value }.sortedBy { it.id }
}
override suspend fun getUserArticles(idUser: Int): List<Article> {
val existArticles = dbUserRepository.getUserArticles(idUser).associateBy { it.id }.toMutableMap()
service.getUserArticles(idUser)
.map { it.toArticle() }
.forEach { art ->
if(art.userId==idUser) {
val existArt = existArticles[art.id]
if (existArt == null) {
dbArticleRepository.insertArticle(art)
} else if (existArt != art) {
dbArticleRepository.updateArticle(art)
}
existArticles[art.id] = art
}
}
return existArticles.map { it.value }.sortedBy { it.id }
}
override suspend fun getUserTags(idUser: Int): List<Tag> {
val existTags = dbUserRepository.getUserTags(idUser).associateBy { it.id }.toMutableMap()
service.getUserTags(idUser)
.map { it.toTag() }
.forEach { tag ->
if(tag.userId==idUser) {
val existTag = existTags[tag.id]
if (existTag == null) {
dbTagRepository.insertTag(tag)
} else if (existTag != tag) {
dbTagRepository.updateTag(tag)
}
existTags[tag.id] = tag
}
}
return existTags.map { it.value }.sortedBy { it.id }
}
}

View File

@ -68,6 +68,7 @@ import com.example.pmulabs.room.models.Tag
import com.example.pmulabs.viewModels.AppViewModelProvider import com.example.pmulabs.viewModels.AppViewModelProvider
import com.example.pmulabs.viewModels.ArticleItemViewModel import com.example.pmulabs.viewModels.ArticleItemViewModel
import com.example.pmulabs.viewModels.CurrentUserViewModel import com.example.pmulabs.viewModels.CurrentUserViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -180,7 +181,7 @@ fun ArticleItem(navController: NavController,article: Article, modifier: Modifie
text= {Text(text=label)}, text= {Text(text=label)},
onClick = { selectedText = label onClick = { selectedText = label
coroutineScope.launch { coroutineScope.launch {
tagId= articleItemViewModel.getTagByName(selectedText).id!! tagId= articleItemViewModel.getTagByName(selectedText)?.id!!
} }
} }
) )
@ -242,16 +243,19 @@ fun ArticleItem(navController: NavController,article: Article, modifier: Modifie
), ),
onClick = { onClick = {
openDialog = false openDialog = false
article.text=text article.text = text
article.title=title article.title = title
tagName=selectedText tagName = selectedText
Log.d("Tag",tagId.toString()) Log.d("Tag", tagId.toString())
if(tagId!=null) { if (tagId != null) {
article.tagId = tagId article.tagId = tagId
} }
coroutineScope.launch { coroutineScope.launch {
val upResult = async {
articleItemViewModel.updateArticle(article) articleItemViewModel.updateArticle(article)
} }
upResult.await()
}
}, },
modifier = Modifier modifier = Modifier
.padding(start=100.dp) .padding(start=100.dp)

View File

@ -1,6 +1,7 @@
package com.example.pmulabs.designElem.items package com.example.pmulabs.designElem.items
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.util.Log
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -51,20 +52,12 @@ import kotlinx.coroutines.launch
@SuppressLint("CoroutineCreationDuringComposition", "UnrememberedMutableState") @SuppressLint("CoroutineCreationDuringComposition", "UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun CommentItem(navController: NavController,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, user : User, commentItemViewModel: CommentItemViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
var getUser by remember { mutableStateOf(currentUserViewModel.user) } var getUser by remember { mutableStateOf(currentUserViewModel.user) }
var openDialog by remember { mutableStateOf(false) } var openDialog by remember { mutableStateOf(false) }
var text by remember { mutableStateOf(comm.text) } var text by remember { mutableStateOf(comm.text) }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
commentItemViewModel.getUsersByArticle(comm.articleId)
var users by mutableStateOf(commentItemViewModel.userListByArticle)
var user by mutableStateOf<User?>(null)
for(item in users){
if(comm.userId==item.id){
user=item
}
}
if (openDialog) { if (openDialog) {
AlertDialog( AlertDialog(
@ -126,7 +119,7 @@ fun CommentItem(navController: NavController,modifier: Modifier = Modifier, comm
} }
}, },
modifier = Modifier modifier = Modifier
.padding(start=100.dp) .padding(start = 100.dp)
.fillMaxWidth(0.5f) .fillMaxWidth(0.5f)
.height(40.dp) .height(40.dp)
) { ) {
@ -150,8 +143,9 @@ fun CommentItem(navController: NavController,modifier: Modifier = Modifier, comm
.padding(start = 10.dp,top=10.dp) .padding(start = 10.dp,top=10.dp)
) { ) {
Row() { Row() {
Log.d("User",user.toString())
Text( Text(
text = "${user?.nickname}", text = "${user.nickname}",
color = Color(0xff423a99), color = Color(0xff423a99),
style = TextStyle( style = TextStyle(
textDecoration = TextDecoration.Underline, textDecoration = TextDecoration.Underline,
@ -161,17 +155,17 @@ fun CommentItem(navController: NavController,modifier: Modifier = Modifier, comm
) )
if(getUser?.id==comm.userId) { if(getUser?.id==comm.userId) {
Icon( Icon(
modifier=Modifier modifier= Modifier
.requiredHeight(20.dp) .requiredHeight(20.dp)
.clickable { .clickable {
openDialog=true openDialog = true
}, },
imageVector = Icons.Filled.Edit, imageVector = Icons.Filled.Edit,
contentDescription = "Update Icon", contentDescription = "Update Icon",
tint = Color(0xff423a99) tint = Color(0xff423a99)
) )
Icon( Icon(
modifier=Modifier modifier= Modifier
.requiredHeight(20.dp) .requiredHeight(20.dp)
.clickable { .clickable {
coroutineScope.launch { coroutineScope.launch {

View File

@ -1,38 +1,78 @@
package com.example.pmulabs.room package com.example.pmulabs.room
import android.content.Context import android.content.Context
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.repository.RestArticleRepository
import com.example.pmulabs.api.repository.RestCommentRepository
import com.example.pmulabs.api.repository.RestTagRepository
import com.example.pmulabs.api.repository.RestUserRepository
import com.example.pmulabs.room.database.NewsPortalDatabase import com.example.pmulabs.room.database.NewsPortalDatabase
import com.example.pmulabs.room.repository.ArticleRepository
import com.example.pmulabs.room.repository.CommentRepository
import com.example.pmulabs.room.repository.OfflineArticleRepository import com.example.pmulabs.room.repository.OfflineArticleRepository
import com.example.pmulabs.room.repository.OfflineCommentRepository import com.example.pmulabs.room.repository.OfflineCommentRepository
import com.example.pmulabs.room.repository.OfflineRemoteKeyRepository
import com.example.pmulabs.room.repository.OfflineTagRepository import com.example.pmulabs.room.repository.OfflineTagRepository
import com.example.pmulabs.room.repository.OfflineUserRepository import com.example.pmulabs.room.repository.OfflineUserRepository
import com.example.pmulabs.room.repository.TagRepository
import com.example.pmulabs.room.repository.UserRepository
interface AppContainer { interface AppContainer {
val userRepository: UserRepository val userRestRepository: RestUserRepository
val articleRepository: ArticleRepository val tagRestRepository: RestTagRepository
val tagRepository: TagRepository val articleRestRepository: RestArticleRepository
val commentRepository: CommentRepository val commentRestRepository: RestCommentRepository
companion object {
const val TIMEOUT = 5000L
const val LIMIT = 10
}
} }
class AppDataContainer(private val context: Context) : AppContainer { class AppDataContainer(private val context: Context) : AppContainer {
override val userRepository: UserRepository by lazy { private val userRepository: OfflineUserRepository by lazy {
OfflineUserRepository(NewsPortalDatabase.getInstance(context).userDao()) OfflineUserRepository(NewsPortalDatabase.getInstance(context).userDao())
} }
override val articleRepository: ArticleRepository by lazy { private val articleRepository: OfflineArticleRepository by lazy {
OfflineArticleRepository(NewsPortalDatabase.getInstance(context).articleDao()) OfflineArticleRepository(NewsPortalDatabase.getInstance(context).articleDao())
} }
override val tagRepository: TagRepository by lazy { private val tagRepository: OfflineTagRepository by lazy {
OfflineTagRepository(NewsPortalDatabase.getInstance(context).tagDao()) OfflineTagRepository(NewsPortalDatabase.getInstance(context).tagDao())
} }
override val commentRepository: CommentRepository by lazy { private val commentRepository: OfflineCommentRepository by lazy {
OfflineCommentRepository(NewsPortalDatabase.getInstance(context).commentDao()) OfflineCommentRepository(NewsPortalDatabase.getInstance(context).commentDao())
} }
private val remoteKeyRepository: OfflineRemoteKeyRepository by lazy {
companion object { OfflineRemoteKeyRepository(NewsPortalDatabase.getInstance(context).remoteKeysDao())
const val TIMEOUT = 5000L }
override val userRestRepository: RestUserRepository by lazy {
RestUserRepository(
NewsPortalService.getInstance(),
userRepository,
commentRepository,
tagRepository,
articleRepository
)
}
override val tagRestRepository: RestTagRepository by lazy {
RestTagRepository(
NewsPortalService.getInstance(),
tagRepository,
remoteKeyRepository,
NewsPortalDatabase.getInstance(context)
)
}
override val articleRestRepository: RestArticleRepository by lazy {
RestArticleRepository(
NewsPortalService.getInstance(),
articleRepository,
remoteKeyRepository,
tagRestRepository,
NewsPortalDatabase.getInstance(context)
)
}
override val commentRestRepository: RestCommentRepository by lazy {
RestCommentRepository(
NewsPortalService.getInstance(),
commentRepository,
remoteKeyRepository,
articleRestRepository,
NewsPortalDatabase.getInstance(context)
)
} }
} }

View File

@ -1,5 +1,6 @@
package com.example.pmulabs.room.dao package com.example.pmulabs.room.dao
import androidx.paging.PagingSource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
@ -11,20 +12,23 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface ArticleDao { interface ArticleDao {
@Query("select * from article") @Query("select * from article")
fun getAll(): Flow<List<Article>> fun getAll(): List<Article>
@Query("select * from article where article.id = :idArticle") @Query("select * from article where article.id = :idArticle")
fun getArticleById(idArticle: Int): Flow<Article> fun getArticleById(idArticle: Int): Flow<Article>
@Query("SELECT * FROM article ORDER BY id DESC LIMIT :limit OFFSET :offset") @Query("SELECT * FROM article ORDER BY id ASC")
suspend fun getArticles(limit: Int, offset: Int): List<Article> fun getArticles(): PagingSource<Int, Article>
@Insert @Insert
suspend fun insert(article: Article) suspend fun insert(vararg article: Article)
@Update @Update
suspend fun update(article: Article) suspend fun update(article: Article)
@Delete @Delete
suspend fun delete(article: Article) suspend fun delete(article: Article)
@Query("DELETE FROM tag")
suspend fun deleteAll()
} }

View File

@ -1,30 +1,38 @@
package com.example.pmulabs.room.dao package com.example.pmulabs.room.dao
import androidx.paging.PagingSource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Update import androidx.room.Update
import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.User
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface CommentDao { interface CommentDao {
@Query("select * from comment") @Query("select * from comment")
fun getAll(): Flow<List<Comment>> fun getAll(): List<Comment>
@Query("select COUNT(*) from comment WHERE comment.text!='' AND comment.article_id= :idArticle") @Query("select COUNT(*) from comment WHERE comment.text!='' AND comment.article_id= :idArticle")
fun getCountComment(idArticle : Int?) : Int fun getCountComment(idArticle : Int?) : Int
@Query("SELECT * FROM comment ORDER BY id DESC LIMIT :limit OFFSET :offset") @Query("SELECT * FROM comment ORDER BY id ASC")
suspend fun getComments(limit: Int, offset: Int): List<Comment> fun getComments(): PagingSource<Int, Comment>
@Query("SELECT * FROM user WHERE user.id=:commId")
fun getUser(commId:Int): Flow<User>
@Insert @Insert
suspend fun insert(comment: Comment) suspend fun insert(vararg comment: Comment)
@Update @Update
suspend fun update(comment: Comment) suspend fun update(comment: Comment)
@Delete @Delete
suspend fun delete(comment: Comment) suspend fun delete(comment: Comment)
@Query("DELETE FROM tag")
suspend fun deleteAll()
} }

View File

@ -0,0 +1,20 @@
package com.example.pmulabs.room.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.pmulabs.room.models.RemoteKeyType
import com.example.pmulabs.room.models.RemoteKeys
@Dao
interface RemoteKeysDao {
@Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type")
suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys?>)
@Query("DELETE FROM remote_keys WHERE type = :type")
suspend fun clearRemoteKeys(type: RemoteKeyType)
}

View File

@ -1,5 +1,6 @@
package com.example.pmulabs.room.dao package com.example.pmulabs.room.dao
import androidx.paging.PagingSource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
@ -11,7 +12,7 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface TagDao { interface TagDao {
@Query("select * from tag") @Query("select * from tag")
fun getAll(): Flow<List<Tag>> fun getAll(): List<Tag>
@Query("select * from tag where tag.id = :idTag") @Query("select * from tag where tag.id = :idTag")
fun getTagById(idTag: Int): Flow<Tag> fun getTagById(idTag: Int): Flow<Tag>
@ -19,15 +20,18 @@ interface TagDao {
@Query("select * from tag where tag.title = :nameTag") @Query("select * from tag where tag.title = :nameTag")
fun getTagByName(nameTag: String): Flow<Tag> fun getTagByName(nameTag: String): Flow<Tag>
@Query("SELECT * FROM tag ORDER BY id ASC LIMIT :limit OFFSET :offset") @Query("SELECT * FROM tag ORDER BY id ASC")
suspend fun getTags(limit: Int, offset: Int): List<Tag> fun getTags(): PagingSource<Int, Tag>
@Insert @Insert
suspend fun insert(tag: Tag) suspend fun insert(vararg tag: Tag)
@Update @Update
suspend fun update(tag: Tag) suspend fun update(tag: Tag)
@Delete @Delete
suspend fun delete(tag: Tag) suspend fun delete(tag: Tag)
@Query("DELETE FROM tag")
suspend fun deleteAll()
} }

View File

@ -14,19 +14,19 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface UserDao { interface UserDao {
@Query("select * from user") @Query("select * from user")
fun getAll(): Flow<List<User>> suspend fun getAll(): List<User>
@Query("select * from user where user.id = :idUser") @Query("select * from user where user.id = :idUser")
fun getUserById(idUser: Int?): Flow<User> fun getUserById(idUser: Int?): Flow<User>
@Query("select * from comment WHERE comment.text!='' AND comment.user_id= :idUser") @Query("select * from comment WHERE comment.text!='' AND comment.user_id= :idUser")
fun getUserComms(idUser: Int): Flow<List<Comment>> suspend fun getUserComms(idUser: Int): List<Comment>
@Query("select * from article WHERE article.text!='' AND article.user_id= :idUser") @Query("select * from article WHERE article.text!='' AND article.user_id= :idUser")
fun getUserArticles(idUser: Int): Flow<List<Article>> suspend fun getUserArticles(idUser: Int): List<Article>
@Query("select * from tag WHERE tag.title!='' AND tag.user_id= :idUser") @Query("select * from tag WHERE tag.title!='' AND tag.user_id= :idUser")
fun getUserTags(idUser: Int): Flow<List<Tag>> suspend fun getUserTags(idUser: Int): List<Tag>
@Insert @Insert
suspend fun insert(user: User) suspend fun insert(user: User)

View File

@ -4,30 +4,29 @@ import android.content.Context
import androidx.room.Database import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.pmulabs.room.dao.ArticleDao import com.example.pmulabs.room.dao.ArticleDao
import com.example.pmulabs.room.dao.CommentDao import com.example.pmulabs.room.dao.CommentDao
import com.example.pmulabs.room.dao.RemoteKeysDao
import com.example.pmulabs.room.dao.TagDao import com.example.pmulabs.room.dao.TagDao
import com.example.pmulabs.room.dao.UserDao import com.example.pmulabs.room.dao.UserDao
import com.example.pmulabs.room.models.Article import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.RemoteKeys
import com.example.pmulabs.room.models.Tag import com.example.pmulabs.room.models.Tag
import com.example.pmulabs.room.models.User import com.example.pmulabs.room.models.User
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.Date import java.util.Date
@Database(entities = [User::class, Tag::class, Comment::class, Article::class], version = 5, exportSchema = false) @Database(entities = [RemoteKeys::class, User::class, Tag::class, Comment::class, Article::class], version = 5, exportSchema = false)
abstract class NewsPortalDatabase : RoomDatabase() { abstract class NewsPortalDatabase : RoomDatabase() {
abstract fun userDao(): UserDao abstract fun userDao(): UserDao
abstract fun tagDao(): TagDao abstract fun tagDao(): TagDao
abstract fun commentDao(): CommentDao abstract fun commentDao(): CommentDao
abstract fun articleDao(): ArticleDao abstract fun articleDao(): ArticleDao
abstract fun remoteKeysDao(): RemoteKeysDao
companion object { companion object {
private const val DB_NAME: String = "news-portal" private const val DB_NAME: String = "newsportaldb"
@Volatile @Volatile
private var INSTANCE: NewsPortalDatabase? = null private var INSTANCE: NewsPortalDatabase? = null
@ -41,12 +40,12 @@ abstract class NewsPortalDatabase : RoomDatabase() {
userDao.insert(user2) userDao.insert(user2)
userDao.insert(user3) userDao.insert(user3)
val tagDao = database.tagDao() val tagDao = database.tagDao()
val tag1 = Tag(1, "Тег_1",2) val tag1 = Tag(id=1, title = "Тег_1", userId = 2)
val tag2 = Tag(2, "Тег_2",1) val tag2 = Tag(id=2, title ="Тег_2",userId = 1)
val tag3 = Tag(3, "Тег_3",3) val tag3 = Tag(id=3, title ="Тег_3",userId = 3)
val tag4 = Tag(4, "Тег_4",2) val tag4 = Tag(id=4, title ="Тег_4",userId = 2)
val tag5 = Tag(5, "Тег_5",1) val tag5 = Tag(id=5, title ="Тег_5",userId = 1)
val tag6 = Tag(6, "Тег_6",3) val tag6 = Tag(id=6, title ="Тег_6",userId = 3)
tagDao.insert(tag1) tagDao.insert(tag1)
tagDao.insert(tag2) tagDao.insert(tag2)
tagDao.insert(tag3) tagDao.insert(tag3)
@ -103,14 +102,14 @@ abstract class NewsPortalDatabase : RoomDatabase() {
NewsPortalDatabase::class.java, NewsPortalDatabase::class.java,
DB_NAME DB_NAME
) )
.addCallback(object : Callback() { /*.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) { override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db) super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
populateDatabase() populateDatabase()
} }
} }
}) })*/
.build() .build()
.also { INSTANCE = it } .also { INSTANCE = it }
} }

View File

@ -0,0 +1,27 @@
package com.example.pmulabs.room.models
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
enum class RemoteKeyType(private val type: String) {
ARTICLE(Article::class.simpleName ?: "Article"),
TAG(Tag::class.simpleName ?: "Tag"),
COMMENT(Comment::class.simpleName ?: "Comment");
@TypeConverter
fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value }
@TypeConverter
fun fromRemoteKeyType(value: RemoteKeyType) = value.type
}
@Entity(tableName = "remote_keys")
data class RemoteKeys(
@PrimaryKey val entityId: Int,
@TypeConverters(RemoteKeyType::class)
val type: RemoteKeyType,
val prevKey: Int?,
val nextKey: Int?
)

View File

@ -10,10 +10,10 @@ import androidx.room.PrimaryKey
foreignKeys = [ForeignKey(entity = User::class, parentColumns = ["id"], childColumns = ["user_id"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE)], foreignKeys = [ForeignKey(entity = User::class, parentColumns = ["id"], childColumns = ["user_id"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE)],
indices = [Index(value = ["title"], unique = true)]) indices = [Index(value = ["title"], unique = true)])
data class Tag( data class Tag(
@PrimaryKey(autoGenerate = true)
val id: Int?,
@ColumnInfo(name = "title") @ColumnInfo(name = "title")
var title: String, var title: String,
@PrimaryKey(autoGenerate = true)
val id: Int?,
@ColumnInfo(name = "user_id") @ColumnInfo(name = "user_id")
val userId: Int val userId: Int
) { ) {

View File

@ -1,38 +0,0 @@
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<Int, Article>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {
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, Article>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}

View File

@ -1,38 +0,0 @@
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<Int, Comment>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Comment> {
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, Comment>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}

View File

@ -1,38 +0,0 @@
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<Int, Tag>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Tag> {
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, Tag>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}

View File

@ -8,7 +8,7 @@ interface ArticleRepository {
suspend fun insertArticle(article: Article) suspend fun insertArticle(article: Article)
suspend fun updateArticle(article: Article) suspend fun updateArticle(article: Article)
suspend fun deleteArticle(article: Article) suspend fun deleteArticle(article: Article)
fun getAllArticles(): Flow<List<Article>> suspend fun getAllArticles(): List<Article>
fun getArticleById(idArticle: Int): Flow<Article> suspend fun getArticleById(idArticle: Int): Article?
fun getArticles(): Flow<PagingData<Article>> fun getArticles(): Flow<PagingData<Article>>
} }

View File

@ -2,13 +2,15 @@ package com.example.pmulabs.room.repository
import androidx.paging.PagingData import androidx.paging.PagingData
import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.User
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface CommentRepository { interface CommentRepository {
suspend fun insertComment(comment: Comment) suspend fun insertComment(comment: Comment)
suspend fun updateComment(comment: Comment) suspend fun updateComment(comment: Comment)
suspend fun deleteComment(comment: Comment) suspend fun deleteComment(comment: Comment)
fun getAllComments(): Flow<List<Comment>> suspend fun getAllComments(): List<Comment>
fun getCountComment(idArticle : Int?) : Int suspend fun getCountComment(idArticle : Int?) : Int?
fun getComments(): Flow<PagingData<Comment>> fun getComments(): Flow<PagingData<Comment>>
suspend fun getUser(commId: Int): User?
} }

View File

@ -3,17 +3,29 @@ package com.example.pmulabs.room.repository
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.pmulabs.room.dao.ArticleDao import com.example.pmulabs.room.dao.ArticleDao
import com.example.pmulabs.room.models.Article import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.pagingSource.ArticlePagingSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
class OfflineArticleRepository(private val articleDao: ArticleDao) : ArticleRepository { class OfflineArticleRepository(private val articleDao: ArticleDao) : ArticleRepository {
override suspend fun insertArticle(article: Article) = articleDao.insert(article) override suspend fun insertArticle(article: Article) = articleDao.insert(article)
override suspend fun updateArticle(article: Article) = articleDao.update(article) override suspend fun updateArticle(article: Article) = articleDao.update(article)
override suspend fun deleteArticle(article: Article) = articleDao.delete(article) override suspend fun deleteArticle(article: Article) = articleDao.delete(article)
override fun getAllArticles(): Flow<List<Article>> = articleDao.getAll() override suspend fun getAllArticles(): List<Article> = articleDao.getAll()
override fun getArticleById(idArticle: Int): Flow<Article> = articleDao.getArticleById(idArticle) override suspend fun getArticleById(idArticle: Int): Article? = articleDao.getArticleById(idArticle).first()
override fun getArticles(): Flow<PagingData<Article>> = Pager(config = PagingConfig(pageSize = 4, jumpThreshold = 4, initialLoadSize = 4) ){ ArticlePagingSource(articleDao) }.flow override fun getArticles(): Flow<PagingData<Article>> = Pager(
config = PagingConfig(
pageSize = 4,
enablePlaceholders = false
),
pagingSourceFactory = articleDao::getArticles
).flow
fun getAllArticlesPagingSource(): PagingSource<Int, Article> = articleDao.getArticles()
suspend fun clearArticles() = articleDao.deleteAll()
suspend fun insertArticles(articles: List<Article>) =
articleDao.insert(*articles.toTypedArray())
} }

View File

@ -3,17 +3,34 @@ package com.example.pmulabs.room.repository
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.pmulabs.room.dao.CommentDao import com.example.pmulabs.room.dao.CommentDao
import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.pagingSource.CommentPagingSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
class OfflineCommentRepository(private val commentDao: CommentDao) : CommentRepository { class OfflineCommentRepository(private val commentDao: CommentDao) : CommentRepository {
override suspend fun insertComment(comment: Comment) = commentDao.insert(comment) override suspend fun insertComment(comment: Comment) = commentDao.insert(comment)
override suspend fun updateComment(comment: Comment) = commentDao.update(comment) override suspend fun updateComment(comment: Comment) = commentDao.update(comment)
override suspend fun deleteComment(comment: Comment) = commentDao.delete(comment) override suspend fun deleteComment(comment: Comment) = commentDao.delete(comment)
override fun getAllComments(): Flow<List<Comment>> = commentDao.getAll() override suspend fun getAllComments(): List<Comment> = commentDao.getAll()
override fun getCountComment(idArticle : Int?) : Int = commentDao.getCountComment(idArticle) override suspend fun getCountComment(idArticle : Int?) : Int = commentDao.getCountComment(idArticle)
override fun getComments(): Flow<PagingData<Comment>> = Pager(config = PagingConfig(pageSize = 4, jumpThreshold = 4, initialLoadSize = 4) ){ CommentPagingSource(commentDao) }.flow override fun getComments(): Flow<PagingData<Comment>> = Pager(
config = PagingConfig(
pageSize = 20,
enablePlaceholders = false
),
pagingSourceFactory =commentDao::getComments
).flow
fun getAllCommentsPagingSource(): PagingSource<Int, Comment> = commentDao.getComments()
suspend fun clearComments() = commentDao.deleteAll()
override suspend fun getUser(commId: Int) = commentDao.getUser(commId).first()
suspend fun insertComments(comments: List<Comment>) {
commentDao.insert(*comments.toTypedArray())
}
} }

View File

@ -0,0 +1,16 @@
package com.example.pmulabs.room.repository
import com.example.pmulabs.room.dao.RemoteKeysDao
import com.example.pmulabs.room.models.RemoteKeyType
import com.example.pmulabs.room.models.RemoteKeys
class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository {
override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) =
remoteKeysDao.getRemoteKeys(id, type)
override suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys?>) =
remoteKeysDao.insertAll(remoteKeys)
override suspend fun deleteRemoteKey(type: RemoteKeyType) =
remoteKeysDao.clearRemoteKeys(type)
}

View File

@ -3,18 +3,32 @@ package com.example.pmulabs.room.repository
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.pmulabs.room.AppContainer
import com.example.pmulabs.room.dao.TagDao import com.example.pmulabs.room.dao.TagDao
import com.example.pmulabs.room.models.Tag import com.example.pmulabs.room.models.Tag
import com.example.pmulabs.room.pagingSource.TagPagingSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
class OfflineTagRepository(private val tagDao: TagDao) : TagRepository { class OfflineTagRepository(private val tagDao: TagDao) : TagRepository {
override suspend fun insertTag(tag: Tag) = tagDao.insert(tag) override suspend fun insertTag(tag: Tag) = tagDao.insert(tag)
override suspend fun updateTag(tag: Tag) = tagDao.update(tag) override suspend fun updateTag(tag: Tag) = tagDao.update(tag)
override suspend fun deleteTag(tag: Tag) = tagDao.delete(tag) override suspend fun deleteTag(tag: Tag) = tagDao.delete(tag)
override fun getAllTags(): Flow<List<Tag>> = tagDao.getAll() override suspend fun getAllTags(): List<Tag> = tagDao.getAll()
override fun getTagById(idTag: Int): Flow<Tag> = tagDao.getTagById(idTag) override suspend fun getTagById(idTag: Int): Tag? = tagDao.getTagById(idTag).first()
override fun getTagByName(nameTag: String): Flow<Tag> = tagDao.getTagByName(nameTag) override suspend fun getTagByName(nameTag: String): Tag? = tagDao.getTagByName(nameTag).first()
override fun getTags(): Flow<PagingData<Tag>> = Pager(config = PagingConfig(pageSize = 4, jumpThreshold = 4, initialLoadSize = 4) ){ TagPagingSource(tagDao) }.flow override fun getTags(): Flow<PagingData<Tag>> = Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = tagDao::getTags
).flow
fun getAllTagsPagingSource(): PagingSource<Int, Tag> = tagDao.getTags()
suspend fun clearTags() = tagDao.deleteAll()
suspend fun insertTags(tags: List<Tag>) =
tagDao.insert(*tags.toTypedArray())
} }

View File

@ -5,15 +5,15 @@ import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.Tag import com.example.pmulabs.room.models.Tag
import com.example.pmulabs.room.models.User import com.example.pmulabs.room.models.User
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first
class OfflineUserRepository(private val userDao: UserDao) : UserRepository { class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
override suspend fun insertUser(user: User) = userDao.insert(user) override suspend fun insertUser(user: User) = userDao.insert(user)
override suspend fun updateUser(user: User) = userDao.update(user) override suspend fun updateUser(user: User) = userDao.update(user)
override suspend fun deleteUser(user: User) = userDao.delete(user) override suspend fun deleteUser(user: User) = userDao.delete(user)
override fun getAllUsers(): Flow<List<User>> = userDao.getAll() override suspend fun getAllUsers(): List<User> = userDao.getAll()
override fun getUserById(idUser: Int?): Flow<User> = userDao.getUserById(idUser) override suspend fun getUserById(idUser: Int?): User? = userDao.getUserById(idUser).first()
override fun getUserComms(idUser: Int): Flow<List<Comment>> = userDao.getUserComms(idUser) override suspend fun getUserComms(idUser: Int): List<Comment> = userDao.getUserComms(idUser)
override fun getUserArticles(idUser: Int): Flow<List<Article>> = userDao.getUserArticles(idUser) override suspend fun getUserArticles(idUser: Int): List<Article> = userDao.getUserArticles(idUser)
override fun getUserTags(idUser: Int): Flow<List<Tag>> = userDao.getUserTags(idUser) override suspend fun getUserTags(idUser: Int): List<Tag> = userDao.getUserTags(idUser)
} }

View File

@ -0,0 +1,10 @@
package com.example.pmulabs.room.repository
import com.example.pmulabs.room.models.RemoteKeyType
import com.example.pmulabs.room.models.RemoteKeys
interface RemoteKeyRepository {
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys?>)
suspend fun deleteRemoteKey(type: RemoteKeyType)
}

View File

@ -8,8 +8,8 @@ interface TagRepository {
suspend fun insertTag(tag: Tag) suspend fun insertTag(tag: Tag)
suspend fun updateTag(tag: Tag) suspend fun updateTag(tag: Tag)
suspend fun deleteTag(tag: Tag) suspend fun deleteTag(tag: Tag)
fun getAllTags(): Flow<List<Tag>> suspend fun getAllTags(): List<Tag>
fun getTagById(idTag: Int): Flow<Tag> suspend fun getTagById(idTag: Int): Tag?
fun getTagByName(nameTag: String): Flow<Tag> suspend fun getTagByName(nameTag: String): Tag?
fun getTags(): Flow<PagingData<Tag>> fun getTags(): Flow<PagingData<Tag>>
} }

View File

@ -4,15 +4,14 @@ import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.Tag import com.example.pmulabs.room.models.Tag
import com.example.pmulabs.room.models.User import com.example.pmulabs.room.models.User
import kotlinx.coroutines.flow.Flow
interface UserRepository { interface UserRepository {
suspend fun insertUser(user: User) suspend fun insertUser(user: User)
suspend fun updateUser(user: User) suspend fun updateUser(user: User)
suspend fun deleteUser(user: User) suspend fun deleteUser(user: User)
fun getAllUsers(): Flow<List<User>> suspend fun getAllUsers(): List<User>
fun getUserById(idUser: Int?): Flow<User> suspend fun getUserById(idUser: Int?): User?
fun getUserComms(idUser: Int): Flow<List<Comment>> suspend fun getUserComms(idUser: Int): List<Comment>
fun getUserArticles(idUser: Int): Flow<List<Article>> suspend fun getUserArticles(idUser: Int): List<Article>
fun getUserTags(idUser: Int): Flow<List<Tag>> suspend fun getUserTags(idUser: Int): List<Tag>
} }

View File

@ -65,7 +65,7 @@ import java.util.Date
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifier,articlePageScreenViewModel: ArticlePageScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) { fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifier,articlePageScreenViewModel: ArticlePageScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
articlePageScreenViewModel.setUserList()
var id = var id =
navController.currentBackStackEntry?.arguments?.getString(ARTICLE_ARGUMENT_KEY).toString() navController.currentBackStackEntry?.arguments?.getString(ARTICLE_ARGUMENT_KEY).toString()
try { try {
@ -89,6 +89,8 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie
val publishDate = formatter.format(Date(article?.publishDate ?: 0)) val publishDate = formatter.format(Date(article?.publishDate ?: 0))
val comms = articlePageScreenViewModel.comments.collectAsLazyPagingItems() val comms = articlePageScreenViewModel.comments.collectAsLazyPagingItems()
var users = mutableStateOf( articlePageScreenViewModel.userList)
Log.d("ListOfUsers", users.toString())
var getUser by remember { mutableStateOf(currentUserViewModel.user) } var getUser by remember { mutableStateOf(currentUserViewModel.user) }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@ -257,6 +259,7 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie
items(count = comms.itemCount) { index -> items(count = comms.itemCount) { index ->
val comm = comms[index] val comm = comms[index]
val user = comm?.userId?.let { users.value.get(it-1) }!!
if (comm != null) { if (comm != null) {
if (comm.articleId == article?.id && comm.text != "") { if (comm.articleId == article?.id && comm.text != "") {
Spacer(modifier = Modifier.padding(0.dp)) Spacer(modifier = Modifier.padding(0.dp))
@ -265,7 +268,7 @@ fun ArticlePageScreen(navController: NavController, modifier: Modifier = Modifie
modifier = Modifier modifier = Modifier
.border(BorderStroke(3.dp, Color(0xff423a99))), color = Color(0xff423a99) .border(BorderStroke(3.dp, Color(0xff423a99))), color = Color(0xff423a99)
) )
CommentItem(navController,comm = comm, currentUserViewModel = currentUserViewModel) CommentItem(navController,comm = comm,user=user, currentUserViewModel = currentUserViewModel)
} }
} }
} }

View File

@ -65,7 +65,9 @@ fun MainScreen(navController: NavController, modifier: Modifier = Modifier, arti
items(count = articles.itemCount) { index -> items(count = articles.itemCount) { index ->
val article = articles[index] val article = articles[index]
if (article != null) { if (article != null) {
ArticleItem(navController=navController,article = article, currentUserViewModel = currentUserViewModel, onArticleClick = {navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))}) ArticleItem(navController=navController,article = article, currentUserViewModel = currentUserViewModel, onArticleClick = {
navController.navigate(BottomBarScreen.ArticlePage.passId(article.id.toString()))
})
} }
} }
articles.apply { articles.apply {

View File

@ -30,7 +30,6 @@ import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonColors
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@ -52,7 +51,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
@ -63,10 +61,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize import androidx.compose.ui.unit.toSize
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.paging.LoadState
import androidx.paging.compose.collectAsLazyPagingItems
import com.example.pmulabs.R 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.ValidateEmail
import com.example.pmulabs.designElem.elem.isValidEmail import com.example.pmulabs.designElem.elem.isValidEmail
import com.example.pmulabs.room.models.Article import com.example.pmulabs.room.models.Article
@ -83,15 +78,14 @@ import java.util.Date
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
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)) { 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
articleScreenViewModel.setArticleList()
var getUser by remember { mutableStateOf(currentUserViewModel.user) } var getUser by remember { mutableStateOf(currentUserViewModel.user) }
var openDialogUser by remember { mutableStateOf(false) } var openDialogUser by remember { mutableStateOf(false) }
var email by remember { mutableStateOf(getUser?.email) } var email by remember { mutableStateOf(getUser?.email) }
var password by remember { mutableStateOf(getUser?.password) } var password by remember { mutableStateOf(getUser?.password) }
var nickname by remember { mutableStateOf(getUser?.nickname) } var nickname by remember { mutableStateOf(getUser?.nickname) }
val getArticles = articleScreenViewModel.articles.collectAsLazyPagingItems() val getArticles = articleScreenViewModel.getArticles
val getComms = articlePageScreenViewModel.comments.collectAsLazyPagingItems()
val getTags = tagItemViewModel.tags.collectAsLazyPagingItems()
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
articlePageScreenViewModel.setTagList() articlePageScreenViewModel.setTagList()
@ -178,7 +172,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
selectedText = label selectedText = label
coroutineScope.launch { coroutineScope.launch {
tagId = tagId =
articlePageScreenViewModel.getTagByName(selectedText).id!! articlePageScreenViewModel.getTagByName(selectedText)?.id!!
} }
} }
) )
@ -523,159 +517,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
} }
} }
} }
item {
Box(
modifier = Modifier
.offset(
x = 9.dp,
y = 220.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 182.dp)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 27.1240234375.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 155.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 155.dp)
.clip(shape = RoundedCornerShape(5.dp))
.background(color = Color.White)
.border(
border = BorderStroke(3.dp, Color(0xffdbdbf1)),
shape = RoundedCornerShape(5.dp)
)
)
LazyColumn(
contentPadding = PaddingValues(
top = 28.dp,
bottom = 0.dp,
start = 10.dp,
end = 10.dp
),
verticalArrangement = Arrangement.spacedBy(1.dp)
) {
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)
)
}
}
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 {
item {
Text(
text = "Комментариев пока нет!",
color = Color(0xff423a99),
style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.align(alignment = Alignment.TopStart)
)
}
}
}
}
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 54.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 54.dp)
.clip(shape = RoundedCornerShape(15.dp))
.background(color = Color(0xff423a99))
)
Text(
text = "Comments:",
color = Color.White,
style = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 24.dp,
y = 0.dp
)
.requiredWidth(width = 210.dp)
.requiredHeight(height = 54.dp)
.wrapContentHeight(align = Alignment.CenterVertically)
)
}
}
}
item { item {
Spacer(modifier = Modifier.padding(10.dp)) Spacer(modifier = Modifier.padding(10.dp))
Box( Box(
@ -685,7 +527,7 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
y = 220.dp y = 220.dp
) )
.requiredWidth(width = 393.dp) .requiredWidth(width = 393.dp)
.requiredHeight(height = 182.dp) .requiredHeight(height = 282.dp)
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@ -695,12 +537,12 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
y = 27.1240234375.dp y = 27.1240234375.dp
) )
.requiredWidth(width = 393.dp) .requiredWidth(width = 393.dp)
.requiredHeight(height = 155.dp) .requiredHeight(height = 255.dp)
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.requiredWidth(width = 393.dp) .requiredWidth(width = 393.dp)
.requiredHeight(height = 155.dp) .requiredHeight(height = 255.dp)
.clip(shape = RoundedCornerShape(5.dp)) .clip(shape = RoundedCornerShape(5.dp))
.background(color = Color.White) .background(color = Color.White)
.border( .border(
@ -718,8 +560,8 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
), ),
verticalArrangement = Arrangement.spacedBy(1.dp) verticalArrangement = Arrangement.spacedBy(1.dp)
) { ) {
if (getArticles.itemCount != 0) { if (getArticles.size != 0) {
items(count = getArticles.itemCount) { index -> items(count = getArticles.size) { index ->
val article = getArticles[index] val article = getArticles[index]
if (article?.userId == getUser?.id) { if (article?.userId == getUser?.id) {
Spacer(modifier = Modifier.padding(5.dp)) Spacer(modifier = Modifier.padding(5.dp))
@ -732,13 +574,13 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
), ),
modifier = Modifier modifier = Modifier
.align(alignment = Alignment.TopStart) .align(alignment = Alignment.TopStart)
.clickable { /*.clickable {
navController.navigate( navController.navigate(
BottomBarScreen.ArticlePage.passId( BottomBarScreen.ArticlePage.passId(
article?.id.toString() article?.id.toString()
) )
) )
} }*/
) )
Spacer(modifier = Modifier.padding(5.dp)) Spacer(modifier = Modifier.padding(5.dp))
HorizontalDivider( HorizontalDivider(
@ -749,37 +591,6 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
) )
} }
} }
getArticles.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 = 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 { item {
Text( Text(
@ -830,129 +641,6 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
} }
} }
} }
item {
Spacer(modifier = Modifier.padding(10.dp))
Box(
modifier = Modifier
.offset(
x = 9.dp,
y = 220.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 182.dp)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 27.1240234375.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 155.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 155.dp)
.clip(shape = RoundedCornerShape(5.dp))
.background(color = Color.White)
.border(
border = BorderStroke(3.dp, Color(0xffdbdbf1)),
shape = RoundedCornerShape(5.dp)
)
)
LazyColumn(
contentPadding = PaddingValues(
top = 28.dp,
bottom = 0.dp,
start = 10.dp,
end = 10.dp
),
verticalArrangement = Arrangement.spacedBy(1.dp)
) {
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 {
item {
Text(
text = "Тэгов пока нет!",
color = Color(0xff423a99),
style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.align(alignment = Alignment.TopStart)
)
}
}
}
}
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 54.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 54.dp)
.clip(shape = RoundedCornerShape(15.dp))
.background(color = Color(0xff423a99))
)
Text(
text = "Tags:",
color = Color.White,
style = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 24.dp,
y = 0.dp
)
.requiredWidth(width = 210.dp)
.requiredHeight(height = 54.dp)
.wrapContentHeight(align = Alignment.CenterVertically)
)
}
}
}
} }
} }

View File

@ -117,7 +117,7 @@ fun TagsScreen(navController: NavController, modifier: Modifier = Modifier, tagI
onClick = { onClick = {
openDialog = false openDialog = false
if(text.isNotEmpty()) { if(text.isNotEmpty()) {
val newTag = Tag(null, text, getUser?.id.toString().toInt()) val newTag = Tag(text, null, getUser?.id.toString().toInt())
coroutineScope.launch { coroutineScope.launch {
tagItemViewModel.insertTag(newTag) tagItemViewModel.insertTag(newTag)
} }

View File

@ -9,28 +9,28 @@ import com.example.pmulabs.NewsPortalApplication
object AppViewModelProvider { object AppViewModelProvider {
val Factory = viewModelFactory { val Factory = viewModelFactory {
initializer { initializer {
CurrentUserViewModel(newsPortalApplication().container.userRepository) CurrentUserViewModel(newsPortalApplication().container.userRestRepository)
} }
initializer { initializer {
TagItemViewModel(newsPortalApplication().container.tagRepository) TagItemViewModel(newsPortalApplication().container.tagRestRepository)
} }
initializer { initializer {
CommentItemViewModel(newsPortalApplication().container.userRepository,newsPortalApplication().container.commentRepository) CommentItemViewModel(newsPortalApplication().container.userRestRepository,newsPortalApplication().container.commentRestRepository)
} }
initializer { initializer {
ArticleItemViewModel(newsPortalApplication().container.tagRepository,newsPortalApplication().container.commentRepository,newsPortalApplication().container.articleRepository) ArticleItemViewModel(newsPortalApplication().container.tagRestRepository,newsPortalApplication().container.commentRestRepository,newsPortalApplication().container.articleRestRepository)
} }
initializer { initializer {
EntryScreenViewModel(newsPortalApplication().container.userRepository) EntryScreenViewModel(newsPortalApplication().container.userRestRepository)
} }
initializer { initializer {
RegisterScreenViewModel(newsPortalApplication().container.userRepository) RegisterScreenViewModel(newsPortalApplication().container.userRestRepository)
} }
initializer { initializer {
ArticleScreenViewModel(newsPortalApplication().container.articleRepository) ArticleScreenViewModel(newsPortalApplication().container.articleRestRepository)
} }
initializer { initializer {
ArticlePageScreenViewModel(newsPortalApplication().container.tagRepository,newsPortalApplication().container.commentRepository,newsPortalApplication().container.articleRepository,newsPortalApplication().container.userRepository) ArticlePageScreenViewModel(newsPortalApplication().container.tagRestRepository,newsPortalApplication().container.commentRestRepository,newsPortalApplication().container.articleRestRepository,newsPortalApplication().container.userRestRepository)
} }
} }
} }

View File

@ -11,7 +11,6 @@ import com.example.pmulabs.room.repository.ArticleRepository
import com.example.pmulabs.room.repository.CommentRepository import com.example.pmulabs.room.repository.CommentRepository
import com.example.pmulabs.room.repository.TagRepository import com.example.pmulabs.room.repository.TagRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -22,8 +21,8 @@ class ArticleItemViewModel(
) : ViewModel() { ) : ViewModel() {
var tagList by mutableStateOf<List<Tag>>(emptyList()) var tagList by mutableStateOf<List<Tag>>(emptyList())
fun setTagList() { fun setTagList() {
viewModelScope.launch { viewModelScope.launch(Dispatchers.IO) {
tagList=tagRepository.getAllTags().first() tagList=tagRepository.getAllTags()
} }
} }
@ -35,11 +34,11 @@ class ArticleItemViewModel(
articleRepository.deleteArticle(article) articleRepository.deleteArticle(article)
} }
suspend fun getTagByName(name: String) : Tag { suspend fun getTagByName(name: String) : Tag? {
return tagRepository.getTagByName(name).first() return tagRepository.getTagByName(name)
} }
suspend fun getCount(articleId: Int?): Int = withContext(Dispatchers.IO) { suspend fun getCount(articleId: Int?): Int? = withContext(Dispatchers.IO) {
commentRepository.getCountComment(articleId) commentRepository.getCountComment(articleId)
} }

View File

@ -14,9 +14,8 @@ import com.example.pmulabs.room.repository.ArticleRepository
import com.example.pmulabs.room.repository.CommentRepository import com.example.pmulabs.room.repository.CommentRepository
import com.example.pmulabs.room.repository.TagRepository import com.example.pmulabs.room.repository.TagRepository
import com.example.pmulabs.room.repository.UserRepository import com.example.pmulabs.room.repository.UserRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ArticlePageScreenViewModel( class ArticlePageScreenViewModel(
@ -31,19 +30,31 @@ class ArticlePageScreenViewModel(
var tag by mutableStateOf<Tag?>(null) var tag by mutableStateOf<Tag?>(null)
val comments: Flow<PagingData<Comment>> = commentRepository.getComments() val comments: Flow<PagingData<Comment>> = commentRepository.getComments()
var getComments by mutableStateOf<List<Comment>>(emptyList())
fun setCommentList() {
viewModelScope.launch(Dispatchers.IO) {
getComments=commentRepository.getAllComments()
}
}
fun getArticleById(articleId:Int) { fun getArticleById(articleId:Int) {
articleid.value = articleId articleid.value = articleId
viewModelScope.launch { viewModelScope.launch {
article = articleRepository.getArticleById(articleid.value!!) article = articleRepository.getArticleById(articleid.value!!)
.filterNotNull() }
.first() }
var userList by mutableStateOf<List<User>>(emptyList())
fun setUserList() {
viewModelScope.launch(Dispatchers.IO) {
userList=userRepository.getAllUsers()
} }
} }
var tagList by mutableStateOf<List<Tag>>(emptyList()) var tagList by mutableStateOf<List<Tag>>(emptyList())
fun setTagList() { fun setTagList() {
viewModelScope.launch { viewModelScope.launch(Dispatchers.IO) {
tagList=tagRepository.getAllTags().first() tagList=tagRepository.getAllTags()
} }
} }
@ -59,16 +70,14 @@ class ArticlePageScreenViewModel(
articleRepository.insertArticle(article) articleRepository.insertArticle(article)
} }
suspend fun getTagByName(name: String) : Tag { suspend fun getTagByName(name: String) : Tag? {
return tagRepository.getTagByName(name).first() return tagRepository.getTagByName(name)
} }
fun getTagById(tagId:Int) { fun getTagById(tagId:Int) {
tagid.value = tagId tagid.value = tagId
viewModelScope.launch { viewModelScope.launch {
tag = tagRepository.getTagById(tagid.value!!) tag = tagRepository.getTagById(tagid.value!!)
.filterNotNull()
.first()
} }
} }
} }

View File

@ -1,11 +1,24 @@
package com.example.pmulabs.viewModels 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.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData import androidx.paging.PagingData
import com.example.pmulabs.room.models.Article import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.repository.ArticleRepository import com.example.pmulabs.room.repository.ArticleRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class ArticleScreenViewModel(private val articleRepository: ArticleRepository) : ViewModel() { class ArticleScreenViewModel(private val articleRepository: ArticleRepository) : ViewModel() {
val articles: Flow<PagingData<Article>> = articleRepository.getArticles() val articles: Flow<PagingData<Article>> = articleRepository.getArticles()
var getArticles by mutableStateOf<List<Article>>(emptyList())
fun setArticleList() {
viewModelScope.launch(Dispatchers.IO) {
getArticles=articleRepository.getAllArticles()
}
}
} }

View File

@ -1,8 +1,5 @@
package com.example.pmulabs.viewModels 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.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.pmulabs.room.models.Comment import com.example.pmulabs.room.models.Comment
@ -10,8 +7,6 @@ import com.example.pmulabs.room.models.User
import com.example.pmulabs.room.repository.CommentRepository import com.example.pmulabs.room.repository.CommentRepository
import com.example.pmulabs.room.repository.UserRepository import com.example.pmulabs.room.repository.UserRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -22,32 +17,15 @@ class CommentItemViewModel(
suspend fun updateComment(comment: Comment) { suspend fun updateComment(comment: Comment) {
commentRepository.updateComment(comment) commentRepository.updateComment(comment)
} }
var user : User? = null
var commentList by mutableStateOf<Flow<List<Comment>>>(commentRepository.getAllComments()) suspend fun getCount(articleId: Int): Int? = withContext(Dispatchers.IO) {
var commListByArticle by mutableStateOf<List<Comment>>(emptyList())
var userListByArticle by mutableStateOf<List<User>>(emptyList())
suspend fun getCount(articleId: Int): Int = withContext(Dispatchers.IO) {
commentRepository.getCountComment(articleId) commentRepository.getCountComment(articleId)
} }
fun getUsersByArticle(articleId: Int) { fun getUser(commId: Int) {
viewModelScope.launch { viewModelScope.launch(Dispatchers.IO) {
val count=getCount(articleId) user=userRepository.getUserById(commId)
for(item in commentList.first()){
if(item.articleId==articleId){
commListByArticle+=item
}
if(commListByArticle.size>count){
commListByArticle -= commListByArticle[commListByArticle.size-1]
}
}
for(item in commListByArticle){
userListByArticle+=userRepository.getUserById(item.userId).first()
if(userListByArticle.size>count){
userListByArticle -= userListByArticle[userListByArticle.size-1]
}
}
} }
} }
@ -55,3 +33,4 @@ class CommentItemViewModel(
commentRepository.deleteComment(comment) commentRepository.deleteComment(comment)
} }
} }

View File

@ -7,8 +7,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.pmulabs.room.models.User import com.example.pmulabs.room.models.User
import com.example.pmulabs.room.repository.UserRepository import com.example.pmulabs.room.repository.UserRepository
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class CurrentUserViewModel(private val userRepository: UserRepository) : ViewModel(){ class CurrentUserViewModel(private val userRepository: UserRepository) : ViewModel(){
@ -21,8 +19,6 @@ class CurrentUserViewModel(private val userRepository: UserRepository) : ViewMod
userid.value = arg.toInt() userid.value = arg.toInt()
viewModelScope.launch { viewModelScope.launch {
user = userRepository.getUserById(userid.value) user = userRepository.getUserById(userid.value)
.filterNotNull()
.first()
} }
} }
} }

View File

@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.pmulabs.room.models.User import com.example.pmulabs.room.models.User
import com.example.pmulabs.room.repository.UserRepository import com.example.pmulabs.room.repository.UserRepository
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class EntryScreenViewModel(private val userRepository: UserRepository) : ViewModel() { class EntryScreenViewModel(private val userRepository: UserRepository) : ViewModel() {
@ -15,7 +14,7 @@ class EntryScreenViewModel(private val userRepository: UserRepository) : ViewMod
var userList by mutableStateOf<List<User>>(emptyList()) var userList by mutableStateOf<List<User>>(emptyList())
fun setUserList() { fun setUserList() {
viewModelScope.launch { viewModelScope.launch {
userList=userRepository.getAllUsers().first() userList=userRepository.getAllUsers()
} }
} }
} }

View File

@ -6,7 +6,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.pmulabs.room.models.User import com.example.pmulabs.room.models.User
import com.example.pmulabs.room.repository.UserRepository import com.example.pmulabs.room.repository.UserRepository
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class RegisterScreenViewModel(private val userRepository: UserRepository) : ViewModel() { class RegisterScreenViewModel(private val userRepository: UserRepository) : ViewModel() {
@ -16,7 +15,7 @@ class RegisterScreenViewModel(private val userRepository: UserRepository) : View
fun setUserList() { fun setUserList() {
viewModelScope.launch { viewModelScope.launch {
_users.value = userRepository.getAllUsers().first() _users.value = userRepository.getAllUsers()
} }
} }

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</network-security-config>

View File

@ -3,4 +3,6 @@ plugins {
id("com.android.application") version "8.1.1" apply false id("com.android.application") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false id("org.jetbrains.kotlin.android") version "1.8.10" apply false
id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.20" apply false
} }

12
server/package.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "fake-db",
"version": "1.0.0",
"scripts": {
"start": "json-server --watch data.json --host 0.0.0.0 -p 8079"
},
"dependencies": {
},
"devDependencies": {
"json-server": "^0.17.4"
}
}