feat: combine profile queries

This commit is contained in:
rebelonion 2024-03-10 05:00:23 -05:00
parent 867a4f36b3
commit eb5e2623a0
5 changed files with 165 additions and 116 deletions

View file

@ -281,7 +281,8 @@ class AnilistQueries {
} else { } else {
if (currContext()?.let { isOnline(it) } == true) { if (currContext()?.let { isOnline(it) } == true) {
snackString(currContext()?.getString(R.string.error_getting_data)) snackString(currContext()?.getString(R.string.error_getting_data))
} else { } } else {
}
} }
} }
val mal = async { val mal = async {
@ -335,7 +336,11 @@ class AnilistQueries {
returnArray.addAll(map.values) returnArray.addAll(map.values)
return returnArray return returnArray
} }
val list = PrefManager.getNullableCustomVal("continueAnimeList", listOf<Int>(), List::class.java) as List<Int> val list = PrefManager.getNullableCustomVal(
"continueAnimeList",
listOf<Int>(),
List::class.java
) as List<Int>
if (list.isNotEmpty()) { if (list.isNotEmpty()) {
list.reversed().forEach { list.reversed().forEach {
if (map.containsKey(it)) returnArray.add(map[it]!!) if (map.containsKey(it)) returnArray.add(map[it]!!)
@ -487,7 +492,11 @@ class AnilistQueries {
returnMap["current$type"] = returnArray returnMap["current$type"] = returnArray
return return
} }
val list = PrefManager.getNullableCustomVal("continueAnimeList", listOf<Int>(), List::class.java) as List<Int> val list = PrefManager.getNullableCustomVal(
"continueAnimeList",
listOf<Int>(),
List::class.java
) as List<Int>
if (list.isNotEmpty()) { if (list.isNotEmpty()) {
list.reversed().forEach { list.reversed().forEach {
if (subMap.containsKey(it)) returnArray.add(subMap[it]!!) if (subMap.containsKey(it)) returnArray.add(subMap[it]!!)
@ -512,7 +521,11 @@ class AnilistQueries {
subMap[m.id] = m subMap[m.id] = m
} }
} }
val list = PrefManager.getNullableCustomVal("continueAnimeList", listOf<Int>(), List::class.java) as List<Int> val list = PrefManager.getNullableCustomVal(
"continueAnimeList",
listOf<Int>(),
List::class.java
) as List<Int>
if (list.isNotEmpty()) { if (list.isNotEmpty()) {
list.reversed().forEach { list.reversed().forEach {
if (subMap.containsKey(it)) returnArray.add(subMap[it]!!) if (subMap.containsKey(it)) returnArray.add(subMap[it]!!)
@ -1291,64 +1304,44 @@ Page(page:$page,perPage:50) {
) )
} }
suspend fun userFavMedia(anime: Boolean, id: Int): ArrayList<Media> {
var hasNextPage = true
var page = 0
suspend fun getNextPage(page: Int): List<Media> {
val response = executeQuery<Query.User>("""{${userFavMediaQuery(anime, page, id)}}""")
val favourites = response?.data?.user?.favourites
val apiMediaList = if (anime) favourites?.anime else favourites?.manga
hasNextPage = apiMediaList?.pageInfo?.hasNextPage ?: false
return apiMediaList?.edges?.mapNotNull {
it.node?.let { i ->
Media(i).apply { isFav = true }
}
} ?: return listOf()
}
val responseArray = arrayListOf<Media>()
while (hasNextPage) {
page++
responseArray.addAll(getNextPage(page))
}
return responseArray
}
private fun userFavMediaQuery(anime: Boolean, page: Int, id: Int): String { private fun userFavMediaQuery(anime: Boolean, page: Int, id: Int): String {
return """User(id:${id}){id favourites{${if (anime) "anime" else "manga"}(page:$page){pageInfo{hasNextPage}edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}""" return """User(id:${id}){id favourites{${if (anime) "anime" else "manga"}(page:$page){pageInfo{hasNextPage}edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}"""
} }
suspend fun userFollowing(id: Int): Query.Following? { suspend fun userFollowing(id: Int): Query.Following? {
return executeQuery<Query.Following>("""{Page {following(userId:${id},sort:[USERNAME]){id name avatar{large medium}bannerImage}}}""", force = true) return executeQuery<Query.Following>(
"""{Page {following(userId:${id},sort:[USERNAME]){id name avatar{large medium}bannerImage}}}""",
force = true
)
} }
suspend fun userFollowers(id: Int): Query.Follower? { suspend fun userFollowers(id: Int): Query.Follower? {
return executeQuery<Query.Follower>("""{Page {followers(userId:${id},sort:[USERNAME]){id name avatar{large medium}bannerImage}}}""", force = true) return executeQuery<Query.Follower>(
"""{Page {followers(userId:${id},sort:[USERNAME]){id name avatar{large medium}bannerImage}}}""",
force = true
)
} }
private suspend fun userBannerImage(type: String,id: Int?): String? { suspend fun initProfilePage(id: Int): Query.ProfilePageMedia? {
val response = return executeQuery<Query.ProfilePageMedia>(
executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: ${id}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } } } """) """{
val random = response?.data?.mediaListCollection?.lists?.mapNotNull { favoriteAnime:${userFavMediaQuery(true, 1, id)}
it.entries?.mapNotNull { entry -> favoriteManga:${userFavMediaQuery(false, 1, id)}
val imageUrl = entry.media?.bannerImage animeMediaList:${bannerImageQuery("ANIME", id)}
if (imageUrl != null && imageUrl != "null") imageUrl mangaMediaList:${bannerImageQuery("MANGA", id)}
else null }""".trimIndent(), force = true
} )
}?.flatten()?.randomOrNull() ?: return null
return random
} }
suspend fun getUserBannerImages(id: Int? = Anilist.userid): ArrayList<String?> { private fun bannerImageQuery(type: String, id: Int?): String {
val default = arrayListOf<String?>(null, null) return """MediaListCollection(userId: ${id}, type: $type, chunk:1,perChunk:25, sort: [SCORE_DESC,UPDATED_TIME_DESC]) { lists { entries{ media { id bannerImage } } } }"""
default[0] = userBannerImage("ANIME", id)
default[1] = userBannerImage("MANGA",id)
return default
} }
suspend fun getNotifications(id: Int, page: Int = 1): NotificationResponse? { 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) 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) { if (res != null) {
Anilist.unreadNotificationCount = 0 Anilist.unreadNotificationCount = 0
} }
@ -1359,7 +1352,9 @@ Page(page:$page,perPage:50) {
val filter = if (userId != null) "userId:$userId," val filter = if (userId != null) "userId:$userId,"
else if (global) "isFollowing:false," else if (global) "isFollowing:false,"
else "isFollowing:true," else "isFollowing:true,"
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}}}}}}""") 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 return res
} }

View file

@ -7,10 +7,8 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import ani.dantotsu.BuildConfig import ani.dantotsu.BuildConfig
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.connections.discord.Discord import ani.dantotsu.connections.discord.Discord
import ani.dantotsu.connections.mal.MAL import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.media.Character
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.AppUpdater
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
@ -339,20 +337,53 @@ class ProfileViewModel : ViewModel(){
private val mangaFav: MutableLiveData<ArrayList<Media>> = private val mangaFav: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null) MutableLiveData<ArrayList<Media>>(null)
fun getMangaFav(): LiveData<ArrayList<Media>> = mangaFav fun getMangaFav(): LiveData<ArrayList<Media>> = mangaFav
private val animeFav: MutableLiveData<ArrayList<Media>> = private val animeFav: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null) MutableLiveData<ArrayList<Media>>(null)
fun getAnimeFav(): LiveData<ArrayList<Media>> = animeFav fun getAnimeFav(): LiveData<ArrayList<Media>> = animeFav
private val listImages: MutableLiveData<ArrayList<String?>> = private val listImages: MutableLiveData<ArrayList<String?>> =
MutableLiveData<ArrayList<String?>>(arrayListOf()) MutableLiveData<ArrayList<String?>>(arrayListOf())
fun getListImages(): LiveData<ArrayList<String?>> = listImages fun getListImages(): LiveData<ArrayList<String?>> = listImages
suspend fun setData(id: Int) { suspend fun setData(id: Int) {
mangaFav.postValue(Anilist.query.userFavMedia(false, id)) val res = Anilist.query.initProfilePage(id)
animeFav.postValue(Anilist.query.userFavMedia(true, id)) val mangaList = res?.data?.favoriteManga?.favourites?.manga?.edges?.mapNotNull {
listImages.postValue(Anilist.query.getUserBannerImages(id)) it.node?.let { i ->
Media(i).apply { isFav = true }
}
}
mangaFav.postValue(ArrayList(mangaList ?: arrayListOf()))
val animeList = res?.data?.favoriteAnime?.favourites?.anime?.edges?.mapNotNull {
it.node?.let { i ->
Media(i).apply { isFav = true }
}
}
animeFav.postValue(ArrayList(animeList ?: arrayListOf()))
val bannerImages = arrayListOf<String?>(null, null)
val animeRandom = res?.data?.animeMediaList?.lists?.mapNotNull {
it.entries?.mapNotNull { entry ->
val imageUrl = entry.media?.bannerImage
if (imageUrl != null && imageUrl != "null") imageUrl
else null
}
}?.flatten()?.randomOrNull()
bannerImages[0] = animeRandom
val mangaRandom = res?.data?.mangaMediaList?.lists?.mapNotNull {
it.entries?.mapNotNull { entry ->
val imageUrl = entry.media?.bannerImage
if (imageUrl != null && imageUrl != "null") imageUrl
else null
}
}?.flatten()?.randomOrNull()
bannerImages[1] = mangaRandom
listImages.postValue(bannerImages)
} }
fun refresh() { fun refresh() {

View file

@ -139,6 +139,20 @@ class Query {
) )
} }
@Serializable
data class ProfilePageMedia(
@SerialName("data")
val data: Data?
) {
@Serializable
data class Data(
@SerialName("favoriteAnime") val favoriteAnime: ani.dantotsu.connections.anilist.api.User?,
@SerialName("favoriteManga") val favoriteManga: ani.dantotsu.connections.anilist.api.User?,
@SerialName("animeMediaList") val animeMediaList: ani.dantotsu.connections.anilist.api.MediaListCollection?,
@SerialName("mangaMediaList") val mangaMediaList: ani.dantotsu.connections.anilist.api.MediaListCollection?
)
}
@Serializable @Serializable
data class ToggleFollow( data class ToggleFollow(
@SerialName("data") @SerialName("data")

View file

@ -34,6 +34,7 @@ class StatsFragment :
private var statType: StatType = StatType.COUNT private var statType: StatType = StatType.COUNT
private lateinit var user: Query.UserProfile private lateinit var user: Query.UserProfile
private lateinit var activity: ProfileActivity private lateinit var activity: ProfileActivity
private var loadedFirstTime = false
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -101,6 +102,13 @@ class StatsFragment :
} }
binding.filterContainer.visibility = View.GONE binding.filterContainer.visibility = View.GONE
}
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
if (!loadedFirstTime) {
activity.lifecycleScope.launch { activity.lifecycleScope.launch {
stats.clear() stats.clear()
stats.add(Anilist.query.getUserStatistics(user.id)?.data?.user) stats.add(Anilist.query.getUserStatistics(user.id)?.data?.user)
@ -118,12 +126,8 @@ class StatsFragment :
binding.statisticProgressBar.visibility = View.GONE binding.statisticProgressBar.visibility = View.GONE
} }
} }
loadedFirstTime = true
} }
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
} }
loadStats(type == MediaType.ANIME) loadStats(type == MediaType.ANIME)
} }

View file

@ -28,6 +28,9 @@ class FeedFragment : Fragment() {
private var activityList: List<Activity> = emptyList() private var activityList: List<Activity> = emptyList()
private lateinit var activity: androidx.activity.ComponentActivity private lateinit var activity: androidx.activity.ComponentActivity
private var page: Int = 1 private var page: Int = 1
private var loadedFirstTime = false
private var userId: Int? = null
private var global: Boolean = false
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -38,7 +41,6 @@ class FeedFragment : Fragment() {
return binding.root return binding.root
} }
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
activity = requireActivity() activity = requireActivity()
@ -46,10 +48,17 @@ class FeedFragment : Fragment() {
binding.listRecyclerView.layoutManager = binding.listRecyclerView.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.listProgressBar.visibility = ViewGroup.VISIBLE binding.listProgressBar.visibility = ViewGroup.VISIBLE
var userId: Int? = arguments?.getInt("userId", -1) userId = arguments?.getInt("userId", -1)
if (userId == -1) userId = null if (userId == -1) userId = null
val global = arguments?.getBoolean("global", false) ?: false global = arguments?.getBoolean("global", false) ?: false
}
@SuppressLint("ClickableViewAccessibility")
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
if (!loadedFirstTime) {
activity.lifecycleScope.launch(Dispatchers.IO) { activity.lifecycleScope.launch(Dispatchers.IO) {
val res = Anilist.query.getFeed(userId, global) val res = Anilist.query.getFeed(userId, global)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -86,12 +95,8 @@ class FeedFragment : Fragment() {
} }
} }
} }
loadedFirstTime = true
} }
override fun onResume() {
super.onResume()
if (this::binding.isInitialized) {
binding.root.requestLayout()
} }
} }