Лабораторная работа №3: как будто бы готова
This commit is contained in:
parent
a3ef2c1f94
commit
392b9e5c0a
@ -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'
|
||||
}
|
@ -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
|
||||
|
26
app/src/main/java/com/example/dtf/dao/CategoryDao.kt
Normal file
26
app/src/main/java/com/example/dtf/dao/CategoryDao.kt
Normal 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>
|
||||
}
|
26
app/src/main/java/com/example/dtf/dao/CommentDao.kt
Normal file
26
app/src/main/java/com/example/dtf/dao/CommentDao.kt
Normal 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>>
|
||||
}
|
26
app/src/main/java/com/example/dtf/dao/PostDao.kt
Normal file
26
app/src/main/java/com/example/dtf/dao/PostDao.kt
Normal 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>
|
||||
}
|
23
app/src/main/java/com/example/dtf/dao/UserDao.kt
Normal file
23
app/src/main/java/com/example/dtf/dao/UserDao.kt
Normal 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
|
||||
}
|
147
app/src/main/java/com/example/dtf/db/AppDatabase.kt
Normal file
147
app/src/main/java/com/example/dtf/db/AppDatabase.kt
Normal 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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
16
app/src/main/java/com/example/dtf/db/Converter.kt
Normal file
16
app/src/main/java/com/example/dtf/db/Converter.kt
Normal 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()
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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>
|
||||
)
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
fun getPostsByCategory(categoryId: Int) : List<Post> {
|
||||
return posts.filter { it.categoryId == categoryId }
|
||||
other as Post
|
||||
|
||||
return id == other.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id ?: -1
|
||||
}
|
||||
}
|
10
app/src/main/java/com/example/dtf/models/PostWithComments.kt
Normal file
10
app/src/main/java/com/example/dtf/models/PostWithComments.kt
Normal 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>
|
||||
)
|
@ -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
|
||||
|
||||
fun currentUser() : User {
|
||||
return getAllUsers()[0]
|
||||
other as User
|
||||
|
||||
return id == other.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id ?: -1
|
||||
}
|
||||
}
|
@ -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()),
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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, "Профиль")
|
||||
|
Loading…
Reference in New Issue
Block a user