fix authorization, add profile layout

This commit is contained in:
varksyu 2025-02-19 11:30:24 +03:00
parent e2f43cb936
commit 5840424b91
13 changed files with 291 additions and 39 deletions

View File

@ -1,12 +1,12 @@
package ru.myitschool.work.data.auth
import ru.myitschool.work.data.user.UserDto
import ru.sicampus.bootcamp2025.domain.auth.AuthRepo
import ru.myitschool.work.domain.auth.AuthRepo
class AuthRepoImpl(
private val authNetworkDataSource: AuthNetworkDataSource,
private val authStorageDataSource: AuthStorageDataSource
) : AuthRepo{
) : AuthRepo {
override suspend fun isUserExist(email: String): Result<Boolean?> {
return authNetworkDataSource.isUserExist(email)
}

View File

@ -12,8 +12,6 @@ data class UserDto(
val id : Long?,
@SerialName("login")
var login: String,
@SerialName("birthDate")
var birthDate : String?,
@SerialName("name")
var name: String,
@SerialName("avatarUrl")

View File

@ -0,0 +1,8 @@
package ru.myitschool.work.domain.auth
import ru.myitschool.work.data.user.UserDto
interface AuthRepo {
suspend fun isUserExist(email: String): Result<Boolean?>
suspend fun login(email: String, password: String) : Result <UserDto>
}

View File

@ -0,0 +1,12 @@
package ru.myitschool.work.domain.auth
import javax.inject.Inject
class IsUserExistUseCase (
private val authRepo : AuthRepo
) {
suspend operator fun invoke(email : String) : Result<Boolean?> {
return authRepo.isUserExist(email)
}
}

View File

@ -0,0 +1,11 @@
package ru.myitschool.work.domain.auth
import ru.myitschool.work.data.user.UserDto
class LoginUseCase(
private val authRepo : AuthRepo
){
suspend operator fun invoke(email : String, password : String) : Result<UserDto> {
return authRepo.login(email, password)
}
}

View File

@ -0,0 +1,18 @@
package ru.sicampus.bootcamp2025.data
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
object Network {
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
isLenient = true
ignoreUnknownKeys = true
})
}
}
}

View File

@ -11,7 +11,9 @@ import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentLoginBinding
import ru.myitschool.work.ui.RootActivity
import ru.myitschool.work.utils.collectWhenStarted
import ru.myitschool.work.utils.collectWithLifecycle
import ru.myitschool.work.utils.visibleOrGone
@AndroidEntryPoint
@ -19,17 +21,17 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
private var _viewBinding: FragmentLoginBinding? = null
private val viewBinding: FragmentLoginBinding get() = _viewBinding!!
private val viewModel: LoginViewModel by viewModels()
private val viewModel: LoginViewModel by viewModels() {LoginViewModel.Factory}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_viewBinding = FragmentLoginBinding.bind(view)
/*viewBinding.signInButton.setOnClickListener {
viewBinding.signInButton.setOnClickListener {
val login = viewBinding.userLogin.text.toString()
val password = viewBinding.userPassword.text.toString()
if (!isValidEmail(login)) {
if (!isValidLogin(login)) {
viewBinding.errorText.text = getString(R.string.error_valid)
viewBinding.errorText.visibility = View.VISIBLE
}
@ -38,13 +40,13 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
viewBinding.errorText.visibility = View.VISIBLE
}
else {
viewModel.auth(email, password)
viewModel.auth(login, password)
viewBinding.errorText.visibility = View.GONE
}
}
viewModel.state.collectWithLifecycle(this) { state ->
if (state is AuthViewModel.State.Show) {
if (state is LoginViewModel.State.Show) {
viewBinding.errorText.text = state.errorText.toString()
viewBinding.errorText.visibility =
if (state.errorText == null) View.GONE else View.VISIBLE
@ -52,7 +54,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
}
viewModel.navigateToMain.collectWithLifecycle(viewLifecycleOwner) { userRole ->
val intent = Intent(requireContext(), MainActivity::class.java).apply {
val intent = Intent(requireContext(), RootActivity::class.java).apply {
putExtra("USER_ROLE", userRole)
}
startActivity(intent)
@ -66,10 +68,10 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
}
})*/
})
}
private fun isValidEmail(email: String): Boolean {
return Patterns.EMAIL_ADDRESS.matcher(email).matches()
private fun isValidLogin(email: String): Boolean {
return true //Patterns.EMAIL_ADDRESS.matcher(email).matches()
}
private fun isValidPassword(password : String) : Boolean {
return password.length >= 8

View File

@ -1,10 +1,11 @@
package ru.myitschool.work.ui.login
import LoginUseCase
import android.content.Context
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableSharedFlow
@ -13,16 +14,20 @@ import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import ru.myitschool.work.R
import ru.myitschool.work.data.auth.AuthNetworkDataSource
import ru.myitschool.work.data.auth.AuthRepoImpl
import ru.myitschool.work.data.auth.AuthStorageDataSource
import ru.myitschool.work.domain.auth.IsUserExistUseCase
import ru.myitschool.work.domain.auth.LoginUseCase
import javax.inject.Inject
import kotlin.reflect.KClass
@HiltViewModel
class LoginViewModel @Inject constructor(
class LoginViewModel constructor(
@ApplicationContext private val context: Context,
private val isUserExistUseCase: IsUserExistUseCase,
private val loginUseCase: LoginUseCase
) : ViewModel() {
private val _state = MutableStateFlow(true)
private val _state = MutableStateFlow<State>(getStateShow())
val state = _state.asStateFlow()
private val _navigateToMain = MutableSharedFlow<String?>()
@ -31,7 +36,7 @@ class LoginViewModel @Inject constructor(
private val _userRole = MutableSharedFlow<String>()
val userRole = _userRole.asSharedFlow()
/*
init {
viewModelScope.launch {
updateState()
@ -39,15 +44,14 @@ class LoginViewModel @Inject constructor(
}
fun auth(
email : String,
login : String,
password : String
)
{
viewModelScope.launch {
_state.emit(State.Loading)
when (checkUserExistence(email)) {
when (checkUserExistence(login)) {
true -> {
loginUser(email, password)
loginUser(login, password)
}
false -> {
updateState(context.getString(R.string.error_invalid_credentials))
@ -63,12 +67,12 @@ class LoginViewModel @Inject constructor(
result.fold(
onSuccess = {isExist -> isExist},
onFailure = {
Log.e("AuthViewModel", "Error checking user existence", it)
Log.e("LoginViewModel", "Error checking user existence", it)
null
}
)
} catch (e: Exception) {
Log.e("AuthViewModel", "Error during user existence check", e)
Log.e("LoginViewModel", "Error during user existence check", e)
null
}
}
@ -105,28 +109,27 @@ class LoginViewModel @Inject constructor(
}
sealed interface State {
data object Loading : State
data class Show(
var errorText : String?
) : State
}
*/
/*companion object {
companion object {
val Factory : ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: KClass<T>, extras: CreationExtras): T {
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
val application = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]!!
val authRepoImpl = AuthRepoImpl(
authNetworkDataSource = AuthNetworkDataSource,
authStorageDataSource = AuthStorageDataSource
)
return AuthViewModel(
application = application,
return LoginViewModel(
application,
isUserExistUseCase = IsUserExistUseCase(authRepoImpl),
loginUseCase = LoginUseCase(authRepoImpl)
) as T
}
}
}*/
}
}

View File

@ -1,8 +1,11 @@
package ru.myitschool.work.utils
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
@ -15,4 +18,14 @@ inline fun <T> Flow<T>.collectWhenStarted(
collector.invoke(value)
}
}
}
fun <T> Flow<T>.collectWithLifecycle(
lifecycleOwner: LifecycleOwner,
function: (T) -> Unit
){
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
collect { function.invoke(it) }
}
}
}

View File

@ -1,5 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="41dp"
android:height="41dp"
android:viewportWidth="41"
android:viewportHeight="41">
<path
android:pathData="M19.219,17.938H5.766C5.596,17.938 5.433,17.87 5.313,17.75C5.192,17.63 5.125,17.467 5.125,17.297C5.125,17.127 5.192,16.964 5.313,16.844C5.433,16.724 5.596,16.656 5.766,16.656H19.219C19.389,16.656 19.552,16.724 19.672,16.844C19.792,16.964 19.859,17.127 19.859,17.297C19.859,17.467 19.792,17.63 19.672,17.75C19.552,17.87 19.389,17.938 19.219,17.938Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M23.063,36.516C22.943,36.516 22.825,36.483 22.723,36.419C22.631,36.362 22.555,36.282 22.502,36.186C22.449,36.091 22.422,35.984 22.422,35.875V11.531C22.422,11.41 22.457,11.292 22.521,11.19C22.586,11.088 22.678,11.007 22.787,10.955L33.735,5.766H28.828C28.658,5.766 28.495,5.698 28.375,5.578C28.255,5.458 28.188,5.295 28.188,5.125C28.188,4.955 28.255,4.792 28.375,4.672C28.495,4.552 28.658,4.484 28.828,4.484H34.594C35.104,4.484 35.592,4.687 35.953,5.047C36.313,5.408 36.516,5.897 36.516,6.406V28.674C36.516,29.032 36.417,29.383 36.229,29.687C36.041,29.992 35.772,30.238 35.452,30.398L23.351,36.445C23.261,36.491 23.163,36.515 23.063,36.516ZM23.703,11.935V34.837L34.882,29.251C34.989,29.197 35.078,29.115 35.14,29.013C35.203,28.911 35.235,28.794 35.234,28.674V6.477L23.703,11.935Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M10.891,23.703C10.806,23.704 10.723,23.687 10.645,23.656C10.567,23.624 10.496,23.577 10.436,23.517L4.67,17.752C4.61,17.692 4.562,17.621 4.53,17.543C4.497,17.465 4.481,17.381 4.481,17.297C4.481,17.212 4.497,17.128 4.53,17.05C4.562,16.972 4.61,16.902 4.67,16.842L10.436,11.076C10.556,10.956 10.72,10.888 10.891,10.888C11.061,10.888 11.225,10.956 11.345,11.076C11.466,11.197 11.534,11.361 11.534,11.531C11.534,11.702 11.466,11.865 11.345,11.986L6.028,17.297L11.345,22.608C11.406,22.667 11.453,22.738 11.486,22.816C11.518,22.894 11.535,22.978 11.535,23.062C11.535,23.147 11.518,23.231 11.486,23.309C11.453,23.387 11.406,23.458 11.345,23.517C11.286,23.577 11.215,23.624 11.137,23.656C11.059,23.687 10.975,23.704 10.891,23.703Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M23.063,30.109H17.297C16.787,30.109 16.298,29.907 15.938,29.546C15.578,29.186 15.375,28.697 15.375,28.188V23.063C15.375,22.893 15.443,22.73 15.563,22.61C15.683,22.489 15.846,22.422 16.016,22.422C16.185,22.422 16.348,22.489 16.469,22.61C16.589,22.73 16.656,22.893 16.656,23.063V28.188C16.656,28.357 16.724,28.52 16.844,28.64C16.964,28.761 17.127,28.828 17.297,28.828H23.063C23.232,28.828 23.395,28.896 23.515,29.016C23.636,29.136 23.703,29.299 23.703,29.469C23.703,29.639 23.636,29.802 23.515,29.922C23.395,30.042 23.232,30.109 23.063,30.109Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M16.016,12.172C15.846,12.172 15.683,12.104 15.563,11.984C15.443,11.864 15.375,11.701 15.375,11.531V6.406C15.375,5.897 15.578,5.408 15.938,5.047C16.298,4.687 16.787,4.484 17.297,4.484H31.391C31.56,4.484 31.723,4.552 31.844,4.672C31.964,4.792 32.031,4.955 32.031,5.125C32.031,5.295 31.964,5.458 31.844,5.578C31.723,5.698 31.56,5.766 31.391,5.766H17.297C17.127,5.766 16.964,5.833 16.844,5.953C16.724,6.073 16.656,6.236 16.656,6.406V11.531C16.656,11.701 16.589,11.864 16.469,11.984C16.348,12.104 16.185,12.172 16.016,12.172Z"
android:fillColor="#FF6900"/>
</vector>

View File

@ -1,5 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M25.905,14.702C26.347,14.702 26.708,15.061 26.678,15.502C26.506,18.069 25.504,20.524 23.811,22.516C21.943,24.714 19.349,26.212 16.475,26.764C13.601,27.316 10.617,26.889 8.029,25.554C5.44,24.218 3.404,22.052 2.274,19.42C1.144,16.786 0.994,13.853 1.852,11.123C2.71,8.394 4.518,6.045 6.961,4.469C9.402,2.894 12.33,2.187 15.245,2.464C17.447,2.674 19.543,3.435 21.334,4.66L20.639,2.158C20.507,1.679 20.789,1.19 21.27,1.066L21.411,1.029C21.892,0.905 22.39,1.192 22.523,1.671L23.935,6.756C23.963,6.86 23.973,6.964 23.965,7.064C23.951,7.444 23.692,7.784 23.302,7.885L17.98,9.262C17.499,9.386 17.001,9.099 16.868,8.62L16.845,8.536C16.712,8.057 16.994,7.568 17.476,7.443L20.64,6.625C19.042,5.409 17.106,4.65 15.056,4.455C12.583,4.22 10.105,4.821 8.045,6.15C5.985,7.478 4.474,9.45 3.76,11.723C3.046,13.995 3.17,16.435 4.112,18.631C5.054,20.827 6.76,22.648 8.946,23.776C11.133,24.905 13.661,25.268 16.098,24.8C18.534,24.332 20.72,23.064 22.287,21.221C23.677,19.586 24.504,17.588 24.673,15.501C24.708,15.061 25.063,14.702 25.505,14.702H25.905Z"
android:fillColor="@color/orange"
android:fillType="evenOdd"/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="41dp"
android:height="41dp"
android:viewportWidth="41"
android:viewportHeight="41">
<path
android:pathData="M37.393,23.063C36.156,31.282 29.064,37.583 20.5,37.583C11.065,37.583 3.417,29.935 3.417,20.5C3.417,11.065 11.065,3.417 20.5,3.417C27.505,3.417 33.526,7.633 36.162,13.667"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#FF6900"
android:strokeLineCap="round"/>
<path
android:pathData="M29.042,13.667H36.558C37.124,13.667 37.583,13.208 37.583,12.642V5.125"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#FF6900"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/logout"
android:elevation="20dp"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="18dp"
android:src="@drawable/ic_logout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:clickable="true"
android:focusable="true">
</ImageView>
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/error"
android:layout_width="160dp"
android:layout_height="160dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/errorText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:gravity="center"
tools:text="Ошибка помогите" />
<Button
android:id="@+id/refresh"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="center"
android:background="@drawable/ic_refresh"
android:gravity="center" />
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/profile"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="60dp"
android:src="@drawable/ic_profile"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/refresh_for_profile"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginTop="88dp"
android:layout_marginEnd="18dp"
android:clickable="true"
android:elevation="20dp"
android:focusable="true"
android:src="@drawable/ic_refresh_profile"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:textColor="@color/black"
android:textSize="18dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:text="Иванова Вера Павловна" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="20dp"
android:textColor="@color/black"
android:textSize="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name"
tools:text="Должность: Разработчик" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:textColor="@color/black"
android:textSize="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:text="Время последнего входа: 12:00 16.09" />
<com.google.android.material.button.MaterialButton
android:layout_width="80dp"
android:layout_height="90dp"
android:layout_marginTop="148dp"
android:backgroundTint="@color/orange"
app:icon="@drawable/ic_qr_code"
app:iconSize="50dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>