From 696228517dfe6f98d03d4e1cca532dd0f5616ab9 Mon Sep 17 00:00:00 2001 From: Matthieu <24-artectrex@users.noreply.shinice.net> Date: Tue, 2 Aug 2022 19:07:06 +0200 Subject: [PATCH] Working comment improvements --- app/build.gradle | 8 +- .../org/pixeldroid/app/posts/PostActivity.kt | 170 ++++++++++++------ .../comments/CommentContentRepository.kt | 33 ++++ .../comments/CommentPagingSource.kt | 34 ++++ app/src/main/res/layout/activity_post.xml | 129 +++++++++---- app/src/main/res/layout/comment.xml | 60 ++++--- app/src/main/res/values/strings.xml | 4 + 7 files changed, 317 insertions(+), 121 deletions(-) create mode 100644 app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentContentRepository.kt create mode 100644 app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentPagingSource.kt diff --git a/app/build.gradle b/app/build.gradle index 2e84a6a9..3e50cd03 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,6 +45,10 @@ android { testBuildType "staging" buildTypes { + debug { + applicationIdSuffix '.debug' + versionNameSuffix "-debug" + } staging { initWith debug testCoverageEnabled true @@ -67,10 +71,6 @@ android { buildConfigField "String", "CLIENT_ID", System.getenv("CLIENT_ID") ?: localProperties['CLIENT_ID'] ?: '""' buildConfigField "String", "CLIENT_SECRET", System.getenv("CLIENT_SECRET") ?: localProperties['CLIENT_SECRET'] ?: '""' } - debug { - applicationIdSuffix '.debug' - versionNameSuffix "-debug" - } release { minifyEnabled true shrinkResources true diff --git a/app/src/main/java/org/pixeldroid/app/posts/PostActivity.kt b/app/src/main/java/org/pixeldroid/app/posts/PostActivity.kt index dd5ae7b0..3d0532b3 100644 --- a/app/src/main/java/org/pixeldroid/app/posts/PostActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/posts/PostActivity.kt @@ -1,25 +1,36 @@ package org.pixeldroid.app.posts -import android.content.Context +import android.content.Intent import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE -import android.widget.LinearLayout import android.widget.Toast +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.paging.ExperimentalPagingApi +import androidx.paging.PagingDataAdapter +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.Job import org.pixeldroid.app.R import org.pixeldroid.app.databinding.ActivityPostBinding import org.pixeldroid.app.databinding.CommentBinding +import org.pixeldroid.app.posts.feeds.initAdapter +import org.pixeldroid.app.posts.feeds.launch +import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel +import org.pixeldroid.app.posts.feeds.uncachedFeeds.comments.CommentContentRepository +import org.pixeldroid.app.profile.ProfileViewModelFactory import org.pixeldroid.app.utils.BaseThemedWithBarActivity import org.pixeldroid.app.utils.api.PixelfedAPI -import org.pixeldroid.app.utils.api.objects.Mention import org.pixeldroid.app.utils.api.objects.Status import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_COMMENT_TAG import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG import org.pixeldroid.app.utils.displayDimensionsInPx +import org.pixeldroid.app.utils.setProfileImageFromURL import retrofit2.HttpException import java.io.IOException @@ -27,6 +38,10 @@ class PostActivity : BaseThemedWithBarActivity() { lateinit var domain : String private lateinit var binding: ActivityPostBinding + private lateinit var profileAdapter: PagingDataAdapter + private lateinit var viewModel: FeedViewModel + private var job: Job? = null + private lateinit var status: Status @@ -45,7 +60,6 @@ class PostActivity : BaseThemedWithBarActivity() { domain = user?.instance_uri.orEmpty() - supportActionBar?.title = getString(R.string.post_title).format(status.account?.getDisplayName()) val holder = StatusViewHolder(binding.postFragmentSingle) @@ -53,6 +67,7 @@ class PostActivity : BaseThemedWithBarActivity() { holder.bind(status, apiHolder, db, lifecycleScope, displayDimensionsInPx(), isActivity = true) activateCommenter() + retrieveComments() if(viewComments || postComment){ //Scroll already down as much as possible (since comments are not loaded yet) @@ -63,12 +78,6 @@ class PostActivity : BaseThemedWithBarActivity() { window.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE) binding.editComment.requestFocus() } - - // also retrieve comments if we're not posting the comment - if(!postComment) retrieveComments(apiHolder.api!!) - } - binding.postFragmentSingle.viewComments.setOnClickListener { - retrieveComments(apiHolder.api!!) } } @@ -92,53 +101,24 @@ class PostActivity : BaseThemedWithBarActivity() { } } - private fun addComment(context: Context, commentContainer: LinearLayout, - commentUsername: String, commentContent: String, mentions: List) { + @OptIn(ExperimentalPagingApi::class) + private fun retrieveComments() { + // get the view model + @Suppress("UNCHECKED_CAST") + viewModel = ViewModelProvider(this@PostActivity, ProfileViewModelFactory( + CommentContentRepository( + apiHolder.setToCurrentUser(), + status.id + ) + ) + )[FeedViewModel::class.java] as FeedViewModel + profileAdapter = CommentAdapter() + initAdapter(binding.postCommentsProgressBar, binding.postRefreshLayout, + binding.commentRecyclerView, binding.motionLayout, binding.errorLayout, + profileAdapter) - val itemBinding = CommentBinding.inflate( - LayoutInflater.from(context), commentContainer, true - ) - - itemBinding.user.text = commentUsername - itemBinding.commentText.text = parseHTMLText( - commentContent, - mentions, - apiHolder, - context, - lifecycleScope - ) - } - - private fun retrieveComments(api: PixelfedAPI) { - lifecycleScope.launchWhenCreated { - status.id.let { - try { - val statuses = api.statusComments(it).descendants - - binding.commentContainer.removeAllViews() - - //Create the new views for each comment - for (status in statuses) { - addComment( - binding.root.context, - binding.commentContainer, - status.account!!.username!!, - status.content!!, - status.mentions.orEmpty() - ) - } - binding.commentContainer.visibility = View.VISIBLE - - //Focus the comments - binding.scrollview.requestChildFocus(binding.commentContainer, binding.commentContainer) - } catch (exception: IOException) { - Log.e("COMMENT FETCH ERROR", exception.toString()) - } catch (exception: HttpException) { - Log.e("COMMENT ERROR", "${exception.code()} with body ${exception.response()?.errorBody()}") - } - } - } + job = launch(job, lifecycleScope, viewModel, profileAdapter) } private suspend fun postComment( @@ -152,10 +132,7 @@ class PostActivity : BaseThemedWithBarActivity() { binding.commentIn.visibility = View.GONE //Add the comment to the comment section - addComment( - binding.root.context, binding.commentContainer, response.account!!.username!!, - response.content!!, response.mentions.orEmpty() - ) + profileAdapter.refresh() Toast.makeText( binding.root.context, @@ -178,4 +155,79 @@ class PostActivity : BaseThemedWithBarActivity() { } } + + inner class CommentViewHolder(val binding: CommentBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(comment: Status) { + + setProfileImageFromURL(binding.profilePic, + comment.account!!.anyAvatar(), + binding.profilePic + ) + binding.user.text = comment.account.username + binding.commentText.text = parseHTMLText( + comment.content!!, + comment.mentions.orEmpty(), + apiHolder, + this@PostActivity, + lifecycleScope + ) + + if(comment.replies_count == 0 || comment.replies_count == null){ + binding.replies.visibility = View.GONE + } else { + binding.replies.visibility = View.VISIBLE + binding.replies.text = resources.getQuantityString( + R.plurals.replies_count, + comment.replies_count, + comment.replies_count + ) + } + + binding.comment.setOnClickListener{ openComment(comment) } + binding.profilePic.setOnClickListener{ comment.account.openProfile(itemView.context) } + binding.user.setOnClickListener { comment.account.openProfile(itemView.context) } + } + + private fun openComment(comment: Status) { + val intent = Intent(itemView.context, PostActivity::class.java).apply { + putExtra(POST_TAG, comment) + } + itemView.context.startActivity(intent) + } + } + + + inner class CommentAdapter : PagingDataAdapter( + UIMODEL_COMPARATOR + ) { + fun create(parent: ViewGroup): CommentViewHolder { + val itemBinding = CommentBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + return CommentViewHolder(itemBinding) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return create(parent) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val post = getItem(position) + + post?.let { + (holder as CommentViewHolder).bind(it) + } + } + } + + + private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Status, newItem: Status): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: Status, newItem: Status): Boolean = + oldItem.content == newItem.content + } } diff --git a/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentContentRepository.kt b/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentContentRepository.kt new file mode 100644 index 00000000..62b6b687 --- /dev/null +++ b/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentContentRepository.kt @@ -0,0 +1,33 @@ +package org.pixeldroid.app.posts.feeds.uncachedFeeds.comments + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow +import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository +import org.pixeldroid.app.posts.feeds.uncachedFeeds.profile.ProfilePagingSource +import org.pixeldroid.app.utils.api.PixelfedAPI +import org.pixeldroid.app.utils.api.objects.Status +import javax.inject.Inject + +class CommentContentRepository @ExperimentalPagingApi +@Inject constructor( + private val api: PixelfedAPI, + private val statusId: String +) : UncachedContentRepository { + override fun getStream(): Flow> { + return Pager( + config = PagingConfig( + initialLoadSize = NETWORK_PAGE_SIZE, + pageSize = NETWORK_PAGE_SIZE), + pagingSourceFactory = { + CommentPagingSource(api, statusId) + } + ).flow + } + + companion object { + private const val NETWORK_PAGE_SIZE = 20 + } +} \ No newline at end of file diff --git a/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentPagingSource.kt b/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentPagingSource.kt new file mode 100644 index 00000000..afa725c5 --- /dev/null +++ b/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/comments/CommentPagingSource.kt @@ -0,0 +1,34 @@ +package org.pixeldroid.app.posts.feeds.uncachedFeeds.comments + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import org.pixeldroid.app.utils.api.PixelfedAPI +import org.pixeldroid.app.utils.api.objects.Status +import retrofit2.HttpException +import java.io.IOException + +class CommentPagingSource( + private val api: PixelfedAPI, + private val statusId: String +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + //val position = params.key + return try { + val comments = api.statusComments(statusId).descendants + + // TODO use pagination to have many comments also work + // For now, don't paginate (nextKey and prevKey null) + LoadResult.Page( + data = comments, + prevKey = null, + nextKey = null + ) + } catch (exception: HttpException) { + LoadResult.Error(exception) + } catch (exception: IOException) { + LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): String? = null +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_post.xml b/app/src/main/res/layout/activity_post.xml index 9108e345..739d8e70 100644 --- a/app/src/main/res/layout/activity_post.xml +++ b/app/src/main/res/layout/activity_post.xml @@ -1,59 +1,112 @@ - + tools:context=".posts.PostActivity"> - + + + + + android:layout_height="wrap_content" + app:layout_scrollFlags="scroll|exitUntilCollapsed"> - + - + + - + + + android:layout_height="wrap_content" + android:hint="@string/comment" + android:importantForAutofill="no" + android:inputType="text" /> + - - +