feat: (wip) user activities

This commit is contained in:
rebelonion 2024-03-08 00:30:11 -06:00
parent 46d8248ffd
commit 49175a962a
7 changed files with 177 additions and 67 deletions

View file

@ -6,6 +6,7 @@ import ani.dantotsu.checkGenreTime
import ani.dantotsu.checkId import ani.dantotsu.checkId
import ani.dantotsu.connections.anilist.Anilist.authorRoles import ani.dantotsu.connections.anilist.Anilist.authorRoles
import ani.dantotsu.connections.anilist.Anilist.executeQuery import ani.dantotsu.connections.anilist.Anilist.executeQuery
import ani.dantotsu.connections.anilist.api.FeedResponse
import ani.dantotsu.connections.anilist.api.FuzzyDate import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.connections.anilist.api.NotificationResponse import ani.dantotsu.connections.anilist.api.NotificationResponse
@ -1348,4 +1349,12 @@ Page(page:$page,perPage:50) {
} }
return res return res
} }
suspend fun getFeed(userId: Int?, global: Boolean = false, page: Int = 1): FeedResponse? {
val filter = if (userId != null) "userId:$userId,"
else if (global) "isFollowing:false,"
else "isFollowing:true,"
val res = executeQuery<FeedResponse>("""{Page(page:$page,perPage:25){activities(${filter}sort:ID_DESC){__typename ... on TextActivity{id userId type replyCount text(asHtml:true)siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on ListActivity{id userId type replyCount status progress siteUrl isLocked isSubscribed likeCount isLiked isPinned createdAt user{id name bannerImage avatar{medium large}}media{id title{english romaji native userPreferred}bannerImage coverImage{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}... on MessageActivity{id recipientId messengerId type replyCount message(asHtml:true)isLocked isSubscribed isLiked isPrivate siteUrl createdAt recipient{id name bannerImage avatar{medium large}}messenger{id name bannerImage avatar{medium large}}replies{id userId activityId text(asHtml:true)likeCount isLiked createdAt user{id name bannerImage avatar{medium large}}likes{id name bannerImage avatar{medium large}}}likes{id name bannerImage avatar{medium large}}}}}}""")
return res
}
} }

View file

@ -0,0 +1,92 @@
package ani.dantotsu.connections.anilist.api
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class FeedResponse(
@SerialName("data")
val data: Data
) {
@Serializable
data class Data(
@SerialName("Page")
val page: ActivityPage
)
}
@Serializable
data class ActivityPage(
@SerialName("activities")
val activities: List<Activity>
)
@Serializable
data class Activity(
@SerialName("__typename")
val typename: String,
@SerialName("id")
val id: Int,
@SerialName("recipientId")
val recipientId: Int?,
@SerialName("messengerId")
val messengerId: Int?,
@SerialName("userId")
val userId: Int?,
@SerialName("type")
val type: String,
@SerialName("replyCount")
val replyCount: Int,
@SerialName("status")
val status: String?,
@SerialName("progress")
val progress: String?,
@SerialName("text")
val text: String?,
@SerialName("message")
val message: String?,
@SerialName("siteUrl")
val siteUrl: String?,
@SerialName("isLocked")
val isLocked: Boolean,
@SerialName("isSubscribed")
val isSubscribed: Boolean,
@SerialName("likeCount")
val likeCount: Int?,
@SerialName("isLiked")
val isLiked: Boolean?,
@SerialName("isPinned")
val isPinned: Boolean?,
@SerialName("isPrivate")
val isPrivate: Boolean?,
@SerialName("createdAt")
val createdAt: Int,
@SerialName("user")
val user: User?,
@SerialName("media")
val media: Media?,
@SerialName("replies")
val replies: List<Reply>?,
@SerialName("likes")
val likes: List<User>?,
)
@Serializable
data class Reply(
@SerialName("id")
val id: Int,
@SerialName("userId")
val userId: Int,
@SerialName("text")
val text: String,
@SerialName("likeCount")
val likeCount: Int,
@SerialName("isLiked")
val isLiked: Boolean,
@SerialName("createdAt")
val createdAt: Int,
@SerialName("user")
val user: User,
@SerialName("likes")
val likes: List<User>?,
)

View file

@ -5,13 +5,22 @@ import android.os.Bundle
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.ActivityFollowBinding import ani.dantotsu.databinding.ActivityFollowBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.profile.activity.ActivityItem
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.launch
class ActivityActivity : AppCompatActivity() { class ActivityActivity : AppCompatActivity() {
private lateinit var binding: ActivityFollowBinding private lateinit var binding: ActivityFollowBinding
private var adapter: GroupieAdapter = GroupieAdapter()
private var activityList: List<Activity> = emptyList()
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -23,7 +32,24 @@ class ActivityActivity : AppCompatActivity() {
binding.listTitle.text = "Activity" binding.listTitle.text = "Activity"
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight } binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight }
binding.listRecyclerView.adapter = adapter
binding.listRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.followerGrid.visibility = ViewGroup.GONE binding.followerGrid.visibility = ViewGroup.GONE
binding.followerList.visibility = ViewGroup.GONE binding.followerList.visibility = ViewGroup.GONE
binding.listBack.setOnClickListener {
onBackPressed()
}
var userId: Int? = intent.getIntExtra("userId", -1)
if (userId == -1) userId = null
val global = intent.getBooleanExtra("global", false)
lifecycleScope.launch {
val res = Anilist.query.getFeed(userId, global)
res?.data?.page?.activities?.let { activities ->
activityList = activities
adapter.update(activityList.map { ActivityItem(it){} })
}
}
} }
} }

View file

@ -1,22 +1,61 @@
package ani.dantotsu.profile.activity package ani.dantotsu.profile.activity
import android.content.Context
import android.view.View import android.view.View
import androidx.core.content.ContextCompat
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.databinding.ItemNotificationBinding import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.ItemActivityBinding
import ani.dantotsu.loadImage
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestOptions
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import jp.wasabeef.glide.transformations.BlurTransformation
class ActivityItem( class ActivityItem(
): BindableItem<ItemNotificationBinding>() { private val activity: Activity,
private lateinit var binding: ItemNotificationBinding val clickCallback: (Int) -> Unit
override fun bind(viewBinding: ItemNotificationBinding, position: Int) { ): BindableItem<ItemActivityBinding>() {
private lateinit var binding: ItemActivityBinding
override fun bind(viewBinding: ItemActivityBinding, position: Int) {
binding = viewBinding binding = viewBinding
when (activity.typename) {
"ListActivity" ->{
binding.activityUserName.text = activity.user?.name
binding.activityUserAvatar.loadImage(activity.user?.avatar?.medium)
binding.activityTime.text = ActivityItemBuilder.getDateTime(activity.createdAt)
val color = if (activity.isLiked == true)
ContextCompat.getColor(binding.root.context, R.color.yt_red)
else
ContextCompat.getColor(binding.root.context, R.color.bg_opp)
binding.activityFavorite.setColorFilter(color)
binding.activityMediaName.text = activity.media?.title?.userPreferred
binding.activityText.text = "${activity.user!!.name} ${activity.status} ${activity.media!!.title!!.userPreferred}"
binding.activityCover.loadImage(activity.media.coverImage?.medium)
val context = binding.root.context
val banner = activity.media.bannerImage
if (banner != null) {
if (!(context as android.app.Activity).isDestroyed)
Glide.with(context as Context)
.load(GlideUrl(banner))
.diskCacheStrategy(DiskCacheStrategy.ALL).override(400)
.apply(RequestOptions.bitmapTransform(BlurTransformation(2, 2)))
.into(binding.activityBannerImage)
} else {
binding.activityBannerImage.setImageResource(R.drawable.linear_gradient_bg)
}
}
}
} }
override fun getLayout(): Int { override fun getLayout(): Int {
return R.layout.item_notification return R.layout.item_activity
} }
override fun initializeViewBinding(view: View): ItemNotificationBinding { override fun initializeViewBinding(view: View): ItemActivityBinding {
return ItemNotificationBinding.bind(view) return ItemActivityBinding.bind(view)
} }
} }

View file

@ -5,63 +5,7 @@ import ani.dantotsu.connections.anilist.api.NotificationType
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
class ActivityItemBuilder {
/*
* 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 { companion object {
fun getContent(notification: Notification): String { fun getContent(notification: Notification): String {

View file

@ -4,7 +4,6 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.view.ViewGroup
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.connections.anilist.api.NotificationType import ani.dantotsu.connections.anilist.api.NotificationType
@ -73,8 +72,8 @@ class NotificationItem(
private fun setBinding() { private fun setBinding() {
val notificationType: NotificationType = val notificationType: NotificationType =
NotificationType.valueOf(notification.notificationType) NotificationType.valueOf(notification.notificationType)
binding.notificationText.text = NotificationItemBuilder.getContent(notification) binding.notificationText.text = ActivityItemBuilder.getContent(notification)
binding.notificationDate.text = NotificationItemBuilder.getDateTime(notification.createdAt) binding.notificationDate.text = ActivityItemBuilder.getDateTime(notification.createdAt)
binding.root.setOnClickListener { clickCallback(id, clickType) } binding.root.setOnClickListener { clickCallback(id, clickType) }
when (notificationType) { when (notificationType) {

View file

@ -61,6 +61,7 @@
</LinearLayout> </LinearLayout>
<ImageView <ImageView
android:id="@+id/activityFavorite"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end|center" android:layout_gravity="end|center"