feat: global/personal feed | like posts | pagination

This commit is contained in:
rebelonion 2024-03-09 04:33:06 -06:00
parent a9b03c45c6
commit 98f4d4f30b
19 changed files with 438 additions and 144 deletions

View file

@ -115,7 +115,7 @@
android:windowSoftInputMode="adjustResize|stateHidden" android:windowSoftInputMode="adjustResize|stateHidden"
android:parentActivityName=".MainActivity" /> android:parentActivityName=".MainActivity" />
<activity <activity
android:name=".profile.activity.ActivityActivity" android:name=".profile.activity.FeedActivity"
android:label="Inbox Activity" android:label="Inbox Activity"
android:parentActivityName=".MainActivity" > android:parentActivityName=".MainActivity" >
</activity> </activity>

View file

@ -28,6 +28,7 @@ import android.telephony.TelephonyManager
import android.text.InputFilter import android.text.InputFilter
import android.text.Spanned import android.text.Spanned
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.*
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
@ -129,7 +130,8 @@ var loadIsMAL = false
fun logger(e: Any?, print: Boolean = true) { fun logger(e: Any?, print: Boolean = true) {
if (print) if (print)
println(e) //println(e)
Log.d("Logger", e.toString())
} }

View file

@ -8,10 +8,10 @@ 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.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.NotificationResponse import ani.dantotsu.connections.anilist.api.NotificationResponse
import ani.dantotsu.connections.anilist.api.Page import ani.dantotsu.connections.anilist.api.Page
import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.connections.anilist.api.ToggleLike
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.isOnline import ani.dantotsu.isOnline
import ani.dantotsu.logError import ani.dantotsu.logError
@ -1265,10 +1265,15 @@ Page(page:$page,perPage:50) {
} }
suspend fun toggleFollow(id: Int): Query.ToggleFollow? { suspend fun toggleFollow(id: Int): Query.ToggleFollow? {
val response = executeQuery<Query.ToggleFollow>( return executeQuery<Query.ToggleFollow>(
"""mutation{ToggleFollow(userId:$id){id, isFollowing, isFollower}}""" """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? { suspend fun getUserProfile(id: Int): Query.UserProfileResponse? {
@ -1342,8 +1347,8 @@ Page(page:$page,perPage:50) {
return default return default
} }
suspend fun getNotifications(id: Int): NotificationResponse? { suspend fun getNotifications(id: Int, page: Int = 1): 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) 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
} }
@ -1354,7 +1359,11 @@ 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: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 return res
} }
companion object {
const val ITEMS_PER_PAGE = 25
}
} }

View file

@ -7,19 +7,19 @@ import kotlinx.serialization.Serializable
data class FeedResponse( data class FeedResponse(
@SerialName("data") @SerialName("data")
val data: Data val data: Data
) { ) : java.io.Serializable {
@Serializable @Serializable
data class Data( data class Data(
@SerialName("Page") @SerialName("Page")
val page: ActivityPage val page: ActivityPage
) ) : java.io.Serializable
} }
@Serializable @Serializable
data class ActivityPage( data class ActivityPage(
@SerialName("activities") @SerialName("activities")
val activities: List<Activity> val activities: List<Activity>
) ) : java.io.Serializable
@Serializable @Serializable
data class Activity( data class Activity(
@ -52,9 +52,9 @@ data class Activity(
@SerialName("isSubscribed") @SerialName("isSubscribed")
val isSubscribed: Boolean, val isSubscribed: Boolean,
@SerialName("likeCount") @SerialName("likeCount")
val likeCount: Int?, var likeCount: Int?,
@SerialName("isLiked") @SerialName("isLiked")
val isLiked: Boolean?, var isLiked: Boolean?,
@SerialName("isPinned") @SerialName("isPinned")
val isPinned: Boolean?, val isPinned: Boolean?,
@SerialName("isPrivate") @SerialName("isPrivate")
@ -69,7 +69,7 @@ data class Activity(
val replies: List<Reply>?, val replies: List<Reply>?,
@SerialName("likes") @SerialName("likes")
val likes: List<User>?, val likes: List<User>?,
) ) : java.io.Serializable
@Serializable @Serializable
data class Reply( data class Reply(
@ -89,4 +89,22 @@ data class Reply(
val user: User, val user: User,
@SerialName("likes") @SerialName("likes")
val likes: List<User>?, 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

View file

@ -14,6 +14,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.User import ani.dantotsu.connections.anilist.api.User
import ani.dantotsu.databinding.ActivityFollowBinding import ani.dantotsu.databinding.ActivityFollowBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
@ -36,6 +37,7 @@ class FollowActivity : AppCompatActivity(){
initActivity(this) initActivity(this)
binding = ActivityFollowBinding.inflate(layoutInflater) binding = ActivityFollowBinding.inflate(layoutInflater)
binding.listToolbar.updateLayoutParams<MarginLayoutParams> { topMargin = statusBarHeight } binding.listToolbar.updateLayoutParams<MarginLayoutParams> { topMargin = statusBarHeight }
binding.listFrameLayout.updateLayoutParams<MarginLayoutParams> { bottomMargin = navBarHeight }
setContentView(binding.root) setContentView(binding.root)
val layoutType = PrefManager.getVal<Int>(PrefName.FollowerLayout) val layoutType = PrefManager.getVal<Int>(PrefName.FollowerLayout)
selected = getSelected(layoutType) selected = getSelected(layoutType)

View file

@ -22,7 +22,7 @@ import ani.dantotsu.initActivity
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.others.ImageViewDialog 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.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
@ -36,8 +36,8 @@ import nl.joery.animatedbottombar.AnimatedBottomBar
class ProfileActivity : AppCompatActivity() { class ProfileActivity : AppCompatActivity() {
private lateinit var binding: ActivityProfileBinding lateinit var binding: ActivityProfileBinding
private var selected: Int = 0 private var selected: Int = 1
private lateinit var navBar: AnimatedBottomBar private lateinit var navBar: AnimatedBottomBar
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -49,8 +49,10 @@ class ProfileActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
navBar = binding.profileNavBar navBar = binding.profileNavBar
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarHeight } 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 profileTab = navBar.createTab(R.drawable.ic_round_person_24, "Profile")
val statsTab = navBar.createTab(R.drawable.ic_stats_24, "Stats") val statsTab = navBar.createTab(R.drawable.ic_stats_24, "Stats")
navBar.addTab(feedTab)
navBar.addTab(profileTab) navBar.addTab(profileTab)
navBar.addTab(statsTab) navBar.addTab(statsTab)
navBar.visibility = View.GONE navBar.visibility = View.GONE
@ -70,6 +72,7 @@ class ProfileActivity : AppCompatActivity() {
} }
binding.profileViewPager.adapter = binding.profileViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle, user) ViewPagerAdapter(supportFragmentManager, lifecycle, user)
binding.profileViewPager.setCurrentItem(selected, false)
navBar.visibility = View.VISIBLE navBar.visibility = View.VISIBLE
navBar.selectTabAt(selected) navBar.selectTabAt(selected)
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
@ -106,15 +109,6 @@ class ProfileActivity : AppCompatActivity() {
} }
binding.profileProgressBar.visibility = View.GONE binding.profileProgressBar.visibility = View.GONE
binding.profileTopContainer.visibility = View.VISIBLE 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 { binding.profileMenuButton.setOnClickListener {
val popup = PopupMenu(this@ProfileActivity, binding.profileMenuButton) val popup = PopupMenu(this@ProfileActivity, binding.profileMenuButton)
popup.menuInflater.inflate(R.menu.menu_profile, popup.menu) popup.menuInflater.inflate(R.menu.menu_profile, popup.menu)
@ -161,7 +155,6 @@ class ProfileActivity : AppCompatActivity() {
binding.profileBannerImage.updateLayoutParams { height += statusBarHeight } binding.profileBannerImage.updateLayoutParams { height += statusBarHeight }
binding.profileBannerGradient.updateLayoutParams { height += statusBarHeight } binding.profileBannerGradient.updateLayoutParams { height += statusBarHeight }
binding.profileMenuButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight } binding.profileMenuButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
binding.profileActivityButton.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
binding.profileBannerImage.setOnLongClickListener { binding.profileBannerImage.setOnLongClickListener {
ImageViewDialog.newInstance( ImageViewDialog.newInstance(
this@ProfileActivity, this@ProfileActivity,
@ -188,10 +181,11 @@ class ProfileActivity : AppCompatActivity() {
) : ) :
FragmentStateAdapter(fragmentManager, lifecycle) { FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int = 2 override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment = when (position) { override fun createFragment(position: Int): Fragment = when (position) {
0 -> ProfileFragment.newInstance(user) 0 -> FeedFragment.newInstance(user.id, false)
1 -> StatsFragment.newInstance(user) 1 -> ProfileFragment.newInstance(user)
2 -> StatsFragment.newInstance(user)
else -> ProfileFragment.newInstance(user) else -> ProfileFragment.newInstance(user)
} }
} }

View file

@ -32,7 +32,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ProfileFragment() : Fragment() { class ProfileFragment : Fragment() {
lateinit var binding: FragmentProfileBinding lateinit var binding: FragmentProfileBinding
private lateinit var activity: ProfileActivity private lateinit var activity: ProfileActivity
private lateinit var user: Query.UserProfile private lateinit var user: Query.UserProfile

View file

@ -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
}
}
}
}

View file

@ -6,21 +6,29 @@ import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.buildMarkwon import ani.dantotsu.buildMarkwon
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Activity import ani.dantotsu.connections.anilist.api.Activity
import ani.dantotsu.databinding.ItemActivityBinding import ani.dantotsu.databinding.ItemActivityBinding
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.snackString
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import jp.wasabeef.glide.transformations.BlurTransformation 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( class ActivityItem(
private val activity: Activity, private val activity: Activity,
val clickCallback: (Int) -> Unit val clickCallback: (Int, type: String) -> Unit
): BindableItem<ItemActivityBinding>() { ) : BindableItem<ItemActivityBinding>() {
private lateinit var binding: ItemActivityBinding private lateinit var binding: ItemActivityBinding
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun bind(viewBinding: ItemActivityBinding, position: Int) { override fun bind(viewBinding: ItemActivityBinding, position: Int) {
binding = viewBinding binding = viewBinding
@ -28,24 +36,48 @@ class ActivityItem(
binding.activityUserName.text = activity.user?.name binding.activityUserName.text = activity.user?.name
binding.activityUserAvatar.loadImage(activity.user?.avatar?.medium) binding.activityUserAvatar.loadImage(activity.user?.avatar?.medium)
binding.activityTime.text = ActivityItemBuilder.getDateTime(activity.createdAt) binding.activityTime.text = ActivityItemBuilder.getDateTime(activity.createdAt)
val color = if (activity.isLiked == true) val likeColor = ContextCompat.getColor(binding.root.context, R.color.yt_red)
ContextCompat.getColor(binding.root.context, R.color.yt_red) val notLikeColor = ContextCompat.getColor(binding.root.context, R.color.bg_opp)
else binding.activityLike.setColorFilter(if (activity.isLiked == true) likeColor else notLikeColor)
ContextCompat.getColor(binding.root.context, R.color.bg_opp) binding.commentRepliesContainer.visibility =
binding.activityFavorite.setColorFilter(color) if (activity.replyCount > 0) View.VISIBLE else View.GONE
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 val context = binding.root.context
when (activity.typename) { when (activity.typename) {
"ListActivity" ->{ "ListActivity" -> {
binding.activityContent.visibility = View.GONE binding.activityContent.visibility = View.GONE
binding.activityBannerContainer.visibility = View.VISIBLE binding.activityBannerContainer.visibility = View.VISIBLE
binding.activityMediaName.text = activity.media?.title?.userPreferred binding.activityMediaName.text = activity.media?.title?.userPreferred
binding.activityText.text = "${activity.user!!.name} ${activity.status} ${activity.media!!.title!!.userPreferred}" binding.activityText.text =
binding.activityCover.loadImage(activity.media.coverImage?.medium) """${activity.user!!.name} ${activity.status} ${activity.progress ?: ""}"""
val banner = activity.media.bannerImage binding.activityCover.loadImage(activity.media?.coverImage?.medium)
val banner = activity.media?.bannerImage
if (banner != null) { if (banner != null) {
if (!(context as android.app.Activity).isDestroyed) { if (!(context as android.app.Activity).isDestroyed) {
Glide.with(context as Context) Glide.with(context as Context)
@ -58,6 +90,7 @@ class ActivityItem(
binding.activityBannerImage.setImageResource(R.drawable.linear_gradient_bg) binding.activityBannerImage.setImageResource(R.drawable.linear_gradient_bg)
} }
} }
"TextActivity" -> { "TextActivity" -> {
binding.activityBannerContainer.visibility = View.GONE binding.activityBannerContainer.visibility = View.GONE
binding.activityContent.visibility = View.VISIBLE binding.activityContent.visibility = View.VISIBLE

View file

@ -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)
}
}
}
}

View 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
}
}
}

View file

@ -3,18 +3,23 @@ package ani.dantotsu.profile.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MotionEvent
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistQueries
import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.connections.anilist.api.Notification
import ani.dantotsu.databinding.ActivityFollowBinding import ani.dantotsu.databinding.ActivityFollowBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
@ -26,8 +31,9 @@ class NotificationActivity : AppCompatActivity() {
private lateinit var binding: ActivityFollowBinding private lateinit var binding: ActivityFollowBinding
private var adapter: GroupieAdapter = GroupieAdapter() private var adapter: GroupieAdapter = GroupieAdapter()
private var notificationList: List<Notification> = emptyList() private var notificationList: List<Notification> = emptyList()
private var page: Int = 1
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n", "ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
@ -36,6 +42,7 @@ class NotificationActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
binding.listTitle.text = "Notifications" binding.listTitle.text = "Notifications"
binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight } binding.listToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight }
binding.listFrameLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarHeight }
binding.listRecyclerView.adapter = adapter binding.listRecyclerView.adapter = adapter
binding.listRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) binding.listRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.followerGrid.visibility = ViewGroup.GONE binding.followerGrid.visibility = ViewGroup.GONE
@ -52,6 +59,30 @@ class NotificationActivity : AppCompatActivity() {
} }
withContext(Dispatchers.Main){ withContext(Dispatchers.Main){
binding.listProgressBar.visibility = ViewGroup.GONE 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
}
} }
} }
} }

View file

@ -25,6 +25,7 @@ import ani.dantotsu.incognitoNotification
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.profile.activity.NotificationActivity import ani.dantotsu.profile.activity.NotificationActivity
import ani.dantotsu.offline.OfflineFragment import ani.dantotsu.offline.OfflineFragment
import ani.dantotsu.profile.activity.FeedActivity
import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
@ -108,6 +109,11 @@ class SettingsDialogFragment : BottomSheetDialogFragment() {
dismiss() dismiss()
} }
binding.settingsActivity.setSafeOnClickListener {
startActivity(Intent(activity, FeedActivity::class.java))
dismiss()
}
binding.settingsNotification.setOnClickListener { binding.settingsNotification.setOnClickListener {
startActivity(Intent(activity, NotificationActivity::class.java)) startActivity(Intent(activity, NotificationActivity::class.java))
dismiss() dismiss()

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,880q-82,0 -155,-31.5t-127.5,-86Q143,708 111.5,635T80,480q0,-83 31.5,-155.5t86,-127Q252,143 325,111.5T480,80q83,0 155.5,31.5t127,86q54.5,54.5 86,127T880,480q0,82 -31.5,155t-86,127.5q-54.5,54.5 -127,86T480,880ZM480,798q26,-36 45,-75t31,-83L404,640q12,44 31,83t45,75ZM376,782q-18,-33 -31.5,-68.5T322,640L204,640q29,50 72.5,87t99.5,55ZM584,782q56,-18 99.5,-55t72.5,-87L638,640q-9,38 -22.5,73.5T584,782ZM170,560h136q-3,-20 -4.5,-39.5T300,480q0,-21 1.5,-40.5T306,400L170,400q-5,20 -7.5,39.5T160,480q0,21 2.5,40.5T170,560ZM386,560h188q3,-20 4.5,-39.5T580,480q0,-21 -1.5,-40.5T574,400L386,400q-3,20 -4.5,39.5T380,480q0,21 1.5,40.5T386,560ZM654,560h136q5,-20 7.5,-39.5T800,480q0,-21 -2.5,-40.5T790,400L654,400q3,20 4.5,39.5T660,480q0,21 -1.5,40.5T654,560ZM638,320h118q-29,-50 -72.5,-87T584,178q18,33 31.5,68.5T638,320ZM404,320h152q-12,-44 -31,-83t-45,-75q-26,36 -45,75t-31,83ZM204,320h118q9,-38 22.5,-73.5T376,178q-56,18 -99.5,55T204,320Z"/>
</vector>

View file

@ -1,19 +1,13 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<ProgressBar
android:id="@+id/listProgressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<FrameLayout <FrameLayout
android:id="@+id/listToolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:orientation="horizontal"> android:orientation="horizontal">
@ -38,18 +32,38 @@
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:gravity="center|start" android:gravity="center|start"
android:singleLine="true" android:singleLine="true"
android:text="Activity"
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
android:textColor="?attr/colorOnBackground" android:textColor="?attr/colorOnBackground"
android:textSize="18sp" android:textSize="18sp"
tools:ignore="HardcodedText" /> tools:text="Activities" />
</FrameLayout> </FrameLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.viewpager2.widget.ViewPager2
android:id="@+id/listRecyclerView" android:id="@+id/feedViewPager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginStart="8dp" android:layout_marginBottom="72dp"
android:layout_marginEnd="8dp" android:layout_marginTop="48dp"
tools:listitem="@layout/item_activity" /> tools:ignore="SpeakableTextPresentCheck" />
</LinearLayout>
<nl.joery.animatedbottombar.AnimatedBottomBar
android:id="@+id/feedNavBar"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_gravity="center_horizontal|bottom"
android:background="?attr/colorSurface"
android:padding="0dp"
app:abb_animationInterpolator="@anim/over_shoot"
app:abb_indicatorAppearance="round"
app:abb_indicatorLocation="top"
app:abb_selectedTabType="text"
app:abb_textAppearance="@style/NavBarText"
app:itemActiveIndicatorStyle="@style/BottomNavBar"
app:itemIconTint="@color/tab_layout_icon"
app:itemRippleColor="#00000000"
app:itemTextAppearanceActive="@style/NavBarText"
app:itemTextAppearanceInactive="@style/NavBarText"
app:itemTextColor="@color/tab_layout_icon" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -93,11 +93,26 @@
</FrameLayout> </FrameLayout>
<androidx.recyclerview.widget.RecyclerView <FrameLayout
android:id="@+id/listRecyclerView" android:id="@+id/listFrameLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" <androidx.recyclerview.widget.RecyclerView
tools:listitem="@layout/item_follower" /> android:id="@+id/listRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:nestedScrollingEnabled="true" />
<ProgressBar
android:id="@+id/followRefresh"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="32dp"
android:visibility="gone" />
</FrameLayout>
</LinearLayout> </LinearLayout>

View file

@ -20,6 +20,7 @@
</LinearLayout> </LinearLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/profileScrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="72dp" android:layout_marginBottom="72dp"
@ -118,15 +119,6 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView
android:id="@+id/profileActivityButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="8dp"
android:contentDescription="@string/menu"
android:src="@drawable/inbox_empty"
app:tint="@color/bg_opp" />
<ImageView <ImageView
android:id="@+id/profileMenuButton" android:id="@+id/profileMenuButton"
android:layout_width="32dp" android:layout_width="32dp"

View file

@ -1,6 +1,42 @@
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:orientation="vertical">
</androidx.constraintlayout.widget.ConstraintLayout> <ProgressBar
android:id="@+id/listProgressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:nestedScrollingEnabled="true"
android:visibility="visible"
tools:listitem="@layout/item_activity" />
<ProgressBar
android:id="@+id/feedRefresh"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="32dp"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>

View file

@ -70,14 +70,14 @@
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:id="@+id/activityFavorite" android:id="@+id/activityLike"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/ic_round_favorite_24" android:src="@drawable/ic_round_favorite_24"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<TextView <TextView
android:id="@+id/activityFavoriteCount" android:id="@+id/activityLikeCount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"