diff --git a/app/build.gradle b/app/build.gradle
index c8a85389..4c7abcbe 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -48,7 +48,7 @@ android {
buildTypes {
alpha {
applicationIdSuffix ".beta" // keep as beta by popular request
- versionNameSuffix "-alpha01"
+ versionNameSuffix "-alpha02"
manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_alpha", icon_placeholder_round: "@mipmap/ic_launcher_alpha_round"]
debuggable System.getenv("CI") == null
isDefault true
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2c1be903..4d994c18 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -109,7 +109,8 @@
android:name=".others.imagesearch.ImageSearchActivity"
android:parentActivityName=".MainActivity" />
+ android:name=".media.comments.CommentsActivity"
+ android:windowSoftInputMode="adjustResize|stateHidden" />
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
index 890e7132..35289fc7 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt
@@ -20,6 +20,7 @@ import ani.dantotsu.others.MalScraper
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
+import ani.dantotsu.toast
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
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 3d591d45..9e7fa491 100644
--- a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt
+++ b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt
@@ -9,8 +9,6 @@ import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import com.lagradost.nicehttp.Requests
import eu.kanade.tachiyomi.network.NetworkHelper
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -27,7 +25,7 @@ import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
object CommentsAPI {
- val address: String = "http://10.0.2.2:8081"
+ val address: String = "https://1224665.xyz:443"
val appSecret = BuildConfig.APP_SECRET
var authToken: String? = null
var userId: String? = null
@@ -55,7 +53,7 @@ object CommentsAPI {
val json = request.post(url)
val res = json.code == 200
if (!res) {
- errorReason(json.code)
+ errorReason(json.code, json.text)
}
return res
}
@@ -73,7 +71,7 @@ object CommentsAPI {
val json = request.post(url, requestBody = body.build())
val res = json.code == 200
if (!res) {
- errorReason(json.code)
+ errorReason(json.code, json.text)
return null
}
val parsed = try {
@@ -105,7 +103,32 @@ object CommentsAPI {
val json = request.delete(url)
val res = json.code == 200
if (!res) {
- errorReason(json.code)
+ errorReason(json.code, json.text)
+ }
+ return res
+ }
+
+ suspend fun editComment(commentId: Int, content: String): Boolean {
+ val url = "$address/comments/$commentId"
+ val body = FormBody.Builder()
+ .add("content", content)
+ .build()
+ val request = requestBuilder()
+ val json = request.put(url, requestBody = body)
+ val res = json.code == 200
+ if (!res) {
+ errorReason(json.code, json.text)
+ }
+ return res
+ }
+
+ suspend fun banUser(userId: String): Boolean {
+ val url = "$address/ban/$userId"
+ val request = requestBuilder()
+ val json = request.post(url)
+ val res = json.code == 200
+ if (!res) {
+ errorReason(json.code, json.text)
}
return res
}
@@ -125,7 +148,7 @@ object CommentsAPI {
val parsed = try {
Json.decodeFromString(json.text)
} catch (e: Exception) {
- println("comment: $e")
+ snackString("Failed to login to comments API: ${e.printStackTrace()}")
return
}
authToken = parsed.authToken
@@ -155,12 +178,12 @@ object CommentsAPI {
)
}
- private fun errorReason(code: Int) {
+ private fun errorReason(code: Int, reason: String? = null) {
val error = when (code) {
429 -> "Rate limited. :("
else -> "Failed to connect"
}
- snackString("Error $code: $error")
+ snackString("Error $code: ${reason ?: error}")
}
@SuppressLint("GetInstance")
@@ -221,7 +244,7 @@ data class Comment(
@SerialName("parent_comment_id")
val parentCommentId: Int?,
@SerialName("content")
- val content: String,
+ var content: String,
@SerialName("timestamp")
val timestamp: String,
@SerialName("deleted")
@@ -236,7 +259,13 @@ data class Comment(
@SerialName("username")
val username: String,
@SerialName("profile_picture_url")
- val profilePictureUrl: String?
+ val profilePictureUrl: String?,
+ @SerialName("is_mod")
+ @Serializable(with = NumericBooleanSerializer::class)
+ val isMod: Boolean? = null,
+ @SerialName("is_admin")
+ @Serializable(with = NumericBooleanSerializer::class)
+ val isAdmin: Boolean? = null
)
@Serializable
diff --git a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt
index 8b447d50..0f26e174 100644
--- a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt
@@ -76,7 +76,7 @@ class MediaInfoFragment : Fragment() {
loaded = true
//Youtube
- if (media.anime!!.youtube != null && PrefManager.getVal(PrefName.ShowYtButton)) {
+ if (media.anime?.youtube != null && PrefManager.getVal(PrefName.ShowYtButton)) {
binding.animeSourceYT.visibility = View.VISIBLE
binding.animeSourceYT.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(media.anime.youtube))
diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt
index fb19ec39..d0e50f3a 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt
@@ -15,10 +15,11 @@ import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
+import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
-import ani.dantotsu.media.comments.CommentsFragment
+import ani.dantotsu.media.comments.CommentsActivity
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.SourceSearchDialogFragment
@@ -60,11 +61,11 @@ class AnimeWatchAdapter(
val binding = holder.binding
_binding = binding
//CommentsAPI
- binding.animeComments.visibility = View.VISIBLE
+ binding.animeComments.visibility = if (CommentsAPI.userId == null) View.GONE else View.VISIBLE
binding.animeComments.setOnClickListener {
startActivity(
fragment.requireContext(),
- Intent(fragment.requireContext(), CommentsFragment::class.java)
+ Intent(fragment.requireContext(), CommentsActivity::class.java)
.putExtra("mediaId", media.id),
null
)
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 bd38d719..9057a60f 100644
--- a/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt
+++ b/app/src/main/java/ani/dantotsu/media/comments/CommentItem.kt
@@ -20,9 +20,10 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.TimeZone
-class CommentItem(private val comment: Comment,
+class CommentItem(val comment: Comment,
private val markwon: Markwon,
- private val section: Section
+ private val section: Section,
+ private val editCallback: (CommentItem) -> Unit
) : BindableItem() {
override fun bind(viewBinding: ItemCommentsBinding, position: Int) {
@@ -30,13 +31,29 @@ class CommentItem(private val comment: Comment,
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) View.VISIBLE else View.GONE
+ viewBinding.commentDelete.visibility = if (isUserComment || CommentsAPI.isAdmin || CommentsAPI.isMod) View.VISIBLE else View.GONE
+ viewBinding.commentBanUser.visibility = if (CommentsAPI.isAdmin || CommentsAPI.isMod) View.VISIBLE else View.GONE
viewBinding.commentEdit.visibility = if (isUserComment) View.VISIBLE else View.GONE
viewBinding.commentReply.visibility = View.GONE //TODO: implement reply
viewBinding.commentTotalReplies.visibility = View.GONE //TODO: implement reply
viewBinding.commentReply.setOnClickListener {
}
+ var isEditing = false
+ viewBinding.commentEdit.setOnClickListener {
+ if (!isEditing) {
+ viewBinding.commentEdit.text = "Cancel"
+ isEditing = true
+ editCallback(this)
+ } else {
+ viewBinding.commentEdit.text = "Edit"
+ isEditing = false
+ editCallback(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 {
@@ -47,6 +64,15 @@ class CommentItem(private val comment: Comment,
}
}
}
+ 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 {
@@ -137,4 +163,11 @@ class CommentItem(private val comment: Comment,
"now"
}
}
+
+ 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
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentsActivity.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentsActivity.kt
new file mode 100644
index 00000000..634f806a
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/media/comments/CommentsActivity.kt
@@ -0,0 +1,332 @@
+package ani.dantotsu.media.comments
+
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.text.TextWatcher
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.PopupMenu
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import ani.dantotsu.R
+import ani.dantotsu.connections.anilist.Anilist
+import ani.dantotsu.connections.comments.CommentsAPI
+import ani.dantotsu.databinding.ActivityCommentsBinding
+import ani.dantotsu.initActivity
+import ani.dantotsu.loadImage
+import ani.dantotsu.snackString
+import ani.dantotsu.themes.ThemeManager
+import com.bumptech.glide.Glide
+import com.bumptech.glide.RequestBuilder
+import com.bumptech.glide.RequestManager
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.load.resource.gif.GifDrawable
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.target.Target
+import com.xwray.groupie.GroupieAdapter
+import com.xwray.groupie.Section
+import io.noties.markwon.Markwon
+import io.noties.markwon.SoftBreakAddsNewLinePlugin
+import io.noties.markwon.editor.MarkwonEditor
+import io.noties.markwon.editor.MarkwonEditorTextWatcher
+import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
+import io.noties.markwon.ext.tables.TablePlugin
+import io.noties.markwon.ext.tasklist.TaskListPlugin
+import io.noties.markwon.html.HtmlPlugin
+import io.noties.markwon.image.AsyncDrawable
+import io.noties.markwon.image.glide.GlideImagesPlugin
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class CommentsActivity : AppCompatActivity() {
+ lateinit var binding: ActivityCommentsBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ ThemeManager(this).applyTheme()
+ binding = ActivityCommentsBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ initActivity(this)
+ //get the media id from the intent
+ val mediaId = intent.getIntExtra("mediaId", -1)
+ if (mediaId == -1) {
+ snackString("Invalid Media ID")
+ finish()
+ }
+
+ val adapter = GroupieAdapter()
+ val section = Section()
+ val markwon = buildMarkwon()
+
+ binding.commentUserAvatar.loadImage(Anilist.avatar)
+ binding.commentTitle.text = getText(R.string.comments)
+ val markwonEditor = MarkwonEditor.create(markwon)
+ binding.commentInput.addTextChangedListener(
+ MarkwonEditorTextWatcher.withProcess(
+ markwonEditor
+ )
+ )
+
+ var editing = false
+ var editingCommentId = -1
+ fun editCallback(comment: CommentItem) {
+ 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)
+ }
+
+ }
+
+ binding.commentsRefresh.setOnRefreshListener {
+ lifecycleScope.launch {
+ binding.commentsList.visibility = View.GONE
+ adapter.clear()
+ section.clear()
+ withContext(Dispatchers.IO) {
+ val comments = CommentsAPI.getCommentsForId(mediaId)
+ comments?.comments?.forEach {
+ withContext(Dispatchers.Main) {
+ section.add(CommentItem(it, buildMarkwon(), section) { comment ->
+ editCallback(comment)
+ })
+ }
+ }
+ }
+ adapter.add(section)
+ binding.commentsList.visibility = View.VISIBLE
+ 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)
+ comments?.comments?.forEach {
+ withContext(Dispatchers.Main) {
+ section.add(CommentItem(it, buildMarkwon(), section) { comment ->
+ editCallback(comment)
+ })
+ }
+ }
+ totalPages = comments?.totalPages ?: 1
+ }
+ binding.commentsProgressBar.visibility = View.GONE
+ binding.commentsList.visibility = View.VISIBLE
+ adapter.add(section)
+ }
+
+ binding.commentSort.setOnClickListener {
+ val popup = PopupMenu(this, it)
+ popup.setOnMenuItemClickListener { item ->
+ when (item.itemId) {
+ R.id.comment_sort_newest -> {
+ val groups = section.groups
+ groups.sortByDescending { comment ->
+ comment as CommentItem
+ comment.timestampToMillis(comment.comment.timestamp)
+ }
+ section.update(groups)
+ binding.commentsList.scrollToPosition(0)
+ }
+
+ R.id.comment_sort_oldest -> {
+ val groups = section.groups
+ groups.sortBy { comment ->
+ comment as CommentItem
+ comment.timestampToMillis(comment.comment.timestamp)
+ }
+ section.update(groups)
+ binding.commentsList.scrollToPosition(0)
+ }
+
+ R.id.comment_sort_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 -> {
+ val groups = section.groups
+ groups.sortBy { comment ->
+ comment as CommentItem
+ comment.comment.upvotes - comment.comment.downvotes
+ }
+ section.update(groups)
+ binding.commentsList.scrollToPosition(0)
+ }
+ }
+ true
+ }
+ popup.inflate(R.menu.comments_sort_menu)
+ popup.show()
+ }
+
+ var fetching = 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
+ ) {
+ 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)
+ })
+ }
+ }
+ totalPages = comments?.totalPages ?: 1
+ }
+ pagesLoaded++
+ fetching = false
+ }
+ }
+ }
+ }
+ })
+
+
+ binding.commentInput.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
+ }
+
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
+ }
+
+ override fun afterTextChanged(s: android.text.Editable?) {
+ if (binding.commentInput.text.length > 300) {
+ binding.commentInput.text.delete(300, binding.commentInput.text.length)
+ snackString("Comment cannot be longer than 300 characters")
+ }
+ }
+ })
+ binding.commentSend.setOnClickListener {
+ if (CommentsAPI.isBanned) {
+ snackString("You are banned from commenting :(")
+ return@setOnClickListener
+ }
+ 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
+ item.notifyChanged()
+ snackString("Comment edited")
+ }
+ }
+
+ }
+ }
+ } else {
+ val success = withContext(Dispatchers.IO) {
+ CommentsAPI.comment(mediaId, null, it)
+ }
+ if (success != null)
+ section.add(0, CommentItem(success, buildMarkwon(), section) { comment ->
+ editCallback(comment)
+ })
+ }
+ }
+ } else {
+ snackString("Comment cannot be empty")
+ }
+ }
+ }
+ }
+
+ private fun buildMarkwon(): Markwon {
+ val markwon = Markwon.builder(this)
+ .usePlugin(SoftBreakAddsNewLinePlugin.create())
+ .usePlugin(StrikethroughPlugin.create())
+ .usePlugin(HtmlPlugin.create())
+ .usePlugin(TablePlugin.create(this))
+ .usePlugin(TaskListPlugin.create(this))
+ .usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore {
+
+ private val requestManager: RequestManager =
+ Glide.with(this@CommentsActivity).apply {
+ addDefaultRequestListener(object : RequestListener {
+ override fun onResourceReady(
+ resource: Any,
+ model: Any,
+ target: Target,
+ dataSource: DataSource,
+ isFirstResource: Boolean
+ ): Boolean {
+ if (resource is GifDrawable) {
+ resource.start()
+ }
+ return false
+ }
+
+ override fun onLoadFailed(
+ e: GlideException?,
+ model: Any?,
+ target: Target,
+ isFirstResource: Boolean
+ ): Boolean {
+ return false
+ }
+ })
+ }
+
+ override fun load(drawable: AsyncDrawable): RequestBuilder {
+ return requestManager.load(drawable.destination)
+ }
+
+ override fun cancel(target: Target<*>) {
+ requestManager.clear(target)
+ }
+ }))
+ .build()
+ return markwon
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt
deleted file mode 100644
index 3acf0a71..00000000
--- a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-package ani.dantotsu.media.comments
-
-import android.graphics.drawable.Drawable
-import android.os.Bundle
-import android.text.TextWatcher
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.PopupMenu
-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.comments.Comment
-import ani.dantotsu.connections.comments.CommentsAPI
-import ani.dantotsu.databinding.FragmentCommentsBinding
-import ani.dantotsu.databinding.ItemCommentsBinding
-import ani.dantotsu.loadImage
-import ani.dantotsu.navBarHeight
-import ani.dantotsu.settings.saving.PrefManager
-import ani.dantotsu.settings.saving.PrefName
-import ani.dantotsu.snackString
-import ani.dantotsu.statusBarHeight
-import ani.dantotsu.themes.ThemeManager
-import com.bumptech.glide.Glide
-import com.bumptech.glide.RequestBuilder
-import com.bumptech.glide.RequestManager
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.load.resource.gif.GifDrawable
-import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.target.Target
-import com.xwray.groupie.GroupAdapter
-import com.xwray.groupie.GroupieAdapter
-import com.xwray.groupie.Section
-import com.xwray.groupie.viewbinding.GroupieViewHolder
-import io.noties.markwon.Markwon
-import io.noties.markwon.SoftBreakAddsNewLinePlugin
-import io.noties.markwon.editor.MarkwonEditor
-import io.noties.markwon.editor.MarkwonEditorTextWatcher
-import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
-import io.noties.markwon.ext.tables.TablePlugin
-import io.noties.markwon.ext.tasklist.TaskListPlugin
-import io.noties.markwon.html.HtmlPlugin
-import io.noties.markwon.image.AsyncDrawable
-import io.noties.markwon.image.glide.GlideImagesPlugin
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class CommentsFragment : AppCompatActivity(){
- lateinit var binding: FragmentCommentsBinding
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- ThemeManager(this).applyTheme()
- binding = FragmentCommentsBinding.inflate(layoutInflater)
- setContentView(binding.root)
- //get the media id from the intent
- val mediaId = intent.getIntExtra("mediaId", -1)
- if (mediaId == -1) {
- snackString("Invalid Media ID")
- finish()
- }
-
- val adapter = GroupieAdapter()
- val section = Section()
- val markwon = buildMarkwon()
-
- binding.commentsLayout.updateLayoutParams {
- topMargin = statusBarHeight
- bottomMargin = navBarHeight
- }
- binding.commentUserAvatar.loadImage(Anilist.avatar)
- binding.commentTitle.text = "Work in progress"
- val markwonEditor = MarkwonEditor.create(markwon)
- binding.commentInput.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(markwonEditor))
- binding.commentSend.setOnClickListener {
- if (CommentsAPI.isBanned) {
- snackString("You are banned from commenting :(")
- return@setOnClickListener
- }
- binding.commentInput.text.toString().let {
- if (it.isNotEmpty()) {
- binding.commentInput.text.clear()
- lifecycleScope.launch {
- val success = withContext(Dispatchers.IO) {
- CommentsAPI.comment(mediaId, null, it)
- }
- if (success != null)
- section.add(CommentItem(success, buildMarkwon(), section))
- }
- } else {
- snackString("Comment cannot be empty")
- }
- }
- }
- binding.commentInput.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
- }
-
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- }
-
- override fun afterTextChanged(s: android.text.Editable?) {
- if (binding.commentInput.text.length > 300) {
- binding.commentInput.text.delete(300, binding.commentInput.text.length)
- snackString("Comment cannot be longer than 300 characters")
- }
- }
- })
-
- binding.commentsList.adapter = adapter
- binding.commentsList.layoutManager = LinearLayoutManager(this)
- lifecycleScope.launch {
- withContext(Dispatchers.IO) {
- val comments = CommentsAPI.getCommentsForId(mediaId)
- comments?.comments?.forEach {
- withContext(Dispatchers.Main) {
- section.add(CommentItem(it, buildMarkwon(), section))
- }
- }
- }
- adapter.add(section)
- }
- binding.commentSort.setOnClickListener {
- val popup = PopupMenu(this, it)
- popup.setOnMenuItemClickListener { item ->
- true
- }
- popup.inflate(R.menu.comments_sort_menu)
- popup.show()
- }
- }
-
- private fun buildMarkwon(): Markwon {
- val markwon = Markwon.builder(this)
- .usePlugin(SoftBreakAddsNewLinePlugin.create())
- .usePlugin(StrikethroughPlugin.create())
- .usePlugin(HtmlPlugin.create())
- .usePlugin(TablePlugin.create(this))
- .usePlugin(TaskListPlugin.create(this))
- .usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore {
-
- private val requestManager: RequestManager = Glide.with(this@CommentsFragment).apply {
- addDefaultRequestListener(object : RequestListener {
- override fun onResourceReady(
- resource: Any,
- model: Any,
- target: Target,
- dataSource: DataSource,
- isFirstResource: Boolean
- ): Boolean {
- if (resource is GifDrawable) {
- resource.start()
- }
- return false
- }
-
- override fun onLoadFailed(
- e: GlideException?,
- model: Any?,
- target: Target,
- isFirstResource: Boolean
- ): Boolean {
- return false
- }
- })
- }
-
- override fun load(drawable: AsyncDrawable): RequestBuilder {
- return requestManager.load(drawable.destination)
- }
- override fun cancel(target: Target<*>) {
- requestManager.clear(target)
- }
- }))
- .build()
- return markwon
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
index 4cc73ea4..17836928 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
@@ -5,9 +5,6 @@ import android.app.AlertDialog
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
-import android.os.Bundle
-import ani.dantotsu.settings.FAQActivity
-import androidx.core.content.ContextCompat.startActivity
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.CheckBox
@@ -15,9 +12,11 @@ import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.NumberPicker
import androidx.core.content.ContextCompat
+import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
+import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
@@ -25,11 +24,13 @@ import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.SourceSearchDialogFragment
import ani.dantotsu.media.anime.handleProgress
+import ani.dantotsu.media.comments.CommentsActivity
import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.others.webview.CookieCatcher
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.MangaReadSources
import ani.dantotsu.parsers.MangaSources
+import ani.dantotsu.settings.FAQActivity
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
@@ -66,9 +67,19 @@ class MangaReadAdapter(
_binding = binding
binding.sourceTitle.setText(R.string.chaps)
+ binding.animeComments.visibility = if (CommentsAPI.userId == null) View.GONE else View.VISIBLE
+ binding.animeComments.setOnClickListener {
+ startActivity(
+ fragment.requireContext(),
+ Intent(fragment.requireContext(), CommentsActivity::class.java)
+ .putExtra("mediaId", media.id),
+ null
+ )
+ }
+
//Fuck u launch
binding.faqbutton.setOnClickListener {
- val intent = Intent(fragment.requireContext(), FAQActivity::class.java)
+ val intent = Intent(fragment.requireContext(), FAQActivity::class.java)
startActivity(fragment.requireContext(), intent, null)
}
@@ -447,11 +458,10 @@ class MangaReadAdapter(
if (media.manga.chapters!!.isNotEmpty()) {
binding.animeSourceNotFound.visibility = View.GONE
binding.faqbutton.visibility = View.GONE
- }
- else {
+ } else {
binding.animeSourceNotFound.visibility = View.VISIBLE
binding.faqbutton.visibility = View.VISIBLE
- }
+ }
} else {
binding.animeSourceContinue.visibility = View.GONE
binding.animeSourceNotFound.visibility = View.GONE
diff --git a/app/src/main/res/drawable/ic_crown.xml b/app/src/main/res/drawable/ic_crown.xml
new file mode 100644
index 00000000..00b5cc98
--- /dev/null
+++ b/app/src/main/res/drawable/ic_crown.xml
@@ -0,0 +1,14 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_shield.xml b/app/src/main/res/drawable/ic_shield.xml
new file mode 100644
index 00000000..c29effe3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_shield.xml
@@ -0,0 +1,14 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_comments.xml b/app/src/main/res/layout/activity_comments.xml
similarity index 82%
rename from app/src/main/res/layout/fragment_comments.xml
rename to app/src/main/res/layout/activity_comments.xml
index 22a867d3..bceeb792 100644
--- a/app/src/main/res/layout/fragment_comments.xml
+++ b/app/src/main/res/layout/activity_comments.xml
@@ -4,6 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:fitsSystemWindows="true"
android:id="@+id/commentsLayout">
-
+
+
+
+
-
-
+
-
diff --git a/app/src/main/res/layout/item_comments.xml b/app/src/main/res/layout/item_comments.xml
index 84836b1e..30669818 100644
--- a/app/src/main/res/layout/item_comments.xml
+++ b/app/src/main/res/layout/item_comments.xml
@@ -19,6 +19,7 @@
android:layout_width="36dp"
android:layout_height="36dp"
android:scaleType="center"
+ style="@style/CircularImageView"
app:srcCompat="@drawable/ic_round_add_circle_24"
tools:ignore="ContentDescription,ImageContrastCheck" />
@@ -33,6 +34,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c624da0d..71fc3417 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -659,8 +659,8 @@
Try Internal Cast (Experimental)
Comments
- Ascending
- Descending
- Most UpVoted
- Most DownVoted
+ newest
+ oldest
+ highest rated
+ lowest rated
diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml
index 4f0677d4..fd5798c0 100644
--- a/app/src/main/res/values/style.xml
+++ b/app/src/main/res/values/style.xml
@@ -91,5 +91,15 @@
- @android:color/transparent
+
+
+
+
+
\ No newline at end of file