From 0ebb34a2df122fe3c978b6958d511e3d0ae77e7c Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 4 Sep 2024 12:31:32 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B0=A8=EB=8B=A8=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 1 + .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 2 + .../sodalive/mypage/MyPageFragment.kt | 5 + .../mypage/block/BlockMemberActivity.kt | 108 ++++++++++++++++++ .../mypage/block/BlockMemberAdapter.kt | 95 +++++++++++++++ .../mypage/block/BlockMemberViewModel.kt | 104 +++++++++++++++++ .../block/GetBlockedMemberListResponse.kt | 18 +++ .../kr/co/vividnext/sodalive/user/UserApi.kt | 8 ++ .../vividnext/sodalive/user/UserRepository.kt | 8 +- .../bg_round_corner_13_3_333bb9f1_3bb9f1.xml | 8 ++ ...g_round_corner_13_3_transparent_3bb9f1.xml | 8 ++ .../main/res/layout/activity_block_member.xml | 74 ++++++++++++ app/src/main/res/layout/fragment_my.xml | 13 +++ .../res/layout/item_blocked_member_list.xml | 46 ++++++++ app/src/main/res/values/colors.xml | 1 + 15 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/block/GetBlockedMemberListResponse.kt create mode 100644 app/src/main/res/drawable/bg_round_corner_13_3_333bb9f1_3bb9f1.xml create mode 100644 app/src/main/res/drawable/bg_round_corner_13_3_transparent_3bb9f1.xml create mode 100644 app/src/main/res/layout/activity_block_member.xml create mode 100644 app/src/main/res/layout/item_blocked_member_list.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7b972ef..f56c48c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -150,6 +150,7 @@ + (FragmentMyBinding::inflat startActivity(Intent(requireContext(), FollowingCreatorActivity::class.java)) } + binding.tvBlockMemberList.setOnClickListener { + startActivity(Intent(requireContext(), BlockMemberActivity::class.java)) + } + val ivHowToUseLp = binding.ivHowToUse.layoutParams as LinearLayout.LayoutParams ivHowToUseLp.width = screenWidth ivHowToUseLp.height = (200 * screenWidth) / 1080 diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberActivity.kt new file mode 100644 index 0000000..b0009cf --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberActivity.kt @@ -0,0 +1,108 @@ +package kr.co.vividnext.sodalive.mypage.block + +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.LoadingDialog +import kr.co.vividnext.sodalive.databinding.ActivityBlockMemberBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject + +class BlockMemberActivity : BaseActivity( + ActivityBlockMemberBinding::inflate +) { + private val viewModel: BlockMemberViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var adapter: BlockMemberAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + bindData() + viewModel.getBlockedMemberList() + } + + override fun setupView() { + loadingDialog = LoadingDialog(this, layoutInflater) + binding.toolbar.tvBack.text = "차단 리스트" + binding.toolbar.tvBack.setOnClickListener { finish() } + + adapter = BlockMemberAdapter( + blockMember = { viewModel.blockMember(it) }, + unBlockMember = { viewModel.unBlockMember(it) } + ) + + binding.rvBlockMember.layoutManager = LinearLayoutManager( + applicationContext, + LinearLayoutManager.VERTICAL, + false + ) + + binding.rvBlockMember.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val layoutManager = recyclerView.layoutManager as? LinearLayoutManager + if ( + layoutManager != null && + layoutManager.findLastCompletelyVisibleItemPosition() == adapter.itemCount - 1 + ) { + viewModel.getBlockedMemberList() + } + } + }) + + binding.rvBlockMember.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + } + }) + + binding.rvBlockMember.adapter = adapter + } + + @SuppressLint("SetTextI18n") + 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.blockedMemberListLiveData.observe(this) { + adapter.addAll(it) + } + + viewModel.blockedMemberTotalCountLiveData.observe(this) { + binding.tvTotalCount.text = " $it " + + if (it > 0) { + binding.tvNone.visibility = View.GONE + binding.rvBlockMember.visibility = View.VISIBLE + } else { + binding.tvNone.visibility = View.VISIBLE + binding.rvBlockMember.visibility = View.GONE + } + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberAdapter.kt new file mode 100644 index 0000000..a97e88a --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberAdapter.kt @@ -0,0 +1,95 @@ +package kr.co.vividnext.sodalive.mypage.block + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +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.databinding.ItemBlockedMemberListBinding + +class BlockMemberAdapter( + private val blockMember: (Long) -> Unit, + private val unBlockMember: (Long) -> Unit +) : RecyclerView.Adapter() { + + private val items = mutableListOf() + + inner class ViewHolder( + private val context: Context, + private val binding: ItemBlockedMemberListBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("NotifyDataSetChanged") + fun bind(item: GetBlockedMemberListItem) { + binding.tvNickname.text = item.nickname + binding.ivProfile.load(item.profileImageUrl) { + transformations(CircleCropTransformation()) + placeholder(R.drawable.bg_placeholder) + crossfade(true) + } + + binding.tvBlock.visibility = View.VISIBLE + if (item.isBlocked) { + binding.tvBlock.text = "차단해제" + binding.tvBlock.background = ContextCompat.getDrawable( + context, + R.drawable.bg_round_corner_13_3_333bb9f1_3bb9f1 + ) + binding.tvBlock.setOnClickListener { + val index = items.indexOf(item) + val copyItem = item.copy(isBlocked = false) + items.add(index, copyItem) + items.removeAt(index + 1) + notifyDataSetChanged() + unBlockMember(item.memberId) + } + } else { + binding.tvBlock.text = "차단" + binding.tvBlock.background = ContextCompat.getDrawable( + context, + R.drawable.bg_round_corner_13_3_transparent_3bb9f1 + ) + binding.tvBlock.setOnClickListener { + val index = items.indexOf(item) + val copyItem = item.copy(isBlocked = true) + items.add(index, copyItem) + items.removeAt(index + 1) + notifyDataSetChanged() + blockMember(item.memberId) + } + } + } + } + + @SuppressLint("NotifyDataSetChanged") + fun addAll(items: List) { + this.items.addAll(items) + notifyDataSetChanged() + } + + fun clear() { + this.items.clear() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemBlockedMemberListBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder( + holder: ViewHolder, + position: Int + ) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.size +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberViewModel.kt new file mode 100644 index 0000000..177c69b --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/BlockMemberViewModel.kt @@ -0,0 +1,104 @@ +package kr.co.vividnext.sodalive.mypage.block + +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.user.UserRepository + +class BlockMemberViewModel(private val userRepository: UserRepository) : BaseViewModel() { + private val _blockedMemberListLiveData = MutableLiveData>() + val blockedMemberListLiveData: LiveData> + get() = _blockedMemberListLiveData + + private val _blockedMemberTotalCountLiveData = MutableLiveData(0) + val blockedMemberTotalCountLiveData: LiveData + get() = _blockedMemberTotalCountLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + var page = 1 + var isLast = false + private val pageSize = 10 + + fun getBlockedMemberList() { + if (!isLast && !_isLoading.value!!) { + _isLoading.value = true + + compositeDisposable.add( + userRepository.getBlockedMemberList( + page = page, + size = pageSize, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success && it.data != null) { + val data = it.data + if (data.items.isEmpty()) { + isLast = true + } else { + page += 1 + _blockedMemberTotalCountLiveData.value = data.totalCount + _blockedMemberListLiveData.value = data.items + } + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + ) + ) + } + } + + fun blockMember(blockMemberId: Long) { + _blockedMemberTotalCountLiveData.value = _blockedMemberTotalCountLiveData.value!! + 1 + compositeDisposable.add( + userRepository.memberBlock( + blockMemberId, + "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({}, {}) + ) + } + + fun unBlockMember(blockMemberId: Long) { + _blockedMemberTotalCountLiveData.value = _blockedMemberTotalCountLiveData.value!! - 1 + compositeDisposable.add( + userRepository.memberUnBlock( + blockMemberId, + "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({}, {}) + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/GetBlockedMemberListResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/GetBlockedMemberListResponse.kt new file mode 100644 index 0000000..8e27b2d --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/block/GetBlockedMemberListResponse.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.mypage.block + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class GetBlockedMemberListResponse( + @SerializedName("totalCount") val totalCount: Int, + @SerializedName("items") val items: List +) + +@Keep +data class GetBlockedMemberListItem( + @SerializedName("memberId") val memberId: Long, + @SerializedName("nickname") val nickname: String, + @SerializedName("profileImageUrl") val profileImageUrl: String, + @SerializedName("isBlocked") val isBlocked: Boolean +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt index bdb989d..30ff7ac 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt @@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser import kr.co.vividnext.sodalive.main.GaidUpdateRequest import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest import kr.co.vividnext.sodalive.mypage.MyPageResponse +import kr.co.vividnext.sodalive.mypage.block.GetBlockedMemberListResponse import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateRequest import kr.co.vividnext.sodalive.mypage.profile.nickname.GetChangeNicknamePriceResponse @@ -144,4 +145,11 @@ interface UserApi { @Body request: GaidUpdateRequest, @Header("Authorization") authHeader: String ): Single> + + @GET("/member/block") + fun getBlockedMemberList( + @Query("page") page: Int, + @Query("size") size: Int, + @Header("Authorization") authHeader: String + ): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt index 5e2d028..e2c6898 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt @@ -10,9 +10,9 @@ import kr.co.vividnext.sodalive.mypage.MyPageResponse import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateRequest import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest +import kr.co.vividnext.sodalive.settings.signout.SignOutRequest import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest import kr.co.vividnext.sodalive.user.login.LoginRequest -import kr.co.vividnext.sodalive.settings.signout.SignOutRequest import okhttp3.MultipartBody import okhttp3.RequestBody @@ -116,4 +116,10 @@ class UserRepository(private val userApi: UserApi) { ): Single> { return userApi.updateGaid(request, authHeader = token) } + + fun getBlockedMemberList(page: Int, size: Int, token: String) = userApi.getBlockedMemberList( + page = page - 1, + size = size, + authHeader = token + ) } diff --git a/app/src/main/res/drawable/bg_round_corner_13_3_333bb9f1_3bb9f1.xml b/app/src/main/res/drawable/bg_round_corner_13_3_333bb9f1_3bb9f1.xml new file mode 100644 index 0000000..514a372 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_13_3_333bb9f1_3bb9f1.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/bg_round_corner_13_3_transparent_3bb9f1.xml b/app/src/main/res/drawable/bg_round_corner_13_3_transparent_3bb9f1.xml new file mode 100644 index 0000000..3d601c8 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_13_3_transparent_3bb9f1.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_block_member.xml b/app/src/main/res/layout/activity_block_member.xml new file mode 100644 index 0000000..1d358ec --- /dev/null +++ b/app/src/main/res/layout/activity_block_member.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_my.xml b/app/src/main/res/layout/fragment_my.xml index 3eb5806..046bc7f 100644 --- a/app/src/main/res/layout/fragment_my.xml +++ b/app/src/main/res/layout/fragment_my.xml @@ -176,6 +176,19 @@ android:text="팔로잉 리스트" android:textColor="@color/color_3bb9f1" android:textSize="15.3sp" /> + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 383cbb1..67f2ffe 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -123,4 +123,5 @@ #312827 #F1291C #FF14D9 + #333BB9F1