From ba7c665a9dc9b6418406f94faa428f4109ae1136 Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Wed, 21 Feb 2024 22:59:45 -0600 Subject: [PATCH] fix: code cleanup | reply/edit stability --- .../connections/comments/CommentsAPI.kt | 15 +- .../dantotsu/media/comments/CommentItem.kt | 57 +- .../media/comments/CommentsActivity.kt | 617 +++++++++--------- 3 files changed, 336 insertions(+), 353 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt index c589ce06..5c16a193 100644 --- a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt +++ b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt @@ -194,10 +194,23 @@ object CommentsAPI { 429 -> "Rate limited. :(" else -> "Failed to connect" } - snackString("Error $code: ${reason ?: error}") + val parsed = try { + Json.decodeFromString(reason!!) + } catch (e: Exception) { + null + } + val message = parsed?.message ?: reason ?: error + + snackString("Error $code: $message") } } +@Serializable +data class ErrorResponse( + @SerialName("message") + val message: String +) + @Serializable data class AuthResponse( @SerialName("authToken") diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt index 3b4c467a..d637f18b 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt @@ -26,13 +26,14 @@ import java.util.TimeZone class CommentItem(val comment: Comment, private val markwon: Markwon, private val section: Section, - private val editCallback: (CommentItem) -> Unit, - private val viewReplyCallback: (CommentItem) -> Unit, - private val replyCallback: (CommentItem) -> Unit + private val commentsActivity: CommentsActivity, ) : BindableItem() { var binding: ItemCommentsBinding? = null val adapter = GroupieAdapter() val repliesSection = Section() + private var isEditing = false + private var isReplying = false + private var repliesVisible = false init { adapter.add(repliesSection) @@ -58,37 +59,25 @@ class CommentItem(val comment: Comment, } viewBinding.commentReply.visibility = View.VISIBLE viewBinding.commentTotalReplies.setOnClickListener { - viewBinding.commentTotalReplies.visibility = View.GONE - viewReplyCallback(this) - } - var isEditing = false - var isReplying = false - viewBinding.commentEdit.setOnClickListener { - if (!isEditing) { - viewBinding.commentEdit.text = currActivity()!!.getString(R.string.cancel) - isEditing = true - isReplying = false - viewBinding.commentReply.text = "Reply" - editCallback(this) + if (repliesVisible) { + repliesSection.clear() + viewBinding.commentTotalReplies.text = "View ${comment.replyCount} repl${if (comment.replyCount == 1) "y" else "ies"}" + repliesVisible = false } else { - viewBinding.commentEdit.text = currActivity()!!.getString(R.string.edit) - isEditing = false - editCallback(this) + viewBinding.commentTotalReplies.text = "Hide Replies" + repliesSection.clear() + commentsActivity.viewReplyCallback(this) + repliesVisible = true } + } + viewBinding.commentEdit.setOnClickListener { + editing(!isEditing) + commentsActivity.editCallback(this) } viewBinding.commentReply.setOnClickListener { - if (!isReplying) { - viewBinding.commentReply.text = currActivity()!!.getString(R.string.cancel) - isReplying = true - isEditing = false - viewBinding.commentEdit.text = currActivity()!!.getString(R.string.edit) - replyCallback(this) - } else { - viewBinding.commentReply.text = "Reply" - isReplying = false - replyCallback(this) - } + replying(!isReplying) + commentsActivity.replyCallback(this) } viewBinding.modBadge.visibility = if (comment.isMod == true) View.VISIBLE else View.GONE viewBinding.adminBadge.visibility = if (comment.isAdmin == true) View.VISIBLE else View.GONE @@ -165,6 +154,16 @@ class CommentItem(val comment: Comment, return ItemCommentsBinding.bind(view) } + fun replying(isReplying: Boolean) { + binding?.commentReply?.text = if (isReplying) currActivity()!!.getString(R.string.cancel) else "Reply" + this.isReplying = isReplying + } + + fun editing(isEditing: Boolean) { + binding?.commentEdit?.text = if (isEditing) currActivity()!!.getString(R.string.cancel) else currActivity()!!.getString(R.string.edit) + this.isEditing = isEditing + } + private fun setVoteButtons(viewBinding: ItemCommentsBinding) { when (comment.userVoteType) { 1 -> { diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentsActivity.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentsActivity.kt index bd5a364d..a98b1f85 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentsActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentsActivity.kt @@ -11,6 +11,8 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.connections.comments.Comment +import ani.dantotsu.connections.comments.CommentResponse import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.databinding.ActivityCommentsBinding import ani.dantotsu.initActivity @@ -43,10 +45,18 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.text.SimpleDateFormat +import java.util.Locale import java.util.TimeZone class CommentsActivity : AppCompatActivity() { lateinit var binding: ActivityCommentsBinding + private var interactionState = InteractionState.NONE + private var commentWithInteraction: CommentItem? = null + private val section = Section() + private val adapter = GroupieAdapter() + private var mediaId: Int = -1 + var pagesLoaded = 1 + var totalPages = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -60,9 +70,8 @@ class CommentsActivity : AppCompatActivity() { snackString("Invalid Media ID") finish() } + this.mediaId = mediaId - val adapter = GroupieAdapter() - val section = Section() val markwon = buildMarkwon() binding.commentUserAvatar.loadImage(Anilist.avatar) @@ -74,250 +83,88 @@ class CommentsActivity : AppCompatActivity() { ) ) binding.commentReplyToContainer.visibility = View.GONE //TODO: implement reply - var editing = false - var editingCommentId = -1 - var replying = false - var replyingComment: CommentItem? = null - fun editCallback(comment: CommentItem) { - replying = false - replyingComment = null - if (editingCommentId == comment.comment.commentId) { - editing = false - editingCommentId = -1 - binding.commentInput.setText("") - val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(binding.commentInput.windowToken, 0) - } else { - editing = true - editingCommentId = comment.comment.commentId - binding.commentInput.setText(comment.comment.content) - binding.commentInput.requestFocus() - binding.commentInput.setSelection(binding.commentInput.text.length) - val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - imm.showSoftInput(binding.commentInput, InputMethodManager.SHOW_IMPLICIT) - } - - } - - fun replyCallback(comment: CommentItem) { - editing = false - editingCommentId = -1 - if (replyingComment?.comment?.commentId == comment.comment.commentId) { - replying = false - replyingComment = null - binding.commentInput.setText("") - val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(binding.commentInput.windowToken, 0) - } else { - replying = true - replyingComment = comment - binding.commentInput.requestFocus() - binding.commentInput.setSelection(binding.commentInput.text.length) - val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - imm.showSoftInput(binding.commentInput, InputMethodManager.SHOW_IMPLICIT) - } - } - - fun viewReplyCallback(comment: CommentItem) { - lifecycleScope.launch { - val replies = withContext(Dispatchers.IO) { - CommentsAPI.getRepliesFromId(comment.comment.commentId) - } - replies?.comments?.forEach { - comment.repliesSection.add(CommentItem(it, buildMarkwon(), comment.repliesSection, { comment -> - editCallback(comment) - }, { comment -> - viewReplyCallback(comment) - }, { comment -> - replyCallback(comment) - })) - } - } - } binding.commentsRefresh.setOnRefreshListener { lifecycleScope.launch { - binding.commentsList.visibility = View.GONE - adapter.clear() - section.clear() - withContext(Dispatchers.IO) { - val comments = CommentsAPI.getCommentsForId(mediaId) - val sorted = when (PrefManager.getVal(PrefName.CommentSortOrder, "newest")) { - "newest" -> comments?.comments?.sortedByDescending { comment -> - CommentItem.timestampToMillis(comment.timestamp) - } - - "oldest" -> comments?.comments?.sortedBy { comment -> - CommentItem.timestampToMillis(comment.timestamp) - } - - "highest_rated" -> comments?.comments?.sortedByDescending { comment -> - comment.upvotes - comment.downvotes - } - - "lowest_rated" -> comments?.comments?.sortedBy { comment -> - comment.upvotes - comment.downvotes - } - - else -> comments?.comments - } - sorted?.forEach { - withContext(Dispatchers.Main) { - section.add(CommentItem(it, buildMarkwon(), section, { comment -> - editCallback(comment) - }, { comment -> - viewReplyCallback(comment) - }, { comment -> - replyCallback(comment) - })) - } - } - } - adapter.add(section) - binding.commentsList.visibility = View.VISIBLE + loadAndDisplayComments() binding.commentsRefresh.isRefreshing = false } } - var pagesLoaded = 1 - var totalPages = 1 binding.commentsList.adapter = adapter binding.commentsList.layoutManager = LinearLayoutManager(this) + lifecycleScope.launch { - binding.commentsProgressBar.visibility = View.VISIBLE - binding.commentsList.visibility = View.GONE - withContext(Dispatchers.IO) { - val comments = CommentsAPI.getCommentsForId(mediaId) - val sorted = when (PrefManager.getVal(PrefName.CommentSortOrder, "newest")) { - "newest" -> comments?.comments?.sortedByDescending { comment -> - CommentItem.timestampToMillis(comment.timestamp) - } - - "oldest" -> comments?.comments?.sortedBy { comment -> - CommentItem.timestampToMillis(comment.timestamp) - } - - "highest_rated" -> comments?.comments?.sortedByDescending { comment -> - comment.upvotes - comment.downvotes - } - - "lowest_rated" -> comments?.comments?.sortedBy { comment -> - comment.upvotes - comment.downvotes - } - - else -> comments?.comments - } - sorted?.forEach { - withContext(Dispatchers.Main) { - section.add(CommentItem(it, buildMarkwon(), section, { comment -> - editCallback(comment) - }, { comment -> - viewReplyCallback(comment) - }, { comment -> - replyCallback(comment) - })) - } - } - totalPages = comments?.totalPages ?: 1 - } - binding.commentsProgressBar.visibility = View.GONE - binding.commentsList.visibility = View.VISIBLE - adapter.add(section) + loadAndDisplayComments() } - binding.commentSort.setOnClickListener { - val popup = PopupMenu(this, it) - popup.setOnMenuItemClickListener { item -> - when (item.itemId) { - R.id.comment_sort_newest -> { - PrefManager.setVal(PrefName.CommentSortOrder, "newest") - val groups = section.groups - groups.sortByDescending { comment -> - comment as CommentItem - CommentItem.timestampToMillis(comment.comment.timestamp) - } - section.update(groups) - binding.commentsList.scrollToPosition(0) - } - - R.id.comment_sort_oldest -> { - PrefManager.setVal(PrefName.CommentSortOrder, "oldest") - val groups = section.groups - groups.sortBy { comment -> - comment as CommentItem - CommentItem.timestampToMillis(comment.comment.timestamp) - } - section.update(groups) - binding.commentsList.scrollToPosition(0) - } - - R.id.comment_sort_highest_rated -> { - PrefManager.setVal(PrefName.CommentSortOrder, "highest_rated") - val groups = section.groups - groups.sortByDescending { comment -> - comment as CommentItem - comment.comment.upvotes - comment.comment.downvotes - } - section.update(groups) - binding.commentsList.scrollToPosition(0) - } - - R.id.comment_sort_lowest_rated -> { - PrefManager.setVal(PrefName.CommentSortOrder, "lowest_rated") - val groups = section.groups - groups.sortBy { comment -> - comment as CommentItem - comment.comment.upvotes - comment.comment.downvotes - } - section.update(groups) - binding.commentsList.scrollToPosition(0) - } + binding.commentSort.setOnClickListener { view -> + fun sortComments(sortOrder: String) { + val groups = section.groups + when (sortOrder) { + "newest" -> groups.sortByDescending { CommentItem.timestampToMillis((it as CommentItem).comment.timestamp) } + "oldest" -> groups.sortBy { CommentItem.timestampToMillis((it as CommentItem).comment.timestamp) } + "highest_rated" -> groups.sortByDescending { (it as CommentItem).comment.upvotes - it.comment.downvotes } + "lowest_rated" -> groups.sortBy { (it as CommentItem).comment.upvotes - it.comment.downvotes } } + section.update(groups) + } + + val popup = PopupMenu(this, view) + popup.setOnMenuItemClickListener { item -> + val sortOrder = when (item.itemId) { + R.id.comment_sort_newest -> "newest" + R.id.comment_sort_oldest -> "oldest" + R.id.comment_sort_highest_rated -> "highest_rated" + R.id.comment_sort_lowest_rated -> "lowest_rated" + else -> return@setOnMenuItemClickListener false + } + + PrefManager.setVal(PrefName.CommentSortOrder, sortOrder) + sortComments(sortOrder) + binding.commentsList.scrollToPosition(0) true } popup.inflate(R.menu.comments_sort_menu) popup.show() } - var fetching = false + var isFetching = false //if we have scrolled to the bottom of the list, load more comments - binding.commentsList.addOnScrollListener(object : - androidx.recyclerview.widget.RecyclerView.OnScrollListener() { - override fun onScrolled( - recyclerView: androidx.recyclerview.widget.RecyclerView, - dx: Int, - dy: Int - ) { + binding.commentsList.addOnScrollListener(object : androidx.recyclerview.widget.RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: androidx.recyclerview.widget.RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) - if (!recyclerView.canScrollVertically(1)) { - if (pagesLoaded < totalPages && !fetching) { - fetching = true - lifecycleScope.launch { - withContext(Dispatchers.IO) { - val comments = - CommentsAPI.getCommentsForId(mediaId, pagesLoaded + 1) - comments?.comments?.forEach { - withContext(Dispatchers.Main) { - section.add( - CommentItem( - it, - buildMarkwon(), - section, { comment -> - editCallback(comment) - }, { comment -> - viewReplyCallback(comment) - }, { comment -> - replyCallback(comment) - }) - ) - } - } - totalPages = comments?.totalPages ?: 1 - } - pagesLoaded++ - fetching = false - } + if (shouldLoadMoreComments(recyclerView)) { + loadMoreComments() + } + } + + private fun shouldLoadMoreComments(recyclerView: androidx.recyclerview.widget.RecyclerView): Boolean { + return !recyclerView.canScrollVertically(1) && pagesLoaded < totalPages && !isFetching + } + + private fun loadMoreComments() { + isFetching = true + lifecycleScope.launch { + val comments = fetchComments() + comments?.comments?.forEach { comment -> + updateUIWithComment(comment) } + totalPages = comments?.totalPages ?: 1 + pagesLoaded++ + isFetching = false + } + } + + private suspend fun fetchComments(): CommentResponse? { + return withContext(Dispatchers.IO) { + CommentsAPI.getCommentsForId(mediaId, pagesLoaded + 1) + } + } + + private suspend fun updateUIWithComment(comment: Comment) { + withContext(Dispatchers.Main) { + section.add(CommentItem(comment, buildMarkwon(), section, this@CommentsActivity)) } } }) @@ -342,111 +189,235 @@ class CommentsActivity : AppCompatActivity() { snackString("You are banned from commenting :(") return@setOnClickListener } - fun onSuccess() { - binding.commentInput.text.toString().let { - if (it.isNotEmpty()) { - binding.commentInput.text.clear() - lifecycleScope.launch { - if (editing) { - val success = withContext(Dispatchers.IO) { - CommentsAPI.editComment(editingCommentId, it) - } - if (success) { - val groups = section.groups - groups.forEach { item -> - if (item is CommentItem) { - if (item.comment.commentId == editingCommentId) { - item.comment.content = it - val dateFormat = - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") - dateFormat.timeZone = - TimeZone.getTimeZone("UTC") - item.comment.timestamp = - dateFormat.format(System.currentTimeMillis()) - item.notifyChanged() - snackString("Comment edited") - } - } - - } - } - } else { - val success = withContext(Dispatchers.IO) { - CommentsAPI.comment(mediaId, if (replying) replyingComment!!.comment.commentId else null, it) - } - if (success != null) - if (replying) { - replyingComment?.repliesSection?.add( - 0, - CommentItem( - success, - buildMarkwon(), - replyingComment?.repliesSection!!, { comment -> - editCallback(comment) - }, { comment -> - viewReplyCallback(comment) - }, { comment -> - replyCallback(comment) - }) - ) - } else { - section.add( - 0, - CommentItem( - success, - buildMarkwon(), - section, { comment -> - editCallback(comment) - }, { comment -> - viewReplyCallback(comment) - }, { comment -> - replyCallback(comment) - }) - ) - } - } - } - } else { - snackString("Comment cannot be empty") - } - } - } - - val firstComment = PrefManager.getVal(PrefName.FirstComment) - if (firstComment) { - //show a dialog explaining the rules - val alertDialog = android.app.AlertDialog.Builder(this, R.style.MyPopup) - .setTitle("Commenting Rules") - .setMessage( - "I WILL BAN YOU WITHOUT HESITATION\n" + - "1. No racism\n" + - "2. No hate speech\n" + - "3. No spam\n" + - "4. No NSFW content\n" + - "6. ENGLISH ONLY\n" + - "7. No self promotion\n" + - "8. No impersonation\n" + - "9. No harassment\n" + - "10. No illegal content\n" + - "11. Anything you know you shouldn't comment\n" - ) - .setPositiveButton("I Understand") { dialog, _ -> - dialog.dismiss() - PrefManager.setVal(PrefName.FirstComment, false) - onSuccess() - } - .setNegativeButton("Cancel") { dialog, _ -> - dialog.dismiss() - } - val dialog = alertDialog.show() - dialog?.window?.setDimAmount(0.8f) + if (PrefManager.getVal(PrefName.FirstComment)) { + showCommentRulesDialog() } else { - onSuccess() + processComment() } } } + enum class InteractionState { + NONE, EDIT, REPLY + } + + private suspend fun loadAndDisplayComments() { + binding.commentsProgressBar.visibility = View.VISIBLE + binding.commentsList.visibility = View.GONE + adapter.clear() + section.clear() + + val comments = withContext(Dispatchers.IO) { + CommentsAPI.getCommentsForId(mediaId) + } + + val sortedComments = sortComments(comments?.comments) + sortedComments.forEach { + withContext(Dispatchers.Main) { + section.add(CommentItem(it, buildMarkwon(), section, this@CommentsActivity)) + } + } + + totalPages = comments?.totalPages ?: 1 + binding.commentsProgressBar.visibility = View.GONE + binding.commentsList.visibility = View.VISIBLE + adapter.add(section) + } + + private fun sortComments(comments: List?): List { + if (comments == null) return emptyList() + val sortOrder = PrefManager.getVal(PrefName.CommentSortOrder, "newest") + return when (sortOrder) { + "newest" -> comments.sortedByDescending { CommentItem.timestampToMillis(it.timestamp) } + "oldest" -> comments.sortedBy { CommentItem.timestampToMillis(it.timestamp) } + "highest_rated" -> comments.sortedByDescending { it.upvotes - it.downvotes } + "lowest_rated" -> comments.sortedBy { it.upvotes - it.downvotes } + else -> comments + } + } + + /** + * Resets the old state of the comment input + * @return the old state + */ + private fun resetOldState(): InteractionState { + val oldState = interactionState + interactionState = InteractionState.NONE + return when (oldState) { + InteractionState.EDIT -> { + binding.commentInput.setText("") + val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(binding.commentInput.windowToken, 0) + commentWithInteraction?.editing(false) + InteractionState.EDIT + } + + InteractionState.REPLY -> { + binding.commentInput.setText("") + val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(binding.commentInput.windowToken, 0) + commentWithInteraction?.replying(false) + InteractionState.REPLY + } + + else -> { + InteractionState.NONE + } + } + } + + /** + * Callback from the comment item to edit the comment + * Called every time the edit button is clicked + * @param comment the comment to edit + */ + fun editCallback(comment: CommentItem) { + if (resetOldState() == InteractionState.EDIT) return + commentWithInteraction = comment + binding.commentInput.setText(comment.comment.content) + binding.commentInput.requestFocus() + binding.commentInput.setSelection(binding.commentInput.text.length) + val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(binding.commentInput, InputMethodManager.SHOW_IMPLICIT) + interactionState = InteractionState.EDIT + } + + /** + * Callback from the comment item to reply to the comment + * Called every time the reply button is clicked + * @param comment the comment to reply to + */ + fun replyCallback(comment: CommentItem) { + if (resetOldState() == InteractionState.REPLY) return + commentWithInteraction = comment + binding.commentInput.requestFocus() + binding.commentInput.setSelection(binding.commentInput.text.length) + val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(binding.commentInput, InputMethodManager.SHOW_IMPLICIT) + interactionState = InteractionState.REPLY + } + + fun viewReplyCallback(comment: CommentItem) { + lifecycleScope.launch { + val replies = withContext(Dispatchers.IO) { + CommentsAPI.getRepliesFromId(comment.comment.commentId) + } + replies?.comments?.forEach { + comment.repliesSection.add( + CommentItem( + it, + buildMarkwon(), + comment.repliesSection, + this@CommentsActivity) + ) + } + } + } + + /** + * Shows the comment rules dialog + * Called when the user tries to comment for the first time + */ + private fun showCommentRulesDialog() { + val alertDialog = android.app.AlertDialog.Builder(this, R.style.MyPopup) + .setTitle("Commenting Rules") + .setMessage( + "I WILL BAN YOU WITHOUT HESITATION\n" + + "1. No racism\n" + + "2. No hate speech\n" + + "3. No spam\n" + + "4. No NSFW content\n" + + "6. ENGLISH ONLY\n" + + "7. No self promotion\n" + + "8. No impersonation\n" + + "9. No harassment\n" + + "10. No illegal content\n" + + "11. Anything you know you shouldn't comment\n" + ) + .setPositiveButton("I Understand") { dialog, _ -> + dialog.dismiss() + PrefManager.setVal(PrefName.FirstComment, false) + processComment() + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.dismiss() + } + val dialog = alertDialog.show() + dialog?.window?.setDimAmount(0.8f) + } + + private fun processComment() { + val commentText = binding.commentInput.text.toString() + if (commentText.isEmpty()) { + snackString("Comment cannot be empty") + return + } + + binding.commentInput.text.clear() + lifecycleScope.launch { + if (interactionState == InteractionState.EDIT) { + handleEditComment(commentText) + } else { + handleNewComment(commentText) + } + } + } + + private suspend fun handleEditComment(commentText: String) { + val success = withContext(Dispatchers.IO) { + CommentsAPI.editComment(commentWithInteraction?.comment?.commentId ?: return@withContext false, commentText) + } + if (success) { + updateCommentInSection(commentText) + } + } + + private fun updateCommentInSection(commentText: String) { + val groups = section.groups + groups.forEach { item -> + if (item is CommentItem && item.comment.commentId == commentWithInteraction?.comment?.commentId) { + updateCommentItem(item, commentText) + snackString("Comment edited") + } + } + } + + private fun updateCommentItem(item: CommentItem, commentText: String) { + item.comment.content = commentText + val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) + dateFormat.timeZone = TimeZone.getTimeZone("UTC") + item.comment.timestamp = dateFormat.format(System.currentTimeMillis()) + item.notifyChanged() + } + + private suspend fun handleNewComment(commentText: String) { + val success = withContext(Dispatchers.IO) { + CommentsAPI.comment( + mediaId, + if (interactionState == InteractionState.REPLY) commentWithInteraction?.comment?.commentId else null, + commentText + ) + } + success?.let { + if (interactionState == InteractionState.REPLY) { + commentWithInteraction?.repliesSection?.add( + 0, + CommentItem(it, buildMarkwon(), commentWithInteraction!!.repliesSection, this@CommentsActivity) + ) + } else { + section.add( + 0, + CommentItem(it, buildMarkwon(), section, this@CommentsActivity) + ) + } + } + } + + /** + * Builds the markwon instance with all the plugins + * @return the markwon instance + */ private fun buildMarkwon(): Markwon { val markwon = Markwon.builder(this) .usePlugin(SoftBreakAddsNewLinePlugin.create())