fix(mypage): 기능 버튼 배치와 쿠폰 분기를 정리한다
This commit is contained in:
@@ -4,11 +4,15 @@ import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.URLUtil
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.net.toUri
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
@@ -20,10 +24,12 @@ import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.FunctionButtonHelper
|
||||
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
||||
import kr.co.vividnext.sodalive.databinding.FragmentMyBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemFunctionButtonBinding
|
||||
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
@@ -52,10 +58,15 @@ import org.koin.android.ext.android.inject
|
||||
@UnstableApi
|
||||
class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflate) {
|
||||
|
||||
companion object {
|
||||
private const val FUNCTION_BUTTON_SPAN_COUNT = 4
|
||||
}
|
||||
|
||||
private val viewModel: MyPageViewModel by inject()
|
||||
private val recentContentViewModel: RecentContentViewModel by inject()
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
private val functionButtonAdapter = FunctionButtonAdapter()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@@ -187,6 +198,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
binding.llProfileLoginContainer.visibility = View.GONE
|
||||
|
||||
binding.llFunctionButtonGrid.visibility = View.VISIBLE
|
||||
setupFunctionButtonGrid()
|
||||
|
||||
binding.ivSettings.setOnClickListener {
|
||||
startActivity(
|
||||
@@ -239,84 +251,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
}
|
||||
}
|
||||
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnStorage.root,
|
||||
iconRes = R.drawable.ic_my_storage,
|
||||
title = getString(R.string.screen_my_storage)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireContext(),
|
||||
AudioContentBoxActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnBlockList.root,
|
||||
iconRes = R.drawable.ic_my_block,
|
||||
title = getString(R.string.screen_my_block_list)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireContext(),
|
||||
BlockMemberActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnMorningCall.root,
|
||||
iconRes = R.drawable.ic_my_alarm,
|
||||
title = getString(R.string.screen_my_morning_call)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
AlarmListActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnNotice.root,
|
||||
iconRes = R.drawable.ic_my_notice,
|
||||
title = getString(R.string.screen_my_notice)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
NoticeActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnEvent.root,
|
||||
iconRes = R.drawable.ic_my_event,
|
||||
title = getString(R.string.screen_my_event)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
EventActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnCustomerService.root,
|
||||
iconRes = R.drawable.ic_my_service_center,
|
||||
title = getString(R.string.screen_my_customer_service)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
ServiceCenterActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (SharedPreferenceManager.role == MemberRole.CREATOR.name) {
|
||||
binding.tvMyChannel.visibility = View.VISIBLE
|
||||
binding.tvMyChannel.setOnClickListener {
|
||||
@@ -335,6 +269,8 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
} else {
|
||||
binding.tvMyChannel.visibility = View.GONE
|
||||
}
|
||||
|
||||
updateFunctionButtons()
|
||||
} else {
|
||||
binding.ivSettings.visibility = View.GONE
|
||||
binding.llFunctionButtonGrid.visibility = View.GONE
|
||||
@@ -380,29 +316,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
}
|
||||
|
||||
viewModel.myPageLiveData.observe(viewLifecycleOwner) {
|
||||
val isKoreanUser = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"
|
||||
|
||||
if (isKoreanUser) {
|
||||
binding.btnIdentityVerification.root.visibility = View.VISIBLE
|
||||
if (it.isAuth) {
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnIdentityVerification.root,
|
||||
iconRes = R.drawable.ic_my_auth,
|
||||
title = getString(R.string.screen_my_identity_verified)
|
||||
)
|
||||
} else {
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnIdentityVerification.root,
|
||||
iconRes = R.drawable.ic_my_auth,
|
||||
title = getString(R.string.screen_my_identity_verification)
|
||||
) {
|
||||
showAuthDialog()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.btnIdentityVerification.root.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
binding.ivProfile.load(it.profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.ic_place_holder)
|
||||
@@ -413,41 +326,75 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
binding.tvCanAmount.text = (it.chargeCan + it.rewardCan).moneyFormat()
|
||||
binding.tvPointAmount.text = it.point.moneyFormat()
|
||||
|
||||
updateFunctionButtons(it.isAuth)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFunctionButtonGrid() {
|
||||
binding.rvFunctionButtons.layoutManager = GridLayoutManager(
|
||||
requireContext(),
|
||||
FUNCTION_BUTTON_SPAN_COUNT
|
||||
)
|
||||
|
||||
if (binding.rvFunctionButtons.itemDecorationCount == 0) {
|
||||
binding.rvFunctionButtons.addItemDecoration(
|
||||
GridSpacingItemDecoration(
|
||||
spanCount = FUNCTION_BUTTON_SPAN_COUNT,
|
||||
spacing = 16f.dpToPx().toInt(),
|
||||
includeEdge = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.rvFunctionButtons.adapter = functionButtonAdapter
|
||||
}
|
||||
|
||||
private fun updateFunctionButtons(isAuth: Boolean? = null) {
|
||||
val isKoreanUser = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"
|
||||
val items = mutableListOf(
|
||||
FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_storage,
|
||||
title = getString(R.string.screen_my_storage)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireContext(),
|
||||
AudioContentBoxActivity::class.java
|
||||
)
|
||||
)
|
||||
},
|
||||
FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_block,
|
||||
title = getString(R.string.screen_my_block_list)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireContext(),
|
||||
BlockMemberActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
val shouldShowCouponButton = if (isKoreanUser) {
|
||||
true
|
||||
isAuth != null
|
||||
} else {
|
||||
SharedPreferenceManager.isAdultContentVisible
|
||||
}
|
||||
|
||||
binding.btnCoupon.root.visibility = if (shouldShowCouponButton) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
|
||||
if (!shouldShowCouponButton) {
|
||||
return@observe
|
||||
}
|
||||
|
||||
if (it.isAuth || !isKoreanUser) {
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnCoupon.root,
|
||||
if (shouldShowCouponButton) {
|
||||
items += FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_coupon,
|
||||
title = getString(R.string.screen_my_coupon_register)
|
||||
) {
|
||||
if ((isAuth == true) || !isKoreanUser) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
CanCouponActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.btnCoupon.root,
|
||||
iconRes = R.drawable.ic_my_coupon,
|
||||
title = getString(R.string.screen_my_coupon_register)
|
||||
) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
getString(R.string.screen_my_auth_required),
|
||||
@@ -458,6 +405,70 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items += listOf(
|
||||
FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_alarm,
|
||||
title = getString(R.string.screen_my_morning_call)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
AlarmListActivity::class.java
|
||||
)
|
||||
)
|
||||
},
|
||||
FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_notice,
|
||||
title = getString(R.string.screen_my_notice)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
NoticeActivity::class.java
|
||||
)
|
||||
)
|
||||
},
|
||||
FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_event,
|
||||
title = getString(R.string.screen_my_event)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
EventActivity::class.java
|
||||
)
|
||||
)
|
||||
},
|
||||
FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_service_center,
|
||||
title = getString(R.string.screen_my_customer_service)
|
||||
) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
ServiceCenterActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (isKoreanUser && isAuth != null) {
|
||||
items += FunctionButtonItem(
|
||||
iconRes = R.drawable.ic_my_auth,
|
||||
title = if (isAuth) {
|
||||
getString(R.string.screen_my_identity_verified)
|
||||
} else {
|
||||
getString(R.string.screen_my_identity_verification)
|
||||
}
|
||||
) {
|
||||
if (!isAuth) {
|
||||
showAuthDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
functionButtonAdapter.submitList(items)
|
||||
}
|
||||
|
||||
private fun showAuthDialog() {
|
||||
@@ -486,3 +497,47 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class FunctionButtonItem(
|
||||
@field:DrawableRes val iconRes: Int,
|
||||
val title: String,
|
||||
val onClick: () -> Unit
|
||||
)
|
||||
|
||||
private class FunctionButtonAdapter : RecyclerView.Adapter<FunctionButtonAdapter.ViewHolder>() {
|
||||
private val items = mutableListOf<FunctionButtonItem>()
|
||||
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemFunctionButtonBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(item: FunctionButtonItem) {
|
||||
FunctionButtonHelper.setupFunctionButton(
|
||||
buttonView = binding.root,
|
||||
iconRes = item.iconRes,
|
||||
title = item.title,
|
||||
clickListener = View.OnClickListener { item.onClick() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||
ItemFunctionButtonBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun submitList(newItems: List<FunctionButtonItem>) {
|
||||
items.clear()
|
||||
items.addAll(newItems)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,86 +276,13 @@
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<!-- First Row -->
|
||||
<LinearLayout
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_function_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_storage"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_block_list"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_coupon"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_morning_call"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Second Row -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_notice"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_event"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_customer_service"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_identity_verification"
|
||||
layout="@layout/item_function_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
android:nestedScrollingEnabled="false"
|
||||
tools:itemCount="8"
|
||||
tools:listitem="@layout/item_function_button" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## 작업 목표
|
||||
- 마이페이지 `btnCoupon` 터치 시 한국 사용자는 기존처럼 본인인증이 필요하고, 한국이 아닌 사용자는 본인인증 없이 쿠폰 등록 화면으로 이동하도록 수정한다.
|
||||
- 마이페이지 `btnCoupon`은 한국이 아닌 사용자에 한해 민감한 콘텐츠 보기 설정이 켜져 있을 때만 화면에 보이도록 수정한다.
|
||||
- 마이페이지 기능 버튼 영역을 `RecyclerView` 기반 그리드로 전환해 숨겨진 버튼이 있어도 중간 빈 슬롯 없이 왼쪽부터 자연스럽게 재배치되도록 수정한다.
|
||||
|
||||
## 체크리스트
|
||||
- [x] AC1: `countryCode == "KR"` 이고 `isAuth == false`인 경우 기존처럼 인증 필요 토스트와 `showAuthDialog()`가 실행된다.
|
||||
@@ -13,12 +14,16 @@
|
||||
- QA: 인증 완료 사용자의 쿠폰 등록 진입 동작 유지 코드 확인
|
||||
- [x] AC4: 변경 파일 진단/검증 명령 결과를 기록한다.
|
||||
- QA: `lsp_diagnostics`, 관련 Gradle 검증 명령 결과 기록
|
||||
- [x] AC5: `countryCode != "KR"` 이고 `SharedPreferenceManager.isAdultContentVisible == true`인 경우에만 `btnCoupon`이 화면에 보인다.
|
||||
- QA: 비한국 사용자에서 `btnCoupon.root.visibility`가 민감한 콘텐츠 보기 설정값에 따라 제어되는지 코드 확인
|
||||
- [x] AC6: `countryCode != "KR"` 이고 `SharedPreferenceManager.isAdultContentVisible == false`인 경우 `btnCoupon`이 화면에 보이지 않는다.
|
||||
- QA: 비한국 사용자에서 쿠폰 버튼이 `View.GONE` 처리되는지 코드 확인
|
||||
- [x] AC5: `countryCode != "KR"` 이고 `SharedPreferenceManager.isAdultContentVisible == true`인 경우에만 쿠폰 버튼이 기능 버튼 목록에 포함되어 화면에 보인다.
|
||||
- QA: 비한국 사용자에서 쿠폰 버튼 아이템이 민감한 콘텐츠 보기 설정값에 따라 리스트에 포함되는지 코드 확인
|
||||
- [x] AC6: `countryCode != "KR"` 이고 `SharedPreferenceManager.isAdultContentVisible == false`인 경우 쿠폰 버튼이 기능 버튼 목록에서 제외되어 화면에 보이지 않는다.
|
||||
- QA: 비한국 사용자에서 쿠폰 버튼 아이템이 리스트에 추가되지 않는지 코드 확인
|
||||
- [x] AC7: `countryCode == "KR"` 인 경우 쿠폰 버튼 노출과 기존 한국/비한국 클릭 분기는 유지된다.
|
||||
- QA: 한국 사용자에서는 버튼이 계속 보이고, 클릭 시 기존 인증 분기가 유지되는지 코드 확인
|
||||
- [x] AC8: 기능 버튼 영역이 `RecyclerView` 기반으로 렌더링되고, 숨겨진 버튼은 데이터 목록에서 제외되어 남은 버튼이 좌→우/상→하로 자연스럽게 압축 배치된다.
|
||||
- QA: `fragment_my.xml`이 `RecyclerView`를 사용하고, `MyPageFragment`가 쿠폰/본인인증 버튼을 조건에 따라 아이템 리스트에 포함/제외하는지 코드 확인
|
||||
- [x] AC9: 쿠폰 버튼이 숨겨지는 비한국 사용자(`isAdultContentVisible == false`)에서도 기능 버튼 간 가로/세로 간격이 기존 4열 그리드와 동일하게 유지된다.
|
||||
- QA: `GridLayoutManager(4)`와 `GridSpacingItemDecoration(..., 16dp, false)` 적용으로 기존 16dp 간격 패턴을 유지하는지 코드 확인
|
||||
|
||||
## 검증 기록
|
||||
- 2026-04-02
|
||||
@@ -45,3 +50,24 @@
|
||||
- 한국 사용자는 쿠폰 버튼이 계속 노출되고, 기존 인증 기반 클릭 분기도 유지된다.
|
||||
- `.kt` 파일 대상 `lsp_diagnostics`는 현재 환경에 Kotlin LSP가 없어 실행 불가(`No LSP server configured for extension: .kt`)였다.
|
||||
- `:app:testDebugUnitTest`, `:app:assembleDebug` 실행은 `BUILD SUCCESSFUL`로 완료됐다.
|
||||
- 2026-04-02
|
||||
- 무엇/왜/어떻게: 기능 버튼 영역이 고정 2행 x 4열 `include` 구조여서 비한국 사용자에게서 쿠폰 버튼이 숨겨질 때 중간 빈칸이 남았다. `fragment_my.xml`을 `RecyclerView` 기반 4열 그리드로 바꾸고, `MyPageFragment`가 기존 버튼 순서를 유지한 채 조건에 맞는 버튼만 리스트에 담아 렌더링하도록 리팩터링했다.
|
||||
- 실행 명령/도구:
|
||||
- `apply_patch(app/src/main/res/layout/fragment_my.xml)`
|
||||
- `apply_patch(app/src/main/res/layout/item_function_button.xml)`
|
||||
- `apply_patch(app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt)`
|
||||
- `apply_patch(docs/20260402_쿠폰등록해외사용자본인인증예외.md)`
|
||||
- `read(app/src/main/res/layout/fragment_my.xml)`
|
||||
- `read(app/src/main/res/layout/item_function_button.xml)`
|
||||
- `read(app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt)`
|
||||
- `lsp_diagnostics(app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt)`
|
||||
- `lsp_diagnostics(app/src/main/res/layout/fragment_my.xml)`
|
||||
- `lsp_diagnostics(app/src/main/res/layout/item_function_button.xml)`
|
||||
- `./gradlew :app:testDebugUnitTest :app:assembleDebug`
|
||||
- 결과:
|
||||
- 쿠폰 버튼과 본인인증 버튼은 더 이상 고정 슬롯의 `visibility`로 숨기지 않고, 조건에 맞을 때만 `RecyclerView` 데이터에 포함된다.
|
||||
- 비한국 사용자이면서 `SharedPreferenceManager.isAdultContentVisible == false`인 경우 쿠폰 버튼이 목록에서 빠져 나머지 기능 버튼이 좌측부터 자연스럽게 압축 배치된다.
|
||||
- 가로/세로 간격은 `GridLayoutManager(4)` + `GridSpacingItemDecoration(..., 16dp, false)`로 기존 4열 레이아웃의 16dp 간격 패턴을 유지한다.
|
||||
- 기존 버튼 제목, 아이콘, 클릭 액션, 한국/비한국 및 인증/민감 콘텐츠 조건은 그대로 유지됐다.
|
||||
- `.kt`/`.xml` 대상 `lsp_diagnostics`는 현재 환경에 Kotlin/XML LSP가 없어 실행 불가(`No LSP server configured for extension: .kt/.xml`)였다.
|
||||
- `:app:testDebugUnitTest`, `:app:assembleDebug` 실행은 `BUILD SUCCESSFUL`로 완료됐다.
|
||||
|
||||
Reference in New Issue
Block a user