Skip to content

Commit

Permalink
Merge pull request #187 from Devendra34/feature/update_profile_compose
Browse files Browse the repository at this point in the history
Migrate update profile feature from XML to Jetpack Compose
  • Loading branch information
hieuwu committed Oct 4, 2023
2 parents 80127c3 + 9bf9d5d commit c8e8fd9
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,89 +4,49 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.hieuwu.groceriesstore.R
import com.hieuwu.groceriesstore.databinding.FragmentUpdateProfileBinding
import com.hieuwu.groceriesstore.utilities.showMessageSnackBar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

@AndroidEntryPoint
class UpdateProfileFragment : Fragment() {

lateinit var binding: FragmentUpdateProfileBinding
private val viewModel: UpdateProfileViewModel by viewModels()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate<FragmentUpdateProfileBinding>(
inflater,
R.layout.fragment_update_profile,
container,
false
)
binding.lifecycleOwner = this

binding.viewModel = viewModel
): View {
return ComposeView(requireContext()).apply {
setContent {
UpdateProfileScreen(onBackClick = ::navigateUp)
}
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setObserver()
setEventListener()

return binding.root
}

private fun setEventListener() {
binding.toolbar.setNavigationOnClickListener {
findNavController().navigateUp()
}

binding.toolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_save -> {
// Update user data to backend
viewModel.updateUserProfile()
true
}
else -> false
}
}
private fun navigateUp() {
findNavController().navigateUp()
}

private fun setObserver() {
viewModel.isDoneUpdate.observe(viewLifecycleOwner) {
if (it != null) {
if (it == true) {
R.string.add_to_basket
showMessageSnackBar(getString(R.string.update_profile_successfully))
} else {
showMessageSnackBar(getString(R.string.update_profile_failed))
}
}
}

viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.user.collect{
if (it != null) {
viewModel.email = it.email
viewModel.name = it.name
viewModel.phoneNumber = it.phone
viewModel.address = it.address
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.hieuwu.groceriesstore.presentation.updateprofile

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import com.hieuwu.groceriesstore.domain.models.UserModel
import com.hieuwu.groceriesstore.presentation.account.DemoUser
import com.hieuwu.groceriesstore.presentation.updateprofile.widgets.UpdateProfileAppBar
import com.hieuwu.groceriesstore.presentation.updateprofile.widgets.UpdateProfileScreenContent

@Composable
fun UpdateProfileScreen(
modifier: Modifier = Modifier,
onBackClick: () -> Unit = {},
viewModel: UpdateProfileViewModel = hiltViewModel(),
) {
val user = viewModel.user.collectAsState()

UpdateProfileScreenView(
modifier = modifier,
user = user.value,
onBackClick = onBackClick,
onSaveClick = { viewModel.updateUserProfile() },
onNameChanged = { viewModel.name = it },
onPhoneChanged = { viewModel.phoneNumber = it },
onEmailChanged = { viewModel.email = it },
onAddressChanged = { viewModel.address = it },
)
}

@Composable
private fun UpdateProfileScreenView(
modifier: Modifier = Modifier,
user: UserModel?,
onBackClick: () -> Unit = {},
onSaveClick: () -> Unit = {},
onNameChanged: (String) -> Unit = {},
onPhoneChanged: (String) -> Unit = {},
onEmailChanged: (String) -> Unit = {},
onAddressChanged: (String) -> Unit = {},
) {

Scaffold(
modifier = modifier,
topBar = {
UpdateProfileAppBar(
onBackClick = onBackClick,
onSaveClick = onSaveClick,
)
}
) { contentPadding ->
UpdateProfileScreenContent(
modifier = Modifier.padding(contentPadding),
user = user,
onNameChanged = onNameChanged,
onPhoneChanged = onPhoneChanged,
onEmailChanged = onEmailChanged,
onAddressChanged = onAddressChanged,
)
}
}

@Preview(showSystemUi = true)
@Composable
private fun UpdateProfileScreenPreview() {
UpdateProfileScreenView(
user = DemoUser,
)
}
Original file line number Diff line number Diff line change
@@ -1,74 +1,34 @@
package com.hieuwu.groceriesstore.presentation.updateprofile

import androidx.databinding.Bindable
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.hieuwu.groceriesstore.BR
import com.hieuwu.groceriesstore.domain.models.UserModel
import com.hieuwu.groceriesstore.domain.usecases.GetProfileUseCase
import com.hieuwu.groceriesstore.domain.usecases.UpdateProfileUseCase
import com.hieuwu.groceriesstore.presentation.utils.ObservableViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import java.lang.Exception
import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class UpdateProfileViewModel @Inject constructor(
private val updateProfileUseCase: UpdateProfileUseCase,
private val getProfileUseCase: GetProfileUseCase,

) :
) :
ObservableViewModel() {
private val _user: MutableStateFlow<UserModel?> = MutableStateFlow(null)
val user: MutableStateFlow<UserModel?>
get() = _user

private var _name: String? = null
var name: String?
@Bindable
get() {
return _name
}
set(value) {
_name = value
notifyPropertyChanged(BR.name)
}
var name: String? = null

private var _email: String? = null
var email: String?
@Bindable
get() {
return _email
}
set(value) {
_email = value
notifyPropertyChanged(BR.email)
}
var email: String? = null

private var _phoneNumber: String? = null
var phoneNumber: String?
@Bindable
get() {
return _phoneNumber
}
set(value) {
_phoneNumber = value
notifyPropertyChanged(BR.phoneNumber)
}
var phoneNumber: String? = null

private var _address: String? = null
var address: String?
@Bindable
get() {
return _address
}
set(value) {
_address = value
notifyPropertyChanged(BR.address)
}
var address: String? = null

private val _isDoneUpdate = MutableLiveData<Boolean>(null)
val isDoneUpdate: LiveData<Boolean?>
Expand All @@ -80,23 +40,31 @@ class UpdateProfileViewModel @Inject constructor(

private fun getCurrentUser() {
viewModelScope.launch {
getProfileUseCase.execute(GetProfileUseCase.Input()).result.collect{
_user.value = it
getProfileUseCase.execute(GetProfileUseCase.Input()).result.collect {
setUserProperties(it)
_user.value = it
}
}
}

private fun setUserProperties(user: UserModel?) {
name = user?.name
phoneNumber = user?.phone
email = user?.email
address = user?.address
}

fun updateUserProfile() {
val id = _user.value!!.id
try {
viewModelScope.launch {
updateProfileUseCase.execute(
UpdateProfileUseCase.Input(
userId = id,
name = _name!!,
email = _email!!,
phone = _phoneNumber!!,
address = _address!!
name = name!!,
email = email!!,
phone = phoneNumber!!,
address = address!!
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.hieuwu.groceriesstore.presentation.updateprofile.widgets

import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.hieuwu.groceriesstore.R

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UpdateProfileAppBar(
modifier: Modifier = Modifier,
onBackClick: () -> Unit,
onSaveClick: () -> Unit
) {
CenterAlignedTopAppBar(
modifier = modifier,
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(
painter = painterResource(id = R.drawable.ic_detail_back),
tint = Color.Unspecified,
contentDescription = null,
)
}
},
title = {
Text(
text = stringResource(R.string.update_profile),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = Color.White,
style = MaterialTheme.typography.titleLarge
)
},
actions = {
TextButton(onClick = onSaveClick) {
Text(
text = stringResource(R.string.save).uppercase(),
style = MaterialTheme.typography.titleMedium,
color = Color.White,
)
}
},
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = colorResource(id = R.color.colorPrimary)
),
)
}

@Preview
@Composable
private fun UpdateProfileAppBarPreview() {
UpdateProfileAppBar(
onBackClick = {},
onSaveClick = {},
)
}
Loading

0 comments on commit c8e8fd9

Please sign in to comment.