WIP posts en compose
This commit is contained in:
parent
5775145a69
commit
37837653cc
@ -101,8 +101,11 @@ dependencies {
|
||||
// Customtabs
|
||||
implementation 'androidx.browser:browser:1.8.0'
|
||||
|
||||
implementation 'io.coil-kt:coil:2.5.0'
|
||||
implementation 'io.coil-kt:coil-svg:2.5.0'
|
||||
// Coil
|
||||
implementation 'io.coil-kt.coil3:coil:3.0.4'
|
||||
implementation 'io.coil-kt.coil3:coil-svg:3.0.4'
|
||||
implementation "io.coil-kt.coil3:coil-compose:3.0.1"
|
||||
implementation("io.coil-kt.coil3:coil-network-okhttp:3.0.1")
|
||||
|
||||
|
||||
implementation "io.noties.markwon:core:$markwon_version"
|
||||
@ -156,8 +159,7 @@ dependencies {
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5'
|
||||
implementation 'androidx.compose.runtime:runtime-livedata'
|
||||
|
||||
implementation "io.coil-kt.coil3:coil-compose:3.0.1"
|
||||
implementation("io.coil-kt.coil3:coil-network-okhttp:3.0.1")
|
||||
|
||||
|
||||
implementation("com.google.accompanist:accompanist-permissions:0.36.0")
|
||||
}
|
||||
@ -1,35 +1,19 @@
|
||||
package com.isolaatti.common
|
||||
|
||||
import android.content.Context
|
||||
import coil.ImageLoader
|
||||
import coil.decode.SvgDecoder
|
||||
import coil.memory.MemoryCache
|
||||
import coil3.ImageLoader
|
||||
import com.isolaatti.MyApplication
|
||||
|
||||
object CoilImageLoader {
|
||||
val imageLoader by lazy {
|
||||
ImageLoader
|
||||
.Builder(MyApplication.myApp)
|
||||
.memoryCache {
|
||||
MemoryCache.Builder(MyApplication.myApp.applicationContext)
|
||||
.maxSizePercent(0.25)
|
||||
.build()
|
||||
}
|
||||
.components {
|
||||
add(SvgDecoder.Factory())
|
||||
}.build()
|
||||
.build()
|
||||
}
|
||||
|
||||
fun getImageLoader(context: Context): ImageLoader {
|
||||
return ImageLoader
|
||||
.Builder(context)
|
||||
.memoryCache {
|
||||
MemoryCache.Builder(context)
|
||||
.maxSizePercent(0.25)
|
||||
.build()
|
||||
}
|
||||
.components {
|
||||
add(SvgDecoder.Factory())
|
||||
}.build()
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ 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 coil3.load
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||
import com.isolaatti.databinding.ItemUserListBinding
|
||||
|
||||
@ -5,9 +5,6 @@ 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.domain.entity.UserProfile
|
||||
import com.isolaatti.profile.domain.use_case.GetProfile
|
||||
import com.isolaatti.utils.Resource
|
||||
|
||||
@ -20,7 +20,7 @@ import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.about.AboutActivity
|
||||
|
||||
@ -4,8 +4,9 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.BundleCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||
import com.isolaatti.databinding.FragmentTouchImageViewWrapperBinding
|
||||
import com.isolaatti.images.common.domain.entity.RemoteImage
|
||||
@ -24,7 +25,7 @@ class PictureViewerImageWrapperFragment : Fragment() {
|
||||
): View {
|
||||
binding = FragmentTouchImageViewWrapperBinding.inflate(inflater)
|
||||
|
||||
image = arguments?.getSerializable(ARGUMENT_IMAGE) as RemoteImage
|
||||
image = BundleCompat.getParcelable(requireArguments(), ARGUMENT_IMAGE, RemoteImage::class.java)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.IntentCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.isolaatti.databinding.FragmentMainPictureViewerBinding
|
||||
@ -36,7 +37,7 @@ class PictureViewerMainFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
images = requireActivity().intent.extras?.getSerializable(PictureViewerActivity.EXTRA_IMAGES) as Array<RemoteImage>
|
||||
images = IntentCompat.getParcelableArrayExtra(requireActivity().intent, PictureViewerActivity.EXTRA_IMAGES, RemoteImage::class.java) as Array<RemoteImage>
|
||||
val position = requireActivity().intent.extras?.getInt(PictureViewerActivity.EXTRA_IMAGE_POSITiON) ?: 0
|
||||
val adapter = PictureViewerViewPagerAdapter(this, images)
|
||||
binding.viewpager.adapter = adapter
|
||||
|
||||
@ -31,7 +31,6 @@ class Module {
|
||||
.create(BuildConfig.backend))
|
||||
}
|
||||
})
|
||||
.usePlugin(CoilImagesPlugin.create(context, CoilImageLoader.imageLoader))
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
}
|
||||
|
||||
@ -6,7 +6,8 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import coil3.request.fallback
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.databinding.NotificationItemBinding
|
||||
import com.isolaatti.notifications.domain.FollowNotification
|
||||
|
||||
@ -4,7 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||
import com.isolaatti.databinding.CommentLayoutBinding
|
||||
import com.isolaatti.posting.comments.domain.model.Comment
|
||||
|
||||
@ -217,7 +217,6 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
|
||||
.create("https://isolaatti.com/"))
|
||||
}
|
||||
})
|
||||
.usePlugin(CoilImagesPlugin.create(requireContext(), imageLoader))
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import android.view.ViewGroup
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||
import com.isolaatti.databinding.FragmentEditCommentBinding
|
||||
@ -51,7 +51,7 @@ class EditCommentDialogFragment : DialogFragment() {
|
||||
.create("https://isolaatti.com/"))
|
||||
}
|
||||
})
|
||||
.usePlugin(CoilImagesPlugin.create(requireContext(), imageLoader))
|
||||
//.usePlugin(CoilImagesPlugin.create(requireContext(), imageLoader))
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
|
||||
|
||||
@ -4,25 +4,22 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
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.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -31,11 +28,14 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.images.common.domain.entity.Image
|
||||
import com.isolaatti.images.common.domain.entity.RemoteImage
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.utils.UrlGen.userProfileImage
|
||||
|
||||
@ -44,13 +44,17 @@ fun Post(
|
||||
post: Post,
|
||||
onClick: () -> Unit = {},
|
||||
onLike: () -> Unit = {},
|
||||
onDislike: () -> Unit = {},
|
||||
onComments: () -> Unit = {},
|
||||
onShare: () -> Unit = {},
|
||||
onInfo: () -> Unit = {},
|
||||
onOptions: () -> Unit = {},
|
||||
onUsernameClick: () -> Unit = {}
|
||||
onUsernameClick: () -> Unit = {},
|
||||
onImageClick: (images: List<RemoteImage>, index: Int) -> Unit = {_, _ -> },
|
||||
onHashtagClick: (hashtag: String) -> Unit = {},
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Card {
|
||||
Card(modifier = modifier.padding(8.dp)) {
|
||||
Row(modifier = Modifier.fillMaxWidth().padding(8.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
AsyncImage(
|
||||
model = userProfileImage(post.userId),
|
||||
@ -78,37 +82,80 @@ fun Post(
|
||||
// TODO audio player here
|
||||
}
|
||||
|
||||
Text(post.textContent, modifier = Modifier.padding(8.dp))
|
||||
if(post.textContent.isNotBlank()) {
|
||||
Text(post.textContent, modifier = Modifier.padding(8.dp))
|
||||
}
|
||||
|
||||
// TODO pager
|
||||
if(post.images.isNotEmpty()) {
|
||||
Box(Modifier.fillMaxWidth().aspectRatio(1f))
|
||||
Box {
|
||||
val pagerState = rememberPagerState(pageCount = {post.images.size})
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
) {
|
||||
val image = post.images[it]
|
||||
AsyncImage(
|
||||
image.reducedImageUrl, null,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(8.dp)
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.clickable {
|
||||
onImageClick(post.images, it)
|
||||
}
|
||||
)
|
||||
}
|
||||
if(post.images.size > 1) {
|
||||
Text("${pagerState.currentPage + 1} of ${pagerState.pageCount}",
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(20.dp)
|
||||
.background(Color.Black.copy(alpha = 0.5f)),
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row(modifier = Modifier.height(50.dp).padding(4.dp)) {
|
||||
FilledTonalIconButton(onClick = onLike, modifier = Modifier.padding(4.dp).weight(1f)) {
|
||||
FilledTonalIconButton(
|
||||
onClick = { if(post.liked) { onDislike() } else { onLike() } },
|
||||
modifier = Modifier.padding(4.dp).weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(painterResource(R.drawable.hands_clapping_solid), null, modifier = Modifier.size(30.dp))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Clap")
|
||||
Text(post.numberOfLikes.toString())
|
||||
}
|
||||
Icon(
|
||||
painterResource(R.drawable.hands_clapping_solid),
|
||||
null,
|
||||
modifier = Modifier.padding(horizontal = 8.dp).size(30.dp),
|
||||
tint = if(post.liked) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Text(
|
||||
text = post.numberOfLikes.toString(),
|
||||
modifier = Modifier.weight(1f).padding(horizontal = 4.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
FilledTonalIconButton(onClick = onComments, modifier = Modifier.padding(4.dp).weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(painterResource(R.drawable.comments_solid), null, modifier = Modifier.size(30.dp))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Comments")
|
||||
Text(post.numberOfComments.toString())
|
||||
}
|
||||
Icon(
|
||||
painterResource(R.drawable.comments_solid),
|
||||
null,
|
||||
modifier = Modifier.padding(horizontal = 8.dp).size(30.dp)
|
||||
)
|
||||
Text(
|
||||
text = post.numberOfComments.toString(),
|
||||
modifier = Modifier.weight(1f).padding(horizontal = 4.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
FilledTonalIconButton(onClick = onShare, modifier = Modifier.padding(4.dp).weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(painterResource(R.drawable.baseline_share_24), null, modifier = Modifier.size(30.dp))
|
||||
Text("Share")
|
||||
}
|
||||
FilledTonalIconButton(onClick = onShare, modifier = Modifier.padding(4.dp)) {
|
||||
Icon(
|
||||
painterResource(R.drawable.baseline_share_24),
|
||||
null,
|
||||
)
|
||||
}
|
||||
FilledTonalIconButton(onClick = onInfo, modifier = Modifier.padding(4.dp)) {
|
||||
Icon(painterResource(R.drawable.baseline_info_24), null)
|
||||
|
||||
@ -2,28 +2,39 @@ package com.isolaatti.posting.posts.domain.entity
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.isolaatti.audio.common.domain.Audio
|
||||
import com.isolaatti.common.Ownable
|
||||
import com.isolaatti.common.hashtagRegex
|
||||
import com.isolaatti.images.common.domain.entity.RemoteImage
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
|
||||
data class Post(
|
||||
class Post(
|
||||
val id: Long,
|
||||
var textContent: String,
|
||||
textContent: String,
|
||||
override val userId: Int,
|
||||
val privacy: Int,
|
||||
val date: String,
|
||||
var audioId: String? = null,
|
||||
val squadId: String? = null,
|
||||
var numberOfLikes: Int,
|
||||
var numberOfComments: Int,
|
||||
numberOfLikes: Int,
|
||||
numberOfComments: Int,
|
||||
val userName: String,
|
||||
val squadName: String? = null,
|
||||
var liked: Boolean,
|
||||
liked: Boolean,
|
||||
val audio: Audio? = null,
|
||||
val images: List<RemoteImage>
|
||||
images: List<RemoteImage>
|
||||
) : Ownable, Parcelable {
|
||||
|
||||
var liked by mutableStateOf(liked)
|
||||
var images by mutableStateOf(images)
|
||||
var textContent by mutableStateOf(textContent)
|
||||
var numberOfLikes by mutableIntStateOf(numberOfLikes)
|
||||
var numberOfComments by mutableStateOf(numberOfComments)
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readLong(),
|
||||
parcel.readString()!!,
|
||||
|
||||
@ -4,7 +4,7 @@ import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.isolaatti.images.common.domain.entity.RemoteImage
|
||||
|
||||
class PostImagesViewPagerAdapter(private val images: List<RemoteImage>) :
|
||||
|
||||
@ -16,17 +16,13 @@ class PostListingViewModel @Inject constructor(private val postsRepository: Post
|
||||
override fun getFeed(refresh: Boolean, hashtag: String?) {
|
||||
viewModelScope.launch {
|
||||
if (refresh) {
|
||||
posts.value = null
|
||||
posts.value = emptyList()
|
||||
}
|
||||
postsRepository.getFeed(getLastId(), hashtag).onEach { listResource ->
|
||||
when (listResource) {
|
||||
is Resource.Success -> {
|
||||
val eventType = if((postsList?.size ?: 0) > 0) UpdateEvent.UpdateType.PAGE_ADDED else UpdateEvent.UpdateType.REFRESH
|
||||
loadingPosts.postValue(false)
|
||||
posts.postValue(Pair(postsList?.apply {
|
||||
addAll(listResource.data ?: listOf())
|
||||
} ?: listResource.data,
|
||||
UpdateEvent(eventType, null)))
|
||||
posts.value += listResource.data ?: emptyList()
|
||||
|
||||
noMoreContent.postValue(listResource.data?.size == 0)
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import com.isolaatti.posting.posts.domain.use_case.DeletePost
|
||||
import com.isolaatti.profile.domain.use_case.GetProfilePosts
|
||||
import com.isolaatti.utils.Resource
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -30,9 +31,8 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
@Inject
|
||||
lateinit var deletePostUseCase: DeletePost
|
||||
|
||||
val posts: MutableLiveData<Pair<MutableList<Post>?, UpdateEvent>?> = MutableLiveData()
|
||||
val posts: MutableStateFlow<List<Post>> = MutableStateFlow(emptyList())
|
||||
|
||||
val postsList get() = posts.value?.first
|
||||
|
||||
val loadingPosts = MutableLiveData(false)
|
||||
|
||||
@ -41,7 +41,7 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
val errorLoading: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||
var isLoadingFromScrolling = false
|
||||
|
||||
fun getLastId(): Long = try { posts.value?.first?.last()?.id ?: 0 } catch (e: NoSuchElementException) { 0 }
|
||||
fun getLastId(): Long = try { posts.value?.last()?.id ?: 0 } catch (e: NoSuchElementException) { 0 }
|
||||
|
||||
|
||||
abstract fun getFeed(refresh: Boolean, hashtag: String?)
|
||||
@ -58,18 +58,9 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
Log.i(TAG, "Loading likePost($postId)")
|
||||
}
|
||||
is Resource.Success -> {
|
||||
val likedPost = posts.value?.first?.find { post -> post.id == like.data?.postId }
|
||||
val index = posts.value?.first?.indexOf(likedPost)
|
||||
if(index != null){
|
||||
val temp = posts.value?.first?.toMutableList()
|
||||
Log.d("***", temp.toString())
|
||||
temp?.set(index, likedPost!!.apply {
|
||||
liked = true
|
||||
numberOfLikes = like.data?.likesCount ?: 0
|
||||
|
||||
})
|
||||
}
|
||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, index)))
|
||||
val likedPost = posts.value?.find { post -> post.id == like.data?.postId }
|
||||
likedPost?.liked = true
|
||||
likedPost?.numberOfLikes = like.data!!.likesCount
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,16 +81,9 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
Log.i(TAG, "Loading unLikePost($postId)")
|
||||
}
|
||||
is Resource.Success -> {
|
||||
val likedPost = posts.value?.first?.find { post -> post.id == like.data?.postId }
|
||||
val index = posts.value?.first?.indexOf(likedPost)
|
||||
if(index != null){
|
||||
val temp = posts.value?.first?.toMutableList()
|
||||
temp?.set(index, likedPost!!.apply {
|
||||
liked = false
|
||||
numberOfLikes = like.data?.likesCount ?: 0
|
||||
})
|
||||
}
|
||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, index)))
|
||||
val likedPost = posts.value?.find { post -> post.id == like.data?.postId }
|
||||
likedPost?.liked = false
|
||||
likedPost?.numberOfLikes = like.data!!.likesCount
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,12 +97,9 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
deletePostUseCase(postId).onEach { res ->
|
||||
when(res) {
|
||||
is Resource.Success -> {
|
||||
val postDeleted = posts.value?.first?.find { post -> post.id == postId }
|
||||
val postDeleted = posts.value?.find { post -> post.id == postId }
|
||||
?: return@onEach
|
||||
val index = posts.value?.first?.indexOf(postDeleted)
|
||||
|
||||
posts.value?.first?.removeAt(index!!)
|
||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_REMOVED, index)))
|
||||
posts.value = posts.value.toMutableList().apply { remove(postDeleted) }
|
||||
}
|
||||
is Resource.Loading -> {}
|
||||
is Resource.Error -> {}
|
||||
|
||||
@ -1,402 +0,0 @@
|
||||
package com.isolaatti.posting.posts.presentation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.SpannableString
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.text.set
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import coil.load
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.audio.common.domain.Audio
|
||||
import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||
import com.isolaatti.common.OnUserInteractedWithPostCallback
|
||||
import com.isolaatti.databinding.PostLayoutBinding
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.utils.UrlGen.userProfileImage
|
||||
|
||||
|
||||
class PostsRecyclerViewAdapter (
|
||||
private val callback: OnUserInteractedWithPostCallback
|
||||
) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){
|
||||
private var postList: List<Post>? = null
|
||||
inner class FeedViewHolder(val itemBinding: PostLayoutBinding) : ViewHolder(itemBinding.root) {
|
||||
fun bindView(post: Post, payloads: List<Any>) {
|
||||
|
||||
if(payloads.isNotEmpty()) {
|
||||
for(payload in payloads) {
|
||||
when {
|
||||
payload is LikeCountUpdatePayload -> {
|
||||
itemBinding.likeButton.isEnabled = true
|
||||
|
||||
if(post.liked) {
|
||||
itemBinding.likeButton.setIconTintResource(R.color.purple_lighter)
|
||||
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.purple_lighter))
|
||||
} else {
|
||||
itemBinding.likeButton.setIconTintResource(R.color.on_surface)
|
||||
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.on_surface))
|
||||
}
|
||||
|
||||
itemBinding.likeButton.text = post.numberOfLikes.toString()
|
||||
}
|
||||
payload is CommentsCountUpdatePayload -> {
|
||||
itemBinding.commentButton.text = post.numberOfComments.toString()
|
||||
}
|
||||
payload is AudioEventPayload && payload == AudioEventPayload.IsPLaying -> {
|
||||
val audio = post.audio
|
||||
if(audio != null){
|
||||
itemBinding.audio.playButton.icon =
|
||||
AppCompatResources.getDrawable(
|
||||
itemView.context,
|
||||
if(audio.isPlaying) R.drawable.baseline_pause_circle_24 else R.drawable.baseline_play_circle_24
|
||||
)
|
||||
}
|
||||
}
|
||||
payload is AudioEventPayload && payload == AudioEventPayload.ProgressChanged -> {
|
||||
val audio = post.audio
|
||||
if(audio != null){
|
||||
itemBinding.audio.audioProgress.progress = audio.progress
|
||||
}
|
||||
}
|
||||
payload is AudioEventPayload && payload == AudioEventPayload.IsLoading -> {
|
||||
val audio = post.audio
|
||||
if(audio != null){
|
||||
itemBinding.audio.audioProgress.isIndeterminate = audio.isLoading
|
||||
}
|
||||
}
|
||||
payload is AudioEventPayload && payload == AudioEventPayload.DurationChanged -> {
|
||||
val audio = post.audio
|
||||
if(audio != null){
|
||||
itemBinding.audio.audioProgress.max = audio.duration
|
||||
}
|
||||
}
|
||||
payload is AudioEventPayload && payload == AudioEventPayload.Ended -> {
|
||||
val audio = post.audio
|
||||
if(audio != null){
|
||||
itemBinding.audio.audioProgress.progress = 0
|
||||
itemBinding.audio.playButton.icon = AppCompatResources.getDrawable(itemView.context, R.drawable.baseline_play_circle_24)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
val username: TextView = itemView.findViewById(R.id.text_view_username)
|
||||
username.text = post.userName
|
||||
username.setOnClickListener {
|
||||
callback.onProfileClick(post.userId)
|
||||
}
|
||||
|
||||
val profileImageView: ImageView = itemView.findViewById(R.id.avatar_picture)
|
||||
profileImageView.load(userProfileImage(post.userId), imageLoader)
|
||||
|
||||
val dateTextView: TextView = itemView.findViewById(R.id.text_view_date)
|
||||
dateTextView.text = post.date
|
||||
|
||||
val content: TextView = itemView.findViewById(R.id.post_content)
|
||||
content.movementMethod = LinkMovementMethod.getInstance()
|
||||
val spannableString = SpannableString(post.textContent).apply {
|
||||
post.hashtagsSpans.forEach {
|
||||
set(it.first, it.last + 1, object: ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
callback.hashtagClicked(post.textContent.substring(it.first + 1, it.last + 1))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
content.text = spannableString
|
||||
|
||||
itemBinding.likeButton.isEnabled = true
|
||||
|
||||
if(post.liked) {
|
||||
itemBinding.likeButton.setIconTintResource(R.color.purple_lighter)
|
||||
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.purple_lighter))
|
||||
} else {
|
||||
itemBinding.likeButton.setIconTintResource(R.color.on_surface)
|
||||
itemBinding.likeButton.setTextColor(itemView.context.getColor(R.color.on_surface))
|
||||
}
|
||||
|
||||
itemBinding.likeButton.text = post.numberOfLikes.toString()
|
||||
|
||||
itemBinding.commentButton.text = post.numberOfComments.toString()
|
||||
|
||||
val moreButton: MaterialButton = itemView.findViewById(R.id.more_button)
|
||||
moreButton.setOnClickListener {
|
||||
callback.onOptions(post)
|
||||
}
|
||||
|
||||
itemBinding.likeButton.setOnClickListener {
|
||||
itemBinding.likeButton.isEnabled = false
|
||||
if(post.liked){
|
||||
callback.onUnLiked(post.id)
|
||||
} else {
|
||||
callback.onLiked(post.id)
|
||||
}
|
||||
}
|
||||
itemBinding.commentButton.setOnClickListener {
|
||||
callback.onComment(post.id)
|
||||
}
|
||||
|
||||
itemView.findViewById<MaterialCardView>(R.id.card).setOnClickListener {
|
||||
callback.onOpenPost(post.id)
|
||||
}
|
||||
if(post.audio != null){
|
||||
itemBinding.audio.apply {
|
||||
root.visibility = View.VISIBLE
|
||||
textViewDescription.text = post.audio.name
|
||||
}
|
||||
itemBinding.audio.playButton.setOnClickListener {
|
||||
callback.onPlay(post.audio)
|
||||
}
|
||||
} else {
|
||||
itemBinding.audio.root.visibility = View.GONE
|
||||
itemBinding.audio.playButton.setOnClickListener(null)
|
||||
}
|
||||
|
||||
if(post.images.isNotEmpty()) {
|
||||
itemBinding.photosViewPager.isVisible = true
|
||||
itemBinding.photosViewPager.adapter = PostImagesViewPagerAdapter(post.images)
|
||||
} else {
|
||||
itemBinding.photosViewPager.isVisible = false
|
||||
itemBinding.photosViewPager.adapter = null
|
||||
}
|
||||
itemBinding.shareButton.setOnClickListener {
|
||||
callback.onShare(post.id)
|
||||
}
|
||||
itemBinding.infoButton.setOnClickListener {
|
||||
callback.onMoreInfo(post.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class LikeCountUpdatePayload(val likeCount: Int)
|
||||
data class CommentsCountUpdatePayload(val commentsCount: Int)
|
||||
|
||||
private var currentAudio: Audio? = null
|
||||
private var currentAudioPosition: Int = -1
|
||||
enum class AudioEventPayload {
|
||||
ProgressChanged, IsLoading, IsPLaying, DurationChanged, Ended
|
||||
}
|
||||
|
||||
fun setIsPlaying(isPlaying: Boolean, audio: Audio) {
|
||||
if(audio == currentAudio) {
|
||||
currentAudio?.isPlaying = isPlaying
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsPLaying)
|
||||
}
|
||||
return
|
||||
}
|
||||
currentAudio?.isPlaying = false
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsPLaying)
|
||||
} else {
|
||||
if(postList != null) {
|
||||
for((index, post) in postList!!.withIndex()){
|
||||
post.audio?.isPlaying = false
|
||||
post.audio?.progress = 0
|
||||
post.audio?.isLoading = false
|
||||
if(post.audio != null) {
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||
Log.d(LOG_TAG, "setIsPlaying currentAudioPosition: $currentAudioPosition")
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
currentAudio = postList?.get(currentAudioPosition)?.audio?.also { it.isPlaying = isPlaying }
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsPLaying)
|
||||
}
|
||||
}
|
||||
|
||||
fun setIsLoading(isLoading: Boolean, audio: Audio) {
|
||||
if(audio == currentAudio) {
|
||||
currentAudio?.isLoading = isLoading
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsLoading)
|
||||
}
|
||||
return
|
||||
}
|
||||
currentAudio?.isPlaying = false
|
||||
currentAudio?.isLoading = false
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsLoading)
|
||||
}
|
||||
|
||||
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||
|
||||
Log.d(LOG_TAG, "setIsLoading currentAudioPosition: $currentAudioPosition")
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
postList?.get(currentAudioPosition)?.audio?.isLoading = isLoading
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.IsLoading)
|
||||
}
|
||||
}
|
||||
|
||||
fun setProgress(progress: Int, audio: Audio){
|
||||
if(audio == currentAudio) {
|
||||
audio.progress = progress
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
return
|
||||
}
|
||||
currentAudio?.isPlaying = false
|
||||
currentAudio?.progress = 0
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
|
||||
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
postList?.get(currentAudioPosition)?.audio?.progress = progress
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
}
|
||||
|
||||
fun setDuration(duration: Int, audio: Audio) {
|
||||
if(audio == currentAudio) {
|
||||
audio.duration = duration
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
return
|
||||
}
|
||||
currentAudio?.isPlaying = false
|
||||
|
||||
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
postList?.get(currentAudioPosition)?.audio?.duration = duration
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
}
|
||||
|
||||
fun setEnded(audio: Audio) {
|
||||
if(audio == currentAudio) {
|
||||
audio.isPlaying = false
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
return
|
||||
}
|
||||
currentAudio?.isPlaying = false
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
|
||||
currentAudioPosition = postList?.indexOf(postList?.find { it.audio == audio }) ?: -1
|
||||
|
||||
if(currentAudioPosition > -1) {
|
||||
postList?.get(currentAudioPosition)?.audio?.isPlaying = false
|
||||
notifyItemChanged(currentAudioPosition, AudioEventPayload.ProgressChanged)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedViewHolder {
|
||||
return FeedViewHolder(PostLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
}
|
||||
|
||||
var previousSize = 0
|
||||
override fun getItemCount(): Int = postList?.size ?: 0
|
||||
|
||||
|
||||
override fun setHasStableIds(hasStableIds: Boolean) {
|
||||
super.setHasStableIds(true)
|
||||
}
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateList(updatedFeed: List<Post>, updateEvent: UpdateEvent? = null) {
|
||||
if(updateEvent == null) {
|
||||
postList = updatedFeed
|
||||
|
||||
notifyDataSetChanged()
|
||||
return
|
||||
}
|
||||
val postUpdated = updateEvent.affectedPosition?.let {
|
||||
if(updateEvent.updateType == UpdateEvent.UpdateType.POST_REMOVED) {
|
||||
null
|
||||
} else {
|
||||
postList?.get(it)
|
||||
}
|
||||
}
|
||||
val position = updateEvent.affectedPosition
|
||||
|
||||
previousSize = itemCount
|
||||
postList = 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(position != null)
|
||||
notifyItemRemoved(position)
|
||||
}
|
||||
UpdateEvent.UpdateType.POST_ADDED -> {
|
||||
notifyItemInserted(0)
|
||||
}
|
||||
|
||||
UpdateEvent.UpdateType.PAGE_ADDED -> {
|
||||
notifyItemInserted(previousSize)
|
||||
}
|
||||
UpdateEvent.UpdateType.REFRESH -> {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FeedViewHolder, position: Int) {}
|
||||
|
||||
private var requestedNewContent = false
|
||||
|
||||
/**
|
||||
* Call this method when new content has been added on onLoadMore() callback
|
||||
*/
|
||||
fun newContentRequestFinished() {
|
||||
requestedNewContent = false
|
||||
}
|
||||
override fun onBindViewHolder(holder: FeedViewHolder, position: Int, payloads: List<Any>) {
|
||||
holder.bindView(postList?.get(position) ?: return, payloads)
|
||||
val totalItems = postList?.size
|
||||
if(totalItems != null && totalItems > 0 && !requestedNewContent) {
|
||||
if(position == totalItems - 1) {
|
||||
requestedNewContent = true
|
||||
if(payloads.isEmpty()) {
|
||||
callback.onLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOG_TAG = "PostsRecyclerViewAdapter"
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
package com.isolaatti.posting.posts.presentation
|
||||
|
||||
data class UpdateEvent(val updateType: UpdateType, val affectedPosition: Int?) {
|
||||
enum class UpdateType {
|
||||
POST_LIKED,
|
||||
POST_COMMENTED,
|
||||
POST_REMOVED,
|
||||
POST_ADDED,
|
||||
PAGE_ADDED,
|
||||
REFRESH
|
||||
}
|
||||
}
|
||||
@ -1,36 +1,48 @@
|
||||
package com.isolaatti.posting.posts.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.gestures.FlingBehavior
|
||||
import androidx.compose.foundation.gestures.ScrollableDefaults
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.isolaatti.BuildConfig
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.audio.common.domain.Audio
|
||||
import com.isolaatti.common.Dialogs
|
||||
import com.isolaatti.common.ErrorMessageViewModel
|
||||
import com.isolaatti.common.IsolaattiTheme
|
||||
import com.isolaatti.common.OnUserInteractedWithPostCallback
|
||||
import com.isolaatti.common.Ownable
|
||||
import com.isolaatti.common.options_bottom_sheet.domain.OptionClicked
|
||||
import com.isolaatti.common.options_bottom_sheet.domain.Options
|
||||
import com.isolaatti.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
||||
import com.isolaatti.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
|
||||
import com.isolaatti.databinding.FragmentPostListingBinding
|
||||
import com.isolaatti.hashtags.ui.HashtagsPostsActivity
|
||||
import com.isolaatti.home.ui.FeedFragment
|
||||
import com.isolaatti.images.picture_viewer.ui.PictureViewerActivity
|
||||
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.posting.posts.presentation.EditPostContract
|
||||
import com.isolaatti.posting.posts.presentation.PostListingViewModel
|
||||
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
|
||||
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
||||
import com.isolaatti.profile.ui.ProfileActivity
|
||||
import com.isolaatti.reports.data.ContentType
|
||||
@ -38,7 +50,7 @@ import com.isolaatti.reports.ui.NewReportBottomSheetDialogFragment
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
class PostListingFragment : Fragment() {
|
||||
companion object {
|
||||
const val ARG_HASHTAG = "hashtag"
|
||||
const val LOG_TAG = "PostListingFragment"
|
||||
@ -46,11 +58,9 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
|
||||
var hashtag: String? = null
|
||||
|
||||
private lateinit var viewBinding: FragmentPostListingBinding
|
||||
private val errorViewModel: ErrorMessageViewModel by activityViewModels()
|
||||
private val viewModel: PostListingViewModel by viewModels()
|
||||
val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
|
||||
private lateinit var adapter: PostsRecyclerViewAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -59,43 +69,75 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
viewModel.getFeed(false, hashtag)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
viewBinding = FragmentPostListingBinding.inflate(inflater, container, false)
|
||||
return viewBinding.root
|
||||
return ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val posts by viewModel.posts.collectAsState(emptyList())
|
||||
IsolaattiTheme {
|
||||
PullToRefreshBox(isRefreshing, onRefresh = {viewModel.getFeed(true, null)}) {
|
||||
LazyColumn(flingBehavior = ScrollableDefaults.flingBehavior()) {
|
||||
items(count = posts.size, key = {posts[it].id}) {
|
||||
val post = posts[it]
|
||||
com.isolaatti.posting.posts.components.Post(
|
||||
modifier = Modifier.animateItem(),
|
||||
post = post,
|
||||
onClick = {
|
||||
PostViewerActivity.startActivity(requireContext(), post.id)
|
||||
},
|
||||
onComments = {
|
||||
val modalBottomSheet = BottomSheetPostComments.getInstance(post.id)
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostComments.TAG)
|
||||
},
|
||||
onOptions = {
|
||||
optionsViewModel.setOptions(Options.POST_OPTIONS, FeedFragment.CALLER_ID, post)
|
||||
val modalBottomSheet = BottomSheetPostOptionsFragment()
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG)
|
||||
},
|
||||
onInfo = {
|
||||
PostInfoActivity.startActivity(requireContext(), post.id)
|
||||
},
|
||||
onLike = {
|
||||
viewModel.likePost(post.id)
|
||||
},
|
||||
onDislike = {
|
||||
viewModel.unLikePost(post.id)
|
||||
},
|
||||
onShare = {
|
||||
val intent = Intent.createChooser(Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, "${BuildConfig.backend}/pub/${post.id}")
|
||||
type = "text/plain"
|
||||
}, getString(R.string.share_post))
|
||||
startActivity(intent)
|
||||
},
|
||||
onImageClick = { images, index ->
|
||||
PictureViewerActivity.startActivityWithImages(requireContext(), images.toTypedArray(), index)
|
||||
},
|
||||
onUsernameClick = {
|
||||
ProfileActivity.startActivity(requireContext(), post.userId)
|
||||
},
|
||||
onHashtagClick = { hashtag ->
|
||||
HashtagsPostsActivity.startActivity(requireContext(), hashtag)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
adapter = PostsRecyclerViewAdapter(this)
|
||||
viewBinding.feedRecyclerView.adapter = adapter
|
||||
viewBinding.feedRecyclerView.setItemViewCacheSize(7)
|
||||
viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
|
||||
viewBinding.swipeToRefresh.setOnRefreshListener {
|
||||
viewModel.getFeed(refresh = true, hashtag)
|
||||
}
|
||||
|
||||
viewModel.posts.observe(viewLifecycleOwner){
|
||||
if (it?.first != null) {
|
||||
adapter.updateList(it.first!!, it.second)
|
||||
adapter.newContentRequestFinished()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.loadingPosts.observe(viewLifecycleOwner) {
|
||||
viewBinding.loadingIndicator.visibility = if(it) View.VISIBLE else View.GONE
|
||||
if(!it) {
|
||||
viewBinding.swipeToRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
viewModel.errorLoading.observe(viewLifecycleOwner) {
|
||||
errorViewModel.error.postValue(it)
|
||||
}
|
||||
@ -103,19 +145,17 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
|
||||
}
|
||||
|
||||
/*
|
||||
override fun onLiked(postId: Long) = viewModel.likePost(postId)
|
||||
|
||||
override fun onUnLiked(postId: Long) = viewModel.unLikePost(postId)
|
||||
|
||||
override fun onOptions(post: Ownable) {
|
||||
optionsViewModel.setOptions(Options.POST_OPTIONS, FeedFragment.CALLER_ID, post)
|
||||
val modalBottomSheet = BottomSheetPostOptionsFragment()
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG)
|
||||
|
||||
}
|
||||
|
||||
override fun onComment(postId: Long) {
|
||||
val modalBottomSheet = BottomSheetPostComments.getInstance(postId)
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostComments.TAG)
|
||||
|
||||
}
|
||||
|
||||
override fun onOpenPost(postId: Long) {
|
||||
@ -126,7 +166,7 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
}
|
||||
|
||||
override fun onMoreInfo(postId: Long) {
|
||||
PostInfoActivity.startActivity(requireContext(), postId)
|
||||
|
||||
}
|
||||
|
||||
override fun onShare(postId: Long) {
|
||||
@ -150,6 +190,8 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
viewModel.getFeed(false, hashtag)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
private val editDiscussion = registerForActivityResult(EditPostContract()) {
|
||||
if(it != null) {
|
||||
viewModel.onPostUpdate(it)
|
||||
|
||||
@ -60,10 +60,10 @@ class PostViewerViewModel @Inject constructor(private val loadSinglePost: LoadSi
|
||||
}
|
||||
|
||||
private fun updateLikesCount(likesCount: Int) {
|
||||
val updatedPost = post.value?.copy(numberOfLikes = likesCount)
|
||||
if(updatedPost != null) {
|
||||
post.postValue(updatedPost!!)
|
||||
}
|
||||
// val updatedPost = post.value?.copy(numberOfLikes = likesCount)
|
||||
// if(updatedPost != null) {
|
||||
// post.postValue(updatedPost!!)
|
||||
// }
|
||||
}
|
||||
|
||||
fun likeDislikePost() {
|
||||
|
||||
@ -8,8 +8,8 @@ import android.util.Log
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.app.TaskStackBuilder
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import coil.imageLoader
|
||||
import coil.load
|
||||
import coil3.imageLoader
|
||||
import coil3.load
|
||||
import com.isolaatti.BuildConfig
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.common.IsolaattiBaseActivity
|
||||
@ -153,7 +153,6 @@ class PostViewerActivity : IsolaattiBaseActivity() {
|
||||
.create(BuildConfig.backend))
|
||||
}
|
||||
})
|
||||
.usePlugin(CoilImagesPlugin.create(this, imageLoader))
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
package com.isolaatti.profile.presentation
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.followers.domain.FollowingState
|
||||
import com.isolaatti.images.common.domain.entity.RemoteImage
|
||||
import com.isolaatti.posting.posts.presentation.PostListingViewModelBase
|
||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||
import com.isolaatti.profile.domain.entity.UserProfile
|
||||
import com.isolaatti.profile.domain.use_case.FollowUser
|
||||
import com.isolaatti.profile.domain.use_case.GetProfile
|
||||
@ -116,14 +116,14 @@ class ProfileViewModel @Inject constructor(
|
||||
override fun getFeed(refresh: Boolean, hashtag: String?) {
|
||||
viewModelScope.launch {
|
||||
if(refresh) {
|
||||
posts.value = Pair(null, UpdateEvent(UpdateEvent.UpdateType.REFRESH, null))
|
||||
getLastId()
|
||||
posts.value = emptyList()
|
||||
}
|
||||
Log.d(TAG, "getFeed")
|
||||
getProfilePostsUseCase(profileId, getLastId(), false, null).onEach { feedDtoResource ->
|
||||
when (feedDtoResource) {
|
||||
is Resource.Success -> {
|
||||
loadingPosts.postValue(false)
|
||||
posts.postValue(Pair(posts.value?.first?.apply { addAll(feedDtoResource.data ?: listOf()) } ?: feedDtoResource.data, UpdateEvent(if(refresh) UpdateEvent.UpdateType.REFRESH else UpdateEvent.UpdateType.PAGE_ADDED, null)))
|
||||
posts.value += feedDtoResource.data ?: emptyList()
|
||||
noMoreContent.postValue(feedDtoResource.data?.size == 0)
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.isolaatti.BuildConfig
|
||||
import com.isolaatti.R
|
||||
@ -40,8 +40,6 @@ import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.posting.posts.presentation.CreatePostContract
|
||||
import com.isolaatti.posting.posts.presentation.EditPostContract
|
||||
import com.isolaatti.posting.posts.presentation.PostListingRecyclerViewAdapterWiring
|
||||
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
|
||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||
import com.isolaatti.posting.posts.ui.PostInfoActivity
|
||||
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
||||
import com.isolaatti.profile.domain.entity.UserProfile
|
||||
@ -67,7 +65,6 @@ class ProfileMainFragment : Fragment() {
|
||||
val errorViewModel: ErrorMessageViewModel by activityViewModels()
|
||||
private var userId: Int? = null
|
||||
|
||||
lateinit var postsAdapter: PostsRecyclerViewAdapter
|
||||
|
||||
private var audioDescriptionAudio: Audio? = null
|
||||
|
||||
@ -129,13 +126,13 @@ class ProfileMainFragment : Fragment() {
|
||||
setupUiForUserType(profile.isUserItself)
|
||||
}
|
||||
|
||||
private val postsObserver: Observer<Pair<List<Post>?, UpdateEvent>?> = Observer {
|
||||
if(it?.first != null) {
|
||||
postsAdapter.updateList(it.first!!, it.second)
|
||||
postsAdapter.newContentRequestFinished()
|
||||
}
|
||||
|
||||
}
|
||||
// private val postsObserver: Observer<Pair<List<Post>?, UpdateEvent>?> = Observer {
|
||||
// if(it?.first != null) {
|
||||
// postsAdapter.updateList(it.first!!, it.second)
|
||||
// postsAdapter.newContentRequestFinished()
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
private val followingStateObserver: Observer<FollowingState> = Observer {
|
||||
when(it) {
|
||||
@ -310,8 +307,8 @@ class ProfileMainFragment : Fragment() {
|
||||
findNavController().navigate(ProfileMainFragmentDirections.actionDiscussionsFragmentToMainFollowersFragment(userId!!))
|
||||
}
|
||||
|
||||
viewBinding.feedRecyclerView.adapter = postsAdapter
|
||||
viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||
// viewBinding.feedRecyclerView.adapter = postsAdapter
|
||||
// viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
viewBinding.swipeToRefresh.setOnRefreshListener {
|
||||
viewModel.getFeed(true, null)
|
||||
@ -330,7 +327,7 @@ class ProfileMainFragment : Fragment() {
|
||||
|
||||
private fun setObservers() {
|
||||
viewModel.profile.observe(viewLifecycleOwner, profileObserver)
|
||||
viewModel.posts.observe(viewLifecycleOwner, postsObserver)
|
||||
//viewModel.posts.observe(viewLifecycleOwner, postsObserver)
|
||||
viewModel.followingState.observe(viewLifecycleOwner, followingStateObserver)
|
||||
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
|
||||
viewModel.loadingPosts.observe(viewLifecycleOwner) {
|
||||
@ -366,11 +363,10 @@ class ProfileMainFragment : Fragment() {
|
||||
.create(BuildConfig.backend))
|
||||
}
|
||||
})
|
||||
.usePlugin(CoilImagesPlugin.create(requireContext(), imageLoader))
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
|
||||
postsAdapter = PostsRecyclerViewAdapter(postListingRecyclerViewAdapterWiring )
|
||||
//postsAdapter = PostsRecyclerViewAdapter(postListingRecyclerViewAdapterWiring )
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,9 @@ import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import coil.request.ImageRequest
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.fallback
|
||||
import coil3.toBitmap
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
import com.isolaatti.MyApplication
|
||||
|
||||
@ -5,7 +5,7 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.databinding.SearchResultItemBinding
|
||||
import com.isolaatti.search.data.SearchResultDto
|
||||
|
||||
@ -5,7 +5,7 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.databinding.UsersCarouselItemBinding
|
||||
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||
|
||||
@ -7,7 +7,7 @@ import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import coil.load
|
||||
import coil3.load
|
||||
import com.isolaatti.databinding.FragmentSettingsBinding
|
||||
import com.isolaatti.settings.presentation.SettingsViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user