feat: comment notifications in notification section

This commit is contained in:
rebelonion 2024-03-17 20:05:38 -05:00
parent 25b85569fe
commit c47d1afa1a
13 changed files with 201 additions and 65 deletions

View file

@ -13,7 +13,7 @@ import ani.dantotsu.aniyomi.anime.custom.AppModule
import ani.dantotsu.aniyomi.anime.custom.PreferenceModule import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.notifications.CommentNotificationWorker import ani.dantotsu.notifications.comment.CommentNotificationWorker
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
import ani.dantotsu.others.DisabledReports import ani.dantotsu.others.DisabledReports
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources
@ -134,7 +134,8 @@ class App : MultiDexApplication() {
// CommentNotificationWorker // CommentNotificationWorker
val commentInterval = CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)] val commentInterval = CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)]
if (commentInterval.toInt() != 0) { if (commentInterval.toInt() != 0) {
val recurringWork = PeriodicWorkRequest.Builder(CommentNotificationWorker::class.java, val recurringWork = PeriodicWorkRequest.Builder(
CommentNotificationWorker::class.java,
commentInterval, java.util.concurrent.TimeUnit.MINUTES) commentInterval, java.util.concurrent.TimeUnit.MINUTES)
.setConstraints(constraints) .setConstraints(constraints)
.build() .build()
@ -146,7 +147,8 @@ class App : MultiDexApplication() {
} else { } else {
androidx.work.WorkManager.getInstance(this).cancelUniqueWork(CommentNotificationWorker.WORK_NAME) androidx.work.WorkManager.getInstance(this).cancelUniqueWork(CommentNotificationWorker.WORK_NAME)
//run once //run once
androidx.work.WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.Companion.from(CommentNotificationWorker::class.java)) androidx.work.WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.Companion.from(
CommentNotificationWorker::class.java))
} }
// AnilistNotificationWorker // AnilistNotificationWorker

View file

@ -54,6 +54,8 @@ class AnilistQueries {
Anilist.chapterRead = user.statistics?.manga?.chaptersRead Anilist.chapterRead = user.statistics?.manga?.chaptersRead
Anilist.adult = user.options?.displayAdultContent ?: false Anilist.adult = user.options?.displayAdultContent ?: false
Anilist.unreadNotificationCount = user.unreadNotificationCount ?: 0 Anilist.unreadNotificationCount = user.unreadNotificationCount ?: 0
val unread = PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications)
Anilist.unreadNotificationCount += unread
return true return true
} }
@ -1392,7 +1394,10 @@ Page(page:$page,perPage:50) {
"""{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){notifications(resetNotificationCount:$reset){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""", """{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){notifications(resetNotificationCount:$reset){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""",
force = true force = true
) )
if (res != null) { if (res != null && resetNotification) {
val commentNotifications = PrefManager.getVal(PrefName.UnreadCommentNotifications, 0)
res.data.user.unreadNotificationCount += commentNotifications
PrefManager.setVal(PrefName.UnreadCommentNotifications, 0)
Anilist.unreadNotificationCount = 0 Anilist.unreadNotificationCount = 0
} }
return res return res

View file

@ -20,7 +20,9 @@ enum class NotificationType(val value: String) {
RELATED_MEDIA_ADDITION("RELATED_MEDIA_ADDITION"), RELATED_MEDIA_ADDITION("RELATED_MEDIA_ADDITION"),
MEDIA_DATA_CHANGE("MEDIA_DATA_CHANGE"), MEDIA_DATA_CHANGE("MEDIA_DATA_CHANGE"),
MEDIA_MERGE("MEDIA_MERGE"), MEDIA_MERGE("MEDIA_MERGE"),
MEDIA_DELETION("MEDIA_DELETION") MEDIA_DELETION("MEDIA_DELETION"),
//custom
COMMENT_REPLY("COMMENT_REPLY"),
} }
@Serializable @Serializable
@ -40,7 +42,7 @@ data class NotificationResponse(
@Serializable @Serializable
data class NotificationUser( data class NotificationUser(
@SerialName("unreadNotificationCount") @SerialName("unreadNotificationCount")
val unreadNotificationCount: Int, var unreadNotificationCount: Int,
) : java.io.Serializable ) : java.io.Serializable
@Serializable @Serializable
@ -56,41 +58,41 @@ data class Notification(
@SerialName("id") @SerialName("id")
val id: Int, val id: Int,
@SerialName("userId") @SerialName("userId")
val userId: Int?, val userId: Int? = null,
@SerialName("CommentId") @SerialName("CommentId")
val commentId: Int?, val commentId: Int?,
@SerialName("type") @SerialName("type")
val notificationType: String, val notificationType: String,
@SerialName("activityId") @SerialName("activityId")
val activityId: Int?, val activityId: Int? = null,
@SerialName("animeId") @SerialName("animeId")
val mediaId: Int?, val mediaId: Int? = null,
@SerialName("episode") @SerialName("episode")
val episode: Int?, val episode: Int? = null,
@SerialName("contexts") @SerialName("contexts")
val contexts: List<String>?, val contexts: List<String>? = null,
@SerialName("context") @SerialName("context")
val context: String?, val context: String? = null,
@SerialName("reason") @SerialName("reason")
val reason: String?, val reason: String? = null,
@SerialName("deletedMediaTitle") @SerialName("deletedMediaTitle")
val deletedMediaTitle: String?, val deletedMediaTitle: String? = null,
@SerialName("deletedMediaTitles") @SerialName("deletedMediaTitles")
val deletedMediaTitles: List<String>?, val deletedMediaTitles: List<String>? = null,
@SerialName("createdAt") @SerialName("createdAt")
val createdAt: Int, val createdAt: Int,
@SerialName("media") @SerialName("media")
val media: ani.dantotsu.connections.anilist.api.Media?, val media: ani.dantotsu.connections.anilist.api.Media? = null,
@SerialName("user") @SerialName("user")
val user: ani.dantotsu.connections.anilist.api.User?, val user: ani.dantotsu.connections.anilist.api.User? = null,
@SerialName("message") @SerialName("message")
val message: MessageActivity?, val message: MessageActivity? = null,
@SerialName("activity") @SerialName("activity")
val activity: ActivityUnion?, val activity: ActivityUnion? = null,
@SerialName("Thread") @SerialName("Thread")
val thread: Thread?, val thread: Thread? = null,
@SerialName("comment") @SerialName("comment")
val comment: ThreadComment?, val comment: ThreadComment? = null,
) : java.io.Serializable ) : java.io.Serializable
@Serializable @Serializable

View file

@ -33,6 +33,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.Logger
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Section import com.xwray.groupie.Section
@ -197,7 +198,8 @@ class CommentsFragment : Fragment() {
} }
} }
} else { } else {
snackString("No more comments") //snackString("No more comments") fix spam?
Logger.log("No more comments")
} }
} }
} }

View file

@ -1,4 +1,4 @@
package ani.dantotsu.notifications package ani.dantotsu.notifications.comment
import android.Manifest import android.Manifest
import android.app.PendingIntent import android.app.PendingIntent
@ -59,8 +59,12 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
if (newRecentGlobal != null) { if (newRecentGlobal != null) {
PrefManager.setVal(PrefName.RecentGlobalNotification, newRecentGlobal) PrefManager.setVal(PrefName.RecentGlobalNotification, newRecentGlobal)
} }
if (notifications.isNullOrEmpty()) return@launch
PrefManager.setVal(PrefName.UnreadCommentNotifications,
PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications) + (notifications?.size ?: 0)
)
notifications?.forEach { notifications.forEach {
val type: NotificationType = when (it.type) { val type: NotificationType = when (it.type) {
1 -> NotificationType.COMMENT_REPLY 1 -> NotificationType.COMMENT_REPLY
2 -> NotificationType.COMMENT_WARNING 2 -> NotificationType.COMMENT_WARNING
@ -72,6 +76,15 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
NotificationType.COMMENT_WARNING -> { NotificationType.COMMENT_WARNING -> {
val title = "You received a warning" val title = "You received a warning"
val message = it.content ?: "Be more thoughtful with your comments" val message = it.content ?: "Be more thoughtful with your comments"
val commentStore = CommentStore(
title,
message,
it.mediaId,
it.commentId
)
addNotificationToStore(commentStore)
createNotification( createNotification(
NotificationType.COMMENT_WARNING, NotificationType.COMMENT_WARNING,
message, message,
@ -87,6 +100,15 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
val title = "New CommentNotificationWorker Reply" val title = "New CommentNotificationWorker Reply"
val mediaName = names[it.mediaId]?.title ?: "Unknown" val mediaName = names[it.mediaId]?.title ?: "Unknown"
val message = "${it.username} replied to your comment in $mediaName" val message = "${it.username} replied to your comment in $mediaName"
val commentStore = CommentStore(
title,
message,
it.mediaId,
it.commentId
)
addNotificationToStore(commentStore)
createNotification( createNotification(
NotificationType.COMMENT_REPLY, NotificationType.COMMENT_REPLY,
message, message,
@ -101,6 +123,15 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
NotificationType.APP_GLOBAL -> { NotificationType.APP_GLOBAL -> {
val title = "Update from Dantotsu" val title = "Update from Dantotsu"
val message = it.content ?: "New feature available" val message = it.content ?: "New feature available"
val commentStore = CommentStore(
title,
message,
null,
null
)
addNotificationToStore(commentStore)
createNotification( createNotification(
NotificationType.APP_GLOBAL, NotificationType.APP_GLOBAL,
message, message,
@ -144,6 +175,22 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
return Result.success() return Result.success()
} }
private fun addNotificationToStore(notification: CommentStore) {
val notificationStore = PrefManager.getNullableVal<List<CommentStore>>(
PrefName.CommentNotificationStore,
null
) ?: listOf()
val newStore = notificationStore.toMutableList()
if (newStore.size > 10) {
newStore.remove(newStore.minByOrNull { it.time })
}
if (newStore.any { it.content == notification.content }) {
return
}
newStore.add(notification)
PrefManager.setVal(PrefName.CommentNotificationStore, newStore)
}
private fun createNotification( private fun createNotification(
notificationType: NotificationType, notificationType: NotificationType,
message: String, message: String,
@ -153,8 +200,10 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
color: String, color: String,
imageUrl: String imageUrl: String
): android.app.Notification? { ): android.app.Notification? {
Logger.log("Creating notification of type $notificationType" + Logger.log(
", message: $message, title: $title, mediaId: $mediaId, commentId: $commentId") "Creating notification of type $notificationType" +
", message: $message, title: $title, mediaId: $mediaId, commentId: $commentId"
)
val notification = when (notificationType) { val notification = when (notificationType) {
NotificationType.COMMENT_WARNING -> { NotificationType.COMMENT_WARNING -> {
val intent = Intent(applicationContext, MainActivity::class.java).apply { val intent = Intent(applicationContext, MainActivity::class.java).apply {
@ -269,6 +318,6 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
companion object { companion object {
val checkIntervals = arrayOf(0L, 720, 1440) val checkIntervals = arrayOf(0L, 720, 1440)
const val WORK_NAME = "ani.dantotsu.notifications.CommentNotificationWorker" const val WORK_NAME = "ani.dantotsu.notifications.comment.CommentNotificationWorker"
} }
} }

View file

@ -0,0 +1,20 @@
package ani.dantotsu.notifications.comment
import kotlinx.serialization.Serializable
@Suppress("INAPPROPRIATE_CONST_NAME")
@Serializable
data class CommentStore(
val title: String,
val content: String,
val mediaId: Int? = null,
val commentId: Int? = null,
val time: Long = System.currentTimeMillis(),
) : java.io.Serializable {
companion object {
@Suppress("INAPPROPRIATE_CONST_NAME")
private const val serialVersionUID = 1L
}
}

View file

@ -1,4 +1,4 @@
package ani.dantotsu.notifications package ani.dantotsu.notifications.comment
import ani.dantotsu.client import ani.dantotsu.client
import com.google.gson.Gson import com.google.gson.Gson

View file

@ -79,6 +79,10 @@ class ActivityItemBuilder {
NotificationType.MEDIA_DELETION -> { NotificationType.MEDIA_DELETION -> {
"${notification.deletedMediaTitle} has been deleted from the site" "${notification.deletedMediaTitle} has been deleted from the site"
} }
NotificationType.COMMENT_REPLY -> {
notification.context ?: "You should not see this"
}
} }
} }

View file

@ -19,6 +19,7 @@ import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -80,7 +81,8 @@ class FeedFragment : Fragment() {
binding.listRecyclerView.setOnTouchListener { _, event -> binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) { if (event?.action == MotionEvent.ACTION_UP) {
if (activityList.size % AnilistQueries.ITEMS_PER_PAGE != 0 && !global) { if (activityList.size % AnilistQueries.ITEMS_PER_PAGE != 0 && !global) {
snackString("No more activities") //snackString("No more activities") fix spam?
Logger.log("No more activities")
} else if (!scrollView.canScrollVertically(1) && !binding.feedRefresh.isVisible } else if (!scrollView.canScrollVertically(1) && !binding.feedRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 && && binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1) (binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)

View file

@ -18,6 +18,7 @@ import ani.dantotsu.databinding.ActivityFollowBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
@ -29,6 +30,7 @@ import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.Locale
class NotificationActivity : AppCompatActivity() { class NotificationActivity : AppCompatActivity() {
private lateinit var binding: ActivityFollowBinding private lateinit var binding: ActivityFollowBinding
@ -73,7 +75,25 @@ class NotificationActivity : AppCompatActivity() {
notifications.filter { it.id == activityId } notifications.filter { it.id == activityId }
} else { } else {
notifications notifications
}.toMutableList()
val commentStore = PrefManager.getNullableVal<List<CommentStore>>(
PrefName.CommentNotificationStore,
null
) ?: listOf()
commentStore.forEach {
val notification = Notification(
"COMMENT_REPLY",
System.currentTimeMillis().toInt(),
commentId = it.commentId,
notificationType = "COMMENT_REPLY",
mediaId = it.mediaId,
context = it.title + "\n" + it.content,
createdAt = (it.time / 1000L).toInt(),
)
notificationList = notificationList + notification
} }
notificationList = notificationList.sortedByDescending { it.createdAt }
adapter.update(notificationList.map { NotificationItem(it, ::onNotificationClick) }) adapter.update(notificationList.map { NotificationItem(it, ::onNotificationClick) })
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -81,7 +101,8 @@ class NotificationActivity : AppCompatActivity() {
binding.listRecyclerView.setOnTouchListener { _, event -> binding.listRecyclerView.setOnTouchListener { _, event ->
if (event?.action == MotionEvent.ACTION_UP) { if (event?.action == MotionEvent.ACTION_UP) {
if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) { if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) {
snackString("No more notifications") //snackString("No more notifications") fix spam?
Logger.log("No more notifications")
} else if (!binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible } else if (!binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible
&& binding.listRecyclerView.adapter!!.itemCount != 0 && && binding.listRecyclerView.adapter!!.itemCount != 0 &&
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1) (binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
@ -105,7 +126,6 @@ class NotificationActivity : AppCompatActivity() {
} }
} }
} }
private fun loadPage(onFinish: () -> Unit = {}) { private fun loadPage(onFinish: () -> Unit = {}) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val res = Anilist.query.getNotifications(Anilist.userid ?: 0, page) val res = Anilist.query.getNotifications(Anilist.userid ?: 0, page)
@ -120,7 +140,7 @@ class NotificationActivity : AppCompatActivity() {
} }
} }
private fun onNotificationClick(id: Int, type: NotificationClickType) { private fun onNotificationClick(id: Int, optional: Int?, type: NotificationClickType) {
when (type) { when (type) {
NotificationClickType.USER -> { NotificationClickType.USER -> {
ContextCompat.startActivity( ContextCompat.startActivity(
@ -143,6 +163,16 @@ class NotificationActivity : AppCompatActivity() {
) )
} }
NotificationClickType.COMMENT -> {
ContextCompat.startActivity(this, Intent(this, MediaDetailsActivity::class.java)
.putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
.putExtra("mediaId", id)
.putExtra("commentId", optional ?: -1),
null
)
}
NotificationClickType.UNDEFINED -> { NotificationClickType.UNDEFINED -> {
// Do nothing // Do nothing
} }
@ -151,7 +181,7 @@ class NotificationActivity : AppCompatActivity() {
companion object { companion object {
enum class NotificationClickType { enum class NotificationClickType {
USER, MEDIA, ACTIVITY, UNDEFINED USER, MEDIA, ACTIVITY, COMMENT, UNDEFINED
} }
} }
} }

View file

@ -14,7 +14,7 @@ import com.xwray.groupie.viewbinding.BindableItem
class NotificationItem( class NotificationItem(
private val notification: Notification, private val notification: Notification,
val clickCallback: (Int, NotificationClickType) -> Unit val clickCallback: (Int, Int?, NotificationClickType) -> Unit
) : BindableItem<ItemNotificationBinding>() { ) : BindableItem<ItemNotificationBinding>() {
private lateinit var binding: ItemNotificationBinding private lateinit var binding: ItemNotificationBinding
override fun bind(viewBinding: ItemNotificationBinding, position: Int) { override fun bind(viewBinding: ItemNotificationBinding, position: Int) {
@ -31,7 +31,7 @@ class NotificationItem(
return ItemNotificationBinding.bind(view) return ItemNotificationBinding.bind(view)
} }
private fun image(user: Boolean = false) { private fun image(user: Boolean = false, commentNotification: Boolean = false) {
val cover = if (user) notification.user?.bannerImage val cover = if (user) notification.user?.bannerImage
?: notification.user?.avatar?.medium else notification.media?.bannerImage ?: notification.user?.avatar?.medium else notification.media?.bannerImage
@ -52,7 +52,13 @@ class NotificationItem(
binding.notificationCover.visibility = View.GONE binding.notificationCover.visibility = View.GONE
binding.notificationCoverUser.visibility = View.VISIBLE binding.notificationCoverUser.visibility = View.VISIBLE
binding.notificationCoverUserContainer.visibility = View.VISIBLE binding.notificationCoverUserContainer.visibility = View.VISIBLE
binding.notificationCoverUser.loadImage(notification.user?.avatar?.large) if (commentNotification) {
binding.notificationCoverUser.setImageResource(R.drawable.ic_dantotsu_round)
binding.notificationCoverUser.scaleX = 1.4f
binding.notificationCoverUser.scaleY = 1.4f
} else {
binding.notificationCoverUser.loadImage(notification.user?.avatar?.large)
}
binding.notificationBannerImage.layoutParams.height = userHeight binding.notificationBannerImage.layoutParams.height = userHeight
} else { } else {
binding.notificationCover.visibility = View.VISIBLE binding.notificationCover.visibility = View.VISIBLE
@ -75,12 +81,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -90,12 +96,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -105,12 +111,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.userId ?: 0, NotificationClickType.USER notification.userId ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -120,12 +126,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -135,12 +141,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -150,12 +156,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -165,12 +171,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -180,7 +186,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.media?.id ?: 0, NotificationClickType.MEDIA notification.media?.id ?: 0, null, NotificationClickType.MEDIA
) )
} }
} }
@ -190,12 +196,12 @@ class NotificationItem(
binding.notificationCover.loadImage(notification.user?.avatar?.large) binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -205,12 +211,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -220,12 +226,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -235,12 +241,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
} }
@ -250,12 +256,12 @@ class NotificationItem(
image(true) image(true)
binding.notificationCoverUser.setOnClickListener { binding.notificationCoverUser.setOnClickListener {
clickCallback( clickCallback(
notification.user?.id ?: 0, NotificationClickType.USER notification.user?.id ?: 0, null, NotificationClickType.USER
) )
} }
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.activityId ?: 0, NotificationClickType.ACTIVITY notification.activityId ?: 0, null, NotificationClickType.ACTIVITY
) )
} }
} }
@ -265,7 +271,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.media?.id ?: 0, NotificationClickType.MEDIA notification.media?.id ?: 0, null, NotificationClickType.MEDIA
) )
} }
} }
@ -275,7 +281,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.media?.id ?: 0, NotificationClickType.MEDIA notification.media?.id ?: 0, null, NotificationClickType.MEDIA
) )
} }
} }
@ -285,7 +291,7 @@ class NotificationItem(
image() image()
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.media?.id ?: 0, NotificationClickType.MEDIA notification.media?.id ?: 0, null, NotificationClickType.MEDIA
) )
} }
} }
@ -293,6 +299,17 @@ class NotificationItem(
NotificationType.MEDIA_DELETION -> { NotificationType.MEDIA_DELETION -> {
binding.notificationCover.visibility = View.GONE binding.notificationCover.visibility = View.GONE
} }
NotificationType.COMMENT_REPLY -> {
image(user = true, commentNotification = true)
if (notification.commentId != null && notification.mediaId != null) {
binding.notificationBannerImage.setOnClickListener {
clickCallback(
notification.mediaId, notification.commentId, NotificationClickType.COMMENT
)
}
}
}
} }
} }

View file

@ -47,7 +47,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.CommentNotificationWorker import ani.dantotsu.notifications.comment.CommentNotificationWorker
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
import ani.dantotsu.openLinkInBrowser import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.AppUpdater

View file

@ -3,6 +3,7 @@ package ani.dantotsu.settings.saving
import android.graphics.Color import android.graphics.Color
import ani.dantotsu.connections.comments.AuthResponse import ani.dantotsu.connections.comments.AuthResponse
import ani.dantotsu.connections.mal.MAL import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.settings.saving.internal.Location import ani.dantotsu.settings.saving.internal.Location
import ani.dantotsu.settings.saving.internal.Pref import ani.dantotsu.settings.saving.internal.Pref
@ -171,6 +172,8 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
CommentTokenExpiry(Pref(Location.Irrelevant, Long::class, 0L)), CommentTokenExpiry(Pref(Location.Irrelevant, Long::class, 0L)),
LogToFile(Pref(Location.Irrelevant, Boolean::class, false)), LogToFile(Pref(Location.Irrelevant, Boolean::class, false)),
RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)), RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)),
CommentNotificationStore(Pref(Location.Irrelevant, List::class, listOf<CommentStore>())),
UnreadCommentNotifications(Pref(Location.Irrelevant, Int::class, 0)),
//Protected //Protected
DiscordToken(Pref(Location.Protected, String::class, "")), DiscordToken(Pref(Location.Protected, String::class, "")),