You've already forked RekomenciMobile
feat: added view model for profile screen
# Conflicts: # .idea/deploymentTargetSelector.xml # app/src/main/java/com/prodhack/moscow2025/presentation/screens/fillProfile/FillProfileScreen.kt
This commit is contained in:
+14
@@ -24,6 +24,20 @@ data class ValidationResult(
|
|||||||
|
|
||||||
@Single
|
@Single
|
||||||
class ValidateAuthFieldsUseCase {
|
class ValidateAuthFieldsUseCase {
|
||||||
|
fun validateProfile(
|
||||||
|
firstName: String,
|
||||||
|
lastName: String,
|
||||||
|
email: String,
|
||||||
|
phone: String
|
||||||
|
): ValidationResult {
|
||||||
|
val errors = buildMap {
|
||||||
|
if (firstName.isBlank()) put(AuthField.FirstName, "Введите имя")
|
||||||
|
if (lastName.isBlank()) put(AuthField.LastName, "Введите фамилию")
|
||||||
|
if (!isEmailValid(email)) put(AuthField.Email, "Некорректный email")
|
||||||
|
if (!isPhoneValid(phone)) put(AuthField.Phone, "Некорректный номер телефона")
|
||||||
|
}
|
||||||
|
return ValidationResult(errors)
|
||||||
|
}
|
||||||
|
|
||||||
fun validateFillProfile(
|
fun validateFillProfile(
|
||||||
chosenPattern: PhoneNumberPattern?,
|
chosenPattern: PhoneNumberPattern?,
|
||||||
|
|||||||
+6
-1
@@ -2,8 +2,13 @@ package com.prodhack.moscow2025.presentation.screens.profile
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.prodhack.moscow2025.presentation.utils.base.BaseViewModel
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ProfileScreen(modifier: Modifier = Modifier) {
|
fun ProfileScreen(
|
||||||
|
viewModel: ProfileScreenViewModel = koinViewModel()
|
||||||
|
) : BaseViewModel {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+204
@@ -0,0 +1,204 @@
|
|||||||
|
package com.prodhack.moscow2025.presentation.screens.profile
|
||||||
|
|
||||||
|
import android.content.ContentUris
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.paging.map
|
||||||
|
import coil.ImageLoader
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import com.prodhack.moscow2025.domain.interfaces.GalleryRepository
|
||||||
|
import com.prodhack.moscow2025.domain.models.UpdateUserData
|
||||||
|
import com.prodhack.moscow2025.domain.usecase.auth.AuthField
|
||||||
|
import com.prodhack.moscow2025.domain.usecase.auth.GetUserUseCase
|
||||||
|
import com.prodhack.moscow2025.domain.usecase.auth.UpdateUserUseCase
|
||||||
|
import com.prodhack.moscow2025.domain.usecase.auth.ValidateAuthFieldsUseCase
|
||||||
|
import com.prodhack.moscow2025.presentation.utils.UIState
|
||||||
|
import com.prodhack.moscow2025.presentation.utils.base.BaseViewModel
|
||||||
|
import com.prodhack.moscow2025.presentation.utils.toByteArray
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.android.annotation.KoinViewModel
|
||||||
|
|
||||||
|
data class ProfileState(
|
||||||
|
val email: String = "",
|
||||||
|
val firstName: String = "",
|
||||||
|
val lastName: String = "",
|
||||||
|
val phone: String = "",
|
||||||
|
val avatar: ByteArray? = null,
|
||||||
|
val errors: Map<AuthField, String> = emptyMap()
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as ProfileState
|
||||||
|
|
||||||
|
if (email != other.email) return false
|
||||||
|
if (firstName != other.firstName) return false
|
||||||
|
if (lastName != other.lastName) return false
|
||||||
|
if (phone != other.phone) return false
|
||||||
|
if (!avatar.contentEquals(other.avatar)) return false
|
||||||
|
if (errors != other.errors) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = email.hashCode()
|
||||||
|
result = 31 * result + firstName.hashCode()
|
||||||
|
result = 31 * result + lastName.hashCode()
|
||||||
|
result = 31 * result + phone.hashCode()
|
||||||
|
result = 31 * result + (avatar?.contentHashCode() ?: 0)
|
||||||
|
result = 31 * result + errors.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@KoinViewModel
|
||||||
|
class ProfileScreenViewModel(
|
||||||
|
private val getUserUseCase: GetUserUseCase,
|
||||||
|
private val updateUserUseCase: UpdateUserUseCase,
|
||||||
|
private val validateAuthFieldsUseCase: ValidateAuthFieldsUseCase,
|
||||||
|
private val galleryRepository: GalleryRepository
|
||||||
|
): BaseViewModel() {
|
||||||
|
private val _formStateProfile = MutableStateFlow(ProfileState())
|
||||||
|
val formStateFillProfile: StateFlow<ProfileState> = _formStateProfile
|
||||||
|
|
||||||
|
private val _profileState = MutableUIStateFlow<String>()
|
||||||
|
val profileState: StateFlow<UIState<String>> = _profileState
|
||||||
|
|
||||||
|
fun onEmailChange(value: String) {
|
||||||
|
_formStateProfile.update {
|
||||||
|
it.copy(
|
||||||
|
email = value,
|
||||||
|
errors = it.errors - AuthField.Email
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onFirstNameChange(value: String) {
|
||||||
|
_formStateProfile.update {
|
||||||
|
it.copy(
|
||||||
|
firstName = value,
|
||||||
|
errors = it.errors - AuthField.FirstName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onLastNameChange(value: String) {
|
||||||
|
_formStateProfile.update {
|
||||||
|
it.copy(
|
||||||
|
lastName = value,
|
||||||
|
errors = it.errors - AuthField.LastName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPhoneChange(value: String) {
|
||||||
|
_formStateProfile.update {
|
||||||
|
it.copy(
|
||||||
|
phone = value,
|
||||||
|
errors = it.errors - AuthField.Phone
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val galleryItems = galleryRepository.getImagesIds().map {
|
||||||
|
it.map { id ->
|
||||||
|
ContentUris.withAppendedId(
|
||||||
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun post(context: Context) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
post(
|
||||||
|
(ImageLoader(context).execute(
|
||||||
|
ImageRequest.Builder(context)
|
||||||
|
.data(currentPhoto).build()
|
||||||
|
).drawable as BitmapDrawable).bitmap
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun post(bitmap: Bitmap) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_formStateProfile.update {
|
||||||
|
it.copy(
|
||||||
|
avatar = bitmap.toByteArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearAvatar() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_formStateProfile.update {
|
||||||
|
it.copy(
|
||||||
|
avatar = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentPhoto: Uri? = null
|
||||||
|
|
||||||
|
fun selectImage(photo: Uri) {
|
||||||
|
currentPhoto = photo
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submit() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val validation = validateAuthFieldsUseCase.validateProfile(
|
||||||
|
firstName = _formStateProfile.value.firstName,
|
||||||
|
lastName = _formStateProfile.value.lastName,
|
||||||
|
email = _formStateProfile.value.email,
|
||||||
|
phone = _formStateProfile.value.phone
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!validation.isValid) {
|
||||||
|
_formStateProfile.update { it.copy(errors = validation.errors) }
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
_profileState.emit(UIState.Loading())
|
||||||
|
|
||||||
|
val result = updateUserUseCase(
|
||||||
|
UpdateUserData(
|
||||||
|
firstName = _formStateProfile.value.firstName,
|
||||||
|
lastName = _formStateProfile.value.lastName,
|
||||||
|
email = _formStateProfile.value.email,
|
||||||
|
phone = _formStateProfile.value.phone
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result.map { it.id }.collectRequest(_profileState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val user = getUserUseCase().getOrNull()
|
||||||
|
if (user != null) {
|
||||||
|
_formStateProfile.update {
|
||||||
|
it.copy(
|
||||||
|
firstName = user.firstName.orEmpty(),
|
||||||
|
lastName = user.lastName.orEmpty(),
|
||||||
|
email = user.email,
|
||||||
|
phone = user.phone.orEmpty()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user