WIP perfil
This commit is contained in:
parent
0ab027b635
commit
4a8d05c87b
@ -24,7 +24,6 @@ import com.isolaatti.databinding.FragmentFeedBinding
|
||||
import com.isolaatti.drafts.ui.DraftsActivity
|
||||
import com.isolaatti.home.presentation.FeedViewModel
|
||||
import com.isolaatti.posting.PostViewerActivity
|
||||
import com.isolaatti.posting.posts.presentation.PostsViewModel
|
||||
import com.isolaatti.posting.comments.presentation.BottomSheetPostComments
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked
|
||||
@ -32,6 +31,7 @@ import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
|
||||
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
|
||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||
import com.isolaatti.posting.posts.ui.CreatePostActivity
|
||||
import com.isolaatti.profile.ui.ProfileActivity
|
||||
import com.isolaatti.settings.ui.SettingsActivity
|
||||
@ -53,9 +53,8 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
const val CALLER_ID = 20
|
||||
}
|
||||
|
||||
private val viewModel: PostsViewModel by activityViewModels()
|
||||
private val errorViewModel: ErrorMessageViewModel by activityViewModels()
|
||||
private val screenViewModel: FeedViewModel by viewModels()
|
||||
private val viewModel: FeedViewModel by activityViewModels()
|
||||
val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
|
||||
|
||||
private lateinit var viewBinding: FragmentFeedBinding
|
||||
@ -158,7 +157,7 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
}
|
||||
}
|
||||
|
||||
screenViewModel.userProfile.observe(viewLifecycleOwner) {
|
||||
viewModel.userProfile.observe(viewLifecycleOwner) {
|
||||
val header = viewBinding.homeDrawer.getHeaderView(0) as? ConstraintLayout
|
||||
|
||||
val image: ImageView? = header?.findViewById(R.id.profileImageView)
|
||||
@ -175,34 +174,23 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
|
||||
|
||||
viewModel.posts.observe(viewLifecycleOwner){
|
||||
if (it != null) {
|
||||
adapter.updateList(it,null)
|
||||
if (it.first != null) {
|
||||
adapter.updateList(it.first!!, it.second)
|
||||
}
|
||||
}
|
||||
|
||||
// viewModel.loadingPosts.observe(viewLifecycleOwner) {
|
||||
// viewBinding.progressBarLoading.visibility = if(it) View.VISIBLE else View.GONE
|
||||
// viewBinding.loadMoreButton.visibility = if(it) View.GONE else View.VISIBLE
|
||||
// }
|
||||
//
|
||||
viewModel.loadingPosts.observe(viewLifecycleOwner) {
|
||||
viewBinding.swipeToRefresh.isRefreshing = it
|
||||
}
|
||||
|
||||
// viewModel.noMoreContent.observe(viewLifecycleOwner) {
|
||||
// val visibility = if(it) View.VISIBLE else View.GONE
|
||||
// viewBinding.noMoreContentToShowTextView.visibility = visibility
|
||||
// viewBinding.refreshButton.visibility = visibility
|
||||
// viewBinding.loadMoreButton.visibility = if(it) View.GONE else View.VISIBLE
|
||||
//
|
||||
// }
|
||||
|
||||
viewModel.errorLoading.observe(viewLifecycleOwner) {
|
||||
errorViewModel.error.postValue(it)
|
||||
}
|
||||
viewModel.postLiked.observe(viewLifecycleOwner) {
|
||||
viewModel.posts.value?.let { feed ->
|
||||
adapter.updateList(
|
||||
feed, PostsRecyclerViewAdapter.UpdateEvent(
|
||||
PostsRecyclerViewAdapter.UpdateEvent.UpdateType.POST_LIKED, it.postId))
|
||||
}
|
||||
}
|
||||
screenViewModel.getProfile()
|
||||
viewModel.getProfile()
|
||||
|
||||
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
|
||||
}
|
||||
@ -210,8 +198,6 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
fun onNewMenuItemClicked(v: View) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onLiked(postId: Long) = viewModel.likePost(postId)
|
||||
|
||||
override fun onUnLiked(postId: Long) = viewModel.unLikePost(postId)
|
||||
@ -234,5 +220,4 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
override fun onProfileClick(userId: Int) {
|
||||
ProfileActivity.startActivity(requireContext(), userId)
|
||||
}
|
||||
|
||||
}
|
||||
@ -8,16 +8,13 @@ import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.common.ErrorMessageViewModel
|
||||
import com.isolaatti.common.IsolaattiBaseActivity
|
||||
import com.isolaatti.databinding.ActivityHomeBinding
|
||||
import com.isolaatti.posting.posts.presentation.PostsViewModel
|
||||
import com.isolaatti.utils.Resource
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import com.isolaatti.home.presentation.FeedViewModel
|
||||
|
||||
class HomeActivity : IsolaattiBaseActivity() {
|
||||
private lateinit var viewBinding: ActivityHomeBinding
|
||||
private val postsViewModel: PostsViewModel by viewModels()
|
||||
private val feedViewModel: FeedViewModel by viewModels()
|
||||
override fun onRetry() {
|
||||
|
||||
}
|
||||
@ -32,7 +29,7 @@ class HomeActivity : IsolaattiBaseActivity() {
|
||||
viewBinding.navigationRail?.setupWithNavController(navHostFragment.navController)
|
||||
|
||||
if(savedInstanceState == null) {
|
||||
postsViewModel.getFeed(false)
|
||||
feedViewModel.getFeed(false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,6 +5,9 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.auth.domain.AuthRepository
|
||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||
import com.isolaatti.posting.posts.presentation.PostListingViewModelBase
|
||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||
import com.isolaatti.profile.data.remote.UserProfileDto
|
||||
import com.isolaatti.profile.domain.use_case.GetProfile
|
||||
import com.isolaatti.utils.Resource
|
||||
@ -17,7 +20,36 @@ import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class FeedViewModel @Inject constructor(private val getProfileUseCase: GetProfile, private val authRepository: AuthRepository) : ViewModel() {
|
||||
class FeedViewModel @Inject constructor(
|
||||
private val getProfileUseCase: GetProfile,
|
||||
private val authRepository: AuthRepository,
|
||||
private val postsRepository: PostsRepository
|
||||
) : PostListingViewModelBase() {
|
||||
|
||||
override fun getFeed(refresh: Boolean) {
|
||||
viewModelScope.launch {
|
||||
if (refresh) {
|
||||
posts.value = null
|
||||
}
|
||||
postsRepository.getFeed(getLastId()).onEach { feedDtoResource ->
|
||||
when (feedDtoResource) {
|
||||
is Resource.Success -> {
|
||||
loadingPosts.postValue(false)
|
||||
posts.postValue(Pair(posts.value?.first?.concatFeed(feedDtoResource.data) ?: feedDtoResource.data, UpdateEvent(UpdateEvent.UpdateType.PAGE_ADDED, null)))
|
||||
noMoreContent.postValue(feedDtoResource.data?.moreContent == false)
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
loadingPosts.postValue(true)
|
||||
}
|
||||
|
||||
is Resource.Error -> {
|
||||
errorLoading.postValue(feedDtoResource.errorType)
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
// User profile
|
||||
private val _userProfile: MutableLiveData<UserProfileDto> = MutableLiveData()
|
||||
@ -25,10 +57,10 @@ class FeedViewModel @Inject constructor(private val getProfileUseCase: GetProfil
|
||||
|
||||
fun getProfile() {
|
||||
viewModelScope.launch {
|
||||
authRepository.getUserId().onEach {userId ->
|
||||
authRepository.getUserId().onEach { userId ->
|
||||
userId?.let {
|
||||
getProfileUseCase(userId).onEach {profile ->
|
||||
if(profile is Resource.Success) {
|
||||
getProfileUseCase(userId).onEach { profile ->
|
||||
if (profile is Resource.Success) {
|
||||
profile.data?.let { _userProfile.postValue(it) }
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
|
||||
@ -10,14 +10,8 @@ import com.isolaatti.posting.posts.data.remote.PostApi
|
||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||
import com.isolaatti.utils.Resource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import retrofit2.await
|
||||
import java.io.IOException
|
||||
import java.lang.RuntimeException
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, private val postApi: PostApi) : PostsRepository {
|
||||
override fun getFeed(lastId: Long): Flow<Resource<FeedDto>> = flow {
|
||||
@ -39,7 +33,7 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto): Flow<Resource<FeedDto>> = flow {
|
||||
override fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>> = flow {
|
||||
emit(Resource.Loading())
|
||||
try {
|
||||
val gson = Gson()
|
||||
|
||||
@ -11,7 +11,7 @@ interface PostsRepository {
|
||||
|
||||
fun getFeed(lastId: Long): Flow<Resource<FeedDto>>
|
||||
|
||||
fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto): Flow<Resource<FeedDto>>
|
||||
fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>>
|
||||
|
||||
fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>>
|
||||
fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>>
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.isolaatti.posting.posts.presentation
|
||||
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||
|
||||
abstract class PostListingRecyclerViewAdapterWiring(private val postsViewModelBase: PostListingViewModelBase) : OnUserInteractedWithPostCallback {
|
||||
|
||||
override fun onLiked(postId: Long) {
|
||||
postsViewModelBase.likePost(postId)
|
||||
}
|
||||
|
||||
override fun onUnLiked(postId: Long) {
|
||||
postsViewModelBase.unLikePost(postId)
|
||||
}
|
||||
|
||||
abstract override fun onComment(postId: Long)
|
||||
|
||||
abstract override fun onOpenPost(postId: Long)
|
||||
|
||||
abstract override fun onOptions(postId: Long)
|
||||
|
||||
abstract override fun onProfileClick(userId: Int)
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.isolaatti.posting.posts.presentation
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.posting.likes.data.remote.LikeDto
|
||||
import com.isolaatti.posting.likes.domain.repository.LikesRepository
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.profile.domain.use_case.GetProfilePosts
|
||||
import com.isolaatti.utils.Resource
|
||||
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
|
||||
|
||||
abstract class PostListingViewModelBase : ViewModel() {
|
||||
@Inject
|
||||
lateinit var likesRepository: LikesRepository
|
||||
@Inject
|
||||
lateinit var getProfilePosts: GetProfilePosts
|
||||
|
||||
val posts: MutableLiveData<Pair<FeedDto?, UpdateEvent>> = MutableLiveData()
|
||||
|
||||
val loadingPosts = MutableLiveData(false)
|
||||
|
||||
val noMoreContent = MutableLiveData(false)
|
||||
|
||||
val errorLoading: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||
|
||||
fun getLastId(): Long = try { posts.value?.first?.data?.last()?.post?.id ?: 0 } catch (e: NoSuchElementException) { 0 }
|
||||
|
||||
|
||||
abstract fun getFeed(refresh: Boolean)
|
||||
|
||||
fun likePost(postId: Long) {
|
||||
viewModelScope.launch {
|
||||
likesRepository.likePost(postId).onEach {likeDto ->
|
||||
val likedPost = posts.value?.first?.data?.find { post -> post.post.id == likeDto.postId }
|
||||
val index = posts.value?.first?.data?.indexOf(likedPost)
|
||||
if(index != null){
|
||||
val temp = posts.value?.first
|
||||
Log.d("***", temp.toString())
|
||||
temp?.data?.set(index, likedPost!!.apply {
|
||||
liked = true
|
||||
numberOfLikes = likeDto.likesCount
|
||||
|
||||
})
|
||||
}
|
||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, likedPost?.post?.id)))
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun unLikePost(postId: Long) {
|
||||
viewModelScope.launch {
|
||||
likesRepository.unLikePost(postId).onEach {likeDto ->
|
||||
val likedPost = posts.value?.first?.data?.find { post -> post.post.id == likeDto.postId }
|
||||
val index = posts.value?.first?.data?.indexOf(likedPost)
|
||||
if(index != null){
|
||||
posts.value?.first?.data?.set(index, likedPost!!.apply {
|
||||
liked = false
|
||||
numberOfLikes = likeDto.likesCount
|
||||
})
|
||||
}
|
||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, likedPost?.post?.id)))
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,15 +114,6 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
||||
data class LikeCountUpdatePayload(val likeCount: Int)
|
||||
data class CommentsCountUpdatePayload(val commentsCount: Int)
|
||||
|
||||
data class UpdateEvent(val updateType: UpdateType, val affectedId: Long) {
|
||||
enum class UpdateType {
|
||||
POST_LIKED,
|
||||
POST_COMMENTED,
|
||||
POST_REMOVED,
|
||||
POST_ADDED
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.post_layout, parent, false)
|
||||
@ -143,24 +134,34 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
||||
notifyDataSetChanged()
|
||||
return
|
||||
}
|
||||
val postUpdated = feedDto?.data?.find { p -> p.post.id == updateEvent.affectedId } ?: return
|
||||
val position = feedDto?.data?.indexOf(postUpdated) ?: return
|
||||
val postUpdated = feedDto?.data?.find { p -> p.post.id == updateEvent.affectedId }
|
||||
val position = feedDto?.data?.indexOf(postUpdated)
|
||||
|
||||
feedDto = updatedFeed
|
||||
|
||||
when(updateEvent.updateType) {
|
||||
UpdateEvent.UpdateType.POST_LIKED -> {
|
||||
if(postUpdated != null && position != null)
|
||||
notifyItemChanged(position, LikeCountUpdatePayload(postUpdated.numberOfLikes))
|
||||
}
|
||||
UpdateEvent.UpdateType.POST_COMMENTED -> {
|
||||
if(postUpdated != null && position != null)
|
||||
notifyItemChanged(position, CommentsCountUpdatePayload(postUpdated.numberOfComments))
|
||||
}
|
||||
UpdateEvent.UpdateType.POST_REMOVED -> {
|
||||
if(postUpdated != null && position != null)
|
||||
notifyItemRemoved(position)
|
||||
}
|
||||
UpdateEvent.UpdateType.POST_ADDED -> {
|
||||
notifyItemInserted(0)
|
||||
}
|
||||
|
||||
UpdateEvent.UpdateType.PAGE_ADDED -> {
|
||||
notifyItemInserted(itemCount - 1)
|
||||
}
|
||||
UpdateEvent.UpdateType.REFRESH -> {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,105 +0,0 @@
|
||||
package com.isolaatti.posting.posts.presentation
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.likes.data.remote.LikeDto
|
||||
import com.isolaatti.posting.likes.domain.repository.LikesRepository
|
||||
import com.isolaatti.posting.posts.data.repository.PostsRepositoryImpl
|
||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||
import com.isolaatti.utils.Resource
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
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 PostsViewModel @Inject constructor(private val postsRepository: PostsRepository, private val likesRepository: LikesRepository) : ViewModel() {
|
||||
|
||||
private val _posts: MutableLiveData<FeedDto?> = MutableLiveData()
|
||||
val posts: LiveData<FeedDto?> get() = _posts
|
||||
|
||||
private val _loadingPosts = MutableLiveData(false)
|
||||
val loadingPosts: LiveData<Boolean> get() = _loadingPosts
|
||||
|
||||
private val _noMoreContent = MutableLiveData(false)
|
||||
val noMoreContent: LiveData<Boolean> get() = _noMoreContent
|
||||
|
||||
private val _errorLoading: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||
val errorLoading: LiveData<Resource.Error.ErrorType?> get() = _errorLoading
|
||||
|
||||
private val _comments: MutableLiveData<FeedCommentsDto> = MutableLiveData()
|
||||
val comments: LiveData<FeedCommentsDto> get() = _comments
|
||||
|
||||
private fun getLastId(): Long = try {_posts.value?.data?.last()?.post?.id ?: 0} catch (e: NoSuchElementException) { 0 }
|
||||
|
||||
private val _postLiked: MutableLiveData<LikeDto> = MutableLiveData()
|
||||
val postLiked: LiveData<LikeDto> get() = _postLiked
|
||||
|
||||
fun getFeed(refresh: Boolean) {
|
||||
viewModelScope.launch {
|
||||
if(refresh) {
|
||||
_posts.value = null
|
||||
}
|
||||
postsRepository.getFeed(getLastId()).onEach {
|
||||
when(it) {
|
||||
is Resource.Success -> {
|
||||
_loadingPosts.postValue(false)
|
||||
_posts.postValue(posts.value?.concatFeed(it.data) ?: it.data)
|
||||
_noMoreContent.postValue(it.data?.moreContent == false)
|
||||
}
|
||||
is Resource.Loading -> {
|
||||
_loadingPosts.postValue(true)
|
||||
}
|
||||
is Resource.Error -> {
|
||||
_errorLoading.postValue(it.errorType)
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun likePost(postId: Long) {
|
||||
viewModelScope.launch {
|
||||
likesRepository.likePost(postId).onEach {likeDto ->
|
||||
val likedPost = _posts.value?.data?.find { post -> post.post.id == likeDto.postId }
|
||||
val index = _posts.value?.data?.indexOf(likedPost)
|
||||
Log.d("***", index.toString())
|
||||
if(index != null){
|
||||
val temp = _posts.value
|
||||
Log.d("***", temp.toString())
|
||||
temp?.data?.set(index, likedPost!!.apply {
|
||||
liked = true
|
||||
numberOfLikes = likeDto.likesCount
|
||||
|
||||
})
|
||||
}
|
||||
_postLiked.postValue(likeDto)
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun unLikePost(postId: Long) {
|
||||
viewModelScope.launch {
|
||||
likesRepository.unLikePost(postId).onEach {likeDto ->
|
||||
val likedPost = _posts.value?.data?.find { post -> post.post.id == likeDto.postId }
|
||||
val index = _posts.value?.data?.indexOf(likedPost)
|
||||
if(index != null){
|
||||
_posts.value?.data?.set(index, likedPost!!.apply {
|
||||
liked = false
|
||||
numberOfLikes = likeDto.likesCount
|
||||
})
|
||||
}
|
||||
_postLiked.postValue(likeDto)
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.isolaatti.posting.posts.presentation
|
||||
|
||||
data class UpdateEvent(val updateType: UpdateType, val affectedId: Long?) {
|
||||
enum class UpdateType {
|
||||
POST_LIKED,
|
||||
POST_COMMENTED,
|
||||
POST_REMOVED,
|
||||
POST_ADDED,
|
||||
PAGE_ADDED,
|
||||
REFRESH
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetProfilePosts @Inject constructor(private val postsRepository: PostsRepository) {
|
||||
operator fun invoke(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto): Flow<Resource<FeedDto>> =
|
||||
operator fun invoke(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>> =
|
||||
postsRepository.getProfilePosts(userId, lastId, olderFirst, filter)
|
||||
}
|
||||
@ -6,6 +6,8 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.posts.data.remote.FeedFilterDto
|
||||
import com.isolaatti.posting.posts.presentation.PostListingViewModelBase
|
||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||
import com.isolaatti.profile.data.remote.UserProfileDto
|
||||
import com.isolaatti.profile.domain.ProfileRepository
|
||||
import com.isolaatti.profile.domain.use_case.GetProfile
|
||||
@ -24,14 +26,13 @@ import java.time.Month
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetProfile, private val getProfilePosts: GetProfilePosts) : ViewModel() {
|
||||
class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetProfile, private val getProfilePostsUseCase: GetProfilePosts) : PostListingViewModelBase() {
|
||||
private val _profile = MutableLiveData<UserProfileDto>()
|
||||
val profile: LiveData<UserProfileDto> get() = _profile
|
||||
|
||||
private val _posts = MutableLiveData<FeedDto>()
|
||||
val posts: LiveData<FeedDto> get() = _posts
|
||||
var profileId: Int = 0
|
||||
|
||||
fun getProfile(profileId: Int) {
|
||||
fun getProfile() {
|
||||
viewModelScope.launch {
|
||||
getProfileUseCase(profileId).onEach {
|
||||
if(it is Resource.Success) {
|
||||
@ -41,24 +42,23 @@ class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetPro
|
||||
}
|
||||
}
|
||||
|
||||
fun getPosts(profileId: Int, refresh: Boolean) {
|
||||
override fun getFeed(refresh: Boolean) {
|
||||
viewModelScope.launch {
|
||||
getProfilePosts(
|
||||
profileId,
|
||||
-1,
|
||||
false,
|
||||
FeedFilterDto(
|
||||
"both",
|
||||
"both",
|
||||
FeedFilterDto.DataRange(
|
||||
false,
|
||||
LocalDate.of(2020, 1, 1),
|
||||
LocalDate.now()
|
||||
)
|
||||
)
|
||||
).onEach {
|
||||
if(it is Resource.Success) {
|
||||
_posts.postValue(it.data!!)
|
||||
getProfilePostsUseCase(profileId, getLastId(), false, null).onEach { feedDtoResource ->
|
||||
when (feedDtoResource) {
|
||||
is Resource.Success -> {
|
||||
loadingPosts.postValue(false)
|
||||
posts.postValue(Pair(posts.value?.first?.concatFeed(feedDtoResource.data) ?: feedDtoResource.data, UpdateEvent(UpdateEvent.UpdateType.PAGE_ADDED, null)))
|
||||
noMoreContent.postValue(feedDtoResource.data?.moreContent == false)
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
loadingPosts.postValue(true)
|
||||
}
|
||||
|
||||
is Resource.Error -> {
|
||||
errorLoading.postValue(feedDtoResource.errorType)
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
|
||||
@ -1,29 +1,36 @@
|
||||
package com.isolaatti.profile.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.viewModels
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.isolaatti.BuildConfig
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.databinding.FragmentDiscussionsBinding
|
||||
import com.isolaatti.home.FeedFragment
|
||||
import com.isolaatti.posting.PostViewerActivity
|
||||
import com.isolaatti.posting.comments.presentation.BottomSheetPostComments
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.posts.presentation.PostListingRecyclerViewAdapterWiring
|
||||
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
|
||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||
import com.isolaatti.profile.data.remote.UserProfileDto
|
||||
import com.isolaatti.profile.presentation.ProfileViewModel
|
||||
import com.isolaatti.utils.PicassoImagesPluginDef
|
||||
import com.isolaatti.utils.UrlGen
|
||||
import com.squareup.picasso.Picasso
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.noties.markwon.AbstractMarkwonPlugin
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.MarkwonConfiguration
|
||||
@ -31,14 +38,19 @@ import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAb
|
||||
import io.noties.markwon.linkify.LinkifyPlugin
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DiscussionsFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
class DiscussionsFragment : Fragment() {
|
||||
lateinit var viewBinding: FragmentDiscussionsBinding
|
||||
|
||||
private val viewModel: ProfileViewModel by viewModels()
|
||||
val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
|
||||
private var userId: Int? = null
|
||||
|
||||
lateinit var postsAdapter: PostsRecyclerViewAdapter
|
||||
|
||||
// collapsing bar
|
||||
private var title = ""
|
||||
lateinit var feedAdapter: PostsRecyclerViewAdapter
|
||||
private var scrollRange = -1
|
||||
private var isShow = false
|
||||
|
||||
|
||||
private val profileObserver = Observer<UserProfileDto> { profile ->
|
||||
Picasso.get()
|
||||
@ -50,30 +62,18 @@ class DiscussionsFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
viewBinding.textViewDescription.text = profile.descriptionText
|
||||
}
|
||||
|
||||
private val postsObserver: Observer<FeedDto> = Observer {
|
||||
feedAdapter.updateList(it, null)
|
||||
private val postsObserver: Observer<Pair<FeedDto?, UpdateEvent>> = Observer {
|
||||
if(it.first != null) {
|
||||
postsAdapter.updateList(it.first!!, it.second)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
userId = (requireActivity()).intent.extras?.getInt(ProfileActivity.EXTRA_USER_ID)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
viewBinding = FragmentDiscussionsBinding.inflate(inflater)
|
||||
private lateinit var postListingRecyclerViewAdapterWiring: PostListingRecyclerViewAdapterWiring
|
||||
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
var scrollRange = -1
|
||||
var isShow = false
|
||||
private fun setupCollapsingBar() {
|
||||
|
||||
viewBinding.topAppBarLayout.addOnOffsetChangedListener { appBarLayout, verticalOffset ->
|
||||
if (scrollRange == -1) scrollRange = appBarLayout.totalScrollRange
|
||||
if (scrollRange + verticalOffset == 0) {
|
||||
@ -83,38 +83,16 @@ class DiscussionsFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
viewBinding.collapsingToolbarLayout.title = " "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bind() {
|
||||
|
||||
|
||||
viewBinding.topAppBar.setNavigationOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
|
||||
viewModel.profile.observe(viewLifecycleOwner, profileObserver)
|
||||
viewModel.posts.observe(viewLifecycleOwner, postsObserver)
|
||||
|
||||
userId?.let {
|
||||
viewModel.getProfile(it)
|
||||
viewModel.getPosts(it, true)
|
||||
}
|
||||
|
||||
val markwon = Markwon.builder(requireContext())
|
||||
.usePlugin(object: AbstractMarkwonPlugin() {
|
||||
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
|
||||
builder
|
||||
.imageDestinationProcessor(
|
||||
ImageDestinationProcessorRelativeToAbsolute
|
||||
.create(BuildConfig.backend))
|
||||
}
|
||||
})
|
||||
.usePlugin(PicassoImagesPluginDef.picassoImagePlugin)
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
|
||||
feedAdapter = PostsRecyclerViewAdapter(markwon, this, null)
|
||||
|
||||
viewBinding.feedRecyclerView.adapter = feedAdapter
|
||||
viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
viewBinding.bottomAppBar.setOnMenuItemClickListener {
|
||||
when(it.itemId) {
|
||||
R.id.audios_menu_item -> {
|
||||
@ -129,29 +107,88 @@ class DiscussionsFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
}
|
||||
}
|
||||
|
||||
viewBinding.feedRecyclerView.adapter = postsAdapter
|
||||
viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||
}
|
||||
|
||||
override fun onLiked(postId: Long) {
|
||||
TODO("Not yet implemented")
|
||||
private fun setObservers() {
|
||||
viewModel.profile.observe(viewLifecycleOwner, profileObserver)
|
||||
viewModel.posts.observe(viewLifecycleOwner, postsObserver)
|
||||
}
|
||||
|
||||
override fun onUnLiked(postId: Long) {
|
||||
TODO("Not yet implemented")
|
||||
private fun getData() {
|
||||
|
||||
userId?.let { profileId ->
|
||||
viewModel.profileId = profileId
|
||||
viewModel.getProfile()
|
||||
viewModel.getFeed(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPostsAdapter() {
|
||||
val markwon = Markwon.builder(requireContext())
|
||||
.usePlugin(object: AbstractMarkwonPlugin() {
|
||||
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
|
||||
builder
|
||||
.imageDestinationProcessor(
|
||||
ImageDestinationProcessorRelativeToAbsolute
|
||||
.create(BuildConfig.backend))
|
||||
}
|
||||
})
|
||||
.usePlugin(PicassoImagesPluginDef.picassoImagePlugin)
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
|
||||
postsAdapter = PostsRecyclerViewAdapter(markwon,postListingRecyclerViewAdapterWiring, null )
|
||||
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
userId = (requireActivity()).intent.extras?.getInt(ProfileActivity.EXTRA_USER_ID)
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
postListingRecyclerViewAdapterWiring = object: PostListingRecyclerViewAdapterWiring(viewModel) {
|
||||
override fun onComment(postId: Long) {
|
||||
TODO("Not yet implemented")
|
||||
val modalBottomSheet = BottomSheetPostComments.getInstance(postId)
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostComments.TAG)
|
||||
}
|
||||
|
||||
override fun onOpenPost(postId: Long) {
|
||||
TODO("Not yet implemented")
|
||||
PostViewerActivity.startActivity(requireContext(), postId)
|
||||
}
|
||||
|
||||
override fun onOptions(postId: Long) {
|
||||
TODO("Not yet implemented")
|
||||
optionsViewModel.setOptions(Options.postOptions, FeedFragment.CALLER_ID)
|
||||
val modalBottomSheet = BottomSheetPostOptionsFragment()
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG)
|
||||
}
|
||||
|
||||
override fun onProfileClick(userId: Int) {
|
||||
TODO("Not yet implemented")
|
||||
//ProfileActivity.startActivity(requireContext(), userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
viewBinding = FragmentDiscussionsBinding.inflate(inflater)
|
||||
|
||||
return viewBinding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupCollapsingBar()
|
||||
setupPostsAdapter()
|
||||
bind()
|
||||
setObservers()
|
||||
getData()
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContentProviderCompat.requireContext
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.findNavController
|
||||
@ -45,7 +46,6 @@ class ProfileActivity : FragmentActivity() {
|
||||
|
||||
lateinit var viewBinding: ActivityProfileBinding
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewBinding = ActivityProfileBinding.inflate(layoutInflater)
|
||||
|
||||
@ -1,8 +1,24 @@
|
||||
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 com.isolaatti.databinding.FragmentUserlinkBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UserLinkFragment : Fragment() {
|
||||
lateinit var binding: FragmentUserlinkBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentUserlinkBinding.inflate(inflater)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<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/topAppBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/audios"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
app:title="@string/audios"
|
||||
app:navigationIcon="@drawable/baseline_arrow_back_24"
|
||||
app:navigationIconTint="@color/on_surface"
|
||||
app:titleCentered="true"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@ -44,9 +44,10 @@
|
||||
android:id="@+id/feed_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
|
||||
@ -1,22 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<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">
|
||||
|
||||
<ScrollView
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/topAppBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="2000dp"
|
||||
android:text="@string/images"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/images"
|
||||
app:navigationIcon="@drawable/baseline_arrow_back_24"
|
||||
app:navigationIconTint="@color/on_surface"
|
||||
app:titleCentered="true"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
6
app/src/main/res/layout/fragment_userlink.xml
Normal file
6
app/src/main/res/layout/fragment_userlink.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Loading…
x
Reference in New Issue
Block a user