From 417e9ea322a94afeedc4cf18a27981e36279989e Mon Sep 17 00:00:00 2001 From: Erik Everardo Date: Sun, 12 Nov 2023 23:13:38 -0600 Subject: [PATCH] WIP 2 --- .../main/java/com/isolaatti/MainActivity.kt | 5 +- .../com/isolaatti/login/LogInViewModel.kt | 8 -- .../main/java/com/isolaatti/sign_up/Module.kt | 25 ++++ .../com/isolaatti/sign_up/data/SignUpApi.kt | 34 +++++ .../sign_up/data/SignUpRepositoryImpl.kt | 70 ++++++++++ .../sign_up/data/dto/CodeValidationDto.kt | 5 + .../com/isolaatti/sign_up/data/dto/DataDto.kt | 5 + .../isolaatti/sign_up/data/dto/ResultDto.kt | 5 + .../sign_up/data/dto/SignUpWithCodeDto.kt | 8 ++ .../sign_up/domain/SignUpRepository.kt | 12 ++ .../sign_up/domain/entity/GetCodeResult.kt | 5 + .../sign_up/domain/entity/SignUpResult.kt | 5 + .../sign_up/presentation/GetCodeViewModel.kt | 43 +++++++ .../sign_up/presentation/SignUpViewModel.kt | 11 ++ .../presentation/ValidateCodeViewModel.kt | 37 ++++++ .../isolaatti/sign_up/ui/GetCodeFragment.kt | 105 ++++++++++++++- .../sign_up/ui/MakeAccountFragment.kt | 16 +++ .../isolaatti/sign_up/ui/SignUpActivity.kt | 9 +- .../sign_up/ui/ValidateCodeFragment.kt | 120 ++++++++++++++++++ .../main/java/com/isolaatti/utils/Resource.kt | 2 +- .../java/com/isolaatti/utils/Validators.kt | 9 ++ app/src/main/res/layout/activity_login.xml | 3 +- app/src/main/res/layout/fragment_get_code.xml | 27 +++- .../main/res/layout/fragment_make_account.xml | 116 ++++++++++++++++- .../res/layout/fragment_validate_code.xml | 69 +++++++++- app/src/main/res/values/strings.xml | 19 +++ 26 files changed, 747 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/isolaatti/sign_up/Module.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/data/SignUpApi.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/data/SignUpRepositoryImpl.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/data/dto/CodeValidationDto.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/data/dto/DataDto.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/data/dto/ResultDto.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/data/dto/SignUpWithCodeDto.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/domain/SignUpRepository.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/domain/entity/GetCodeResult.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/domain/entity/SignUpResult.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/presentation/GetCodeViewModel.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/presentation/SignUpViewModel.kt create mode 100644 app/src/main/java/com/isolaatti/sign_up/presentation/ValidateCodeViewModel.kt create mode 100644 app/src/main/java/com/isolaatti/utils/Validators.kt diff --git a/app/src/main/java/com/isolaatti/MainActivity.kt b/app/src/main/java/com/isolaatti/MainActivity.kt index 0fff242..afcb8e0 100644 --- a/app/src/main/java/com/isolaatti/MainActivity.kt +++ b/app/src/main/java/com/isolaatti/MainActivity.kt @@ -35,8 +35,9 @@ class MainActivity : ComponentActivity() { val currentToken = authRepository.getCurrentToken() if(currentToken == null) { - - signInActivityResult.launch(Intent(this@MainActivity, LogInActivity::class.java)) + val loginIntent = Intent(this@MainActivity, LogInActivity::class.java) + loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + signInActivityResult.launch(loginIntent) } else { val homeActivityIntent = Intent(this@MainActivity, HomeActivity::class.java) homeActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) diff --git a/app/src/main/java/com/isolaatti/login/LogInViewModel.kt b/app/src/main/java/com/isolaatti/login/LogInViewModel.kt index bd9d862..9a3f158 100644 --- a/app/src/main/java/com/isolaatti/login/LogInViewModel.kt +++ b/app/src/main/java/com/isolaatti/login/LogInViewModel.kt @@ -58,12 +58,4 @@ class LogInViewModel @Inject constructor(private val authRepository: AuthReposit }.flowOn(Dispatchers.IO).launchIn(this) } } - - fun signUp() { - - } - - fun forgotPassword() { - - } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/Module.kt b/app/src/main/java/com/isolaatti/sign_up/Module.kt new file mode 100644 index 0000000..90dba67 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/Module.kt @@ -0,0 +1,25 @@ +package com.isolaatti.sign_up + +import com.isolaatti.connectivity.RetrofitClient +import com.isolaatti.sign_up.data.SignUpApi +import com.isolaatti.sign_up.data.SignUpRepositoryImpl +import com.isolaatti.sign_up.domain.SignUpRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +class Module { + + @Provides + fun provideSignUpApi(retrofitClient: RetrofitClient): SignUpApi { + return retrofitClient.client.create(SignUpApi::class.java) + } + + @Provides + fun provideSignUpRepository(signUpApi: SignUpApi): SignUpRepository { + return SignUpRepositoryImpl(signUpApi) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/data/SignUpApi.kt b/app/src/main/java/com/isolaatti/sign_up/data/SignUpApi.kt new file mode 100644 index 0000000..83847b9 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/data/SignUpApi.kt @@ -0,0 +1,34 @@ +package com.isolaatti.sign_up.data + +import com.isolaatti.sign_up.data.dto.CodeValidationDto +import com.isolaatti.sign_up.data.dto.DataDto +import com.isolaatti.sign_up.data.dto.ResultDto +import com.isolaatti.sign_up.data.dto.SignUpWithCodeDto +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.Header +import retrofit2.http.POST + +interface SignUpApi { + + @POST("signUp/get_code") + fun getCode( + @Header("clientId") apiClientId: String, + @Header("clientSecret") apiSecret: String, + @Body email: DataDto + ): Call + + @POST("signUp/validate_code") + fun validateCode( + @Header("clientId") apiClientId: String, + @Header("clientSecret") apiSecret: String, + @Body code: DataDto + ): Call + + @POST("signUp/sign_up_with_code") + fun signUpWithCode( + @Header("clientId") apiClientId: String, + @Header("clientSecret") apiSecret: String, + @Body dto: SignUpWithCodeDto + ): Call +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/data/SignUpRepositoryImpl.kt b/app/src/main/java/com/isolaatti/sign_up/data/SignUpRepositoryImpl.kt new file mode 100644 index 0000000..35f6a56 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/data/SignUpRepositoryImpl.kt @@ -0,0 +1,70 @@ +package com.isolaatti.sign_up.data + +import com.isolaatti.BuildConfig +import com.isolaatti.sign_up.data.dto.DataDto +import com.isolaatti.sign_up.data.dto.SignUpWithCodeDto +import com.isolaatti.sign_up.domain.SignUpRepository +import com.isolaatti.sign_up.domain.entity.GetCodeResult +import com.isolaatti.sign_up.domain.entity.SignUpResult +import com.isolaatti.utils.Resource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import retrofit2.awaitResponse +import javax.inject.Inject + +class SignUpRepositoryImpl @Inject constructor(private val signUpApi: SignUpApi): SignUpRepository { + override fun getCode(email: String): Flow> = flow { + emit(Resource.Loading()) + try { + val response = signUpApi.getCode(BuildConfig.clientId, BuildConfig.secret, DataDto(email)).awaitResponse() + if(response.isSuccessful){ + response.body()?.let { emit(Resource.Success(GetCodeResult.valueOf(it.result)))} + } else { + emit(Resource.Error(Resource.Error.mapErrorCode(response.code()))) + } + } catch (e: IllegalArgumentException) { + emit(Resource.Error(Resource.Error.ErrorType.OtherError, "Could not map response. $e")) + } catch(_: Exception) { + emit(Resource.Error(Resource.Error.ErrorType.NetworkError)) + } + } + + override fun validateCode(code: String): Flow> = flow { + emit(Resource.Loading()) + try { + val response = signUpApi.validateCode(BuildConfig.clientId, BuildConfig.secret, DataDto(code)).awaitResponse() + if(response.isSuccessful) { + response.body()?.let { emit(Resource.Success(it.valid)) } + } else { + emit(Resource.Error(Resource.Error.mapErrorCode(response.code()))) + } + } catch (_: Exception) { + emit(Resource.Error(Resource.Error.ErrorType.NetworkError)) + } + } + + override fun signUpWithCode( + username: String, + displayName: String, + password: String, + code: String + ): Flow> = flow { + emit(Resource.Loading()) + try { + val response = signUpApi.signUpWithCode( + BuildConfig.clientId, + BuildConfig.secret, + SignUpWithCodeDto(username, password, displayName, code) + ).awaitResponse() + if(response.isSuccessful){ + response.body()?.let { GetCodeResult.valueOf(it.result)} + } else { + emit(Resource.Error(Resource.Error.mapErrorCode(response.code()))) + } + } catch (e: IllegalArgumentException) { + emit(Resource.Error(Resource.Error.ErrorType.OtherError, "Could not map response. $e")) + } catch(_: Exception) { + emit(Resource.Error(Resource.Error.ErrorType.NetworkError)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/data/dto/CodeValidationDto.kt b/app/src/main/java/com/isolaatti/sign_up/data/dto/CodeValidationDto.kt new file mode 100644 index 0000000..bf5eff3 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/data/dto/CodeValidationDto.kt @@ -0,0 +1,5 @@ +package com.isolaatti.sign_up.data.dto + +data class CodeValidationDto( + val valid: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/data/dto/DataDto.kt b/app/src/main/java/com/isolaatti/sign_up/data/dto/DataDto.kt new file mode 100644 index 0000000..bde76b1 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/data/dto/DataDto.kt @@ -0,0 +1,5 @@ +package com.isolaatti.sign_up.data.dto + +data class DataDto( + val data: String +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/data/dto/ResultDto.kt b/app/src/main/java/com/isolaatti/sign_up/data/dto/ResultDto.kt new file mode 100644 index 0000000..2e601a4 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/data/dto/ResultDto.kt @@ -0,0 +1,5 @@ +package com.isolaatti.sign_up.data.dto + +data class ResultDto( + val result: String +) diff --git a/app/src/main/java/com/isolaatti/sign_up/data/dto/SignUpWithCodeDto.kt b/app/src/main/java/com/isolaatti/sign_up/data/dto/SignUpWithCodeDto.kt new file mode 100644 index 0000000..4dda578 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/data/dto/SignUpWithCodeDto.kt @@ -0,0 +1,8 @@ +package com.isolaatti.sign_up.data.dto + +data class SignUpWithCodeDto( + val username: String, + val password: String, + val displayName: String, + val code: String +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/domain/SignUpRepository.kt b/app/src/main/java/com/isolaatti/sign_up/domain/SignUpRepository.kt new file mode 100644 index 0000000..ce30cee --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/domain/SignUpRepository.kt @@ -0,0 +1,12 @@ +package com.isolaatti.sign_up.domain + +import com.isolaatti.sign_up.domain.entity.GetCodeResult +import com.isolaatti.sign_up.domain.entity.SignUpResult +import com.isolaatti.utils.Resource +import kotlinx.coroutines.flow.Flow + +interface SignUpRepository { + fun getCode(email: String): Flow> + fun validateCode(code: String): Flow> + fun signUpWithCode(username: String, displayName: String, password: String, code: String): Flow> +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/domain/entity/GetCodeResult.kt b/app/src/main/java/com/isolaatti/sign_up/domain/entity/GetCodeResult.kt new file mode 100644 index 0000000..d2f5242 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/domain/entity/GetCodeResult.kt @@ -0,0 +1,5 @@ +package com.isolaatti.sign_up.domain.entity + +enum class GetCodeResult { + EmailUsed, Success, EmailValidationError, CodesSentLimitReached +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/domain/entity/SignUpResult.kt b/app/src/main/java/com/isolaatti/sign_up/domain/entity/SignUpResult.kt new file mode 100644 index 0000000..a77d0e3 --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/domain/entity/SignUpResult.kt @@ -0,0 +1,5 @@ +package com.isolaatti.sign_up.domain.entity + +enum class SignUpResult { + EmailNotAvailable, ValidationProblems, Ok, Error, UsernameUnavailable +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/presentation/GetCodeViewModel.kt b/app/src/main/java/com/isolaatti/sign_up/presentation/GetCodeViewModel.kt new file mode 100644 index 0000000..22af8ae --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/presentation/GetCodeViewModel.kt @@ -0,0 +1,43 @@ +package com.isolaatti.sign_up.presentation + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.isolaatti.sign_up.domain.SignUpRepository +import com.isolaatti.sign_up.domain.entity.GetCodeResult +import com.isolaatti.utils.Resource +import com.isolaatti.utils.Validators +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class GetCodeViewModel @Inject constructor(private val signUpRepository: SignUpRepository) : ViewModel() { + + var email: String = "" + set(value) { + field = value + + emailIsValid.value = Validators.isEmailValid(value) + } + + + val emailIsValid: MutableLiveData = MutableLiveData(false) + val response: MutableLiveData?> = MutableLiveData() + + fun getCode() { + if(!Validators.isEmailValid(email)) { + return + } + + viewModelScope.launch { + signUpRepository.getCode(email).onEach { + response.postValue(it) + }.flowOn(Dispatchers.IO).launchIn(this) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/presentation/SignUpViewModel.kt b/app/src/main/java/com/isolaatti/sign_up/presentation/SignUpViewModel.kt new file mode 100644 index 0000000..e99690d --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/presentation/SignUpViewModel.kt @@ -0,0 +1,11 @@ +package com.isolaatti.sign_up.presentation + +import androidx.lifecycle.ViewModel +import com.isolaatti.sign_up.domain.SignUpRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + + +class SignUpViewModel : ViewModel(){ + var code: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/presentation/ValidateCodeViewModel.kt b/app/src/main/java/com/isolaatti/sign_up/presentation/ValidateCodeViewModel.kt new file mode 100644 index 0000000..b2d4a2a --- /dev/null +++ b/app/src/main/java/com/isolaatti/sign_up/presentation/ValidateCodeViewModel.kt @@ -0,0 +1,37 @@ +package com.isolaatti.sign_up.presentation + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.isolaatti.sign_up.domain.SignUpRepository +import com.isolaatti.utils.Resource +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class ValidateCodeViewModel @Inject constructor(private val signUpRepository: SignUpRepository) : ViewModel() { + var code: String? = null + set(value) { + field = value + codeIsValid.value = value?.isNotBlank() == true && value.contains(" ") == false + } + + val codeIsValid: MutableLiveData = MutableLiveData(false) + val result: MutableLiveData?> = MutableLiveData() + fun validateCode() { + if(code != null && code!!.isBlank() || code!!.contains(" ")) { + return + } + viewModelScope.launch { + signUpRepository.validateCode(code!!.trim()).onEach { + result.postValue(it) + }.flowOn(Dispatchers.IO).launchIn(this) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/ui/GetCodeFragment.kt b/app/src/main/java/com/isolaatti/sign_up/ui/GetCodeFragment.kt index c9e4597..4e33a6e 100644 --- a/app/src/main/java/com/isolaatti/sign_up/ui/GetCodeFragment.kt +++ b/app/src/main/java/com/isolaatti/sign_up/ui/GetCodeFragment.kt @@ -1,21 +1,124 @@ package com.isolaatti.sign_up.ui import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.isolaatti.R +import com.isolaatti.SignUpNavigationDirections +import com.isolaatti.common.ErrorMessageViewModel import com.isolaatti.databinding.FragmentGetCodeBinding +import com.isolaatti.sign_up.domain.entity.GetCodeResult +import com.isolaatti.sign_up.presentation.GetCodeViewModel +import com.isolaatti.sign_up.presentation.SignUpViewModel +import com.isolaatti.utils.Resource +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch +@AndroidEntryPoint class GetCodeFragment : Fragment() { private lateinit var binding: FragmentGetCodeBinding + private val errorViewModel: ErrorMessageViewModel by activityViewModels() + private val viewModel: GetCodeViewModel by viewModels() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentGetCodeBinding.inflate(inflater) return binding.root } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupListeners() + setupObservers() + } + + private fun retry() { + // TODO retry here + } + + private fun setupListeners() { + binding.goToCodeButton.setOnClickListener { + findNavController().navigate(SignUpNavigationDirections.actionGlobalValidateCodeFragment()) + } + binding.backButton.setOnClickListener { + requireActivity().finish() + } + binding.sendButton.setOnClickListener { + binding.sendButton.isEnabled = false + viewModel.getCode() + } + binding.textFieldEmail.editText?.doOnTextChanged { text, _, _, _ -> + viewModel.email = text.toString() + } + } + + private fun setupObservers() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + errorViewModel.retry.collect { + retry() + } + } + } + + viewModel.emailIsValid.observe(viewLifecycleOwner) { + binding.sendButton.isEnabled = it + } + viewModel.response.observe(viewLifecycleOwner) { + binding.sendButton.isEnabled = true + + when(it) { + is Resource.Error -> { + errorViewModel.error.postValue(it.errorType) + viewModel.response.value = null + } + is Resource.Loading -> { + viewModel.response.value = null + } + is Resource.Success -> { + viewModel.response.value = null + if(it.data == GetCodeResult.Success) { + findNavController().navigate(GetCodeFragmentDirections.actionGetCodeFragmentToValidateCodeFragment()) + return@observe + } + showResultDialog(it.data!!) + + } + + null -> {} + } + + + + } + } + + private fun showResultDialog(result: GetCodeResult) { + val message = when(result) { + GetCodeResult.EmailUsed -> R.string.email_used_when_getting_code + GetCodeResult.EmailValidationError -> R.string.invalid_email + GetCodeResult.CodesSentLimitReached -> R.string.codes_sent_limit_reached + else -> 0 + } + MaterialAlertDialogBuilder(requireContext()) + .setMessage(message) + .setPositiveButton(R.string.accept, null) + .show() + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/ui/MakeAccountFragment.kt b/app/src/main/java/com/isolaatti/sign_up/ui/MakeAccountFragment.kt index b0fe772..a8dca09 100644 --- a/app/src/main/java/com/isolaatti/sign_up/ui/MakeAccountFragment.kt +++ b/app/src/main/java/com/isolaatti/sign_up/ui/MakeAccountFragment.kt @@ -1,6 +1,22 @@ package com.isolaatti.sign_up.ui +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment +import com.isolaatti.databinding.FragmentMakeAccountBinding class MakeAccountFragment : Fragment() { + + private lateinit var binding: FragmentMakeAccountBinding + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentMakeAccountBinding.inflate(inflater) + + return binding.root + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/sign_up/ui/SignUpActivity.kt b/app/src/main/java/com/isolaatti/sign_up/ui/SignUpActivity.kt index a34a4f1..4511fed 100644 --- a/app/src/main/java/com/isolaatti/sign_up/ui/SignUpActivity.kt +++ b/app/src/main/java/com/isolaatti/sign_up/ui/SignUpActivity.kt @@ -3,12 +3,17 @@ package com.isolaatti.sign_up.ui import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.core.app.ActivityCompat +import com.isolaatti.common.IsolaattiBaseActivity import com.isolaatti.databinding.ActivitySignUpBinding +import com.isolaatti.sign_up.presentation.SignUpViewModel +import dagger.hilt.android.AndroidEntryPoint -class SignUpActivity : AppCompatActivity() { +@AndroidEntryPoint +class SignUpActivity : IsolaattiBaseActivity() { private lateinit var binding: ActivitySignUpBinding + private val viewModel: SignUpViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/isolaatti/sign_up/ui/ValidateCodeFragment.kt b/app/src/main/java/com/isolaatti/sign_up/ui/ValidateCodeFragment.kt index d2d8b33..48c2a27 100644 --- a/app/src/main/java/com/isolaatti/sign_up/ui/ValidateCodeFragment.kt +++ b/app/src/main/java/com/isolaatti/sign_up/ui/ValidateCodeFragment.kt @@ -1,6 +1,126 @@ package com.isolaatti.sign_up.ui +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.isolaatti.R +import com.isolaatti.common.ErrorMessageViewModel +import com.isolaatti.databinding.FragmentValidateCodeBinding +import com.isolaatti.sign_up.presentation.SignUpViewModel +import com.isolaatti.sign_up.presentation.ValidateCodeViewModel +import com.isolaatti.utils.Resource +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch +@AndroidEntryPoint class ValidateCodeFragment : Fragment() { + + private lateinit var binding: FragmentValidateCodeBinding + private val activityViewModel: SignUpViewModel by activityViewModels() + private val errorViewModel: ErrorMessageViewModel by activityViewModels() + private val viewModel: ValidateCodeViewModel by viewModels() + + private val loadingDialog: AlertDialog by lazy { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(R.string.loading) + .show() + } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentValidateCodeBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupListeners() + setupObservers() + } + + private fun retry() { + // TODO retry here + } + + private fun setupListeners() { + binding.backButton.setOnClickListener { + findNavController().popBackStack() + } + binding.textFieldCode.editText?.doOnTextChanged { text, _, _, _ -> + viewModel.code = text.toString() + } + binding.acceptButton.setOnClickListener { + viewModel.validateCode() + } + } + + private fun setupObservers() { + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + errorViewModel.retry.collect { + retry() + } + } + } + + viewModel.codeIsValid.observe(viewLifecycleOwner) { + binding.acceptButton.isEnabled = it + } + viewModel.result.observe(viewLifecycleOwner) { + when(it) { + is Resource.Error -> { + errorViewModel.error.postValue(it.errorType) + } + is Resource.Loading -> { + showLoading(true) + } + is Resource.Success -> { + showLoading(false) + if(it.data!!) { + viewModel.result.value = null + + activityViewModel.code = viewModel.code + findNavController().navigate(ValidateCodeFragmentDirections.actionValidateCodeFragmentToMakeAccountFragment()) + } else { + showError() + } + } + null -> {} + } + + } + } + + private fun showLoading(loading: Boolean) { + binding.acceptButton.isEnabled = !loading + binding.textFieldCode.isEnabled = !loading + if(loading) { + loadingDialog.show() + } else { + loadingDialog.dismiss() + } + } + + private fun showError() { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(R.string.invalid_code) + .setPositiveButton(R.string.accept) {_,_ -> + binding.acceptButton.isEnabled = true + } + .show() + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/utils/Resource.kt b/app/src/main/java/com/isolaatti/utils/Resource.kt index 87523fe..3bd2c10 100644 --- a/app/src/main/java/com/isolaatti/utils/Resource.kt +++ b/app/src/main/java/com/isolaatti/utils/Resource.kt @@ -3,7 +3,7 @@ package com.isolaatti.utils sealed class Resource { class Success(val data: T?): Resource() class Loading: Resource() - class Error(val errorType: ErrorType? = null): Resource() { + class Error(val errorType: ErrorType? = null, val message: String? = null): Resource() { enum class ErrorType { NetworkError, AuthError, NotFoundError, ServerError, OtherError } diff --git a/app/src/main/java/com/isolaatti/utils/Validators.kt b/app/src/main/java/com/isolaatti/utils/Validators.kt new file mode 100644 index 0000000..6cde4d3 --- /dev/null +++ b/app/src/main/java/com/isolaatti/utils/Validators.kt @@ -0,0 +1,9 @@ +package com.isolaatti.utils + +object Validators { + fun isEmailValid(email: String): Boolean { + val emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\$".toRegex() + + return email.matches(emailRegex) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 763b9c0..1680e78 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -25,10 +25,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="24dp" - android:fontFamily="@font/zen_dots_regular" android:text="@string/app_name" android:textAlignment="center" - android:textAppearance="?attr/textAppearanceHeadlineLarge" /> + style="@style/toolbar_text" /> - + android:orientation="horizontal" + android:gravity="center_vertical"> +