231 lines
No EOL
9.4 KiB
Kotlin
231 lines
No EOL
9.4 KiB
Kotlin
package ani.dantotsu.media.comments
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.view.View
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import ani.dantotsu.R
|
|
import ani.dantotsu.connections.comments.Comment
|
|
import ani.dantotsu.connections.comments.CommentsAPI
|
|
import ani.dantotsu.copyToClipboard
|
|
import ani.dantotsu.currActivity
|
|
import ani.dantotsu.databinding.ItemCommentsBinding
|
|
import ani.dantotsu.loadImage
|
|
import ani.dantotsu.openLinkInBrowser
|
|
import ani.dantotsu.others.ImageViewDialog
|
|
import ani.dantotsu.snackString
|
|
import com.xwray.groupie.GroupieAdapter
|
|
import com.xwray.groupie.Section
|
|
|
|
import com.xwray.groupie.viewbinding.BindableItem
|
|
import io.noties.markwon.Markwon
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.SupervisorJob
|
|
import kotlinx.coroutines.launch
|
|
import java.text.SimpleDateFormat
|
|
import java.util.Date
|
|
import java.util.TimeZone
|
|
|
|
class CommentItem(val comment: Comment,
|
|
private val markwon: Markwon,
|
|
private val section: Section,
|
|
private val commentsActivity: CommentsActivity,
|
|
) : BindableItem<ItemCommentsBinding>() {
|
|
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)
|
|
}
|
|
|
|
@SuppressLint("SetTextI18n")
|
|
override fun bind(viewBinding: ItemCommentsBinding, position: Int) {
|
|
binding = viewBinding
|
|
viewBinding.commentRepliesList.layoutManager = LinearLayoutManager(currActivity())
|
|
viewBinding.commentRepliesList.adapter = adapter
|
|
val isUserComment = CommentsAPI.userId == comment.userId
|
|
val node = markwon.parse(comment.content)
|
|
val spanned = markwon.render(node)
|
|
markwon.setParsedMarkdown(viewBinding.commentText, viewBinding.commentText.setSpoilerText(spanned, markwon))
|
|
viewBinding.commentDelete.visibility = if (isUserComment || CommentsAPI.isAdmin || CommentsAPI.isMod) View.VISIBLE else View.GONE
|
|
viewBinding.commentBanUser.visibility = if ((CommentsAPI.isAdmin || CommentsAPI.isMod) && !isUserComment) View.VISIBLE else View.GONE
|
|
viewBinding.commentEdit.visibility = if (isUserComment) View.VISIBLE else View.GONE
|
|
if ((comment.replyCount ?: 0) > 0) {
|
|
viewBinding.commentTotalReplies.visibility = View.VISIBLE
|
|
viewBinding.commentRepliesDivider.visibility = View.VISIBLE
|
|
viewBinding.commentTotalReplies.text = "View ${comment.replyCount} repl${if (comment.replyCount == 1) "y" else "ies"}"
|
|
} else {
|
|
viewBinding.commentTotalReplies.visibility = View.GONE
|
|
viewBinding.commentRepliesDivider.visibility = View.GONE
|
|
}
|
|
viewBinding.commentReply.visibility = View.VISIBLE
|
|
viewBinding.commentTotalReplies.setOnClickListener {
|
|
if (repliesVisible) {
|
|
repliesSection.clear()
|
|
viewBinding.commentTotalReplies.text = "View ${comment.replyCount} repl${if (comment.replyCount == 1) "y" else "ies"}"
|
|
repliesVisible = false
|
|
} else {
|
|
viewBinding.commentTotalReplies.text = "Hide Replies"
|
|
repliesSection.clear()
|
|
commentsActivity.viewReplyCallback(this)
|
|
repliesVisible = true
|
|
}
|
|
}
|
|
|
|
viewBinding.commentUserName.setOnClickListener {
|
|
openLinkInBrowser("https://anilist.co/user/${comment.username}")
|
|
}
|
|
viewBinding.commentText.setOnLongClickListener {
|
|
copyToClipboard(comment.content)
|
|
true
|
|
}
|
|
|
|
viewBinding.commentEdit.setOnClickListener {
|
|
editing(!isEditing)
|
|
commentsActivity.editCallback(this)
|
|
}
|
|
viewBinding.commentReply.setOnClickListener {
|
|
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
|
|
viewBinding.commentDelete.setOnClickListener {
|
|
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
scope.launch {
|
|
val success = CommentsAPI.deleteComment(comment.commentId)
|
|
if (success) {
|
|
snackString("Comment Deleted")
|
|
section.remove(this@CommentItem)
|
|
}
|
|
}
|
|
}
|
|
viewBinding.commentBanUser.setOnClickListener {
|
|
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
scope.launch {
|
|
val success = CommentsAPI.banUser(comment.userId)
|
|
if (success) {
|
|
snackString("User Banned")
|
|
}
|
|
}
|
|
}
|
|
//fill the icon if the user has liked the comment
|
|
setVoteButtons(viewBinding)
|
|
viewBinding.commentUpVote.setOnClickListener {
|
|
val voteType = if (comment.userVoteType == 1) 0 else 1
|
|
val previousVoteType = comment.userVoteType
|
|
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
scope.launch {
|
|
val success = CommentsAPI.vote(comment.commentId, voteType)
|
|
if (success) {
|
|
comment.userVoteType = voteType
|
|
|
|
if (previousVoteType == -1) {
|
|
comment.downvotes -= 1
|
|
}
|
|
comment.upvotes += if (voteType == 1) 1 else -1
|
|
|
|
notifyChanged()
|
|
}
|
|
}
|
|
}
|
|
|
|
viewBinding.commentDownVote.setOnClickListener {
|
|
val voteType = if (comment.userVoteType == -1) 0 else -1
|
|
val previousVoteType = comment.userVoteType
|
|
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
scope.launch {
|
|
val success = CommentsAPI.vote(comment.commentId, voteType)
|
|
if (success) {
|
|
comment.userVoteType = voteType
|
|
|
|
if (previousVoteType == 1) {
|
|
comment.upvotes -= 1
|
|
}
|
|
comment.downvotes += if (voteType == -1) 1 else -1
|
|
|
|
notifyChanged()
|
|
}
|
|
}
|
|
}
|
|
viewBinding.commentTotalVotes.text = (comment.upvotes - comment.downvotes).toString()
|
|
viewBinding.commentUserAvatar
|
|
comment.profilePictureUrl?.let { viewBinding.commentUserAvatar.loadImage(it) }
|
|
viewBinding.commentUserName.text = comment.username
|
|
viewBinding.commentUserTime.text = "● ${formatTimestamp(comment.timestamp)}"
|
|
}
|
|
|
|
override fun getLayout(): Int {
|
|
return R.layout.item_comments
|
|
}
|
|
|
|
override fun initializeViewBinding(view: View): ItemCommentsBinding {
|
|
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 -> {
|
|
viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_active_24)
|
|
viewBinding.commentUpVote.alpha = 1f
|
|
viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
|
}
|
|
-1 -> {
|
|
viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
|
viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_active_24)
|
|
viewBinding.commentDownVote.alpha = 1f
|
|
}
|
|
else -> {
|
|
viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
|
viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun formatTimestamp(timestamp: String): String {
|
|
return try {
|
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
|
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
|
val parsedDate = dateFormat.parse(timestamp)
|
|
val currentDate = Date()
|
|
|
|
val diff = currentDate.time - (parsedDate?.time ?: 0)
|
|
|
|
val days = diff / (24 * 60 * 60 * 1000)
|
|
val hours = diff / (60 * 60 * 1000) % 24
|
|
val minutes = diff / (60 * 1000) % 60
|
|
|
|
return when {
|
|
days > 0 -> "$days days ago"
|
|
hours > 0 -> "$hours hours ago"
|
|
minutes > 0 -> "$minutes minutes ago"
|
|
else -> "just now"
|
|
}
|
|
} catch (e: Exception) {
|
|
"now"
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
fun timestampToMillis(timestamp: String): Long {
|
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
|
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
|
val parsedDate = dateFormat.parse(timestamp)
|
|
return parsedDate?.time ?: 0
|
|
}
|
|
}
|
|
} |