feat: global/personal feed | like posts | pagination
This commit is contained in:
parent
a9b03c45c6
commit
98f4d4f30b
19 changed files with 438 additions and 144 deletions
|
@ -28,6 +28,7 @@ import android.telephony.TelephonyManager
|
|||
import android.text.InputFilter
|
||||
import android.text.Spanned
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.*
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
|
@ -129,7 +130,8 @@ var loadIsMAL = false
|
|||
|
||||
fun logger(e: Any?, print: Boolean = true) {
|
||||
if (print)
|
||||
println(e)
|
||||
//println(e)
|
||||
Log.d("Logger", e.toString())
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ import ani.dantotsu.connections.anilist.Anilist.authorRoles
|
|||
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.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.connections.anilist.api.ToggleLike
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.isOnline
|
||||
import ani.dantotsu.logError
|
||||
|
@ -1265,10 +1265,15 @@ Page(page:$page,perPage:50) {
|
|||
}
|
||||
|
||||
suspend fun toggleFollow(id: Int): Query.ToggleFollow? {
|
||||
val response = executeQuery<Query.ToggleFollow>(
|
||||
return executeQuery<Query.ToggleFollow>(
|
||||
"""mutation{ToggleFollow(userId:$id){id, isFollowing, isFollower}}"""
|
||||
)
|
||||
return response
|
||||
}
|
||||
|
||||
suspend fun toggleLike(id: Int, type: String): ToggleLike? {
|
||||
return executeQuery<ToggleLike>(
|
||||
"""mutation Like{ToggleLikeV2(id:$id,type:$type){__typename}}"""
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getUserProfile(id: Int): Query.UserProfileResponse? {
|
||||
|
@ -1342,8 +1347,8 @@ Page(page:$page,perPage:50) {
|
|||
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)
|
||||
suspend fun getNotifications(id: Int, page: Int = 1): NotificationResponse? {
|
||||
val res = executeQuery<NotificationResponse>("""{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_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
|
||||
}
|
||||
|
@ -1354,7 +1359,11 @@ Page(page:$page,perPage:50) {
|
|||
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}}}}}}""")
|
||||
val res = executeQuery<FeedResponse>("""{Page(page:$page,perPage:$ITEMS_PER_PAGE){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
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ITEMS_PER_PAGE = 25
|
||||
}
|
||||
}
|
|
@ -7,19 +7,19 @@ import kotlinx.serialization.Serializable
|
|||
data class FeedResponse(
|
||||
@SerialName("data")
|
||||
val data: Data
|
||||
) {
|
||||
) : java.io.Serializable {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("Page")
|
||||
val page: ActivityPage
|
||||
)
|
||||
) : java.io.Serializable
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ActivityPage(
|
||||
@SerialName("activities")
|
||||
val activities: List<Activity>
|
||||
)
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Activity(
|
||||
|
@ -52,9 +52,9 @@ data class Activity(
|
|||
@SerialName("isSubscribed")
|
||||
val isSubscribed: Boolean,
|
||||
@SerialName("likeCount")
|
||||
val likeCount: Int?,
|
||||
var likeCount: Int?,
|
||||
@SerialName("isLiked")
|
||||
val isLiked: Boolean?,
|
||||
var isLiked: Boolean?,
|
||||
@SerialName("isPinned")
|
||||
val isPinned: Boolean?,
|
||||
@SerialName("isPrivate")
|
||||
|
@ -69,7 +69,7 @@ data class Activity(
|
|||
val replies: List<Reply>?,
|
||||
@SerialName("likes")
|
||||
val likes: List<User>?,
|
||||
)
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Reply(
|
||||
|
@ -89,4 +89,22 @@ data class Reply(
|
|||
val user: User,
|
||||
@SerialName("likes")
|
||||
val likes: List<User>?,
|
||||
)
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ToggleLike(
|
||||
@SerialName("data")
|
||||
val data: Data
|
||||
) : java.io.Serializable {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("ToggleLikeV2")
|
||||
val toggleLike: LikeData
|
||||
) : java.io.Serializable
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LikeData(
|
||||
@SerialName("__typename")
|
||||
val typename: String
|
||||
) : java.io.Serializable
|
|
@ -14,6 +14,7 @@ import ani.dantotsu.connections.anilist.Anilist
|
|||
import ani.dantotsu.connections.anilist.api.User
|
||||
import ani.dantotsu.databinding.ActivityFollowBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.statusBarHeight
|
||||
|
@ -36,6 +37,7 @@ class FollowActivity : AppCompatActivity(){
|
|||
initActivity(this)
|
||||
binding = ActivityFollowBinding.inflate(layoutInflater)
|
||||
binding.listToolbar.updateLayoutParams<MarginLayoutParams> { topMargin = statusBarHeight }
|
||||
binding.listFrameLayout.updateLayoutParams<MarginLayoutParams> { bottomMargin = navBarHeight }
|
||||
setContentView(binding.root)
|
||||
val layoutType = PrefManager.getVal<Int>(PrefName.FollowerLayout)
|
||||
selected = getSelected(layoutType)
|
||||
|
|
|
@ -22,7 +22,7 @@ import ani.dantotsu.initActivity
|
|||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.others.ImageViewDialog
|
||||
import ani.dantotsu.profile.activity.ActivityActivity
|
||||
import ani.dantotsu.profile.activity.FeedFragment
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.snackString
|
||||
|
@ -36,8 +36,8 @@ import nl.joery.animatedbottombar.AnimatedBottomBar
|
|||
|
||||
|
||||
class ProfileActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityProfileBinding
|
||||
private var selected: Int = 0
|
||||
lateinit var binding: ActivityProfileBinding
|
||||
private var selected: Int = 1
|
||||
private lateinit var navBar: AnimatedBottomBar
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
|
@ -49,8 +49,10 @@ class ProfileActivity : AppCompatActivity() {
|
|||
setContentView(binding.root)
|
||||
navBar = binding.profileNavBar
|
||||
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarHeight }
|
||||
val feedTab = navBar.createTab(R.drawable.ic_round_filter_24, "Feed")
|
||||
val profileTab = navBar.createTab(R.drawable.ic_round_person_24, "Profile")
|
||||
val statsTab = navBar.createTab(R.drawable.ic_stats_24, "Stats")
|
||||
navBar.addTab(feedTab)
|
||||
navBar.addTab(profileTab)
|
||||
navBar.addTab(statsTab)
|
||||
navBar.visibility = View.GONE
|
||||
|
@ -70,6 +72,7 @@ class ProfileActivity : AppCompatActivity() {
|
|||
}
|
||||
binding.profileViewPager.adapter =
|
||||
ViewPagerAdapter(supportFragmentManager, lifecycle, user)
|
||||
binding.profileViewPager.setCurrentItem(selected, false)
|
||||
navBar.visibility = View.VISIBLE
|
||||
navBar.selectTabAt(selected)
|
||||
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
|
||||
|
@ -106,15 +109,6 @@ class ProfileActivity : AppCompatActivity() {
|
|||
}
|
||||
binding.profileProgressBar.visibility = View.GONE
|
||||
binding.profileTopContainer.visibility = View.VISIBLE
|
||||
binding.profileActivityButton.setOnClickListener {
|
||||
ContextCompat.startActivity(
|
||||
this@ProfileActivity,
|
||||
Intent(this@ProfileActivity, ActivityActivity::class.java)
|
||||
.putExtra("userId", user.id)
|
||||
.putExtra("username", user.name),
|
||||
null
|
||||
)
|
||||
}
|
||||
binding.profileMenuButton.setOnClickListener {
|
||||
val popup = PopupMenu(this@ProfileActivity, binding.profileMenuButton)
|
||||
popup.menuInflater.inflate(R.menu.menu_profile, popup.menu)
|
||||
|
@ -161,7 +155,6 @@ class ProfileActivity : AppCompatActivity() {
|
|||
binding.profileBannerImage.updateLayoutParams { height += statusBarHeight }
|
||||
binding.profileBannerGradient.updateLayoutParams { height += statusBarHeight }
|
||||
binding.profileMenuButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
||||
binding.profileActivityButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
||||
binding.profileBannerImage.setOnLongClickListener {
|
||||
ImageViewDialog.newInstance(
|
||||
this@ProfileActivity,
|
||||
|
@ -188,10 +181,11 @@ class ProfileActivity : AppCompatActivity() {
|
|||
) :
|
||||
FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
|
||||
override fun getItemCount(): Int = 2
|
||||
override fun getItemCount(): Int = 3
|
||||
override fun createFragment(position: Int): Fragment = when (position) {
|
||||
0 -> ProfileFragment.newInstance(user)
|
||||
1 -> StatsFragment.newInstance(user)
|
||||
0 -> FeedFragment.newInstance(user.id, false)
|
||||
1 -> ProfileFragment.newInstance(user)
|
||||
2 -> StatsFragment.newInstance(user)
|
||||
else -> ProfileFragment.newInstance(user)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class ProfileFragment() : Fragment() {
|
||||
class ProfileFragment : Fragment() {
|
||||
lateinit var binding: FragmentProfileBinding
|
||||
private lateinit var activity: ProfileActivity
|
||||
private lateinit var user: Query.UserProfile
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package ani.dantotsu.profile.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
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.initActivity
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ActivityActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityFollowBinding
|
||||
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||
private var activityList: List<Activity> = emptyList()
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityFollowBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.listTitle.text = "Activity"
|
||||
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.followerList.visibility = ViewGroup.GONE
|
||||
binding.listBack.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
binding.listProgressBar.visibility = ViewGroup.VISIBLE
|
||||
var userId: Int? = intent.getIntExtra("userId", -1)
|
||||
if (userId == -1) userId = null
|
||||
val global = intent.getBooleanExtra("global", false)
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val res = Anilist.query.getFeed(userId, global)
|
||||
|
||||
withContext(Dispatchers.Main){
|
||||
res?.data?.page?.activities?.let { activities ->
|
||||
activityList = activities
|
||||
adapter.update(activityList.map { ActivityItem(it){} })
|
||||
}
|
||||
binding.listProgressBar.visibility = ViewGroup.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,21 +6,29 @@ import android.view.View
|
|||
import androidx.core.content.ContextCompat
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.buildMarkwon
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.api.Activity
|
||||
import ani.dantotsu.databinding.ItemActivityBinding
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.snackString
|
||||
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 jp.wasabeef.glide.transformations.BlurTransformation
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ActivityItem(
|
||||
private val activity: Activity,
|
||||
val clickCallback: (Int) -> Unit
|
||||
): BindableItem<ItemActivityBinding>() {
|
||||
val clickCallback: (Int, type: String) -> Unit
|
||||
) : BindableItem<ItemActivityBinding>() {
|
||||
private lateinit var binding: ItemActivityBinding
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bind(viewBinding: ItemActivityBinding, position: Int) {
|
||||
binding = viewBinding
|
||||
|
@ -28,24 +36,48 @@ class ActivityItem(
|
|||
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.commentRepliesContainer.visibility = if (activity.replyCount > 0) View.VISIBLE else View.GONE
|
||||
val likeColor = ContextCompat.getColor(binding.root.context, R.color.yt_red)
|
||||
val notLikeColor = ContextCompat.getColor(binding.root.context, R.color.bg_opp)
|
||||
binding.activityLike.setColorFilter(if (activity.isLiked == true) likeColor else notLikeColor)
|
||||
binding.commentRepliesContainer.visibility =
|
||||
if (activity.replyCount > 0) View.VISIBLE else View.GONE
|
||||
binding.activityLikeCount.text = activity.likeCount.toString()
|
||||
|
||||
binding.activityLike.setOnClickListener {
|
||||
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
scope.launch {
|
||||
val res = Anilist.query.toggleLike(activity.id, "ACTIVITY")
|
||||
withContext(Dispatchers.Main) {
|
||||
if (res != null) {
|
||||
|
||||
if (activity.isLiked == true) {
|
||||
activity.likeCount = activity.likeCount?.minus(1)
|
||||
} else {
|
||||
activity.likeCount = activity.likeCount?.plus(1)
|
||||
}
|
||||
binding.activityLikeCount.text = activity.likeCount.toString()
|
||||
activity.isLiked = !activity.isLiked!!
|
||||
binding.activityLike.setColorFilter(if (activity.isLiked == true) likeColor else notLikeColor)
|
||||
|
||||
} else {
|
||||
snackString("Failed to like activity")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val context = binding.root.context
|
||||
|
||||
when (activity.typename) {
|
||||
"ListActivity" ->{
|
||||
"ListActivity" -> {
|
||||
binding.activityContent.visibility = View.GONE
|
||||
binding.activityBannerContainer.visibility = View.VISIBLE
|
||||
|
||||
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 banner = activity.media.bannerImage
|
||||
binding.activityText.text =
|
||||
"""${activity.user!!.name} ${activity.status} ${activity.progress ?: ""}"""
|
||||
binding.activityCover.loadImage(activity.media?.coverImage?.medium)
|
||||
val banner = activity.media?.bannerImage
|
||||
if (banner != null) {
|
||||
if (!(context as android.app.Activity).isDestroyed) {
|
||||
Glide.with(context as Context)
|
||||
|
@ -58,6 +90,7 @@ class ActivityItem(
|
|||
binding.activityBannerImage.setImageResource(R.drawable.linear_gradient_bg)
|
||||
}
|
||||
}
|
||||
|
||||
"TextActivity" -> {
|
||||
binding.activityBannerContainer.visibility = View.GONE
|
||||
binding.activityContent.visibility = View.VISIBLE
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package ani.dantotsu.profile.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.databinding.ActivityFeedBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import nl.joery.animatedbottombar.AnimatedBottomBar
|
||||
|
||||
class FeedActivity: AppCompatActivity() {
|
||||
private lateinit var binding: ActivityFeedBinding
|
||||
private var selected: Int = 0
|
||||
private lateinit var navBar: AnimatedBottomBar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityFeedBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
navBar = binding.feedNavBar
|
||||
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin += navBarHeight }
|
||||
val personalTab = navBar.createTab(R.drawable.ic_round_person_24, "Personal")
|
||||
val globalTab = navBar.createTab(R.drawable.ic_globe_24, "Global")
|
||||
navBar.addTab(personalTab)
|
||||
navBar.addTab(globalTab)
|
||||
binding.listTitle.text = "Activities"
|
||||
binding.feedViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin += navBarHeight
|
||||
topMargin += statusBarHeight
|
||||
}
|
||||
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
||||
binding.feedViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle)
|
||||
binding.feedViewPager.setCurrentItem(selected, false)
|
||||
binding.feedViewPager.isUserInputEnabled = false
|
||||
navBar.selectTabAt(selected)
|
||||
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
|
||||
override fun onTabSelected(
|
||||
lastIndex: Int,
|
||||
lastTab: AnimatedBottomBar.Tab?,
|
||||
newIndex: Int,
|
||||
newTab: AnimatedBottomBar.Tab
|
||||
) {
|
||||
selected = newIndex
|
||||
binding.feedViewPager.setCurrentItem(selected, true)
|
||||
}
|
||||
})
|
||||
binding.listBack.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
navBar.selectTabAt(selected)
|
||||
}
|
||||
|
||||
|
||||
private class ViewPagerAdapter(
|
||||
fragmentManager: FragmentManager,
|
||||
lifecycle: Lifecycle
|
||||
) : FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
override fun getItemCount(): Int = 2
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> FeedFragment.newInstance(null, false)
|
||||
else -> FeedFragment.newInstance(null, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
111
app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt
Normal file
111
app/src/main/java/ani/dantotsu/profile/activity/FeedFragment.kt
Normal file
|
@ -0,0 +1,111 @@
|
|||
package ani.dantotsu.profile.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.AnilistQueries
|
||||
import ani.dantotsu.connections.anilist.api.Activity
|
||||
import ani.dantotsu.databinding.FragmentFeedBinding
|
||||
import ani.dantotsu.logger
|
||||
import ani.dantotsu.profile.ProfileActivity
|
||||
import ani.dantotsu.snackString
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class FeedFragment : Fragment() {
|
||||
private lateinit var binding: FragmentFeedBinding
|
||||
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||
private var activityList: List<Activity> = emptyList()
|
||||
private lateinit var activity: androidx.activity.ComponentActivity
|
||||
private var page: Int = 1
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentFeedBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
activity = requireActivity()
|
||||
binding.listRecyclerView.adapter = adapter
|
||||
binding.listRecyclerView.layoutManager =
|
||||
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||
binding.listProgressBar.visibility = ViewGroup.VISIBLE
|
||||
var userId: Int? = arguments?.getInt("userId", -1)
|
||||
if (userId == -1) userId = null
|
||||
val global = arguments?.getBoolean("global", false) ?: false
|
||||
|
||||
activity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
val res = Anilist.query.getFeed(userId, global)
|
||||
withContext(Dispatchers.Main) {
|
||||
res?.data?.page?.activities?.let { activities ->
|
||||
activityList = activities
|
||||
adapter.update(activityList.map { ActivityItem(it) { _, _ -> } })
|
||||
}
|
||||
binding.listProgressBar.visibility = ViewGroup.GONE
|
||||
val scrollView = if (activity is ProfileActivity) {
|
||||
(activity as ProfileActivity).binding.profileScrollView
|
||||
} else {
|
||||
binding.listRecyclerView
|
||||
}
|
||||
binding.listRecyclerView.setOnTouchListener { _, event ->
|
||||
if (event?.action == MotionEvent.ACTION_UP) {
|
||||
if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) {
|
||||
snackString("No more activities")
|
||||
} else if (!scrollView.canScrollVertically(1) && !binding.feedRefresh.isVisible
|
||||
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
|
||||
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
|
||||
) {
|
||||
page++
|
||||
binding.feedRefresh.visibility = ViewGroup.VISIBLE
|
||||
activity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
val res = Anilist.query.getFeed(userId, global, page)
|
||||
withContext(Dispatchers.Main) {
|
||||
res?.data?.page?.activities?.let { activities ->
|
||||
activityList += activities
|
||||
adapter.addAll(activities.map { ActivityItem(it) { _, _ -> } })
|
||||
}
|
||||
binding.feedRefresh.visibility = ViewGroup.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (this::binding.isInitialized) {
|
||||
binding.root.requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(userId: Int?, global: Boolean): FeedFragment {
|
||||
val fragment = FeedFragment()
|
||||
val args = Bundle()
|
||||
args.putInt("userId", userId ?: -1)
|
||||
args.putBoolean("global", global)
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,18 +3,23 @@ package ani.dantotsu.profile.activity
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
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.AnilistQueries
|
||||
import ani.dantotsu.connections.anilist.api.Notification
|
||||
import ani.dantotsu.databinding.ActivityFollowBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.media.MediaDetailsActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.profile.ProfileActivity
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
|
@ -26,8 +31,9 @@ class NotificationActivity : AppCompatActivity() {
|
|||
private lateinit var binding: ActivityFollowBinding
|
||||
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||
private var notificationList: List<Notification> = emptyList()
|
||||
private var page: Int = 1
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
|
@ -36,6 +42,7 @@ class NotificationActivity : AppCompatActivity() {
|
|||
setContentView(binding.root)
|
||||
binding.listTitle.text = "Notifications"
|
||||
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight }
|
||||
binding.listFrameLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarHeight }
|
||||
binding.listRecyclerView.adapter = adapter
|
||||
binding.listRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
binding.followerGrid.visibility = ViewGroup.GONE
|
||||
|
@ -52,6 +59,30 @@ class NotificationActivity : AppCompatActivity() {
|
|||
}
|
||||
withContext(Dispatchers.Main){
|
||||
binding.listProgressBar.visibility = ViewGroup.GONE
|
||||
binding.listRecyclerView.setOnTouchListener { _, event ->
|
||||
if (event?.action == MotionEvent.ACTION_UP) {
|
||||
if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) {
|
||||
snackString("No more notifications")
|
||||
} else if (!binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible
|
||||
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
|
||||
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
|
||||
) {
|
||||
page++
|
||||
binding.followRefresh.visibility = ViewGroup.VISIBLE
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val res = Anilist.query.getNotifications(Anilist.userid?:0, page)
|
||||
withContext(Dispatchers.Main) {
|
||||
res?.data?.page?.notifications?.let { notifications ->
|
||||
notificationList += notifications
|
||||
adapter.addAll(notifications.map { NotificationItem(it, ::onNotificationClick) })
|
||||
}
|
||||
binding.followRefresh.visibility = ViewGroup.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import ani.dantotsu.incognitoNotification
|
|||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.profile.activity.NotificationActivity
|
||||
import ani.dantotsu.offline.OfflineFragment
|
||||
import ani.dantotsu.profile.activity.FeedActivity
|
||||
import ani.dantotsu.setSafeOnClickListener
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
|
@ -108,6 +109,11 @@ class SettingsDialogFragment : BottomSheetDialogFragment() {
|
|||
dismiss()
|
||||
}
|
||||
|
||||
binding.settingsActivity.setSafeOnClickListener {
|
||||
startActivity(Intent(activity, FeedActivity::class.java))
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.settingsNotification.setOnClickListener {
|
||||
startActivity(Intent(activity, NotificationActivity::class.java))
|
||||
dismiss()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue