mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 14:20:30 +05:30
Merge pull request #2890 from faisalcodes/master
Fixes #2878 : Using multi-level comments display.
This commit is contained in:
commit
d9985e2567
@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
|
||||
data class Comment(
|
||||
val author: String,
|
||||
val commentId: String,
|
||||
val commentText: String,
|
||||
val commentText: String?,
|
||||
val commentedTime: String,
|
||||
val commentorUrl: String,
|
||||
val repliesPage: String? = null,
|
||||
|
@ -18,4 +18,5 @@ object IntentData {
|
||||
const val downloading = "downloading"
|
||||
const val openAudioPlayer = "openAudioPlayer"
|
||||
const val fragmentToOpen = "fragmentToOpen"
|
||||
const val replyPage = "replyPage"
|
||||
}
|
||||
|
@ -1,42 +1,33 @@
|
||||
package com.github.libretube.ui.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.obj.Comment
|
||||
import com.github.libretube.api.obj.CommentsPage
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.CommentsRowBinding
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.extensions.formatShort
|
||||
import com.github.libretube.ui.fragments.CommentsRepliesFragment
|
||||
import com.github.libretube.ui.viewholders.CommentsViewHolder
|
||||
import com.github.libretube.util.ClipboardHelper
|
||||
import com.github.libretube.util.ImageHelper
|
||||
import com.github.libretube.util.NavigationHelper
|
||||
import com.github.libretube.util.TextUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class CommentsAdapter(
|
||||
private val fragment: Fragment?,
|
||||
private val videoId: String,
|
||||
private val comments: MutableList<Comment>,
|
||||
private val isRepliesAdapter: Boolean = false,
|
||||
private val dismiss: () -> Unit
|
||||
) : RecyclerView.Adapter<CommentsViewHolder>() {
|
||||
|
||||
private var isLoading = false
|
||||
private lateinit var repliesPage: CommentsPage
|
||||
|
||||
fun clear() {
|
||||
val size: Int = comments.size
|
||||
comments.clear()
|
||||
@ -59,15 +50,8 @@ class CommentsAdapter(
|
||||
override fun onBindViewHolder(holder: CommentsViewHolder, position: Int) {
|
||||
val comment = comments[position]
|
||||
holder.binding.apply {
|
||||
if (isRepliesAdapter) {
|
||||
root.scaleX = REPLIES_ADAPTER_SCALE
|
||||
root.scaleY = REPLIES_ADAPTER_SCALE
|
||||
commentorImage.scaleX = REPLIES_ADAPTER_SCALE
|
||||
commentorImage.scaleY = REPLIES_ADAPTER_SCALE
|
||||
}
|
||||
|
||||
commentInfos.text = comment.author + TextUtils.SEPARATOR + comment.commentedTime
|
||||
commentText.text = comment.commentText.parseAsHtml()
|
||||
commentText.text = comment.commentText?.parseAsHtml()
|
||||
|
||||
ImageHelper.loadImage(comment.thumbnail, commentorImage)
|
||||
likesTextView.text = comment.likeCount.formatShort()
|
||||
@ -85,79 +69,31 @@ class CommentsAdapter(
|
||||
dismiss.invoke()
|
||||
}
|
||||
|
||||
repliesRecView.layoutManager = LinearLayoutManager(root.context)
|
||||
val repliesAdapter = CommentsAdapter(videoId, mutableListOf(), true, dismiss)
|
||||
repliesRecView.adapter = repliesAdapter
|
||||
if (!isRepliesAdapter && comment.repliesPage != null) {
|
||||
val repliesFragment = CommentsRepliesFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(IntentData.videoId, videoId)
|
||||
putString(IntentData.replyPage, comment.repliesPage)
|
||||
}
|
||||
}
|
||||
root.setOnClickListener {
|
||||
showMoreReplies(comment.repliesPage, showMore, repliesAdapter)
|
||||
fragment!!.parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.commentFragContainer, repliesFragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
root.setOnLongClickListener {
|
||||
ClipboardHelper(root.context).save(comment.commentText)
|
||||
ClipboardHelper(root.context).save(comment.commentText ?: "")
|
||||
Toast.makeText(root.context, R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMoreReplies(
|
||||
nextPage: String,
|
||||
showMoreBtn: Button,
|
||||
repliesAdapter: CommentsAdapter
|
||||
) {
|
||||
when (repliesAdapter.itemCount) {
|
||||
0 -> {
|
||||
fetchReplies(nextPage) {
|
||||
repliesAdapter.updateItems(it.comments)
|
||||
if (repliesPage.nextpage == null) {
|
||||
showMoreBtn.visibility = View.GONE
|
||||
return@fetchReplies
|
||||
}
|
||||
showMoreBtn.visibility = View.VISIBLE
|
||||
showMoreBtn.setOnClickListener { view ->
|
||||
if (repliesPage.nextpage == null) {
|
||||
view.visibility = View.GONE
|
||||
return@setOnClickListener
|
||||
}
|
||||
fetchReplies(
|
||||
repliesPage.nextpage!!
|
||||
) {
|
||||
repliesAdapter.updateItems(repliesPage.comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
repliesAdapter.clear()
|
||||
showMoreBtn.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return comments.size
|
||||
}
|
||||
|
||||
private fun fetchReplies(nextPage: String, onFinished: (CommentsPage) -> Unit) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (isLoading) return@launch
|
||||
isLoading = true
|
||||
repliesPage = try {
|
||||
RetrofitInstance.api.getCommentsNextPage(videoId, nextPage)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@launch
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
onFinished.invoke(repliesPage)
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val REPLIES_ADAPTER_SCALE = 0.9f
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
package com.github.libretube.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.FragmentCommentsBinding
|
||||
import com.github.libretube.ui.adapters.CommentsAdapter
|
||||
import com.github.libretube.ui.models.CommentsViewModel
|
||||
|
||||
class CommentsMainFragment : Fragment() {
|
||||
private lateinit var binding: FragmentCommentsBinding
|
||||
private lateinit var commentsAdapter: CommentsAdapter
|
||||
|
||||
private val viewModel: CommentsViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentCommentsBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.commentsRV.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.commentsRV.setItemViewCacheSize(20)
|
||||
|
||||
binding.commentsRV.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (!binding.commentsRV.canScrollVertically(1)) {
|
||||
viewModel.fetchNextComments()
|
||||
}
|
||||
}
|
||||
|
||||
commentsAdapter = CommentsAdapter(
|
||||
this,
|
||||
viewModel.videoId!!,
|
||||
viewModel.commentsPage.value?.comments.orEmpty().toMutableList()
|
||||
) {
|
||||
viewModel.commentsSheetDismiss?.invoke()
|
||||
}
|
||||
binding.commentsRV.adapter = commentsAdapter
|
||||
|
||||
if (viewModel.commentsPage.value?.comments.orEmpty().isEmpty()) {
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
viewModel.fetchComments()
|
||||
}
|
||||
|
||||
// listen for new comments to be loaded
|
||||
viewModel.commentsPage.observe(viewLifecycleOwner) {
|
||||
it ?: return@observe
|
||||
binding.progress.visibility = View.GONE
|
||||
if (it.disabled == true) {
|
||||
binding.errorTV.visibility = View.VISIBLE
|
||||
return@observe
|
||||
}
|
||||
if (it.comments.isEmpty()) {
|
||||
binding.errorTV.text = getString(R.string.no_comments_available)
|
||||
binding.errorTV.visibility = View.VISIBLE
|
||||
return@observe
|
||||
}
|
||||
commentsAdapter.updateItems(
|
||||
// only add the new comments to the recycler view
|
||||
it.comments.subList(commentsAdapter.itemCount, it.comments.size)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package com.github.libretube.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.obj.CommentsPage
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.FragmentCommentsBinding
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.ui.adapters.CommentsAdapter
|
||||
import com.github.libretube.ui.models.CommentsViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class CommentsRepliesFragment : Fragment() {
|
||||
private lateinit var binding: FragmentCommentsBinding
|
||||
private lateinit var repliesPage: CommentsPage
|
||||
private lateinit var repliesAdapter: CommentsAdapter
|
||||
private val viewModel: CommentsViewModel by activityViewModels()
|
||||
|
||||
private var isLoading = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentCommentsBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val videoId = arguments?.getString(IntentData.videoId) ?: ""
|
||||
val nextPage = arguments?.getString(IntentData.replyPage) ?: ""
|
||||
|
||||
repliesAdapter = CommentsAdapter(null, videoId, mutableListOf(), true) {
|
||||
viewModel.commentsSheetDismiss?.invoke()
|
||||
}
|
||||
|
||||
binding.commentsRV.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.commentsRV.adapter = repliesAdapter
|
||||
|
||||
binding.commentsRV.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (!binding.commentsRV.canScrollVertically(1) &&
|
||||
::repliesPage.isInitialized &&
|
||||
repliesPage.nextpage != null
|
||||
) {
|
||||
fetchReplies(videoId, repliesPage.nextpage!!) {
|
||||
repliesAdapter.updateItems(repliesPage.comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadInitialReplies(videoId, nextPage, repliesAdapter)
|
||||
}
|
||||
|
||||
private fun loadInitialReplies(
|
||||
videoId: String,
|
||||
nextPage: String,
|
||||
repliesAdapter: CommentsAdapter
|
||||
) {
|
||||
when (repliesAdapter.itemCount) {
|
||||
0 -> {
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
fetchReplies(videoId, nextPage) {
|
||||
repliesAdapter.updateItems(it.comments)
|
||||
binding.progress.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
repliesAdapter.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchReplies(
|
||||
videoId: String,
|
||||
nextPage: String,
|
||||
onFinished: (CommentsPage) -> Unit
|
||||
) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (isLoading) return@launch
|
||||
isLoading = true
|
||||
repliesPage = try {
|
||||
RetrofitInstance.api.getCommentsNextPage(videoId, nextPage)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@launch
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
onFinished.invoke(repliesPage)
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ class CommentsViewModel : ViewModel() {
|
||||
|
||||
var videoId: String? = null
|
||||
var maxHeight: Int = 0
|
||||
var commentsSheetDismiss: (() -> Unit)? = null
|
||||
|
||||
fun fetchComments() {
|
||||
videoId ?: return
|
||||
|
@ -1,25 +1,24 @@
|
||||
package com.github.libretube.ui.sheets
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import android.view.WindowManager
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.CommentsSheetBinding
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.ui.adapters.CommentsAdapter
|
||||
import com.github.libretube.ui.fragments.CommentsMainFragment
|
||||
import com.github.libretube.ui.fragments.CommentsRepliesFragment
|
||||
import com.github.libretube.ui.models.CommentsViewModel
|
||||
|
||||
class CommentsSheet : ExpandedBottomSheet() {
|
||||
private lateinit var binding: CommentsSheetBinding
|
||||
|
||||
private lateinit var commentsAdapter: CommentsAdapter
|
||||
|
||||
private val viewModel: CommentsViewModel by activityViewModels()
|
||||
private val commentsViewModel: CommentsViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@ -33,56 +32,83 @@ class CommentsSheet : ExpandedBottomSheet() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.dragHandle.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
binding.dragHandle.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
// limit the recyclerview height to not cover the video
|
||||
binding.commentsRV.updateLayoutParams {
|
||||
height = viewModel.maxHeight - (binding.dragHandle.height + 20.dpToPx().toInt())
|
||||
commentsViewModel.commentsSheetDismiss = this::dismiss
|
||||
|
||||
binding.apply {
|
||||
dragHandle.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
dragHandle.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
|
||||
// limit the recyclerview height to not cover the video
|
||||
binding.standardBottomSheet.layoutParams =
|
||||
binding.commentFragContainer.layoutParams.apply {
|
||||
height = commentsViewModel.maxHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
binding.commentsRV.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.commentsRV.setItemViewCacheSize(20)
|
||||
|
||||
binding.commentsRV.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (!binding.commentsRV.canScrollVertically(1)) {
|
||||
viewModel.fetchNextComments()
|
||||
btnBack.setOnClickListener {
|
||||
if (childFragmentManager.backStackEntryCount > 0) {
|
||||
childFragmentManager.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
commentsAdapter = CommentsAdapter(
|
||||
viewModel.videoId!!,
|
||||
viewModel.commentsPage.value?.comments.orEmpty().toMutableList()
|
||||
) {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
binding.commentsRV.adapter = commentsAdapter
|
||||
|
||||
if (viewModel.commentsPage.value?.comments.orEmpty().isEmpty()) {
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
viewModel.fetchComments()
|
||||
btnClose.setOnClickListener { dismiss() }
|
||||
}
|
||||
|
||||
// listen for new comments to be loaded
|
||||
viewModel.commentsPage.observe(viewLifecycleOwner) {
|
||||
it ?: return@observe
|
||||
binding.progress.visibility = View.GONE
|
||||
if (it.disabled == true) {
|
||||
binding.errorTV.visibility = View.VISIBLE
|
||||
return@observe
|
||||
}
|
||||
if (it.comments.isEmpty()) {
|
||||
binding.errorTV.text = getString(R.string.no_comments_available)
|
||||
binding.errorTV.visibility = View.VISIBLE
|
||||
return@observe
|
||||
}
|
||||
commentsAdapter.updateItems(
|
||||
// only add the new comments to the recycler view
|
||||
it.comments.subList(commentsAdapter.itemCount, it.comments.size)
|
||||
)
|
||||
childFragmentManager.apply {
|
||||
addOnBackStackChangedListener(this@CommentsSheet::onFragmentChanged)
|
||||
|
||||
beginTransaction()
|
||||
.replace(R.id.commentFragContainer, CommentsMainFragment())
|
||||
.runOnCommit(this@CommentsSheet::onFragmentChanged)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFragmentChanged() {
|
||||
childFragmentManager.findFragmentById(R.id.commentFragContainer)?.let {
|
||||
when (it) {
|
||||
is CommentsRepliesFragment -> {
|
||||
binding.btnBack.visibility = View.VISIBLE
|
||||
binding.commentsTitle.text = getString(R.string.replies)
|
||||
}
|
||||
else -> {
|
||||
binding.btnBack.visibility = View.GONE
|
||||
binding.commentsTitle.text = getString(R.string.comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
commentsViewModel.commentsSheetDismiss = null
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
||||
dialog.apply {
|
||||
setOnKeyListener { _, keyCode, _ ->
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (childFragmentManager.backStackEntryCount > 0) {
|
||||
childFragmentManager.popBackStack()
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
}
|
||||
return@setOnKeyListener false
|
||||
}
|
||||
|
||||
window?.let {
|
||||
it.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
|
||||
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
||||
}
|
||||
|
||||
setCanceledOnTouchOutside(false)
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/rounded_ripple">
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -25,9 +25,10 @@
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/commentor_image"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@android:color/darker_gray"
|
||||
app:shapeAppearance="@style/CircleImageView"
|
||||
app:srcCompat="@mipmap/ic_launcher" />
|
||||
|
||||
@ -47,8 +48,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_color_secondary"
|
||||
tools:text="Author and Time" />
|
||||
|
||||
<ImageView
|
||||
@ -74,7 +75,9 @@
|
||||
android:id="@+id/comment_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textSize="15sp"
|
||||
android:autoLink="web"
|
||||
tools:text="Comment Text" />
|
||||
|
||||
@ -120,7 +123,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
tools:text="LikeCount" />
|
||||
tools:text="ReplyCount" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -128,26 +131,5 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/replies_recView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/show_more"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/show_more"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:cornerRadius="20dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -9,7 +10,6 @@
|
||||
style="@style/Widget.Material3.BottomSheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="20dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<LinearLayout
|
||||
@ -20,35 +20,56 @@
|
||||
<!-- Drag handle for accessibility -->
|
||||
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
|
||||
android:id="@+id/drag_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="0dp"
|
||||
app:tint="@color/drag_handle_color" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingBottom="7dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/btnBack"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:visibility="gone"
|
||||
android:src="?homeAsUpIndicator"
|
||||
android:padding="7dp"
|
||||
android:background="?selectableItemBackgroundBorderless" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/commentsTitle"
|
||||
style="@style/TextAppearance.Material3.ActionBar.Title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
tools:text="Title" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/btnClose"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:src="@drawable/ic_close"
|
||||
android:padding="7dp"
|
||||
android:background="?selectableItemBackgroundBorderless" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/commentFragContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/commentsRV"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/errorTV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:text="@string/comments_disabled"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
29
app/src/main/res/layout/fragment_comments.xml
Normal file
29
app/src/main/res/layout/fragment_comments.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/commentsRV"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="15dp"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/errorTV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:text="@string/comments_disabled"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
5
app/src/main/res/values-night/colors.xml
Normal file
5
app/src/main/res/values-night/colors.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="text_color_secondary">#BFBFBF</color>
|
||||
<color name="drag_handle_color">#3A3A3A</color>
|
||||
</resources>
|
@ -3,6 +3,8 @@
|
||||
<color name="duration_background_color">#AA000000</color>
|
||||
<color name="duration_text_color">#EEFFFFFF</color>
|
||||
<color name="shortcut_color">#0061A6</color>
|
||||
<color name="text_color_secondary">#505050</color>
|
||||
<color name="drag_handle_color">#CCCCCC</color>
|
||||
|
||||
<color name="blue_md_theme_light_primary">#0058CB</color>
|
||||
<color name="blue_md_theme_light_onPrimary">#FFFFFF</color>
|
||||
|
@ -72,6 +72,7 @@
|
||||
<string name="noInternet">Connect to the Internet first.</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="comments">Comments</string>
|
||||
<string name="replies">Replies</string>
|
||||
<string name="choose_filter">Choose search filter</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="all">All</string>
|
||||
|
Loading…
Reference in New Issue
Block a user