-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 팝업창 구현 #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
The head ref may contain hidden characters: "\uC628\uBCF4\uB529-\uD31D\uC5C5\uCC3D"
feat: 팝업창 구현 #31
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,14 +1,12 @@ | ||||||||||||||||||||||||||||||||||||||
| package umc.onairmate.ui.friend.list | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| import android.content.Context | ||||||||||||||||||||||||||||||||||||||
| import android.content.Intent | ||||||||||||||||||||||||||||||||||||||
| import androidx.fragment.app.Fragment | ||||||||||||||||||||||||||||||||||||||
| import android.os.Bundle | ||||||||||||||||||||||||||||||||||||||
| import android.util.Log | ||||||||||||||||||||||||||||||||||||||
| import android.view.LayoutInflater | ||||||||||||||||||||||||||||||||||||||
| import android.view.View | ||||||||||||||||||||||||||||||||||||||
| import android.view.ViewGroup | ||||||||||||||||||||||||||||||||||||||
| import android.widget.Toast | ||||||||||||||||||||||||||||||||||||||
| import androidx.fragment.app.Fragment | ||||||||||||||||||||||||||||||||||||||
| import androidx.fragment.app.viewModels | ||||||||||||||||||||||||||||||||||||||
| import androidx.lifecycle.Observer | ||||||||||||||||||||||||||||||||||||||
| import androidx.recyclerview.widget.LinearLayoutManager | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -18,26 +16,24 @@ import umc.onairmate.data.model.entity.RequestedFriendData | |||||||||||||||||||||||||||||||||||||
| import umc.onairmate.data.model.entity.UserData | ||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.databinding.FragmentFriendListTabBinding | ||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.ui.friend.FriendViewModel | ||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.ui.friend.chat.FriendChatActivity | ||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.ui.friend.chat.FriendChatViewModel | ||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.ui.pop_up.PopupClick | ||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.ui.pop_up.TwoButtonPopup | ||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.ui.util.SharedPrefUtil | ||||||||||||||||||||||||||||||||||||||
| import kotlin.getValue | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @AndroidEntryPoint | ||||||||||||||||||||||||||||||||||||||
| class FriendListTabFragment() : Fragment() { | ||||||||||||||||||||||||||||||||||||||
| private val TAG = this.javaClass.simpleName | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private var _binding: FragmentFriendListTabBinding? = null | ||||||||||||||||||||||||||||||||||||||
| private val binding get() = _binding!! | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private lateinit var adapter : FriendListRVAdapter | ||||||||||||||||||||||||||||||||||||||
| private lateinit var adapter: FriendListRVAdapter | ||||||||||||||||||||||||||||||||||||||
| private val viewModel: FriendViewModel by viewModels() | ||||||||||||||||||||||||||||||||||||||
| private val friendChatViewModel: FriendChatViewModel by viewModels() | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private var user : UserData = UserData() | ||||||||||||||||||||||||||||||||||||||
| private var friendId : Int = 0 | ||||||||||||||||||||||||||||||||||||||
| private var type : Int = 0 | ||||||||||||||||||||||||||||||||||||||
| private var user: UserData = UserData() | ||||||||||||||||||||||||||||||||||||||
| private var friendId: Int = 0 | ||||||||||||||||||||||||||||||||||||||
| private var type: Int = 0 | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| companion object { | ||||||||||||||||||||||||||||||||||||||
| private const val ARG_POSITION = "arg_position" | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -52,7 +48,6 @@ class FriendListTabFragment() : Fragment() { | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const val LIST_TYPE = 0 | ||||||||||||||||||||||||||||||||||||||
| const val REQUEST_TYPE = 1 | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| override fun onCreateView( | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -61,13 +56,13 @@ class FriendListTabFragment() : Fragment() { | |||||||||||||||||||||||||||||||||||||
| savedInstanceState: Bundle? | ||||||||||||||||||||||||||||||||||||||
| ): View { | ||||||||||||||||||||||||||||||||||||||
| _binding = FragmentFriendListTabBinding.inflate(inflater, container, false) | ||||||||||||||||||||||||||||||||||||||
| val root: View = binding.root | ||||||||||||||||||||||||||||||||||||||
| type = arguments?.getInt(ARG_POSITION) ?: 0 | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| setAdapter() | ||||||||||||||||||||||||||||||||||||||
| setObservers() | ||||||||||||||||||||||||||||||||||||||
| setBindings() | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return root | ||||||||||||||||||||||||||||||||||||||
| return binding.root | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| override fun onResume() { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -80,92 +75,95 @@ class FriendListTabFragment() : Fragment() { | |||||||||||||||||||||||||||||||||||||
| _binding = null | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private fun initData(){ | ||||||||||||||||||||||||||||||||||||||
| private fun initData() { | ||||||||||||||||||||||||||||||||||||||
| if (type == LIST_TYPE) viewModel.getFriendList() | ||||||||||||||||||||||||||||||||||||||
| if (type == REQUEST_TYPE) viewModel.getRequestedFriendList() | ||||||||||||||||||||||||||||||||||||||
| user = SharedPrefUtil.getData("user_info")?: UserData() | ||||||||||||||||||||||||||||||||||||||
| user = SharedPrefUtil.getData("user_info") ?: UserData() | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private fun setObservers() { | ||||||||||||||||||||||||||||||||||||||
| viewModel.friendList.observe(viewLifecycleOwner, Observer { list -> | ||||||||||||||||||||||||||||||||||||||
| if (list == null) return@Observer | ||||||||||||||||||||||||||||||||||||||
| binding.tvEmptyMessage.text = "아직 친구가 없습니다.\n함께할 친구를 초대해보세요!" | ||||||||||||||||||||||||||||||||||||||
| binding.layoutEmpty.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE | ||||||||||||||||||||||||||||||||||||||
| adapter.initFriendList(list) | ||||||||||||||||||||||||||||||||||||||
| binding.layoutEmpty.visibility = if (list.isNullOrEmpty()) View.VISIBLE else View.GONE | ||||||||||||||||||||||||||||||||||||||
| adapter.initFriendList(list ?: emptyList()) | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| viewModel.requestedFriendList.observe(viewLifecycleOwner, Observer { list -> | ||||||||||||||||||||||||||||||||||||||
| if (list == null) return@Observer | ||||||||||||||||||||||||||||||||||||||
| binding.tvEmptyMessage.text = "받은 친구 요청이 아직 없어요." | ||||||||||||||||||||||||||||||||||||||
| binding.layoutEmpty.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE | ||||||||||||||||||||||||||||||||||||||
| adapter.initRequestList(list) | ||||||||||||||||||||||||||||||||||||||
| binding.layoutEmpty.visibility = if (list.isNullOrEmpty()) View.VISIBLE else View.GONE | ||||||||||||||||||||||||||||||||||||||
| adapter.initRequestList(list ?: emptyList()) | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| viewModel.result.observe(viewLifecycleOwner, Observer { message -> | ||||||||||||||||||||||||||||||||||||||
| if (message == null) return@Observer | ||||||||||||||||||||||||||||||||||||||
| if (message == "친구가 삭제되었습니다.") friendChatViewModel.deleteFriend(friendId,user.userId) | ||||||||||||||||||||||||||||||||||||||
| message ?: return@Observer | ||||||||||||||||||||||||||||||||||||||
| if (message == "친구가 삭제되었습니다.") friendChatViewModel.deleteFriend(friendId, user.userId) | ||||||||||||||||||||||||||||||||||||||
| initData() | ||||||||||||||||||||||||||||||||||||||
| Toast.makeText(requireContext(),message, Toast.LENGTH_SHORT).show() | ||||||||||||||||||||||||||||||||||||||
| Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() | ||||||||||||||||||||||||||||||||||||||
| viewModel.clearResult() | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private fun setAdapter(){ | ||||||||||||||||||||||||||||||||||||||
| private fun setAdapter() { | ||||||||||||||||||||||||||||||||||||||
| adapter = FriendListRVAdapter(requireContext()) | ||||||||||||||||||||||||||||||||||||||
| adapter.setItemClickListener(object: FriendItemClickListener{ | ||||||||||||||||||||||||||||||||||||||
| adapter.setItemClickListener(object : FriendItemClickListener { | ||||||||||||||||||||||||||||||||||||||
| override fun clickMessage(data: FriendData) { | ||||||||||||||||||||||||||||||||||||||
| val bundle = Bundle().apply { | ||||||||||||||||||||||||||||||||||||||
| putParcelable("friendData", data) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| val bundle = Bundle().apply { putParcelable("friendData", data) } | ||||||||||||||||||||||||||||||||||||||
| parentFragmentManager.setFragmentResult("open_friend_chat_activity", bundle) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| override fun acceptRequest(data: RequestedFriendData) { | ||||||||||||||||||||||||||||||||||||||
| val text = data.nickname+"님의 친구요청을 수락하시겠습니까?" | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf(text,"수락","거절") | ||||||||||||||||||||||||||||||||||||||
| showPopup( | ||||||||||||||||||||||||||||||||||||||
| text =textList, | ||||||||||||||||||||||||||||||||||||||
| left = { viewModel.acceptFriend(data.userId, "REJECT") }, | ||||||||||||||||||||||||||||||||||||||
| right = { viewModel.acceptFriend(data.userId, "ACCEPT")} ) | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf("${data.nickname}님의 친구요청을 수락하시겠습니까?", "수락", "거절") | ||||||||||||||||||||||||||||||||||||||
| showPopup(textList, | ||||||||||||||||||||||||||||||||||||||
| left = { viewModel.acceptFriend(data.userId, "REJECT") }, | ||||||||||||||||||||||||||||||||||||||
| right = { viewModel.acceptFriend(data.userId, "ACCEPT") }) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
114
to
119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수락/거절 라벨과 동작이 반대로 매핑된 것으로 보입니다
라벨과 동작을 맞추도록 하나를 정정하세요. 예시(라벨 순서를 “거절”, “수락”으로 교체): - val textList = listOf("${data.nickname}님의 친구요청을 수락하시겠습니까?", "수락", "거절")
+ val textList = listOf("${data.nickname}님의 친구요청을 수락하시겠습니까?", "거절", "수락")
showPopup(textList,
- left = { viewModel.acceptFriend(data.userId, "REJECT") },
- right = { viewModel.acceptFriend(data.userId, "ACCEPT") })
+ left = { viewModel.acceptFriend(data.userId, "REJECT") },
+ right = { viewModel.acceptFriend(data.userId, "ACCEPT") })또는 라벨은 유지하고 left/right 동작을 서로 바꾸는 방식으로 수정해도 됩니다. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| override fun clickCollection(data: FriendData) { | ||||||||||||||||||||||||||||||||||||||
| // 인탠트 필요 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| override fun clickCollection(data: FriendData) {} | ||||||||||||||||||||||||||||||||||||||
| override fun clickDelete(data: FriendData) { | ||||||||||||||||||||||||||||||||||||||
| val text = data.nickname+"님을 친구 목록에서 삭제하시겠습니까?" | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf(text,"예","아니오") | ||||||||||||||||||||||||||||||||||||||
| friendId = data.userId | ||||||||||||||||||||||||||||||||||||||
| showPopup(text =textList, left = { | ||||||||||||||||||||||||||||||||||||||
| viewModel.deleteFriend(data.userId) }, right = {} ) | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf("${data.nickname}님을 친구 목록에서 삭제하시겠습니까?", "예", "아니오") | ||||||||||||||||||||||||||||||||||||||
| showPopup(textList, left = { viewModel.deleteFriend(data.userId) }, right = {}) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| override fun clickBlock(data: FriendData) { | ||||||||||||||||||||||||||||||||||||||
| val text = data.nickname+"님을 차단하시겠습니까?" | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf(text,"예","아니오") | ||||||||||||||||||||||||||||||||||||||
| showPopup(text =textList, left = { }, right = {} ) | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf("${data.nickname}님을 차단하시겠습니까?", "예", "아니오") | ||||||||||||||||||||||||||||||||||||||
| showPopup(textList, left = { /* 차단 로직 */ }, right = { /* 취소 */ }) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| override fun clickReport(data: FriendData) { | ||||||||||||||||||||||||||||||||||||||
| val text = data.nickname+"님을 신고하시겠습니까?" | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf(text,"예","아니오") | ||||||||||||||||||||||||||||||||||||||
| showPopup(text =textList, left = { }, right = {} ) | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf("${data.nickname}님을 신고하시겠습니까?", "예", "아니오") | ||||||||||||||||||||||||||||||||||||||
| showPopup(textList, left = { /* 신고 로직 */ }, right = { /* 취소 */ }) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| binding.rvFriendList.adapter = adapter | ||||||||||||||||||||||||||||||||||||||
| binding.rvFriendList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private fun showPopup(text : List<String>, right : ()-> Unit?, left: () -> Unit?) { | ||||||||||||||||||||||||||||||||||||||
| val dialog = TwoButtonPopup(text,object : PopupClick{ | ||||||||||||||||||||||||||||||||||||||
| private fun showPopup(text: List<String>, right: () -> Unit?, left: () -> Unit?) { | ||||||||||||||||||||||||||||||||||||||
| val dialog = TwoButtonPopup(text, object : PopupClick { | ||||||||||||||||||||||||||||||||||||||
| override fun rightClickFunction() { right() } | ||||||||||||||||||||||||||||||||||||||
| override fun leftClickFunction() { | ||||||||||||||||||||||||||||||||||||||
| left() | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }, false) // 뒤로 가기 막고 싶으면 false 넣어주세요, 아니면 생략가능합니다. | ||||||||||||||||||||||||||||||||||||||
| override fun leftClickFunction() { left() } | ||||||||||||||||||||||||||||||||||||||
| }, false) | ||||||||||||||||||||||||||||||||||||||
| dialog.show(activity?.supportFragmentManager!!, "FriendPopup") | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+142
to
148
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NPE 위험과 불필요한 nullable 람다 — childFragmentManager 사용 및 타입 정교화
- private fun showPopup(text: List<String>, right: () -> Unit?, left: () -> Unit?) {
+ private fun showPopup(text: List<String>, right: () -> Unit, left: () -> Unit) {
val dialog = TwoButtonPopup(text, object : PopupClick {
override fun rightClickFunction() { right() }
override fun leftClickFunction() { left() }
}, false)
- dialog.show(activity?.supportFragmentManager!!, "FriendPopup")
+ dialog.show(childFragmentManager, "FriendPopup")
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // btn_block 클릭 시 popup 생성 | ||||||||||||||||||||||||||||||||||||||
| private fun setBindings() { | ||||||||||||||||||||||||||||||||||||||
| // 빈 화면 버튼이 보일 때만 작동 | ||||||||||||||||||||||||||||||||||||||
| binding.btnBlock.setOnClickListener { | ||||||||||||||||||||||||||||||||||||||
| val nickname = "친구" // 빈 화면 상태용 닉네임 | ||||||||||||||||||||||||||||||||||||||
| val textList = listOf("$nickname 님을 차단하시겠습니까?", "예", "아니오") | ||||||||||||||||||||||||||||||||||||||
| showPopup(textList, | ||||||||||||||||||||||||||||||||||||||
| left = { | ||||||||||||||||||||||||||||||||||||||
| Toast.makeText( | ||||||||||||||||||||||||||||||||||||||
| requireContext(), | ||||||||||||||||||||||||||||||||||||||
| "$nickname 님을 차단했습니다.", | ||||||||||||||||||||||||||||||||||||||
| Toast.LENGTH_SHORT | ||||||||||||||||||||||||||||||||||||||
| ).show() | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| right = { | ||||||||||||||||||||||||||||||||||||||
| Toast.makeText(requireContext(), "차단 취소", Toast.LENGTH_SHORT).show() | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+150
to
+168
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 팝업 문구 하드코딩 — 문자열 리소스화 및 문구 일관성 "예"/"아니오", "차단했습니다/취소" 등은 strings.xml로 이동하세요. 또한 예시(strings.xml): <resources>
<string name="common_yes">예</string>
<string name="common_no">아니오</string>
<string name="block_confirm_title">%1$s님을 차단하시겠습니까?</string>
<string name="block_done">%1$s님을 차단했습니다.</string>
<string name="block_cancel">차단 취소</string>
</resources>사용: val nickname = "친구"
val textList = listOf(
getString(R.string.block_confirm_title, nickname),
getString(R.string.common_yes),
getString(R.string.common_no)
)🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,14 +3,15 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="match_parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 루트 ConstraintLayout의 height=wrap_content로 인해 RV/오버레이 제약 해석 오류 가능 — match_parent로 변경 권장 자식이 0dp(height)로 상하 제약을 사용하는 구조에서는 부모가 고정 크기여야 합니다. 현재 wrap_content면 측정이 불안정해 오버레이가 꽉 차지 않거나 RV가 0 높이로 렌더링될 수 있습니다. 다음 변경을 권장합니다: - android:layout_height="wrap_content"
+ android:layout_height="match_parent"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <!-- | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <LinearLayout | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/layout_empty" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="match_parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:orientation="vertical" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:gravity="center" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintTop_toTopOf="parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:visibility="gone"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintTop_toTopOf="parent"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ImageView | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/iv_empty_image" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="58dp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -30,7 +31,15 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintTop_toBottomOf="@id/iv_empty_image" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintStart_toStartOf="parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintEnd_toEndOf="parent"/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/btn_block" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:text="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:textAppearance="@style/TextAppearance.App.Medium.14sp"/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </LinearLayout> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <androidx.recyclerview.widget.RecyclerView | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/rv_friend_list" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="match_parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -39,5 +48,47 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_marginTop="20dp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintTop_toTopOf="parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintBottom_toBottomOf="parent"/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <androidx.recyclerview.widget.RecyclerView | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/rv_friend_list" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="match_parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="0dp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintTop_toTopOf="parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintBottom_toBottomOf="parent"/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <LinearLayout | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/layout_empty" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="match_parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="match_parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:orientation="vertical" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:gravity="center" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:background="@android:color/transparent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintTop_toTopOf="parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintBottom_toBottomOf="parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintStart_toStartOf="parent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| app:layout_constraintEnd_toEndOf="parent"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 빈 상태 오버레이가 기본 VISIBLE — 초기 화면 가림/터치 혼선 유발 layout_empty가 기본값(VISIBLE)이라 화면 진입 시 목록 유무 판단 전까지 전체를 덮습니다. 초기 플래시/깜빡임 및 뒤의 RV 클릭 혼선이 생길 수 있습니다. 기본을 GONE으로 두고, 필요 시만 VISIBLE로 토글하세요. 또한 오버레이가 보일 때 뒤의 RV 터치를 막으려면 clickable/focusable 설정이 필요합니다. <LinearLayout
android:id="@+id/layout_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center"
- android:background="@android:color/transparent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:background="@android:color/transparent"
+ android:visibility="gone"
+ android:clickable="true"
+ android:focusable="true"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ImageView | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/iv_empty_image" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="58dp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="58dp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_marginTop="66dp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:src="@drawable/ic_empty_room"/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+72
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 접근성: 빈 상태 이미지에 contentDescription 누락 장식용 이미지면 스크린리더 무시를 위해 <ImageView
android:id="@+id/iv_empty_image"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_marginTop="66dp"
- android:src="@drawable/ic_empty_room"/>
+ android:src="@drawable/ic_empty_room"
+ android:contentDescription="@null"/>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TextView | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/tv_empty_message" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:text="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:gravity="center" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_marginTop="13dp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_marginBottom="48dp"/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:id="@+id/btn_block" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_width="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:layout_height="wrap_content" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| android:text="친구 차단"/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </LinearLayout> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+88
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 하드코딩 문자열 사용 — 문자열 리소스로 분리하여 i18n/일관성 확보 Button 텍스트는 리소스로 관리하세요. <Button
android:id="@+id/btn_block"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="친구 차단"/>
+ android:text="@string/friend_block"/>strings.xml에 다음을 추가하세요(파일 외 변경): <resources>
<string name="friend_block">친구 차단</string>
</resources>🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
문자열 비교 기반 로직은 취약하며 userId=0 가드 필요
user.userId가 기본값 0일 수 있어 잘못된 삭제 동기화 호출 위험이 있습니다. 가드를 추가하세요.예시 수정(문자열 리소스로 이전 + userId 가드):
strings.xml(파일 외 추가):
또는 더 바람직하게는
viewModel.result를sealed class Result { object FriendDeleted : Result ... }형태로 바꿔 타입 안정적으로 분기해 주세요. 변경 원하시면 제안 드리겠습니다.🏁 Script executed:
Length of output: 17676
🏁 Script executed:
Length of output: 15434
친구 삭제 분기: 문자열 비교 취약 및 userId=0 가드 필요
간단히: viewModel.result가 문자열("친구가 삭제되었습니다.")로 분기되고 있으며, SharedPref에서 유저를 못 불러오면 user.userId가 기본값 0이 되어 잘못된 소켓 호출을 할 수 있습니다. 수정이 필요합니다.
수정이 필요한 위치:
권장 최소 수정 예시(간단 패치):
strings.xml(추가):
더 권장되는 개선(우선순위):
태그:
🤖 Prompt for AI Agents