-
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 all commits
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() | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
97
to
103
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. 💡 Verification agent 🧩 Analysis chain문자열 비교 기반 로직은 취약하며 userId=0 가드 필요
예시 수정(문자열 리소스로 이전 + userId 가드): - if (message == "친구가 삭제되었습니다.") friendChatViewModel.deleteFriend(friendId, user.userId)
+ if (message == getString(R.string.friend_deleted)) {
+ if (user.userId != 0) {
+ friendChatViewModel.deleteFriend(friendId, user.userId)
+ }
+ }strings.xml(파일 외 추가): <resources>
<string name="friend_deleted">친구가 삭제되었습니다.</string>
</resources>또는 더 바람직하게는 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== repo root: ==="
pwd
echo
echo "=== Find FriendListTabFragment file ==="
rg --hidden -n "FriendListTabFragment" || true
echo
echo "=== Print FriendListTabFragment (first 400 lines) if exists ==="
if [ -f "app/src/main/java/umc/onairmate/ui/friend/list/FriendListTabFragment.kt" ]; then
sed -n '1,400p' app/src/main/java/umc/onairmate/ui/friend/list/FriendListTabFragment.kt || true
else
echo "File not found at expected path."
fi
echo
echo "=== Search for viewModel.result, deleteFriend, userId, and User model ==="
rg --hidden -n "viewModel.result" || true
rg --hidden -n "deleteFriend" || true
rg --hidden -n "friendChatViewModel" || true
rg --hidden -n "data class .*User|class .*User" || true
rg --hidden -n "userId" || trueLength of output: 17676 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== FriendViewModel.kt ==="
if [ -f "app/src/main/java/umc/onairmate/ui/friend/FriendViewModel.kt" ]; then
sed -n '1,400p' app/src/main/java/umc/onairmate/ui/friend/FriendViewModel.kt
else
echo "FriendViewModel.kt not found"
fi
echo
echo "=== FriendChatViewModel.kt ==="
if [ -f "app/src/main/java/umc/onairmate/ui/friend/chat/FriendChatViewModel.kt" ]; then
sed -n '1,400p' app/src/main/java/umc/onairmate/ui/friend/chat/FriendChatViewModel.kt
else
echo "FriendChatViewModel.kt not found"
fi
echo
echo "=== SharedPrefUtil.kt ==="
if [ -f "app/src/main/java/umc/onairmate/ui/util/SharedPrefUtil.kt" ]; then
sed -n '1,400p' app/src/main/java/umc/onairmate/ui/util/SharedPrefUtil.kt
else
rg --hidden -n "object SharedPrefUtil|class SharedPrefUtil" || true
fi
echo
echo "=== UserData.kt ==="
if [ -f "app/src/main/java/umc/onairmate/data/model/entity/UserData.kt" ]; then
sed -n '1,200p' app/src/main/java/umc/onairmate/data/model/entity/UserData.kt
else
echo "UserData.kt not found"
fi
echo
echo "=== Search for literal message '친구가 삭제되었습니다.' ==="
rg --hidden -n "친구가 삭제되었습니다." || true
echo
echo "=== Search for LiveData result in FriendViewModel ==="
rg --hidden -n "val result" app/src/main/java/umc/onairmate/ui/friend || true
rg --hidden -n "MutableLiveData.*result|MutableLiveData<.*>.*result" || trueLength of output: 15434 친구 삭제 분기: 문자열 비교 취약 및 userId=0 가드 필요 간단히: viewModel.result가 문자열("친구가 삭제되었습니다.")로 분기되고 있으며, SharedPref에서 유저를 못 불러오면 user.userId가 기본값 0이 되어 잘못된 소켓 호출을 할 수 있습니다. 수정이 필요합니다. 수정이 필요한 위치:
권장 최소 수정 예시(간단 패치): - viewModel.result.observe(viewLifecycleOwner, Observer { message ->
- message ?: return@Observer
- if (message == "친구가 삭제되었습니다.") friendChatViewModel.deleteFriend(friendId, user.userId)
- initData()
- Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
- viewModel.clearResult()
- })
+ viewModel.result.observe(viewLifecycleOwner, Observer { message ->
+ message ?: return@Observer
+ // 문자열 리터럴 대신 리소스(또는 더 나은 타입)를 사용하고 userId 가드 추가
+ if (message == getString(R.string.friend_deleted)) {
+ if (user.userId != 0) {
+ friendChatViewModel.deleteFriend(friendId, user.userId)
+ }
+ }
+ initData()
+ Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
+ viewModel.clearResult()
+ })strings.xml(추가): <string name="friend_deleted">친구가 삭제되었습니다.</string>더 권장되는 개선(우선순위):
태그: 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| 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 |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:orientation="vertical" | ||
| android:padding="20dp" | ||
| android:layout_width="320dp" | ||
| android:layout_height="494dp"> | ||
|
Comment on lines
+5
to
+6
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 고정 크기(320dp x 494dp)로 인한 화면 깨짐/키보드 가림 가능성 다양한 해상도/회전/키보드 상황에서 다이얼로그 콘텐츠가 잘리는 리스크가 큽니다. 스크롤 가능 영역으로 감싸고, 다이얼로그 콘텐츠는 wrap_content로 두는 구성이 안전합니다. 아래처럼 ScrollView + 내부 LinearLayout으로 변경을 제안합니다: -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:padding="20dp"
- android:layout_width="320dp"
- android:layout_height="494dp">
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:padding="20dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
...
-</LinearLayout>
+</LinearLayout>
+</ScrollView> |
||
|
|
||
| <TextView | ||
| android:id="@+id/tvTitle" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:text="[닉네임]의 신고 사유" | ||
| android:textSize="16sp" | ||
| android:textStyle="bold" | ||
| android:gravity="center" | ||
| android:paddingBottom="16dp"/> | ||
|
|
||
| <CheckBox | ||
| android:id="@+id/cbSpam" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:text="스팸, 도배"/> | ||
|
|
||
| <CheckBox | ||
| android:id="@+id/cbSports" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:text="스포성 채팅"/> | ||
|
|
||
| <CheckBox | ||
| android:id="@+id/cbAbuse" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:text="욕설"/> | ||
|
|
||
| <CheckBox | ||
| android:id="@+id/cbHate" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:text="혐오 발언"/> | ||
|
|
||
| <CheckBox | ||
| android:id="@+id/cbNickname" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:text="부적절한 닉네임 사용"/> | ||
|
|
||
| <EditText | ||
| android:id="@+id/etExtraReason" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:hint="추가적인 신고 사유를 작성해주세요." | ||
| android:minHeight="80dp" | ||
| android:padding="10dp" | ||
| android:layout_marginTop="10dp"/> | ||
|
|
||
| <Button | ||
| android:id="@+id/btnSubmit" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:text="제출" | ||
| android:layout_marginTop="16dp"/> | ||
| </LinearLayout> | ||
| 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.
Block 클릭 시 실제 차단/신고 로직이 호출되지 않음
현재 다이얼로그만 표시되고 어떤 콜백도 호출되지 않아 기능적으로 회귀가 발생합니다. 최소한 제출 버튼에서 기존 clickBlock(data)를 호출하거나, 사유까지 전달할 새로운 콜백을 추가해야 합니다.
아래와 같이 다이얼로그 바인딩을 사용하고 제출 버튼에서 검증 후 기존 로직을 호출하도록 제안합니다. (MaterialAlertDialogBuilder 사용 권장)
필요한 외부 import(파일 상단에 추가):
추가 제안:
🤖 Prompt for AI Agents