feat: anilist notifications

This commit is contained in:
rebelonion 2024-03-07 02:51:04 -06:00
parent e2eae6250b
commit 7ac679f927
15 changed files with 572 additions and 28 deletions

View file

@ -28,6 +28,7 @@ object Anilist {
var bg: String? = null
var episodesWatched: Int? = null
var chapterRead: Int? = null
var unreadNotificationCount: Int = 0
var genres: ArrayList<String>? = null
var tags: Map<Boolean, List<String>>? = null

View file

@ -7,6 +7,8 @@ import ani.dantotsu.checkId
import ani.dantotsu.connections.anilist.Anilist.authorRoles
import ani.dantotsu.connections.anilist.Anilist.executeQuery
import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.connections.anilist.api.NotificationResponse
import ani.dantotsu.connections.anilist.api.Page
import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.currContext
@ -36,7 +38,7 @@ class AnilistQueries {
val response: Query.Viewer?
measureTimeMillis {
response =
executeQuery("""{Viewer{name options{displayAdultContent}avatar{medium}bannerImage id mediaListOptions{rowOrder animeList{sectionOrder customLists}mangaList{sectionOrder customLists}}statistics{anime{episodesWatched}manga{chaptersRead}}}}""")
executeQuery("""{Viewer{name options{displayAdultContent}avatar{medium}bannerImage id mediaListOptions{rowOrder animeList{sectionOrder customLists}mangaList{sectionOrder customLists}}statistics{anime{episodesWatched}manga{chaptersRead}}unreadNotificationCount}}""")
}.also { println("time : $it") }
val user = response?.data?.user ?: return false
@ -49,6 +51,7 @@ class AnilistQueries {
Anilist.episodesWatched = user.statistics?.anime?.episodesWatched
Anilist.chapterRead = user.statistics?.manga?.chaptersRead
Anilist.adult = user.options?.displayAdultContent ?: false
Anilist.unreadNotificationCount = user.unreadNotificationCount?:0
return true
}
@ -1337,4 +1340,12 @@ Page(page:$page,perPage:50) {
default[1] = userBannerImage("MANGA",id)
return default
}
suspend fun getNotifications(id: Int): NotificationResponse? {
val res = executeQuery<NotificationResponse>("""{User(id:$id){unreadNotificationCount}Page{notifications(resetNotificationCount:true){__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)
if (res != null) {
Anilist.unreadNotificationCount = 0
}
return res
}
}

View file

@ -0,0 +1,118 @@
package ani.dantotsu.connections.anilist.api
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
enum class NotificationType(val value: String) {
ACTIVITY_MESSAGE("ACTIVITY_MESSAGE"),
ACTIVITY_REPLY("ACTIVITY_REPLY"),
FOLLOWING("FOLLOWING"),
ACTIVITY_MENTION("ACTIVITY_MENTION"),
THREAD_COMMENT_MENTION("THREAD_COMMENT_MENTION"),
THREAD_SUBSCRIBED("THREAD_SUBSCRIBED"),
THREAD_COMMENT_REPLY("THREAD_COMMENT_REPLY"),
AIRING("AIRING"),
ACTIVITY_LIKE("ACTIVITY_LIKE"),
ACTIVITY_REPLY_LIKE("ACTIVITY_REPLY_LIKE"),
THREAD_LIKE("THREAD_LIKE"),
THREAD_COMMENT_LIKE("THREAD_COMMENT_LIKE"),
ACTIVITY_REPLY_SUBSCRIBED("ACTIVITY_REPLY_SUBSCRIBED"),
RELATED_MEDIA_ADDITION("RELATED_MEDIA_ADDITION"),
MEDIA_DATA_CHANGE("MEDIA_DATA_CHANGE"),
MEDIA_MERGE("MEDIA_MERGE"),
MEDIA_DELETION("MEDIA_DELETION")
}
@Serializable
data class NotificationResponse(
@SerialName("data")
val data: Data,
) : java.io.Serializable {
@Serializable
data class Data(
@SerialName("User")
val user: NotificationUser,
@SerialName("Page")
val page: NotificationPage,
) : java.io.Serializable
}
@Serializable
data class NotificationUser(
@SerialName("unreadNotificationCount")
val unreadNotificationCount: Int,
) : java.io.Serializable
@Serializable
data class NotificationPage(
@SerialName("notifications")
val notifications: List<Notification>,
) : java.io.Serializable
@Serializable
data class Notification(
@SerialName("__typename")
val typename: String,
@SerialName("id")
val id: Int,
@SerialName("userId")
val userId: Int?,
@SerialName("CommentId")
val commentId: Int?,
@SerialName("type")
val notificationType: String,
@SerialName("activityId")
val activityId: Int?,
@SerialName("animeId")
val mediaId: Int?,
@SerialName("episode")
val episode: Int?,
@SerialName("contexts")
val contexts: List<String>?,
@SerialName("context")
val context: String?,
@SerialName("reason")
val reason: String?,
@SerialName("deletedMediaTitle")
val deletedMediaTitle: String?,
@SerialName("deletedMediaTitles")
val deletedMediaTitles: List<String>?,
@SerialName("createdAt")
val createdAt: Int,
@SerialName("media")
val media: ani.dantotsu.connections.anilist.api.Media?,
@SerialName("user")
val user: ani.dantotsu.connections.anilist.api.User?,
@SerialName("message")
val message: MessageActivity?,
@SerialName("activity")
val activity: ActivityUnion?,
@SerialName("Thread")
val thread: Thread?,
@SerialName("comment")
val comment: ThreadComment?,
) : java.io.Serializable
@Serializable
data class MessageActivity(
@SerialName("id")
val id: Int?,
) : java.io.Serializable
@Serializable
data class ActivityUnion(
@SerialName("id")
val id: Int?,
) : java.io.Serializable
@Serializable
data class Thread(
@SerialName("id")
val id: Int?,
) : java.io.Serializable
@Serializable
data class ThreadComment(
@SerialName("id")
val id: Int?,
) : java.io.Serializable

View file

@ -46,7 +46,7 @@ data class User(
@SerialName("statistics") var statistics: UserStatisticTypes?,
// The number of unread notifications the user has
// @SerialName("unreadNotificationCount") var unreadNotificationCount: Int?,
@SerialName("unreadNotificationCount") var unreadNotificationCount: Int?,
// The url for the user page on the AniList website
// @SerialName("siteUrl") var siteUrl: String?,

View file

@ -80,6 +80,8 @@ class HomeFragment : Fragment() {
binding.homeUserBg.loadImage(Anilist.bg)
binding.homeUserDataProgressBar.visibility = View.GONE
binding.homeNotificationDot.visibility = if (Anilist.unreadNotificationCount > 0) View.VISIBLE else View.GONE
binding.homeAnimeList.setOnClickListener {
ContextCompat.startActivity(
requireActivity(), Intent(requireActivity(), ListActivity::class.java)
@ -361,6 +363,8 @@ class HomeFragment : Fragment() {
override fun onResume() {
if (!model.loaded) Refresh.activity[1]!!.postValue(true)
if (_binding != null)
binding.homeNotificationDot.visibility = if (Anilist.unreadNotificationCount > 0) View.VISIBLE else View.GONE
super.onResume()
}
}

View file

@ -1,5 +1,6 @@
package ani.dantotsu.notifications
import android.content.Intent
import android.os.Bundle
import android.view.ViewGroup
import android.view.Window
@ -7,16 +8,27 @@ import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.databinding.ActivityNotificationBinding
import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.NotificationItem
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.launch
class NotificationActivity : AppCompatActivity() {
private lateinit var binding: ActivityNotificationBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var notificationList: List<Notification> = emptyList()
override fun onCreate(savedInstanceState: Bundle?) {
val immersiveMode = PrefManager.getVal<Boolean>(PrefName.ImmersiveMode)
@ -43,5 +55,46 @@ class NotificationActivity : AppCompatActivity() {
}
}
setContentView(binding.root)
binding.notificationList.adapter = adapter
binding.notificationList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.listBack.setOnClickListener {
onBackPressed()
}
lifecycleScope.launch {
val res = Anilist.query.getNotifications(Anilist.userid?:0)
res?.data?.page?.notifications?.let { notifications ->
notificationList = notifications
adapter.update(notificationList.map { NotificationItem(it, ::onNotificationClick) })
}
}
}
private fun onNotificationClick(id: Int, type: NotificationClickType) {
when (type) {
NotificationClickType.USER -> {
ContextCompat.startActivity(
this, Intent(this, ProfileActivity::class.java)
.putExtra("userId", id), null
)
}
NotificationClickType.MEDIA -> {
ContextCompat.startActivity(
this, Intent(this, MediaDetailsActivity::class.java)
.putExtra("mediaId", id), null
)
}
NotificationClickType.UNDEFINED -> {
// Do nothing
}
}
}
companion object {
enum class NotificationClickType {
USER, MEDIA, UNDEFINED
}
}
}

View file

@ -70,13 +70,14 @@ class ProfileFragment() : Fragment() {
binding.profileUserBio.settings.loadWithOverviewMode = true
binding.profileUserBio.settings.useWideViewPort = true
binding.profileUserBio.setInitialScale(1)
val styledHtml = styled(
convertMarkdownToHtml(user.about ?: ""),
backGroundColorTypedValue.data,
textColorTypedValue.data
)
binding.profileUserBio.loadDataWithBaseURL(
null,
styled(
convertMarkdownToHtml(user.about ?: ""),
backGroundColorTypedValue.data,
textColorTypedValue.data
),
styledHtml,
"text/html; charset=utf-8",
"UTF-8",
null
@ -215,7 +216,22 @@ class ProfileFragment() : Fragment() {
}
}
private fun styled(html: String, backGroundColor: Int, textColor: Int): String {
private fun styled(html: String, backGroundColor: Int, textColor: Int): String { //istg anilist has the worst api
//remove some of the html entities
val step1 = html.replace("&nbsp;", " ")
.replace("&amp;", "&")
.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&quot;", "\"")
.replace("&apos;", "'")
.replace("<pre>", "")
.replace("`", "")
.replace("~", "")
val step2 = step1.replace("(?s)___(.*?)___".toRegex(), "<br><em><strong>$1</strong></em><br>")
val step3 = step2.replace("(?s)__(.*?)__".toRegex(), "<br><strong>$1</strong><br>")
return """
<html>
<head>
@ -233,6 +249,10 @@ class ProfileFragment() : Fragment() {
max-width: 100%;
height: auto; /* Maintain aspect ratio */
}
video {
max-width: 100%;
height: auto; /* Maintain aspect ratio */
}
a {
color: ${textColor.toCssColor()};
}
@ -240,7 +260,7 @@ class ProfileFragment() : Fragment() {
</style>
</head>
<body>
$html
$step3
</body>
""".trimIndent()

View file

@ -0,0 +1,22 @@
package ani.dantotsu.profile.activity
import android.view.View
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemNotificationBinding
import com.xwray.groupie.viewbinding.BindableItem
class ActivityItem(
): BindableItem<ItemNotificationBinding>() {
private lateinit var binding: ItemNotificationBinding
override fun bind(viewBinding: ItemNotificationBinding, position: Int) {
binding = viewBinding
}
override fun getLayout(): Int {
return R.layout.item_notification
}
override fun initializeViewBinding(view: View): ItemNotificationBinding {
return ItemNotificationBinding.bind(view)
}
}

View file

@ -0,0 +1,144 @@
package ani.dantotsu.profile.activity
import android.view.View
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.connections.anilist.api.NotificationType
import ani.dantotsu.databinding.ItemNotificationBinding
import ani.dantotsu.loadImage
import ani.dantotsu.notifications.NotificationActivity
import com.xwray.groupie.viewbinding.BindableItem
class NotificationItem(
private val notification: Notification,
val clickCallback: (Int, NotificationActivity.Companion.NotificationClickType) -> Unit
): BindableItem<ItemNotificationBinding>() {
private lateinit var binding: ItemNotificationBinding
private lateinit var clickType: NotificationActivity.Companion.NotificationClickType
private var id = 0
override fun bind(viewBinding: ItemNotificationBinding, position: Int) {
binding = viewBinding
setBinding()
}
override fun getLayout(): Int {
return R.layout.item_notification
}
override fun initializeViewBinding(view: View): ItemNotificationBinding {
return ItemNotificationBinding.bind(view)
}
private fun setBinding() {
val notificationType: NotificationType =
NotificationType.valueOf(notification.notificationType)
binding.notificationText.text = NotificationItemBuilder.getContent(notification)
binding.notificationDate.text = NotificationItemBuilder.getDateTime(notification.createdAt)
binding.root.setOnClickListener { clickCallback(id, clickType) }
when (notificationType) {
NotificationType.ACTIVITY_MESSAGE -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.ACTIVITY_REPLY -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.FOLLOWING -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.ACTIVITY_MENTION -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.THREAD_COMMENT_MENTION -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.THREAD_SUBSCRIBED -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.THREAD_COMMENT_REPLY -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.AIRING -> {
binding.notificationCover.loadImage(notification.media?.coverImage?.large)
binding.notificationBannerImage.loadImage(notification.media?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.MEDIA
id = notification.media?.id ?: 0
}
NotificationType.ACTIVITY_LIKE -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.ACTIVITY_REPLY_LIKE -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.THREAD_LIKE -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.THREAD_COMMENT_LIKE -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.ACTIVITY_REPLY_SUBSCRIBED -> {
binding.notificationCover.loadImage(notification.user?.avatar?.large)
binding.notificationBannerImage.loadImage(notification.user?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.USER
id = notification.user?.id ?: 0
}
NotificationType.RELATED_MEDIA_ADDITION -> {
binding.notificationCover.loadImage(notification.media?.coverImage?.large)
binding.notificationBannerImage.loadImage(notification.media?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.MEDIA
id = notification.media?.id ?: 0
}
NotificationType.MEDIA_DATA_CHANGE -> {
binding.notificationCover.loadImage(notification.media?.coverImage?.large)
binding.notificationBannerImage.loadImage(notification.media?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.MEDIA
id = notification.media?.id ?: 0
}
NotificationType.MEDIA_MERGE -> {
binding.notificationCover.loadImage(notification.media?.coverImage?.large)
binding.notificationBannerImage.loadImage(notification.media?.bannerImage)
clickType = NotificationActivity.Companion.NotificationClickType.MEDIA
id = notification.media?.id ?: 0
}
NotificationType.MEDIA_DELETION -> {
binding.notificationCover.visibility = View.GONE
clickType = NotificationActivity.Companion.NotificationClickType.UNDEFINED
id = 0
}
}
}
}

View file

@ -0,0 +1,148 @@
package ani.dantotsu.profile.activity
import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.connections.anilist.api.NotificationType
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/*
* ACTIVITY_MESSAGE
A user has sent you message
ACTIVITY_REPLY
A user has replied to your activity
FOLLOWING
A user has followed you
ACTIVITY_MENTION
A user has mentioned you in their activity
THREAD_COMMENT_MENTION
A user has mentioned you in a forum comment
THREAD_SUBSCRIBED
A user has commented in one of your subscribed forum threads
THREAD_COMMENT_REPLY
A user has replied to your forum comment
AIRING
An anime you are currently watching has aired
ACTIVITY_LIKE
A user has liked your activity
ACTIVITY_REPLY_LIKE
A user has liked your activity reply
THREAD_LIKE
A user has liked your forum thread
THREAD_COMMENT_LIKE
A user has liked your forum comment
ACTIVITY_REPLY_SUBSCRIBED
A user has replied to activity you have also replied to
RELATED_MEDIA_ADDITION
A new anime or manga has been added to the site where its related media is on the user's list
MEDIA_DATA_CHANGE
An anime or manga has had a data change that affects how a user may track it in their lists
MEDIA_MERGE
Anime or manga entries on the user's list have been merged into a single entry
MEDIA_DELETION
An anime or manga on the user's list has been deleted from the site
* */
interface NotificationItemBuilder {
companion object {
fun getContent(notification: Notification): String {
val notificationType: NotificationType =
NotificationType.valueOf(notification.notificationType)
return when (notificationType) {
NotificationType.ACTIVITY_MESSAGE -> {
"${notification.user?.name} sent you a message"
}
NotificationType.ACTIVITY_REPLY -> {
"${notification.user?.name} replied to your activity"
}
NotificationType.FOLLOWING -> {
"${notification.user?.name} followed you"
}
NotificationType.ACTIVITY_MENTION -> {
"${notification.user?.name} mentioned you in their activity"
}
NotificationType.THREAD_COMMENT_MENTION -> {
"${notification.user?.name} mentioned you in a forum comment"
}
NotificationType.THREAD_SUBSCRIBED -> {
"${notification.user?.name} commented in one of your subscribed forum threads"
}
NotificationType.THREAD_COMMENT_REPLY -> {
"${notification.user?.name} replied to your forum comment"
}
NotificationType.AIRING -> {
"Episode ${notification.episode} of ${notification.media?.title?.english ?: notification.media?.title?.romaji} has aired"
}
NotificationType.ACTIVITY_LIKE -> {
"${notification.user?.name} liked your activity"
}
NotificationType.ACTIVITY_REPLY_LIKE -> {
"${notification.user?.name} liked your reply"
}
NotificationType.THREAD_LIKE -> {
"${notification.user?.name} liked your forum thread"
}
NotificationType.THREAD_COMMENT_LIKE -> {
"${notification.user?.name} liked your forum comment"
}
NotificationType.ACTIVITY_REPLY_SUBSCRIBED -> {
"${notification.user?.name} replied to activity you have also replied to"
}
NotificationType.RELATED_MEDIA_ADDITION -> {
"${notification.media?.title?.english ?: notification.media?.title?.romaji} has been added to the site"
}
NotificationType.MEDIA_DATA_CHANGE -> {
"${notification.media?.title?.english ?: notification.media?.title?.romaji} has had a data change: ${notification.reason}"
}
NotificationType.MEDIA_MERGE -> {
"${notification.deletedMediaTitles?.joinToString(", ")} have been merged into ${notification.media?.title?.english ?: notification.media?.title?.romaji}"
}
NotificationType.MEDIA_DELETION -> {
"${notification.deletedMediaTitle} has been deleted from the site"
}
}
}
fun getDateTime(time: Int): String {
val date = Date(time * 1000L)
val sdf = SimpleDateFormat("dd/MM/yyyy hh:mm a", Locale.getDefault())
return sdf.format(date)
}
}
}

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="?attr/colorError"/>
<size
android:width="100dp"
android:height="100dp"/>
</shape>

View file

@ -46,7 +46,7 @@
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listRecyclerView"
android:id="@+id/notificationList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"

View file

@ -58,6 +58,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
@ -96,14 +97,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="?attr/colorOnSecondary"
android:backgroundTint="?attr/colorSecondaryContainer"
android:enabled="true"
android:fontFamily="@font/poppins_bold"
android:text="Follow"
android:textColor="@color/bg_opp"
android:textSize="14sp"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorOnSecondary"
app:strokeColor="?attr/colorSecondaryContainer"
tools:ignore="HardcodedText,SpeakableTextPresentCheck" />
</LinearLayout>

View file

@ -135,23 +135,35 @@
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/homeUserAvatarContainer"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_marginTop="4dp"
android:backgroundTint="@color/nav_bg_inv"
app:cardCornerRadius="26dp">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/homeUserAvatar"
<com.google.android.material.card.MaterialCardView
android:id="@+id/homeUserAvatarContainer"
android:layout_width="52dp"
android:layout_height="52dp"
android:scaleType="center"
app:srcCompat="@drawable/ic_round_settings_24"
tools:ignore="ContentDescription,ImageContrastCheck" />
android:layout_marginTop="4dp"
android:backgroundTint="@color/nav_bg_inv"
app:cardCornerRadius="26dp">
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/homeUserAvatar"
android:layout_width="52dp"
android:layout_height="52dp"
android:scaleType="center"
app:srcCompat="@drawable/ic_round_settings_24"
tools:ignore="ContentDescription,ImageContrastCheck" />
</com.google.android.material.card.MaterialCardView>
<View
android:id="@+id/homeNotificationDot"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/notification_circle"/>
</FrameLayout>
</LinearLayout>

View file

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_height="170dp"
android:layout_marginTop="16dp"
android:orientation="horizontal">
@ -39,9 +39,10 @@
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/notificationCover"
android:layout_width="102dp"
android:layout_height="154dp"
android:layout_width="96dp"
android:layout_height="144dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_round_add_circle_24"
tools:ignore="ContentDescription,ImageContrastCheck"
tools:tint="@color/transparent" />
@ -63,6 +64,7 @@
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/notificationDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"