se agrega pantalla "browse profiles" y se realiza refactorización
This commit is contained in:
parent
50a70c6207
commit
ab76e3827a
@ -1,6 +1,7 @@
|
|||||||
package com.isolaatti.profile.domain.entity
|
package com.isolaatti.profile.domain.entity
|
||||||
|
|
||||||
import com.isolaatti.profile.data.remote.ProfileListItemDto
|
import com.isolaatti.profile.data.remote.ProfileListItemDto
|
||||||
|
import com.isolaatti.search.data.ProfileSearchDto
|
||||||
|
|
||||||
class ProfileListItem(
|
class ProfileListItem(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@ -15,6 +16,10 @@ class ProfileListItem(
|
|||||||
fun fromDto(dto: ProfileListItemDto) = ProfileListItem(
|
fun fromDto(dto: ProfileListItemDto) = ProfileListItem(
|
||||||
dto.id,dto.name, dto.profileImageId, dto.following
|
dto.id,dto.name, dto.profileImageId, dto.following
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun fromDto(dto: ProfileSearchDto) = ProfileListItem(
|
||||||
|
dto.id, dto.name, dto.imageId, dto.following
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import com.isolaatti.common.UpdateEvent
|
|||||||
import com.isolaatti.followers.domain.FollowersRepository
|
import com.isolaatti.followers.domain.FollowersRepository
|
||||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||||
import com.isolaatti.profile.domain.entity.ProfileListItem
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
|
import com.isolaatti.search.domain.SearchRepository
|
||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -21,7 +22,8 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ProfileListingViewModel @Inject constructor(
|
class ProfileListingViewModel @Inject constructor(
|
||||||
private val followersRepository: FollowersRepository,
|
private val followersRepository: FollowersRepository,
|
||||||
private val postsRepository: PostsRepository
|
private val postsRepository: PostsRepository,
|
||||||
|
private val searchRepository: SearchRepository
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
// one of these values must be set from fragment, depending on what action will be performed
|
// 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 userId: Int = 0 // for followers and followings
|
||||||
@ -145,7 +147,7 @@ class ProfileListingViewModel @Inject constructor(
|
|||||||
when(resource) {
|
when(resource) {
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
toRetry.add {
|
toRetry.add {
|
||||||
fetchFollowings()
|
fetchPostLikedBy(refresh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Resource.Loading -> {}
|
is Resource.Loading -> {}
|
||||||
@ -161,6 +163,34 @@ class ProfileListingViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fetchProfiles(refresh: Boolean = false) {
|
||||||
|
if(refresh) {
|
||||||
|
usersList = mutableListOf()
|
||||||
|
}
|
||||||
|
val updateListEvent = if(refresh) ListUpdateEvent.Refresh else ListUpdateEvent.ItemsAdded
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
searchRepository.getNewestUsers(getUsersLastId()).onEach { resource ->
|
||||||
|
when(resource) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
toRetry.add {
|
||||||
|
fetchProfiles(refresh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
private fun replaceOnLists(user: ProfileListItem) {
|
||||||
val userIndex = usersList.indexOf(user)
|
val userIndex = usersList.indexOf(user)
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,7 @@ class ProfileListingFragment : Fragment(), UserItemCallback {
|
|||||||
FOLLOWING -> viewModel.fetchFollowings(refresh)
|
FOLLOWING -> viewModel.fetchFollowings(refresh)
|
||||||
FOLLOWERS -> viewModel.fetchFollowers(refresh)
|
FOLLOWERS -> viewModel.fetchFollowers(refresh)
|
||||||
POST_LIKES -> viewModel.fetchPostLikedBy(refresh)
|
POST_LIKES -> viewModel.fetchPostLikedBy(refresh)
|
||||||
BROWSE_PROFILES -> {}
|
BROWSE_PROFILES -> viewModel.fetchProfiles(refresh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ class ProfileListingFragment : Fragment(), UserItemCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG_MODE = "mode"
|
const val ARG_MODE = "mode"
|
||||||
private const val ARG_USER_ID = "userId"
|
private const val ARG_USER_ID = "userId"
|
||||||
private const val ARG_POST_ID = "postId"
|
private const val ARG_POST_ID = "postId"
|
||||||
const val FOLLOWERS = 1
|
const val FOLLOWERS = 1
|
||||||
|
|||||||
@ -1,6 +1,39 @@
|
|||||||
package com.isolaatti.profile.ui
|
package com.isolaatti.profile.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.Fragment
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.isolaatti.R
|
||||||
|
import com.isolaatti.databinding.FragmentBrowseProfileBinding
|
||||||
|
import com.isolaatti.posting.posts.ui.PostListingFragment
|
||||||
|
import com.isolaatti.profile.profile_listing.ui.ProfileListingFragment
|
||||||
|
|
||||||
class BrowseProfilesFragment : Fragment() {
|
class BrowseProfilesFragment : Fragment() {
|
||||||
|
private lateinit var binding: FragmentBrowseProfileBinding
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
binding = FragmentBrowseProfileBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.toolbar.setNavigationOnClickListener {
|
||||||
|
findNavController().popBackStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
(childFragmentManager.findFragmentById(R.id.post_list_fragment_container) as NavHostFragment)
|
||||||
|
.navController.setGraph(R.navigation.profile_listing_navigation, Bundle().apply { putInt(
|
||||||
|
ProfileListingFragment.ARG_MODE, ProfileListingFragment.BROWSE_PROFILES) })
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package com.isolaatti.search.data
|
package com.isolaatti.search.data
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
import com.isolaatti.search.domain.SearchRepository
|
import com.isolaatti.search.domain.SearchRepository
|
||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -66,16 +67,16 @@ class SearchRepositoryImpl(private val searchApi: SearchApi, private val searchD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNewestUsers(): Flow<Resource<NewestUsersDto>> = flow {
|
override fun getNewestUsers(after: Int?): Flow<Resource<List<ProfileListItem>>> = flow {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val result = searchApi.getNewestUsers(10, null).awaitResponse()
|
val result = searchApi.getNewestUsers(10, after).awaitResponse()
|
||||||
|
|
||||||
if(result.isSuccessful) {
|
if(result.isSuccessful) {
|
||||||
val dto = result.body()
|
val dto = result.body()
|
||||||
if(dto != null) {
|
if(dto != null) {
|
||||||
emit(Resource.Success(dto))
|
emit(Resource.Success(dto.result.map { ProfileListItem.fromDto(it) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package com.isolaatti.search.domain
|
package com.isolaatti.search.domain
|
||||||
|
|
||||||
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
import com.isolaatti.search.data.HashtagsDto
|
import com.isolaatti.search.data.HashtagsDto
|
||||||
import com.isolaatti.search.data.NewestUsersDto
|
|
||||||
import com.isolaatti.search.data.SearchDto
|
import com.isolaatti.search.data.SearchDto
|
||||||
import com.isolaatti.search.data.SearchHistoryEntity
|
import com.isolaatti.search.data.SearchHistoryEntity
|
||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
@ -15,5 +15,5 @@ interface SearchRepository {
|
|||||||
fun removeSuggestion(query: String): Flow<Resource<Boolean>>
|
fun removeSuggestion(query: String): Flow<Resource<Boolean>>
|
||||||
|
|
||||||
fun getTrendingHashtags(): Flow<Resource<HashtagsDto>>
|
fun getTrendingHashtags(): Flow<Resource<HashtagsDto>>
|
||||||
fun getNewestUsers(): Flow<Resource<NewestUsersDto>>
|
fun getNewestUsers(after: Int?): Flow<Resource<List<ProfileListItem>>>
|
||||||
}
|
}
|
||||||
@ -4,6 +4,7 @@ import android.util.Log
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
import com.isolaatti.search.data.HashtagsDto
|
import com.isolaatti.search.data.HashtagsDto
|
||||||
import com.isolaatti.search.data.NewestUsersDto
|
import com.isolaatti.search.data.NewestUsersDto
|
||||||
import com.isolaatti.search.data.SearchDto
|
import com.isolaatti.search.data.SearchDto
|
||||||
@ -27,7 +28,7 @@ class SearchViewModel @Inject constructor(private val searchRepository: SearchRe
|
|||||||
val searchSuggestions: MutableLiveData<List<SearchHistoryEntity>> = MutableLiveData()
|
val searchSuggestions: MutableLiveData<List<SearchHistoryEntity>> = MutableLiveData()
|
||||||
val searchResults: MutableLiveData<SearchDto> = MutableLiveData()
|
val searchResults: MutableLiveData<SearchDto> = MutableLiveData()
|
||||||
val trendingHashtags: MutableLiveData<HashtagsDto> = MutableLiveData()
|
val trendingHashtags: MutableLiveData<HashtagsDto> = MutableLiveData()
|
||||||
val newestUsers: MutableLiveData<NewestUsersDto> = MutableLiveData()
|
val newestUsers: MutableLiveData<List<ProfileListItem>> = MutableLiveData()
|
||||||
|
|
||||||
var searchQuery = ""
|
var searchQuery = ""
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ class SearchViewModel @Inject constructor(private val searchRepository: SearchRe
|
|||||||
|
|
||||||
fun loadNewestUsers() {
|
fun loadNewestUsers() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
searchRepository.getNewestUsers().onEach { resource ->
|
searchRepository.getNewestUsers(null).onEach { resource ->
|
||||||
when(resource) {
|
when(resource) {
|
||||||
is Resource.Error -> {}
|
is Resource.Error -> {}
|
||||||
is Resource.Loading -> {}
|
is Resource.Loading -> {}
|
||||||
|
|||||||
@ -8,25 +8,26 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
|||||||
import coil.load
|
import coil.load
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
import com.isolaatti.databinding.UsersCarouselItemBinding
|
import com.isolaatti.databinding.UsersCarouselItemBinding
|
||||||
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
import com.isolaatti.search.data.ProfileSearchDto
|
import com.isolaatti.search.data.ProfileSearchDto
|
||||||
import com.isolaatti.utils.UrlGen
|
import com.isolaatti.utils.UrlGen
|
||||||
|
|
||||||
class UserCarouselAdapter(
|
class UserCarouselAdapter(
|
||||||
private val onProfileClick: (profileId: Int) -> Unit = {}
|
private val onProfileClick: (profileId: Int) -> Unit = {}
|
||||||
) : ListAdapter<ProfileSearchDto, UserCarouselAdapter.UserCarouselItemViewHolder>(itemCallback) {
|
) : ListAdapter<ProfileListItem, UserCarouselAdapter.UserCarouselItemViewHolder>(itemCallback) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val itemCallback = object: DiffUtil.ItemCallback<ProfileSearchDto>() {
|
val itemCallback = object: DiffUtil.ItemCallback<ProfileListItem>() {
|
||||||
override fun areItemsTheSame(
|
override fun areItemsTheSame(
|
||||||
oldItem: ProfileSearchDto,
|
oldItem: ProfileListItem,
|
||||||
newItem: ProfileSearchDto
|
newItem: ProfileListItem
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return oldItem.id == newItem.id
|
return oldItem.id == newItem.id
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
override fun areContentsTheSame(
|
||||||
oldItem: ProfileSearchDto,
|
oldItem: ProfileListItem,
|
||||||
newItem: ProfileSearchDto
|
newItem: ProfileListItem
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return oldItem == newItem
|
return oldItem == newItem
|
||||||
}
|
}
|
||||||
@ -44,8 +45,8 @@ class UserCarouselAdapter(
|
|||||||
val user = getItem(position)
|
val user = getItem(position)
|
||||||
|
|
||||||
holder.usersCarouselItemBinding.userCarouselName.text = user.name
|
holder.usersCarouselItemBinding.userCarouselName.text = user.name
|
||||||
if(user.imageId != null) {
|
if(user.profileImageId != null) {
|
||||||
holder.usersCarouselItemBinding.userCarouselImageView.load(UrlGen.imageUrl(user.imageId))
|
holder.usersCarouselItemBinding.userCarouselImageView.load(UrlGen.imageUrl(user.profileImageId))
|
||||||
} else {
|
} else {
|
||||||
holder.usersCarouselItemBinding.userCarouselImageView.load(R.drawable.avatar)
|
holder.usersCarouselItemBinding.userCarouselImageView.load(R.drawable.avatar)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@ import com.google.android.material.chip.Chip
|
|||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
import com.isolaatti.databinding.FragmentSearchBinding
|
import com.isolaatti.databinding.FragmentSearchBinding
|
||||||
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
||||||
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
|
import com.isolaatti.profile.profile_listing.ui.ProfileListingFragment
|
||||||
import com.isolaatti.profile.ui.ProfileActivity
|
import com.isolaatti.profile.ui.ProfileActivity
|
||||||
import com.isolaatti.search.data.HashtagsDto
|
import com.isolaatti.search.data.HashtagsDto
|
||||||
import com.isolaatti.search.data.NewestUsersDto
|
import com.isolaatti.search.data.NewestUsersDto
|
||||||
@ -67,8 +69,8 @@ class SearchFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val newestUsersObserver: Observer<NewestUsersDto> = Observer {
|
private val newestUsersObserver: Observer<List<ProfileListItem>> = Observer {
|
||||||
newestUsersAdapter?.submitList(it.result)
|
newestUsersAdapter?.submitList(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|||||||
26
app/src/main/res/layout/fragment_browse_profile.xml
Normal file
26
app/src/main/res/layout/fragment_browse_profile.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:title="@string/browse_profiles"
|
||||||
|
app:navigationIcon="@drawable/baseline_close_24"/>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/post_list_fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
app:defaultNavHost="true"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
11
app/src/main/res/navigation/profile_listing_navigation.xml
Normal file
11
app/src/main/res/navigation/profile_listing_navigation.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/profile_listing_navigation"
|
||||||
|
app:startDestination="@id/profileListingFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/profileListingFragment"
|
||||||
|
android:name="com.isolaatti.profile.profile_listing.ui.ProfileListingFragment"
|
||||||
|
android:label="ProfileListingFragment" />
|
||||||
|
</navigation>
|
||||||
Loading…
x
Reference in New Issue
Block a user