feat: reviews
This commit is contained in:
parent
831b99ae40
commit
a0fabd3ca6
16 changed files with 642 additions and 34 deletions
|
@ -517,26 +517,24 @@ class MediaInfoFragment : Fragment() {
|
|||
}
|
||||
parent.addView(root)
|
||||
}
|
||||
}
|
||||
|
||||
ItemTitleSearchBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
parent,
|
||||
false
|
||||
).apply {
|
||||
ItemTitleSearchBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
parent,
|
||||
false
|
||||
).apply {
|
||||
|
||||
titleSearchImage.loadImage(media.banner ?: media.cover)
|
||||
titleSearchText.text =
|
||||
getString(R.string.search_title, media.mainName())
|
||||
titleSearchCard.setSafeOnClickListener {
|
||||
val query = Intent(requireContext(), SearchActivity::class.java)
|
||||
.putExtra("type", "ANIME")
|
||||
.putExtra("query", media.mainName())
|
||||
.putExtra("search", true)
|
||||
ContextCompat.startActivity(requireContext(), query, null)
|
||||
}
|
||||
|
||||
parent.addView(root)
|
||||
titleSearchImage.loadImage(media.banner ?: media.cover)
|
||||
titleSearchText.text =
|
||||
getString(R.string.reviews)
|
||||
titleSearchCard.setSafeOnClickListener {
|
||||
val query = Intent(requireContext(), ReviewActivity::class.java)
|
||||
.putExtra("mediaId", media.id)
|
||||
ContextCompat.startActivity(requireContext(), query, null)
|
||||
}
|
||||
|
||||
parent.addView(root)
|
||||
}
|
||||
|
||||
ItemTitleRecyclerBinding.inflate(
|
||||
|
|
149
app/src/main/java/ani/dantotsu/media/ReviewActivity.kt
Normal file
149
app/src/main/java/ani/dantotsu/media/ReviewActivity.kt
Normal file
|
@ -0,0 +1,149 @@
|
|||
package ani.dantotsu.media
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableString
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.databinding.ActivityFollowBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.profile.FollowerItem
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ReviewActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityFollowBinding
|
||||
val adapter = GroupieAdapter()
|
||||
private val reviews = mutableListOf<Query.Review>()
|
||||
var mediaId = 0
|
||||
private var currentPage: Int = 1
|
||||
private var hasNextPage: Boolean = true
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityFollowBinding.inflate(layoutInflater)
|
||||
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
}
|
||||
binding.listFrameLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = navBarHeight
|
||||
}
|
||||
setContentView(binding.root)
|
||||
mediaId = intent.getIntExtra("mediaId", -1)
|
||||
if (mediaId == -1) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
binding.followerGrid.visibility = View.GONE
|
||||
binding.followerList.visibility = View.GONE
|
||||
binding.followFilterButton.visibility = View.GONE
|
||||
binding.listTitle.text = getString(R.string.reviews)
|
||||
binding.listRecyclerView.adapter = adapter
|
||||
binding.listRecyclerView.layoutManager = LinearLayoutManager(
|
||||
this,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
binding.listProgressBar.visibility = View.VISIBLE
|
||||
binding.listBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val response = Anilist.query.getReviews(mediaId)
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.listProgressBar.visibility = View.GONE
|
||||
binding.listRecyclerView.setOnTouchListener { _, event ->
|
||||
if (event?.action == MotionEvent.ACTION_UP) {
|
||||
if (hasNextPage && !binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible
|
||||
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
|
||||
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
|
||||
) {
|
||||
binding.followRefresh.visibility = ViewGroup.VISIBLE
|
||||
loadPage(++currentPage) {
|
||||
binding.followRefresh.visibility = ViewGroup.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
currentPage = response?.data?.page?.pageInfo?.currentPage ?: 1
|
||||
hasNextPage = response?.data?.page?.pageInfo?.hasNextPage ?: false
|
||||
response?.data?.page?.reviews?.let {
|
||||
reviews.addAll(it)
|
||||
fillList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadPage(page: Int, callback: () -> Unit) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val response = Anilist.query.getReviews(mediaId, page)
|
||||
currentPage = response?.data?.page?.pageInfo?.currentPage ?: 1
|
||||
hasNextPage = response?.data?.page?.pageInfo?.hasNextPage ?: false
|
||||
withContext(Dispatchers.Main) {
|
||||
response?.data?.page?.reviews?.let {
|
||||
reviews.addAll(it)
|
||||
fillList()
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillList() {
|
||||
adapter.clear()
|
||||
reviews.forEach {
|
||||
val username = it.user?.name ?: "Unknown"
|
||||
val name = SpannableString(username + " - " + it.score)
|
||||
//change the size of the score
|
||||
name.setSpan(
|
||||
android.text.style.RelativeSizeSpan(0.9f),
|
||||
0,
|
||||
name.length,
|
||||
android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
//give the text an underline
|
||||
name.setSpan(
|
||||
android.text.style.UnderlineSpan(),
|
||||
username.length + 3,
|
||||
name.length,
|
||||
android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
adapter.add(
|
||||
FollowerItem(
|
||||
it.id,
|
||||
name,
|
||||
it.user?.avatar?.medium,
|
||||
it.user?.bannerImage,
|
||||
it.summary,
|
||||
this::onUserClick
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onUserClick(userId: Int) {
|
||||
val review = reviews.find { it.id == userId }
|
||||
if (review != null) {
|
||||
startActivity(Intent(this, ReviewViewActivity::class.java).putExtra("review", review))
|
||||
}
|
||||
}
|
||||
}
|
178
app/src/main/java/ani/dantotsu/media/ReviewViewActivity.kt
Normal file
178
app/src/main/java/ani/dantotsu/media/ReviewViewActivity.kt
Normal file
|
@ -0,0 +1,178 @@
|
|||
package ani.dantotsu.media
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.databinding.ActivityReviewViewBinding
|
||||
import ani.dantotsu.getThemeColor
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.profile.activity.ActivityItemBuilder
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.toast
|
||||
import ani.dantotsu.util.AniMarkdown
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ReviewViewActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityReviewViewBinding
|
||||
private lateinit var review: Query.Review
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityReviewViewBinding.inflate(layoutInflater)
|
||||
binding.userContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
}
|
||||
binding.reviewContent.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin += navBarHeight
|
||||
}
|
||||
setContentView(binding.root)
|
||||
review = intent.getSerializableExtra("review") as Query.Review
|
||||
binding.userName.text = review.user?.name
|
||||
binding.userAvatar.loadImage(review.user?.avatar?.medium)
|
||||
binding.userTime.text = ActivityItemBuilder.getDateTime(review.createdAt)
|
||||
binding.profileUserBio.settings.loadWithOverviewMode = true
|
||||
binding.profileUserBio.settings.useWideViewPort = true
|
||||
binding.profileUserBio.setInitialScale(1)
|
||||
val styledHtml = AniMarkdown.getFullAniHTML(
|
||||
review.body,
|
||||
ContextCompat.getColor(this, R.color.bg_opp)
|
||||
)
|
||||
binding.profileUserBio.loadDataWithBaseURL(
|
||||
null,
|
||||
styledHtml,
|
||||
"text/html",
|
||||
"utf-8",
|
||||
null
|
||||
)
|
||||
binding.profileUserBio.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
this,
|
||||
android.R.color.transparent
|
||||
)
|
||||
)
|
||||
binding.profileUserBio.setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
binding.profileUserBio.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
binding.profileUserBio.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
this@ReviewViewActivity,
|
||||
android.R.color.transparent
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
userVote(review.userRating)
|
||||
enableVote()
|
||||
binding.voteCount.text = review.rating.toString()
|
||||
binding.voteText.text = getString(
|
||||
R.string.vote_out_of_total,
|
||||
review.rating.toString(),
|
||||
review.ratingAmount.toString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun userVote(type: String) {
|
||||
val selectedColor = getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
val unselectedColor = getThemeColor(androidx.appcompat.R.attr.colorControlNormal)
|
||||
when (type) {
|
||||
"NO_VOTE" -> {
|
||||
binding.upvote.setColorFilter(unselectedColor)
|
||||
binding.downvote.setColorFilter(unselectedColor)
|
||||
}
|
||||
|
||||
"UP_VOTE" -> {
|
||||
binding.upvote.setColorFilter(selectedColor)
|
||||
binding.downvote.setColorFilter(unselectedColor)
|
||||
}
|
||||
|
||||
"DOWN_VOTE" -> {
|
||||
binding.upvote.setColorFilter(unselectedColor)
|
||||
binding.downvote.setColorFilter(selectedColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rateReview(rating: String) {
|
||||
disableVote()
|
||||
lifecycleScope.launch {
|
||||
val result = Anilist.mutation.rateReview(review.id, rating)
|
||||
if (result != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
val res = result.data.rateReview
|
||||
review.rating = res.rating
|
||||
review.ratingAmount = res.ratingAmount
|
||||
review.userRating = res.userRating
|
||||
userVote(review.userRating)
|
||||
binding.voteCount.text = review.rating.toString()
|
||||
binding.voteText.text = getString(
|
||||
R.string.vote_out_of_total,
|
||||
review.rating.toString(),
|
||||
review.ratingAmount.toString()
|
||||
)
|
||||
userVote(review.userRating)
|
||||
enableVote()
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
toast(
|
||||
getString(R.string.error_message, "response is null")
|
||||
)
|
||||
enableVote()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableVote() {
|
||||
binding.upvote.setOnClickListener(null)
|
||||
binding.downvote.setOnClickListener(null)
|
||||
binding.upvote.isEnabled = false
|
||||
binding.downvote.isEnabled = false
|
||||
}
|
||||
|
||||
private fun enableVote() {
|
||||
binding.upvote.setOnClickListener {
|
||||
if (review.userRating == "UP_VOTE") {
|
||||
rateReview("NO_VOTE")
|
||||
} else {
|
||||
rateReview("UP_VOTE")
|
||||
}
|
||||
disableVote()
|
||||
}
|
||||
binding.downvote.setOnClickListener {
|
||||
if (review.userRating == "DOWN_VOTE") {
|
||||
rateReview("NO_VOTE")
|
||||
} else {
|
||||
rateReview("DOWN_VOTE")
|
||||
}
|
||||
disableVote()
|
||||
}
|
||||
binding.upvote.isEnabled = true
|
||||
binding.downvote.isEnabled = true
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue