From 5840424b916f80c2359f28c286b3921414555d33 Mon Sep 17 00:00:00 2001 From: varksyu Date: Wed, 19 Feb 2025 11:30:24 +0300 Subject: [PATCH] fix authorization, add profile layout --- .../myitschool/work/data/auth/AuthRepoImpl.kt | 4 +- .../ru/myitschool/work/data/user/UserDto.kt | 2 - .../myitschool/work/domain/auth/AuthRepo.kt | 8 + .../work/domain/auth/IsUserExistUseCase.kt | 12 ++ .../work/domain/auth/LoginUseCase.kt | 11 ++ .../ru/myitschool/work/domain/auth/Network.kt | 18 +++ .../myitschool/work/ui/login/LoginFragment.kt | 20 +-- .../work/ui/login/LoginViewModel.kt | 39 ++--- .../work/utils/FragmentExtesions.kt | 13 ++ app/src/main/res/drawable/ic_logout.xml | 24 ++- app/src/main/res/drawable/ic_refresh.xml | 13 +- .../main/res/drawable/ic_refresh_profile.xml | 20 +++ app/src/main/res/layout/fragment_profile.xml | 146 ++++++++++++++++++ 13 files changed, 291 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/AuthRepo.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/IsUserExistUseCase.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/LoginUseCase.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/Network.kt create mode 100644 app/src/main/res/drawable/ic_refresh_profile.xml create mode 100644 app/src/main/res/layout/fragment_profile.xml diff --git a/app/src/main/java/ru/myitschool/work/data/auth/AuthRepoImpl.kt b/app/src/main/java/ru/myitschool/work/data/auth/AuthRepoImpl.kt index b5b6952..f0b3c20 100644 --- a/app/src/main/java/ru/myitschool/work/data/auth/AuthRepoImpl.kt +++ b/app/src/main/java/ru/myitschool/work/data/auth/AuthRepoImpl.kt @@ -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 { return authNetworkDataSource.isUserExist(email) } diff --git a/app/src/main/java/ru/myitschool/work/data/user/UserDto.kt b/app/src/main/java/ru/myitschool/work/data/user/UserDto.kt index a591b96..03c4e94 100644 --- a/app/src/main/java/ru/myitschool/work/data/user/UserDto.kt +++ b/app/src/main/java/ru/myitschool/work/data/user/UserDto.kt @@ -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") diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/AuthRepo.kt b/app/src/main/java/ru/myitschool/work/domain/auth/AuthRepo.kt new file mode 100644 index 0000000..07dbf8b --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/AuthRepo.kt @@ -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 + suspend fun login(email: String, password: String) : Result +} diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/IsUserExistUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/auth/IsUserExistUseCase.kt new file mode 100644 index 0000000..7131138 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/IsUserExistUseCase.kt @@ -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 { + return authRepo.isUserExist(email) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/LoginUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/auth/LoginUseCase.kt new file mode 100644 index 0000000..aab6db4 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/LoginUseCase.kt @@ -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 { + return authRepo.login(email, password) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/Network.kt b/app/src/main/java/ru/myitschool/work/domain/auth/Network.kt new file mode 100644 index 0000000..9b814c7 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/Network.kt @@ -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 + }) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt index 725834c..b3ffdb6 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt @@ -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 diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt index c7edf14..66bb100 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt @@ -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(getStateShow()) val state = _state.asStateFlow() private val _navigateToMain = MutableSharedFlow() @@ -31,7 +36,7 @@ class LoginViewModel @Inject constructor( private val _userRole = MutableSharedFlow() 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 create(modelClass: KClass, extras: CreationExtras): T { + override fun create(modelClass: Class, 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 } } - }*/ + } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt b/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt index 8c99ef3..3ea3ccd 100644 --- a/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt +++ b/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt @@ -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 Flow.collectWhenStarted( collector.invoke(value) } } +} +fun Flow.collectWithLifecycle( + lifecycleOwner: LifecycleOwner, + function: (T) -> Unit +){ + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + collect { function.invoke(it) } + } + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_logout.xml b/app/src/main/res/drawable/ic_logout.xml index c22a96f..2f0c8a3 100644 --- a/app/src/main/res/drawable/ic_logout.xml +++ b/app/src/main/res/drawable/ic_logout.xml @@ -1,5 +1,21 @@ - - - - + + + + + + diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml index 86504d0..7d628a2 100644 --- a/app/src/main/res/drawable/ic_refresh.xml +++ b/app/src/main/res/drawable/ic_refresh.xml @@ -1,5 +1,10 @@ - - - - + + diff --git a/app/src/main/res/drawable/ic_refresh_profile.xml b/app/src/main/res/drawable/ic_refresh_profile.xml new file mode 100644 index 0000000..52858c9 --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh_profile.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml new file mode 100644 index 0000000..630d275 --- /dev/null +++ b/app/src/main/res/layout/fragment_profile.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + +