From 37837653cce1fdceb11a7abcda5726f64fb7c462 Mon Sep 17 00:00:00 2001 From: erik-everardo Date: Wed, 29 Jan 2025 00:41:04 -0600 Subject: [PATCH] WIP posts en compose --- app/build.gradle | 10 +- .../com/isolaatti/common/CoilImageLoader.kt | 22 +- .../common/UserListRecyclerViewAdapter.kt | 2 +- .../home/presentation/FeedViewModel.kt | 3 - .../com/isolaatti/home/ui/FeedFragment.kt | 2 +- .../ui/PictureViewerImageWrapperFragment.kt | 5 +- .../ui/PictureViewerMainFragment.kt | 3 +- .../java/com/isolaatti/markdown/Module.kt | 1 - .../presentation/NotificationsAdapter.kt | 3 +- .../CommentsRecyclerViewAdapter.kt | 2 +- .../comments/ui/BottomSheetPostComments.kt | 1 - .../comments/ui/EditCommentDialogFragment.kt | 4 +- .../posting/posts/components/Post.kt | 103 +++-- .../posting/posts/domain/entity/Post.kt | 23 +- .../PostImagesViewPagerAdapter.kt | 2 +- .../presentation/PostListingViewModel.kt | 8 +- .../presentation/PostListingViewModelBase.kt | 41 +- .../presentation/PostsRecyclerViewAdapter.kt | 402 ------------------ .../posting/posts/presentation/UpdateEvent.kt | 12 - .../posting/posts/ui/PostListingFragment.kt | 122 ++++-- .../presentation/PostViewerViewModel.kt | 8 +- .../posts/viewer/ui/PostViewerActivity.kt | 5 +- .../profile/presentation/ProfileViewModel.kt | 8 +- .../profile/ui/ProfileMainFragment.kt | 28 +- .../push_notifications/FcmService.kt | 4 +- .../presentation/SearchResultsAdapter.kt | 2 +- .../presentation/UserCarouselAdapter.kt | 2 +- .../isolaatti/settings/ui/SettingsFragment.kt | 2 +- 28 files changed, 237 insertions(+), 593 deletions(-) delete mode 100644 app/src/main/java/com/isolaatti/posting/posts/presentation/PostsRecyclerViewAdapter.kt delete mode 100644 app/src/main/java/com/isolaatti/posting/posts/presentation/UpdateEvent.kt diff --git a/app/build.gradle b/app/build.gradle index db2eaee..1aec3db 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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") } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/common/CoilImageLoader.kt b/app/src/main/java/com/isolaatti/common/CoilImageLoader.kt index baace6e..e182f19 100644 --- a/app/src/main/java/com/isolaatti/common/CoilImageLoader.kt +++ b/app/src/main/java/com/isolaatti/common/CoilImageLoader.kt @@ -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() } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt b/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt index f265dda..d547181 100644 --- a/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt +++ b/app/src/main/java/com/isolaatti/common/UserListRecyclerViewAdapter.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/home/presentation/FeedViewModel.kt b/app/src/main/java/com/isolaatti/home/presentation/FeedViewModel.kt index fb9a27a..669602c 100644 --- a/app/src/main/java/com/isolaatti/home/presentation/FeedViewModel.kt +++ b/app/src/main/java/com/isolaatti/home/presentation/FeedViewModel.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/home/ui/FeedFragment.kt b/app/src/main/java/com/isolaatti/home/ui/FeedFragment.kt index 74c7e2e..f0b9900 100644 --- a/app/src/main/java/com/isolaatti/home/ui/FeedFragment.kt +++ b/app/src/main/java/com/isolaatti/home/ui/FeedFragment.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerImageWrapperFragment.kt b/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerImageWrapperFragment.kt index 678ffb6..ea75632 100644 --- a/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerImageWrapperFragment.kt +++ b/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerImageWrapperFragment.kt @@ -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 } diff --git a/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerMainFragment.kt b/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerMainFragment.kt index d41f878..4f2e447 100644 --- a/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerMainFragment.kt +++ b/app/src/main/java/com/isolaatti/images/picture_viewer/ui/PictureViewerMainFragment.kt @@ -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 + images = IntentCompat.getParcelableArrayExtra(requireActivity().intent, PictureViewerActivity.EXTRA_IMAGES, RemoteImage::class.java) as Array val position = requireActivity().intent.extras?.getInt(PictureViewerActivity.EXTRA_IMAGE_POSITiON) ?: 0 val adapter = PictureViewerViewPagerAdapter(this, images) binding.viewpager.adapter = adapter diff --git a/app/src/main/java/com/isolaatti/markdown/Module.kt b/app/src/main/java/com/isolaatti/markdown/Module.kt index 5e20141..ee00ec9 100644 --- a/app/src/main/java/com/isolaatti/markdown/Module.kt +++ b/app/src/main/java/com/isolaatti/markdown/Module.kt @@ -31,7 +31,6 @@ class Module { .create(BuildConfig.backend)) } }) - .usePlugin(CoilImagesPlugin.create(context, CoilImageLoader.imageLoader)) .usePlugin(LinkifyPlugin.create()) .build() } diff --git a/app/src/main/java/com/isolaatti/notifications/presentation/NotificationsAdapter.kt b/app/src/main/java/com/isolaatti/notifications/presentation/NotificationsAdapter.kt index 4591419..e3950b8 100644 --- a/app/src/main/java/com/isolaatti/notifications/presentation/NotificationsAdapter.kt +++ b/app/src/main/java/com/isolaatti/notifications/presentation/NotificationsAdapter.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt b/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt index dab8146..8409d80 100644 --- a/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt +++ b/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt b/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt index c45fdfa..2356baf 100644 --- a/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt +++ b/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt @@ -217,7 +217,6 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC .create("https://isolaatti.com/")) } }) - .usePlugin(CoilImagesPlugin.create(requireContext(), imageLoader)) .usePlugin(LinkifyPlugin.create()) .build() diff --git a/app/src/main/java/com/isolaatti/posting/comments/ui/EditCommentDialogFragment.kt b/app/src/main/java/com/isolaatti/posting/comments/ui/EditCommentDialogFragment.kt index c7d6e84..58fd14a 100644 --- a/app/src/main/java/com/isolaatti/posting/comments/ui/EditCommentDialogFragment.kt +++ b/app/src/main/java/com/isolaatti/posting/comments/ui/EditCommentDialogFragment.kt @@ -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() diff --git a/app/src/main/java/com/isolaatti/posting/posts/components/Post.kt b/app/src/main/java/com/isolaatti/posting/posts/components/Post.kt index 19c8114..4ec4cce 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/components/Post.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/components/Post.kt @@ -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, 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) diff --git a/app/src/main/java/com/isolaatti/posting/posts/domain/entity/Post.kt b/app/src/main/java/com/isolaatti/posting/posts/domain/entity/Post.kt index 2480a66..abd06fb 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/domain/entity/Post.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/domain/entity/Post.kt @@ -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 + images: List ) : 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()!!, diff --git a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostImagesViewPagerAdapter.kt b/app/src/main/java/com/isolaatti/posting/posts/presentation/PostImagesViewPagerAdapter.kt index 7bfb21c..b37f784 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostImagesViewPagerAdapter.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/presentation/PostImagesViewPagerAdapter.kt @@ -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) : diff --git a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModel.kt b/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModel.kt index 4fb6e28..31ef4df 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModel.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModel.kt @@ -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) } diff --git a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModelBase.kt b/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModelBase.kt index 8303f1d..367ff01 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModelBase.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/presentation/PostListingViewModelBase.kt @@ -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?, UpdateEvent>?> = MutableLiveData() + val posts: MutableStateFlow> = MutableStateFlow(emptyList()) - val postsList get() = posts.value?.first val loadingPosts = MutableLiveData(false) @@ -41,7 +41,7 @@ abstract class PostListingViewModelBase : ViewModel() { val errorLoading: MutableLiveData = 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 -> {} diff --git a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostsRecyclerViewAdapter.kt b/app/src/main/java/com/isolaatti/posting/posts/presentation/PostsRecyclerViewAdapter.kt deleted file mode 100644 index 9f72a06..0000000 --- a/app/src/main/java/com/isolaatti/posting/posts/presentation/PostsRecyclerViewAdapter.kt +++ /dev/null @@ -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(){ - private var postList: List? = null - inner class FeedViewHolder(val itemBinding: PostLayoutBinding) : ViewHolder(itemBinding.root) { - fun bindView(post: Post, payloads: List) { - - 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(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, 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) { - 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" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/posts/presentation/UpdateEvent.kt b/app/src/main/java/com/isolaatti/posting/posts/presentation/UpdateEvent.kt deleted file mode 100644 index b3cd7c9..0000000 --- a/app/src/main/java/com/isolaatti/posting/posts/presentation/UpdateEvent.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt b/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt index c3b8478..bec7596 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt @@ -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) diff --git a/app/src/main/java/com/isolaatti/posting/posts/viewer/presentation/PostViewerViewModel.kt b/app/src/main/java/com/isolaatti/posting/posts/viewer/presentation/PostViewerViewModel.kt index 9a5ca7d..4702916 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/viewer/presentation/PostViewerViewModel.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/viewer/presentation/PostViewerViewModel.kt @@ -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() { diff --git a/app/src/main/java/com/isolaatti/posting/posts/viewer/ui/PostViewerActivity.kt b/app/src/main/java/com/isolaatti/posting/posts/viewer/ui/PostViewerActivity.kt index 17deebb..82227f2 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/viewer/ui/PostViewerActivity.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/viewer/ui/PostViewerActivity.kt @@ -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() diff --git a/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt b/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt index a5dec74..e8d77a6 100644 --- a/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt +++ b/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt @@ -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) } diff --git a/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt index ac65ca8..787b813 100644 --- a/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt +++ b/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt @@ -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?, UpdateEvent>?> = Observer { - if(it?.first != null) { - postsAdapter.updateList(it.first!!, it.second) - postsAdapter.newContentRequestFinished() - } - - } +// private val postsObserver: Observer?, UpdateEvent>?> = Observer { +// if(it?.first != null) { +// postsAdapter.updateList(it.first!!, it.second) +// postsAdapter.newContentRequestFinished() +// } +// +// } private val followingStateObserver: Observer = 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 ) } diff --git a/app/src/main/java/com/isolaatti/push_notifications/FcmService.kt b/app/src/main/java/com/isolaatti/push_notifications/FcmService.kt index 0a47e43..d7d3c56 100644 --- a/app/src/main/java/com/isolaatti/push_notifications/FcmService.kt +++ b/app/src/main/java/com/isolaatti/push_notifications/FcmService.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/search/presentation/SearchResultsAdapter.kt b/app/src/main/java/com/isolaatti/search/presentation/SearchResultsAdapter.kt index 37f8590..cb8cf0f 100644 --- a/app/src/main/java/com/isolaatti/search/presentation/SearchResultsAdapter.kt +++ b/app/src/main/java/com/isolaatti/search/presentation/SearchResultsAdapter.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/search/presentation/UserCarouselAdapter.kt b/app/src/main/java/com/isolaatti/search/presentation/UserCarouselAdapter.kt index 84459cc..e467365 100644 --- a/app/src/main/java/com/isolaatti/search/presentation/UserCarouselAdapter.kt +++ b/app/src/main/java/com/isolaatti/search/presentation/UserCarouselAdapter.kt @@ -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 diff --git a/app/src/main/java/com/isolaatti/settings/ui/SettingsFragment.kt b/app/src/main/java/com/isolaatti/settings/ui/SettingsFragment.kt index 49754be..2bbc01c 100644 --- a/app/src/main/java/com/isolaatti/settings/ui/SettingsFragment.kt +++ b/app/src/main/java/com/isolaatti/settings/ui/SettingsFragment.kt @@ -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