diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 6f0dce4..4b26bc2 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -22,6 +22,7 @@ import kr.co.vividnext.sodalive.explorer.ExplorerApi import kr.co.vividnext.sodalive.explorer.ExplorerRepository import kr.co.vividnext.sodalive.explorer.ExplorerViewModel import kr.co.vividnext.sodalive.explorer.profile.UserProfileViewModel +import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewModel import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListViewModel import kr.co.vividnext.sodalive.following.FollowingCreatorRepository import kr.co.vividnext.sodalive.following.FollowingCreatorViewModel @@ -188,6 +189,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { ProfileUpdateViewModel(get()) } viewModel { NicknameUpdateViewModel(get()) } viewModel { MemberTagViewModel(get()) } + viewModel { UserProfileDonationAllViewModel(get()) } } private val repositoryModule = module { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerApi.kt index e1b38f2..409a840 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerApi.kt @@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.explorer.profile.GetCreatorProfileResponse import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest import kr.co.vividnext.sodalive.explorer.profile.cheers.PostWriteCheersRequest import kr.co.vividnext.sodalive.explorer.profile.cheers.PutModifyCheersRequest +import kr.co.vividnext.sodalive.explorer.profile.donation.GetDonationAllResponse import kr.co.vividnext.sodalive.explorer.profile.follow.GetFollowerListResponse import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser import retrofit2.http.Body @@ -35,6 +36,14 @@ interface ExplorerApi { @Header("Authorization") authHeader: String ): Single> + @GET("/explorer/profile/{id}/donation-rank") + fun getCreatorProfileDonationRanking( + @Path("id") id: Long, + @Query("page") page: Int, + @Query("size") size: Int, + @Header("Authorization") authHeader: String + ): Single> + @POST("/explorer/profile/cheers") fun writeCheers( @Body request: PostWriteCheersRequest, diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerRepository.kt index c461412..f0bece8 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerRepository.kt @@ -1,8 +1,11 @@ package kr.co.vividnext.sodalive.explorer +import io.reactivex.rxjava3.core.Single +import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest import kr.co.vividnext.sodalive.explorer.profile.cheers.PostWriteCheersRequest import kr.co.vividnext.sodalive.explorer.profile.cheers.PutModifyCheersRequest +import kr.co.vividnext.sodalive.explorer.profile.donation.GetDonationAllResponse import java.util.TimeZone class ExplorerRepository( @@ -63,4 +66,18 @@ class ExplorerRepository( size = size, authHeader = token ) + + fun getCreatorProfileDonationRanking( + id: Long, + page: Int, + size: Int, + token: String + ): Single> { + return api.getCreatorProfileDonationRanking( + id = id, + page = page - 1, + size = size, + authHeader = token + ) + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/GetDonationAllResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/GetDonationAllResponse.kt new file mode 100644 index 0000000..5cac362 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/GetDonationAllResponse.kt @@ -0,0 +1,17 @@ +package kr.co.vividnext.sodalive.explorer.profile.donation + +import com.google.gson.annotations.SerializedName +import kr.co.vividnext.sodalive.explorer.profile.UserDonationRankingResponse + +data class GetDonationAllResponse( + @SerializedName("accumulatedCansToday") + val accumulatedCansToday: Int, + @SerializedName("accumulatedCansLastWeek") + val accumulatedCansLastWeek: Int, + @SerializedName("accumulatedCansThisMonth") + val accumulatedCansThisMonth: Int, + @SerializedName("totalCount") + val totalCount: Int, + @SerializedName("userDonationRanking") + val userDonationRanking: List, +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllAdapter.kt new file mode 100644 index 0000000..eb468d0 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllAdapter.kt @@ -0,0 +1,154 @@ +package kr.co.vividnext.sodalive.explorer.profile.donation + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.RelativeLayout +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.CircleCropTransformation +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.databinding.ItemUserProfileDonationAllBinding +import kr.co.vividnext.sodalive.explorer.profile.UserDonationRankingResponse +import kr.co.vividnext.sodalive.extensions.dpToPx +import kr.co.vividnext.sodalive.extensions.moneyFormat + +class UserProfileDonationAllAdapter(private val userId: Long) : + RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val context: Context, + private val binding: ItemUserProfileDonationAllBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("SetTextI18n") + fun bind(item: UserDonationRankingResponse, position: Int) { + binding.tvRank.text = "${position + 1}" + binding.tvNickname.text = item.nickname + + binding.ivProfile.load(item.profileImage) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(CircleCropTransformation()) + } + + if (item.donationCan > 0 && SharedPreferenceManager.userId == userId) { + binding.tvTotalDonationCan.visibility = View.VISIBLE + binding.tvTotalDonationCan.text = "${item.donationCan.moneyFormat()} 코인" + } + + val lp = binding.rlDonationRanking.layoutParams as RelativeLayout.LayoutParams + + when (position) { + 0 -> { + binding.ivBg.setImageResource(R.drawable.bg_circle_ffdc00_ffb600) + binding.ivBg.visibility = View.VISIBLE + + binding.ivCrown.setImageResource(R.drawable.ic_crown_1) + binding.ivCrown.visibility = View.VISIBLE + binding.rlDonationRankingRoot.setBackgroundResource( + if (items.size == 1) { + R.drawable.bg_round_corner_4_7_2b2635 + } else { + R.drawable.bg_top_round_corner_4_7_2b2635 + } + ) + + lp.setMargins( + 20.dpToPx().toInt(), + 20.dpToPx().toInt(), + 20.dpToPx().toInt(), + if (items.size == 1) { + 20.dpToPx().toInt() + } else { + 13.3f.dpToPx().toInt() + } + ) + binding.rlDonationRanking.layoutParams = lp + } + + 1 -> { + binding.ivBg.setImageResource(R.drawable.bg_circle_ffffff_9f9f9f) + binding.ivBg.visibility = View.VISIBLE + + binding.ivCrown.setImageResource(R.drawable.ic_crown_2) + binding.ivCrown.visibility = View.VISIBLE + + if (items.size == 2) { + binding.rlDonationRankingRoot.setBackgroundResource( + R.drawable.bg_bottom_round_corner_4_7_2b2635 + ) + } else { + binding.rlDonationRankingRoot.setBackgroundColor( + ContextCompat.getColor(context, R.color.color_2b2635) + ) + } + + lp.setMargins( + 20.dpToPx().toInt(), + 0, + 20.dpToPx().toInt(), + if (items.size == 2) { + 20.dpToPx().toInt() + } else { + 13.3f.dpToPx().toInt() + } + ) + binding.rlDonationRanking.layoutParams = lp + } + + 2 -> { + binding.ivBg.setImageResource(R.drawable.bg_circle_e6a77a_c67e4a) + binding.ivBg.visibility = View.VISIBLE + + binding.ivCrown.setImageResource(R.drawable.ic_crown_3) + binding.ivCrown.visibility = View.VISIBLE + binding.rlDonationRankingRoot.setBackgroundResource( + R.drawable.bg_bottom_round_corner_4_7_2b2635 + ) + + lp.setMargins( + 20.dpToPx().toInt(), + 0, + 20.dpToPx().toInt(), + 20.dpToPx().toInt() + ) + binding.rlDonationRanking.layoutParams = lp + } + + else -> { + binding.ivBg.setImageResource(0) + binding.ivBg.visibility = View.GONE + binding.ivCrown.visibility = View.GONE + binding.rlDonationRanking.setBackgroundResource(0) + binding.rlDonationRanking.background = null + binding.rlDonationRankingRoot.setBackgroundResource(0) + binding.rlDonationRankingRoot.background = null + + lp.setMargins(0, 0, 0, 0) + binding.rlDonationRanking.layoutParams = lp + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemUserProfileDonationAllBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position], position) + } + + override fun getItemCount() = items.count() +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.kt index ee23b15..0d5bf3a 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.kt @@ -1,10 +1,149 @@ package kr.co.vividnext.sodalive.explorer.profile.donation +import android.annotation.SuppressLint +import android.graphics.Rect +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.Constants +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.databinding.ActivityUserProfileLiveAllBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import kr.co.vividnext.sodalive.extensions.moneyFormat +import org.koin.android.ext.android.inject class UserProfileDonationAllViewActivity : BaseActivity( ActivityUserProfileLiveAllBinding::inflate ) { - override fun setupView() {} + + private val viewModel: UserProfileDonationAllViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var adapter: UserProfileDonationAllAdapter + + private var userId: Long = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + userId = intent.getLongExtra(Constants.EXTRA_USER_ID, 0) + super.onCreate(savedInstanceState) + + if (userId > 0) { + bindData() + + viewModel.getCreatorProfileDonationRanking(userId) + } else { + Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show() + finish() + } + } + + override fun setupView() { + loadingDialog = LoadingDialog(this, layoutInflater) + binding.toolbar.tvBack.text = "후원랭킹 전체보기" + binding.toolbar.tvBack.setOnClickListener { finish() } + + setupDonationRankingView() + } + + private fun setupDonationRankingView() { + val recyclerView = binding.rvLiveAll + adapter = UserProfileDonationAllAdapter(userId = userId) + + recyclerView.layoutManager = LinearLayoutManager( + applicationContext, + LinearLayoutManager.VERTICAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + outRect.left = 20.dpToPx().toInt() + outRect.right = 20.dpToPx().toInt() + + when (parent.getChildAdapterPosition(view)) { + 0, 1, 2 -> { + outRect.top = 0 + outRect.bottom = 0 + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + } + + 3 -> { + outRect.top = 20.dpToPx().toInt() + outRect.bottom = 6.7f.dpToPx().toInt() + } + + else -> { + outRect.top = 6.7f.dpToPx().toInt() + outRect.bottom = 6.7f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = adapter + + recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val lastVisiblePosition = (recyclerView.layoutManager as LinearLayoutManager) + .findLastVisibleItemPosition() + val itemTotalCount = adapter.itemCount - 1 + + if (itemTotalCount > 0 && lastVisiblePosition == itemTotalCount) { + viewModel.getCreatorProfileDonationRanking(userId) + } + } + }) + + binding.swipeRefreshLayout.setOnRefreshListener { + adapter.items.clear() + viewModel.refresh(userId) + + binding.swipeRefreshLayout.isRefreshing = false + } + + if (SharedPreferenceManager.userId == userId) { + binding.llTotal.visibility = View.VISIBLE + } else { + binding.llTotal.visibility = View.GONE + } + } + + @SuppressLint("NotifyDataSetChanged") + private fun bindData() { + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth, "") + } else { + loadingDialog.dismiss() + } + } + + viewModel.donationLiveData.observe(this) { + binding.tvCanToday.text = it.accumulatedCansToday.moneyFormat() + binding.tvCanLastWeek.text = it.accumulatedCansLastWeek.moneyFormat() + binding.tvCanThisMonth.text = it.accumulatedCansThisMonth.moneyFormat() + + binding.tvTotalCount.text = "${it.totalCount}" + adapter.items.addAll(it.userDonationRanking) + adapter.notifyDataSetChanged() + } + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewModel.kt new file mode 100644 index 0000000..ab7f41d --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewModel.kt @@ -0,0 +1,81 @@ +package kr.co.vividnext.sodalive.explorer.profile.donation + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.explorer.ExplorerRepository + +class UserProfileDonationAllViewModel( + private val repository: ExplorerRepository +) : BaseViewModel() { + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _donationLiveData = MutableLiveData() + val donationLiveData: LiveData + get() = _donationLiveData + + private var isLast = false + private var page = 1 + private val size = 10 + + fun getCreatorProfileDonationRanking(userId: Long) { + if (!_isLoading.value!! && !isLast) { + _isLoading.value = true + + compositeDisposable.add( + repository.getCreatorProfileDonationRanking( + id = userId, + page = page, + size = size, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + if (it.data.userDonationRanking.isNotEmpty()) { + page += 1 + _donationLiveData.postValue(it.data!!) + } else { + isLast = true + } + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + + _isLoading.value = false + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } + + fun refresh(userId: Long) { + page = 1 + isLast = false + getCreatorProfileDonationRanking(userId) + } +}