Лабораторная работа №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 { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
} }
android { android {
@ -18,6 +19,10 @@ android {
vectorDrawables { vectorDrawables {
useSupportLibrary true useSupportLibrary true
} }
kapt {
arguments {arg("room.schemaLocation", "$projectDir/schemas")}
}
} }
buildTypes { buildTypes {
@ -62,4 +67,8 @@ dependencies {
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$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.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navigation import androidx.navigation.navigation
import com.example.dtf.models.currentUser
import com.example.dtf.screens.EditPostScreen import com.example.dtf.screens.EditPostScreen
import com.example.dtf.screens.LoginScreen import com.example.dtf.screens.LoginScreen
import com.example.dtf.screens.NewPostScreen 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 package com.example.dtf.models
data class Category( import androidx.room.*
val id: Int,
val name: String
)
fun getAllCategories() : List<Category> { @Entity(tableName = "category")
return listOf( data class Category(
Category(1, "Игры"), @PrimaryKey(autoGenerate = true)
Category(2, "Кино"), @ColumnInfo
Category(3, "Аниме"), 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 package com.example.dtf.models
import androidx.room.*
import java.util.Date import java.util.Date
@Entity(tableName = "comment")
data class Comment( data class Comment(
val id: Int, @PrimaryKey(autoGenerate = true)
@ColumnInfo
val id: Int?,
@ColumnInfo(name = "user_id")
val userId: Int, val userId: Int,
@ColumnInfo(name = "post_id")
val postId: Int,
@ColumnInfo
val content: String, val content: String,
@ColumnInfo
val date: Date 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.MutableVector
import androidx.compose.runtime.collection.mutableVectorOf import androidx.compose.runtime.collection.mutableVectorOf
import androidx.room.*
import java.util.Date import java.util.Date
private val posts = listOf( //private val posts = listOf(
Post( // Post(
1, // 1,
"Что не так с half-life 2", // "Что не так с half-life 2",
"Да всё не так", // "Да всё не так",
1, // 1,
mutableVectorOf( // mutableVectorOf(
Comment(1, 2, "Пост бред. Начнём с того, что вот эта твоя манера речи клоунская...", Date(2023, 10, 20)), // Comment(1, 2, "Пост бред. Начнём с того, что вот эта твоя манера речи клоунская...", Date(2023, 10, 20)),
Comment(2, 1, "Да какой бред, чел, я всё по факту сказал", Date(2023, 10, 20)) // Comment(2, 1, "Да какой бред, чел, я всё по факту сказал", Date(2023, 10, 20))
), // ),
Date(2023, 10, 22) // Date(2023, 10, 22)
), // ),
Post( // Post(
2, // 2,
"Я действительно ненавижу фильм про титаник", // "Я действительно ненавижу фильм про титаник",
"Пруфов не будет", // "Пруфов не будет",
2, // 2,
mutableVectorOf(Comment(1, 2, "Очередной бред от автора", Date(2023, 9, 20))), // mutableVectorOf(Comment(1, 2, "Очередной бред от автора", Date(2023, 9, 20))),
Date(2023, 9, 22) // Date(2023, 9, 22)
), // ),
Post( // Post(
3, // 3,
"\"Госпожа Кагуя\" это переоценённый тайтл", // "\"Госпожа Кагуя\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", // "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3, // 3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), // mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22) // Date(2023, 9, 22)
), // ),
Post( // Post(
4, // 4,
"\"Восхождение в тени\" это переоценённый тайтл", // "\"Восхождение в тени\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", // "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3, // 3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), // mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22) // Date(2023, 9, 22)
), // ),
Post( // Post(
5, // 5,
"\"Тетрадь смерти\" это переоценённый тайтл", // "\"Тетрадь смерти\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", // "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3, // 3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), // mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22) // Date(2023, 9, 22)
), // ),
Post( // Post(
6, // 6,
"\"Бакуман\" это переоценённый тайтл", // "\"Бакуман\" это переоценённый тайтл",
"Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", // "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово",
3, // 3,
mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), // mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))),
Date(2023, 9, 22) // Date(2023, 9, 22)
) // )
) //)
@Entity(
tableName = "post",
foreignKeys = [
ForeignKey(User::class, ["id"], ["user_id"])
]
)
data class Post( data class Post(
val id: Int, @PrimaryKey(autoGenerate = true)
var title: String, @ColumnInfo
var content: String, val id: Int?,
val categoryId: Int, @ColumnInfo
val comments: MutableVector<Comment>, val title: String,
@ColumnInfo
val content: String,
@ColumnInfo
val date: Date, 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> { if (javaClass != other?.javaClass) return false
return posts
other as Post
return id == other.id
} }
fun getPostsByCategory(categoryId: Int) : List<Post> { override fun hashCode(): Int {
return posts.filter { it.categoryId == categoryId } 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 package com.example.dtf.models
import androidx.room.*
@Entity(tableName = "user")
data class User( data class User(
val id: Int, @PrimaryKey(autoGenerate = true)
@ColumnInfo
val id: Int?,
@ColumnInfo
val username: String, val username: String,
@ColumnInfo
val password: String, val password: String,
@ColumnInfo
val isModerator: Boolean val isModerator: Boolean
) ) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
fun getAllUsers() : List<User> { if (javaClass != other?.javaClass) return false
return listOf(
User(1, "Sheriff", "123456", true), other as User
User(2, "Shailushaika", "654321", false),
) return id == other.id
} }
fun currentUser() : User { override fun hashCode(): Int {
return getAllUsers()[0] return id ?: -1
}
} }

View File

@ -9,22 +9,31 @@ import androidx.compose.material.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.* import androidx.compose.ui.*
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import com.example.dtf.models.getAllCategories import com.example.dtf.db.AppDatabase
import com.example.dtf.models.getAllPosts
import com.example.dtf.utils.ScreenPaths
import com.example.dtf.widgets.MyTextField import com.example.dtf.widgets.MyTextField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable @Composable
fun EditPostScreen(postId: Int) { fun EditPostScreen(postId: Int) {
val post = getAllPosts().first {p -> p.id == postId} val context = LocalContext.current
val title = remember { mutableStateOf(TextFieldValue(post.title)) } val title = remember { mutableStateOf(TextFieldValue("")) }
val content = remember { mutableStateOf(TextFieldValue(post.content)) } 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( Column(
modifier = Modifier.verticalScroll(rememberScrollState()), modifier = Modifier.verticalScroll(rememberScrollState()),

View File

@ -9,18 +9,33 @@ import androidx.compose.material.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.* import androidx.compose.ui.*
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import com.example.dtf.models.getAllCategories import com.example.dtf.db.AppDatabase
import com.example.dtf.utils.ScreenPaths import com.example.dtf.models.Category
import com.example.dtf.widgets.MyTextField import com.example.dtf.widgets.MyTextField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable @Composable
fun NewPostScreen() { 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 expanded = remember { mutableStateOf(false) }
val title = remember { mutableStateOf(TextFieldValue()) } val title = remember { mutableStateOf(TextFieldValue()) }
@ -53,7 +68,7 @@ fun NewPostScreen() {
expanded = expanded.value, expanded = expanded.value,
onDismissRequest = {expanded.value = false} onDismissRequest = {expanded.value = false}
) { ) {
getAllCategories().forEach {category -> categories.forEach {category ->
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
selectedCategory.value = category.name selectedCategory.value = category.name

View File

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

View File

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

View File

@ -5,15 +5,31 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.Composable 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.*
import androidx.compose.ui.graphics.* import androidx.compose.ui.graphics.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import androidx.navigation.NavHostController 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 com.example.dtf.utils.ScreenPaths
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable @Composable
fun ProfileScreen(navController: NavHostController) { 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( Column(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
@ -36,14 +52,14 @@ fun ProfileScreen(navController: NavHostController) {
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text( Text(
text = currentUser().username[0].toString(), text = if (user.value.username.isNotEmpty()) { user.value.username[0].toString() } else {""},
fontSize = 30.sp, fontSize = 30.sp,
) )
} }
} }
Spacer(modifier = Modifier.fillMaxHeight(0.1f)) Spacer(modifier = Modifier.fillMaxHeight(0.1f))
Text( Text(
text = currentUser().username, text = user.value.username,
fontSize = 30.sp, fontSize = 30.sp,
color = Color.White 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.Home
import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.compose.currentBackStackEntryAsState 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 com.example.dtf.utils.ScreenPaths
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable @Composable
fun BottomNavBar(navController: NavController) { 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( BottomNavigation(
modifier = Modifier.height(70.dp), modifier = Modifier.height(70.dp),
backgroundColor = Color.White, backgroundColor = Color.White,
@ -38,7 +69,7 @@ fun BottomNavBar(navController: NavController) {
val currentRoute = navBackStackEntry?.destination?.route val currentRoute = navBackStackEntry?.destination?.route
listOfNotNull( listOfNotNull(
Triple(ScreenPaths.Posts.route, Icons.Default.Home, "Новости"), Triple(ScreenPaths.Posts.route, Icons.Default.Home, "Новости"),
if (currentUser().isModerator) { if (user.value.isModerator) {
Triple(ScreenPaths.NewPost.route, Icons.Default.Edit, "Создать") Triple(ScreenPaths.NewPost.route, Icons.Default.Edit, "Создать")
} else { null }, } else { null },
Triple(ScreenPaths.Profile.route, Icons.Default.Person, "Профиль") Triple(ScreenPaths.Profile.route, Icons.Default.Person, "Профиль")