api_auth
This commit is contained in:
parent
fa5067e017
commit
ba5e517178
@ -7,19 +7,12 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.shoesapp.ui.screen.SigninScreen
|
||||
import com.example.shoesapp.ui.screen.SignInScreen
|
||||
import com.example.shoesapp.ui.theme.MatuleTheme
|
||||
import com.example.shoesapptest.data.local.DataStore
|
||||
import com.example.shoesapptest.data.remote.network.RetrofitClient
|
||||
import com.example.shoesapptest.data.repository.AuthRepository
|
||||
import com.example.shoesapptest.di.appModules
|
||||
import com.example.shoesapptest.domain.usecase.AuthUseCase
|
||||
import com.example.shoesapptest.screen.StartsScreens.FirstScreen
|
||||
import com.example.shoesapptest.screen.StartsScreens.SlideScreen
|
||||
import com.example.shoesapptest.screen.forgotpassword.ForgotPassScreen
|
||||
import com.example.shoesapptest.screen.regscreen.RegisterAccountScreen
|
||||
import org.koin.core.context.GlobalContext
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -28,12 +21,6 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
setContent {
|
||||
MatuleTheme {
|
||||
if (GlobalContext.getOrNull() == null) {
|
||||
startKoin {
|
||||
modules(appModules)
|
||||
}
|
||||
}
|
||||
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(
|
||||
@ -57,10 +44,13 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
composable(Screen.SignIn.route) {
|
||||
SigninScreen(
|
||||
SignInScreen(
|
||||
onNavigationToRegScreen = {
|
||||
navController.navigate(Screen.Registration.route)
|
||||
},
|
||||
onSignInSuccess = {
|
||||
// Добавить навигацию после успешного входа
|
||||
},
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ fun CommonButton(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
buttonColors: ButtonColors,
|
||||
enabled: Boolean = true,
|
||||
content: @Composable () -> Unit)
|
||||
{
|
||||
Button(
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.example.shoesapptest.data.remote.network.auth
|
||||
|
||||
import com.example.shoesapptest.data.remote.network.dto.AuthorizationRequest
|
||||
import com.example.shoesapptest.data.remote.network.dto.AuthorizationResponse
|
||||
import com.example.shoesapptest.data.remote.network.dto.RegistrationRequest
|
||||
import com.example.shoesapptest.data.remote.network.dto.RegistrationResponse
|
||||
import retrofit2.http.Body
|
||||
@ -9,5 +11,6 @@ interface AuthRemoteSource {
|
||||
@POST("/registration")
|
||||
suspend fun registration(@Body registrationRequest: RegistrationRequest): RegistrationResponse
|
||||
|
||||
|
||||
@POST("/authorization")
|
||||
suspend fun authorization(@Body authorizationRequest: AuthorizationRequest): AuthorizationResponse
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.example.shoesapptest.data.remote.network.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class AuthorizationRequest (
|
||||
val email: String,
|
||||
val password: String
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.example.shoesapptest.data.remote.network.dto
|
||||
|
||||
data class AuthorizationResponse (
|
||||
val token: String,
|
||||
val userId: String,
|
||||
val userName: String,
|
||||
val email: String,
|
||||
val expiresIn: Long
|
||||
)
|
@ -3,6 +3,8 @@ package com.example.shoesapptest.data.repository
|
||||
import com.example.shoesapptest.data.local.DataStore
|
||||
import com.example.shoesapptest.data.remote.network.NetworkResponse
|
||||
import com.example.shoesapptest.data.remote.network.auth.AuthRemoteSource
|
||||
import com.example.shoesapptest.data.remote.network.dto.AuthorizationRequest
|
||||
import com.example.shoesapptest.data.remote.network.dto.AuthorizationResponse
|
||||
import com.example.shoesapptest.data.remote.network.dto.RegistrationRequest
|
||||
import com.example.shoesapptest.data.remote.network.dto.RegistrationResponse
|
||||
|
||||
@ -10,4 +12,8 @@ class AuthRepository(val authRemoteSource: AuthRemoteSource) {
|
||||
suspend fun registration(registrationRequest: RegistrationRequest): RegistrationResponse {
|
||||
return authRemoteSource.registration(registrationRequest)
|
||||
}
|
||||
|
||||
suspend fun authorization(authorizationRequest: AuthorizationRequest): AuthorizationResponse {
|
||||
return authRemoteSource.authorization(authorizationRequest)
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import com.example.shoesapptest.data.remote.network.auth.AuthRemoteSource
|
||||
import com.example.shoesapptest.data.repository.AuthRepository
|
||||
import com.example.shoesapptest.domain.usecase.AuthUseCase
|
||||
import com.example.shoesapptest.screen.regscreen.RegistrationViewModel
|
||||
import com.example.shoesapptest.screen.signin.SignInViewModel
|
||||
import org.koin.core.module.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
@ -15,4 +16,5 @@ val appModules = module {
|
||||
single <AuthRepository> {AuthRepository(get()) }
|
||||
single { AuthUseCase(get (),get()) }
|
||||
viewModel { RegistrationViewModel(get()) }
|
||||
viewModel { SignInViewModel(get())}
|
||||
}
|
@ -2,6 +2,8 @@ package com.example.shoesapptest.domain.usecase
|
||||
|
||||
import com.example.shoesapptest.data.local.DataStore
|
||||
import com.example.shoesapptest.data.remote.network.NetworkResponse
|
||||
import com.example.shoesapptest.data.remote.network.dto.AuthorizationRequest
|
||||
import com.example.shoesapptest.data.remote.network.dto.AuthorizationResponse
|
||||
import com.example.shoesapptest.data.remote.network.dto.RegistrationRequest
|
||||
import com.example.shoesapptest.data.remote.network.dto.RegistrationResponse
|
||||
import com.example.shoesapptest.data.repository.AuthRepository
|
||||
@ -25,4 +27,19 @@ class AuthUseCase(private val dataStore: DataStore, private val authRepository:
|
||||
emit(NetworkResponse.Error("Unknown Error"))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun authorization(authorizationRequest: AuthorizationRequest): Flow<NetworkResponse<AuthorizationResponse>> = flow {
|
||||
try {
|
||||
emit(NetworkResponse.Loading)
|
||||
val result = authRepository.authorization(authorizationRequest)
|
||||
dataStore.setToken(result.token)
|
||||
emit(NetworkResponse.Success(result))
|
||||
} catch (e: Exception) {
|
||||
e.message?.let {
|
||||
emit(NetworkResponse.Error(it))
|
||||
return@flow
|
||||
}
|
||||
emit(NetworkResponse.Error("Unknown Error"))
|
||||
}
|
||||
}
|
||||
}
|
@ -3,25 +3,61 @@ package com.example.shoesapptest.screen.signin
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.shoesapptest.data.remote.network.NetworkResponse
|
||||
import com.example.shoesapptest.data.remote.network.dto.AuthorizationRequest
|
||||
import com.example.shoesapptest.domain.usecase.AuthUseCase
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SignInViewMode: ViewModel(){
|
||||
class SignInViewModel(private val authUseCase: AuthUseCase) : ViewModel() {
|
||||
var signInState = mutableStateOf(SignInState())
|
||||
private set
|
||||
|
||||
val emailHasError = derivedStateOf {
|
||||
if(signInState.value.email.isEmpty()) return@derivedStateOf false
|
||||
if (signInState.value.email.isEmpty()) return@derivedStateOf false
|
||||
!android.util.Patterns.EMAIL_ADDRESS.matcher(signInState.value.email).matches()
|
||||
}
|
||||
|
||||
fun setEmail(email: String){
|
||||
fun setEmail(email: String) {
|
||||
signInState.value = signInState.value.copy(email = email)
|
||||
}
|
||||
|
||||
|
||||
fun setPassword(password: String){
|
||||
fun setPassword(password: String) {
|
||||
signInState.value = signInState.value.copy(password = password)
|
||||
}
|
||||
|
||||
fun setErrorMessage(message: String?) {
|
||||
signInState.value = signInState.value.copy(errorMessage = message)
|
||||
}
|
||||
|
||||
private fun setLoading(isLoading: Boolean) {
|
||||
signInState.value = signInState.value.copy(isLoading = isLoading)
|
||||
}
|
||||
|
||||
fun signIn(onSuccess: () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
val authorizationRequest = AuthorizationRequest(
|
||||
email = signInState.value.email,
|
||||
password = signInState.value.password
|
||||
)
|
||||
|
||||
authUseCase.authorization(authorizationRequest).collect { response ->
|
||||
when (response) {
|
||||
is NetworkResponse.Error -> {
|
||||
setLoading(false)
|
||||
setErrorMessage(response.errorMessage)
|
||||
}
|
||||
is NetworkResponse.Success -> {
|
||||
setLoading(false)
|
||||
signInState.value = signInState.value.copy(isSignIn = true)
|
||||
onSuccess()
|
||||
}
|
||||
is NetworkResponse.Loading -> {
|
||||
setLoading(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,13 +16,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
@ -41,19 +45,36 @@ import androidx.navigation.NavController
|
||||
import com.example.shoesapp.ui.theme.MatuleTheme
|
||||
import com.example.shoesapptest.R
|
||||
import com.example.shoesapptest.common.CommonButton
|
||||
import com.example.shoesapptest.screen.signin.SignInViewMode
|
||||
import com.example.shoesapptest.screen.signin.SignInViewModel
|
||||
import com.example.shoesapptest.screen.signin.component.AuthButton
|
||||
import com.example.shoesapptest.screen.signin.component.AuthTextField
|
||||
import com.example.shoesapptest.screen.signin.component.TitleWithSubtitleText
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
fun SigninScreen(
|
||||
fun SignInScreen(
|
||||
onNavigationToRegScreen: () -> Unit,
|
||||
onSignInSuccess: () -> Unit,
|
||||
navController: NavController
|
||||
) {
|
||||
val signInViewModel: SignInViewMode = viewModel()
|
||||
val viewModel: SignInViewModel = koinViewModel()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
LaunchedEffect(viewModel.signInState.value.isSignIn) {
|
||||
if (viewModel.signInState.value.isSignIn) {
|
||||
onSignInSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel.signInState.value.errorMessage) {
|
||||
viewModel.signInState.value.errorMessage?.let {
|
||||
snackbarHostState.showSnackbar(it)
|
||||
viewModel.setErrorMessage(null)
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
topBar = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@ -92,17 +113,24 @@ fun SigninScreen(
|
||||
) { paddingValues ->
|
||||
SignInContent(
|
||||
paddingValues = paddingValues,
|
||||
signInViewMode = signInViewModel,
|
||||
viewModel = viewModel,
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SignInContent(paddingValues: PaddingValues, signInViewMode: SignInViewMode, navController: NavController) {
|
||||
val signInState = signInViewMode.signInState
|
||||
fun SignInContent(
|
||||
paddingValues: PaddingValues,
|
||||
viewModel: SignInViewModel,
|
||||
navController: NavController
|
||||
) {
|
||||
val state = viewModel.signInState.value
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.padding(20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
TitleWithSubtitleText(
|
||||
@ -112,24 +140,23 @@ fun SignInContent(paddingValues: PaddingValues, signInViewMode: SignInViewMode,
|
||||
Spacer(modifier = Modifier.height(35.dp))
|
||||
|
||||
AuthTextField(
|
||||
value = signInState.value.email,
|
||||
onChangeValue = { signInViewMode.setEmail(it) },
|
||||
isError = signInViewMode.emailHasError.value,
|
||||
value = state.email,
|
||||
onChangeValue = { viewModel.setEmail(it) },
|
||||
isError = viewModel.emailHasError.value,
|
||||
supportingText = { Text(text = stringResource(R.string.LoginError))},
|
||||
placeholder = { Text(text = stringResource(R.string.template_email)) },
|
||||
label = { Text(text = stringResource(R.string.email)) }
|
||||
)
|
||||
|
||||
AuthTextField(
|
||||
value = signInState.value.password,
|
||||
onChangeValue = { signInViewMode.setPassword(it) },
|
||||
value = state.password,
|
||||
onChangeValue = { viewModel.setPassword(it) },
|
||||
isError = false,
|
||||
supportingText = { Text(text = "Неверный пароль")},
|
||||
placeholder = { Text(text = stringResource(R.string.PasswordPlaceHolder)) },
|
||||
label = { Text(text = stringResource(R.string.Password)) }
|
||||
)
|
||||
|
||||
|
||||
Text(
|
||||
text = "Забыл пароль",
|
||||
style = MatuleTheme.typography.bodyRegular16.copy(color = MatuleTheme.colors.text),
|
||||
@ -140,10 +167,19 @@ fun SignInContent(paddingValues: PaddingValues, signInViewMode: SignInViewMode,
|
||||
.padding(top = 8.dp)
|
||||
)
|
||||
|
||||
AuthButton(onClick = {}) {
|
||||
AuthButton(
|
||||
onClick = { viewModel.signIn {} },
|
||||
enabled = !viewModel.emailHasError.value &&
|
||||
state.email.isNotEmpty() &&
|
||||
state.password.isNotEmpty()
|
||||
) {
|
||||
if (state.isLoading) {
|
||||
CircularProgressIndicator()
|
||||
} else {
|
||||
Text(stringResource(R.string.Sign_In))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -152,4 +188,3 @@ fun SignInContent(paddingValues: PaddingValues, signInViewMode: SignInViewMode,
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -13,16 +13,20 @@ import com.example.shoesapptest.common.CommonButton
|
||||
fun AuthButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
){
|
||||
) {
|
||||
CommonButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.padding(50.dp),
|
||||
enabled = enabled,
|
||||
buttonColors = ButtonColors(
|
||||
contentColor = MatuleTheme.colors.background,
|
||||
containerColor = MatuleTheme.colors.accent,
|
||||
disabledContainerColor = MatuleTheme.colors.accent,
|
||||
disabledContentColor = MatuleTheme.colors.accent
|
||||
),
|
||||
modifier = modifier.padding(50.dp)
|
||||
) { content()}
|
||||
)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user