From 75b93da0444135f9abccde29430b4a5234afcc60 Mon Sep 17 00:00:00 2001 From: erik-everardo Date: Sun, 7 Jan 2024 19:41:28 -0600 Subject: [PATCH] lista de seguidores y seguidos --- .../com/isolaatti/common/ListUpdateEvent.kt | 38 ++++++++++++- .../common/UserListRecyclerViewAdapter.kt | 39 +++++++------- .../presentation/FollowersViewModel.kt | 53 +++++++++++++------ .../followers/ui/FollowersFragment.kt | 13 +++-- .../followers/ui/FollowingFragment.kt | 12 ++++- .../profile/domain/entity/ProfileListItem.kt | 4 +- 6 files changed, 112 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/isolaatti/common/ListUpdateEvent.kt b/app/src/main/java/com/isolaatti/common/ListUpdateEvent.kt index 209fac4..25b343a 100644 --- a/app/src/main/java/com/isolaatti/common/ListUpdateEvent.kt +++ b/app/src/main/java/com/isolaatti/common/ListUpdateEvent.kt @@ -1,6 +1,42 @@ package com.isolaatti.common +import androidx.recyclerview.widget.RecyclerView + enum class ListUpdateEvent { ItemUpdated, - ItemsAdded + ItemsAdded, + Refresh +} + +/** + * @param listUpdateEvent indicate what type of update was made to the list + * @param items list of positions. For ItemUpdated these positions indicate the changed positions. + * For ItemsAdded indicate the range where the item was inserted, first element the beginning, second element the end. + * If list contains only one element, only that position will be inserted + */ +class UpdateEvent(private val listUpdateEvent: ListUpdateEvent, private val items: Array) { + fun notify(adapter: RecyclerView.Adapter<*>) { + when(listUpdateEvent) { + ListUpdateEvent.ItemUpdated -> { + items.forEach { position -> + adapter.notifyItemChanged(position) + } + } + ListUpdateEvent.ItemsAdded -> { + if(items.isEmpty()) { + return + } + if(items.count() == 1) { + adapter.notifyItemInserted(items[0]) + } else { + adapter.notifyItemRangeInserted(items[0], items[1]) + } + } + + ListUpdateEvent.Refresh -> { + adapter.notifyDataSetChanged() + } + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt b/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt index dba9af3..f265dda 100644 --- a/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt +++ b/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt @@ -5,6 +5,8 @@ import android.view.ViewGroup import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.Adapter import androidx.recyclerview.widget.RecyclerView.ViewHolder import coil.load import com.isolaatti.R @@ -13,18 +15,30 @@ import com.isolaatti.databinding.ItemUserListBinding import com.isolaatti.profile.domain.entity.ProfileListItem import com.isolaatti.utils.UrlGen -class UserListRecyclerViewAdapter(private val callback: UserItemCallback) : ListAdapter(diffCallback) { +class UserListRecyclerViewAdapter(private val callback: UserItemCallback) : Adapter() { + + private var data: List = listOf() inner class UserListViewHolder(val item: ItemUserListBinding) : ViewHolder(item.root) + fun updateData(newData: List, updateEvent: UpdateEvent) { + data = newData + updateEvent.notify(this) + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListViewHolder { return UserListViewHolder(ItemUserListBinding.inflate(LayoutInflater.from(parent.context))) } + override fun getItemCount(): Int { + return data.size + } + override fun onBindViewHolder(holder: UserListViewHolder, position: Int) { val context = holder.itemView.context holder.itemView.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT) - getItem(position).let { user -> + holder.item.followButton.isEnabled = true + data[position].let { user -> holder.item.root.setOnClickListener { callback.itemClick(user.id) } @@ -34,34 +48,17 @@ class UserListRecyclerViewAdapter(private val callback: UserItemCallback) : List holder.item.followButton.text = context.getText(R.string.unfollow) holder.item.followButton.setTextColor(ResourcesCompat.getColor(context.resources, R.color.danger, null)) holder.item.followButton.setOnClickListener { + it.isEnabled = false callback.followButtonClick(user, UserItemCallback.FollowButtonAction.Unfollow) } } else { holder.item.followButton.text = context.getText(R.string.follow) holder.item.followButton.setTextColor(ResourcesCompat.getColor(context.resources, R.color.purple_lighter, null)) holder.item.followButton.setOnClickListener { + it.isEnabled = false callback.followButtonClick(user, UserItemCallback.FollowButtonAction.Follow) } } } } - - companion object { - val diffCallback: DiffUtil.ItemCallback = object: DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: ProfileListItem, - newItem: ProfileListItem - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: ProfileListItem, - newItem: ProfileListItem - ): Boolean { - return oldItem == newItem - } - } - } - } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/followers/presentation/FollowersViewModel.kt b/app/src/main/java/com/isolaatti/followers/presentation/FollowersViewModel.kt index f3bf535..4f3eff0 100644 --- a/app/src/main/java/com/isolaatti/followers/presentation/FollowersViewModel.kt +++ b/app/src/main/java/com/isolaatti/followers/presentation/FollowersViewModel.kt @@ -4,6 +4,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.isolaatti.common.ListUpdateEvent +import com.isolaatti.common.UpdateEvent import com.isolaatti.followers.domain.FollowersRepository import com.isolaatti.profile.domain.entity.ProfileListItem import com.isolaatti.utils.Resource @@ -19,15 +21,15 @@ import javax.inject.Inject class FollowersViewModel @Inject constructor(private val followersRepository: FollowersRepository) : ViewModel() { var userId: Int = 0 - private val followersList: MutableList = mutableListOf() - private val followingsList: MutableList = mutableListOf() + private var followersList: List = listOf() + private var followingsList: List = listOf() - private val _followers: MutableLiveData> = MutableLiveData() - private val _followings: MutableLiveData> = MutableLiveData() + private val _followers: MutableLiveData, UpdateEvent>> = MutableLiveData() + private val _followings: MutableLiveData, UpdateEvent>> = MutableLiveData() - val followers: LiveData> get() = _followers - val followings: LiveData> get() = _followings + val followers: LiveData, UpdateEvent>> get() = _followers + val followings: LiveData, UpdateEvent>> get() = _followings private val toRetry: MutableList = mutableListOf() @@ -59,20 +61,26 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo return followingsList.last().id } - fun fetchFollowers() { + fun fetchFollowers(refresh: Boolean = false) { if(userId <= 0) { return } + if(refresh) { + followersList = mutableListOf() + } + + val updateListEvent = if(refresh) ListUpdateEvent.Refresh else ListUpdateEvent.ItemsAdded + viewModelScope.launch { followersRepository.getFollowersOfUser(userId, getFollowersLastId()).onEach { when(it) { is Resource.Success -> { if(it.data != null) { - followersList.addAll(it.data) + val prevCount = followersList.count() + followersList += it.data + _followers.postValue(Pair(followersList, UpdateEvent(updateListEvent, arrayOf(prevCount)))) } - - _followers.postValue(followersList) } is Resource.Error -> { toRetry.add { @@ -85,11 +93,17 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo } } - fun fetchFollowings() { + fun fetchFollowings(refresh: Boolean = false) { if(userId <= 0) { return } + if(refresh) { + followingsList = mutableListOf() + } + + val updateListEvent = if(refresh) ListUpdateEvent.Refresh else ListUpdateEvent.ItemsAdded + viewModelScope.launch { followersRepository.getFollowingsOfUser(userId, getFollowingsLastId()).onEach { when(it) { @@ -101,8 +115,9 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo is Resource.Loading -> {} is Resource.Success -> { if(it.data != null) { - followingsList.addAll(it.data) - _followings.postValue(followingsList) + val prevCount = followersList.count() + followingsList += it.data + _followings.postValue(Pair(followingsList, UpdateEvent(updateListEvent, arrayOf(prevCount)))) } } } @@ -115,15 +130,19 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo val followingsIndex = followingsList.indexOf(user) if(followersIndex >= 0) { - followersList[followersIndex] = user + followersList = followersList.toMutableList().apply { + set(followersIndex, user) + } - _followers.postValue(followersList) + _followers.postValue(Pair(followersList, UpdateEvent(ListUpdateEvent.ItemUpdated, arrayOf(followersIndex)))) } if(followingsIndex >= 0) { - followingsList[followingsIndex] = user + followingsList = followingsList.toMutableList().apply { + set(followingsIndex, user) + } - _followings.postValue(followingsList) + _followings.postValue(Pair(followingsList, UpdateEvent(ListUpdateEvent.ItemUpdated, arrayOf(followingsIndex)))) } } diff --git a/app/src/main/java/com/isolaatti/followers/ui/FollowersFragment.kt b/app/src/main/java/com/isolaatti/followers/ui/FollowersFragment.kt index 3285b7f..6d05ec8 100644 --- a/app/src/main/java/com/isolaatti/followers/ui/FollowersFragment.kt +++ b/app/src/main/java/com/isolaatti/followers/ui/FollowersFragment.kt @@ -1,6 +1,7 @@ package com.isolaatti.followers.ui import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -12,6 +13,7 @@ import com.isolaatti.common.UserListRecyclerViewAdapter import com.isolaatti.databinding.FragmentFollowersBinding import com.isolaatti.followers.presentation.FollowersViewModel import com.isolaatti.profile.domain.entity.ProfileListItem +import com.isolaatti.profile.ui.ProfileActivity class FollowersFragment : Fragment(), UserItemCallback { private lateinit var binding: FragmentFollowersBinding @@ -28,8 +30,9 @@ class FollowersFragment : Fragment(), UserItemCallback { } private fun setObservers() { - viewModel.followers.observe(viewLifecycleOwner) { - adapter.submitList(it) + viewModel.followers.observe(viewLifecycleOwner) { (list, updateEvent) -> + adapter.updateData(list, updateEvent) + binding.swipeToRefresh.isRefreshing = false } } @@ -37,6 +40,10 @@ class FollowersFragment : Fragment(), UserItemCallback { adapter = UserListRecyclerViewAdapter(this) binding.recyclerUsers.adapter = adapter binding.recyclerUsers.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + + binding.swipeToRefresh.setOnRefreshListener { + viewModel.fetchFollowers(refresh = true) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -48,7 +55,7 @@ class FollowersFragment : Fragment(), UserItemCallback { } override fun itemClick(userId: Int) { - + ProfileActivity.startActivity(requireContext(), userId) } override fun followButtonClick(user: ProfileListItem, action: UserItemCallback.FollowButtonAction) { diff --git a/app/src/main/java/com/isolaatti/followers/ui/FollowingFragment.kt b/app/src/main/java/com/isolaatti/followers/ui/FollowingFragment.kt index c9678ba..2322550 100644 --- a/app/src/main/java/com/isolaatti/followers/ui/FollowingFragment.kt +++ b/app/src/main/java/com/isolaatti/followers/ui/FollowingFragment.kt @@ -1,6 +1,7 @@ package com.isolaatti.followers.ui import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -19,6 +20,8 @@ class FollowingFragment : Fragment(), UserItemCallback { private val viewModel: FollowersViewModel by viewModels({ requireParentFragment() }) private lateinit var adapter: UserListRecyclerViewAdapter + + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -29,8 +32,9 @@ class FollowingFragment : Fragment(), UserItemCallback { } private fun setObservers() { - viewModel.followings.observe(viewLifecycleOwner) { - adapter.submitList(it) + viewModel.followings.observe(viewLifecycleOwner) { (list, updateEvent) -> + adapter.updateData(list, updateEvent) + binding.swipeToRefresh.isRefreshing = false } } @@ -38,6 +42,10 @@ class FollowingFragment : Fragment(), UserItemCallback { adapter = UserListRecyclerViewAdapter(this) binding.recyclerUsers.adapter = adapter binding.recyclerUsers.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + + binding.swipeToRefresh.setOnRefreshListener { + viewModel.fetchFollowings(refresh = true) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/com/isolaatti/profile/domain/entity/ProfileListItem.kt b/app/src/main/java/com/isolaatti/profile/domain/entity/ProfileListItem.kt index fdc6328..9cbb4e8 100644 --- a/app/src/main/java/com/isolaatti/profile/domain/entity/ProfileListItem.kt +++ b/app/src/main/java/com/isolaatti/profile/domain/entity/ProfileListItem.kt @@ -26,9 +26,7 @@ class ProfileListItem( if (id != other.id) return false if (name != other.name) return false if (profileImageId != other.profileImageId) return false - if (following != other.following) return false - - return true + return following == other.following } override fun hashCode(): Int {