diff --git a/app/build.gradle b/app/build.gradle index 76c65d4..a8c82a9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' } \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/MainActivity.kt b/app/src/main/java/com/example/dtf/MainActivity.kt index e5f7fee..1341af0 100644 --- a/app/src/main/java/com/example/dtf/MainActivity.kt +++ b/app/src/main/java/com/example/dtf/MainActivity.kt @@ -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 diff --git a/app/src/main/java/com/example/dtf/dao/CategoryDao.kt b/app/src/main/java/com/example/dtf/dao/CategoryDao.kt new file mode 100644 index 0000000..b749de7 --- /dev/null +++ b/app/src/main/java/com/example/dtf/dao/CategoryDao.kt @@ -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> + + @Query("select * from category where category.id = :id") + fun getById(id: Int) : Flow + + @Query("select * from category where category.name = :name") + fun getByName(name: String) : Flow +} diff --git a/app/src/main/java/com/example/dtf/dao/CommentDao.kt b/app/src/main/java/com/example/dtf/dao/CommentDao.kt new file mode 100644 index 0000000..6913a96 --- /dev/null +++ b/app/src/main/java/com/example/dtf/dao/CommentDao.kt @@ -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> + + @Query("select * from comment where comment.id = :id") + fun getById(id: Int) : Flow + + @Query("select * from comment where comment.post_id = :postId") + fun getByPost(postId: Int) : Flow> +} diff --git a/app/src/main/java/com/example/dtf/dao/PostDao.kt b/app/src/main/java/com/example/dtf/dao/PostDao.kt new file mode 100644 index 0000000..037156b --- /dev/null +++ b/app/src/main/java/com/example/dtf/dao/PostDao.kt @@ -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> + + @Query("select * from post where post.id = :id") + fun getById(id: Int) : Flow + + @Query("select * from post where post.category_id = :categoryId") + fun getByCategory(categoryId: String) : Flow +} diff --git a/app/src/main/java/com/example/dtf/dao/UserDao.kt b/app/src/main/java/com/example/dtf/dao/UserDao.kt new file mode 100644 index 0000000..436b2f5 --- /dev/null +++ b/app/src/main/java/com/example/dtf/dao/UserDao.kt @@ -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> + + @Query("select * from user where user.id = :id") + fun getById(id: Int): User +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/db/AppDatabase.kt b/app/src/main/java/com/example/dtf/db/AppDatabase.kt new file mode 100644 index 0000000..7359ca3 --- /dev/null +++ b/app/src/main/java/com/example/dtf/db/AppDatabase.kt @@ -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 } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/db/Converter.kt b/app/src/main/java/com/example/dtf/db/Converter.kt new file mode 100644 index 0000000..f6235ac --- /dev/null +++ b/app/src/main/java/com/example/dtf/db/Converter.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/models/Category.kt b/app/src/main/java/com/example/dtf/models/Category.kt index 5e81eb7..9700eca 100644 --- a/app/src/main/java/com/example/dtf/models/Category.kt +++ b/app/src/main/java/com/example/dtf/models/Category.kt @@ -1,14 +1,26 @@ package com.example.dtf.models -data class Category( - val id: Int, - val name: String -) +import androidx.room.* -fun getAllCategories() : List { - 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 + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/models/CategoryWithPosts.kt b/app/src/main/java/com/example/dtf/models/CategoryWithPosts.kt new file mode 100644 index 0000000..f666c25 --- /dev/null +++ b/app/src/main/java/com/example/dtf/models/CategoryWithPosts.kt @@ -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 +) diff --git a/app/src/main/java/com/example/dtf/models/Comment.kt b/app/src/main/java/com/example/dtf/models/Comment.kt index ce35473..fc3c6d7 100644 --- a/app/src/main/java/com/example/dtf/models/Comment.kt +++ b/app/src/main/java/com/example/dtf/models/Comment.kt @@ -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 + } +} diff --git a/app/src/main/java/com/example/dtf/models/Post.kt b/app/src/main/java/com/example/dtf/models/Post.kt index e9a2fe8..25c3b6a 100644 --- a/app/src/main/java/com/example/dtf/models/Post.kt +++ b/app/src/main/java/com/example/dtf/models/Post.kt @@ -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, + @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 { - return posts -} + if (javaClass != other?.javaClass) return false -fun getPostsByCategory(categoryId: Int) : List { - return posts.filter { it.categoryId == categoryId } + other as Post + + return id == other.id + } + + override fun hashCode(): Int { + return id ?: -1 + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/models/PostWithComments.kt b/app/src/main/java/com/example/dtf/models/PostWithComments.kt new file mode 100644 index 0000000..ef64a9a --- /dev/null +++ b/app/src/main/java/com/example/dtf/models/PostWithComments.kt @@ -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 +) diff --git a/app/src/main/java/com/example/dtf/models/User.kt b/app/src/main/java/com/example/dtf/models/User.kt index 5f33ec5..f04ed75 100644 --- a/app/src/main/java/com/example/dtf/models/User.kt +++ b/app/src/main/java/com/example/dtf/models/User.kt @@ -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 { - 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 + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/EditPostScreen.kt b/app/src/main/java/com/example/dtf/screens/EditPostScreen.kt index ed8e556..377aa2b 100644 --- a/app/src/main/java/com/example/dtf/screens/EditPostScreen.kt +++ b/app/src/main/java/com/example/dtf/screens/EditPostScreen.kt @@ -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()), diff --git a/app/src/main/java/com/example/dtf/screens/NewPostScreen.kt b/app/src/main/java/com/example/dtf/screens/NewPostScreen.kt index c31b977..78a1753 100644 --- a/app/src/main/java/com/example/dtf/screens/NewPostScreen.kt +++ b/app/src/main/java/com/example/dtf/screens/NewPostScreen.kt @@ -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() } + 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 diff --git a/app/src/main/java/com/example/dtf/screens/PostScreen.kt b/app/src/main/java/com/example/dtf/screens/PostScreen.kt index 0c7ca14..98fa7c7 100644 --- a/app/src/main/java/com/example/dtf/screens/PostScreen.kt +++ b/app/src/main/java/com/example/dtf/screens/PostScreen.kt @@ -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( diff --git a/app/src/main/java/com/example/dtf/screens/PostsScreen.kt b/app/src/main/java/com/example/dtf/screens/PostsScreen.kt index a51fabe..6fa8247 100644 --- a/app/src/main/java/com/example/dtf/screens/PostsScreen.kt +++ b/app/src/main/java/com/example/dtf/screens/PostsScreen.kt @@ -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 () } + 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) { +private fun Categories(scope: CoroutineScope, categoryWithPosts: MutableState, categoryState: MutableState, categories: MutableList) { + 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) { } @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.value.posts.forEach { post -> Column( modifier = Modifier .fillMaxHeight(0.3f) diff --git a/app/src/main/java/com/example/dtf/screens/ProfileScreen.kt b/app/src/main/java/com/example/dtf/screens/ProfileScreen.kt index 3154798..25af8c9 100644 --- a/app/src/main/java/com/example/dtf/screens/ProfileScreen.kt +++ b/app/src/main/java/com/example/dtf/screens/ProfileScreen.kt @@ -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 ) diff --git a/app/src/main/java/com/example/dtf/widgets/BottomNavBar.kt b/app/src/main/java/com/example/dtf/widgets/BottomNavBar.kt index 7002357..6de2910 100644 --- a/app/src/main/java/com/example/dtf/widgets/BottomNavBar.kt +++ b/app/src/main/java/com/example/dtf/widgets/BottomNavBar.kt @@ -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, "Профиль")