-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/온보딩 프로필신고및차단 #29
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: "feature/\uC628\uBCF4\uB529-\uD504\uB85C\uD544\uC2E0\uACE0\uBC0F\uCC28\uB2E8"
Changes from all commits
07799ab
32fa6ae
b89c89c
3ab683b
7019f20
3c8a7b1
1a6f511
f08afb6
fb07c17
e364f12
a61d912
533b736
d4a2755
0cd1ac6
adfaa89
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 |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package umc.onairmate.ui.profile | ||
|
|
||
| import android.view.LayoutInflater | ||
| import android.view.ViewGroup | ||
| import android.widget.PopupMenu | ||
| import androidx.recyclerview.widget.DiffUtil | ||
| import androidx.recyclerview.widget.ListAdapter | ||
| import androidx.recyclerview.widget.RecyclerView | ||
| import umc.onairmate.R | ||
| import umc.onairmate.databinding.ItemBlockedUserBinding | ||
|
|
||
| data class BlockedUser(val nickname: String, val reason: String, val date: String) | ||
|
|
||
| class BlockListAdapter( | ||
| private val onUnblockClick: (BlockedUser) -> Unit | ||
| ) : ListAdapter<BlockedUser, BlockListAdapter.BlockViewHolder>(diffCallback) { | ||
|
|
||
| override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BlockViewHolder { | ||
| val binding = ItemBlockedUserBinding.inflate(LayoutInflater.from(parent.context), parent, false) | ||
| return BlockViewHolder(binding) | ||
| } | ||
|
|
||
| override fun onBindViewHolder(holder: BlockViewHolder, position: Int) { | ||
| holder.bind(getItem(position)) | ||
| } | ||
|
|
||
| inner class BlockViewHolder(private val binding: ItemBlockedUserBinding) : | ||
| RecyclerView.ViewHolder(binding.root) { | ||
|
|
||
| fun bind(user: BlockedUser) { | ||
| // 데이터 바인딩 | ||
| binding.tvNickname.text = "[${user.nickname}]" | ||
| binding.tvReason.text = "[${user.reason}]" | ||
| binding.tvDate.text = "[${user.date}]" | ||
|
|
||
| // btnMore 클릭 시 팝업 메뉴 표시 | ||
| binding.btnMore.setOnClickListener { view -> | ||
| val popup = PopupMenu(view.context, view) | ||
| popup.menuInflater.inflate(R.menu.menu_block_option, popup.menu) | ||
| popup.setOnMenuItemClickListener { item -> | ||
| when (item.itemId) { | ||
| R.id.menuUnblock -> { | ||
| onUnblockClick(user) // 콜백 호출 | ||
| true | ||
| } | ||
| else -> false | ||
| } | ||
| } | ||
| popup.show() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| companion object { | ||
| val diffCallback = object : DiffUtil.ItemCallback<BlockedUser>() { | ||
| override fun areItemsTheSame(oldItem: BlockedUser, newItem: BlockedUser) = | ||
| oldItem.nickname == newItem.nickname | ||
|
|
||
| override fun areContentsTheSame(oldItem: BlockedUser, newItem: BlockedUser) = | ||
| oldItem == newItem | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||||||||||||||||
| package umc.onairmate.ui.profile | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import android.os.Bundle | ||||||||||||||||||||
| import android.util.Log | ||||||||||||||||||||
| import android.view.LayoutInflater | ||||||||||||||||||||
| import android.view.View | ||||||||||||||||||||
| import android.view.ViewGroup | ||||||||||||||||||||
| import android.widget.ImageButton | ||||||||||||||||||||
| import android.widget.PopupMenu | ||||||||||||||||||||
| import androidx.fragment.app.Fragment | ||||||||||||||||||||
| import androidx.recyclerview.widget.LinearLayoutManager | ||||||||||||||||||||
| import umc.onairmate.R | ||||||||||||||||||||
| import umc.onairmate.databinding.FragmentBlockListBinding | ||||||||||||||||||||
|
|
||||||||||||||||||||
| class BlockListFragment : Fragment() { | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private var _binding: FragmentBlockListBinding? = null | ||||||||||||||||||||
| private val binding get() = _binding!! | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private lateinit var blockListAdapter: BlockListAdapter | ||||||||||||||||||||
|
|
||||||||||||||||||||
| override fun onCreateView( | ||||||||||||||||||||
| inflater: LayoutInflater, container: ViewGroup?, | ||||||||||||||||||||
| savedInstanceState: Bundle? | ||||||||||||||||||||
| ): View { | ||||||||||||||||||||
| _binding = FragmentBlockListBinding.inflate(inflater, container, false) | ||||||||||||||||||||
| return binding.root | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||||||||||||||||||
| super.onViewCreated(view, savedInstanceState) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Log.d("BlockListFragment", "view created, binding.ivBack=${binding.ivBack}") | ||||||||||||||||||||
| setupRecyclerView() | ||||||||||||||||||||
| setupListeners() | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private fun setupRecyclerView() { | ||||||||||||||||||||
| blockListAdapter = BlockListAdapter { blockedUser -> | ||||||||||||||||||||
| // 차단 해제 클릭 시 처리 | ||||||||||||||||||||
| } | ||||||||||||||||||||
| binding.rvBlockList.apply { | ||||||||||||||||||||
| layoutManager = LinearLayoutManager(requireContext()) | ||||||||||||||||||||
| adapter = blockListAdapter | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // 테스트 데이터 | ||||||||||||||||||||
| blockListAdapter.submitList( | ||||||||||||||||||||
| listOf( | ||||||||||||||||||||
| BlockedUser("차단한 사용자 닉네임", "차단 사유", "0000년 00월 00일"), | ||||||||||||||||||||
| BlockedUser("차단한 사용자 닉네임", "차단 사유", "0000년 00월 00일"), | ||||||||||||||||||||
| BlockedUser("차단한 사용자 닉네임", "차단 사유", "0000년 00월 00일") | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private fun setupListeners() { | ||||||||||||||||||||
| binding.ivBack.setOnClickListener { | ||||||||||||||||||||
| requireActivity().onBackPressed() | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| override fun onDestroyView() { | ||||||||||||||||||||
| super.onDestroyView() | ||||||||||||||||||||
| _binding = null | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+65
to
+68
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 onDestroyView에서 RecyclerView 어댑터 해제 Fragment 뷰 생명주기와 어댑터/뷰홀더 참조가 얽혀 메모리 누수가 발생할 수 있습니다. 뷰 파괴 시 어댑터를 null로 해제하세요. override fun onDestroyView() {
super.onDestroyView()
- _binding = null
+ binding.rvBlockList.adapter = null
+ _binding = null
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| } | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package umc.onairmate.data.api | ||
|
|
||
| import retrofit2.http.Body | ||
| import retrofit2.http.GET | ||
| import retrofit2.http.Header | ||
| import retrofit2.http.PUT | ||
| import retrofit2.http.Path | ||
| import umc.onairmate.data.model.response.NicknameResponse | ||
| import umc.onairmate.data.model.response.RawDefaultResponse | ||
|
|
||
| interface NicknameService { | ||
| @GET("auth/check-nickname/{nickname}") | ||
| suspend fun checkNickname( | ||
| @Path("nickname") nickname: String | ||
| ): NicknameResponse | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package umc.onairmate.data.model.entity | ||
|
|
||
| import com.google.gson.annotations.SerializedName | ||
|
|
||
| data class NicknameData( | ||
| @SerializedName("available") | ||
| val available: Boolean, | ||
|
|
||
| @SerializedName("message") | ||
| val message: String | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package umc.onairmate.data.model.response | ||
|
|
||
| import com.google.gson.annotations.SerializedName | ||
| import umc.onairmate.data.model.entity.NicknameData | ||
|
|
||
| class NicknameResponse ( | ||
| @SerializedName("success") | ||
| val success: Boolean, | ||
|
|
||
| @SerializedName("data") | ||
| val data: NicknameData | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package umc.onairmate.data.repository | ||
|
|
||
| interface NicknameRepository { | ||
| suspend fun isNicknameDuplicated(nickname: String): Boolean | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,23 @@ | ||||||||||||||||||||||||||||||||||||||||||
| package umc.onairmate.data.repository.repositoryImpl | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import android.util.Log | ||||||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.data.api.NicknameService | ||||||||||||||||||||||||||||||||||||||||||
| import umc.onairmate.data.repository.NicknameRepository | ||||||||||||||||||||||||||||||||||||||||||
| import javax.inject.Inject | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| class NicknameRepositoryImpl @Inject constructor( | ||||||||||||||||||||||||||||||||||||||||||
| private val api: NicknameService | ||||||||||||||||||||||||||||||||||||||||||
| ): NicknameRepository { | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| override suspend fun isNicknameDuplicated(nickname: String): Boolean { | ||||||||||||||||||||||||||||||||||||||||||
| return try { | ||||||||||||||||||||||||||||||||||||||||||
| val response = api.checkNickname(nickname) | ||||||||||||||||||||||||||||||||||||||||||
| // available == true 면 사용 가능한 닉네임, 중복 아님 → 따라서 중복 여부는 반대(!) | ||||||||||||||||||||||||||||||||||||||||||
| !response.data.available | ||||||||||||||||||||||||||||||||||||||||||
| } catch (e: Exception) { | ||||||||||||||||||||||||||||||||||||||||||
| Log.e("NicknameRepository", "닉네임 중복 검사 실패", e) | ||||||||||||||||||||||||||||||||||||||||||
| // 실패 시 기본값 false 또는 true 선택 가능 (보통 실패는 중복 아님 false로 처리) | ||||||||||||||||||||||||||||||||||||||||||
| false | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+12
to
+21
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 에러를 ‘중복 아님(false)’으로 간주하면 잘못된 승인 위험이 큽니다 네트워크/서버 오류 시 false를 반환하면 중복된 닉네임이 ‘사용 가능’으로 오판될 수 있습니다. 최소 조치로 실패 시 보수적으로 true(중복) 처리하고, 후속 PR에서 3-state(Result/에러 구분)로 개선하는 것을 권장합니다. 다음과 같이 변경을 제안합니다: return try {
val response = api.checkNickname(nickname)
// available == true 면 사용 가능한 닉네임, 중복 아님 → 따라서 중복 여부는 반대(!)
!response.data.available
} catch (e: Exception) {
Log.e("NicknameRepository", "닉네임 중복 검사 실패", e)
- // 실패 시 기본값 false 또는 true 선택 가능 (보통 실패는 중복 아님 false로 처리)
- false
+ // 실패 시 보수적으로 중복(true) 처리하여 잘못된 승인 방지
+ true
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -70,25 +70,6 @@ object NetworkModule { | |||||||||||||
| private inline fun <reified T> Retrofit.buildService(): T{ | ||||||||||||||
| return this.create(T::class.java) | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
70
to
72
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 private 확장 함수는 다른 모듈/파일에서 사용할 수 없어 DI 사용 시 컴파일 오류 가능. ServiceModule 등에서 - private inline fun <reified T> Retrofit.buildService(): T{
- return this.create(T::class.java)
- }
+ inline fun <reified T> Retrofit.buildService(): T {
+ return this.create(T::class.java)
+ }또는, 팀 표준에 맞춰 top-level 함수로 분리(파일 전역 public)하면 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents💡 Verification agent 🧩 Analysis chainbuildService 확장 함수 사용 위치 자동 검증 제안. ServiceModule 등에서 실제로 이 함수가 import되어 사용되는지 확인이 필요합니다. 아래 스크립트로 사용처를 빠르게 점검할 수 있습니다. 🏁 Script executed: #!/bin/bash
# buildService 확장 함수 사용처 및 import 확인
rg -n --no-heading $'buildService<' -A 2
rg -n --no-heading $'^import .+NetworkModule\.buildService$' -A 1Length of output: 108 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Searching for 'buildService' occurrences..."
rg -n --no-heading --hidden 'buildService' || true
echo
echo "Searching for generic calls 'buildService<'..."
rg -n --no-heading --hidden 'buildService<' || true
echo
echo "Searching for '.buildService'..."
rg -n --no-heading --hidden '\.buildService' || true
echo
echo "Show NetworkModule.kt content (first 300 lines if exists)..."
FILE=app/src/main/java/umc/onairmate/module/NetworkModule.kt
if [ -f "$FILE" ]; then
sed -n '1,300p' "$FILE"
else
echo "File not found: $FILE"
fiLength of output: 5259 buildService 확장 함수 중복 선언 — NetworkModule.kt의 함수 제거 권고 검증 결과: ServiceModule.kt에 동일한 확장 함수가 선언되어 여러 곳에서 사용되고 있으며, NetworkModule.kt(라인 70)의 buildService는 레포 전역에서 호출처가 발견되지 않습니다.
제안 조치: NetworkModule.kt의 중복 확장 함수를 제거하거나(권장) 한 곳으로 통합해 중복을 제거하세요. 의도적 분리라면 이유를 주석으로 명시해 주세요. 문맥용 코드(참고) 🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| @Module | ||||||||||||||
| @InstallIn(SingletonComponent::class) | ||||||||||||||
| object NetworkModule { | ||||||||||||||
|
|
||||||||||||||
| @Provides | ||||||||||||||
| @Singleton | ||||||||||||||
| fun provideRetrofit(): Retrofit { | ||||||||||||||
| return Retrofit.Builder() | ||||||||||||||
| .baseUrl(OnAirMateApplication.getString(R.string.base_url)) // 수정 필요 | ||||||||||||||
| .addConverterFactory(GsonConverterFactory.create()) | ||||||||||||||
| .build() | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| @Provides | ||||||||||||||
| @Singleton | ||||||||||||||
| fun provideJoinService(retrofit: Retrofit): JoinService { | ||||||||||||||
| return retrofit.create(JoinService::class.java) | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| } | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,8 @@ import umc.onairmate.data.api.CollectionService | |
| import umc.onairmate.data.api.HomeService | ||
| import umc.onairmate.data.api.JoinService | ||
| import umc.onairmate.data.repository.repository.BookmarkRepository | ||
| import umc.onairmate.data.api.NicknameService | ||
| import umc.onairmate.data.repository.NicknameRepository | ||
| import umc.onairmate.data.repository.repository.FriendRepository | ||
|
Comment on lines
+16
to
18
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 chainNicknameRepository 패키지 경로 확인 필요(다른 레포지토리들과 불일치 가능성) 다른 레포지토리 인터페이스는 다음 스크립트로 인터페이스의 실제 패키지/경로를 확인해 주세요: 🏁 Script executed: #!/bin/bash
# NicknameRepository 선언 위치와 패키지명 확인
rg -n -A 2 -B 2 $'^package .*NicknameRepository' app | sed -n '1,200p'
rg -n $'interface\\s+NicknameRepository' app -A 3 -B 3Length of output: 675 조치 필요: NicknameRepository 패키지/파일 위치 불일치 확인 확인 결과: 파일은 app/src/main/java/umc/onairmate/data/repository/repository/NicknameRepository.kt에 위치하지만 내부 패키지 선언은 수정 권고 (택1)
수정할 파일(우선 확인 대상)
🤖 Prompt for AI Agents |
||
| import umc.onairmate.data.repository.repository.ChatRoomRepository | ||
| import umc.onairmate.data.repository.repository.CollectionRepository | ||
|
|
@@ -26,6 +28,7 @@ import umc.onairmate.data.repository.repositoryImpl.CollectionRepositoryImpl | |
| import umc.onairmate.data.repository.repositoryImpl.HomeRepositoryImpl | ||
| import umc.onairmate.data.repository.repositoryImpl.JoinRepositoryImpl | ||
| import umc.onairmate.data.repository.repositoryImpl.AuthRepositoryImpl | ||
| import umc.onairmate.data.repository.repositoryImpl.NicknameRepositoryImpl | ||
|
|
||
| @Module | ||
| @InstallIn(ViewModelComponent::class) | ||
|
|
@@ -35,7 +38,7 @@ object RepositoryModule { | |
| @Provides | ||
| fun providesHomeRepository( | ||
| homeService: HomeService | ||
| ) : HomeRepository = HomeRepositoryImpl(homeService) | ||
| ): HomeRepository = HomeRepositoryImpl(homeService) | ||
|
|
||
| @ViewModelScoped | ||
| @Provides | ||
|
|
@@ -47,13 +50,13 @@ object RepositoryModule { | |
| @Provides | ||
| fun providesFriendRepository( | ||
| friendService: FriendService | ||
| ) : FriendRepository = FriendRepositoryImpl(friendService) | ||
| ): FriendRepository = FriendRepositoryImpl(friendService) | ||
|
|
||
| @ViewModelScoped | ||
| @Provides | ||
| fun providesChatRoomRepository( | ||
| chatRoomService: ChatRoomService | ||
| ) : ChatRoomRepository = ChatRoomRepositoryImpl(chatRoomService) | ||
| ): ChatRoomRepository = ChatRoomRepositoryImpl(chatRoomService) | ||
|
|
||
| @ViewModelScoped | ||
| @Provides | ||
|
|
@@ -67,16 +70,16 @@ object RepositoryModule { | |
| collectionService: CollectionService | ||
| ) : CollectionRepository = CollectionRepositoryImpl(collectionService) | ||
|
|
||
| @Module | ||
| @InstallIn(ViewModelComponent::class) | ||
| object RepositoryModule { | ||
| @ViewModelScoped | ||
| @Provides | ||
| fun provideJoinRepository( | ||
| joinService: JoinService | ||
| ): JoinRepository = JoinRepositoryImpl(joinService) | ||
|
|
||
| @ViewModelScoped | ||
| @Provides | ||
| fun provideNicknameRepository( | ||
| nicknameService: NicknameService | ||
| ): NicknameRepository = NicknameRepositoryImpl(nicknameService) | ||
|
|
||
| @Provides | ||
| @ViewModelScoped | ||
| fun provideJoinRepository( | ||
| joinService: JoinService | ||
| ): JoinRepository { | ||
| return JoinRepositoryImpl(joinService) | ||
| } | ||
| } | ||
| } | ||
| } | ||
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.
🛠️ Refactor suggestion
백버튼 처리: Navigation API 사용 권장
Activity.onBackPressed()는 deprecated입니다. NavController를 통해 navigateUp/popBackStack을 호출하는 방식이 안전합니다.
추가로 파일 상단에 다음 import가 필요합니다:
📝 Committable suggestion
🤖 Prompt for AI Agents