Compare commits

...

2 Commits

62 changed files with 856 additions and 326 deletions

View File

@ -2,11 +2,22 @@ package com.example.dtf
import android.app.Application
import androidx.room.*
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.repositories.*
import com.example.dtf.repositories.offline.*
import com.example.dtf.repositories.online.*
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.repositories.ICategoryRepository
import com.example.dtf.data.repositories.ICommentRepository
import com.example.dtf.data.repositories.ILikeRepository
import com.example.dtf.data.repositories.IPostRepository
import com.example.dtf.data.repositories.IUserRepository
import com.example.dtf.data.repositories.offline.OfflineCategoryRepository
import com.example.dtf.data.repositories.offline.OfflineCommentRepository
import com.example.dtf.data.repositories.offline.OfflineLikeRepository
import com.example.dtf.data.repositories.offline.OfflinePostRepository
import com.example.dtf.data.repositories.offline.OfflineUserRepository
import com.example.dtf.data.repositories.online.RestCommentRepository
import com.example.dtf.data.repositories.online.RestLikeRepository
import com.example.dtf.data.repositories.online.RestPostRepository
import com.example.dtf.data.repositories.online.RestUserRepository
import com.example.dtf.repositories.online.mediator.RestCategoryRepository
import dagger.Module
import dagger.Provides

View File

@ -1,15 +1,16 @@
package com.example.dtf
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
@ -24,19 +25,21 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navigation
import com.example.dtf.screens.EditPostScreen
import com.example.dtf.screens.LoginScreen
import com.example.dtf.screens.NewPostScreen
import com.example.dtf.screens.PostScreen
import com.example.dtf.screens.PostsScreen
import com.example.dtf.screens.ProfileScreen
import com.example.dtf.screens.RegisterScreen
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.widgets.BottomNavBar
import com.example.dtf.ui.screens.EditPostScreen
import com.example.dtf.ui.screens.LoginScreen
import com.example.dtf.ui.screens.NewPostScreen
import com.example.dtf.ui.screens.PostScreen
import com.example.dtf.ui.screens.PostsScreen
import com.example.dtf.ui.screens.ProfileScreen
import com.example.dtf.ui.screens.RegisterScreen
import com.example.dtf.ui.screens.TopPostsScreen
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.widgets.BottomNavBar
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter",
"UnusedMaterialScaffoldPaddingParameter"
)
@ -100,6 +103,10 @@ class MainActivity : ComponentActivity() {
includeBackButton.value = false
PostsScreen(navController)
}
composable(ScreenPaths.TopPosts.route) {
includeBackButton.value = false
TopPostsScreen(navController)
}
composable(ScreenPaths.NewPost.route) {
includeBackButton.value = true
NewPostScreen(navController)

View File

@ -1,27 +0,0 @@
package com.example.dtf.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.example.dtf.models.*
import kotlinx.coroutines.flow.Flow
@Dao
interface CommentDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(comment: Comment)
@Update
suspend fun update(comment: Comment)
@Delete
suspend fun delete(comment: Comment)
@Query("select * from comment where comment.id = :id")
fun getById(id: Int) : Flow<Comment>
@Query("select * from comment where comment.post_id = :postId ORDER BY id DESC")
fun getByPost(postId: Int) : PagingSource<Int, Comment>
@Query("delete from comment where post_id = :query")
fun deleteByQuery(query: String)
}

View File

@ -1,30 +1,28 @@
package com.example.dtf.api
package com.example.dtf.data.api
import com.example.dtf.dto.Credentials
import com.example.dtf.dto.EditPostDto
import com.example.dtf.dto.MeUser
import com.example.dtf.dto.NewCommentDto
import com.example.dtf.dto.NewPostDto
import com.example.dtf.dto.Token
import com.example.dtf.dto.remote.CategoriesResponse
import com.example.dtf.dto.remote.CommentsResponse
import com.example.dtf.dto.remote.PostsResponse
import com.example.dtf.models.Category
import com.example.dtf.models.Comment
import com.example.dtf.models.Post
import com.example.dtf.models.User
import com.example.dtf.data.dto.Credentials
import com.example.dtf.data.dto.EditPostDto
import com.example.dtf.data.dto.MeUser
import com.example.dtf.data.dto.NewCommentDto
import com.example.dtf.data.dto.NewPostDto
import com.example.dtf.data.dto.Token
import com.example.dtf.data.dto.remote.CategoriesResponse
import com.example.dtf.data.dto.remote.CommentsResponse
import com.example.dtf.data.dto.remote.PostsResponse
import com.example.dtf.data.models.Comment
import com.example.dtf.data.models.Post
import com.example.dtf.data.models.User
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.http.POST
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.http.Header
interface ServerService {
@POST("auth/login")
@ -53,6 +51,13 @@ interface ServerService {
@Query("limit") limit: Int?
): PostsResponse
@GET("post/top")
suspend fun getTopPosts(
@Query("category") categoryId: Int,
@Query("from_date") fromDate: String?,
@Query("to_date") toDate: String?
): List<Post>
@GET("post/{postId}")
suspend fun getPost(
@Path("postId") postId: Int
@ -107,7 +112,7 @@ interface ServerService {
): User
companion object {
private const val BASE_URL = "http://192.168.1.9:8000/"
private const val BASE_URL = "http://192.168.0.103:8000/"
private var _token: String = ""

View File

@ -1,8 +1,13 @@
package com.example.dtf.dao
package com.example.dtf.data.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.example.dtf.models.*
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.dtf.data.models.Category
import kotlinx.coroutines.flow.Flow
@Dao
@ -27,4 +32,7 @@ interface CategoryDao {
@Query("select * from category limit 1")
fun getFirst() : Flow<Category>
@Query("select * from category where id >= :loadKey limit :limit")
fun getByLoadKey(loadKey: Int, limit: Int) : Flow<List<Category>>
}

View File

@ -0,0 +1,35 @@
package com.example.dtf.data.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.dtf.data.models.Comment
import kotlinx.coroutines.flow.Flow
@Dao
interface CommentDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(comment: Comment)
@Update
suspend fun update(comment: Comment)
@Delete
suspend fun delete(comment: Comment)
@Query("select * from comment where comment.id = :id")
fun getById(id: Int) : Flow<Comment>
@Query("select * from comment where comment.post_id = :postId ORDER BY id DESC")
fun getByPost(postId: Int) : PagingSource<Int, Comment>
@Query("select * from comment where id >= :loadKey and post_id = :postId order by id desc limit :limit")
fun getByLoadKeyInitial(postId: Int, loadKey: Int, limit: Int) : Flow<List<Comment>>
@Query("select * from comment where id <= :loadKey and post_id = :postId order by id desc limit :limit")
fun getByLoadKey(postId: Int, loadKey: Int, limit: Int) : Flow<List<Comment>>
}

View File

@ -1,7 +1,11 @@
package com.example.dtf.dao
package com.example.dtf.data.dao
import androidx.room.*
import com.example.dtf.models.Like
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.dtf.data.models.Like
import kotlinx.coroutines.flow.Flow
@Dao

View File

@ -1,8 +1,13 @@
package com.example.dtf.dao
package com.example.dtf.data.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.example.dtf.models.*
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.dtf.data.models.Post
import kotlinx.coroutines.flow.Flow
@Dao
@ -22,6 +27,6 @@ interface PostDao {
@Query("select * from post where post.category_id = :categoryId ORDER BY date DESC")
fun getByCategory(categoryId: String) : PagingSource<Int, Post>
@Query("delete from post where category_id = :query")
fun deleteByQuery(query: String)
@Query("select * from post where category_id = :categoryId and id >= :loadKey ORDER BY date DESC limit :limit")
fun getByLoadKey(categoryId: Int, loadKey: Int, limit: Int) : Flow<List<Post>>
}

View File

@ -1,7 +1,12 @@
package com.example.dtf.dao
package com.example.dtf.data.dao
import androidx.room.*
import com.example.dtf.models.User
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.dtf.data.models.User
import kotlinx.coroutines.flow.Flow
@Dao

View File

@ -1,18 +1,18 @@
package com.example.dtf.db
package com.example.dtf.data.db
import android.content.Context
import androidx.room.*
import com.example.dtf.models.Category
import com.example.dtf.models.Comment
import com.example.dtf.models.Post
import com.example.dtf.models.User
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.dtf.dao.*
import com.example.dtf.models.Like
import java.util.Date
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.example.dtf.data.dao.CategoryDao
import com.example.dtf.data.dao.CommentDao
import com.example.dtf.data.dao.LikeDao
import com.example.dtf.data.dao.PostDao
import com.example.dtf.data.dao.UserDao
import com.example.dtf.data.models.Category
import com.example.dtf.data.models.Comment
import com.example.dtf.data.models.Like
import com.example.dtf.data.models.Post
import com.example.dtf.data.models.User
@Database(
entities = [

View File

@ -1,4 +1,4 @@
package com.example.dtf.db
package com.example.dtf.data.db
import androidx.room.TypeConverter
import java.util.Date

View File

@ -1,4 +1,4 @@
package com.example.dtf.dto
package com.example.dtf.data.dto
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.example.dtf.dto
package com.example.dtf.data.dto
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.example.dtf.dto
package com.example.dtf.data.dto
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.example.dtf.dto
package com.example.dtf.data.dto
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.example.dtf.dto
package com.example.dtf.data.dto
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.example.dtf.dto
package com.example.dtf.data.dto
import kotlinx.serialization.Serializable

View File

@ -1,6 +1,6 @@
package com.example.dtf.dto.remote
package com.example.dtf.data.dto.remote
import com.example.dtf.models.Category
import com.example.dtf.data.models.Category
import kotlinx.serialization.Serializable
@Serializable

View File

@ -1,6 +1,6 @@
package com.example.dtf.dto.remote
package com.example.dtf.data.dto.remote
import com.example.dtf.models.Comment
import com.example.dtf.data.models.Comment
import kotlinx.serialization.Serializable
@Serializable

View File

@ -1,6 +1,6 @@
package com.example.dtf.dto.remote
package com.example.dtf.data.dto.remote
import com.example.dtf.models.Post
import com.example.dtf.data.models.Post
import kotlinx.serialization.Serializable
@Serializable

View File

@ -1,6 +1,8 @@
package com.example.dtf.models
package com.example.dtf.data.models
import androidx.room.*
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
@Entity(tableName = "category")

View File

@ -1,6 +1,8 @@
package com.example.dtf.models
package com.example.dtf.data.models
import androidx.room.*
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
@Entity(tableName = "comment")

View File

@ -1,6 +1,7 @@
package com.example.dtf.models
package com.example.dtf.data.models
import androidx.room.*
import androidx.room.ColumnInfo
import androidx.room.Entity
@Entity(tableName = "likes", primaryKeys = ["user_id", "post_id"])
data class Like (

View File

@ -1,6 +1,8 @@
package com.example.dtf.models
package com.example.dtf.data.models
import androidx.room.*
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
//private val posts = listOf(

View File

@ -1,6 +1,8 @@
package com.example.dtf.models
package com.example.dtf.data.models
import androidx.room.*
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
@Entity(tableName = "user")

View File

@ -1,7 +1,7 @@
package com.example.dtf.repositories
package com.example.dtf.data.repositories
import androidx.paging.PagingData
import com.example.dtf.models.Category
import com.example.dtf.data.models.Category
import kotlinx.coroutines.flow.Flow
interface ICategoryRepository {

View File

@ -1,7 +1,7 @@
package com.example.dtf.repositories
package com.example.dtf.data.repositories
import androidx.paging.PagingData
import com.example.dtf.models.Comment
import com.example.dtf.data.models.Comment
import kotlinx.coroutines.flow.Flow
interface ICommentRepository {

View File

@ -1,6 +1,6 @@
package com.example.dtf.repositories
package com.example.dtf.data.repositories
import com.example.dtf.models.Like
import com.example.dtf.data.models.Like
import kotlinx.coroutines.flow.Flow
interface ILikeRepository {

View File

@ -1,8 +1,9 @@
package com.example.dtf.repositories
package com.example.dtf.data.repositories
import androidx.paging.PagingData
import com.example.dtf.models.Post
import com.example.dtf.data.models.Post
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
interface IPostRepository {
suspend fun insert(post: Post)
@ -10,4 +11,6 @@ interface IPostRepository {
fun getById(id: Int): Flow<Post>
fun getByCategory(categoryId: Int): Flow<PagingData<Post>>
fun getTopTen(categoryId: Int, fromDate: LocalDate, toDate: LocalDate): Flow<List<Post>>
}

View File

@ -1,6 +1,6 @@
package com.example.dtf.repositories
package com.example.dtf.data.repositories
import com.example.dtf.models.User
import com.example.dtf.data.models.User
import kotlinx.coroutines.flow.Flow
interface IUserRepository {

View File

@ -1,10 +1,10 @@
package com.example.dtf.repositories.offline
package com.example.dtf.data.repositories.offline
import androidx.paging.Pager
import androidx.paging.PagingConfig
import com.example.dtf.dao.CategoryDao
import com.example.dtf.models.Category
import com.example.dtf.repositories.ICategoryRepository
import com.example.dtf.data.dao.CategoryDao
import com.example.dtf.data.models.Category
import com.example.dtf.data.repositories.ICategoryRepository
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

View File

@ -1,9 +1,10 @@
package com.example.dtf.repositories.offline
package com.example.dtf.data.repositories.offline
import androidx.paging.*
import com.example.dtf.dao.CommentDao
import com.example.dtf.models.Comment
import com.example.dtf.repositories.ICommentRepository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import com.example.dtf.data.dao.CommentDao
import com.example.dtf.data.models.Comment
import com.example.dtf.data.repositories.ICommentRepository
import javax.inject.Inject
class OfflineCommentRepository @Inject constructor(

View File

@ -1,8 +1,8 @@
package com.example.dtf.repositories.offline
package com.example.dtf.data.repositories.offline
import com.example.dtf.dao.LikeDao
import com.example.dtf.models.Like
import com.example.dtf.repositories.ILikeRepository
import com.example.dtf.data.dao.LikeDao
import com.example.dtf.data.models.Like
import com.example.dtf.data.repositories.ILikeRepository
import javax.inject.Inject
class OfflineLikeRepository @Inject constructor(

View File

@ -1,9 +1,12 @@
package com.example.dtf.repositories.offline
package com.example.dtf.data.repositories.offline
import androidx.paging.*
import com.example.dtf.dao.PostDao
import com.example.dtf.models.Post
import com.example.dtf.repositories.IPostRepository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import com.example.dtf.data.dao.PostDao
import com.example.dtf.data.models.Post
import com.example.dtf.data.repositories.IPostRepository
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
import javax.inject.Inject
class OfflinePostRepository @Inject constructor(
@ -22,4 +25,8 @@ class OfflinePostRepository @Inject constructor(
),
pagingSourceFactory = { postDao.getByCategory(categoryId.toString()) }
).flow
override fun getTopTen(categoryId: Int, fromDate: LocalDate, toDate: LocalDate): Flow<List<Post>> {
throw NotImplementedError("Cannot access top without internet connection")
}
}

View File

@ -1,8 +1,7 @@
package com.example.dtf.repositories.offline
package com.example.dtf.data.repositories.offline
import com.example.dtf.dao.UserDao
import com.example.dtf.models.User
import com.example.dtf.repositories.IUserRepository
import com.example.dtf.data.dao.UserDao
import com.example.dtf.data.repositories.IUserRepository
import javax.inject.Inject
class OfflineUserRepository @Inject constructor(

View File

@ -4,12 +4,12 @@ import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Category
import com.example.dtf.repositories.ICategoryRepository
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.models.Category
import com.example.dtf.data.repositories.ICategoryRepository
import com.example.dtf.data.repositories.online.mediator.CategoryMediator
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
class RestCategoryRepository @Inject constructor(

View File

@ -1,15 +1,15 @@
package com.example.dtf.repositories.online
package com.example.dtf.data.repositories.online
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.dto.NewCommentDto
import com.example.dtf.models.Comment
import com.example.dtf.repositories.ICommentRepository
import com.example.dtf.repositories.online.mediator.CommentMediator
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.dto.NewCommentDto
import com.example.dtf.data.models.Comment
import com.example.dtf.data.repositories.ICommentRepository
import com.example.dtf.data.repositories.online.mediator.CommentMediator
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

View File

@ -1,9 +1,9 @@
package com.example.dtf.repositories.online
package com.example.dtf.data.repositories.online
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Like
import com.example.dtf.repositories.ILikeRepository
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.models.Like
import com.example.dtf.data.repositories.ILikeRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

View File

@ -1,18 +1,21 @@
package com.example.dtf.repositories.online
package com.example.dtf.data.repositories.online
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.dto.EditPostDto
import com.example.dtf.dto.NewPostDto
import com.example.dtf.models.Post
import com.example.dtf.repositories.IPostRepository
import com.example.dtf.repositories.online.mediator.PostMediator
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.dto.EditPostDto
import com.example.dtf.data.dto.NewPostDto
import com.example.dtf.data.models.Post
import com.example.dtf.data.repositories.IPostRepository
import com.example.dtf.data.repositories.online.mediator.PostMediator
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import java.time.LocalDate
import javax.inject.Inject
class RestPostRepository @Inject constructor(
@ -46,4 +49,17 @@ class RestPostRepository @Inject constructor(
remoteMediator = PostMediator(appDatabase, serverService, categoryId.toString()),
pagingSourceFactory = { appDatabase.postDao().getByCategory(categoryId.toString()) }
).flow
@RequiresApi(Build.VERSION_CODES.O)
override fun getTopTen(categoryId: Int, fromDate: LocalDate, toDate: LocalDate): Flow<List<Post>> {
return flow {
emit(
serverService.getTopPosts(
categoryId,
"${fromDate.year}-${fromDate.monthValue}-${fromDate.dayOfMonth}",
"${toDate.year}-${toDate.monthValue}-${toDate.dayOfMonth}"
)
)
}
}
}

View File

@ -1,9 +1,9 @@
package com.example.dtf.repositories.online
package com.example.dtf.data.repositories.online
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.User
import com.example.dtf.repositories.IUserRepository
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.models.User
import com.example.dtf.data.repositories.IUserRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

View File

@ -1,13 +1,14 @@
package com.example.dtf.repositories.online.mediator
package com.example.dtf.data.repositories.online.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Category
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.models.Category
import kotlinx.coroutines.flow.first
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
@ -42,11 +43,14 @@ class CategoryMediator (
limit = state.config.pageSize
)
database.withTransaction {
val present = categoryDao.getByLoadKey(loadKey, state.config.pageSize).first()
if (loadType == LoadType.REFRESH) {
for (category in response.categories) {
categoryDao.delete(category)
for (category in present) {
if (category !in response.categories){
categoryDao.delete(category)
}
}
}

View File

@ -1,13 +1,14 @@
package com.example.dtf.repositories.online.mediator
package com.example.dtf.data.repositories.online.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Comment
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.models.Comment
import kotlinx.coroutines.flow.first
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
@ -46,8 +47,18 @@ class CommentMediator (
database.withTransaction {
val present = if (loadKey == 0) {
commentDao.getByLoadKeyInitial(query.toInt(), loadKey, state.config.pageSize).first()
} else {
commentDao.getByLoadKey(query.toInt(), loadKey, state.config.pageSize).first()
}
if (loadType == LoadType.REFRESH) {
commentDao.deleteByQuery(query)
for (comment in present) {
if (comment !in response.comments) {
commentDao.delete(comment)
}
}
}
for (comment in response.comments) {

View File

@ -1,13 +1,14 @@
package com.example.dtf.repositories.online.mediator
package com.example.dtf.data.repositories.online.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.dtf.api.ServerService
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Post
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.db.AppDatabase
import com.example.dtf.data.models.Post
import kotlinx.coroutines.flow.first
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
@ -46,12 +47,18 @@ class PostMediator (
database.withTransaction {
val present = postDao.getByLoadKey(query.toInt(), loadKey, state.config.pageSize).first()
if (loadType == LoadType.REFRESH) {
postDao.deleteByQuery(query)
for (post in present) {
if (post !in response.posts) {
postDao.delete(post)
}
}
}
for (comment in response.posts) {
postDao.insert(comment)
for (post in response.posts) {
postDao.insert(post)
}
}

View File

@ -1,26 +1,40 @@
package com.example.dtf.screens
package com.example.dtf.ui.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import com.example.dtf.PreferencesManager
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.viewmodels.EditPostViewModel
import com.example.dtf.widgets.MyTextField
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.EditPostViewModel
import com.example.dtf.ui.widgets.MyTextField
@Composable
fun EditPostScreen(navController: NavHostController, postId: Int) {

View File

@ -1,25 +1,40 @@
package com.example.dtf.screens
package com.example.dtf.ui.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import com.example.dtf.PreferencesManager
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.viewmodels.LoginViewModel
import com.example.dtf.widgets.MyTextField
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.LoginViewModel
import com.example.dtf.ui.widgets.MyTextField
@Composable
fun LoginScreen(navController: NavHostController) {

View File

@ -1,29 +1,45 @@
package com.example.dtf.screens
package com.example.dtf.ui.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import com.example.dtf.PreferencesManager
import com.example.dtf.models.Category
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.viewmodels.NewPostViewModel
import com.example.dtf.widgets.MyTextField
import com.example.dtf.data.models.Category
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.NewPostViewModel
import com.example.dtf.ui.widgets.MyTextField
@Composable
fun NewPostScreen(navController: NavHostController) {

View File

@ -1,32 +1,51 @@
package com.example.dtf.screens
package com.example.dtf.ui.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddCircle
import androidx.compose.material.icons.filled.ThumbUp
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.*
import androidx.compose.ui.draw.*
import androidx.compose.ui.graphics.*
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.dtf.PreferencesManager
import com.example.dtf.models.Comment
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.viewmodels.PostViewModel
import com.example.dtf.widgets.MyTextField
import com.example.dtf.data.models.Comment
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.PostViewModel
import com.example.dtf.ui.widgets.MyTextField
@Composable
fun PostScreen(postId: Int, navController: NavHostController) {

View File

@ -1,31 +1,49 @@
package com.example.dtf.screens
package com.example.dtf.ui.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material.*
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ThumbUp
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.*
import androidx.compose.ui.draw.*
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.*
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.dtf.PreferencesManager
import com.example.dtf.models.Category
import com.example.dtf.models.Post
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.viewmodels.PostsViewModel
import com.example.dtf.data.models.Category
import com.example.dtf.data.models.Post
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.PostsViewModel
import kotlinx.coroutines.flow.Flow
@Composable

View File

@ -1,20 +1,33 @@
package com.example.dtf.screens
package com.example.dtf.ui.screens
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.Card
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import com.example.dtf.PreferencesManager
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.viewmodels.ProfileViewModel
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.ProfileViewModel
@Composable
fun ProfileScreen(navController: NavHostController) {

View File

@ -1,11 +1,15 @@
package com.example.dtf.screens
package com.example.dtf.ui.screens
import android.graphics.drawable.shapes.Shape
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
@ -16,22 +20,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.viewmodels.RegisterViewModel
import com.example.dtf.widgets.MyTextField
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.RegisterViewModel
import com.example.dtf.ui.widgets.MyTextField
@Composable
fun RegisterScreen(navController: NavHostController) {

View File

@ -0,0 +1,279 @@
package com.example.dtf.ui.screens
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ThumbUp
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.dtf.PreferencesManager
import com.example.dtf.data.models.Category
import com.example.dtf.data.models.Post
import com.example.dtf.ui.utils.ScreenPaths
import com.example.dtf.ui.viewmodels.TopPostsViewModel
import java.time.LocalDate
import java.time.Period
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun TopPostsScreen(navController: NavHostController) {
val viewModel = hiltViewModel<TopPostsViewModel>()
val currentCategory = remember { mutableStateOf<Category?>(null) }
val categories = viewModel.getCategoriesListUiState().collectAsLazyPagingItems()
val dateFrom = remember { mutableStateOf(LocalDate.now()) }
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(15.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(15.dp))
Text(
text = "Топ 10 новостей",
fontSize = 36.sp
)
DateSelect(viewModel, dateFrom, currentCategory)
Categories(viewModel, currentCategory, categories, dateFrom)
Posts(viewModel, navController)
Spacer(modifier = Modifier.height(60.dp))
}
}
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun DateSelect(
viewModel: TopPostsViewModel,
dateFrom: MutableState<LocalDate>,
currentCategory: MutableState<Category?>
) {
val week = Pair(LocalDate.now() - Period.ofWeeks(1), "Неделя")
val month = Pair(LocalDate.now() - Period.ofMonths(1), "Месяц")
val year = Pair(LocalDate.now() - Period.ofYears(1), "Год")
dateFrom.value = week.first
Row(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.1f)
.background(Color.White),
horizontalArrangement = Arrangement.spacedBy(25.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier)
listOf(week, month, year).forEach { (date, name) ->
Text(
modifier = Modifier
.clickable {
dateFrom.value = date
if (currentCategory.value != null) {
viewModel.retrievePosts(currentCategory.value!!.id!!, dateFrom.value, LocalDate.now())
}
}
.drawBehind {
if (dateFrom.value == date) {
val strokeWidthPx = 2.dp.toPx()
val verticalOffset = size.height + 2.sp.toPx()
drawLine(
color = Color(0xFF319CFF),
strokeWidth = strokeWidthPx,
start = Offset(0f, verticalOffset),
end = Offset(size.width, verticalOffset)
)
}
},
text = name,
fontSize = 22.sp
)
}
}
}
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun Categories(
viewModel: TopPostsViewModel,
currentCategory: MutableState<Category?>,
categories: LazyPagingItems<Category>,
dateFrom: MutableState<LocalDate>) {
LazyRow(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.1f)
.background(Color.White),
horizontalArrangement = Arrangement.spacedBy(25.dp),
verticalAlignment = Alignment.CenterVertically
) {
item {
Spacer(modifier = Modifier)
}
items(
count = categories.itemCount,
key = categories.itemKey()
) {
if (currentCategory.value == null) {
currentCategory.value = categories[0]
viewModel.retrievePosts(currentCategory.value!!.id!!, dateFrom.value, LocalDate.now())
}
Text(
modifier = Modifier
.clickable {
currentCategory.value = categories[it]!!
viewModel.retrievePosts(
currentCategory.value!!.id!!,
dateFrom.value,
LocalDate.now()
)
}
.drawBehind {
if (categories[it]!!.name == currentCategory.value?.name) {
val strokeWidthPx = 2.dp.toPx()
val verticalOffset = size.height + 2.sp.toPx()
drawLine(
color = Color(0xFF319CFF),
strokeWidth = strokeWidthPx,
start = Offset(0f, verticalOffset),
end = Offset(size.width, verticalOffset)
)
}
},
text = categories[it]!!.name,
fontSize = 22.sp
)
}
}
}
@Composable
fun Posts(viewModel: TopPostsViewModel, navController: NavHostController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
for (post in viewModel.posts.observeAsState(listOf()).value) {
Post(viewModel, navController, post)
}
}
}
@Composable
fun Post(viewModel: TopPostsViewModel, navController: NavHostController, post: Post) {
val sharedPref = PreferencesManager(LocalContext.current)
val likes = remember { mutableIntStateOf(post.likes) }
val isLiked = remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
viewModel.isLiked(sharedPref, post.id!!).collect {
isLiked.value = it
}
}
Column(
modifier = Modifier
.heightIn(min = 250.dp, max = 300.dp)
.fillMaxWidth()
.background(Color.White)
.clickable {
navController.navigate(ScreenPaths.Post.route.replace("{post}", post.id.toString()))
},
verticalArrangement = Arrangement.spacedBy(5.dp),
horizontalAlignment = Alignment.Start
) {
Text(
modifier = Modifier.padding(10.dp),
text = post.title,
fontSize = 26.sp
)
Text(
modifier = Modifier
.fillMaxHeight(0.6f)
.padding(10.dp, 0.dp, 10.dp, 0.dp),
text = post.content,
fontSize = 20.sp,
overflow = TextOverflow.Ellipsis
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = post.date,
fontSize = 14.sp,
color = Color(0xFFCECCCC)
)
Row (
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
Text(
text = likes.intValue.toString(),
fontSize = 16.sp,
color = Color.Green
)
Icon(
modifier = Modifier
.padding(start = 8.dp)
.clickable {
if (isLiked.value) {
viewModel.unlikePost(sharedPref, post.id!!)
likes.intValue--
} else {
viewModel.likePost(sharedPref, post.id!!)
likes.intValue++
}
isLiked.value = !isLiked.value
},
imageVector = Icons.Default.ThumbUp,
contentDescription = null,
tint = if (isLiked.value) { Color(40, 200, 40, 255) } else {
Color.Black}
)
}
}
}
}

View File

@ -1,4 +1,4 @@
package com.example.dtf.utils
package com.example.dtf.ui.utils
sealed class ScreenPaths (val route: String){
object Auth: ScreenPaths("auth")
@ -6,6 +6,7 @@ sealed class ScreenPaths (val route: String){
object Register: ScreenPaths("auth/register")
object Post: ScreenPaths("posts/{post}")
object Posts: ScreenPaths("posts")
object TopPosts: ScreenPaths("posts/top")
object NewPost: ScreenPaths("posts/new")
object EditPost: ScreenPaths("posts/{post}/edit")
object Profile: ScreenPaths("profile")

View File

@ -1,12 +1,12 @@
package com.example.dtf.viewmodels
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.dtf.PreferencesManager
import com.example.dtf.models.Post
import com.example.dtf.repositories.IPostRepository
import com.example.dtf.data.models.Post
import com.example.dtf.data.repositories.IPostRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

View File

@ -1,13 +1,12 @@
package com.example.dtf.viewmodels
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.dtf.PreferencesManager
import com.example.dtf.api.ServerService
import com.example.dtf.dto.Credentials
import com.example.dtf.repositories.IUserRepository
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.dto.Credentials
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

View File

@ -1,13 +1,13 @@
package com.example.dtf.viewmodels
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.dtf.PreferencesManager
import com.example.dtf.models.Post
import com.example.dtf.repositories.ICategoryRepository
import com.example.dtf.repositories.IPostRepository
import com.example.dtf.data.models.Post
import com.example.dtf.data.repositories.ICategoryRepository
import com.example.dtf.data.repositories.IPostRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.util.Date

View File

@ -1,4 +1,4 @@
package com.example.dtf.viewmodels
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@ -6,13 +6,13 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.example.dtf.PreferencesManager
import com.example.dtf.models.Comment
import com.example.dtf.models.Like
import com.example.dtf.models.Post
import com.example.dtf.repositories.ICommentRepository
import com.example.dtf.repositories.ILikeRepository
import com.example.dtf.repositories.IPostRepository
import com.example.dtf.repositories.IUserRepository
import com.example.dtf.data.models.Comment
import com.example.dtf.data.models.Like
import com.example.dtf.data.models.Post
import com.example.dtf.data.repositories.ICommentRepository
import com.example.dtf.data.repositories.ILikeRepository
import com.example.dtf.data.repositories.IPostRepository
import com.example.dtf.data.repositories.IUserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch

View File

@ -1,12 +1,12 @@
package com.example.dtf.viewmodels
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.dtf.PreferencesManager
import com.example.dtf.models.Like
import com.example.dtf.repositories.ICategoryRepository
import com.example.dtf.repositories.ILikeRepository
import com.example.dtf.repositories.IPostRepository
import com.example.dtf.data.models.Like
import com.example.dtf.data.repositories.ICategoryRepository
import com.example.dtf.data.repositories.ILikeRepository
import com.example.dtf.data.repositories.IPostRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -47,7 +47,5 @@ class PostsViewModel @Inject constructor(
fun getCategoriesListUiState() = categoryRepository.getAll()
fun getInitialCategory() = categoryRepository.getFirst()
fun getPostsListUiState(categoryId: Int) = postRepository.getByCategory(categoryId)
}

View File

@ -1,14 +1,12 @@
package com.example.dtf.viewmodels
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.dtf.PreferencesManager
import com.example.dtf.models.User
import com.example.dtf.repositories.offline.OfflineUserRepository
import com.example.dtf.data.models.User
import com.example.dtf.data.repositories.offline.OfflineUserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel

View File

@ -1,12 +1,11 @@
package com.example.dtf.viewmodels
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.dtf.api.ServerService
import com.example.dtf.dto.Credentials
import com.example.dtf.repositories.offline.OfflineUserRepository
import com.example.dtf.data.api.ServerService
import com.example.dtf.data.dto.Credentials
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

View File

@ -0,0 +1,63 @@
package com.example.dtf.ui.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.dtf.PreferencesManager
import com.example.dtf.data.models.Like
import com.example.dtf.data.models.Post
import com.example.dtf.data.repositories.ICategoryRepository
import com.example.dtf.data.repositories.ILikeRepository
import com.example.dtf.data.repositories.IPostRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.time.LocalDate
import javax.inject.Inject
@HiltViewModel
class TopPostsViewModel @Inject constructor(
private val postRepository: IPostRepository,
private val categoryRepository: ICategoryRepository,
private val likeRepository: ILikeRepository
) : ViewModel() {
private val _posts = MutableLiveData<List<Post>>()
val posts: LiveData<List<Post>>
get() = _posts
fun getCategoriesListUiState() = categoryRepository.getAll()
fun retrievePosts(categoryId: Int, fromDate: LocalDate, toDate: LocalDate) {
viewModelScope.launch {
postRepository.getTopTen(categoryId, fromDate, toDate).collect {
_posts.value = it
}
}
}
fun likePost(sharedPref: PreferencesManager, postId: Int) {
val userId = sharedPref.getData("userId", "-1").toInt()
if (userId == -1) return
viewModelScope.launch {
likeRepository.insert(Like(userId, postId))
}
}
fun unlikePost(sharedPref: PreferencesManager, postId: Int) {
val userId = sharedPref.getData("userId", "-1").toInt()
if (userId == -1) return
viewModelScope.launch {
likeRepository.delete(Like(userId, postId))
}
}
fun isLiked(sharedPref: PreferencesManager, postId: Int)
= likeRepository.isLikedByUser(
sharedPref.getData("userId", "-1").toInt(),
postId
)
}

View File

@ -1,9 +1,5 @@
package com.example.dtf.widgets
package com.example.dtf.ui.widgets
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material.BottomNavigation
@ -14,27 +10,17 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Star
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigation.NavController
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.example.dtf.PreferencesManager
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.User
import com.example.dtf.utils.ScreenPaths
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.example.dtf.ui.utils.ScreenPaths
@Composable
fun BottomNavBar(navController: NavController) {
@ -49,6 +35,7 @@ fun BottomNavBar(navController: NavController) {
val currentRoute = navBackStackEntry?.destination?.route
listOfNotNull(
Triple(ScreenPaths.Posts.route, Icons.Default.Home, "Новости"),
Triple(ScreenPaths.TopPosts.route, Icons.Default.Star, "Топ"),
if (sharedPref.getData("isModerator", "false").toBooleanStrict()) {
Triple(ScreenPaths.NewPost.route, Icons.Default.Edit, "Создать")
} else { null },

View File

@ -1,4 +1,4 @@
package com.example.dtf.widgets
package com.example.dtf.ui.widgets
import androidx.compose.foundation.background
import androidx.compose.foundation.border