You've already forked RekomenciMobile
feat: added profile edir screen
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
package com.prodhack.moscow2025.common
|
package com.prodhack.moscow2025.common
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
const val BASE_API_URL = "https://hackaton.paas.itqdev.xyz/"
|
const val BASE_API_URL = "https://team-39-alpha-gm5qjkou.hack.prodcontest.ru/"
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,9 @@ fun TTasksNavHost(
|
|||||||
|
|
||||||
composable(AppDestination.Profile.route)
|
composable(AppDestination.Profile.route)
|
||||||
{
|
{
|
||||||
ProfileScreen()
|
ProfileScreen(
|
||||||
|
snackbarHostState = snackbarHostState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+148
-3
@@ -1,14 +1,159 @@
|
|||||||
package com.prodhack.moscow2025.presentation.screens.profile
|
package com.prodhack.moscow2025.presentation.screens.profile
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.imePadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material3.SnackbarDuration
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
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.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.prodhack.moscow2025.presentation.utils.base.BaseViewModel
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.prodhack.moscow2025.R
|
||||||
|
import com.prodhack.moscow2025.domain.usecase.auth.AuthField
|
||||||
|
import com.prodhack.moscow2025.presentation.components.standart.BigButton
|
||||||
|
import com.prodhack.moscow2025.presentation.components.standart.TTTextField
|
||||||
|
import com.prodhack.moscow2025.presentation.theme.Paddings
|
||||||
|
import com.prodhack.moscow2025.presentation.utils.ErrorCollectorScope
|
||||||
|
import com.prodhack.moscow2025.presentation.utils.UIState
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ProfileScreen(
|
fun ErrorCollectorScope.ProfileScreen(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
snackbarHostState: SnackbarHostState,
|
||||||
viewModel: ProfileScreenViewModel = koinViewModel()
|
viewModel: ProfileScreenViewModel = koinViewModel()
|
||||||
) : BaseViewModel {
|
) {
|
||||||
|
val typography = androidx.compose.material3.MaterialTheme.typography
|
||||||
|
|
||||||
|
val formState by viewModel.formStateFillProfile.collectAsState()
|
||||||
|
|
||||||
|
var errorText by remember { mutableStateOf("") }
|
||||||
|
val profileState by viewModel.profileState.collectAsStateWithCallbacks(
|
||||||
|
onInputError = {
|
||||||
|
errorText = it.error
|
||||||
|
},
|
||||||
|
onConnectionError = {
|
||||||
|
errorText = "Нет подключения к сети"
|
||||||
|
},
|
||||||
|
onUnexpectedError = {
|
||||||
|
errorText = it.error
|
||||||
|
},
|
||||||
|
onLoading = {
|
||||||
|
errorText = ""
|
||||||
|
},
|
||||||
|
onSuccess = {
|
||||||
|
errorText = ""
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
LaunchedEffect(profileState) {
|
||||||
|
if (profileState is UIState.Success) {
|
||||||
|
snackbarHostState.showSnackbar(
|
||||||
|
message = "Данные профиля обновлены",
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(errorText) {
|
||||||
|
if (errorText.isNotEmpty()) {
|
||||||
|
snackbarHostState.showSnackbar(
|
||||||
|
message = "Ошибка: $errorText",
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.imePadding()
|
||||||
|
.systemBarsPadding(),
|
||||||
|
contentAlignment = Alignment.BottomStart
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.lottie),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomStart)
|
||||||
|
.padding(start = 16.dp)
|
||||||
|
.fillMaxWidth(0.35f),
|
||||||
|
contentScale = ContentScale.FillWidth
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 30.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Top
|
||||||
|
) {
|
||||||
|
Spacer(Modifier.height(32.dp))
|
||||||
|
Text(
|
||||||
|
text = "Профиль",
|
||||||
|
style = typography.titleLarge,
|
||||||
|
fontSize = 32.sp
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(20.dp))
|
||||||
|
TTTextField(
|
||||||
|
value = formState.firstName,
|
||||||
|
onValueChange = viewModel::onFirstNameChange,
|
||||||
|
label = "Имя",
|
||||||
|
error = formState.errors[AuthField.FirstName],
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(12.dp))
|
||||||
|
TTTextField(
|
||||||
|
value = formState.lastName,
|
||||||
|
onValueChange = viewModel::onLastNameChange,
|
||||||
|
label = "Фамилия",
|
||||||
|
error = formState.errors[AuthField.LastName],
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(12.dp))
|
||||||
|
TTTextField(
|
||||||
|
value = formState.email,
|
||||||
|
onValueChange = viewModel::onEmailChange,
|
||||||
|
label = "Email",
|
||||||
|
error = formState.errors[AuthField.Email],
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(12.dp))
|
||||||
|
TTTextField(
|
||||||
|
value = formState.phone,
|
||||||
|
onValueChange = viewModel::onPhoneChange,
|
||||||
|
label = "Телефон",
|
||||||
|
error = formState.errors[AuthField.Phone],
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
BigButton(
|
||||||
|
onClick = viewModel::submit,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
buttonText = "Сохранить",
|
||||||
|
isLoading = profileState is UIState.Loading
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(48.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user