This commit is contained in:
Alex 2025-03-24 11:21:32 +03:00
parent d04e173f01
commit 26e0b74bf3
47 changed files with 1250 additions and 940 deletions

View File

@ -5,9 +5,6 @@
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="CommonButton">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@ -49,6 +49,7 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@ -56,4 +57,12 @@ dependencies {
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
implementation("androidx.datastore:datastore-preferences:1.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
implementation("androidx.navigation:navigation-compose:2.7.0")
}

View File

@ -0,0 +1,19 @@
package com.example.shoesapptest
import com.example.shoesapptest.data.model.RegistrationRequest
import com.example.shoesapptest.data.model.TokenResponse
import com.example.shoesapptest.data.remote.AuthRemoteSource
import com.example.shoesapptest.data.remote.retrofit.Auth
import com.example.shoesapptest.data.remote.retrofit.RetrofitClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class AuthRemoteSourceImpl :AuthRemoteSource {
private val api: Auth = RetrofitClient.retrofit
override suspend fun registration(registrationRequest: RegistrationRequest): TokenResponse {
return withContext(Dispatchers.IO) {
api.registration(registrationRequest)
}
}
}

View File

@ -1,30 +1,64 @@
package com.example.shoesapptest
package com.example.shoesapptest.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.shoesapp.ui.screen.SignInContent
import com.example.shoesapp.ui.screen.SigninScreen
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.screen.ForgotPasswordScreen
import com.example.shoesapptest.screen.RegistrtionScreen
import com.example.shoesapptest.AuthRemoteSourceImpl
import com.example.shoesapptest.RegistrationViewModelFactory
import com.example.shoesapptest.data.local.datastore.LocalDataStore
import com.example.shoesapptest.data.repository.AuthRepositoryImpl
import com.example.shoesapptest.screen.RegistrationScreen
import com.example.shoesapptest.screen.TestScreen
import com.example.shoesapptest.screen.registrationscreen.RegistrationViewModel
class MainActivity : ComponentActivity() {
private lateinit var authLocalStore: LocalDataStore
private lateinit var authRemoteSource: AuthRemoteSourceImpl
private lateinit var authRepository: AuthRepositoryImpl
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// Инициализация зависимостей
authLocalStore = LocalDataStore(applicationContext)
authRemoteSource = AuthRemoteSourceImpl()
authRepository = AuthRepositoryImpl(authLocalStore, authRemoteSource)
setContent {
MatuleTheme {
RegistrtionScreen()
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "register"
) {
composable("register") {
// Создание и передача ViewModel
val registrationViewModel: RegistrationViewModel = ViewModelProvider(
this@MainActivity,
RegistrationViewModelFactory(authRepository)
).get(RegistrationViewModel::class.java)
RegistrationScreen(
navController = navController,
registrationViewModel = registrationViewModel
)
}
composable("test") {
TestScreen()
}
}
}
}
}
}

View File

@ -0,0 +1,18 @@
package com.example.shoesapptest
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.shoesapptest.data.repository.AuthRepository
import com.example.shoesapptest.screen.registrationscreen.RegistrationViewModel
class RegistrationViewModelFactory(
private val authRepository: AuthRepository
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(RegistrationViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return RegistrationViewModel(authRepository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

View File

@ -0,0 +1,35 @@
package com.example.shoesapptest.common
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.focusModifier
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
@Composable
fun CommonButton(
modifier: Modifier = Modifier,
onClick: () -> Unit,
buttonColors: ButtonColors,
content: @Composable () -> Unit
){
Button(
modifier = modifier.padding(horizontal = 20.dp)
.fillMaxWidth()
.height(50.dp)
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.accent),
colors = buttonColors,
onClick = onClick
){
content()
}
}

View File

@ -0,0 +1,71 @@
package com.example.shoesapptest.common
import android.view.ViewAnimationUtils
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CommonTextField(
value: String,
onChangeValue: (String) -> Unit,
modifier: Modifier = Modifier,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
trailingIcon: @Composable ()-> Unit = {},
supportingText: @Composable ()-> Unit = {},
placeHolder: @Composable ()-> Unit = {},
){
val interaction = remember { MutableInteractionSource() }
BasicTextField(
value = value,
onValueChange = { onChangeValue(it) },
modifier = modifier
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
){ innerTextField ->
TextFieldDefaults.DecorationBox(
value = value,
singleLine = true,
innerTextField = innerTextField,
enabled = true,
visualTransformation = visualTransformation,
interactionSource = interaction,
trailingIcon = trailingIcon,
isError = isError,
supportingText = if (isError) supportingText else null,
colors = TextFieldDefaults.colors(
focusedContainerColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.background,
unfocusedContainerColor = MatuleTheme.colors.background,
errorContainerColor = MatuleTheme.colors.background,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Red
),
placeholder = placeHolder
)
}
}

View File

@ -0,0 +1,9 @@
package com.example.shoesapptest.data.local
import com.example.shoesapptest.data.model.SaveTokenRequest
import com.example.shoesapptest.data.model.TokenResponse
interface AuthLocalStore {
suspend fun getToken(): TokenResponse
suspend fun setToken(saveTokenRequest: SaveTokenRequest): Boolean
}

View File

@ -0,0 +1,30 @@
package com.example.shoesapptest.data.local.datastore
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.example.shoesapptest.data.local.AuthLocalStore
import com.example.shoesapptest.data.model.SaveTokenRequest
import com.example.shoesapptest.data.model.TokenResponse
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
private val Context.dataStore by preferencesDataStore(name="auth")
class LocalDataStore(context: Context): AuthLocalStore {
private val dataStore = context.dataStore
override suspend fun getToken(): TokenResponse {
val token = dataStore.data.map { preferences ->
preferences[stringPreferencesKey("token")] ?: ""
}.first()
return TokenResponse("token")
}
override suspend fun setToken(saveTokenRequest: SaveTokenRequest): Boolean {
dataStore.edit { prefences -> prefences[stringPreferencesKey("token")] = saveTokenRequest.tokenAuthentication }
return true
}
}

View File

@ -0,0 +1,7 @@
package com.example.shoesapptest.data.model
data class RegistrationRequest(
val userName: String,
val email: String,
val password: String
)

View File

@ -0,0 +1,5 @@
package com.example.shoesapptest.data.model
data class SaveTokenRequest(
val tokenAuthentication: String
)

View File

@ -0,0 +1,5 @@
package com.example.shoesapptest.data.model
data class TokenResponse(
val tokenAuthentication: String
)

View File

@ -0,0 +1,8 @@
package com.example.shoesapptest.data.remote
import com.example.shoesapptest.data.model.RegistrationRequest
import com.example.shoesapptest.data.model.TokenResponse
interface AuthRemoteSource {
suspend fun registration(registrationRequest: RegistrationRequest):TokenResponse
}

View File

@ -0,0 +1,12 @@
package com.example.shoesapptest.data.remote.retrofit
import com.example.shoesapptest.data.model.RegistrationRequest
import com.example.shoesapptest.data.model.TokenResponse
import com.example.shoesapptest.data.remote.AuthRemoteSource
import retrofit2.http.Body
import retrofit2.http.POST
interface Auth:AuthRemoteSource {
@POST("/registration")
override suspend fun registration(@Body registrationRequest: RegistrationRequest): TokenResponse
}

View File

@ -0,0 +1,15 @@
package com.example.shoesapptest.data.remote.retrofit
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
private const val URL = "http://172.18.112.1:8080"
object RetrofitClient {
private val retrofitBuilder = Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofit by lazy {
retrofitBuilder.create(Auth::class.java)
}
}

View File

@ -0,0 +1,10 @@
package com.example.shoesapptest.data.repository
import com.example.shoesapptest.data.model.RegistrationRequest
import com.example.shoesapptest.data.model.TokenResponse
interface AuthRepository {
suspend fun registration(registrationRequest: RegistrationRequest): TokenResponse
suspend fun login()
}

View File

@ -0,0 +1,23 @@
package com.example.shoesapptest.data.repository
import com.example.shoesapptest.data.local.AuthLocalStore
import com.example.shoesapptest.data.model.RegistrationRequest
import com.example.shoesapptest.data.model.SaveTokenRequest
import com.example.shoesapptest.data.model.TokenResponse
import com.example.shoesapptest.data.remote.AuthRemoteSource
class AuthRepositoryImpl(
private val authLocalStore: AuthLocalStore,
private val authRemoteSource: AuthRemoteSource
):AuthRepository {
override suspend fun registration(registrationRequest: RegistrationRequest): TokenResponse {
val tokenResponse = authRemoteSource.registration(registrationRequest)
authLocalStore.setToken(SaveTokenRequest(tokenResponse.tokenAuthentication))
return tokenResponse
}
override suspend fun login() {
}
}

View File

@ -0,0 +1,18 @@
package com.example.shoesapptest.screen
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@Composable
fun TestScreen() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = "Добро пожаловать!")
}
}

View File

@ -0,0 +1,6 @@
package com.example.shoesapptest.screen.forgotscreen
data class ChangePass(
var email: String = "",
var errorMessage: String? = null
)

View File

@ -0,0 +1,166 @@
package com.example.shoesapptest.screen
import android.app.Dialog
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.R
import androidx.compose.material3.AlertDialog
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.input.pointer.motionEventSpy
import androidx.compose.ui.res.colorResource
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.shoesapptest.screen.forgotscreen.ForgotPasswordViewModel
import com.example.shoesapptest.screen.forgotscreen.component.TitleWithSubtitleText
import com.example.shoesapptest.screen.forgotscreen.component.EmailTextField
import com.example.shoesapptest.screen.forgotscreen.component.SendButton
@Composable
fun ForgotPasswordScreen(){
val forgotPasswordViewModel: ForgotPasswordViewModel = viewModel()
val showDialog = remember { mutableStateOf(false) }
Scaffold(
topBar = {
Row(
modifier = Modifier
.padding(top = 35.dp)
.fillMaxWidth()
.height(40.dp)
) {
IconButton(onClick = {}) {
Icon(painter= painterResource(R.drawable.back_arrow),
contentDescription = null)
}
}
}
){ paddingValues ->
ForgotPasswordContent(paddingValues, showDialog, forgotPasswordViewModel)
}
if (showDialog.value){
CheckEmailDialog(onDismiss = {showDialog.value=false})
}
}
@Composable
fun ForgotPasswordContent(
paddingValues: PaddingValues,
showDialog: MutableState<Boolean>,
forgotPasswordViewModel: ForgotPasswordViewModel
){
val changePass = forgotPasswordViewModel.changePass
Column(
modifier = Modifier.fillMaxWidth()
.padding(paddingValues),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
){
TitleWithSubtitleText(
title = stringResource(R.string.title_forgot_password),
subTitle = stringResource(R.string.subtitle_forgot_password)
)
Spacer(modifier = Modifier.height(35.dp))
EmailTextField(
value = changePass.value.email,
onChangeValue = {forgotPasswordViewModel.setEmail(it)},
isError = forgotPasswordViewModel.emailHasError.value,
supportingText = {Text(text = stringResource(R.string.wrong_email))},
placeholder = { Text(text = stringResource(R.string.placeholder_email))},
label = { Text(text = stringResource(R.string.email))}
)
SendButton(onClick = {showDialog.value = true}) { Text(text="Отправить") }
}
}
@Composable
fun CheckEmailDialog(onDismiss: () -> Unit) {
AlertDialog(
onDismissRequest = onDismiss,
confirmButton = {},
title = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Row(
modifier = Modifier.padding(bottom = 8.dp),
horizontalArrangement = Arrangement.Center
) {
Icon(
painter = painterResource(R.drawable.email_image),
contentDescription = null,
tint = MatuleTheme.colors.accent,
modifier = Modifier.size(40.dp)
)
}
Text(
text = "Проверьте Ваш Email",
style = MatuleTheme.typography.bodyRegular16.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
}
},
text = {
Text(
text = "Мы отправили код восстановления пароля на вашу электронную почту.",
textAlign = TextAlign.Center,
style = MatuleTheme.typography.bodyRegular14.copy(color = MatuleTheme.colors.hint)
)
},
modifier = Modifier.clip(RoundedCornerShape(14.dp)),
containerColor = Color.White
)
}

View File

@ -0,0 +1,16 @@
package com.example.shoesapptest.screen.forgotscreen
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
class ForgotPasswordViewModel {
var changePass = mutableStateOf(ChangePass())
private set
val emailHasError = derivedStateOf {
if(changePass.value.email.isEmpty()) return@derivedStateOf false
!android.util.Patterns.EMAIL_ADDRESS.matcher(changePass.value.email).matches()
}
fun setEmail(email: String){
changePass.value = changePass.value.copy(email=email)
}
}

View File

@ -0,0 +1,34 @@
package com.example.shoesapptest.screen.forgotscreen.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.shoesapptest.common.CommonTextField
@Composable
fun EmailTextField(value: String,
onChangeValue: (String)->Unit,
isError: Boolean,
supportingText: @Composable () -> Unit,
placeholder: @Composable () -> Unit,
label: @Composable () -> Unit) {
Column(
modifier = Modifier
.padding(20.dp)
.wrapContentSize(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
label()
CommonTextField(
value = value,
onChangeValue = onChangeValue,
isError = isError,
supportingText = supportingText,
placeHolder = placeholder
)
}
}

View File

@ -0,0 +1,29 @@
package com.example.shoesapptest.screen.forgotscreen.component
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.common.CommonButton
@Composable
fun SendButton(
modifier: Modifier = Modifier,
onClick: () -> Unit,
content: @Composable () -> Unit
){
CommonButton(
onClick = onClick,
modifier = modifier.padding(50.dp),
buttonColors = ButtonColors(
containerColor = MatuleTheme.colors.accent,
contentColor = MatuleTheme.colors.background,
disabledContentColor = MatuleTheme.colors.accent,
disabledContainerColor = MatuleTheme.colors.accent
)
) {
content()
}
}

View File

@ -0,0 +1,37 @@
package com.example.shoesapptest.screen.forgotscreen.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
@Composable
fun TitleWithSubtitleText(title: String, subTitle:String){
Column(
modifier = Modifier
.padding(horizontal = 20.dp)
.padding(top = 50.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
)
{
Text(
text = title,
style = MatuleTheme.typography.headingBold32.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
Text(
text = subTitle,
maxLines = 2,
style = MatuleTheme.typography.subTitleRegular16.copy(color = MatuleTheme.colors.subTextDark),
textAlign = TextAlign.Center
)
}
}

View File

@ -0,0 +1,13 @@
package com.example.shoesapptest.screen.registrationscreen
import android.provider.ContactsContract.CommonDataKinds.Email
import com.example.shoesapptest.screen.forgotscreen.ChangePass
import java.lang.Error
data class Registration(
var email: String = "",
var password: String = "",
var name: String = "",
var isVisiblePassword: Boolean = false,
var errorMessage: String? = null
)

View File

@ -0,0 +1,197 @@
package com.example.shoesapptest.screen
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.example.shoesapptest.R
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.navigation.NavController
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.screen.forgotscreen.component.TitleWithSubtitleText
import com.example.shoesapptest.screen.registrationscreen.RegistrationViewModel
import com.example.shoesapptest.screen.registrationscreen.component.RegistrationBtn
import com.example.shoesapptest.screen.registrationscreen.component.RegistrationTextField
@Composable
fun RegistrationScreen(
navController: NavController,
registrationViewModel: RegistrationViewModel
){
Scaffold(
topBar = {
Row(
modifier = Modifier
.padding(top = 35.dp)
.fillMaxWidth()
.height(40.dp)
){
IconButton(onClick={}) {
Icon(painter = painterResource(R.drawable.back_arrow),
contentDescription = null)
}
}
},
bottomBar = {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(bottom = 50.dp)
.fillMaxWidth()
.height(40.dp)
) {
Text(
text= stringResource(R.string.sign_in_on_reg),
style=MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
}
}
) { paddingValues ->
RegistrationScreen(
paddingValues=paddingValues,
registrationViewModel=registrationViewModel,
onRegistrationSuccess = {navController.navigate("test")},
onRegistrationError = {errorMessage -> }
)
}
}
@Composable
fun RegistrationScreen(paddingValues: PaddingValues,
registrationViewModel: RegistrationViewModel,
onRegistrationSuccess: () -> Unit,
onRegistrationError: (String) -> Unit){
val register = registrationViewModel.registration
Column(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
) {
TitleWithSubtitleText(
title = "Регистрация",
subTitle = "Заполните Свои данные или продолжите через социальные медиа"
)
Spacer(modifier = Modifier.height(20.dp))
RegistrationTextField(
value = register.value.name,
onChangeValue = { registrationViewModel.setUserName(it) },
isError = false,
supportingText = { Text(text = stringResource(R.string.wrong_name)) },
placeholder = { Text(text = stringResource(R.string.template_name)) },
label = { Text(text = stringResource(R.string.name)) }
)
RegistrationTextField(
value = register.value.email,
onChangeValue = { registrationViewModel.setEmail(it) },
isError = registrationViewModel.emailHasError.value,
supportingText = { Text(text = stringResource(R.string.wrong_email)) },
placeholder = { Text(text = stringResource(R.string.template_email)) },
label = { Text(text = stringResource(R.string.email)) }
)
RegistrationTextField(
value = register.value.password,
onChangeValue = { registrationViewModel.setPassword(it) },
isError = false,
supportingText = { Text(text = stringResource(R.string.wrong_password)) },
placeholder = { Text(text = stringResource(R.string.template_password)) },
label = { Text(text = stringResource(R.string.password)) }
)
SimpleCheckbox()
RegistrationBtn(onClick = {
registrationViewModel.registration(
onSuccess = {
onRegistrationSuccess()
},
onError = { errorMessage ->
onRegistrationError(errorMessage)
}
)
}
) {
Text(text = stringResource(R.string.sign_up_on_reg))
}
}
}
@Composable
fun SimpleCheckbox(){
val isChecked = remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
){
Checkbox(
checked = isChecked.value,
onCheckedChange = {isChecked.value = it}
)
ClickableText(
text = AnnotatedString(
"Даю согласие на обработку персональных данных"
),
onClick={},
style = MatuleTheme.typography.bodyRegular16.copy(color = MatuleTheme.colors.text),
modifier = Modifier.padding(start = 8.dp)
)
}
}

View File

@ -0,0 +1,47 @@
package com.example.shoesapptest.screen.registrationscreen
import androidx.compose.animation.core.animateDecay
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.shoesapptest.data.model.RegistrationRequest
import com.example.shoesapptest.data.repository.AuthRepository
import kotlinx.coroutines.launch
import java.lang.Error
class RegistrationViewModel(
private val authRepository: AuthRepository
):ViewModel() {
var registration = mutableStateOf(Registration())
private set
val emailHasError = derivedStateOf {
if (registration.value.email.isEmpty()) return@derivedStateOf false
!android.util.Patterns.EMAIL_ADDRESS.matcher(registration.value.email).matches()
}
fun setEmail(email: String){
registration.value=registration.value.copy(email=email)
}
fun setPassword(password: String){
registration.value = registration.value.copy(password=password)
}
fun setUserName(name: String){
registration.value = registration.value.copy(name=name)
}
fun registration(onSuccess: () -> Unit, onError: (String) -> Unit){
viewModelScope.launch {
try {
val registrationRequest = RegistrationRequest(
userName = registration.value.name,
email = registration.value.email,
password = registration.value.password
)
authRepository.registration(registrationRequest)
onSuccess()
} catch (e: Exception) {
onError(e.message ?: "Ошибка регистрации")
}
}
}
}

View File

@ -0,0 +1,26 @@
package com.example.shoesapptest.screen.registrationscreen.component
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.common.CommonButton
@Composable
fun RegistrationBtn(
onClick: () -> Unit,
content: @Composable () -> Unit
){
CommonButton(
modifier = Modifier.padding(50.dp),
onClick = onClick,
buttonColors = ButtonColors(
containerColor = MatuleTheme.colors.accent,
contentColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.accent,
disabledContentColor = MatuleTheme.colors.accent
)
) { content()}
}

View File

@ -0,0 +1,36 @@
package com.example.shoesapptest.screen.registrationscreen.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.unit.dp
import androidx.datastore.preferences.protobuf.Internal.BooleanList
import com.example.shoesapptest.common.CommonTextField
import java.lang.Error
@Composable
fun RegistrationTextField(
value: String,
onChangeValue: (String) -> Unit,
isError: Boolean,
supportingText: @Composable () -> Unit,
placeholder: @Composable () -> Unit,
label: @Composable () -> Unit
){
Column(Modifier.padding(50.dp)
.wrapContentSize(),
verticalArrangement = Arrangement.spacedBy(12.dp)){
label()
CommonTextField(
value=value,
onChangeValue=onChangeValue,
isError = isError,
supportingText = supportingText,
placeHolder = placeholder
)
}
}

View File

@ -0,0 +1,35 @@
package com.example.shoesapptest.screen.registrationscreen.component
import android.icu.text.CaseMap.Title
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
@Composable
fun TextWithSubTitle(title: String,
subtitle:String){
Column(
modifier = Modifier.padding(horizontal = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
){
Text(
text = title,
style = MatuleTheme.typography.headingBold32.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
Text(
text = subtitle,
style = MatuleTheme.typography.subTitleRegular16.copy(color = MatuleTheme.colors.subTextDark),
textAlign = TextAlign.Center
)
}
}

View File

@ -0,0 +1,10 @@
package com.example.shoesapptest.screen.signin
data class SignInState (
var email: String = "",
var password: String = "",
var isVisiblePassword: Boolean = false,
var isLoading: Boolean = false,
var isSignIn: Boolean = false,
var errorMessage: String? = null
)

View File

@ -0,0 +1,27 @@
package com.example.shoesapptest.screen.signin
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
class SignInViewMode: ViewModel(){
var signInState = mutableStateOf(SignInState())
private set
val emailHasError = derivedStateOf {
if(signInState.value.email.isEmpty()) return@derivedStateOf false
!android.util.Patterns.EMAIL_ADDRESS.matcher(signInState.value.email).matches()
}
fun setEmail(email: String){
signInState.value = signInState.value.copy(email = email)
}
fun setPassword(password: String){
signInState.value = signInState.value.copy(password = password)
}
}

View File

@ -0,0 +1,128 @@
package com.example.shoesapp.ui.screen
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.R
import com.example.shoesapptest.screen.signin.SignInViewMode
import com.example.shoesapptest.screen.signin.component.AuthButton
import com.example.shoesapptest.screen.signin.component.AuthTextField
import com.example.shoesapptest.screen.signin.component.TitleWithSubtitleText
@Composable
fun SigninScreen() {
val signInViewModel: SignInViewMode = viewModel()
Scaffold(
topBar = {
Row(
modifier = Modifier
.padding(top = 35.dp)
.fillMaxWidth()
.height(40.dp)
) {
IconButton(onClick = {}) {
Icon(
painter = painterResource(R.drawable.back_arrow),
contentDescription = null
)
}
}
},
bottomBar = {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(bottom = 50.dp)
.fillMaxWidth()
.height(40.dp)
) {
Text(
stringResource(R.string.sign_up),
style = MatuleTheme.typography.bodyRegular16.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
}
}
) { paddingValues ->
SignInContent(paddingValues, signInViewModel)
}
}
@Composable
fun SignInContent(paddingValues: PaddingValues, signInViewMode: SignInViewMode) {
val signInState = signInViewMode.signInState
Column(
modifier = Modifier.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
TitleWithSubtitleText(
title = stringResource(R.string.hello),
subText = stringResource(R.string.sign_in_subtitle)
)
Spacer(modifier = Modifier.height(35.dp))
AuthTextField(
value = signInState.value.email,
onChangeValue = { signInViewMode.setEmail(it) },
isError = signInViewMode.emailHasError.value,
supportingText = { Text(text = stringResource(R.string.wrong_email))},
placeholder = { Text(text = stringResource(R.string.template_email)) },
label = { Text(text = stringResource(R.string.email))
}
)
AuthTextField(
value = signInState.value.password,
onChangeValue = { signInViewMode.setPassword(it)},
isError = false,
supportingText = { Text(text = "Неверный пароль")},
placeholder = { Text(text = stringResource(R.string.template_password))},
label = { Text(text = stringResource(R.string.password))}
)
AuthButton(onClick = {}) {
Text(stringResource(R.string.sign_in))
}
}
}

View File

@ -0,0 +1,27 @@
package com.example.shoesapptest.screen.signin.component
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.common.CommonButton
@Composable
fun AuthButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
){
CommonButton(
onClick = onClick,
buttonColors = ButtonColors(
contentColor = MatuleTheme.colors.background,
containerColor = MatuleTheme.colors.accent,
disabledContainerColor = MatuleTheme.colors.accent,
disabledContentColor = MatuleTheme.colors.accent
),
modifier = modifier.padding(50.dp)
) { content()}
}

View File

@ -0,0 +1,36 @@
package com.example.shoesapptest.screen.signin.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.shoesapptest.common.CommonTextField
@Composable
fun AuthTextField(
value: String,
onChangeValue: (String)-> Unit,
isError: Boolean,
supportingText: @Composable () -> Unit,
placeholder: @Composable () -> Unit,
label: @Composable () -> Unit)
{
Column(
modifier = Modifier
.padding(horizontal = 20.dp)
.wrapContentSize(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
label()
CommonTextField(
value = value,
onChangeValue = onChangeValue,
isError = isError,
supportingText = supportingText,
placeHolder = placeholder
)
}
}

View File

@ -0,0 +1,33 @@
package com.example.shoesapptest.screen.signin.component
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
@Composable
fun TitleWithSubtitleText(title: String, subText: String) {
Column(
modifier = Modifier.padding(horizontal = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = title,
style = MatuleTheme.typography.headingBold32.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center,
)
Text(
text = subText,
maxLines = 2,
style = MatuleTheme.typography.subTitleRegular16.copy(color = MatuleTheme.colors.subTextDark),
textAlign = TextAlign.Center,
)
}
}

View File

@ -1,2 +0,0 @@
package com.example.shoesapptest.ui.common

View File

@ -1,2 +0,0 @@
package com.example.shoesapptest.ui.screen.signin.component

View File

@ -1,260 +0,0 @@
package com.example.shoesapptest.screen
import android.app.Dialog
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalBottomSheetDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.R
import androidx.compose.material3.AlertDialog
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
@Composable
fun ForgotPasswordScreen(){
Scaffold(
topBar = {
Row(
modifier = Modifier
.padding(top = 35.dp)
.fillMaxWidth()
.height(40.dp)
) {
IconButton(onClick = {}) {
Icon(painter= painterResource(R.drawable.back_arrow),
contentDescription = null)
}
}
}
){ paddingValues ->
ScreenContent(paddingValues)
}
}
@Composable
fun ScreenContent(paddingValues: PaddingValues){
Column(
modifier = Modifier.padding(paddingValues = paddingValues)
){
TitleWithSubtitleText(
title = stringResource(R.string.title_forgot_password),
subTitle = stringResource(R.string.subtitle_forgot_password)
)
val email = remember { mutableStateOf("") }
Spacer(modifier = Modifier.height(35.dp))
EmailTextField(
placeHolderText = stringResource(R.string.placeholder_email),
EmailValue = email.value,
onEmailChange = {
email.value=it
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EmailTextField(EmailValue: String,
onEmailChange: (String) -> Unit,
placeHolderText: String){
Column(
modifier = Modifier
.padding(20.dp)
.wrapContentSize(),
verticalArrangement = Arrangement.spacedBy(8.dp)
){
val email = remember { MutableInteractionSource() }
BasicTextField(
value=EmailValue,
onValueChange = {onEmailChange(it)},
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
){ innerTextField ->
TextFieldDefaults.DecorationBox(
value = EmailValue,
singleLine = true,
innerTextField = innerTextField,
colors = TextFieldDefaults.colors(
focusedContainerColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.background,
unfocusedContainerColor = MatuleTheme.colors.background,
errorContainerColor = MatuleTheme.colors.background,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent
),
visualTransformation = VisualTransformation.None,
interactionSource = email,
enabled = true,
placeholder = {
if (placeHolderText != null)
Text(
text = placeHolderText,
style = MatuleTheme.typography.bodyRegular14.copy(color = MatuleTheme.colors.hint)
)
}
)
}
CommonButton(
modifier = Modifier.padding(top=30.dp),
buttonLabel = stringResource(R.string.send_email_forgot)
) {}
}
}
@Composable
fun TitleWithSubtitleText(title: String, subTitle:String){
Column(
modifier = Modifier
.padding(horizontal = 20.dp)
.padding(top = 50.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
)
{
Text(
text = title,
style = MatuleTheme.typography.headingBold32.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
Text(
text = subTitle,
maxLines = 2,
style = MatuleTheme.typography.subTitleRegular16.copy(color = MatuleTheme.colors.subTextDark),
textAlign = TextAlign.Center
)
}
}
@Composable
fun CommonButton(modifier: Modifier, buttonLabel: String, onClick: () -> Unit){
var showDialog by remember { mutableStateOf(false) }
Button(
modifier = modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
.height(50.dp)
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.accent)
,
colors = ButtonColors(
containerColor = MatuleTheme.colors.accent,
disabledContentColor = Color.Transparent,
disabledContainerColor = MatuleTheme.colors.accent,
contentColor = Color.Transparent
),
onClick= {showDialog=true}
){
Text(
text=buttonLabel,
style = MatuleTheme.typography.bodyRegular14.copy(color= MatuleTheme.colors.background),
textAlign = TextAlign.Center
)
}
if (showDialog) {
CheckEmailDialog(onDismiss = { showDialog = false })
}
}
@Composable
fun CheckEmailDialog(onDismiss: () -> Unit) {
AlertDialog(
onDismissRequest = onDismiss,
confirmButton = {},
title = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Row(
modifier = Modifier.padding(bottom = 8.dp),
horizontalArrangement = Arrangement.Center
) {
Icon(
painter = painterResource(R.drawable.email_image),
contentDescription = null,
tint = MatuleTheme.colors.accent,
modifier = Modifier.size(40.dp)
)
}
Text(
text = "Проверьте Ваш Email",
style = MatuleTheme.typography.bodyRegular16.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
}
},
text = {
Text(
text = "Мы отправили код восстановления пароля на вашу электронную почту.",
textAlign = TextAlign.Center,
style = MatuleTheme.typography.bodyRegular14.copy(color = MatuleTheme.colors.hint)
)
},
modifier = Modifier.clip(RoundedCornerShape(14.dp)),
containerColor = Color.White
)
}

View File

@ -1,350 +0,0 @@
package com.example.shoesapptest.screen
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeGesturesPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxColors
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.example.shoesapptest.R
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapp.ui.theme.matuleFontFamily
import kotlin.math.sin
@Composable
fun RegistrtionScreen(){
Scaffold(
topBar = {
Row(
modifier = Modifier
.padding(top = 35.dp)
.fillMaxWidth()
.height(40.dp)
){
IconButton(onClick={}) {
Icon(painter = painterResource(R.drawable.back_arrow),
contentDescription = null)
}
}
},
bottomBar = {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(bottom = 50.dp)
.fillMaxWidth()
.height(40.dp)
) {
Text(
text= stringResource(R.string.sign_in_on_reg),
style=MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
}
}
) { paddingValues ->
RegistrationScreen(paddingValues)
}
}
@Composable
fun RegistrationScreen(paddingValues: PaddingValues){
Column(
modifier = Modifier.padding(paddingValues=paddingValues)
) {
val title= stringResource(R.string.sign_up_on_reg)
val subtitle= stringResource(R.string.sign_in_subtitle)
TitleWithSubtitleText(title, subtitle)
val name = remember { mutableStateOf("") }
val email = remember { mutableStateOf("") }
val password = remember { mutableStateOf("") }
RegistrationTextFields(
labelNameText = stringResource(R.string.name),
labelEmailText = stringResource(R.string.email),
labelPasswordText = stringResource(R.string.password),
nameValue = name.value,
emailValue = email.value,
passwordValue = password.value,
onNameChange = {name.value=it},
onEmailChange = {email.value=it},
onPasswordChange = {password.value=it},
placeholderTextName = stringResource(R.string.template_name),
placeholderTextEmail = stringResource(R.string.template_email),
placeholderTextPassword = stringResource(R.string.template_password)
)
RegButton(modifier = Modifier.padding(top = 50.dp),
buttonLabel = stringResource(R.string.sign_up_btn)
) { }
}
}
@Composable
fun RegButton(modifier: Modifier, buttonLabel: String, onClick: () -> Unit){
Button(
modifier = modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
.height(50.dp)
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
,
colors=ButtonColors(
containerColor = MatuleTheme.colors.accent,
disabledContainerColor = MatuleTheme.colors.accent,
contentColor = Color.Transparent,
disabledContentColor = Color.Transparent
),
onClick=onClick
){
Text(
text=buttonLabel,
style = MatuleTheme.typography.bodyRegular14.copy(color=MatuleTheme.colors.background),
textAlign = TextAlign.Center
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RegistrationTextFields(nameValue: String,
onNameChange: (String) -> Unit,
placeholderTextName: String,
labelNameText: String,
emailValue: String,
onEmailChange: (String) -> Unit,
placeholderTextEmail: String,
labelEmailText: String,
passwordValue: String,
onPasswordChange: (String) -> Unit,
placeholderTextPassword: String,
labelPasswordText: String){
Column(
modifier = Modifier
.padding(horizontal = 20.dp)
.wrapContentSize(),
verticalArrangement = Arrangement.spacedBy(8.dp)
){
if (labelNameText!=null){
Text(
text=labelNameText,
style=MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
textAlign = TextAlign.Right
)
}
val name = remember { MutableInteractionSource() }
BasicTextField(
value = nameValue,
onValueChange = { onNameChange(it) },
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
){
innerTextField ->
TextFieldDefaults.DecorationBox(
value=nameValue,
singleLine = true,
innerTextField = innerTextField,
enabled = true,
colors= TextFieldDefaults.colors(
focusedContainerColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.background,
unfocusedContainerColor = MatuleTheme.colors.background,
errorContainerColor = MatuleTheme.colors.background,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent
),
visualTransformation = VisualTransformation.None,
interactionSource = name,
placeholder = {
if (placeholderTextName!=null)
Text(
text=placeholderTextName,
style = MatuleTheme.typography.bodyRegular14.copy(MatuleTheme.colors.hint)
)
}
)
}
if(labelEmailText!=null){
Text(
text = labelEmailText,
style = MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
textAlign = TextAlign.Right
)
}
val email = remember { MutableInteractionSource() }
BasicTextField(
value=emailValue,
onValueChange = {onEmailChange(it)},
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
){
innerTextField ->
TextFieldDefaults.DecorationBox(
value = emailValue,
singleLine = true,
innerTextField = innerTextField,
enabled = true,
colors = TextFieldDefaults.colors(
focusedContainerColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.background,
unfocusedContainerColor = MatuleTheme.colors.background,
errorContainerColor = MatuleTheme.colors.background,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent
),
visualTransformation = VisualTransformation.None,
interactionSource = email,
placeholder = {
if(placeholderTextEmail!=null)
Text(
text = placeholderTextEmail,
style = MatuleTheme.typography.bodyRegular14.copy(MatuleTheme.colors.hint)
)
}
)
}
if (labelPasswordText!=null){
Text(
text = labelPasswordText,
style = MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
}
val password = remember { MutableInteractionSource() }
var passwordVisible by remember { mutableStateOf(false) }
Row(
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
){
BasicTextField(
value=passwordValue,
onValueChange = { onPasswordChange(it) },
visualTransformation = if(passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
modifier = Modifier
.weight(1f)
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
){ innerTextField ->
TextFieldDefaults.DecorationBox(
value = passwordValue,
singleLine = true,
innerTextField = innerTextField,
enabled = true,
colors = TextFieldDefaults.colors(
focusedContainerColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.background,
unfocusedContainerColor = MatuleTheme.colors.background,
errorContainerColor = MatuleTheme.colors.background,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent
),
visualTransformation = if(passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
interactionSource = password,
placeholder = {
if (placeholderTextPassword!=null){
Text(
text = placeholderTextPassword,
style = MatuleTheme.typography.bodyRegular14.copy(MatuleTheme.colors.hint)
)
}
}
)
}
IconButton(
onClick = {passwordVisible = !passwordVisible}
){
Icon(
painter = painterResource(if(passwordVisible) R.drawable.eye_close else R.drawable.eye_open),
contentDescription = null
)
}
}
val checkedState = remember { mutableStateOf(false) }
Row {
Checkbox(
checked = checkedState.value,
onCheckedChange = { checkedState.value = it }
)
ClickableText(
text = AnnotatedString(stringResource(R.string.personal_data)),
style = MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
modifier = Modifier
.padding(top=8.dp),
onClick = {})
}
}
}

View File

@ -1,3 +0,0 @@
package com.example.shoesapptest.ui.screen.signin
data class SignInState()

View File

@ -1,4 +0,0 @@
package com.example.shoesapptest.ui.screen.signin
class SignInViewModel {
}

View File

@ -1,294 +0,0 @@
package com.example.shoesapp.ui.screen
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.shoesapp.ui.theme.MatuleTheme
import com.example.shoesapptest.R
@Composable
fun SigninScreen(){
Scaffold(
topBar = {
Row(
modifier = Modifier
.padding(top = 35.dp)
.fillMaxWidth()
.height(40.dp)
) {
IconButton(onClick = {}) {
Icon(painter= painterResource(R.drawable.back_arrow),
contentDescription = null)
}
}
},
bottomBar = {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(bottom = 50.dp)
.fillMaxWidth()
.height(40.dp)
) {
Text(
text= stringResource(R.string.sign_up),
style = MatuleTheme.typography.bodyRegular16.copy(color=MatuleTheme.colors.text),
textAlign = TextAlign.Center
)
}
}
) { paddingValues ->
SignInContent(paddingValues)
}
}
@Composable
fun SignInContent(paddingValues: PaddingValues) {
Column(
modifier = Modifier.padding(paddingValues = paddingValues)
) {
TitleWithSubtitleText(
title = stringResource(R.string.hello),
subTitle = stringResource(R.string.sign_in_subtitle)
)
val email = remember { mutableStateOf("") }
val password = remember { mutableStateOf("")}
Spacer(modifier = Modifier.height(35.dp))
AuthTextField(
labelTextEmail = stringResource(R.string.email),
labelTextPassword = stringResource(R.string.password),
placeHolderTextEmail = stringResource(R.string.template_email),
placeHolderTextPassword= stringResource(R.string.template_password),
emailValue = email.value,
onEmailChange = {
email.value = it
},
passwordValue = password.value,
onPasswordChange = {
password.value = it
}
)
CommonButton(
modifier = Modifier.padding(top=50.dp),
buttonLabel = stringResource(R.string.sign_in)){
}
}
}
@Composable
fun TitleWithSubtitleText(title: String, subTitle:String){
Column(
modifier = Modifier.run { padding(horizontal = 20.dp) },
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
)
{
Text(
text = title,
style = MatuleTheme.typography.headingBold32.copy(color = MatuleTheme.colors.text),
textAlign = TextAlign.Center,
)
Text(
text = subTitle,
maxLines = 2,
style = MatuleTheme.typography.subTitleRegular16.copy(color = MatuleTheme.colors.subTextDark),
textAlign = TextAlign.Center
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AuthTextField( emailValue: String,
onEmailChange: (String) -> Unit,
passwordValue: String,
onPasswordChange: (String) -> Unit,
placeHolderTextEmail: String? = null,
placeHolderTextPassword: String? = null,
labelTextEmail: String? = null,
labelTextPassword: String? = null){
Column (
modifier = Modifier
.padding(horizontal = 20.dp)
.wrapContentSize(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
if (labelTextEmail != null) {
Text(
text = labelTextEmail,
style = MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
textAlign = TextAlign.Right
)
}
val interaction = remember { MutableInteractionSource() }
BasicTextField(
value = emailValue,
onValueChange = { onEmailChange(it) },
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
) { innerTextField ->
TextFieldDefaults.DecorationBox(
value = emailValue,
singleLine = true,
innerTextField = innerTextField,
enabled = true,
colors = TextFieldDefaults.colors(
focusedContainerColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.background,
unfocusedContainerColor = MatuleTheme.colors.background,
errorContainerColor = MatuleTheme.colors.background,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent
),
visualTransformation = VisualTransformation.None,
interactionSource = interaction,
placeholder = {
if (placeHolderTextEmail != null)
Text(
text = placeHolderTextEmail,
style = MatuleTheme.typography.bodyRegular14.copy(color = MatuleTheme.colors.hint)
)
}
)
}
if (labelTextPassword != null) {
Text(
text = labelTextPassword,
style = MatuleTheme.typography.bodyRegular16.copy(MatuleTheme.colors.text),
textAlign = TextAlign.Right
)
}
val password = remember { MutableInteractionSource() }
var passwordVisible by remember { mutableStateOf(false) }
Row(
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
)
{
BasicTextField(
value = passwordValue,
onValueChange = { onPasswordChange(it) },
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
modifier = Modifier
.weight(1f)
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.background)
) { innerTextField ->
TextFieldDefaults.DecorationBox(
value = passwordValue,
singleLine = true,
innerTextField = innerTextField,
enabled = true,
colors = TextFieldDefaults.colors(
focusedContainerColor = MatuleTheme.colors.background,
disabledContainerColor = MatuleTheme.colors.background,
unfocusedContainerColor = MatuleTheme.colors.background,
errorContainerColor = MatuleTheme.colors.background,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent
),
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
interactionSource = password,
placeholder = {
if (placeHolderTextPassword != null)
Text(
text = placeHolderTextPassword,
style = MatuleTheme.typography.bodyRegular14.copy(color = MatuleTheme.colors.hint)
)
}
)
}
IconButton(
onClick={passwordVisible = !passwordVisible}
) {
Icon(
painter= painterResource(if (passwordVisible) R.drawable.eye_close else R.drawable.eye_open),
contentDescription = "Показать пароль"
)
}
}
}
}
@Composable
fun CommonButton(modifier: Modifier, buttonLabel: String, onClick: () -> Unit){
Button(
modifier = modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()
.height(50.dp)
.clip(RoundedCornerShape(14.dp))
.background(MatuleTheme.colors.accent)
,
colors = ButtonColors(
containerColor = MatuleTheme.colors.accent,
disabledContentColor = Color.Transparent,
disabledContainerColor = MatuleTheme.colors.accent,
contentColor = Color.Transparent
),
onClick=onClick
){
Text(
text=buttonLabel,
style = MatuleTheme.typography.bodyRegular14.copy(color= MatuleTheme.colors.background),
textAlign = TextAlign.Center
)
}
}

View File

@ -1,2 +0,0 @@
package com.example.shoesapptest.ui.screen.signin.component

View File

@ -1,2 +0,0 @@
package com.example.shoesapptest.ui.screen.signin.component

View File

@ -1,2 +0,0 @@
package com.example.shoesapptest.ui.screen.signin.component

View File

@ -8,7 +8,7 @@
<string name="sign_up">Вы впервые? Создать пользователя</string>
<string name="template_password">••••••••</string>
<string name="password">Пароль</string>
<string name="title_forgot_password">Забыл Пароль</string>
<string name="title_forgot_password">Забыли Пароль</string>
<string name="subtitle_forgot_password">Введите Свою Учётную Запись Для Сброса</string>
<string name="placeholder_email">xyz@gmail.com</string>
<string name="send_email_forgot">Отправить</string>
@ -16,6 +16,9 @@
<string name="sign_up_on_reg">Регистрация</string>
<string name="template_name">XXXXXXXX</string>
<string name="sign_up_btn">Зарегистрироваться</string>
<string name="name">Имя</string>
<string name="name">Ваше Имя</string>
<string name="personal_data">Даю согласие на обработку персональных данных</string>
<string name="wrong_email">Неверный Логин</string>
<string name="wrong_password">Неверный пароль</string>
<string name="wrong_name">Неверное имя пользователя</string>
</resources>