agrego ProfileListingFragment: pantalla para mostrar lista de usuarios de varios origenes
This commit is contained in:
parent
a8c262048e
commit
1540d8ec2a
@ -4,15 +4,16 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.isolaatti.followers.ui.FollowersFragment
|
||||
import com.isolaatti.followers.ui.FollowingFragment
|
||||
import com.isolaatti.profile.profile_listing.ui.ProfileListingFragment
|
||||
|
||||
class FollowersViewPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
class FollowersViewPagerAdapter(fragment: Fragment, private val userId: Int) : FragmentStateAdapter(fragment) {
|
||||
override fun getItemCount(): Int = 2
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
if(position == 0) {
|
||||
return FollowersFragment()
|
||||
return ProfileListingFragment.getInstanceForFollowers(userId)
|
||||
}
|
||||
|
||||
return FollowingFragment()
|
||||
return ProfileListingFragment.getInstanceForFollowing(userId)
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
class MainFollowersFragment : Fragment() {
|
||||
|
||||
private lateinit var binding: FragmentFollowersMainBinding
|
||||
private val viewModel: FollowersViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@ -33,12 +32,9 @@ class MainFollowersFragment : Fragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val userId = arguments?.getInt(ARGUMENT_USER_ID) ?: 0
|
||||
|
||||
arguments?.getInt(ARGUMENT_USER_ID)?.let {
|
||||
viewModel.userId = it
|
||||
}
|
||||
|
||||
binding.viewPagerFollowersMain.adapter = FollowersViewPagerAdapter(this)
|
||||
binding.viewPagerFollowersMain.adapter = FollowersViewPagerAdapter(this, userId)
|
||||
TabLayoutMediator(binding.tabLayoutFollowers, binding.viewPagerFollowersMain) { tab, position ->
|
||||
when(position) {
|
||||
0 -> tab.text = getText(R.string.followers)
|
||||
|
||||
@ -5,12 +5,13 @@ import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.isolaatti.posting.posts.ui.PostLikesFragment
|
||||
import com.isolaatti.posting.posts.ui.PostVersionsFragment
|
||||
import com.isolaatti.profile.profile_listing.ui.ProfileListingFragment
|
||||
|
||||
class PostInfoViewPagerAdapter(fragmentActivity: FragmentActivity, private val postId: Long) : FragmentStateAdapter(fragmentActivity) {
|
||||
override fun getItemCount(): Int = 2
|
||||
|
||||
override fun createFragment(position: Int): Fragment = if(position == 0) {
|
||||
PostLikesFragment.getInstance(postId)
|
||||
ProfileListingFragment.getInstanceForPostLikes(postId)
|
||||
} else{
|
||||
PostVersionsFragment.getInstance(postId)
|
||||
}
|
||||
|
||||
@ -0,0 +1,226 @@
|
||||
package com.isolaatti.profile.profile_listing.presentation
|
||||
|
||||
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.posting.posts.domain.PostsRepository
|
||||
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||
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 ProfileListingViewModel @Inject constructor(
|
||||
private val followersRepository: FollowersRepository,
|
||||
private val postsRepository: PostsRepository
|
||||
): ViewModel() {
|
||||
// one of these values must be set from fragment, depending on what action will be performed
|
||||
var userId: Int = 0 // for followers and followings
|
||||
var postId: Long = 0 // for "liked by"
|
||||
|
||||
private var usersList: List<ProfileListItem> = listOf()
|
||||
|
||||
|
||||
private val _users: MutableLiveData<Pair<List<ProfileListItem>, UpdateEvent>> = MutableLiveData()
|
||||
|
||||
val users: LiveData<Pair<List<ProfileListItem>, UpdateEvent>> get() = _users
|
||||
|
||||
private val toRetry: MutableList<Runnable> = mutableListOf()
|
||||
|
||||
|
||||
// runs the lists of "Runnable" one by one and clears list. After this is executed,
|
||||
// caller should report as handled
|
||||
fun retry() {
|
||||
toRetry.forEach {
|
||||
it.run()
|
||||
}
|
||||
|
||||
toRetry.clear()
|
||||
}
|
||||
|
||||
|
||||
private fun getUsersLastId(): Int {
|
||||
if(usersList.isEmpty()) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return usersList.last().id
|
||||
}
|
||||
|
||||
/**
|
||||
* fetchFollowers for user previously set. userId must be set beforehand
|
||||
*/
|
||||
fun fetchFollowers(refresh: Boolean = false) {
|
||||
if(userId <= 0) {
|
||||
throw IllegalStateException("userId must not be 0. Be sure to set userId on view model before calling fetchFollowers method")
|
||||
}
|
||||
|
||||
if(refresh) {
|
||||
usersList = mutableListOf()
|
||||
}
|
||||
|
||||
val updateListEvent = if(refresh) ListUpdateEvent.Refresh else ListUpdateEvent.ItemsAdded
|
||||
|
||||
viewModelScope.launch {
|
||||
followersRepository.getFollowersOfUser(userId, getUsersLastId()).onEach {
|
||||
when(it) {
|
||||
is Resource.Success -> {
|
||||
if(it.data != null) {
|
||||
val prevCount = usersList.count()
|
||||
usersList += it.data
|
||||
_users.postValue(Pair(usersList, UpdateEvent(updateListEvent, arrayOf(prevCount))))
|
||||
}
|
||||
}
|
||||
is Resource.Error -> {
|
||||
toRetry.add {
|
||||
fetchFollowers()
|
||||
}
|
||||
}
|
||||
is Resource.Loading -> {}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fetchFollowings for user previously set. userId must be set beforehand
|
||||
*/
|
||||
fun fetchFollowings(refresh: Boolean = false) {
|
||||
if(userId <= 0) {
|
||||
throw IllegalStateException("userId must not be 0. Be sure to set userId on view model before calling fetchFollowings method")
|
||||
}
|
||||
|
||||
if(refresh) {
|
||||
usersList = mutableListOf()
|
||||
}
|
||||
|
||||
val updateListEvent = if(refresh) ListUpdateEvent.Refresh else ListUpdateEvent.ItemsAdded
|
||||
|
||||
viewModelScope.launch {
|
||||
followersRepository.getFollowingsOfUser(userId, getUsersLastId()).onEach {
|
||||
when(it) {
|
||||
is Resource.Error -> {
|
||||
toRetry.add {
|
||||
fetchFollowings()
|
||||
}
|
||||
}
|
||||
is Resource.Loading -> {}
|
||||
is Resource.Success -> {
|
||||
if(it.data != null) {
|
||||
val prevCount = usersList.count()
|
||||
usersList += it.data
|
||||
_users.postValue(Pair(usersList, UpdateEvent(updateListEvent, arrayOf(prevCount))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fetchPostLikedBy for post previously set. postId must be set beforehand
|
||||
*/
|
||||
fun fetchPostLikedBy(refresh: Boolean = false) {
|
||||
if(postId == 0L) {
|
||||
throw IllegalStateException("postId must not be 0. Be sure to set postId on view model before calling fetchPostLikedBy method")
|
||||
}
|
||||
|
||||
if(refresh) {
|
||||
usersList = mutableListOf()
|
||||
}
|
||||
|
||||
val updateListEvent = if(refresh) ListUpdateEvent.Refresh else ListUpdateEvent.ItemsAdded
|
||||
|
||||
viewModelScope.launch {
|
||||
postsRepository.getUsersLikedPost(postId).onEach { resource ->
|
||||
when(resource) {
|
||||
is Resource.Error -> {
|
||||
toRetry.add {
|
||||
fetchFollowings()
|
||||
}
|
||||
}
|
||||
is Resource.Loading -> {}
|
||||
is Resource.Success -> {
|
||||
if(resource.data != null) {
|
||||
val prevCount = usersList.count()
|
||||
usersList += resource.data
|
||||
_users.postValue(Pair(usersList, UpdateEvent(updateListEvent, arrayOf(prevCount))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceOnLists(user: ProfileListItem) {
|
||||
val userIndex = usersList.indexOf(user)
|
||||
|
||||
|
||||
if(userIndex >= 0) {
|
||||
usersList = usersList.toMutableList().apply {
|
||||
set(userIndex, user)
|
||||
}
|
||||
|
||||
_users.postValue(Pair(usersList, UpdateEvent(ListUpdateEvent.ItemUpdated, arrayOf(userIndex))))
|
||||
}
|
||||
}
|
||||
|
||||
fun followUser(user: ProfileListItem) {
|
||||
user.updatingFollowState = true
|
||||
|
||||
replaceOnLists(user)
|
||||
|
||||
viewModelScope.launch {
|
||||
followersRepository.followUser(user.id).onEach {
|
||||
when(it) {
|
||||
is Resource.Error -> {
|
||||
toRetry.add {
|
||||
followUser(user)
|
||||
}
|
||||
}
|
||||
is Resource.Loading -> {}
|
||||
is Resource.Success -> {
|
||||
user.following = true
|
||||
user.updatingFollowState = false
|
||||
replaceOnLists(user)
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun unfollowUser(user: ProfileListItem) {
|
||||
user.updatingFollowState = true
|
||||
|
||||
replaceOnLists(user)
|
||||
|
||||
viewModelScope.launch {
|
||||
followersRepository.unfollowUser(user.id).onEach {
|
||||
when(it) {
|
||||
is Resource.Error -> {
|
||||
toRetry.add {
|
||||
unfollowUser(user)
|
||||
}
|
||||
}
|
||||
is Resource.Loading -> {}
|
||||
is Resource.Success -> {
|
||||
user.following = false
|
||||
user.updatingFollowState = false
|
||||
replaceOnLists(user)
|
||||
}
|
||||
}
|
||||
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package com.isolaatti.profile.profile_listing.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.isolaatti.common.UserItemCallback
|
||||
import com.isolaatti.common.UserListRecyclerViewAdapter
|
||||
import com.isolaatti.databinding.FragmentUserListBinding
|
||||
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||
import com.isolaatti.profile.profile_listing.presentation.ProfileListingViewModel
|
||||
import com.isolaatti.profile.ui.ProfileActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ProfileListingFragment : Fragment(), UserItemCallback {
|
||||
|
||||
private val viewModel: ProfileListingViewModel by viewModels()
|
||||
private lateinit var binding: FragmentUserListBinding
|
||||
private var mode: Int = 0
|
||||
|
||||
private lateinit var adapter: UserListRecyclerViewAdapter
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentUserListBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mode = arguments?.getInt(ARG_MODE) ?: 0
|
||||
when(mode) {
|
||||
FOLLOWING, FOLLOWERS -> viewModel.userId = arguments?.getInt(ARG_USER_ID) ?: 0
|
||||
POST_LIKES -> viewModel.postId = arguments?.getLong(ARG_POST_ID) ?: 0
|
||||
}
|
||||
loadData()
|
||||
|
||||
}
|
||||
|
||||
private fun loadData(refresh: Boolean = false) {
|
||||
when(mode) {
|
||||
FOLLOWING -> viewModel.fetchFollowings(refresh)
|
||||
FOLLOWERS -> viewModel.fetchFollowers(refresh)
|
||||
POST_LIKES -> viewModel.fetchPostLikedBy(refresh)
|
||||
BROWSE_PROFILES -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
adapter = UserListRecyclerViewAdapter(this)
|
||||
binding.recyclerUsers.adapter = adapter
|
||||
binding.recyclerUsers.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
viewModel.users.observe(viewLifecycleOwner) { (list, updateEvent) ->
|
||||
adapter.updateData(list, updateEvent)
|
||||
binding.swipeToRefresh.isRefreshing = false
|
||||
}
|
||||
|
||||
binding.swipeToRefresh.setOnRefreshListener {
|
||||
loadData(refresh = true)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_MODE = "mode"
|
||||
private const val ARG_USER_ID = "userId"
|
||||
private const val ARG_POST_ID = "postId"
|
||||
const val FOLLOWERS = 1
|
||||
const val FOLLOWING = 2
|
||||
const val POST_LIKES = 3
|
||||
const val BROWSE_PROFILES = 4
|
||||
|
||||
fun getInstanceForFollowers(userId: Int): ProfileListingFragment {
|
||||
return ProfileListingFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(ARG_MODE, FOLLOWERS)
|
||||
putInt(ARG_USER_ID, userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstanceForFollowing(userId: Int): ProfileListingFragment {
|
||||
return ProfileListingFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(ARG_MODE, FOLLOWING)
|
||||
putInt(ARG_USER_ID, userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstanceForPostLikes(postId: Long): ProfileListingFragment {
|
||||
return ProfileListingFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(ARG_MODE, POST_LIKES)
|
||||
putLong(ARG_POST_ID, postId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstanceForBrowseProfile(): ProfileListingFragment {
|
||||
return ProfileListingFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(ARG_MODE, BROWSE_PROFILES)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun itemClick(userId: Int) {
|
||||
ProfileActivity.startActivity(requireContext(), userId)
|
||||
}
|
||||
|
||||
override fun followButtonClick(
|
||||
user: ProfileListItem,
|
||||
action: UserItemCallback.FollowButtonAction
|
||||
) {
|
||||
when(action) {
|
||||
UserItemCallback.FollowButtonAction.Follow -> viewModel.followUser(user)
|
||||
UserItemCallback.FollowButtonAction.Unfollow -> viewModel.unfollowUser(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user