Лабораторная работа №3: как будто бы готова

This commit is contained in:
Сергей Полевой 2023-11-21 02:26:39 +04:00
parent a3ef2c1f94
commit 392b9e5c0a
20 changed files with 611 additions and 131 deletions

View File

@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
@ -18,6 +19,10 @@ android {
vectorDrawables {
useSupportLibrary true
}
kapt {
arguments {arg("room.schemaLocation", "$projectDir/schemas")}
}
}
buildTypes {
@ -62,4 +67,8 @@ dependencies {
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
implementation 'androidx.room:room-runtime:2.5.0' // Библиотека "Room"
kapt "androidx.room:room-compiler:2.5.0" // Кодогенератор
implementation 'androidx.room:room-ktx:2.5.0'
}

View File

@ -24,7 +24,6 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navigation
import com.example.dtf.models.currentUser
import com.example.dtf.screens.EditPostScreen
import com.example.dtf.screens.LoginScreen
import com.example.dtf.screens.NewPostScreen

View File

@ -0,0 +1,26 @@
package com.example.dtf.dao
import androidx.room.*
import com.example.dtf.models.*
import kotlinx.coroutines.flow.Flow
@Dao
interface CategoryDao {
@Insert
suspend fun insert(category: Category)
@Update
suspend fun update(category: Category)
@Delete
suspend fun delete(category: Category)
@Query("select * from category")
fun getAll() : Flow<List<Category>>
@Query("select * from category where category.id = :id")
fun getById(id: Int) : Flow<Category>
@Query("select * from category where category.name = :name")
fun getByName(name: String) : Flow<CategoryWithPosts>
}

View File

@ -0,0 +1,26 @@
package com.example.dtf.dao
import androidx.room.*
import com.example.dtf.models.*
import kotlinx.coroutines.flow.Flow
@Dao
interface CommentDao {
@Insert
suspend fun insert(comment: Comment)
@Update
suspend fun update(comment: Comment)
@Delete
suspend fun delete(comment: Comment)
@Query("select * from comment")
fun getAll() : Flow<List<Comment>>
@Query("select * from comment where comment.id = :id")
fun getById(id: Int) : Flow<Comment>
@Query("select * from comment where comment.post_id = :postId")
fun getByPost(postId: Int) : Flow<List<Comment>>
}

View File

@ -0,0 +1,26 @@
package com.example.dtf.dao
import androidx.room.*
import com.example.dtf.models.*
import kotlinx.coroutines.flow.Flow
@Dao
interface PostDao {
@Insert
suspend fun insert(post: Post)
@Update
suspend fun update(post: Post)
@Delete
suspend fun delete(post: Post)
@Query("select * from post")
fun getAll() : Flow<List<Post>>
@Query("select * from post where post.id = :id")
fun getById(id: Int) : Flow<PostWithComments>
@Query("select * from post where post.category_id = :categoryId")
fun getByCategory(categoryId: String) : Flow<Post>
}

View File

@ -0,0 +1,23 @@
package com.example.dtf.dao
import androidx.room.*
import com.example.dtf.models.User
import kotlinx.coroutines.flow.Flow
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
@Query("select * from user order by username collate nocase asc")
fun getAll() : Flow<List<User>>
@Query("select * from user where user.id = :id")
fun getById(id: Int): User
}

View File

@ -0,0 +1,147 @@
package com.example.dtf.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 java.util.Date
@Database(
entities = [
User::class,
Category::class,
Post::class,
Comment::class
],
version = 1,
exportSchema = false
)
@TypeConverters(Converter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao() : UserDao
abstract fun categoryDao() : CategoryDao
abstract fun commentDao() : CommentDao
abstract fun postDao() : PostDao
companion object {
private const val DB_NAME: String = "news-db"
@Volatile
private var INSTANCE: AppDatabase? = null
private suspend fun populateDatabase() {
INSTANCE?.let { database ->
val userDao = database.userDao()
userDao.insert(User(1, "Sheriff", "123456", true))
userDao.insert(User(2, "Человек", "123456", true))
val categoryDao = database.categoryDao()
categoryDao.insert(Category(1, "Игры"))
categoryDao.insert(Category(2, "Кино"))
categoryDao.insert(Category(3, "Аниме"))
val postDao = database.postDao()
postDao.insert(
Post(
1,
"Что не так с half-life 2",
"Да всё не так",
Date(2023, 10, 22),
1,
1
)
)
postDao.insert(
Post(
2,
"Я действительно ненавижу фильм про титаник",
"Пруфов не будет",
Date(2023, 9, 22),
1,
2
)
)
postDao.insert(
Post(
3,
"\"Госпожа Кагуя\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
Date(2023, 9, 22),
1,
3
)
)
postDao.insert(
Post(
4,
"\"Восхождение в тени\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
Date(2023, 9, 22),
1,
3
)
)
postDao.insert(
Post(
5,
"\"Наруто\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
Date(2023, 9, 22),
1,
3
)
)
postDao.insert(
Post(
6,
"\"Блич\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
Date(2023, 9, 22),
1,
3
)
)
val commentDao = database.commentDao()
commentDao.insert(Comment(1, 2, 1, "Пост бред. Начнём с того, что вот эта твоя манера речи клоунская...", Date(2023, 10, 20)))
commentDao.insert(Comment(2, 1, 1, "Да какой бред, чел, я всё по факту сказал", Date(2023, 10, 20)))
commentDao.insert(Comment(3, 2, 3,"Автора на увольнение", Date(2023, 9, 20)))
commentDao.insert(Comment(4, 2, 4,"Автора на увольнение дважды", Date(2023, 9, 20)))
commentDao.insert(Comment(5, 2, 5,"Автора на увольнение трижды", Date(2023, 9, 20)))
commentDao.insert(Comment(6, 2, 6,"Автора на увольнение четырежды", Date(2023, 9, 20)))
}
}
fun getInstance(appContext: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
DB_NAME
)
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch {
populateDatabase()
}
}
})
.build()
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,16 @@
package com.example.dtf.db
import androidx.room.TypeConverter
import java.util.Date
class Converter {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time?.toLong()
}
}

View File

@ -1,14 +1,26 @@
package com.example.dtf.models
data class Category(
val id: Int,
val name: String
)
import androidx.room.*
fun getAllCategories() : List<Category> {
return listOf(
Category(1, "Игры"),
Category(2, "Кино"),
Category(3, "Аниме"),
)
@Entity(tableName = "category")
data class Category(
@PrimaryKey(autoGenerate = true)
@ColumnInfo
val id: Int?,
@ColumnInfo
val name: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Category
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,10 @@
package com.example.dtf.models
import androidx.room.Embedded
import androidx.room.Relation
data class CategoryWithPosts(
@Embedded val category: Category,
@Relation(parentColumn = "id", entityColumn = "category_id")
val posts: List<Post>
)

View File

@ -1,10 +1,33 @@
package com.example.dtf.models
import androidx.room.*
import java.util.Date
@Entity(tableName = "comment")
data class Comment(
val id: Int,
@PrimaryKey(autoGenerate = true)
@ColumnInfo
val id: Int?,
@ColumnInfo(name = "user_id")
val userId: Int,
@ColumnInfo(name = "post_id")
val postId: Int,
@ColumnInfo
val content: String,
@ColumnInfo
val date: Date
)
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Comment
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -2,75 +2,95 @@ package com.example.dtf.models
import androidx.compose.runtime.collection.MutableVector
import androidx.compose.runtime.collection.mutableVectorOf
import androidx.room.*
import java.util.Date
private val posts = listOf(
Post(
1,
"Что не так с half-life 2",
"Да всё не так",
1,
mutableVectorOf(
Comment(1, 2, "Пост бред. Начнём с того, что вот эта твоя манера речи клоунская...", Date(2023, 10, 20)),
Comment(2, 1, "Да какой бред, чел, я всё по факту сказал", Date(2023, 10, 20))
),
Date(2023, 10, 22)
),
Post(
2,
"Я действительно ненавижу фильм про титаник",
"Пруфов не будет",
2,
mutableVectorOf(Comment(1, 2, "Очередной бред от автора", Date(2023, 9, 20))),
Date(2023, 9, 22)
),
Post(
3,
"\"Госпожа Кагуя\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22)
),
Post(
4,
"\"Восхождение в тени\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22)
),
Post(
5,
"\"Тетрадь смерти\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22)
),
Post(
6,
"\"Бакуман\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22)
)
)
//private val posts = listOf(
// Post(
// 1,
// "Что не так с half-life 2",
// "Да всё не так",
// 1,
// mutableVectorOf(
// Comment(1, 2, "Пост бред. Начнём с того, что вот эта твоя манера речи клоунская...", Date(2023, 10, 20)),
// Comment(2, 1, "Да какой бред, чел, я всё по факту сказал", Date(2023, 10, 20))
// ),
// Date(2023, 10, 22)
// ),
// Post(
// 2,
// "Я действительно ненавижу фильм про титаник",
// "Пруфов не будет",
// 2,
// mutableVectorOf(Comment(1, 2, "Очередной бред от автора", Date(2023, 9, 20))),
// Date(2023, 9, 22)
// ),
// Post(
// 3,
// "\"Госпожа Кагуя\" это переоценённый тайтл",
// "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
// 3,
// mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
// Date(2023, 9, 22)
// ),
// Post(
// 4,
// "\"Восхождение в тени\" это переоценённый тайтл",
// "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
// 3,
// mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
// Date(2023, 9, 22)
// ),
// Post(
// 5,
// "\"Тетрадь смерти\" это переоценённый тайтл",
// "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
// 3,
// mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
// Date(2023, 9, 22)
// ),
// Post(
// 6,
// "\"Бакуман\" это переоценённый тайтл",
// "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
// 3,
// mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
// Date(2023, 9, 22)
// )
//)
@Entity(
tableName = "post",
foreignKeys = [
ForeignKey(User::class, ["id"], ["user_id"])
]
)
data class Post(
val id: Int,
var title: String,
var content: String,
val categoryId: Int,
val comments: MutableVector<Comment>,
@PrimaryKey(autoGenerate = true)
@ColumnInfo
val id: Int?,
@ColumnInfo
val title: String,
@ColumnInfo
val content: String,
@ColumnInfo
val date: Date,
)
@ColumnInfo(name = "user_id")
val userId: Int,
@ColumnInfo(name = "category_id")
val categoryId: Int
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
fun getAllPosts() : List<Post> {
return posts
if (javaClass != other?.javaClass) return false
other as Post
return id == other.id
}
fun getPostsByCategory(categoryId: Int) : List<Post> {
return posts.filter { it.categoryId == categoryId }
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,10 @@
package com.example.dtf.models
import androidx.room.Embedded
import androidx.room.Relation
data class PostWithComments(
@Embedded val post: Post,
@Relation(parentColumn = "id", entityColumn = "post_id")
val comments: List<Comment>
)

View File

@ -1,19 +1,30 @@
package com.example.dtf.models
import androidx.room.*
@Entity(tableName = "user")
data class User(
val id: Int,
@PrimaryKey(autoGenerate = true)
@ColumnInfo
val id: Int?,
@ColumnInfo
val username: String,
@ColumnInfo
val password: String,
@ColumnInfo
val isModerator: Boolean
)
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
fun getAllUsers() : List<User> {
return listOf(
User(1, "Sheriff", "123456", true),
User(2, "Shailushaika", "654321", false),
)
if (javaClass != other?.javaClass) return false
other as User
return id == other.id
}
fun currentUser() : User {
return getAllUsers()[0]
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -9,22 +9,31 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
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 com.example.dtf.models.getAllCategories
import com.example.dtf.models.getAllPosts
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.db.AppDatabase
import com.example.dtf.widgets.MyTextField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun EditPostScreen(postId: Int) {
val post = getAllPosts().first {p -> p.id == postId}
val context = LocalContext.current
val title = remember { mutableStateOf(TextFieldValue(post.title)) }
val content = remember { mutableStateOf(TextFieldValue(post.content)) }
val title = remember { mutableStateOf(TextFieldValue("")) }
val content = remember { mutableStateOf(TextFieldValue("")) }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).postDao().getById(postId).collect { data ->
title.value = TextFieldValue(data.post.title)
content.value = TextFieldValue(data.post.content)
}
}
}
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),

View File

@ -9,18 +9,33 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
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 com.example.dtf.models.getAllCategories
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Category
import com.example.dtf.widgets.MyTextField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun NewPostScreen() {
val selectedCategory = remember { mutableStateOf(getAllCategories()[0].name) }
val categories = remember { mutableStateListOf<Category>() }
val context = LocalContext.current
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).categoryDao().getAll().collect {data ->
categories.clear()
categories.addAll(data)
}
}
}
val selectedCategory = remember { mutableStateOf("") }
val expanded = remember { mutableStateOf(false) }
val title = remember { mutableStateOf(TextFieldValue()) }
@ -53,7 +68,7 @@ fun NewPostScreen() {
expanded = expanded.value,
onDismissRequest = {expanded.value = false}
) {
getAllCategories().forEach {category ->
categories.forEach {category ->
DropdownMenuItem(
onClick = {
selectedCategory.value = category.name

View File

@ -5,33 +5,44 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
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.ui.*
import androidx.compose.ui.draw.*
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import androidx.navigation.NavHostController
import com.example.dtf.R
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Comment
import com.example.dtf.models.currentUser
import com.example.dtf.models.getAllCategories
import com.example.dtf.models.getAllPosts
import com.example.dtf.models.getAllUsers
import com.example.dtf.models.getPostsByCategory
import com.example.dtf.models.Post
import com.example.dtf.models.PostWithComments
import com.example.dtf.models.User
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.widgets.MyTextField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.Date
@Composable
fun PostScreen(postId: Int, navController: NavHostController) {
val post = getAllPosts().first {p -> p.id == postId}
val post = remember { mutableStateOf(PostWithComments(Post(null, "", "", Date(), -1, -1), listOf())) }
val user = remember { mutableStateOf(User(-1, "", "", false)) }
val context = LocalContext.current
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
user.value = AppDatabase.getInstance(context).userDao().getById(1)
AppDatabase.getInstance(context).postDao().getById(postId).collect {data ->
post.value = data
}
}
}
val content = remember { mutableStateOf(TextFieldValue("")) }
@ -45,12 +56,12 @@ fun PostScreen(postId: Int, navController: NavHostController) {
) {
Text(
modifier = Modifier.padding(10.dp),
text = post.title,
text = post.value.post.title,
fontSize = 26.sp
)
Text(
modifier = Modifier.fillMaxHeight().padding(10.dp, 0.dp, 10.dp, 0.dp),
text = post.content,
text = post.value.post.content,
fontSize = 20.sp
)
Row(
@ -61,18 +72,18 @@ fun PostScreen(postId: Int, navController: NavHostController) {
Text(
text = "day.month.year".replace(
"day",
post.date.day.toString()
post.value.post.date.day.toString()
).replace(
"month",
post.date.month.toString()
post.value.post.date.month.toString()
).replace(
"year",
post.date.year.toString()
post.value.post.date.year.toString()
),
fontSize = 14.sp,
color = Color(0xFFCECCCC)
)
if (currentUser().isModerator) {
if (user.value.isModerator) {
Text(
modifier = Modifier.clickable {
navController.navigate(ScreenPaths.EditPost.route.replace("{post}", postId.toString()))
@ -126,7 +137,7 @@ fun PostScreen(postId: Int, navController: NavHostController) {
)
}
}
post.comments.forEachReversed {comment ->
post.value.comments.reversed().forEach() {comment ->
Comment(comment)
}
Spacer(modifier = Modifier.height(60.dp))
@ -135,6 +146,15 @@ fun PostScreen(postId: Int, navController: NavHostController) {
@Composable
private fun Comment(comment: Comment) {
val user = remember { mutableStateOf(User(-1, "", "", false)) }
val context = LocalContext.current
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
user.value = AppDatabase.getInstance(context).userDao().getById(comment.userId)
}
}
Column(
modifier = Modifier
.fillMaxWidth()
@ -148,7 +168,7 @@ private fun Comment(comment: Comment) {
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = getAllUsers().first {u -> u.id == comment.userId}.username,
text = user.value.username,
fontSize = 20.sp
)
Text(

View File

@ -11,17 +11,39 @@ import androidx.compose.ui.*
import androidx.compose.ui.draw.*
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import androidx.navigation.NavHostController
import com.example.dtf.models.getAllCategories
import com.example.dtf.models.getAllPosts
import com.example.dtf.models.getPostsByCategory
import com.example.dtf.db.AppDatabase
import com.example.dtf.models.Category
import com.example.dtf.models.CategoryWithPosts
import com.example.dtf.utils.ScreenPaths
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable
fun PostsScreen(navController: NavHostController) {
val currentCategory = remember { mutableStateOf(getAllCategories()[0].name) }
val currentCategory = remember { mutableStateOf("") }
val categories = remember { mutableListOf<Category> () }
val categoryWithPosts = remember { mutableStateOf(CategoryWithPosts(Category(-1, ""), listOf()))}
val context = LocalContext.current
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).categoryDao().getAll().collect {data ->
currentCategory.value = data.first().name
categories.clear()
categories.addAll(data)
AppDatabase.getInstance(context).categoryDao().getByName(currentCategory.value).collect {data ->
categoryWithPosts.value = data
}
}
}
}
Column(
modifier = Modifier.fillMaxSize(),
@ -38,7 +60,7 @@ fun PostsScreen(navController: NavHostController) {
horizontalArrangement = Arrangement.spacedBy(25.dp),
verticalAlignment = Alignment.CenterVertically
) {
Categories(currentCategory)
Categories(scope, categoryWithPosts, currentCategory, categories)
}
Row(
modifier = Modifier.fillMaxSize()
@ -47,7 +69,9 @@ fun PostsScreen(navController: NavHostController) {
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
PostsByCategory(navController, currentCategory.value)
if (currentCategory.value.isNotEmpty()) {
PostsByCategory(navController, categoryWithPosts)
}
Spacer(modifier = Modifier.height(60.dp))
}
}
@ -55,13 +79,20 @@ fun PostsScreen(navController: NavHostController) {
}
@Composable
private fun Categories(categoryState: MutableState<String>) {
private fun Categories(scope: CoroutineScope, categoryWithPosts: MutableState<CategoryWithPosts>, categoryState: MutableState<String>, categories: MutableList<Category>) {
val context = LocalContext.current
Spacer(modifier = Modifier.width(5.dp))
getAllCategories().forEach {category ->
categories.forEach {category ->
Text(
modifier = Modifier
.clickable {
categoryState.value = category.name
scope.launch {
AppDatabase.getInstance(context).categoryDao().getByName(categoryState.value).collect { data ->
categoryWithPosts.value = data
}
}
}
.drawBehind {
if (category.name == categoryState.value) {
@ -82,8 +113,8 @@ private fun Categories(categoryState: MutableState<String>) {
}
@Composable
private fun PostsByCategory(navController: NavHostController, category: String) {
getPostsByCategory(getAllCategories().first { c -> c.name == category }.id).forEach { post ->
private fun PostsByCategory(navController: NavHostController, categoryWithPosts: MutableState<CategoryWithPosts>) {
categoryWithPosts.value.posts.forEach { post ->
Column(
modifier = Modifier
.fillMaxHeight(0.3f)

View File

@ -5,15 +5,31 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.*
import androidx.navigation.NavHostController
import com.example.dtf.models.currentUser
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
@Composable
fun ProfileScreen(navController: NavHostController) {
val user = remember { mutableStateOf(User(-1, "", "", false)) }
val context = LocalContext.current
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
user.value = AppDatabase.getInstance(context).userDao().getById(1)
}
}
Column(
modifier = Modifier.fillMaxSize()
) {
@ -36,14 +52,14 @@ fun ProfileScreen(navController: NavHostController) {
contentAlignment = Alignment.Center
) {
Text(
text = currentUser().username[0].toString(),
text = if (user.value.username.isNotEmpty()) { user.value.username[0].toString() } else {""},
fontSize = 30.sp,
)
}
}
Spacer(modifier = Modifier.fillMaxHeight(0.1f))
Text(
text = currentUser().username,
text = user.value.username,
fontSize = 30.sp,
color = Color.White
)

View File

@ -15,20 +15,51 @@ import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Person
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.compose.currentBackStackEntryAsState
import com.example.dtf.models.currentUser
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
@Composable
fun BottomNavBar(navController: NavController) {
val user = remember { mutableStateOf(User(-1, "", "", false)) }
val buttons = remember { mutableListOf(
Triple(ScreenPaths.Posts.route, Icons.Default.Home, "Новости"),
Triple(ScreenPaths.Profile.route, Icons.Default.Person, "Профиль")
) }
val context = LocalContext.current
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
user.value = AppDatabase.getInstance(context).userDao().getById(1)
if (user.value.isModerator) {
buttons.clear()
buttons.addAll(
listOf(
Triple(ScreenPaths.Posts.route, Icons.Default.Home, "Новости"),
Triple(ScreenPaths.NewPost.route, Icons.Default.Edit, "Создать"),
Triple(ScreenPaths.Profile.route, Icons.Default.Person, "Профиль")
)
)
}
}
}
BottomNavigation(
modifier = Modifier.height(70.dp),
backgroundColor = Color.White,
@ -38,7 +69,7 @@ fun BottomNavBar(navController: NavController) {
val currentRoute = navBackStackEntry?.destination?.route
listOfNotNull(
Triple(ScreenPaths.Posts.route, Icons.Default.Home, "Новости"),
if (currentUser().isModerator) {
if (user.value.isModerator) {
Triple(ScreenPaths.NewPost.route, Icons.Default.Edit, "Создать")
} else { null },
Triple(ScreenPaths.Profile.route, Icons.Default.Person, "Профиль")