Compare commits

...

5 Commits

30 changed files with 989 additions and 240 deletions

View File

@ -76,4 +76,8 @@ dependencies {
ksp("androidx.room:room-compiler:$room_version") ksp("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version") implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version") implementation("androidx.room:room-paging:$room_version")
//Paging
implementation ("androidx.paging:paging-compose:3.2.1")
implementation ("androidx.paging:paging-runtime:3.2.1")
} }

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<application <application
android:name=".MobileApp"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"

View File

@ -0,0 +1,12 @@
package com.example.mobileapp
import android.app.Application
class MobileApp: Application() {
lateinit var container: MobileAppContainer
override fun onCreate() {
super.onCreate()
container = MobileAppDataContainer(this)
}
}

View File

@ -0,0 +1,34 @@
package com.example.mobileapp
import android.content.Context
import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.repositories.MailRepository
import com.example.mobileapp.database.repositories.OfflineMailRepository
import com.example.mobileapp.database.repositories.OfflineStoryRepository
import com.example.mobileapp.database.repositories.OfflineUserRepository
import com.example.mobileapp.database.repositories.StoryRepository
import com.example.mobileapp.database.repositories.UserRepository
interface MobileAppContainer {
val mailRepository: MailRepository
val storyRepository: StoryRepository
val userRepository: UserRepository
}
class MobileAppDataContainer(private val context: Context): MobileAppContainer {
override val mailRepository: MailRepository by lazy {
OfflineMailRepository(MobileAppDataBase.getInstance(context).mailDao())
}
override val storyRepository: StoryRepository by lazy {
OfflineStoryRepository(MobileAppDataBase.getInstance(context).storyDao())
}
override val userRepository: UserRepository by lazy {
OfflineUserRepository(MobileAppDataBase.getInstance(context).userDao())
}
companion object{
const val TIMEOUT = 5000L
}
}

View File

@ -82,13 +82,14 @@ fun PasswordInputField(label: String, startValue: String? = null, onPasswordChan
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SearchInputField(){ fun SearchInputField(onTextChanged: (String) -> Unit){
var text = remember { mutableStateOf("") } var text = remember { mutableStateOf("") }
OutlinedTextField( OutlinedTextField(
value = text.value, value = text.value,
onValueChange = { onValueChange = {
text.value = it text.value = it
onTextChanged(it)
}, },
leadingIcon = { leadingIcon = {
Icon( Icon(

View File

@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
@ -36,15 +38,22 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
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.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.R import com.example.mobileapp.R
import com.example.mobileapp.database.MobileAppDataBase import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.Mail import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.entities.Story import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.StoryViewModel
import com.example.mobileapp.database.viewmodels.UserViewModel
import com.example.mobileapp.ui.theme.BackgroundItem2 import com.example.mobileapp.ui.theme.BackgroundItem2
import com.example.mobileapp.ui.theme.ButtonColor1 import com.example.mobileapp.ui.theme.ButtonColor1
import com.example.mobileapp.ui.theme.ButtonColor2 import com.example.mobileapp.ui.theme.ButtonColor2
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@ -67,7 +76,7 @@ fun <T : Any> DataListScroll(navController: NavHostController, dataList: List<T>
items(dataList){ item -> items(dataList){ item ->
when(item){ when(item){
is Story -> StoryListItem(item = item, navController = navController) is Story -> StoryListItem(item = item, navController = navController)
is Mail -> MailListItem(item = item) is Mail -> MailListItem(item = item, navController = navController)
} }
} }
} }
@ -78,9 +87,11 @@ inline fun <reified T> List<*>.isListOf(): Boolean {
} }
@Composable @Composable
fun StoryListItem(item: Story, navController: NavHostController){ fun StoryListItem(item: Story, navController: NavHostController,
val context = LocalContext.current isReadOnly: Boolean? = false,
storyViewModel: StoryViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val isExpanded = remember { val isExpanded = remember {
mutableStateOf(false) mutableStateOf(false)
} }
@ -89,10 +100,6 @@ fun StoryListItem(item: Story, navController: NavHostController){
mutableStateOf(false) mutableStateOf(false)
} }
val delete = remember {
mutableStateOf(false)
}
Card( Card(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -135,16 +142,24 @@ fun StoryListItem(item: Story, navController: NavHostController){
visible = isExpanded.value, visible = isExpanded.value,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Row( if (isReadOnly!!){
modifier = Modifier.fillMaxWidth(), DataListItemButton(label = "Подробнее", backgroundColor = ButtonColor2,
horizontalArrangement = Arrangement.End textColor = Color.White, onClickAction = {
){ navController.navigate("viewstory/${item.id}")
DataListItemButton("Изменить", ButtonColor2, Color.White, onClickAction = { })
navController.navigate("editstory/${item.id}") }
}) else{
DataListItemButton("Удалить", Color.Red, Color.White, onClickAction = { Row(
showDialog.value = !showDialog.value modifier = Modifier.fillMaxWidth(),
}) horizontalArrangement = Arrangement.End
){
DataListItemButton("Изменить", ButtonColor2, Color.White, onClickAction = {
navController.navigate("editstory/${item.id}")
})
DataListItemButton("Удалить", Color.Red, Color.White, onClickAction = {
showDialog.value = !showDialog.value
})
}
} }
} }
} }
@ -153,22 +168,12 @@ fun StoryListItem(item: Story, navController: NavHostController){
if(showDialog.value) { if(showDialog.value) {
DialogWindow(label = "Подтверждение", DialogWindow(label = "Подтверждение",
message = "Вы уверены что хотите удалить запись?", onConfirmAction = { message = "Вы уверены что хотите удалить запись?", onConfirmAction = {
delete.value = !delete.value storyViewModel.deleteStory(item)
showDialog.value = !showDialog.value showDialog.value = !showDialog.value
}, onDismissAction = { }, onDismissAction = {
showDialog.value = !showDialog.value showDialog.value = !showDialog.value
}) })
} }
if(delete.value) {
LaunchedEffect(Unit){
withContext(Dispatchers.IO){
MobileAppDataBase.getInstance(context).storyDao().delete(item)
}
}
delete.value = !delete.value
navController.navigate("story")
}
} }
@Composable @Composable
@ -190,11 +195,24 @@ fun DataListItemButton(label: String, backgroundColor: Color, textColor: Color,
} }
@Composable @Composable
fun MailListItem(item: Mail){ fun MailListItem(item: Mail, navController: NavHostController,
userViewModel: UserViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val isExpanded = remember { val isExpanded = remember {
mutableStateOf(false) mutableStateOf(false)
} }
val userName = remember { mutableStateOf("") }
LaunchedEffect(Unit){
userViewModel.getUser(item.userId).collect {
if (it != null) {
userName.value = it.email
}
}
}
Card( Card(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -226,7 +244,7 @@ fun MailListItem(item: Mail){
modifier = Modifier.padding(8.dp) modifier = Modifier.padding(8.dp)
){ ){
Text( Text(
text = "item.username | ${dateFormat.format(Date(item.postdate!!))}", text = "${userName.value} | ${dateFormat.format(Date(item.postdate!!))}",
fontSize = 20.sp, fontSize = 20.sp,
fontWeight = FontWeight.Bold) fontWeight = FontWeight.Bold)
Text(text = item.message) Text(text = item.message)
@ -236,22 +254,10 @@ fun MailListItem(item: Mail){
visible = isExpanded.value, visible = isExpanded.value,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Button( DataListItemButton(label = "Подробнее", backgroundColor = ButtonColor2,
onClick = { /* Действие при нажатии кнопки */ }, textColor = Color.White, onClickAction = {
modifier = Modifier navController.navigate("viewmail/${item.id}")
.requiredHeight(64.dp) })
.fillMaxWidth(),
shape = RoundedCornerShape(10.dp),
colors = ButtonDefaults.buttonColors(
containerColor = ButtonColor2
)
) {
Text(
text = "Подробнее",
color = Color.White,
fontSize = 18.sp,
)
}
} }
} }
} }

View File

@ -18,13 +18,17 @@ import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
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.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
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.NavHostController import androidx.navigation.NavHostController
@ -37,11 +41,13 @@ import com.example.mobileapp.screens.Authorization
import com.example.mobileapp.screens.EditMailScreen import com.example.mobileapp.screens.EditMailScreen
import com.example.mobileapp.screens.EditStoryScreen import com.example.mobileapp.screens.EditStoryScreen
import com.example.mobileapp.screens.EditUserScreen import com.example.mobileapp.screens.EditUserScreen
import com.example.mobileapp.screens.ListDataScreen
import com.example.mobileapp.screens.ListMailScreen import com.example.mobileapp.screens.ListMailScreen
import com.example.mobileapp.screens.ListStoryScreen
import com.example.mobileapp.screens.MailViewScreen
import com.example.mobileapp.screens.MainScreen import com.example.mobileapp.screens.MainScreen
import com.example.mobileapp.screens.Registration import com.example.mobileapp.screens.Registration
import com.example.mobileapp.screens.SettingsScreen import com.example.mobileapp.screens.SettingsScreen
import com.example.mobileapp.screens.StoryViewScreen
val navBarItems = listOf( val navBarItems = listOf(
NavBarItem(route = "main", label = "Главная", icon = R.drawable.home), NavBarItem(route = "main", label = "Главная", icon = R.drawable.home),
@ -53,9 +59,32 @@ val navBarItems = listOf(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun NavBar(navController: NavHostController) { fun NavBar(navController: NavHostController) {
val topBarState = rememberSaveable { (mutableStateOf(false)) }
val bottomBarState = rememberSaveable { (mutableStateOf(false)) } val bottomBarState = rememberSaveable { (mutableStateOf(false)) }
Scaffold( Scaffold(
topBar = {
AnimatedVisibility(
visible = topBarState.value,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
content = {
TopAppBar(
title = {
Text(
text = "Storyteller!",
textAlign = TextAlign.Center,
fontFamily = FontFamily(
Font(
R.font.irishgrover_regular, FontWeight.Bold
)
)
)
}
)
}
)
},
bottomBar = { bottomBar = {
AnimatedVisibility( AnimatedVisibility(
visible = bottomBarState.value, visible = bottomBarState.value,
@ -98,30 +127,37 @@ fun NavBar(navController: NavHostController) {
modifier = Modifier.padding(innerPaddings) modifier = Modifier.padding(innerPaddings)
) { ) {
composable("authorization"){ composable("authorization"){
topBarState.value = true
bottomBarState.value = false bottomBarState.value = false
Authorization(navController = navController) Authorization(navController = navController)
} }
composable("registration"){ composable("registration"){
topBarState.value = true
bottomBarState.value = false bottomBarState.value = false
Registration(navController = navController) Registration(navController = navController)
} }
composable("main"){ composable("main"){
topBarState.value = false
bottomBarState.value = true bottomBarState.value = true
MainScreen(navController = navController) MainScreen(navController = navController)
} }
composable("story"){ composable("story"){
topBarState.value = false
bottomBarState.value = true bottomBarState.value = true
ListDataScreen(navController = navController) ListStoryScreen(navController = navController)
} }
composable("mail"){ composable("mail"){
topBarState.value = false
bottomBarState.value = true bottomBarState.value = true
ListMailScreen(navController = navController) ListMailScreen(navController = navController)
} }
composable("settings"){ composable("settings"){
topBarState.value = true
bottomBarState.value = true bottomBarState.value = true
SettingsScreen(navController = navController) SettingsScreen(navController = navController)
} }
composable("editstory"){ // Без аргумента composable("editstory"){ // Без аргумента
topBarState.value = false
bottomBarState.value = false bottomBarState.value = false
EditStoryScreen(navController = navController) EditStoryScreen(navController = navController)
} }
@ -130,18 +166,41 @@ fun NavBar(navController: NavHostController) {
arguments = listOf(navArgument("id") { type = NavType.IntType }) //С аргументом arguments = listOf(navArgument("id") { type = NavType.IntType }) //С аргументом
) { backStackEntry -> ) { backStackEntry ->
backStackEntry.arguments?.let { backStackEntry.arguments?.let {
topBarState.value = false
bottomBarState.value = false bottomBarState.value = false
EditStoryScreen(navController = navController, storyId = it.getInt("id")) EditStoryScreen(navController = navController, storyId = it.getInt("id"))
} }
} }
composable("editmail"){ // Без аргумента composable("editmail"){ // Без аргумента
topBarState.value = false
bottomBarState.value = false bottomBarState.value = false
EditMailScreen(navController = navController) EditMailScreen(navController = navController)
} }
composable("edituser"){ composable("edituser"){
topBarState.value = false
bottomBarState.value = false bottomBarState.value = false
EditUserScreen(navController = navController) EditUserScreen(navController = navController)
} }
composable(
"viewstory/{id}",
arguments = listOf(navArgument("id") { type = NavType.IntType }) //С аргументом
) { backStackEntry ->
backStackEntry.arguments?.let {
topBarState.value = false
bottomBarState.value = false
StoryViewScreen(navController = navController, storyId = it.getInt("id"))
}
}
composable(
"viewmail/{id}",
arguments = listOf(navArgument("id") { type = NavType.IntType }) //С аргументом
) { backStackEntry ->
backStackEntry.arguments?.let {
topBarState.value = false
bottomBarState.value = false
MailViewScreen(navController = navController, mailId = it.getInt("id"))
}
}
} }
} }
} }

View File

@ -45,8 +45,16 @@ abstract class MobileAppDataBase : RoomDatabase() {
cover = BitmapFactory.decodeResource(appContext.resources, R.drawable.king), userId = 2)) cover = BitmapFactory.decodeResource(appContext.resources, R.drawable.king), userId = 2))
val mailDao = database.mailDao() val mailDao = database.mailDao()
mailDao.insert(Mail(message = "Выложил новые страницы", userId = 1)) for (i in 0..50){
mailDao.insert(Mail(message = "Меня отменили в Твиттере", userId = 2)) if (i % 2 == 0){
mailDao.insert(Mail(message = "Выложил новые страницы", userId = 1))
}
else{
mailDao.insert(Mail(message = "Меня отменили в Твиттере", userId = 2))
}
}
/*mailDao.insert(Mail(message = "Выложил новые страницы", userId = 1))
mailDao.insert(Mail(message = "Меня отменили в Твиттере", userId = 2))*/
} }
} }

View File

@ -1,5 +1,6 @@
package com.example.mobileapp.database.dao package com.example.mobileapp.database.dao
import androidx.paging.PagingSource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
@ -7,15 +8,16 @@ import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import androidx.room.Update import androidx.room.Update
import com.example.mobileapp.database.entities.Mail import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.entities.Story
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface MailDao { interface MailDao {
@Query("select * from mails") @Query("select * from mails order by id desc")
fun getAll(): Flow<List<Mail>> fun getAll(): PagingSource<Int, Mail>
@Query("select * from mails where mails.id = :id") @Query("select * from mails where mails.id = :id")
fun getById(id: Int): Mail? fun getById(id: Int): Flow<Mail?>
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(mail: Mail) suspend fun insert(mail: Mail)

View File

@ -1,5 +1,6 @@
package com.example.mobileapp.database.dao package com.example.mobileapp.database.dao
import androidx.paging.PagingSource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
@ -11,14 +12,14 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface StoryDao { interface StoryDao {
@Query("select * from stories") @Query("select * from stories order by id desc")
fun getAll(): Flow<List<Story>> fun getAll(): PagingSource<Int, Story>
@Query("select * from stories where stories.id = :id") @Query("select * from stories where stories.id = :id")
fun getById(id: Int): Story? fun getById(id: Int): Flow<Story?>
@Query("select * from stories where stories.user_id = :userId") @Query("select * from stories where stories.user_id = :userId order by stories.id desc")
fun getByUserId(userId: Int): Flow<List<Story>> fun getByUserId(userId: Int): PagingSource<Int, Story>
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(story: Story) suspend fun insert(story: Story)

View File

@ -12,10 +12,13 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface UserDao { interface UserDao {
@Query("select * from users") @Query("select * from users")
fun getAll():Flow<List<User>> fun getAll(): Flow<List<User>>
@Query("select * from users where users.id = :id") @Query("select * from users where users.id = :id")
fun getById(id: Int): User? fun getById(id: Int): Flow<User?>
@Query("select * from users where users.login = :login")
suspend fun getByLogin(login: String): User?
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(user: User) suspend fun insert(user: User)

View File

@ -0,0 +1,17 @@
package com.example.mobileapp.database.repositories
import androidx.paging.PagingData
import com.example.mobileapp.database.entities.Mail
import kotlinx.coroutines.flow.Flow
interface MailRepository {
fun getAllMails(): Flow<PagingData<Mail>>
fun getMail(id: Int): Flow<Mail?>
suspend fun insertMail(mail: Mail)
suspend fun updateMail(mail: Mail)
suspend fun deleteMail(mail: Mail)
}

View File

@ -0,0 +1,33 @@
package com.example.mobileapp.database.repositories
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.mobileapp.database.dao.MailDao
import com.example.mobileapp.database.entities.Mail
import kotlinx.coroutines.flow.Flow
class OfflineMailRepository(private val mailDao: MailDao): MailRepository {
override fun getAllMails(): Flow<PagingData<Mail>> {
return Pager(
config = PagingConfig(
pageSize = 8,
prefetchDistance = 2,
enablePlaceholders = true,
initialLoadSize = 12,
maxSize = 24
),
pagingSourceFactory = {
mailDao.getAll()
}
).flow
}
override fun getMail(id: Int): Flow<Mail?> = mailDao.getById(id)
override suspend fun insertMail(mail: Mail) = mailDao.insert(mail)
override suspend fun updateMail(mail: Mail) = mailDao.update(mail)
override suspend fun deleteMail(mail: Mail) = mailDao.delete(mail)
}

View File

@ -0,0 +1,48 @@
package com.example.mobileapp.database.repositories
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.mobileapp.database.dao.StoryDao
import com.example.mobileapp.database.entities.Story
import kotlinx.coroutines.flow.Flow
class OfflineStoryRepository(private val storyDao: StoryDao): StoryRepository {
override fun getAllStories(): Flow<PagingData<Story>> {
return Pager(
config = PagingConfig(
pageSize = 5,
prefetchDistance = 1,
enablePlaceholders = true,
initialLoadSize = 10,
maxSize = 15
),
pagingSourceFactory = {
storyDao.getAll()
}
).flow
}
override fun getStoriesByUserId(userId: Int): Flow<PagingData<Story>> {
return Pager(
config = PagingConfig(
pageSize = 5,
prefetchDistance = 1,
enablePlaceholders = true,
initialLoadSize = 10,
maxSize = 15
),
pagingSourceFactory = {
storyDao.getByUserId(userId)
}
).flow
}
override fun getStoryById(id: Int): Flow<Story?> = storyDao.getById(id)
override suspend fun insertStory(story: Story) = storyDao.insert(story)
override suspend fun updateStory(story: Story) = storyDao.update(story)
override suspend fun deleteStory(story: Story) = storyDao.delete(story)
}

View File

@ -0,0 +1,19 @@
package com.example.mobileapp.database.repositories
import com.example.mobileapp.database.dao.UserDao
import com.example.mobileapp.database.entities.User
import kotlinx.coroutines.flow.Flow
class OfflineUserRepository(private val userDao: UserDao): UserRepository {
override fun getAllUsers(): Flow<List<User>> = userDao.getAll()
override fun getUser(id: Int): Flow<User?> = userDao.getById(id)
override suspend fun getUserByLogin(login: String): User? = userDao.getByLogin(login)
override suspend fun insertUser(user: User) = userDao.insert(user)
override suspend fun updateUser(user: User) = userDao.update(user)
override suspend fun deleteUser(user: User) = userDao.delete(user)
}

View File

@ -0,0 +1,20 @@
package com.example.mobileapp.database.repositories
import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.mobileapp.database.entities.Story
import kotlinx.coroutines.flow.Flow
interface StoryRepository {
fun getAllStories(): Flow<PagingData<Story>>
fun getStoriesByUserId(userId: Int): Flow<PagingData<Story>>
fun getStoryById(id: Int): Flow<Story?>
suspend fun insertStory(story: Story)
suspend fun updateStory(story: Story)
suspend fun deleteStory(story: Story)
}

View File

@ -0,0 +1,18 @@
package com.example.mobileapp.database.repositories
import com.example.mobileapp.database.entities.User
import kotlinx.coroutines.flow.Flow
interface UserRepository {
fun getAllUsers(): Flow<List<User>>
fun getUser(id: Int): Flow<User?>
suspend fun getUserByLogin(login: String): User?
suspend fun insertUser(user: User)
suspend fun updateUser(user: User)
suspend fun deleteUser(user: User)
}

View File

@ -0,0 +1,28 @@
package com.example.mobileapp.database.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.repositories.MailRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class MailViewModel(private val mailRepository: MailRepository): ViewModel() {
val getAllMails: Flow<PagingData<Mail>> = mailRepository.getAllMails().cachedIn(viewModelScope)
fun getMail(id: Int): Flow<Mail?> = mailRepository.getMail(id)
fun insertMail(mail: Mail) = viewModelScope.launch {
mailRepository.insertMail(mail)
}
fun updateMail(mail: Mail) = viewModelScope.launch {
mailRepository.updateMail(mail)
}
fun deleteMail(mail: Mail) = viewModelScope.launch {
mailRepository.deleteMail(mail)
}
}

View File

@ -0,0 +1,24 @@
package com.example.mobileapp.database.viewmodels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.mobileapp.MobileApp
object MobileAppViewModelProvider {
val Factory = viewModelFactory {
initializer {
MailViewModel(app().container.mailRepository)
}
initializer {
StoryViewModel(app().container.storyRepository)
}
initializer {
UserViewModel(app().container.userRepository)
}
}
}
fun CreationExtras.app(): MobileApp =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as MobileApp)

View File

@ -0,0 +1,31 @@
package com.example.mobileapp.database.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.repositories.StoryRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class StoryViewModel(private val storyRepository: StoryRepository): ViewModel() {
val getAllStories: Flow<PagingData<Story>> = storyRepository.getAllStories().cachedIn(viewModelScope)
fun getStoryById(id: Int): Flow<Story?> = storyRepository.getStoryById(id)
fun getStoriesByUserId(userId: Int): Flow<PagingData<Story>> = storyRepository.getStoriesByUserId(userId).cachedIn(viewModelScope)
fun insertStory(story: Story) = viewModelScope.launch {
storyRepository.insertStory(story)
}
fun updateStory(story: Story) = viewModelScope.launch {
storyRepository.updateStory(story)
}
fun deleteStory(story: Story) = viewModelScope.launch {
storyRepository.deleteStory(story)
}
}

View File

@ -0,0 +1,65 @@
package com.example.mobileapp.database.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.mobileapp.GlobalUser
import com.example.mobileapp.database.entities.User
import com.example.mobileapp.database.repositories.UserRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class UserViewModel(private val userRepository: UserRepository): ViewModel() {
val getAllUsers = userRepository.getAllUsers()
fun getUser(id: Int): Flow<User?> = userRepository.getUser(id)
fun updateUser(user: User) = viewModelScope.launch {
if (user.login.isEmpty()){
return@launch
}
if (user.email.isEmpty() || !isValidEmail(user.email)){
return@launch
}
if (user.password.isEmpty()){
return@launch
}
userRepository.updateUser(user)
GlobalUser.getInstance().setUser(user)
}
fun deleteUser(user: User) = viewModelScope.launch {
userRepository.deleteUser(user)
}
fun regUser(user: User) = viewModelScope.launch {
val globalUser = userRepository.getUserByLogin(user.login)
globalUser?.let {
return@launch
} ?: run {
if(user.password.isEmpty()){
return@launch
}
if(user.email.isEmpty() || !isValidEmail(user.email)){
return@launch
}
userRepository.insertUser(user)
GlobalUser.getInstance().setUser(userRepository.getUserByLogin(user.login))
}
}
fun authUser(user: User) = viewModelScope.launch {
val globalUser = userRepository.getUserByLogin(user.login)
globalUser?.let {
if (user.password.isNotEmpty() && user.password == globalUser.password){
GlobalUser.getInstance().setUser(globalUser)
}
}
}
private fun isValidEmail(email: String): Boolean {
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()
}
}

View File

@ -8,7 +8,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember 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
@ -17,31 +19,32 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext 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.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.example.mobileapp.R import com.example.mobileapp.R
import com.example.mobileapp.components.ActiveButton
import com.example.mobileapp.components.NavigationButton import com.example.mobileapp.components.NavigationButton
import com.example.mobileapp.components.PasswordInputField import com.example.mobileapp.components.PasswordInputField
import com.example.mobileapp.components.PlaceholderInputField import com.example.mobileapp.components.PlaceholderInputField
import com.example.mobileapp.database.MobileAppDataBase import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.User import com.example.mobileapp.database.entities.User
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.UserViewModel
import com.example.mobileapp.ui.theme.ButtonColor1 import com.example.mobileapp.ui.theme.ButtonColor1
import com.example.mobileapp.ui.theme.ButtonColor2 import com.example.mobileapp.ui.theme.ButtonColor2
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@Composable @Composable
fun Authorization(navController: NavHostController){ fun Authorization(navController: NavHostController,
val context = LocalContext.current userViewModel: UserViewModel = viewModel(
val users = remember { mutableStateListOf<User>() } factory = MobileAppViewModelProvider.Factory
)) {
val users = userViewModel.getAllUsers.collectAsState(emptyList()).value
val login = remember { mutableStateOf("") }
val password = remember { mutableStateOf("") }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
MobileAppDataBase.getInstance(context).userDao().getAll().collect { data ->
users.clear()
users.addAll(data)
}
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -53,13 +56,28 @@ fun Authorization(navController: NavHostController){
contentDescription = "login", contentDescription = "login",
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.size(512.dp) .size(448.dp)
.padding(8.dp) .padding(8.dp)
.align(Alignment.CenterHorizontally)) .align(Alignment.CenterHorizontally))
PlaceholderInputField(label = "Логин", isSingleLine = true, onTextChanged = {}) PlaceholderInputField(label = "Логин", isSingleLine = true, onTextChanged = {newlogin ->
PasswordInputField(label = "Пароль", onPasswordChanged = {}) login.value = newlogin
NavigationButton(navController = navController, destination = "main", label = "Вход", })
backgroundColor = ButtonColor2, textColor = Color.White) PasswordInputField(label = "Пароль", onPasswordChanged = {newpassword ->
password.value = newpassword
})
ActiveButton(label = "Вход", backgroundColor = ButtonColor2,
textColor = Color.White, onClickAction = {
if (login.value.isNotEmpty() && password.value.isNotEmpty()) {
userViewModel.authUser(
User(
login = login.value,
password = password.value,
email = String()
)
)
navController.navigate("main")
}
})
NavigationButton(navController = navController, destination = "registration", label = "Регистрация", NavigationButton(navController = navController, destination = "registration", label = "Регистрация",
backgroundColor = ButtonColor1, textColor = Color.Black) backgroundColor = ButtonColor1, textColor = Color.Black)
} }

View File

@ -9,40 +9,51 @@ import android.provider.MediaStore
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember 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.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext 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.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.example.mobileapp.GlobalUser
import com.example.mobileapp.R import com.example.mobileapp.R
import com.example.mobileapp.components.ActiveButton import com.example.mobileapp.components.ActiveButton
import com.example.mobileapp.components.NavigationButton import com.example.mobileapp.components.NavigationButton
import com.example.mobileapp.components.PasswordInputField
import com.example.mobileapp.components.PlaceholderInputField import com.example.mobileapp.components.PlaceholderInputField
import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.Mail import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.entities.Story import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.entities.User
import com.example.mobileapp.database.viewmodels.MailViewModel
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.StoryViewModel
import com.example.mobileapp.database.viewmodels.UserViewModel
import com.example.mobileapp.ui.theme.ButtonColor1 import com.example.mobileapp.ui.theme.ButtonColor1
import com.example.mobileapp.ui.theme.ButtonColor2 import com.example.mobileapp.ui.theme.ButtonColor2
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable @Composable
fun EditStoryScreen(navController: NavHostController, storyId: Int? = null) { fun EditStoryScreen(navController: NavHostController, storyId: Int? = null,
storyViewModel: StoryViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val context = LocalContext.current val context = LocalContext.current
val cover = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.editplaceholder)) } val cover = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.editplaceholder)) }
@ -58,7 +69,6 @@ fun EditStoryScreen(navController: NavHostController, storyId: Int? = null) {
if (Build.VERSION.SDK_INT < 28) { if (Build.VERSION.SDK_INT < 28) {
cover.value = MediaStore.Images cover.value = MediaStore.Images
.Media.getBitmap(context.contentResolver, imageData.value) .Media.getBitmap(context.contentResolver, imageData.value)
} else { } else {
val source = ImageDecoder val source = ImageDecoder
.createSource(context.contentResolver, imageData.value!!) .createSource(context.contentResolver, imageData.value!!)
@ -66,46 +76,20 @@ fun EditStoryScreen(navController: NavHostController, storyId: Int? = null) {
} }
} }
storyId?.let{ LaunchedEffect(Unit) {
LaunchedEffect(Unit) { storyId?.let {
withContext(Dispatchers.IO) { storyViewModel.getStoryById(storyId).collect {
val story = MobileAppDataBase.getInstance(context).storyDao().getById(storyId!!) if (it != null) {
cover.value = story!!.cover cover.value = it.cover
title.value = story!!.title }
description.value = story!!.description if (it != null) {
} title.value = it.title
} }
} if (it != null) {
description.value = it.description
val edit = remember { mutableStateOf(false) }
if (edit.value){
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
storyId?.let {
MobileAppDataBase.getInstance(context).storyDao()
.update(
Story(
id = storyId,
title = title.value,
description = description.value,
cover = cover.value,
userId = 1)
)
} ?: run {
MobileAppDataBase.getInstance(context).storyDao()
.insert(
Story(
title = title.value,
description = description.value,
cover = cover.value,
userId = 1)
)
} }
} }
} }
edit.value = !edit.value
navController.navigate("story")
} }
Column( Column(
@ -134,7 +118,27 @@ fun EditStoryScreen(navController: NavHostController, storyId: Int? = null) {
description.value = newDescription description.value = newDescription
}) })
ActiveButton(label = "Сохранить", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = { ActiveButton(label = "Сохранить", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = {
edit.value = !edit.value storyId?.let {
storyViewModel.updateStory(
Story(
id = storyId,
cover = cover.value,
title = title.value,
description = description.value,
userId = GlobalUser.getInstance().getUser()?.id!!
)
)
} ?: run {
storyViewModel.insertStory(
Story(
cover = cover.value,
title = title.value,
description = description.value,
userId = GlobalUser.getInstance().getUser()?.id!!
)
)
}
navController.navigate("story")
}) })
NavigationButton(navController = navController, destination = "story", label = "Назад", NavigationButton(navController = navController, destination = "story", label = "Назад",
backgroundColor = ButtonColor2, textColor = Color.White) backgroundColor = ButtonColor2, textColor = Color.White)
@ -142,22 +146,12 @@ fun EditStoryScreen(navController: NavHostController, storyId: Int? = null) {
} }
@Composable @Composable
fun EditMailScreen(navController: NavHostController) { fun EditMailScreen(navController: NavHostController,
val context = LocalContext.current mailViewModel: MailViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val message = remember { mutableStateOf("") } val message = remember { mutableStateOf("") }
val create = remember { mutableStateOf(false) }
if(create.value){
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
MobileAppDataBase.getInstance(context).mailDao()
.insert(Mail(message = message.value, userId = 2))
}
}
create.value = !create.value
navController.navigate("mail")
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -176,7 +170,13 @@ fun EditMailScreen(navController: NavHostController) {
message.value = newmessage message.value = newmessage
}) })
ActiveButton(label = "Сохранить", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = { ActiveButton(label = "Сохранить", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = {
create.value = !create.value mailViewModel.insertMail(
Mail(
message = message.value,
userId = GlobalUser.getInstance().getUser()?.id!!
)
)
navController.navigate("mail")
}) })
NavigationButton(navController = navController, destination = "mail", label = "Назад", NavigationButton(navController = navController, destination = "mail", label = "Назад",
backgroundColor = ButtonColor2, textColor = Color.White) backgroundColor = ButtonColor2, textColor = Color.White)
@ -184,11 +184,15 @@ fun EditMailScreen(navController: NavHostController) {
} }
@Composable @Composable
fun EditUserScreen(navController: NavHostController){ fun EditUserScreen(navController: NavHostController,
userViewModel: UserViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val context = LocalContext.current val context = LocalContext.current
var userId = remember { mutableStateOf(0) }
val photo = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.photoplaceholder)) } val photo = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.photoplaceholder)) }
val name = remember { mutableStateOf("") } val login = remember { mutableStateOf("") }
val password = remember { mutableStateOf("") } val password = remember { mutableStateOf("") }
val email = remember { mutableStateOf("") } val email = remember { mutableStateOf("") }
@ -201,7 +205,6 @@ fun EditUserScreen(navController: NavHostController){
if (Build.VERSION.SDK_INT < 28) { if (Build.VERSION.SDK_INT < 28) {
photo.value = MediaStore.Images photo.value = MediaStore.Images
.Media.getBitmap(context.contentResolver, imageData.value) .Media.getBitmap(context.contentResolver, imageData.value)
} else { } else {
val source = ImageDecoder val source = ImageDecoder
.createSource(context.contentResolver, imageData.value!!) .createSource(context.contentResolver, imageData.value!!)
@ -209,6 +212,17 @@ fun EditUserScreen(navController: NavHostController){
} }
} }
LaunchedEffect(Unit) {
GlobalUser.getInstance().getUser()?.let { user ->
if (user!!.photo != null)
photo.value = user!!.photo!!
userId.value = user!!.id!!
login.value = user!!.login
password.value = user!!.password
email.value = user!!.email
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -220,15 +234,20 @@ fun EditUserScreen(navController: NavHostController){
contentDescription = "editplaceholder", contentDescription = "editplaceholder",
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.size(384.dp)
.padding(8.dp) .padding(8.dp)
.clip(CircleShape)
.size(384.dp)
.border(
width = 2.dp,
color = MaterialTheme.colorScheme.onPrimary,
)
.align(Alignment.CenterHorizontally)) .align(Alignment.CenterHorizontally))
ActiveButton(label = "Выбрать фото", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = { ActiveButton(label = "Выбрать фото", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = {
launcher.launch("image/*") launcher.launch("image/*")
}) })
PlaceholderInputField(label = "Никнейм", isSingleLine = true, PlaceholderInputField(label = "Никнейм", isSingleLine = true,
startValue = name.value, onTextChanged = { newName -> startValue = login.value, onTextChanged = { newLogin ->
name.value = newName login.value = newLogin
}) })
PlaceholderInputField(label = "Пароль", isSingleLine = true, PlaceholderInputField(label = "Пароль", isSingleLine = true,
startValue = password.value, onTextChanged = { newPassword -> startValue = password.value, onTextChanged = { newPassword ->
@ -239,9 +258,20 @@ fun EditUserScreen(navController: NavHostController){
email.value = newEmail email.value = newEmail
}) })
ActiveButton(label = "Сохранить", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = { ActiveButton(label = "Сохранить", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = {
//edit.value = !edit.value userViewModel.updateUser(
User(
id = userId.value,
login = login.value,
password = password.value,
email = email.value,
photo = photo.value
)
)
navController.navigate("settings")
})
ActiveButton(label = "Назад", backgroundColor = ButtonColor2, textColor = Color.White,
onClickAction = {
navController.navigate("settings")
}) })
NavigationButton(navController = navController, destination = "story", label = "Назад",
backgroundColor = ButtonColor2, textColor = Color.White)
} }
} }

View File

@ -1,48 +0,0 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavHostController
import com.example.mobileapp.components.DataListScroll
import com.example.mobileapp.components.NavBar
import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.ui.theme.BackgroundItem1
import com.example.mobileapp.ui.theme.BackgroundItem2
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun ListDataScreen(navController: NavHostController){
val context = LocalContext.current
val stories = remember { mutableStateListOf<Story>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
MobileAppDataBase.getInstance(context).storyDao().getAll().collect { data ->
stories.clear()
stories.addAll(data)
}
}
}
Column(
modifier = Modifier
.fillMaxSize()
.background(BackgroundItem1)
) {
DataListScroll(navController, stories)
}
}

View File

@ -3,40 +3,61 @@ package com.example.mobileapp.screens
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.components.DataListScroll import com.example.mobileapp.components.DataListScroll
import com.example.mobileapp.components.MailListItem
import com.example.mobileapp.components.StoryListItem
import com.example.mobileapp.components.addNewListItem
import com.example.mobileapp.database.MobileAppDataBase import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.Mail import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.viewmodels.MailViewModel
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.ui.theme.BackgroundItem1 import com.example.mobileapp.ui.theme.BackgroundItem1
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@Composable @Composable
fun ListMailScreen(navController: NavHostController){ fun ListMailScreen(navController: NavHostController,
mailViewModel: MailViewModel = viewModel(
val context = LocalContext.current factory = MobileAppViewModelProvider.Factory
val mails = remember { mutableStateListOf<Mail>() } )) {
val mails = mailViewModel.getAllMails.collectAsLazyPagingItems()
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
MobileAppDataBase.getInstance(context).mailDao().getAll().collect { data ->
mails.clear()
mails.addAll(data)
}
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(BackgroundItem1) .background(BackgroundItem1)
) { ) {
DataListScroll(navController, mails) LazyVerticalGrid(
columns = GridCells.Fixed(1)
) {
item {
addNewListItem(navController, "editmail")
}
items(
count = mails.itemCount,
key = mails.itemKey { item -> item.id!! }
) { index: Int ->
val mail: Mail? = mails[index]
if (mail != null) {
MailListItem(item = mail, navController = navController)
}
}
}
//DataListScroll(navController, mails)
} }
} }

View File

@ -0,0 +1,55 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.GlobalUser
import com.example.mobileapp.components.DataListScroll
import com.example.mobileapp.components.StoryListItem
import com.example.mobileapp.components.addNewListItem
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.StoryViewModel
import com.example.mobileapp.ui.theme.BackgroundItem1
@Composable
fun ListStoryScreen(navController: NavHostController,
storyViewModel: StoryViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val stories = storyViewModel.getStoriesByUserId(GlobalUser.getInstance().getUser()?.id!!).collectAsLazyPagingItems()
//val stories = storyViewModel.getAllStories.collectAsLazyPagingItems()
Column(
modifier = Modifier
.fillMaxSize()
.background(BackgroundItem1)
) {
LazyVerticalGrid(
columns = GridCells.Fixed(1)
) {
item {
addNewListItem(navController, "editstory")
}
items(
count = stories.itemCount,
key = stories.itemKey { item -> item.id!! }
) { index: Int ->
val story: Story? = stories[index]
if (story != null) {
StoryListItem(item = story, navController = navController)
}
}
}
//DataListScroll(navController, stories)
}
}

View File

@ -8,8 +8,12 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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
@ -18,38 +22,69 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
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.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.R import com.example.mobileapp.R
import com.example.mobileapp.components.NavBar import com.example.mobileapp.components.NavBar
import com.example.mobileapp.components.SearchInputField import com.example.mobileapp.components.SearchInputField
import com.example.mobileapp.components.StoryListItem
import com.example.mobileapp.components.addNewListItem
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.StoryViewModel
@Composable @Composable
fun MainScreen(navController: NavHostController) { fun MainScreen(navController: NavHostController,
storyViewModel: StoryViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val stories = storyViewModel.getAllStories.collectAsLazyPagingItems()
val search = remember { mutableStateOf("") }
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
) { ) {
SearchInputField() SearchInputField(onTextChanged = {newsearch ->
Column( search.value = newsearch
modifier = Modifier })
.fillMaxWidth() if (stories.itemCount > 0){
.fillMaxHeight(0.89f), LazyVerticalGrid(
verticalArrangement = Arrangement.Center, columns = GridCells.Fixed(1)
horizontalAlignment = Alignment.CenterHorizontally ) {
) { items(
Image( count = stories.itemCount,
painter = painterResource(id = R.drawable.main), key = stories.itemKey { item -> item.id!! }
contentDescription = "main", ) { index: Int ->
contentScale = ContentScale.Crop, val story: Story? = stories[index]
modifier = Modifier if (story != null && (search.value.isEmpty() || story.title.contains(search.value, ignoreCase = true))) {
.size(512.dp) StoryListItem(item = story, navController = navController, isReadOnly = true)
.padding(8.dp) }
) }
Text( }
text = "Здесь будут посты авторов", }
fontSize = 20.sp, else {
fontWeight = FontWeight.Bold Column(
) verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.main),
contentDescription = "main",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(512.dp)
.padding(8.dp)
)
Text(
text = "Здесь будут посты авторов",
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
}
} }
} }
} }

View File

@ -7,22 +7,37 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.layout.ContentScale import androidx.compose.ui.layout.ContentScale
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.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.example.mobileapp.R import com.example.mobileapp.R
import com.example.mobileapp.components.ActiveButton
import com.example.mobileapp.components.NavigationButton import com.example.mobileapp.components.NavigationButton
import com.example.mobileapp.components.PasswordInputField import com.example.mobileapp.components.PasswordInputField
import com.example.mobileapp.components.PlaceholderInputField import com.example.mobileapp.components.PlaceholderInputField
import com.example.mobileapp.database.entities.User
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.UserViewModel
import com.example.mobileapp.ui.theme.ButtonColor1 import com.example.mobileapp.ui.theme.ButtonColor1
import com.example.mobileapp.ui.theme.ButtonColor2 import com.example.mobileapp.ui.theme.ButtonColor2
@Composable @Composable
fun Registration(navController: NavHostController){ fun Registration(navController: NavHostController,
userViewModel: UserViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val login = remember { mutableStateOf("") }
val email = remember { mutableStateOf("") }
val password = remember { mutableStateOf("") }
val repeatepassword = remember { mutableStateOf("") }
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -34,15 +49,34 @@ fun Registration(navController: NavHostController){
contentDescription = "registration", contentDescription = "registration",
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.size(384.dp) .size(320.dp)
.padding(8.dp) .padding(8.dp)
.align(Alignment.CenterHorizontally)) .align(Alignment.CenterHorizontally))
PlaceholderInputField(label = "Логин", isSingleLine = true, onTextChanged = {}) PlaceholderInputField(label = "Логин", isSingleLine = true, onTextChanged = {newlogin ->
PlaceholderInputField(label = "Email", isSingleLine = true, onTextChanged = {}) login.value = newlogin
PasswordInputField(label = "Пароль", onPasswordChanged = {}) })
PasswordInputField(label = "Пароль ещё раз", onPasswordChanged = {}) PlaceholderInputField(label = "Email", isSingleLine = true, onTextChanged = {newemail ->
NavigationButton(navController = navController, destination = "main", email.value = newemail
label = "Зарегистрироваться", backgroundColor = ButtonColor2, textColor = Color.White) })
PasswordInputField(label = "Пароль", onPasswordChanged = {newpassword ->
password.value = newpassword
})
PasswordInputField(label = "Пароль ещё раз", onPasswordChanged = {newpassword ->
repeatepassword.value = newpassword
})
ActiveButton(label = "Зарегистрироваться", backgroundColor = ButtonColor2,
textColor = Color.White, onClickAction = {
if (password.value == repeatepassword.value){
userViewModel.regUser(
User(
login = login.value,
password = password.value,
email = email.value
)
)
}
navController.navigate("main")
})
NavigationButton(navController = navController, destination = "authorization", NavigationButton(navController = navController, destination = "authorization",
label = "Назад", backgroundColor = ButtonColor1, textColor = Color.Black) label = "Назад", backgroundColor = ButtonColor1, textColor = Color.Black)
} }

View File

@ -27,6 +27,7 @@ import androidx.compose.ui.text.font.FontWeight
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.NavHostController import androidx.navigation.NavHostController
import com.example.mobileapp.GlobalUser
import com.example.mobileapp.R import com.example.mobileapp.R
import com.example.mobileapp.components.IconButton import com.example.mobileapp.components.IconButton
import com.example.mobileapp.components.NavBar import com.example.mobileapp.components.NavBar
@ -46,7 +47,7 @@ fun SettingsScreen(navController: NavHostController){
contentDescription = "settings", contentDescription = "settings",
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.size(384.dp) .size(320.dp)
.padding(8.dp)) .padding(8.dp))
IconButton(iconLeft = Icons.Default.AccountCircle, label = "Учётная запись", IconButton(iconLeft = Icons.Default.AccountCircle, label = "Учётная запись",
backgroundColor = ButtonColor2, textColor = Color.White, onClickAction = { backgroundColor = ButtonColor2, textColor = Color.White, onClickAction = {
@ -66,6 +67,7 @@ fun SettingsScreen(navController: NavHostController){
}) })
IconButton(iconLeft = Icons.Default.ExitToApp, label = "Выйти", IconButton(iconLeft = Icons.Default.ExitToApp, label = "Выйти",
backgroundColor = Color.Red, textColor = Color.White, onClickAction = { backgroundColor = Color.Red, textColor = Color.White, onClickAction = {
GlobalUser.getInstance().setUser(null)
navController.navigate("authorization") navController.navigate("authorization")
}) })
} }

View File

@ -0,0 +1,168 @@
package com.example.mobileapp.screens
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.mobileapp.R
import com.example.mobileapp.components.NavigationButton
import com.example.mobileapp.database.viewmodels.MailViewModel
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.StoryViewModel
import com.example.mobileapp.database.viewmodels.UserViewModel
import com.example.mobileapp.ui.theme.ButtonColor2
import java.text.SimpleDateFormat
import java.util.Date
val dateFormat = SimpleDateFormat("dd.MM.yyyy")
@Composable
fun StoryViewScreen(navController: NavHostController, storyId: Int,
storyViewModel: StoryViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val context = LocalContext.current
val cover = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.editplaceholder)) }
val title = remember { mutableStateOf("") }
val description = remember { mutableStateOf("") }
val postdate = remember { mutableStateOf<Long>(0) }
val story by storyViewModel.getStoryById(storyId).collectAsState(null)
story?.let {
cover.value = it.cover
title.value = it.title
description.value = it.description
postdate.value = it.postdate!!
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp),
verticalArrangement = Arrangement.Center
) {
Image(
bitmap = cover.value.asImageBitmap(),
contentDescription = "cover",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(512.dp)
.padding(8.dp)
.align(Alignment.CenterHorizontally))
Text(text = "Название: ${title.value}",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))
Text(text = "Дата публикации: ${dateFormat.format(Date(postdate.value))}",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))
Text(text = "Описание: ${description.value}",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))
NavigationButton(navController = navController, destination = "main", label = "Назад",
backgroundColor = ButtonColor2, textColor = Color.White)
}
}
@Composable
fun MailViewScreen(navController: NavHostController, mailId: Int,
mailViewModel: MailViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
),
userViewModel: UserViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val context = LocalContext.current
val userName = remember { mutableStateOf("") }
val photo = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.photoplaceholder)) }
val message = remember { mutableStateOf("") }
val postdate = remember { mutableStateOf<Long>(0) }
LaunchedEffect(Unit){
mailViewModel.getMail(mailId).collect{
if (it != null) {
message.value = it.message
postdate.value = it.postdate!!
userViewModel.getUser(it.userId).collect {user ->
if (user != null) {
if(user.photo != null) {
photo.value = user.photo
}
userName.value = user.email
}
}
}
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp),
verticalArrangement = Arrangement.Center
) {
Image(
bitmap = photo.value.asImageBitmap(),
contentDescription = "editplaceholder",
contentScale = ContentScale.Crop,
modifier = Modifier
.padding(8.dp)
.clip(CircleShape)
.size(384.dp)
.border(
width = 2.dp,
color = MaterialTheme.colorScheme.onPrimary,
)
.align(Alignment.CenterHorizontally))
Text(text = "Автор: ${userName.value}",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))
Text(text = "Дата публикации: ${dateFormat.format(Date(postdate.value))}",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))
Text(text = "Текст: ${message.value}",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))
NavigationButton(navController = navController, destination = "mail", label = "Назад",
backgroundColor = ButtonColor2, textColor = Color.White)
}
}